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:
Jeff Sharkey
2013-09-20 16:01:28 -07:00
committed by Android Git Automerger
4 changed files with 179 additions and 29 deletions

View File

@@ -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;

View File

@@ -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} */

View File

@@ -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;
}

View File

@@ -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();
}
}