Skip to content

The fd in a Dir object cannot be used with fsync() #47

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

Open
zackw opened this issue Dec 3, 2023 · 1 comment · Fixed by cehteh/openat#5
Open

The fd in a Dir object cannot be used with fsync() #47

zackw opened this issue Dec 3, 2023 · 1 comment · Fixed by cehteh/openat#5

Comments

@zackw
Copy link

zackw commented Dec 3, 2023

If you want a guarantee that a rename() operation has been committed to disk, you need to call fsync() on all the directories that were involved, which of course means you need file descriptors referring to those directories. I was hoping I could use openat::Dir to abstract over the process of acquiring those fds, and just write this in my code:

use std::io;
use std::os::fd::AsRawFd;
use openat::Dir;
fn sync_dir(d: &Dir) -> io::Result<()> {
    // SAFETY: trusts you to supply a valid fd, manual error checking required
    let status = unsafe { libc::fsync(d.as_raw_fd()) };
    if status != 0 {
        return Err(io::Error::last_os_error());
    }
    Ok(())
}

Unfortunately, this doesn't work; the fsync() call fails with code EBADF. The problem appears to be that openat::Dir wraps a fd that was opened with O_PATH, and to use fsync you instead need a fd that was opened with O_RDONLY|O_DIRECTORY. A C program that demonstrates this requirement is attached.

As far as I know, the only other differences between O_PATH and O_RDONLY|O_DIRECTORY, when applied to a directory, are that in the latter case you need read permission on the directory, and, if it succeeds, you are then allowed to read the contents of the directory (readdir in C, getdents at the syscall level). Thus, the Right Thing™, in my opinion, would be, on Linux, to attempt an open call first with O_RDONLY|O_DIRECTORY and then fall back to O_PATH|O_DIRECTORY if that fails with EPERM. This change would fix my problem and should also fix #34.

fsync_dir_requires_o_directory.c

@cehteh
Copy link
Contributor

cehteh commented Dec 3, 2023

In my fork (openat_ct) I added a DirFlags builder which allows to specify the flags for creating Dir's. I just noticed that the documentation is wrong there in a earlier implementation i forced O_PATH flags, that's not there anymore. Eventually i should go over it and fix the doc and bring that crate up to date (remove the build.rs). A sync() -> Result<(), CanNotSync> method on a Dir could be nice, PR welcome.

zackw added a commit to zackw/openat that referenced this issue Dec 12, 2023
It calls fsync() on the file descriptor.  This is necessary with some
operating systems and/or file systems to ensure that changes to the
contents of the directory make it to persistent storage — for the same
reason it is sometimes necessary to use `std::fs::File::sync_{all,contents}`.

Note that this method will fail if used on a `Dir` opened with `O_PATH`.
(Same issue as `list_dir`; see tailhook#34.)

Closes tailhook#47.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants