Linux程序开发中如何判断目录是否为根目录?

问题引入

判断某个目录字符串是否是根目录,咋一听很简单,只要判断字符串是否是"/"即可,但是,很多情况下使用的路径是相对路径,那么如何判断相对路径是根目录呢?

思路分析

熟悉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判断即可。

完整的isRoot代码如下:

 1 bool isRoot(const char* path)
 2 {
 3     if (strcmp(path, "/") == 0)
 4         return true;
 5 
 6     char pp[256] = {0};
 7 
 8     int l = strlen(pp);
 9     strcpy(pp, path);
10 
11     if (pp[l - 1] != '/')
12     {
13         pp[l] = '/';
14         l += 1;
15     }
16 
17     strcat(pp, "..");
18 
19     struct stat ps;
20     stat(pp, &ps);
21 
22     struct stat cs;
23     stat(path, &cs);
24 
25     return ps.st_ino   ==  cs.st_ino &&
26            ps.st_nlink ==  cs.st_nlink &&
27            ps.st_mtime ==  cs.st_mtime;
28 }

总结

到此就完成了目录是否为根目录的判断,需要对Linux的API慢慢进行熟悉。

posted @ 2020-11-18 22:34  soso101  阅读(1099)  评论(0编辑  收藏  举报