要確定文件F是否位於目錄D中,首先需要確定其設備號和inode編號(struct stat的st_dev和st_ino成員)的屬性D.
然後stat F來確定它是否是一個目錄。如果沒有,請調用basename來確定包含它的目錄的名稱。將G設置爲該目錄的名稱。如果F已經是目錄,則設置G = F。
現在,當且僅當G在D之內時,F在D之內。接下來我們有一個循環。
while (1) {
if (samefile(d_statinfo.d_dev, d_statinfo.d_ino, G)) {
return 1; // F was within D
} else if (0 == strcmp("/", G) {
return 0; // F was not within D.
}
G = dirname(G);
}
的samefile功能很簡單:
int samefile(dev_t ddev, ino_t dino, const char *path) {
struct stat st;
if (0 == stat(path, &st)) {
return ddev == st.st_dev && dino == st.st_no;
} else {
throw ...; // or return error value (but also change the caller to detect it)
}
}
這將在POSIX文件系統的工作。但是許多文件系統不是POSIX。需要注意的問題包括:
- 設備/ inode不唯一的文件系統。一些FUSE文件系統就是這樣的例子;當底層文件系統沒有它們時,它們有時會構成inode數字。他們不應該重新使用inode號碼,但是一些FUSE文件系統有錯誤。
- 破壞的NFS實現。在某些系統上,所有的NFS文件系統都有相同的設備編號。如果它們通過服務器上存在的inode號碼,這可能會導致問題(儘管我從來沒有在實踐中看到它發生過)。
- Linux綁定掛載點。如果
/a
是/b
的綁定掛載,則/a/1
正確地看起來在/a
內,但是對於上面的實現,/b/1
也似乎在/a
內。我認爲這可能是正確的答案。但是,如果這不是您喜歡的結果,則可以通過更改return 1
大小寫以調用strcmp()
來比較路徑名,從而輕鬆修復此問題。然而,爲了這個工作,您需要先在F和D上撥打realpath
開始。realpath
調用可能非常昂貴(因爲它可能需要多次點擊磁盤)。
- 特殊路徑
//foo/bar
。 POSIX允許以//
開頭的路徑名稱是特殊的,這種方式有點不明確。實際上,我忘記了POSIX提供的關於語義的確切級別的保證。我認爲POSIX允許//foo/bar
和//baz/ugh
引用同一個文件。設備/ inode檢查應該仍然適合您,但您可能會發現它不會(也就是說,您可能會發現//foo/bar
和//baz/ugh
可以引用相同的文件,但具有不同的設備/ inode編號)。
這個回答假設我們開始與F和D.如果不能保證這一點,你可能需要做使用realpath()
和getcwd()
一些轉換的絕對路徑。如果當前目錄的名稱長於PATH_MAX
(這肯定會發生),這將會成爲問題。
我認爲這是'chroot(2)'發明的原因! –
@Carl Norum:如果你給某人有限的shell權限,chroot會更好。如果你不想限制你創建的程序的訪問權限,那麼比chroot有更好的選擇。 – Dani