简体   繁体   English

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

[英]How to check if a path specifies a volume root directory

How can I check whether a given path (absolute or relative) specifies the root directory of a volume with POSIX or standard C runtime calls? 如何检查给定路径(绝对路径或相对路径)是否指定了带有POSIX或标准C运行时调用的卷的根目录? Ideally, the code should work on both, Linux and Mac OS X. 理想情况下,该代码应在Linux和Mac OS X上都可以工作。

First, all you can really check using standard functions is if the path presented is a mount point, and that mount point may or may not be the root of its filesystem. 首先,使用标准功能可以真正检查的只是显示的路径是否是挂载点,而该挂载点可能是也可能不是其文件系统的根。

Assuming your system assigns a unique f_fsid value to each mount point, you can use the POSIX-stanard statvfs() function and compare the f_fsid field of the relevant statvfs structure of the path component with its parent: 假设系统为每个安装点分配了唯一的f_fsid值,则可以使用POSIX- statvfs()函数 ,并将路径组件的相关statvfs结构f_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 );
}

All error checking is left as an exercise (and it would also make this example longer and harder to understand...). 所有错误检查都作为练习来进行(这也会使该示例更长且更难以理解...)。

If the f_fsid of a path differs from its parent's f_fsid , or if the path is the root directory itself, the path passed in must be the mount point of its filesystem. 如果f_fsid的路径,从其父不同f_fsid ,或者如果路径是根目录本身的路径传递的必须是其文件系统的挂载点。 Note that this does not have to be the actual root of the filesystem. 请注意,这不必是文件系统的实际根目录。 For example, a host can export a file system via NFS, and an NFS client can mount any subdirectory in the remote file system as the "local root", or a loopback mount can refer to a subdirectory of another filesystem. 例如,主机可以通过NFS导出文件系统,而NFS客户端可以将远程文件系统中的任何子目录挂载为“本地根”,或者回送挂载可以引用另一个文件系统的子目录。

Well, you have several ways. 好吧,您有几种方法。 Historically, the root inode has had inum of 2 , so just stat(path, &buf); 从历史上看,根索引节点的inum值为2 ,所以只有stat(path, &buf); and check if (buf.st_ino == 2) . 并检查if (buf.st_ino == 2)

Recently, with proliferation of different filesystem types, there's no warranty of having inum equal to 2 in the root inode, so you must follow a different approach: Just check if the given path and the parent directory (just append "/.." to path) reside in different devices ( st_dev field of struct stat info) 最近,随着不同文件系统类型的激增,不能保证根inode中的inum等于2 ,因此您必须遵循不同的方法:只需检查给定的路径和父目录(只需将"/.."附加到路径)驻留在不同的设备中( struct stat信息的st_dev字段)

The following code illustrates this: 以下代码说明了这一点:

#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 */

Execution on a FreeBSD system: 在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

PORTABILITY NOTE 可移植性注意

This should work in any un*x/linux that implements the stat(2) system call. 这应该在实现stat(2)系统调用的任何un * x / linux中都有效。 UNIX v7 already implements this, so it's supposed to be found in all unices available. UNIX v7已经实现了此功能,因此应该在所有可用的unice中都可以找到它。

ADDITIONAL NOTE 补充说明

This approach does not work for the actual filesystem root ( / ) because the parent dir and the root dir both point to the same inode. 这种方法不适用于实际的文件系统根目录( / ),因为父目录和根目录都指向同一个inode。 This can be solved by checking the st_ino of both parent and child inodes to be equal. 这可以通过检查父级和子级inode的st_ino是否相等来解决。 Just change the test for 只需更改测试

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 */

POSIX has a realpath function that returns the canonical path of any given path (thus eliminating/resolving dots, dotdots, links, etc). POSIX具有realpath函数,该函数返回任何给定路径的规范路径(从而消除/解析点,点,链接等)。

Now POSIX doesn't define volume, only the hierarchy of filenames from a unique given root / . 现在,POSIX不再定义卷,而仅定义唯一的给定根目录/的文件名层次。 Now Unix variants are able to mount file trees one on top of other to obtain a single tree at runtime. 现在,Unix变体能够将文件树一个接一个地挂载,以在运行时获得单个树。 There is no standard way to "mount" these volumes, even if mount is a very common way, because there is plenty of ways to realize this feature. 即使mount是非常常见的方式,也没有标准的方法来“挂载”这些卷,因为有很多方法可以实现此功能。 So you have to read the documentation of your OS variant to determine how you can obtain the list of all mounted points. 因此,您必须阅读OS变体的文档,以确定如何获取所有安装点的列表。

You may also read Linux function to get mount points and How to get mount point information from an API on Mac? 您还可以阅读Linux函数来获取安装点,以及如何从Mac上的API获取安装点信息? .

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM