Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

the write_at function from std::os::unix::prelude::FileExt doesn't work as described #113627

Closed
nnayo opened this issue Jul 12, 2023 · 9 comments · Fixed by #113876
Closed

the write_at function from std::os::unix::prelude::FileExt doesn't work as described #113627

nnayo opened this issue Jul 12, 2023 · 9 comments · Fixed by #113876
Assignees
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools C-bug Category: This is a bug. E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-help-wanted Call for participation: Help is requested to fix this issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@nnayo
Copy link

nnayo commented Jul 12, 2023

I'm pretty new to rust and I'm working on a small project where I need to write into a file some data at various offsets, so the write_at function seems to perfectly fit my need.

So I tried this code (with is the provided example):

use std::fs::File;
use std::io;
use std::os::unix::prelude::FileExt;

fn main() -> io::Result<()> {
    let file = File::open("foo.txt")?;

    // We now write at the offset 10.
    file.write_at(b"sushi", 10)?;
    Ok(())
}

and I do cargo run

I expected to see this happen: a foo.txt in the current directory with a size of 15 bytes

Instead, this happened: Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

So, I modifed the filename to /tmp/foo.txt (in order to have an absolute path).
I got the same error.

So, I created the file with touch /tmp/foo.txt
Then I got the error: Error: Os { code: 9, kind: Uncategorized, message: "Bad file descriptor" }

So, I add the line println!("{:?}", file); to get some info on the file
Then I got: File { fd: 3, path: "/tmp/foo.txt", read: true, write: false }

So, the file is open read-only, I searched and modified the code to: let file = File::options().append(true).open("/tmp/foo.txt")?;
Then I got: File { fd: 3, path: "/tmp/foo.txt", read: false, write: true } and finally no error (on stdout),

But the file is only 5-byte long whereas I expected it to be 15-byte long.

And checking foo.txt content with od -t x1 -a /tmp/foo.txt gives:

0000000  73  75  73  68  69
          s   u   s   h   i
0000005

The final code is

use std::fs::File;
use std::io;
use std::os::unix::prelude::FileExt;

fn main() -> io::Result<()> {
    let file = File::options().append(true).open("/tmp/foo.txt")?;
    println!("{:?}", file);
              
    // We now write at the offset 10.
    file.write_at(b"sushi", 10)?;
    Ok(())
}

and you need to manually create the file before running the binary.

Meta

rustc --version --verbose:

rustc 1.70.0 (90c541806 2023-05-31)
binary: rustc
commit-hash: 90c541806f23a127002de5b4038be731ba1458ca
commit-date: 2023-05-31
host: x86_64-unknown-linux-gnu
release: 1.70.0
LLVM version: 16.0.2

Backtrace

<backtrace>

@nnayo nnayo added the C-bug Category: This is a bug. label Jul 12, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jul 12, 2023
@workingjubilee workingjubilee added T-libs Relevant to the library team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Jul 12, 2023
@workingjubilee
Copy link
Member

workingjubilee commented Jul 12, 2023

Wow those docs are terrible, how did that happen?

We can immediately patch the docs to code that actually would work to write to a file, but we need to define what "extended appropriately" means. i.e. Is this intended behavior?

But perhaps more importantly, we need to figure out why the hell we have doctest examples that don't actually work and what can we do to fix that?

@workingjubilee workingjubilee added the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Jul 12, 2023
@ChrisDenton
Copy link
Member

Yeah it's weird that we have a non-working example. That should be fixed asap. It looks like it was copy/pasted from read_at but not updated to use File::create or something like that.

I'll mark this as help wanted. Fixing that should not be controversial.

@ChrisDenton ChrisDenton added the E-help-wanted Call for participation: Help is requested to fix this issue. label Jul 12, 2023
@ChrisDenton
Copy link
Member

The issue with the final code in the OP is that it opens the file in append only mode. The docs do not cover this case at all so I can't say what the intended behaviour is. I guess write_at is really incompatible with append only because it needs to have some knowledge of the real file position in order to work as expected.

@workingjubilee workingjubilee added the E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. label Jul 12, 2023
@workingjubilee
Copy link
Member

( for the doc fix part, anyways. )

@workingjubilee
Copy link
Member

workingjubilee commented Jul 12, 2023

It does explain that returning short writes is valid, so I guess the behavior exhibited in the append-only case presented here is technically acceptable behavior. 🫠

@saethlin
Copy link
Member

On Linux, write_at just calls pwrite64 which documents this:

BUGS
       POSIX requires that opening a file with the O_APPEND flag should have no effect on the location at which pwrite()
       writes data. However, on Linux, if a file is opened with O_APPEND, pwrite() appends data to the end of the file, re‐
       gardless of the value of offset.

@joboet
Copy link
Member

joboet commented Jul 14, 2023

On Linux, write_at just calls pwrite64 which documents this:

BUGS
       POSIX requires that opening a file with the O_APPEND flag should have no effect on the location at which pwrite()
       writes data. However, on Linux, if a file is opened with O_APPEND, pwrite() appends data to the end of the file, re‐
       gardless of the value of offset.

There was a kernel proposal to fix this via a flag on pwritev2, but that didn't get merged...

@Amanieu Amanieu removed the I-libs-api-nominated Nominated for discussion during a libs-api team meeting. label Jul 18, 2023
@Amanieu
Copy link
Member

Amanieu commented Jul 18, 2023

We discussed this in the libs-api meeting. It seems that there are 2 actionable items here, both in the documentation:

  • The example in write_at needs to be fixed to use File::create (write mode) instead of File::open (read mode).
  • The documentation for write_at should link to https://man7.org/linux/man-pages/man2/pwrite.2.html#BUGS to indicate that, on some platforms, files opened in append mode ignore the position passed in and always append to the end of the file.

@darklyspaced
Copy link
Contributor

@rustbot claim

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jul 22, 2023
fix docs & example for `std::os::unix::prelude::FileExt::write_at`

 Changelog:
 * used `File::create` instead of `File::read` to get a writeable file
 * explicity mentioned the bug with `pwrite64` in docs

Unfortunately, I don't think that there is really much we can do about this since the feature has already been stabilised.

We could potentially add a clippy lint warning people on Linux that using `write_at` with the `O_APPEND` flag does not exhibit the behaviour that they would have assumed.

fixes rust-lang#113627
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jul 22, 2023
fix docs & example for `std::os::unix::prelude::FileExt::write_at`

 Changelog:
 * used `File::create` instead of `File::read` to get a writeable file
 * explicity mentioned the bug with `pwrite64` in docs

Unfortunately, I don't think that there is really much we can do about this since the feature has already been stabilised.

We could potentially add a clippy lint warning people on Linux that using `write_at` with the `O_APPEND` flag does not exhibit the behaviour that they would have assumed.

fixes rust-lang#113627
@bors bors closed this as completed in 746d507 Jul 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools C-bug Category: This is a bug. E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-help-wanted Call for participation: Help is requested to fix this issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants