如何检查路径是否指定卷根目录

时间:2017-08-28 14:14:33

标签: c linux macos posix stdio

如何检查给定路径(绝对路径或相对路径)是否指定具有POSIX或标准C运行时调用的卷的根目录?理想情况下,代码应该适用于Linux和Mac OS X.

3 个答案:

答案 0 :(得分:1)

首先,使用标准函数真正检查的是,如果提供的路径是挂载点,并且挂载点可能是也可能不是其文件系统的根。

假设您的系统为每个挂载点分配了唯一的f_fsid值,您可以使用POSIX-stanard statvfs() function并比较路径组件的相关statvfs structuref_fsid字段与其父母:

#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>


int isMountPoint( const char *path )
{
    struct statvfs sv1, sv2;

    char *realP = realpath( path, NULL );

    // if the real path is "/", yeah, it's the root
    if ( !strcmp( realP, "/" ) )
    {
        free( realP );
        return( 1 );
    }

    statvfs( realP, &sv1 );

    // find the parent by truncating at the last "/"
    char *pp = strrchr( realP, '/' );

    // if there is no parent, must be root
    if ( NULL == pp )
    {
        free( realP );
        return( 1 );
    }

    // keep the final / (makes handling things like "/mnt"
    // much easier)
    pp++;
    *pp = '\0';

    statvfs( realP, &sv2 );
    free( realP );

    // if f_fsid differs, it's the root of
    // the mounted filesystem
    return( sv1.f_fsid != sv2.f_fsid );
}

所有错误检查都留作练习(它也会使这个例子更长,更难理解......)。

如果路径的f_fsid与其父项f_fsid不同,或者路径是根目录本身,则传入的路径必须是其文件系统的挂载点。请注意,这不必是文件系统的实际根。例如,主机可以通过NFS导出文件系统,NFS客户端可以将远程文件系统中的任何子目录作为“本地根”挂载,或者环回挂载可以引用另一个文件系统的子目录。

答案 1 :(得分:1)

嗯,你有几种方法。从历史上看,根inode的内容为2,因此只需stat(path, &buf);并检查if (buf.st_ino == 2)

最近,随着不同文件系统类型的激增,根本不存在inum等于2的保证,所以你必须遵循不同的方法:只检查给定的路径和父目录(只需将"/.."追加到路径中)驻留在不同的设备中(st_dev信息的struct stat字段)

以下代码说明了这一点:

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

int is_mount(char *path)
{
    struct stat sdir; /* inode info */
    struct stat spdir; /* parent inode info */
    char buffer[PATH_MAX];
    int res = stat(path, &sdir);
    if (res < 0) return -1;
    if (snprintf(buffer, sizeof buffer, "%s/..", path) >= sizeof buffer) {
            errno = ENAMETOOLONG;
            return -1;
    }
    res = stat(buffer, &spdir);
    if (res < 0) return -1;
    return sdir.st_dev != spdir.st_dev;  /* SEE ADDITIONAL NOTE */
}

int main(int argc, char **argv)
{
    int i;
    for (i = 1; i < argc; i++) {
            int res = is_mount(argv[i]);
            if ( res < 0 ) {
                    fprintf(stderr,
                            "%s: %s (errno = %d)\n", 
                            argv[i], strerror(errno), errno);
                    continue;
            }
            printf("%5s\t%s\n", res ? "true" : "false", argv[i]);
    } /* for */
} /* main */

在FreeBSD系统上执行:

$ ismount /home/luis/usr.ports/german/geonext/Makefile /home/luis/usr.ports/german/geonext /home/luis/usr.ports/german /home/luis/usr.ports /home/luis  /home / /var/run/cups /var/run /var pru3.c
/home/luis/usr.ports/german/geonext/Makefile: Not a directory (errno = 20)
pru3.c: Not a directory (errno = 20)
false       /home/luis/usr.ports/german/geonext
false       /home/luis/usr.ports/german
 true       /home/luis/usr.ports
false       /home/luis
false       /home
 true       /             <-- in the above code, this returns false, see ADDITIONAL NOTE.
false       /var/run/cups
 true       /var/run
false       /var

可移植性说明

这应该适用于实现stat(2)系统调用的任何un * x / linux。 UNIX v7已经实现了这一点,所以它应该可以在所有可用的unices中找到。

附加说明

此方法不适用于实际的文件系统根目录(/),因为父目录和根目录都指向同一个inode。这可以通过检查父和子inode的st_ino是否相等来解决。只需更改

的测试即可
return    (sdir.st_dev != spdir.st_dev)  /* different devices */
       || ( /* sdir.st_dev == spdir.st_dev && ---redundant */ 
               sdir.st_ino == spdir.st_ino); /* root dir case */

答案 2 :(得分:-1)

POSIX有一个realpath函数,它返回任何给定路径的规范路径(从而消除/解析点,点,链接等)。

现在POSIX没有定义卷,只定义来自唯一给定根/的文件名的层次结构。现在,Unix变体能够将文件树一个安装在另一个之上,以便在运行时获得单个树。即使mount是一种非常常见的方式,也没有标准方法可以“装载”这些卷,因为有很多方法可以实现此功能。因此,您必须阅读操作系统变体的文档,以确定如何获取所有已安装点的列表。

您还可以阅读Linux function to get mount pointsHow to get mount point information from an API on Mac?