Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between fileReferenceURL and NSURLFileResourceIdentifierKey

It appears that [NSURL fileReferenceURL] returns the same information as calling [NSURL getResourceValue:forKey:error:] for the key NSURLFileResourceIdentifierKey.

Are they indeed functionally the same? (Spoiler: No, they aren't!)

like image 529
Thomas Tempelmann Avatar asked Oct 24 '25 12:10

Thomas Tempelmann


1 Answers

There is a fine but important difference between the two - they behave differently if the item is a hard linked file.

If, especially on a APFS formatted volume, you have multiple hard links for the same file content, then

  • NSURLFileResourceIdentifierKey will return the same value (classic "inode") for all these hard links, whereas
  • fileReferenceURL returns unique "link IDs" that keep the reference for each hard link entry's path.

In depth

APFS manages two "inode" values for hard links:

  1. The classic inode is the one that points to the file content. So, if multiple hard links point to the same content, you get that content's inode number. That's what the stat() function and the stat command line tool will show as well, BTW.

  2. The linkID is another inode, but this time it points to the directory entry (which, for non-hardlinked files is identical to the content inode). This value can be retrieved with the getattrlist() function, using the ATTR_CMNEXT_LINKID key. Additionally, since macOS 10.13, the searchfs() function can also return this value along with the classic inode number.

Resolving an inode or a link ID

Invoke fsgetpath by declaring the following:

#include <sys/syscall.h>
#define fsgetpath(buf, bufsize, fsid, objid)  \
    (ssize_t)syscall(SYS_fsgetpath, buf, (size_t)bufsize, fsid, (uint64_t)objid)

Call it like this:

uint64_t inode = 2;         // set this to the linkID or inode
const char *volPath = "/";  // must be set to the file's volume!

struct statfs fsinfo;
int err = statfs (volPath, &fsinfo);
if (err == 0) {
    char path[MAXPATHLEN+1];
    ssize_t len = fsgetpath (path, sizeof(path)-1, &fsinfo.f_fsid, inode);
    if (len > 0) {
        ... process the path
    }
}

To determine the correct volume, ideally at the time of storing the inode value, you can call stat() and pass the file's full path and then store the returned st_dev value or call statfs() as shown above.

like image 85
3 revsThomas Tempelmann Avatar answered Oct 26 '25 19:10

3 revsThomas Tempelmann



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!