1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tests.vendoroverlay; 18 19 import com.android.tradefed.device.DeviceNotAvailableException; 20 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 21 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 22 import com.android.tradefed.util.CommandResult; 23 import com.android.tradefed.util.CommandStatus; 24 25 import java.util.regex.Matcher; 26 import java.util.regex.Pattern; 27 28 import org.junit.After; 29 import org.junit.Assert; 30 import org.junit.Assume; 31 import org.junit.Before; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 35 /** 36 * Test the vendor overlay feature. Requires adb remount with OverlayFS. 37 */ 38 @RunWith(DeviceJUnit4ClassRunner.class) 39 public class VendorOverlayHostTest extends BaseHostJUnit4Test { 40 boolean wasRoot = false; 41 42 @Before setup()43 public void setup() throws DeviceNotAvailableException { 44 wasRoot = getDevice().isAdbRoot(); 45 if (!wasRoot) { 46 Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot()); 47 } 48 49 Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support", 50 testConditionsMet()); 51 52 getDevice().remountSystemWritable(); 53 // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity. 54 Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE); 55 Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE); 56 CommandResult result = getDevice().executeShellV2Command("df"); 57 Assume.assumeTrue("OverlayFS not used for adb remount on /vendor", 58 vendorPattern.matcher(result.getStdout()).find()); 59 Assume.assumeTrue("OverlayFS not used for adb remount on /product", 60 productPattern.matcher(result.getStdout()).find()); 61 } 62 cmdSucceeded(CommandResult result)63 private boolean cmdSucceeded(CommandResult result) { 64 return result.getStatus() == CommandStatus.SUCCESS; 65 } 66 assumeMkdirSuccess(String dir)67 private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException { 68 CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir); 69 Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result)); 70 } 71 72 /** 73 * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor. 74 */ 75 @Test testVendorOverlay()76 public void testVendorOverlay() throws DeviceNotAvailableException { 77 String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout(); 78 79 // Create files and modify policy 80 CommandResult result = getDevice().executeShellV2Command( 81 "echo '/(product|system/product)/vendor_overlay/" + vndkVersion + 82 "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts"); 83 Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result)); 84 assumeMkdirSuccess("/vendor/testdir"); 85 assumeMkdirSuccess("/vendor/diffcontext"); 86 assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir"); 87 result = getDevice().executeShellV2Command( 88 "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test"); 89 Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result)); 90 assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test"); 91 assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test"); 92 result = getDevice().executeShellV2Command( 93 "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir"); 94 Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result)); 95 96 getDevice().reboot(); 97 98 // Test that the file was overlaid properly 99 result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]"); 100 Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result)); 101 result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]"); 102 Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result)); 103 result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]"); 104 Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result)); 105 } 106 107 // Duplicate of fs_mgr_overlayfs_valid() logic 108 // Requires root testConditionsMet()109 public boolean testConditionsMet() throws DeviceNotAvailableException { 110 if (cmdSucceeded(getDevice().executeShellV2Command( 111 "[ -e /sys/module/overlay/parameters/override_creds ]"))) { 112 return true; 113 } 114 if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) { 115 return false; 116 } 117 CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version"); 118 Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*"); 119 Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout()); 120 kernelVersionMatcher.find(); 121 int majorKernelVersion; 122 int minorKernelVersion; 123 try { 124 majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1)); 125 minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2)); 126 } catch (Exception e) { 127 return false; 128 } 129 if (majorKernelVersion < 4) { 130 return true; 131 } 132 if (majorKernelVersion > 4) { 133 return false; 134 } 135 if (minorKernelVersion > 6) { 136 return false; 137 } 138 return true; 139 } 140 141 @After tearDown()142 public void tearDown() throws DeviceNotAvailableException { 143 if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) { 144 getDevice().reboot(); 145 } 146 if (!wasRoot) { 147 getDevice().disableAdbRoot(); 148 } 149 } 150 } 151 152