問題引入
判斷某個目錄字串是否是根目錄,咋一聽很簡單,只要判斷字串是否是"/"即可,但是,很多情況下使用的路徑是相對路徑,那麼如何判斷相對路徑是根目錄呢?
思路分析
熟悉Linux的同學應該知道,每個目錄下都有.和..兩個目錄,分別指代當前目錄和父目錄,考慮從這個點下手,根目錄的當前目錄和父目錄指向相同,也就是說這兩個檔案的描述符是一樣的。
大體思路有了之後,來看下Linux中常用的目錄操作的函式:
1 DIR *opendir(const char *) 2 struct dirent *readdir(DIR *) 3 int closedir(DIR *)
它們位於dirent.h標頭檔案中。
再來看一下dirent的結構
1 struct dirent { 2 ino_t d_ino; /* file number of entry */ 3 __uint16_t d_reclen; /* length of this record */ 4 __uint8_t d_type; /* file type, see below */ 5 __uint8_t d_namlen; /* length of string in d_name */ 6 char d_name[__DARWIN_MAXNAMLEN + 1]; /* name must be no longer than this */ 7 };
解決方案
開始動手編碼,如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <dirent.h> 5 6 bool isRoot(const char* path) 7 { 8 if (strcmp(path, "/") == 0) 9 return true; 10 11 char dp[256] = {0}; 12 int l = strlen(path); 13 memcpy(dp, path, l); 14 15 if (dp[l - 1] != '/') 16 { 17 dp[l] = '/'; 18 l += 1; 19 } 20 21 DIR* d = opendir(dp); 22 if (!d) 23 { 24 printf("failed to open dir\n"); 25 return false; 26 } 27 28 uint64_t dino = 0, ddino = 0; 29 while (dirent* ent = readdir(d)) 30 { 31 if (strcmp(ent->d_name, "..") == 0) 32 { 33 ddino = ent->d_ino; 34 } 35 if (strcmp(ent->d_name, ".") == 0) 36 { 37 dino = ent->d_ino; 38 } 39 40 if (dino > 0 && ddino > 0) 41 break; 42 } 43 return dino == ddino && dino != 0; 44 } 45 46 int main(int argc, char* argv[]) 47 { 48 if (argc != 2) 49 { 50 printf("usage : app path\n"); 51 return 0; 52 } 53 54 if (isRoot(argv[1])) 55 printf("this path is root\n"); 56 else 57 printf("this path is not root\n"); 58 return 0; 59 }
編譯
g++ -o root root.cpp
下面來驗證一下
# ./root / this path is root # ./root ./ this path is not root # ./root ./../ this path is not root # ./root ./../../ this path is not root # ./root ./../../../ this path is not root # ./root ./../../../.. #注意,我的機器上這裡其實已經是根目錄了 this path is not root
奇怪的問題發生了,本應該通過的內容竟然不是根目錄。進入程式碼,列印一下isRoot函式中.和..目錄的name和ino。
. 2 .. 1
難道是假設錯誤?如果想要取得inode可以通過stat函式,那麼我們該用stat函式試一下
int stat(const char *, struct stat *)
修改程式碼後如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <dirent.h> 5 #include <sys/stat.h> 6 7 bool isRoot(const char* path) 8 { 9 if (strcmp(path, "/") == 0) 10 return true; 11 12 char dp[256] = {0}; 13 int l = strlen(path); 14 memcpy(dp, path, l); 15 16 if (dp[l - 1] != '/') 17 { 18 dp[l] = '/'; 19 l += 1; 20 } 21 22 DIR* d = opendir(dp); 23 if (!d) 24 { 25 printf("failed to open dir\n"); 26 return false; 27 } 28 uint64_t dino = 0, ddino = 0; 29 while (dirent* ent = readdir(d)) 30 { 31 if (strcmp(ent->d_name, "..") == 0) 32 { 33 char pp[256] = {0}; 34 memcpy(pp, dp, l); 35 pp[l] = '.'; 36 pp[l + 1] = '.'; 37 struct stat s; 38 stat(pp, &s); 39 //printf("ddot %s %lld\n", ent->d_name, s.st_ino); 40 ddino = s.st_ino; 41 } 42 if (strcmp(ent->d_name, ".") == 0) 43 { 44 char sp[256] = {0}; 45 memcpy(sp, dp, l); 46 sp[l] = '.'; 47 struct stat s; 48 stat(sp, &s); 49 //printf("dot %s %lld\n", ent->d_name, s.st_ino); 50 dino = s.st_ino; 51 } 52 53 if (dino > 0 && ddino > 0) 54 break; 55 } 56 return dino == ddino && dino != 0; 57 } 58 59 int main(int argc, char* argv[]) 60 { 61 if (argc != 2) 62 { 63 printf("usage : app path\n"); 64 return 0; 65 } 66 67 if (isRoot(argv[1])) 68 printf("this path is root\n"); 69 else 70 printf("this path is not root\n"); 71 return 0; 72 } 73
再次編譯驗證,發現這次的結果是正確的。經過查證後發現,在使用readdir時取得的dirent中的iNode不一定是正確的,還需要從stat中取。其實在isRoot中沒有必要使用dir相關的操作,只需要組織出輸入路徑的當前路徑和父目錄,再使用stat判斷即可。
總結
到此就完成了目錄是否為根目錄的判斷,需要對Linux的API慢慢進行熟悉。