Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 578f67c2 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android Git Automerger
Browse files

am dfed2448: Merge "Delegate mkdirs() to vold when lacking perms." into klp-dev

* commit 'dfed2448':
  Delegate mkdirs() to vold when lacking perms.
parents dd47d78a dfed2448
Loading
Loading
Loading
Loading
+18 −4
Original line number Diff line number Diff line
@@ -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,16 +2130,27 @@ 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()) {
                    // 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;
        }
        return result;
+42 −22
Original line number Diff line number Diff line
@@ -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} */
+38 −2
Original line number Diff line number Diff line
@@ -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;
}
+81 −1
Original line number Diff line number Diff line
@@ -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();
        }
    }