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 import java.util.regex.Matcher; 25 import java.util.regex.Pattern; 26 import org.junit.After; 27 import org.junit.Assert; 28 import org.junit.Assume; 29 import org.junit.Before; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 33 /** 34 * Test the vendor overlay feature. Requires adb remount with OverlayFS. 35 */ 36 @RunWith(DeviceJUnit4ClassRunner.class) 37 public class VendorOverlayHostTest extends BaseHostJUnit4Test { 38 boolean wasRoot = false; 39 String vndkVersion = null; 40 41 @Before setup()42 public void setup() throws DeviceNotAvailableException { 43 vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout(); 44 Assume.assumeTrue( 45 "Vendor Overlay is disabled for VNDK deprecated devices", 46 vndkVersion != null && !vndkVersion.trim().isEmpty()); 47 48 wasRoot = getDevice().isAdbRoot(); 49 if (!wasRoot) { 50 Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot()); 51 } 52 53 Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support", 54 testConditionsMet()); 55 56 getDevice().remountSystemWritable(); 57 // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity. 58 Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE); 59 Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE); 60 CommandResult result = getDevice().executeShellV2Command("df"); 61 Assume.assumeTrue("OverlayFS not used for adb remount on /vendor", 62 vendorPattern.matcher(result.getStdout()).find()); 63 Assume.assumeTrue("OverlayFS not used for adb remount on /product", 64 productPattern.matcher(result.getStdout()).find()); 65 } 66 cmdSucceeded(CommandResult result)67 private boolean cmdSucceeded(CommandResult result) { 68 return result.getStatus() == CommandStatus.SUCCESS; 69 } 70 assumeMkdirSuccess(String dir)71 private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException { 72 CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir); 73 Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result)); 74 } 75 76 /** 77 * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor. 78 */ 79 @Test testVendorOverlay()80 public void testVendorOverlay() throws DeviceNotAvailableException { 81 // Create files and modify policy 82 CommandResult result = getDevice().executeShellV2Command( 83 "echo '/(product|system/product)/vendor_overlay/" + vndkVersion + 84 "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts"); 85 Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result)); 86 assumeMkdirSuccess("/vendor/testdir"); 87 assumeMkdirSuccess("/vendor/diffcontext"); 88 assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir"); 89 result = getDevice().executeShellV2Command( 90 "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test"); 91 Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result)); 92 assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test"); 93 assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test"); 94 result = getDevice().executeShellV2Command( 95 "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir"); 96 Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result)); 97 98 getDevice().reboot(); 99 100 // Test that the file was overlaid properly 101 result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]"); 102 Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result)); 103 result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]"); 104 Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result)); 105 result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]"); 106 Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result)); 107 } 108 109 // Duplicate of fs_mgr_overlayfs_valid() logic 110 // Requires root testConditionsMet()111 public boolean testConditionsMet() throws DeviceNotAvailableException { 112 if (cmdSucceeded(getDevice().executeShellV2Command( 113 "[ -e /sys/module/overlay/parameters/override_creds ]"))) { 114 return true; 115 } 116 if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) { 117 return false; 118 } 119 CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version"); 120 Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*"); 121 Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout()); 122 kernelVersionMatcher.find(); 123 int majorKernelVersion; 124 int minorKernelVersion; 125 try { 126 majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1)); 127 minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2)); 128 } catch (Exception e) { 129 return false; 130 } 131 if (majorKernelVersion < 4) { 132 return true; 133 } 134 if (majorKernelVersion > 4) { 135 return false; 136 } 137 if (minorKernelVersion > 6) { 138 return false; 139 } 140 return true; 141 } 142 143 @After tearDown()144 public void tearDown() throws DeviceNotAvailableException { 145 if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) { 146 getDevice().reboot(); 147 } 148 if (!wasRoot) { 149 getDevice().disableAdbRoot(); 150 } 151 } 152 } 153 154