目录可以被任何有这个目录读权限的人读。然而只有内核才能写一个目录,来保证文件系统的健全。回想4.5节目录的写权限位和执行权限位决定我们是否可以在该目录创建和删除文件--他们并没有指定我们是否可以写这个目录本身。
目录的真实格式取决于UNIS系统实现和文件系统的设计。早期系统,比如Version
7,有一个简单的结构:每个目录项有16字节,其中14字节为文件名,而2个字节为i-node号。当更长的文件名被加入到4.2BSD时,每个项变为可
变长度,这意味着任何读目录的程序都依赖于系统。为了简化这个,一堆目录指令被发展起来,并成为POSIX.1的部分。许多系统避免程序使用read函数
来访问目录的内容,因此更加隔离了程序和目录格式的实现相关的细节。
- #include <dirent.h>
- DIR *opendir(const char *pathname);
- 成功返回指针,失败返回null。
- struct dirent *readdir(DIR *dp);
- 成功返回指针,目录尾或失败返回null。
- void rewinddir(DIR *dp);
- int closedir(DIR *dp);
- 成功返回0,失败返回-1。
- long telldir(DIR *dp);
- 返回dp相关的目录的当前位置。
- void seekdir(DIR *dp, long loc);
telldir和seekdir函数不是基本POSIX.1标准的一部分,它们是SUS的XSI扩展,所以所有遵守协议的UNIX系统实现都被期望来提供它们。
看下面的代码,ls的简单实现:
- #include <dirent.h>
- #include <unistd.h>
- int
- main(int argc, char *argv[])
- {
- DIR *dp;
- struct dirent *dirp;
- if (argc != 2)
- exit(1);
- if ((dp = opendir(argv[1])) == NULL)
- exit(1);
- while ((dirp = readdir(dp)) != NULL)
- printf("%s\n", dirp->d_name);
- closedir(dp);
- exit(0);
- }
在文件
里定义的dirent结构体依赖于系统实现。实现定义这个结构体至少要包括以下两个成员:
struct dirent {
ino_t d_ino; /* i-node号 */
char d_name[NAME_MAX + 1]; /* null结尾的文件名 */
};
d_ino项不在POSIX.1中定义,因为它是一个实现特性,但它定义在POSIX.1的XSI扩展中。POSIX.1只在这个结构体里定义了d_name项。
注意NAME_MAX在Solaris上不是一个定义好的常量--它的值取决于目录所在的文件系统,而且它的值通常是从fpathconf函数得到。
NAME_MAX的一个普遍的值是255。(回想下第2节关于限量的表。)尽管如此,既然文件名是null终止的,数组d_name在头文件里怎么定义并
不重要,因为数据尺寸并不表明文件名的长度。
DIR结构体是一个内部结构,被那六个函数用来维护关于被读目录的信息。DIR结构体的作用与标准I/O库维护的FILE结构体类似,我们会在第5章讲述。
从opendir返回的DIR结构体的指针被其它5个函数使用。opendir函数初始化所有事情,以便第一个readdir操作读到目录的第一项。在目录里的项的顺序是实现相关的,而且通常不是字母序。
我们将使用这些目录函数来写一个遍历文件层次的程序。目标是产生各种类型的文件的计数,就如4.3节末的表一样。程序接受一个参数--开始的路径名--并
从那一点开始递归地遍历层次结构。Solaris提供了一个函数,ftw,来进行层次结构的真正遍历,并为每个文件调用用户定义的函数。
这个函数的问题是
它为每个文件调用stat函数,这会导致程序去解析符号链接。例如,如果我们从根目录开始,并有一个指向/usr/lib的名为/lib的符号链接,
/usr/lib目录里的所有文件都会被计数两次。为了更正它,Solaris提供了一个额外的函数,nftw,带有一个阻止解析符号链接的可选项。尽管
我们可以使用ntfw,但我们仍将写我们自己的简单的文件遍历器来展示目录函数的用法。
在SUS,ftw和nftw都包含在基本POSIX.1规范的XSI扩展中。Solaris 9和Linux
2.4.22包含了实现。基于BSD的系统有一个不同的函数,fts,提供了相似的功能。它在FreeBSD 5.2.1、Mac OS X
10.3和Linux 2.4.22中可用。
看下面的代码:
- #include <dirent.h>
- #include <limits.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <errno.h>
- /* function type that is called for each filename */
- typedef int Myfunc(const char *, const struct stat *, int);
- static Myfunc myfunc;
- static int myftw(char *, Myfunc *);
- static int dopath(Myfunc *);
- char *path_alloc(int *sizep); /* from section 2.5.5 */
- static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;
- int
- main(int argc, char *argv[])
- {
- int ret;
- if (argc != 2)
- exit(1);
- ret = myftw(argv[1], myfunc); /* does it all */
- ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
- if (ntot == 0)
- ntot = 1; /* avoid divide by 0; print 0 for all counts */
- printf("regular files = %71d, %5.2f %%\n", nreg, nreg*100.0/ntot);
- printf("directories = %71d, %5.2f %%\n", ndir, ndir*100.0/ntot);
- printf("block special = %71d, %5.2f %%\n", nblk, nblk*100.0/ntot);
- printf("char special = %71d, %5.2f %%\n", nchr, nchr*100.0/ntot);
- printf("FIFOs = %71d, %5.2f %%\n", nfifo, nfifo*100.0/ntot);
- printf("symbolic links = %71d, %5.2f %%\n", nslink, nslink*100.0/ntot);
- printf("sockets = %71d, %5.2f %%\n", nsock, nsock*100.0/ntot);
- exit(ret);
- }
- /*
- * Descend through the hierarchy, starting at "pathname".
- * The caller's func() is called for every file.
- */
- #define FTW_F 1 /* file other than directory */
- #define FTW_D 2 /* directory */
- #define FTW_DNR 3 /* directory that can't be read */
- #define FTW_NS 4 /* file that we can't stat */
- static char *fullpath; /*contains full pathname for every file */
- static int /* we return whatever func() returns */
- myftw(char *pathname, Myfunc *func)
- {
- int len;
- fullpath = path_alloc(&len); /* malloc's for PATH_MAX+1 bytes */
- strncpy(fullpath, pathname, len); /* protect against */
- fullpath[len -1] = 0; /* buffer overrun */
- return(dopath(func));
- }
- /*
- * Descend through the hierarchy, starting at "fullpath".
- * If "fullpath" is anything other than a directory, we lstat() it,
- * call func(), and return. For a directory, we call ourself
- * recursively for each name in the directory.
- */
- static int /* we return whatever func() returns */
- dopath(Myfunc* func)
- {
- struct stat statbuf;
- struct dirent *dirp;
- DIR *dp;
- int ret;
- char *ptr;
- if (lstat(fullpath, &statbuf) < 0) /* stat error */
- return(func(fullpath, &statbuf, FTW_NS));
- if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
- return(func(fullpath, &statbuf, FTW_F));
- /*
- * It's a directory. First call func() for the directory,
- * then process each filename in the directory.
- */
- if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
- return(ret);
- ptr = fullpath + strlen(fullpath); /*point to end of fullpath */
- *ptr++ = '/';
- *ptr = 0;
- if ((dp = opendir(fullpath)) == NULL) /* can't read directory */
- return(func(fullpath, &statbuf, FTW_DNR));
- while ((dirp = readdir(dp)) != NULL) {
- if (strcmp(dirp->d_name, ".") == 0 ||
- strcmp(dirp->d_name, "..") == 0)
- continue; /*ignore dot and dot-dot */
-
- strcpy(ptr, dirp->d_name); /* append name after slash */
-
- if ((ret = dopath(func)) != 0)
- break; /* time to leave */
- }
- ptr[-1] = 0; /* erase everything from slash onwards */
-
- if (closedir(dp) < 0) {
- printf("can't close directory %s", fullpath);
- return -1;
- }
- return ret;
- }
- static int
- myfunc(const char *pathname, const struct stat *statptr, int type)
- {
- switch (type) {
- case FTW_F:
- switch (statptr->st_mode & S_IFMT) {
- case S_IFREG: nreg++; break;
- case S_IFBLK: nblk++; break;
- case S_IFCHR: nchr++; break;
- case S_IFIFO: nfifo++;break;
- case S_IFLNK: nslink++; break;
- case S_IFSOCK: nsock++; break;
- case S_IFDIR:
- printf("for S_IFDIR for %s", pathname);
- /* directories should have type = FTW_D */
- }
- break;
- case FTW_D:
- ndir++;
- break;
- case FTW_DNR:
- printf("can't read directory %s", pathname);
- break;
-
- case FTW_NS:
- printf("stat error for %s", pathname);
- break;
-
- default:
- printf("unknown type %d for pathname %s", type, pathname);
- }
- return(0);
- }
- #ifdef PATH_MAX
- static int pathmax = PATH_MAX;
- #else
- static int pathmax = 0;
- #endif
- #define SUSV3 200112L
- static long posix_version = 0;
- /* If MATH_MAX is indeterminate, no guarantee this is adquate */
- #define PATH_MAX_GUESS 1024
- char *
- path_alloc(int *sizep) /* also return allocated size, if nonnull */
- {
- char *ptr;
- int size;
- if (posix_version == 0)
- posix_version = sysconf(_SC_VERSION);
- if (pathmax == 0) { /*first time through */
- errno = 0;
- if ((pathmax = pathconf("/", _PC_PATH_MAX)) < 0) {
- if (errno == 0)
- pathmax = PATH_MAX_GUESS; /* it's indeterminate */
- else
- printf("pathconf error for _PC_PATH_MAX\n");
- } else {
- pathmax++; /* add one since it's relative to root */
- }
- }
- if (posix_version < SUSV3)
- size = pathmax + 1;
- else
- size = pathmax;
- if ((ptr = malloc(size)) == NULL)
- printf("malloc error for pathname\n");
- if (sizep != NULL)
- *sizep = size;
- return(ptr);
- }
运行./a.out /dev,结果为:
regular files = 283, 32.49 %
directories = 75, 8.61 %
block special = 29, 3.33 %
char special = 196, 22.50 %
FIFOs = 1, 0.11 %
symbolic links = 285, 32.72 %
sockets = 2, 0.23 %
更多关于遍历文件系统和许多标准UNIX系统命令(find、ls、tar等)里这种技术的使用,参考Fowler, Korn, and Vo[1989]。
阅读(1174) | 评论(0) | 转发(0) |