如何检查路径是否指定卷根目录
How to check if a path specifies a volume root directory
如何使用 POSIX 或标准 C 运行时调用检查给定路径(绝对或相对)是否指定卷的根目录?理想情况下,代码应该适用于 Linux 和 Mac OS X.
POSIX 有一个 realpath
函数,returns 任何给定路径的规范路径(因此 eliminating/resolving 点、点、链接等)。
现在 POSIX 不定义卷,只定义来自唯一给定根 /
的文件名层次结构。现在,Unix 变体能够将文件树一个接一个地挂载,以便在运行时获得一棵树。 "mount" 这些卷没有标准的方法,即使 mount
是一种非常常见的方法,因为有很多方法可以实现此功能。因此,您必须阅读 OS 变体的文档以确定如何获取所有安装点的列表。
您还可以阅读 Linux function to get mount points and How to get mount point information from an API on Mac?。
首先,您使用标准函数真正可以检查的是所提供的路径是否为挂载点,并且该挂载点可能是也可能不是其文件系统的根目录。
假设您的系统为每个挂载点分配了一个唯一的 f_fsid
值,您可以使用路径组件的 POSIX-stanard statvfs()
function and compare the f_fsid
field of the relevant statvfs
structure 及其父组件:
#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 = '[=10=]';
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客户端可以挂载远程文件系统中的任何子目录作为"local root",或者环回挂载可以引用另一个文件系统的子目录。
好吧,你有几种方法。从历史上看,根 inode 的 inum 为 2
,因此只需 stat(path, &buf);
并检查 if (buf.st_ino == 2)
.
最近,随着不同文件系统类型的激增,无法保证根 inode 中的 inum 等于 2
,因此您必须采用不同的方法:只需检查给定路径和父目录(只需将 "/.."
附加到路径)驻留在不同的设备中(struct stat
信息的 st_dev
字段)
以下代码说明了这一点:
#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 已经实现了这个,所以它应该在所有可用的 unice 中找到。
补充说明
这种方法不适用于实际的文件系统根目录 (/
),因为父目录和根目录都指向同一个索引节点。这可以通过检查父节点和子节点的 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 */
如何使用 POSIX 或标准 C 运行时调用检查给定路径(绝对或相对)是否指定卷的根目录?理想情况下,代码应该适用于 Linux 和 Mac OS X.
POSIX 有一个 realpath
函数,returns 任何给定路径的规范路径(因此 eliminating/resolving 点、点、链接等)。
现在 POSIX 不定义卷,只定义来自唯一给定根 /
的文件名层次结构。现在,Unix 变体能够将文件树一个接一个地挂载,以便在运行时获得一棵树。 "mount" 这些卷没有标准的方法,即使 mount
是一种非常常见的方法,因为有很多方法可以实现此功能。因此,您必须阅读 OS 变体的文档以确定如何获取所有安装点的列表。
您还可以阅读 Linux function to get mount points and How to get mount point information from an API on Mac?。
首先,您使用标准函数真正可以检查的是所提供的路径是否为挂载点,并且该挂载点可能是也可能不是其文件系统的根目录。
假设您的系统为每个挂载点分配了一个唯一的 f_fsid
值,您可以使用路径组件的 POSIX-stanard statvfs()
function and compare the f_fsid
field of the relevant statvfs
structure 及其父组件:
#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 = '[=10=]';
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客户端可以挂载远程文件系统中的任何子目录作为"local root",或者环回挂载可以引用另一个文件系统的子目录。
好吧,你有几种方法。从历史上看,根 inode 的 inum 为 2
,因此只需 stat(path, &buf);
并检查 if (buf.st_ino == 2)
.
最近,随着不同文件系统类型的激增,无法保证根 inode 中的 inum 等于 2
,因此您必须采用不同的方法:只需检查给定路径和父目录(只需将 "/.."
附加到路径)驻留在不同的设备中(struct stat
信息的 st_dev
字段)
以下代码说明了这一点:
#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 已经实现了这个,所以它应该在所有可用的 unice 中找到。
补充说明
这种方法不适用于实际的文件系统根目录 (/
),因为父目录和根目录都指向同一个索引节点。这可以通过检查父节点和子节点的 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 */