More work on descending directory structure.
authorMichael Vrable <mvrable@cs.ucsd.edu>
Tue, 12 Dec 2006 05:46:33 +0000 (21:46 -0800)
committerMichael Vrable <mvrable@beleg.ucsd.edu>
Tue, 12 Dec 2006 05:46:33 +0000 (21:46 -0800)
Improvements:
  - Directory listings are processed in sorted order.
  - Open regular files, but be very paranoid in doing so (try to avoid
    danger from race conditions).

scandir.cc

index 7e02f27..64aec8a 100644 (file)
@@ -9,14 +9,30 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <string>
+#include <vector>
 
 using std::string;
+using std::vector;
 
 void scandir(const string& path);
 
+void dumpfile(int fd)
+{
+    struct stat stat_buf;
+    fstat(fd, &stat_buf);
+
+    if ((stat_buf.st_mode & S_IFMT) != S_IFREG) {
+        printf("file is no longer a regular file!\n");
+        return;
+    }
+}
+
 void scanfile(const string& path)
 {
+    int fd;
+    long flags;
     struct stat stat_buf;
     lstat(path.c_str(), &stat_buf);
 
@@ -36,6 +52,27 @@ void scanfile(const string& path)
         break;
     case S_IFREG:
         printf("  regular file\n");
+        /* Be paranoid when opening the file.  We have no guarantee that the
+         * file was not replaced between the stat() call above and the open()
+         * call below, so we might not even be opening a regular file.  That
+         * the file descriptor refers to a regular file is checked in
+         * dumpfile().  But we also supply flags to open to to guard against
+         * various conditions before we can perform that verification:
+         *   - O_NOFOLLOW: in the event the file was replaced by a symlink
+         *   - O_NONBLOCK: prevents open() from blocking if the file was
+         *     replaced by a fifo
+         * We also add in O_NOATIME, since this may reduce disk writes (for
+         * inode updates). */
+        fd = open(path.c_str(), O_RDONLY|O_NOATIME|O_NOFOLLOW|O_NONBLOCK);
+
+        /* Drop the use of the O_NONBLOCK flag; we only wanted that for file
+         * open. */
+        flags = fcntl(fd, F_GETFL);
+        fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+
+        dumpfile(fd);
+        close(fd);
+
         break;
     case S_IFDIR:
         printf("  directory\n");
@@ -56,10 +93,19 @@ void scandir(const string& path)
     }
 
     struct dirent *ent;
+    vector<string> contents;
     while ((ent = readdir(dir)) != NULL) {
         string filename(ent->d_name);
         if (filename == "." || filename == "..")
             continue;
+        contents.push_back(filename);
+    }
+
+    sort(contents.begin(), contents.end());
+
+    for (vector<string>::iterator i = contents.begin();
+         i != contents.end(); ++i) {
+        const string& filename = *i;
         printf("  d_name = '%s'\n", filename.c_str());
         scanfile(path + "/" + filename);
     }