Skip to content

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

@zackw

Description

@zackw

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions