Having a peculiar issue trying to support the use of O_EXCL. (Fail if O_CREAT and file exists). It will fail the first time, then if the call is repeated, it works as expected.
It is not entirely clear how macOS should handle O_EXCL, it has been mentioned that vnop_create() should always return EEXIST - does that mean even in the success case, it should return EEXIST instead of 0? That seems odd.
Output of test program is:
# (1) Create the file with (O_WRONLY|O_CREAT).
open okay
write okay
close okay
86 -rw-r----- 1 501 0 29 Jan 12 17:08 /Volumes/BOOM/teest.out
Deleting /Volumes/BOOM/teest.out
# (2) Try creating with (O_WRONLY|O_CREAT|O_EXCL).
writef: Stale NFS file handle
436207628 87 ---------- 1 501 wheel 0 0 "Jul 9 07:53:53 2037" "Jan 12 17:09:02 2022" "Jan 12 17:09:02 2022" "Jan 1 09:00:00 1970" 1048576 0 0 /Volumes/BOOM/teest.out
So, since the file is deleted in between the tests, O_EXCL shouldn't really kick in here, and yet, something goes wrong.
The nfs server sends ESTALE to the nfs client. The dtrace stack is:
Stack:
kernel.development`nfsrv_setattr+0x7c6
kernel.development`nfssvc_nfsd+0xbdc
kernel.development`nfssvc+0x106
kernel.development`unix_syscall64+0x2ba
kernel.development`hndl_unix_scall64+0x16
Result:
0 259014 nfsrv_setattr: entry
0 259014 mac_vnode_check_open:entry
0 259015 hook_vnode_check_open:return 2 nfsd
0 259015 mac_vnode_check_open:return 2 nfsd
0 229396 nfsrv_rephead:entry
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
0: 46 00 00 00 F...
So, nfssrv_setattr() replies with 0x46/70 (ESTALE) seemingly because the call hook_vnode_check_open()
returns 2 (ENOENT).
Why though, the file was removed, I verified the cache has no entry. Then created again, confirmed it IS in the cache.
<zfs`zfs_vnop_remove (zfs_vnops_osx.c:1700)> zfs_vnop_remove error 0: checking cache: NOTFOUND
<zfs`zfs_vnop_create (zfs_vnops_osx.c:1427)> *** zfs_vnop_create: with 1: EXCL
<zfs`zfs_create (zfs_vnops_os.c:660)> zfs_create: zp is here 0x0
<zfs`zfs_vnop_create (zfs_vnops_osx.c:1458)> ** zfs_vnop_create created id 82
<zfs`zfs_vnop_create (zfs_vnops_osx.c:1475)> zfs_vnop_create error -1: checking cache: FOUND
I am having issues finding where the code for hook_vnode_check_open
comes from anyway?
The failure call in nfs server is:
if (!error && mac_vnode_check_open(ctx, vp, FREAD | FWRITE)) {
error = ESTALE;
}
So uh, why? If I let the test run again, this time the file exists, it returns EEXIST as expected.
If I run the first test twice, ie, without O_EXCL, both work. So it seems to only go wrong with O_EXCL, and file doesn't exist.
It is curious as to why nfs server figures out that exclusive is set, then clears va_mode?
case NFS_CREATE_EXCLUSIVE:
exclusive_flag = 1;
if (vp == NULL) {
VATTR_SET(vap, va_mode, 0);
But doesn't use exclusive_flag until after calling VNOP_CREATE()
, and it doesn't pass it either.