Search code examples
freebsdbsdinodezfs

How to get a path from an inode on FreeBSD (ZFS and UFS)?


Windows (NtQueryObject), Linux (/proc/self/fd/x) and OS X (F_GETPATH) all have methods for retrieving the path of a currently open file descriptor. Supposedly so does FreeBSD via something like the following code:

        size_t len;
        int mib[4]={CTL_KERN, KERN_PROC, KERN_PROC_FILEDESC, getpid()};
        BOOST_AFIO_ERRHOS(sysctl(mib, 4, NULL, &len, NULL, 0));
        std::vector<char> buffer(len*2);
        BOOST_AFIO_ERRHOS(sysctl(mib, 4, buffer.data(), &len, NULL, 0));
        for(char *p=buffer.data(); p<buffer.data()+len;)
        {
          struct kinfo_file *kif=(struct kinfo_file *) p;
          if(kif->kf_fd==fd)
          {
            lock_guard<pathlock_t> g(pathlock);
            _path=path::string_type(kif->kf_path);
            return _path;
          }
          p+=kif->kf_structsize;
        }

This irritatingly works for pretty much every kind of file descriptor except regular files which return a null path, at least in FreeBSD 10. I assume this is an oversight as from examining the kernel code it would seem trivial to return the path, though maybe there were performance reasons not to do so.

procstat uses the same API as the above, and therefore also too only returns paths for everything but regular files. Using statfs() I can at least get the path of the mount point for the file, but retrieving the path fragment between the mount point and the actual file is the problem.

So let me ask this question: Is it possible to ask UFS or ZFS to return a path fragment from an inode, maybe using some magic ioctl or sysctl, or even some utility library? The code in question doesn't need the path the file descriptor was opened with, it only needs some canonical path the file descriptor can currently be found at (this is to handle tracking third party file renames for proposed Boost.AFIO).

My thanks in advance for any help. FreeBSD is the only showstopper out of the major operating systems for this feature, and without it it makes writing filing system race safety detection code impossible :(

Edit: I found a discussion about getting ZFS to convert inodes to paths at http://comments.gmane.org/gmane.os.solaris.opensolaris.zfs/38277. Apparently there is a ZFS_IOC_OBJ_TO_PATH ioctl, but this is not a well trodden code path judging from google


Solution

  • It turns out that the fact that KERN_PROC_FILEDESC doesn't work is in fact a bug in the FreeBSD kernel. I've logged it at https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197695.

    KERN_PROC_FILEDESC does work perfectly for directory file descriptors, including tracking renames and deletions. It imperfectly works for regular file descriptors, sometimes returning a path and then quite suddenly returning a null path, and then randomly later returning a path again. I think somewhere in the 10.x release something got broken somewhere :(

    Hope that helps anyone else similarly stuck.