diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-09-17 17:30:33 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-09-20 14:34:02 -0700 |
commit | 2d8b4e801332e02d6aad615b85cc9dd056ef805c (patch) | |
tree | 8100d77b8cef8d1b11ed366006b1035706640400 /services/java/com/android/server/MountService.java | |
parent | 6df7d4a574ffd85c82cad402552e3854df3a3f85 (diff) |
Delegate mkdirs() to vold when lacking perms.
Apps without sdcard_r or sdcard_rw are still able to write to
their package-specific directory, but someone needs to first make
that directory on their behalf. This change will delegate the
mkdirs() call through to vold when an app fails to create directly.
MountService validates that the path belongs to the calling user, and
that it's actually on external storage, before passing to vold.
Update Environment to make app-vs-vold paths clearer.
Bug: 10577808
Change-Id: I43b4a77fd6d2b9af2a0d899790da8d9d89386776
Diffstat (limited to 'services/java/com/android/server/MountService.java')
-rw-r--r-- | services/java/com/android/server/MountService.java | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 1facb8093625..6ab86f5a3c4e 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -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; @@ -2128,6 +2129,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(); final boolean accessAll = (mContext.checkPermission( @@ -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(); } } |