Bind mount obb directory in emulated storage.

- Create obb directories for packages when zygote starts.

- During zygote fork, bind all visible obb package directories
for an app.

- If app data isolation is not enabled for that process, bind
mount the whole obb directory instead.

Bug: 148049767
Bug: 137890172
Test: atest android.appsecurity.cts.AdoptableHostTest
Change-Id: I841e98c120d447b148c22cfd809d738427b04f2e
This commit is contained in:
Ricky Wai
2020-01-22 17:52:52 +00:00
parent cd1ca07b3e
commit e5088d92df
4 changed files with 166 additions and 3 deletions

View File

@@ -19,6 +19,8 @@ package android.os.storage;
import android.annotation.Nullable;
import android.os.IVold;
import java.util.Set;
/**
* Mount service local interface.
*
@@ -96,6 +98,12 @@ public abstract class StorageManagerInternal {
void onReset(IVold vold);
}
/**
* Check if fuse is running in target user, if it's running then setup its obb directories.
* TODO: System server should store a list of active pids that obb is not mounted and use it.
*/
public abstract void prepareObbDirs(int userId, Set<String> packageList, String processName);
/**
* Add a listener to listen to reset event in StorageManagerService.
*

View File

@@ -105,10 +105,12 @@ namespace {
using namespace std::placeholders;
using android::String8;
using android::base::ReadFileToString;
using android::base::StringAppendF;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::base::GetBoolProperty;
using android::base::GetProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
@@ -174,6 +176,12 @@ static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751;
static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.zygote.app_data_isolation";
/**
* Property to enable app data isolation for sdcard obb or data in vold.
*/
static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
@@ -603,6 +611,15 @@ static void EnableDebugger() {
}
}
static bool IsFilesystemSupported(const std::string& fsType) {
std::string supported;
if (!ReadFileToString("/proc/filesystems", &supported)) {
ALOGE("Failed to read supported filesystems");
return false;
}
return supported.find(fsType + "\n") != std::string::npos;
}
static void PreApplicationInit() {
// The child process sets this to indicate it's not the zygote.
android_mallopt(M_SET_ZYGOTE_CHILD, nullptr, 0);
@@ -782,6 +799,31 @@ static void MountAppDataTmpFs(const std::string& target_dir,
}
}
static void BindMountObbPackage(std::string_view package_name, int userId, fail_fn_t fail_fn) {
// TODO(148772775): Pass primary volume name from zygote argument to here
std::string source;
if (IsFilesystemSupported("sdcardfs")) {
source = StringPrintf("/mnt/runtime/default/emulated/%d/Android/obb/%s",
userId, package_name.data());
} else {
source = StringPrintf("/mnt/pass_through/%d/emulated/%d/Android/obb/%s",
userId, userId, package_name.data());
}
std::string target(
StringPrintf("/storage/emulated/%d/Android/obb/%s", userId, package_name.data()));
if (access(source.c_str(), F_OK) != 0) {
fail_fn(CREATE_ERROR("Cannot access source %s: %s", source.c_str(), strerror(errno)));
}
if (access(target.c_str(), F_OK) != 0) {
fail_fn(CREATE_ERROR("Cannot access target %s: %s", target.c_str(), strerror(errno)));
}
BindMount(source, target, fail_fn);
}
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static void MountEmulatedStorage(uid_t uid, jint mount_mode,
@@ -1504,6 +1546,60 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list,
}
}
// Bind mount all obb directories that are visible to this app.
// If app data isolation is not enabled for this process, bind mount the whole obb
// directory instead.
static void BindMountAppObbDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
uid_t uid, const char* process_name, jstring managed_nice_name, fail_fn_t fail_fn) {
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
const userid_t user_id = multiuser_get_user_id(uid);
// If FUSE is not ready for this user, skip it
// TODO(148772775): Pass primary volume name from zygote argument to here
std::string tmp = GetProperty("vold.fuse_running_users", "");
std::istringstream fuse_running_users(tmp);
bool user_found = false;
std::string s;
std::string user_id_str = std::to_string(user_id);
while (!user_found && std::getline(fuse_running_users, s, ',')) {
if (user_id_str == s) {
user_found = true;
}
}
if (!user_found) {
ALOGI("User %d is not running fuse yet, fuse_running_users=%s", user_id, tmp.c_str());
return;
}
// Fuse is ready, so we can start using fuse path.
int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
if (size == 0) {
// App data isolation is not enabled for this process, so we bind mount to whole obb/ dir.
std::string source;
if (IsFilesystemSupported("sdcardfs")) {
source = StringPrintf("/mnt/runtime/default/emulated/%d/Android/obb", user_id);
} else {
source = StringPrintf("/mnt/pass_through/%d/emulated/%d/Android/obb", user_id, user_id);
}
std::string target(StringPrintf("/storage/emulated/%d/Android/obb", user_id));
if (access(source.c_str(), F_OK) != 0) {
fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno)));
}
BindMount(source, target, fail_fn);
return;
}
// Bind mount each package obb directory
for (int i = 0; i < size; i += 3) {
jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
std::string packageName = extract_fn(package_str).value();
BindMountObbPackage(packageName, user_id, fail_fn);
}
}
// Utility routine to specialize a zygote child process.
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
@@ -1552,6 +1648,12 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
if ((mount_external != MOUNT_EXTERNAL_INSTALLER) &&
GetBoolProperty(kPropFuse, false) &&
GetBoolProperty(ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
BindMountAppObbDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
if (!is_system_server && getuid() == 0) {

View File

@@ -2120,8 +2120,6 @@ class StorageManagerService extends IStorageManager.Stub
private void unmount(VolumeInfo vol) {
try {
mVold.unmount(vol.id);
mStorageSessionController.onVolumeUnmount(vol);
try {
if (vol.type == VolumeInfo.TYPE_PRIVATE) {
mInstaller.onPrivateVolumeRemoved(vol.getFsUuid());
@@ -2129,6 +2127,8 @@ class StorageManagerService extends IStorageManager.Stub
} catch (Installer.InstallerException e) {
Slog.e(TAG, "Failed unmount mirror data", e);
}
mVold.unmount(vol.id);
mStorageSessionController.onVolumeUnmount(vol);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -4346,6 +4346,42 @@ class StorageManagerService extends IStorageManager.Stub
mPolicies.add(policy);
}
/**
* Check if fuse is running in target user, if it's running then setup its obb directories.
* TODO: System server should store a list of active pids that obb is not mounted and use it.
*/
@Override
public void prepareObbDirs(int userId, Set<String> packageList, String processName) {
String fuseRunningUsersList = SystemProperties.get("vold.fuse_running_users", "");
String[] fuseRunningUsers = fuseRunningUsersList.split(",");
boolean fuseReady = false;
String targetUserId = String.valueOf(userId);
for (String user : fuseRunningUsers) {
if (targetUserId.equals(user)) {
fuseReady = true;
}
}
if (fuseReady) {
try {
final IVold vold = IVold.Stub.asInterface(
ServiceManager.getServiceOrThrow("vold"));
for (String pkg : packageList) {
final String obbDir =
String.format("/storage/emulated/%d/Android/obb", userId);
final String packageObbDir = String.format("%s/%s/", obbDir, pkg);
// Create package obb dir if it doesn't exist.
File file = new File(packageObbDir);
if (!file.exists()) {
vold.setupAppDir(packageObbDir, mPmInternal.getPackage(pkg).getUid());
}
}
} catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
Slog.e(TAG, "Unable to create obb directories for " + processName, e);
}
}
}
@Override
public void onExternalStoragePolicyChanged(int uid, String packageName) {
final int mountMode = getExternalStorageMountMode(uid, packageName);

View File

@@ -80,11 +80,13 @@ import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.IVold;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -142,10 +144,14 @@ import java.util.Map;
public final class ProcessList {
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
// A device config to control the minimum target SDK to enable app data isolation
// A system property to control if app data isolation is enabled.
static final String ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.zygote.app_data_isolation";
// A system property to control if obb app data isolation is enabled in vold.
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
// A device config to control the minimum target SDK to enable app data isolation
static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK = "android_app_data_isolation_min_sdk";
@@ -379,6 +385,8 @@ public final class ProcessList {
private boolean mAppDataIsolationEnabled = false;
private boolean mVoldAppDataIsolationEnabled = false;
private ArrayList<String> mAppDataIsolationWhitelistedApps;
/**
@@ -691,6 +699,8 @@ public final class ProcessList {
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mAppDataIsolationWhitelistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
@@ -2113,6 +2123,13 @@ public final class ProcessList {
app.info.packageName, app.userId);
pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
? new String[]{app.info.packageName} : sharedPackages, uid);
if (mVoldAppDataIsolationEnabled) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
storageManagerInternal.prepareObbDirs(UserHandle.getUserId(uid),
pkgDataInfoMap.keySet(), app.processName);
}
} else {
pkgDataInfoMap = null;
}