More work on descending directory structure.
[cumulus.git] / scandir.cc
1 /* Recursively descend the filesystem and visit each file. */
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <stdint.h>
6 #include <dirent.h>
7 #include <fcntl.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11
12 #include <algorithm>
13 #include <string>
14 #include <vector>
15
16 using std::string;
17 using std::vector;
18
19 void scandir(const string& path);
20
21 void dumpfile(int fd)
22 {
23     struct stat stat_buf;
24     fstat(fd, &stat_buf);
25
26     if ((stat_buf.st_mode & S_IFMT) != S_IFREG) {
27         printf("file is no longer a regular file!\n");
28         return;
29     }
30 }
31
32 void scanfile(const string& path)
33 {
34     int fd;
35     long flags;
36     struct stat stat_buf;
37     lstat(path.c_str(), &stat_buf);
38
39     printf("%s:\n", path.c_str());
40     printf("  ino=%Ld, perm=%04o, uid=%d, gid=%d, nlink=%d, blksize=%d, size=%Ld\n",
41            (int64_t)stat_buf.st_ino, stat_buf.st_mode & 07777,
42            stat_buf.st_uid, stat_buf.st_gid, stat_buf.st_nlink,
43            (int)stat_buf.st_blksize, (int64_t)stat_buf.st_size);
44
45     switch (stat_buf.st_mode & S_IFMT) {
46     case S_IFIFO:
47     case S_IFSOCK:
48     case S_IFCHR:
49     case S_IFBLK:
50     case S_IFLNK:
51         printf("  special file\n");
52         break;
53     case S_IFREG:
54         printf("  regular file\n");
55         /* Be paranoid when opening the file.  We have no guarantee that the
56          * file was not replaced between the stat() call above and the open()
57          * call below, so we might not even be opening a regular file.  That
58          * the file descriptor refers to a regular file is checked in
59          * dumpfile().  But we also supply flags to open to to guard against
60          * various conditions before we can perform that verification:
61          *   - O_NOFOLLOW: in the event the file was replaced by a symlink
62          *   - O_NONBLOCK: prevents open() from blocking if the file was
63          *     replaced by a fifo
64          * We also add in O_NOATIME, since this may reduce disk writes (for
65          * inode updates). */
66         fd = open(path.c_str(), O_RDONLY|O_NOATIME|O_NOFOLLOW|O_NONBLOCK);
67
68         /* Drop the use of the O_NONBLOCK flag; we only wanted that for file
69          * open. */
70         flags = fcntl(fd, F_GETFL);
71         fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
72
73         dumpfile(fd);
74         close(fd);
75
76         break;
77     case S_IFDIR:
78         printf("  directory\n");
79         scandir(path);
80         break;
81     }
82 }
83
84 void scandir(const string& path)
85 {
86     printf("Scan directory: %s\n", path.c_str());
87
88     DIR *dir = opendir(path.c_str());
89
90     if (dir == NULL) {
91         printf("Error: %m\n");
92         return;
93     }
94
95     struct dirent *ent;
96     vector<string> contents;
97     while ((ent = readdir(dir)) != NULL) {
98         string filename(ent->d_name);
99         if (filename == "." || filename == "..")
100             continue;
101         contents.push_back(filename);
102     }
103
104     sort(contents.begin(), contents.end());
105
106     for (vector<string>::iterator i = contents.begin();
107          i != contents.end(); ++i) {
108         const string& filename = *i;
109         printf("  d_name = '%s'\n", filename.c_str());
110         scanfile(path + "/" + filename);
111     }
112
113     closedir(dir);
114 }
115
116 int main(int argc, char *argv[])
117 {
118     scandir(".");
119
120     return 0;
121 }