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