在Linux上使用C语言找到任意文件的设备或挂载点

3

这个问题与Linux有关,解决方案不需要具有可移植性。

我正在寻找一个库函数/系统调用或一些组合,可以为任意文件提供设备(例如:/dev/sdb1)或挂载点(例如:/home)。看起来statfs结构体的f_fsid字段可以实现此功能,但在Linux上未被使用。

我可以轻松地使用shell查找这些信息:

df "$filename" | awk 'NR==1 {next} {print $6; exit}'

但似乎exec系列函数都不会返回所运行命令的输出结果,而且我更倾向于只使用纯C来解决问题。


2
你需要使用 popen,而不是 exec - Karoly Horvath
1
http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/df.c;h=ccb1f5eb62b70198cc525e89f1f111e34edadd24;hb=HEAD - FatalError
@KarolyHorvath:确实我需要,谢谢! - Darren Kirby
@FatalError:哈哈,谢谢。已经看过了,希望不必这样做。我想现在先采用快速而简单的 popen() 解决方案,然后再改成纯 C。 - Darren Kirby
1
df,像其他工具一样,是开源的(coreutils?fileutils?),因此您可以查看它在C中是如何完成的。 - Karoly Horvath
1
你可能需要考虑使用realpath()或类似的函数,以防路径名中包含符号链接等情况。 - Jonathan Leffler
3个回答

1

看起来 stat(2)(或lstat)是您要查找的函数。它将为您获取st_dev中的设备号。

我认为df(1)只是读取/etc/mtab。


嗨remram。df绝对使用stat来查找挂载点。我选择了一个快速而简单的popen()解决方案,因为我希望代码尽快运行,但今天我已经用纯C重新编写了它。 - Darren Kirby
找到挂载点很容易,获取设备编号也很容易。但是获取/dev/下的设备文件名(这就是df打印的内容)需要读取mtab。 - remram

0

我使用了stat()编写了一个纯C解决方案。 对于任何在搜索中找到它的人,这是函数:

int getmntpt(char *path, char *mount_point) {
    struct stat cur_stat;
    struct stat last_stat;

    char dir_name[PATH_MAX];
    char *dirname_p = dir_name;
    char cur_cwd[255];
    char *cur_cwd_p = cur_cwd;
    char saved_cwd[PATH_MAX];
    if (getcwd(saved_cwd, PATH_MAX) == NULL) {
        errno = EIO;
        return ERROR;
    }

    if (lstat(path, &cur_stat) < 0) {
        errno = EIO;
        return ERROR;
    }

    if (S_ISDIR (cur_stat.st_mode)) {
        last_stat = cur_stat;
        if (chdir("..") < 0)
            return ERROR;
        if (getcwd(cur_cwd_p, 255) == NULL) {
            errno = EIO;
            return ERROR;
        }
    } else { /* path is a file */
        size_t path_len, suffix_len, dir_len;
        path_len = strlen(path);
        suffix_len = strlen(strrchr(path, 47)); /* 47 = '/' */
        dir_len = path_len - suffix_len;
        dirname_p = strncpy(dirname_p, path, dir_len);
        if (chdir(dirname_p) < 0) 
            return ERROR;
        if (lstat(".", &last_stat) < 0)
            return ERROR;
    }

    for (;;) {
        if (lstat("..", &cur_stat) < 0)
            return ERROR;
        if (cur_stat.st_dev != last_stat.st_dev || cur_stat.st_ino == last_stat.st_ino)
            break; /* this is the mount point */
        if (chdir("..") < 0)
            return ERROR;
        last_stat = cur_stat;
    }
    if (getcwd(mount_point, PATH_MAX) == NULL)
        return ERROR;
    if (chdir(saved_cwd) < 0)
        return ERROR;
    return SUCCESS;
}

如果将挂载点本身作为参数传递,这将无法正常工作。 - Reinstate Monica

0
这是我基于@DarrenKirby的答案编写的C语言解决方案,但它避免使用chdir,而且代码更短。[此外,它还处理了将挂载点本身作为参数传递的边缘情况]。
int getmntpt(char const  *path, char *mount_point) {

    char *test_path = malloc(PATH_MAX), *test_end;
    struct stat cur_stat, prev_stat;

    if (lstat(path, &prev_stat) < 0)
        return ERROR;

    test_end = stpcpy(test_path, path);
    if (!S_ISDIR(prev_stat.st_mode)) {
        test_end = strrchr(test_path, '/');
        if (test_end == NULL)
            test_end = stpcpy(test_path, ".");
        else
            *test_end = '\0';
    }

    for (;;) {
        test_end = stpcpy(test_end, "/..");     
        if (lstat(test_path, &cur_stat) < 0)
            return ERROR;
        if (cur_stat.st_dev != prev_stat.st_dev || cur_stat.st_ino == prev_stat.st_ino) /* root */
            break; /* this is the mount point */
        prev_stat = cur_stat;
    }

    *(test_end - 3) = '\0';
    if (realpath(test_path, mount_point) == NULL) {
        free(test_path);
        return -1;
    }

    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接