Linux程式開發中如何判斷目錄是否為根目錄?

soso101發表於2020-11-18

問題引入

判斷某個目錄字串是否是根目錄,咋一聽很簡單,只要判斷字串是否是"/"即可,但是,很多情況下使用的路徑是相對路徑,那麼如何判斷相對路徑是根目錄呢?

思路分析

熟悉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慢慢進行熟悉。

相關文章