After creating a hardlink on a distributed filesystem of my own via:
% ln f.txt hlf.txt
Neither the original file, f.txt, nor the hardlink, hlf.txt, are immediately accessible, e.g. via cat(1) with ENOENT returned. A short time later though, both the original file and the hardlink are accessible. Both files can be stat(1)ed though, which confirms that vnop_getattr returns success for both files.
Dtruss(1) indicates it's the open(2) syscall that fails:
% sudo dtruss -f cat hlf.txt
2038/0x4f68: open("hlf.txt\0", 0x0, 0x0) = -1 Err#2 ;ENOENT
2038/0x4f68: write_nocancel(0x2, "cat: \0", 0x5) = 5 0
2038/0x4f68: write_nocancel(0x2, "hlf.txt\0", 0x7) = 7 0
2038/0x4f68: write_nocancel(0x2, ": \0", 0x2) = 2 0
2038/0x4f68: write_nocancel(0x2, "No such file or directory\n\0", 0x1A) = 26 0
Dtrace(1)ing my KEXT no longer works on macOS Sequoia, so based on the diagnostics print statements I inserted into my KEXT, the following sequence of calls is observed:
vnop_lookup(hlf.txt) -> EJUSTRETURN ;ln(1)
vnop_link(hlf.txt) -> KERN_SUCCESS ;ln(1)
vnop_lookup(hlf.txt) -> KERN_SUCCESS ;cat(1)
vnop_open(/) ; I expected to see vnop_open(hlf.txt) here instead of the parent directory.
Internally, hardlinks are created in vnop_link via a call to vnode_setmultipath with cache_purge_negatives called on the destination directory.
On macOS Monterey for example, where the same code does result in hardlinks being accessible, the following calls are made:
vnop_lookup(hlf.txt) -> EJUSTRETURN ;ln(1)
vnop_link(hlf.txt) -> KERN_SUCCESS ;ln(1)
vnop_lookup(hlf.txt) -> KERN_SUCCESS ;cat(1)
vnop_open(hlf.txt) -> KERN_SUCCESS ;cat(1)
Not sure how else to debug this.
Perusing the kernel sources for uses of VISHARDLINK, VNOP_LINK and vnode_setmultipath call sites did not clear things up for me.
Any pointers would be greatly appreciated.