Merge "init: fix format issue"
diff --git a/init/README.md b/init/README.md
index f3b57bc..0ea00fb 100644
--- a/init/README.md
+++ b/init/README.md
@@ -447,6 +447,9 @@
 `rmdir <path>`
 > Calls rmdir(2) on the given path.
 
+`readahead <file|dir>`
+> Calls readahead(2) on the file or files within given directory.
+
 `setprop <name> <value>`
 > Set system property _name_ to _value_. Properties are expanded
   within _value_.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 5335608..eea78fd 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <linux/loop.h>
 #include <linux/module.h>
 #include <mntent.h>
@@ -627,7 +628,7 @@
 
 static int do_sysclktz(const std::vector<std::string>& args) {
     struct timezone tz = {};
-    if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(NULL, &tz) != -1) {
+    if (android::base::ParseInt(args[1], &tz.tz_minuteswest) && settimeofday(nullptr, &tz) != -1) {
         return 0;
     }
     return -1;
@@ -660,6 +661,66 @@
     return 0;
 }
 
+static int do_readahead(const std::vector<std::string>& args) {
+    struct stat sb;
+
+    if (stat(args[1].c_str(), &sb)) {
+        PLOG(ERROR) << "Error opening " << args[1];
+        return -1;
+    }
+
+    // We will do readahead in a forked process in order not to block init
+    // since it may block while it reads the
+    // filesystem metadata needed to locate the requested blocks.  This
+    // occurs frequently with ext[234] on large files using indirect blocks
+    // instead of extents, giving the appearance that the call blocks until
+    // the requested data has been read.
+    pid_t pid = fork();
+    if (pid == 0) {
+        android::base::Timer t;
+        if (S_ISREG(sb.st_mode)) {
+            android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(args[1].c_str(), O_RDONLY)));
+            if (fd == -1) {
+                PLOG(ERROR) << "Error opening file: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+            if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+                PLOG(ERROR) << "Error readahead file: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+        } else if (S_ISDIR(sb.st_mode)) {
+            char* paths[] = {const_cast<char*>(args[1].data()), nullptr};
+            std::unique_ptr<FTS, decltype(&fts_close)> fts(
+                fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);
+            if (!fts) {
+                PLOG(ERROR) << "Error opening directory: " << args[1];
+                _exit(EXIT_FAILURE);
+            }
+            // Traverse the entire hierarchy and do readahead
+            for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
+                 ftsent = fts_read(fts.get())) {
+                if (ftsent->fts_info & FTS_F) {
+                    android::base::unique_fd fd(
+                        TEMP_FAILURE_RETRY(open(ftsent->fts_accpath, O_RDONLY)));
+                    if (fd == -1) {
+                        PLOG(ERROR) << "Error opening file: " << args[1];
+                        continue;
+                    }
+                    if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+                        PLOG(ERROR) << "Unable to readahead on file: " << ftsent->fts_accpath;
+                    }
+                }
+            }
+        }
+        LOG(INFO) << "Readahead " << args[1] << " took " << t;
+        _exit(0);
+    } else if (pid < 0) {
+        PLOG(ERROR) << "Fork failed";
+        return -1;
+    }
+    return 0;
+}
+
 static int do_copy(const std::vector<std::string>& args) {
     std::string data;
     std::string err;
@@ -898,6 +959,7 @@
         {"mount_all",               {1,     kMax, do_mount_all}},
         {"mount",                   {3,     kMax, do_mount}},
         {"umount",                  {1,     1,    do_umount}},
+        {"readahead",               {1,     1,    do_readahead}},
         {"restart",                 {1,     1,    do_restart}},
         {"restorecon",              {1,     kMax, do_restorecon}},
         {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},