问题引入
判断某个目录字符串是否是根目录,咋一听很简单,只要判断字符串是否是"/"即可,但是,很多情况下使用的路径是相对路径,那么如何判断相对路径是根目录呢?
思路分析
熟悉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中取。
总结
到此就完成了目录是否为根目录的判断,需要对Linux的API慢慢进行熟悉。