am dfed2448: Merge "Delegate mkdirs() to vold when lacking perms." into klp-dev
* commit 'dfed244876d6c0ddd8d8bd2b8f3b970cd0a1ab95': Delegate mkdirs() to vold when lacking perms.
This commit is contained in:
@@ -92,6 +92,7 @@ import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.SystemVibrator;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.IMountService;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.print.IPrintManager;
|
||||
import android.print.PrintManager;
|
||||
@@ -864,7 +865,9 @@ class ContextImpl extends Context {
|
||||
if (mExternalObbDirs == null) {
|
||||
mExternalObbDirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
|
||||
}
|
||||
return mExternalObbDirs;
|
||||
|
||||
// Create dirs if needed
|
||||
return ensureDirsExistOrFilter(mExternalObbDirs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2127,14 +2130,25 @@ class ContextImpl extends Context {
|
||||
* Ensure that given directories exist, trying to create them if missing. If
|
||||
* unable to create, they are filtered by replacing with {@code null}.
|
||||
*/
|
||||
private static File[] ensureDirsExistOrFilter(File[] dirs) {
|
||||
private File[] ensureDirsExistOrFilter(File[] dirs) {
|
||||
File[] result = new File[dirs.length];
|
||||
for (int i = 0; i < dirs.length; i++) {
|
||||
File dir = dirs[i];
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) {
|
||||
Log.w(TAG, "Failed to ensure directory: " + dir);
|
||||
dir = null;
|
||||
// Failing to mkdir() may be okay, since we might not have
|
||||
// enough permissions; ask vold to create on our behalf.
|
||||
final IMountService mount = IMountService.Stub.asInterface(
|
||||
ServiceManager.getService("mount"));
|
||||
int res = -1;
|
||||
try {
|
||||
res = mount.mkdirs(getPackageName(), dir.getAbsolutePath());
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
if (res != 0) {
|
||||
Log.w(TAG, "Failed to ensure directory: " + dir);
|
||||
dir = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
result[i] = dir;
|
||||
|
||||
@@ -109,29 +109,36 @@ public class Environment {
|
||||
// TODO: generalize further to create package-specific environment
|
||||
// TODO: add support for secondary external storage
|
||||
|
||||
private final File[] mExternalDirs;
|
||||
private final File mMediaDir;
|
||||
/** External storage dirs, as visible to vold */
|
||||
private final File[] mExternalDirsForVold;
|
||||
/** External storage dirs, as visible to apps */
|
||||
private final File[] mExternalDirsForApp;
|
||||
/** Primary emulated storage dir for direct access */
|
||||
private final File mEmulatedDirForDirect;
|
||||
|
||||
public UserEnvironment(int userId) {
|
||||
// See storage config details at http://source.android.com/tech/storage/
|
||||
String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
|
||||
String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
|
||||
String rawEmulatedSource = System.getenv(ENV_EMULATED_STORAGE_SOURCE);
|
||||
String rawEmulatedTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
|
||||
String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
|
||||
if (TextUtils.isEmpty(rawMediaStorage)) {
|
||||
rawMediaStorage = "/data/media";
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) {
|
||||
if (!TextUtils.isEmpty(rawEmulatedTarget)) {
|
||||
// Device has emulated storage; external storage paths should have
|
||||
// userId burned into them.
|
||||
final String rawUserId = Integer.toString(userId);
|
||||
final File emulatedBase = new File(rawEmulatedStorageTarget);
|
||||
final File emulatedSourceBase = new File(rawEmulatedSource);
|
||||
final File emulatedTargetBase = new File(rawEmulatedTarget);
|
||||
final File mediaBase = new File(rawMediaStorage);
|
||||
|
||||
// /storage/emulated/0
|
||||
mExternalDirs = new File[] { buildPath(emulatedBase, rawUserId) };
|
||||
mExternalDirsForVold = new File[] { buildPath(emulatedSourceBase, rawUserId) };
|
||||
mExternalDirsForApp = new File[] { buildPath(emulatedTargetBase, rawUserId) };
|
||||
// /data/media/0
|
||||
mMediaDir = buildPath(mediaBase, rawUserId);
|
||||
mEmulatedDirForDirect = buildPath(mediaBase, rawUserId);
|
||||
|
||||
} else {
|
||||
// Device has physical external storage; use plain paths.
|
||||
@@ -141,15 +148,16 @@ public class Environment {
|
||||
}
|
||||
|
||||
// /storage/sdcard0
|
||||
mExternalDirs = new File[] { new File(rawExternalStorage) };
|
||||
mExternalDirsForVold = new File[] { new File(rawExternalStorage) };
|
||||
mExternalDirsForApp = new File[] { new File(rawExternalStorage) };
|
||||
// /data/media
|
||||
mMediaDir = new File(rawMediaStorage);
|
||||
mEmulatedDirForDirect = new File(rawMediaStorage);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public File getExternalStorageDirectory() {
|
||||
return mExternalDirs[0];
|
||||
return mExternalDirsForApp[0];
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@@ -157,44 +165,56 @@ public class Environment {
|
||||
return buildExternalStoragePublicDirs(type)[0];
|
||||
}
|
||||
|
||||
public File[] getExternalDirs() {
|
||||
return mExternalDirs;
|
||||
public File[] getExternalDirsForVold() {
|
||||
return mExternalDirsForVold;
|
||||
}
|
||||
|
||||
public File[] getExternalDirsForApp() {
|
||||
return mExternalDirsForApp;
|
||||
}
|
||||
|
||||
public File getMediaDir() {
|
||||
return mMediaDir;
|
||||
return mEmulatedDirForDirect;
|
||||
}
|
||||
|
||||
public File[] buildExternalStoragePublicDirs(String type) {
|
||||
return buildPaths(mExternalDirs, type);
|
||||
return buildPaths(mExternalDirsForApp, type);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAndroidDataDirs() {
|
||||
return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA);
|
||||
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAndroidObbDirs() {
|
||||
return buildPaths(mExternalDirs, DIR_ANDROID, DIR_OBB);
|
||||
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAppDataDirs(String packageName) {
|
||||
return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA, packageName);
|
||||
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAppDataDirsForVold(String packageName) {
|
||||
return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_DATA, packageName);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAppMediaDirs(String packageName) {
|
||||
return buildPaths(mExternalDirs, DIR_ANDROID, DIR_MEDIA, packageName);
|
||||
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAppObbDirs(String packageName) {
|
||||
return buildPaths(mExternalDirs, DIR_ANDROID, DIR_OBB, packageName);
|
||||
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAppObbDirsForVold(String packageName) {
|
||||
return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_OBB, packageName);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAppFilesDirs(String packageName) {
|
||||
return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
|
||||
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
|
||||
}
|
||||
|
||||
public File[] buildExternalStorageAppCacheDirs(String packageName) {
|
||||
return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
|
||||
return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +364,7 @@ public class Environment {
|
||||
*/
|
||||
public static File getExternalStorageDirectory() {
|
||||
throwIfUserRequired();
|
||||
return sCurrentUser.getExternalDirs()[0];
|
||||
return sCurrentUser.getExternalDirsForApp()[0];
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
|
||||
@@ -20,9 +20,7 @@ import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.storage.StorageVolume;
|
||||
|
||||
/**
|
||||
* WARNING! Update IMountService.h and IMountService.cpp if you change this
|
||||
@@ -737,7 +735,25 @@ public interface IMountService extends IInterface {
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mkdirs(String callingPkg, String path) throws RemoteException {
|
||||
Parcel _data = Parcel.obtain();
|
||||
Parcel _reply = Parcel.obtain();
|
||||
int _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeString(callingPkg);
|
||||
_data.writeString(path);
|
||||
mRemote.transact(Stub.TRANSACTION_mkdirs, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
_result = _reply.readInt();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,6 +827,8 @@ public interface IMountService extends IInterface {
|
||||
|
||||
static final int TRANSACTION_fixPermissionsSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 33;
|
||||
|
||||
static final int TRANSACTION_mkdirs = IBinder.FIRST_CALL_TRANSACTION + 34;
|
||||
|
||||
/**
|
||||
* Cast an IBinder object into an IMountService interface, generating a
|
||||
* proxy if needed.
|
||||
@@ -1154,6 +1172,15 @@ public interface IMountService extends IInterface {
|
||||
reply.writeInt(resultCode);
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_mkdirs: {
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
String callingPkg = data.readString();
|
||||
String path = data.readString();
|
||||
int result = mkdirs(callingPkg, path);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
@@ -1376,4 +1403,13 @@ public interface IMountService extends IInterface {
|
||||
*/
|
||||
public int fixPermissionsSecureContainer(String id, int gid, String filename)
|
||||
throws RemoteException;
|
||||
|
||||
/**
|
||||
* Ensure that all directories along given path exist, creating parent
|
||||
* directories as needed. Validates that given path is absolute and that it
|
||||
* contains no relative "." or ".." paths or symlinks. Also ensures that
|
||||
* path belongs to a volume managed by vold, and that path is either
|
||||
* external storage data or OBB directory belonging to calling app.
|
||||
*/
|
||||
public int mkdirs(String callingPkg, String path) throws RemoteException;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -2127,6 +2128,85 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mkdirs(String callingPkg, String appPath) {
|
||||
final int userId = UserHandle.getUserId(Binder.getCallingUid());
|
||||
final UserEnvironment userEnv = new UserEnvironment(userId);
|
||||
|
||||
// Validate that reported package name belongs to caller
|
||||
final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
|
||||
Context.APP_OPS_SERVICE);
|
||||
appOps.checkPackage(Binder.getCallingUid(), callingPkg);
|
||||
|
||||
try {
|
||||
appPath = new File(appPath).getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Try translating the app path into a vold path, but require that it
|
||||
// belong to the calling package.
|
||||
String voldPath = maybeTranslatePathForVold(appPath,
|
||||
userEnv.buildExternalStorageAppDataDirs(callingPkg),
|
||||
userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
|
||||
if (voldPath != null) {
|
||||
try {
|
||||
mConnector.execute("volume", "mkdirs", voldPath);
|
||||
return 0;
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
return e.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
voldPath = maybeTranslatePathForVold(appPath,
|
||||
userEnv.buildExternalStorageAppObbDirs(callingPkg),
|
||||
userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
|
||||
if (voldPath != null) {
|
||||
try {
|
||||
mConnector.execute("volume", "mkdirs", voldPath);
|
||||
return 0;
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
return e.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
throw new SecurityException("Invalid mkdirs path: " + appPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the given path from an app-visible path to a vold-visible path,
|
||||
* but only if it's under the given whitelisted paths.
|
||||
*
|
||||
* @param path a canonicalized app-visible path.
|
||||
* @param appPaths list of app-visible paths that are allowed.
|
||||
* @param voldPaths list of vold-visible paths directly corresponding to the
|
||||
* allowed app-visible paths argument.
|
||||
* @return a vold-visible path representing the original path, or
|
||||
* {@code null} if the given path didn't have an app-to-vold
|
||||
* mapping.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static String maybeTranslatePathForVold(
|
||||
String path, File[] appPaths, File[] voldPaths) {
|
||||
if (appPaths.length != voldPaths.length) {
|
||||
throw new IllegalStateException("Paths must be 1:1 mapping");
|
||||
}
|
||||
|
||||
for (int i = 0; i < appPaths.length; i++) {
|
||||
final String appPath = appPaths[i].getAbsolutePath();
|
||||
if (path.startsWith(appPath)) {
|
||||
path = new File(voldPaths[i], path.substring(appPath.length() + 1))
|
||||
.getAbsolutePath();
|
||||
if (!path.endsWith("/")) {
|
||||
path = path + "/";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageVolume[] getVolumeList() {
|
||||
final int callingUserId = UserHandle.getCallingUserId();
|
||||
@@ -2651,7 +2731,7 @@ class MountService extends IMountService.Stub
|
||||
if (forVold) {
|
||||
return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
|
||||
} else {
|
||||
return new File(userEnv.getExternalDirs()[0], path).getAbsolutePath();
|
||||
return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user