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

What should a file system do if asked to create a file that already exists? #209

Closed
jacobsa opened this issue May 21, 2015 · 9 comments
Closed
Assignees
Milestone

Comments

@jacobsa
Copy link

jacobsa commented May 21, 2015

What are the expections of osxfuse when the userspace daemon is sent a create request, but the file already exists? Should it return EEXIST, just open the file, or what?

Some notes on what I've determined experimentally:

  • If I call open(2) with several workers in parallel using O_CREAT, on Linux I only ever receive a single create request, but with osxfuse I receive many. So I conclude that Linux is holding a lock around the "look up, find doesn't exist, send create request" process, but osxfuse isn't.
  • Of course this question still applies on Linux if a file may be created remotely, so that it springs into existence after the kernel does a lookup but before it calls create.
  • Unless I've missed something, osxfuse doesn't seem to pass on the O_EXCL flag to the userspace daemon, so the daemon probably can't be responsible for deciding whether it is safe to open the file if it already exists.
  • Returning EEXIST appears to do the right thing whether O_EXCL is set or not, but just opening fails to work right if O_EXCL is set.
@jacobsa
Copy link
Author

jacobsa commented May 21, 2015

Also worth noting: I see the same Linux/OS X behavior difference for creating a symlink and creating a directory: in both cases, Linux excludes concurrent creation, but OS X doesn't. (This is fine, as long as I know what I'm expected to do when it happens.)

jacobsa added a commit to jacobsa/fuse that referenced this issue May 21, 2015
Added tests that confirm behavior of "real" file systems, then made memfs make
those tests pass, too. Doing this by returning EEXIST appears to work on both
OS X and Linux, but I can't find documentation anywhere that says this is what
is expected.

More:
    osxfuse/osxfuse#209
    http://stackoverflow.com/q/30364838/1505451
    http://sourceforge.net/p/fuse/mailman/message/34130996/
@yobert
Copy link

yobert commented Jan 22, 2016

I'm pretty sure returning EEXIST is the right thing to do. O_EXCL I think would try to get an exclusive lock on the file handle after creation but before returning. So whether or not you can see O_EXCL, you probably want to fail with EEXIST.

@bfleischer bfleischer self-assigned this Jul 14, 2016
@bfleischer bfleischer added this to the 3.4.1 milestone Jul 14, 2016
@bfleischer
Copy link
Member

The macOS kernel expects all create calls to return EEXIST in case the file already exists, regardless of whether O_EXCL was specified for the original create call or not. To ensure this, starting with version 3.4.1, the FUSE kernel extension will set the O_EXCL flag for every FUSE_CREATE request sent to user space.

Apple states in hfs_vnop_create:

We leave handling of certain race conditions here to the caller which will have a better understanding of the semantics it requires. For example, if it turns out that the file exists, it would be wrong of us to return a reference to the existing file because the caller might not want that and it would be misleading to suggest the file had been created when it hadn’t been. Note that our NFS server code does not set the VA_EXCLUSIVE flag so you cannot assume that callers don't want EEXIST errors if it's not set. The common case, where users are calling open with the O_CREAT mode, is handled in VFS; when we return EEXIST, it will loop and do the look-up again.

I've added a new mount-time option to add some flexibility for network file systems:

If the mount-time option excl_create is specified, the O_EXCL flag will only be set for "truly" exclusive creates, i.e. create calls for which the initiator explicitly set the O_EXCL flag. This allows network file systems to determine whether or not to acquire a potentially costly lock to prevent remote create races. Nonetheless, the file system still needs to make sure that there are no local create races.

bfleischer added a commit to osxfuse/kext that referenced this issue Jul 18, 2016
The macOS kernel expects all create calls to return EEXIST in case the
file already exists, regardless of whether O_EXCL was specified or not.

Apple states in hfs_vnop_create:

    We leave handling of certain race conditions here to the caller
    which will have a better understanding of the semantics it requires.
    For example, if it turns out that the file exists, it would be wrong
    of us to return a reference to the existing file because the caller
    might not want that and it would be misleading to suggest the file had
    been created when it hadn’t been. Note that our NFS server code does
    not set the VA_EXCLUSIVE flag so you cannot assume that callers don't
    want EEXIST errors if it's not set. The common case, where users are
    calling open with the O_CREAT mode, is handled in VFS; when we return
    EEXIST, it will loop and do the look-up again.

Resolves osxfuse/osxfuse#209
@jacobsa
Copy link
Author

jacobsa commented Jul 18, 2016

@bfleischer: thank you very much for the explanation! Could you link to the source of the quote from Apple? I'm not familiar with VFS documentation for darwin, and would love to have a reference to consult.

@bfleischer
Copy link
Member

The quote is from the following XNU source file:

http://opensource.apple.com//source/xnu/xnu-3248.40.184/bsd/hfs/hfs_vnops.c

@jacobsa
Copy link
Author

jacobsa commented Jul 19, 2016

Ah I see. Thanks!

@tv42
Copy link

tv42 commented Jul 20, 2016

I think excl_create should really be the default. It's how the linux side behaves anyway, and it only makes sense that an operation called create insists on creating something.

@bfleischer
Copy link
Member

I disagree, the OS X kernel expects create to fail if the file already exists. When auto-setting O_EXCL for every create you can use the same callback code on Linux and OS X and it should always be semantically correct, with regard to the kernel, whether the file system runs on Linux or OS X. Am I missing something?

@tv42
Copy link

tv42 commented Jul 20, 2016

Hrmm. You seem to be right, and my understanding of the linux behavior is wrong. Sorry for the noise. This is really unfortunate :(

https://sourceforge.net/p/fuse/mailman/message/34144052/

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

No branches or pull requests

4 participants