Add presubmit test for vendor overlay

Add a test that creates files in the appropriate vendor_overlay directory and
verifies that they are correctly overlaid (or not) onto /vendor after rebooting.

Test: locally running atest
Change-Id: I65860dbeb837f86ac030fa51b3af93844e82de96
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc85408..716378b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,9 @@
       "name": "fs_mgr_unit_test"
     },
     {
+      "name": "fs_mgr_vendor_overlay_test"
+    },
+    {
       "name": "init_tests"
     },
     {
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 565597a..59af924 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -53,3 +53,15 @@
     },
     host_supported: true,
 }
+
+java_test_host {
+    name: "fs_mgr_vendor_overlay_test",
+
+    srcs:  ["src/**/VendorOverlayHostTest.java"],
+
+    libs: ["tradefed"],
+
+    test_config: "vendor-overlay-test.xml",
+
+    test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+  boolean wasRoot = false;
+
+  @Before
+  public void setup() throws DeviceNotAvailableException {
+    wasRoot = getDevice().isAdbRoot();
+    if (!wasRoot) {
+      Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+    }
+
+    Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+        testConditionsMet());
+
+    getDevice().remountSystemWritable();
+    // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+    Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+    Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+    CommandResult result = getDevice().executeShellV2Command("df");
+    Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+        vendorPattern.matcher(result.getStdout()).find());
+    Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+        productPattern.matcher(result.getStdout()).find());
+  }
+
+  private boolean cmdSucceeded(CommandResult result) {
+    return result.getStatus() == CommandStatus.SUCCESS;
+  }
+
+  private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+    CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+    Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+  }
+
+  /**
+   * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+   */
+  @Test
+  public void testVendorOverlay() throws DeviceNotAvailableException {
+    String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+    // Create files and modify policy
+    CommandResult result = getDevice().executeShellV2Command(
+        "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+        "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+    Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+    assumeMkdirSuccess("/vendor/testdir");
+    assumeMkdirSuccess("/vendor/diffcontext");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    result = getDevice().executeShellV2Command(
+        "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+    Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+    assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+    result = getDevice().executeShellV2Command(
+        "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+    Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+    getDevice().reboot();
+
+    // Test that the file was overlaid properly
+    result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+    Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+    Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+    result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+    Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+  }
+
+  // Duplicate of fs_mgr_overlayfs_valid() logic
+  // Requires root
+  public boolean testConditionsMet() throws DeviceNotAvailableException {
+    if (cmdSucceeded(getDevice().executeShellV2Command(
+        "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+      return true;
+    }
+    if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+      return false;
+    }
+    CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+    Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+    Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+    kernelVersionMatcher.find();
+    int majorKernelVersion;
+    int minorKernelVersion;
+    try {
+      majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+      minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+    } catch (Exception e) {
+      return false;
+    }
+    if (majorKernelVersion < 4) {
+      return true;
+    }
+    if (majorKernelVersion > 4) {
+      return false;
+    }
+    if (minorKernelVersion > 6) {
+      return false;
+    }
+    return true;
+  }
+
+  @After
+  public void tearDown() throws DeviceNotAvailableException {
+    if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+      getDevice().reboot();
+    }
+    if (!wasRoot) {
+      getDevice().disableAdbRoot();
+    }
+  }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<configuration description="Config for vendor overlay test cases">
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+    </test>
+</configuration>
+