blob: f08cab23977399753abeedfcdb4a0020f2f42d8e [file] [log] [blame]
/*
* 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();
}
}
}