smoku smoku - 2 months ago 14
C Question

Linux stat(2) call gives non-existing device ID

My test program is calling stat(2) to obtain a device the file resides on.

stat.c
(built with
cc stat.c -o stat
)

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/sysmacros.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main()
{
char *path = "/home/smoku/test.txt";
unsigned int maj, min;
struct stat sb;
if (stat(path, &sb) < 0) {
fprintf(stderr, "Error getting stat for '%s': %d %s\n", path, errno, strerror(errno));
return 1;
}
maj = major(sb.st_dev);
min = minor(sb.st_dev);
fprintf(stderr, "Found '%s' => %u:%u\n", path, maj, min);
return 0;
}


Got
0:44


$ ls -l /home/smoku/test.txt
-rw-r--r-- 1 smoku smoku 306 08-30 09:33 /home/smoku/test.txt

$ ./stat
Found '/home/smoku/test.txt' => 0:44

$ /usr/bin/stat -c "%d" /home/smoku/test.txt
44


But... there is no such device in my system and
/home
is
0:35


$ grep /home /proc/self/mountinfo
75 59 0:35 /home /home rw,relatime shared:30 - btrfs /dev/bcache0 rw,ssd,space_cache,subvolid=258,subvol=/home


Why do I get a device ID that does not exist in my system?

Answer

stat(2) in fs/stat.c uses inode->i_sb->s_dev to fill stat.st_dev

/proc/self/mountinfo in fs/proc_namespace.c uses mnt->mnt_sb->s_dev

Apparently struct inode.i_sb superblock may be different to struct vfsmount.mnt_sb superblock in case of mount of btrfs subvolume.

This is an issue inherent to btrfs implementation, which "requires non-trivial changes in the VFS layer" to fix: https://mail-archive.com/linux-btrfs@vger.kernel.org/msg57667.html