Merge "libcutils: fs_config: fix "system/<partition>/" aliasing"
am: 449bfd7a93

Change-Id: I9f3d0f0e4dc147d9d2a5a1e41486805ada29d62a
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 221dea2..a1dbd78 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -266,14 +266,27 @@
     return false;
 }
 
+static inline bool prefix_cmp(bool partial, const char* prefix, size_t len, const char* path,
+                              size_t plen) {
+    return ((partial && plen >= len) || (plen == len)) && !strncmp(prefix, path, len);
+}
+
 // alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
 // "system/<partition>/<stuff>" to "<partition>/<stuff>"
-static bool prefix_cmp(const char* prefix, const char* path, size_t len) {
-    if (!strncmp(prefix, path, len)) return true;
+static bool fs_config_cmp(bool partial, const char* prefix, size_t len, const char* path,
+                          size_t plen) {
+    // If name ends in * then allow partial matches.
+    if (!partial && prefix[len - 1] == '*') {
+        len--;
+        partial = true;
+    }
+
+    if (prefix_cmp(partial, prefix, len, path, plen)) return true;
 
     static const char system[] = "system/";
     if (!strncmp(path, system, strlen(system))) {
         path += strlen(system);
+        plen -= strlen(system);
     } else if (len <= strlen(system)) {
         return false;
     } else if (strncmp(prefix, system, strlen(system))) {
@@ -282,25 +295,11 @@
         prefix += strlen(system);
         len -= strlen(system);
     }
-    return is_partition(prefix, len) && !strncmp(prefix, path, len);
+    return is_partition(prefix, len) && prefix_cmp(partial, prefix, len, path, plen);
 }
-
-static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
-    if (dir) {
-        if (plen < len) {
-            return false;
-        }
-    } else {
-        // If name ends in * then allow partial matches.
-        if (prefix[len - 1] == '*') {
-            return prefix_cmp(prefix, path, len - 1);
-        }
-        if (plen != len) {
-            return false;
-        }
-    }
-    return prefix_cmp(prefix, path, len);
-}
+#ifndef __ANDROID_VNDK__
+auto __for_testing_only__fs_config_cmp = fs_config_cmp;
+#endif
 
 void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
                unsigned* mode, uint64_t* capabilities) {
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index a62cd51..391adb6 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -29,12 +29,39 @@
 
 extern const fs_path_config* __for_testing_only__android_dirs;
 extern const fs_path_config* __for_testing_only__android_files;
+extern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);
 
 // Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
 // hit a nullptr termination, before we declare the list is just too big or
 // could be missing the nullptr.
 static constexpr size_t max_idx = 4096;
 
+static const struct fs_config_cmp_test {
+    bool dir;
+    const char* prefix;
+    const char* path;
+    bool match;
+} fs_config_cmp_tests[] = {
+    // clang-format off
+    { true,  "system/lib",             "system/lib/hw",           true  },
+    { true,  "vendor/lib",             "system/vendor/lib/hw",    true  },
+    { true,  "system/vendor/lib",      "vendor/lib/hw",           true  },
+    { true,  "system/vendor/lib",      "system/vendor/lib/hw",    true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/w",     false },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi",  true  },
+    { false, "vendor/bin/wifi",        "system/vendor/bin/wifi2", false },
+    { false, "system/vendor/bin/wifi", "system/vendor/bin/wifi",  true, },
+    { false, "odm/bin/wifi",           "system/odm/bin/wifi",     true  },
+    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     true  },
+    { false, "data/bin/wifi",          "system/data/bin/wifi",    false },
+    { false, "system/bin/*",           "system/bin/wifi",         true  },
+    { false, "vendor/bin/*",           "system/vendor/bin/wifi",  true  },
+    { false, "system/bin/*",           "system/bin",              false },
+    { false, "system/vendor/bin/*",    "vendor/bin/wifi",         true  },
+    { false, NULL,                     NULL,                      false },
+    // clang-format on
+};
+
 static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
                          const std::string& prefix) {
     bool retval = false;
@@ -106,6 +133,22 @@
     return check_unique(paths_tmp, config, prefix) || retval;
 }
 
+static bool check_fs_config_cmp(const fs_config_cmp_test* tests) {
+    bool match, retval = false;
+    for (size_t idx = 0; tests[idx].prefix; ++idx) {
+        match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,
+                                                  strlen(tests[idx].prefix), tests[idx].path,
+                                                  strlen(tests[idx].path));
+        if (match != tests[idx].match) {
+            GTEST_LOG_(ERROR) << tests[idx].path << (match ? " matched " : " didn't match ")
+                              << tests[idx].prefix;
+            retval = true;
+            break;
+        }
+    }
+    return retval;
+}
+
 #define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
 
 static bool check_unique(const std::string& config, const std::string& prefix) {
@@ -199,3 +242,7 @@
 TEST(fs_config, odm_files_alias) {
     check_two(__for_testing_only__android_files, "files", "odm/");
 }
+
+TEST(fs_config, system_alias) {
+    EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));
+}