[英]How to check if a path specifies a volume root directory
如何檢查給定路徑(絕對路徑或相對路徑)是否指定了帶有POSIX或標准C運行時調用的卷的根目錄? 理想情況下,該代碼應在Linux和Mac OS X上都可以工作。
首先,使用標准功能可以真正檢查的只是顯示的路徑是否是掛載點,而該掛載點可能是也可能不是其文件系統的根。
假設系統為每個安裝點分配了唯一的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 );
}
所有錯誤檢查都作為練習來進行(這也會使該示例更長且更難以理解...)。
如果f_fsid
的路徑,從其父不同f_fsid
,或者如果路徑是根目錄本身的路徑傳遞的必須是其文件系統的掛載點。 請注意,這不必是文件系統的實際根目錄。 例如,主機可以通過NFS導出文件系統,而NFS客戶端可以將遠程文件系統中的任何子目錄掛載為“本地根”,或者回送掛載可以引用另一個文件系統的子目錄。
好吧,您有幾種方法。 從歷史上看,根索引節點的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中都可以找到它。
這種方法不適用於實際的文件系統根目錄( /
),因為父目錄和根目錄都指向同一個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 */
POSIX具有realpath
函數,該函數返回任何給定路徑的規范路徑(從而消除/解析點,點,鏈接等)。
現在,POSIX不再定義卷,而僅定義唯一的給定根目錄/
的文件名層次。 現在,Unix變體能夠將文件樹一個接一個地掛載,以在運行時獲得單個樹。 即使mount
是非常常見的方式,也沒有標准的方法來“掛載”這些卷,因為有很多方法可以實現此功能。 因此,您必須閱讀OS變體的文檔,以確定如何獲取所有安裝點的列表。
您還可以閱讀Linux函數來獲取安裝點,以及如何從Mac上的API獲取安裝點信息? 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.