c8e43a8068776218b2f0da47354ab9a27ce58ecc
[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=%Ld\n", size);
50 }
51
52 void scanfile(const string& path)
53 {
54     int fd;
55     long flags;
56     struct stat stat_buf;
57     char *buf;
58     ssize_t len;
59
60     lstat(path.c_str(), &stat_buf);
61
62     printf("%s:\n", path.c_str());
63     printf("  ino=%Ld, perm=%04o, uid=%d, gid=%d, nlink=%d, blksize=%d, size=%Ld\n",
64            (int64_t)stat_buf.st_ino, stat_buf.st_mode & 07777,
65            stat_buf.st_uid, stat_buf.st_gid, stat_buf.st_nlink,
66            (int)stat_buf.st_blksize, (int64_t)stat_buf.st_size);
67
68     switch (stat_buf.st_mode & S_IFMT) {
69     case S_IFIFO:
70     case S_IFSOCK:
71     case S_IFCHR:
72     case S_IFBLK:
73         printf("    special file\n");
74         break;
75     case S_IFLNK:
76         printf("    symlink\n");
77
78         /* Use the reported file size to allocate a buffer large enough to read
79          * the symlink.  Allocate slightly more space, so that we ask for more
80          * bytes than we expect and so check for truncation. */
81         buf = new char[stat_buf.st_size + 2];
82         len = readlink(path.c_str(), buf, stat_buf.st_size + 1);
83         if (len < 0) {
84             printf("error reading symlink: %m\n");
85         } else if (len <= stat_buf.st_size) {
86             buf[len] = '\0';
87             printf("    contents=%s\n", buf);
88         } else if (len > stat_buf.st_size) {
89             printf("error reading symlink: name truncated\n");
90         }
91         delete[] buf;
92         break;
93     case S_IFREG:
94         printf("    regular file\n");
95         /* Be paranoid when opening the file.  We have no guarantee that the
96          * file was not replaced between the stat() call above and the open()
97          * call below, so we might not even be opening a regular file.  That
98          * the file descriptor refers to a regular file is checked in
99          * dumpfile().  But we also supply flags to open to to guard against
100          * various conditions before we can perform that verification:
101          *   - O_NOFOLLOW: in the event the file was replaced by a symlink
102          *   - O_NONBLOCK: prevents open() from blocking if the file was
103          *     replaced by a fifo
104          * We also add in O_NOATIME, since this may reduce disk writes (for
105          * inode updates). */
106         fd = open(path.c_str(), O_RDONLY|O_NOATIME|O_NOFOLLOW|O_NONBLOCK);
107
108         /* Drop the use of the O_NONBLOCK flag; we only wanted that for file
109          * open. */
110         flags = fcntl(fd, F_GETFL);
111         fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
112
113         dumpfile(fd);
114         close(fd);
115
116         break;
117     case S_IFDIR:
118         printf("    directory\n");
119         scandir(path);
120         break;
121     }
122 }
123
124 void scandir(const string& path)
125 {
126     DIR *dir = opendir(path.c_str());
127
128     if (dir == NULL) {
129         printf("Error: %m\n");
130         return;
131     }
132
133     struct dirent *ent;
134     vector<string> contents;
135     while ((ent = readdir(dir)) != NULL) {
136         string filename(ent->d_name);
137         if (filename == "." || filename == "..")
138             continue;
139         contents.push_back(filename);
140     }
141
142     sort(contents.begin(), contents.end());
143
144     for (vector<string>::iterator i = contents.begin();
145          i != contents.end(); ++i) {
146         const string& filename = *i;
147         scanfile(path + "/" + filename);
148     }
149
150     closedir(dir);
151 }
152
153 int main(int argc, char *argv[])
154 {
155     scandir(".");
156
157     return 0;
158 }