Merge "init: log services requested restart"
diff --git a/init/util.cpp b/init/util.cpp
index af6cf50..1801d17 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -61,6 +61,8 @@
 
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
 
+const std::string kDataDirPrefix("/data/");
+
 void (*trigger_shutdown)(const std::string& command) = nullptr;
 
 // DecodeUid() - decodes and returns the given string, which can be either the
@@ -458,52 +460,34 @@
     return {};
 }
 
-static FscryptAction FscryptInferAction(const std::string& dir) {
-    const std::string prefix = "/data/";
-
-    if (!android::base::StartsWith(dir, prefix)) {
-        return FscryptAction::kNone;
-    }
-
-    // Only set policy on first level /data directories
-    // To make this less restrictive, consider using a policy file.
-    // However this is overkill for as long as the policy is simply
-    // to apply a global policy to all /data folders created via makedir
-    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
-        return FscryptAction::kNone;
-    }
-
-    // Special case various directories that must not be encrypted,
-    // often because their subdirectories must be encrypted.
-    // This isn't a nice way to do this, see b/26641735
-    std::vector<std::string> directories_to_exclude = {
-            "lost+found", "system_ce", "system_de", "misc_ce",     "misc_de",
-            "vendor_ce",  "vendor_de", "media",     "data",        "user",
-            "user_de",    "apex",      "preloads",  "app-staging", "gsi",
-    };
-    for (const auto& d : directories_to_exclude) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kNone;
+// Remove unnecessary slashes so that any later checks (e.g., the check for
+// whether the path is a top-level directory in /data) don't get confused.
+std::string CleanDirPath(const std::string& path) {
+    std::string result;
+    result.reserve(path.length());
+    // Collapse duplicate slashes, e.g. //data//foo// => /data/foo/
+    for (char c : path) {
+        if (c != '/' || result.empty() || result.back() != '/') {
+            result += c;
         }
     }
-    // Empty these directories if policy setting fails.
-    std::vector<std::string> wipe_on_failure = {
-            "rollback", "rollback-observer",  // b/139193659
-    };
-    for (const auto& d : wipe_on_failure) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kDeleteIfNecessary;
-        }
+    // Remove trailing slash, e.g. /data/foo/ => /data/foo
+    if (result.length() > 1 && result.back() == '/') {
+        result.pop_back();
     }
-    return FscryptAction::kRequire;
+    return result;
 }
 
 Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
+    std::string path = CleanDirPath(args[1]);
+    const bool is_toplevel_data_dir =
+            StartsWith(path, kDataDirPrefix) &&
+            path.find_first_of('/', kDataDirPrefix.size()) == std::string::npos;
+    FscryptAction fscrypt_action =
+            is_toplevel_data_dir ? FscryptAction::kRequire : FscryptAction::kNone;
     mode_t mode = 0755;
     Result<uid_t> uid = -1;
     Result<gid_t> gid = -1;
-    FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
-    FscryptAction fscrypt_action = fscrypt_inferred_action;
     std::string ref_option = "ref";
     bool set_option_encryption = false;
     bool set_option_key = false;
@@ -568,24 +552,17 @@
     if (set_option_key && fscrypt_action == FscryptAction::kNone) {
         return Error() << "Key option set but encryption action is none";
     }
-    const std::string prefix = "/data/";
-    if (StartsWith(args[1], prefix) &&
-        args[1].find_first_of('/', prefix.size()) == std::string::npos) {
+    if (is_toplevel_data_dir) {
         if (!set_option_encryption) {
-            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
+            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << path
                          << " <mode> <uid> <gid> encryption=Require";
         }
         if (fscrypt_action == FscryptAction::kNone) {
-            LOG(INFO) << "Not setting encryption policy on: " << args[1];
+            LOG(INFO) << "Not setting encryption policy on: " << path;
         }
     }
-    if (fscrypt_action != fscrypt_inferred_action) {
-        LOG(WARNING) << "Inferred action different from explicit one, expected "
-                     << static_cast<int>(fscrypt_inferred_action) << " but got "
-                     << static_cast<int>(fscrypt_action);
-    }
 
-    return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
+    return MkdirOptions{path, mode, *uid, *gid, fscrypt_action, ref_option};
 }
 
 Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
diff --git a/init/util.h b/init/util.h
index bf53675..47d4ff5 100644
--- a/init/util.h
+++ b/init/util.h
@@ -69,6 +69,7 @@
 
 bool IsLegalPropertyName(const std::string& name);
 Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
+std::string CleanDirPath(const std::string& path);
 
 struct MkdirOptions {
     std::string target;
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 565e7d4..e8144c3 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -170,5 +170,18 @@
     EXPECT_TRUE(is_dir(path1.c_str()));
 }
 
+TEST(util, CleanDirPath) {
+    EXPECT_EQ("", CleanDirPath(""));
+    EXPECT_EQ("/", CleanDirPath("/"));
+    EXPECT_EQ("/", CleanDirPath("//"));
+    EXPECT_EQ("/foo", CleanDirPath("/foo"));
+    EXPECT_EQ("/foo", CleanDirPath("//foo"));
+    EXPECT_EQ("/foo", CleanDirPath("/foo/"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar/"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar////"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("//foo//bar"));
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 2746314..70a3736 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -922,7 +922,6 @@
     # contain any subdirectories other than the per-user ones.  /data/media/obb
     # is an exception that exists for legacy reasons.
     mkdir /data/media 0770 media_rw media_rw encryption=None
-    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
     mkdir /data/misc_ce 01771 system misc encryption=None
     mkdir /data/misc_de 01771 system misc encryption=None
     mkdir /data/system_ce 0770 system system encryption=None
@@ -932,6 +931,11 @@
     mkdir /data/vendor_ce 0771 root root encryption=None
     mkdir /data/vendor_de 0771 root root encryption=None
 
+    # Set the casefold flag on /data/media.  For upgrades, a restorecon can be
+    # needed first to relabel the directory from media_rw_data_file.
+    restorecon /data/media
+    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
+
     # A tmpfs directory, which will contain all apps CE DE data directory that
     # bind mount from the original source.
     mount tmpfs tmpfs /data_mirror nodev noexec nosuid mode=0700,uid=0,gid=1000