在哪里安装了一个文件?

给定一个文件或目录的path,我如何确定该文件的安装点? 例如,如果将/tmp挂载为tmpfs文件系统,那么给定文件名/tmp/foo/bar我想知道它存储在以/tmp根的tmpfs上。

这将在C + +中,我想避免通过system()调用外部命令。 代码应该是健壮的 – 不一定是有意的篡改,但肯定是面对嵌套的挂载点,符号链接等。

我一直无法find一个简单的系统调用来做到这一点。 看起来我必须自己写检查。 这是我正在计划的一个粗略的轮廓。

  1. 使用readlink shell命令规范文件名。 怎么样?
  2. getmntent() &co。读取/etc/mtab
  3. 确定文件的相应装入条目。 怎么样?

对于#1是否有一个简单的系统调用,或者我需要读取path的每个目录组件,并解决它们与readlink(2)如果他们是符号链接? 并处理...我自己? 看起来像一个痛苦。

对于#3我有如何做到这一点的各种想法。 不知道哪个是最好的。

  1. 使用openat(fd, "..") open()文件,其父文件,父文件的父文件等openat(fd, "..")直到到达/etc/mtab条目之一。 ( 我怎么知道我什么时候? fstat()他们和比较inode号码?
  2. 在装载表中查找最长的目录名,这是我的文件名的子string。

我倾向于第一个选项,但在我编写代码之前,我想确保我没有忽略任何东西 – 理想情况下,这是一个内置的函数,已经这样做了!

这是我想出来的。 事实证明,通常不需要遍历父目录。 您只需获取文件的设备编号,然后使用相同的设备编号找到相应的安装条目。

 struct mntent *mountpoint(char *filename, struct mntent *mnt, char *buf, size_t buflen) { struct stat s; FILE * fp; dev_t dev; if (stat(filename, &s) != 0) { return NULL; } dev = s.st_dev; if ((fp = setmntent("/proc/mounts", "r")) == NULL) { return NULL; } while (getmntent_r(fp, mnt, buf, buflen)) { if (stat(mnt->mnt_dir, &s) != 0) { continue; } if (s.st_dev == dev) { endmntent(fp); return mnt; } } endmntent(fp); // Should never reach here. errno = EINVAL; return NULL; } 

感谢@RichardPennington在realpath()上的头像,以及比较设备号码而不是inode号码。

你可以从realpath开始,并用stat检查每个目录,看看它是否在同一个设备上。 这似乎应该有一个更简单的方法。


编辑:

 #include <stdio.h> #include <limits.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(int argc, char **argv) { char *p; char path[PATH_MAX]; struct stat buf; dev_t dev; if (realpath(argv[1], path) == NULL) { fprintf(stderr, "can't find %s\n", argv[1]); exit(1); } if (stat(path, &buf) != 0) { fprintf(stderr, "can't statind %s\n", path); exit(1); } dev = buf.st_dev; while((p = strrchr(path, '/'))) { *p = '\0'; stat(path, &buf); if (buf.st_dev != dev) { printf("mount point = %s\n", path); exit(0); } } printf("mount point = /\n"); } 

感谢分心;-)

这在OSX上适用于我,它不提供mntent功能。 在C ++中:

 struct stat fileStat; int result = stat(path, &fileStat); if (result != 0) { // handle error } struct statfs* mounts; int numMounts = getmntinfo(&mounts, MNT_WAIT); if (numMounts == 0) { // handle error } for (int i = 0; i < numMounts; i++) { if (fileStat.st_dev == mounts[i].f_fsid.val[0]) // mounts[i].f_mntonname is the mount path } 

你应该能够读入/etc/mtab ,解析出已经挂载的所有位置,看看你的任何文件是否位于任何这些位置(或它们的子目录)中。 你不应该为此需要任何特殊的系统函数,如果你有挂载点和文件路径作为字符串,那么你可以使用普通的字符串处理函数来处理它们。

显然,符号链接可能会使整个过程陷入困境。 包含符号链接的任何文件路径在处理之前都必须转换为“实际”路径。 希望有一种方法可以做到这一点,而不是单独检查一个文件和每个父母,但是如果你必须的话,你总是可以蛮横的。 你可能会想使用realpath来删除符号链接。