【问题标题】:Where is a file mounted?文件挂载在哪里?
【发布时间】:2011-01-21 04:23:37
【问题描述】:

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

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

我无法找到一个简单的系统调用来执行此操作。看来我得自己写支票了。这是我计划的大致轮廓。

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

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

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

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

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

【问题讨论】:

  • 为什么要投票关闭这个?这是个好问题。
  • 投票关闭它的人可能只是阅读了标题和/或前两行。这显然是一个适当(且有趣)的问题。

标签: c++ linux mount


【解决方案1】:

这在不提供 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
}

【讨论】:

  • 你的 OSX(C++) 是不是任何类型的 linux? getmntinfo 听起来像 FreeBSD/OSX(C++) 库调用,但不是 linux 或 POSIX。
  • 问题是关于 linux 的。
  • 谢谢你的回答,彼得。
  • 是的,我确实指出它是 OSX,它通常很容易移植到 Linux。
【解决方案2】:

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

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

【讨论】:

  • 不错。比我的要好得多,因为您可以获得所需的所有信息。
  • 您可能想要使用 /proc/mounts 而不是 /etc/mtab - /etc/mtab 由 mount 命令维护并且可能会被篡改,而 /proc/mounts 是挂载表本身。 BSD 用户应该使用 getmntinfo()
  • 这甚至应该适用于设备特殊文件,而不需要最后的特殊情况(stat.st_dev 总是包含该文件的设备的 ID,即使对于设备特殊文件)。
  • 由于设备可以多次安装,是否需要进行一些额外的检查来检查您是否正确安装了设备?
【解决方案3】:

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

感谢您的分心;-)

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 2022-01-10
      • 1970-01-01
      • 2019-02-11
      • 1970-01-01
      • 2011-08-16
      • 1970-01-01
      • 2018-06-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多