23

给定文件或目录的路径,我如何确定该文件的挂载点?例如,如果/tmp作为tmpfs文件系统挂载,那么给定文件名/tmp/foo/bar我想知道它存储在tmpfs根目录为/tmp.

这将在 C++ 中,我想避免通过system(). 代码应该是健壮的——不一定要防止故意篡改,但绝对要面对嵌套的挂载点、符号链接等。

我还没有找到一个简单的系统调用来做到这一点。看来我得自己写支票了。这是我计划的大致轮廓。

  1. readlink通过shell 命令规范化文件名。如何?
  2. /etc/mtabgetmntent()& co一起阅读。
  3. 确定文件的相应挂载条目。如何?

对于#1,是否有一个简单的系统调用,或者我是否需要读取路径的每个目录组件并readlink(2)在它们是符号链接时解析它们?和手柄...我自己?好像很痛。

对于#3,我对如何做到这一点有各种想法。不确定哪个最好。

  1. open()使用该文件、其父级、其父级的父级等,openat(fd, "..")直到我到达其中一个/etc/mtab条目。(我怎么知道我什么时候做?fstat()他们并比较 inode 号?
  2. 在挂载表中找到最长的目录名,它是我的文件名的子字符串。

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

4

4 回答 4

19

这就是我想出的。事实证明,通常不需要遍历父目录。您所要做的就是获取文件的设备号,然后找到具有相同设备号的相应挂载条目。

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 编号。

于 2010-02-25T21:26:41.800 回答
4

您可以从 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");
}

谢谢你的分心;-)

于 2010-02-25T20:13:55.947 回答
2

这在不提供 mntent 功能的 OSX 上对我有用。在 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
}
于 2014-05-29T23:37:20.160 回答
0

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

显然,符号链接可能会给整个过程带来麻烦。任何包含符号链接的文件路径都必须在处理之前转换为其“实际”路径。 希望有一种方法可以做到这一点,而无需单独检查文件及其每个父级,但如果必须,您始终可以暴力破解。 您可能想要使用realpath删除符号链接。

于 2010-02-25T20:20:14.940 回答