OSDN Git Service

Consistent creation/destruction of user data.
authorJeff Sharkey <jsharkey@android.com>
Fri, 15 Apr 2016 02:44:58 +0000 (20:44 -0600)
committerJeff Sharkey <jsharkey@android.com>
Fri, 15 Apr 2016 19:35:36 +0000 (13:35 -0600)
Preparing and destroying users currently needs to be split across
installd, system_server, and vold, since no single party has all the
required SELinux permissions.

When preparing user directories on a storage device, always enforce
the serial number and destroy data if we run into a mismatch.  When
deleting a user, write the updated user list first before we start
destroying data.  Also start reconciling users on internal storage
at boot, so we can recover from stale data left behind from partially
destroyed users.

Check both CE and DE user directories when reconciling user storage
on a newly mounted storage device.

Bug: 27896918
Change-Id: I4536c82b0196e2720628c4f73fccb742c233350b

core/java/android/os/FileUtils.java
core/java/android/os/storage/IMountService.java
core/java/android/os/storage/StorageManager.java
services/core/java/com/android/server/MountService.java
services/core/java/com/android/server/pm/Installer.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/pm/UserManagerService.java

index dd73e53..7d74a12 100644 (file)
@@ -437,6 +437,14 @@ public class FileUtils {
         return filePath.startsWith(dirPath);
     }
 
+    public static boolean deleteContentsAndDir(File dir) {
+        if (deleteContents(dir)) {
+            return dir.delete();
+        } else {
+            return false;
+        }
+    }
+
     public static boolean deleteContents(File dir) {
         File[] files = dir.listFiles();
         boolean success = true;
index c21c65a..3915b02 100644 (file)
@@ -1325,6 +1325,24 @@ public interface IMountService extends IInterface {
             }
 
             @Override
+            public void destroyUserStorage(String volumeUuid, int userId, int flags)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(volumeUuid);
+                    _data.writeInt(userId);
+                    _data.writeInt(flags);
+                    mRemote.transact(Stub.TRANSACTION_destroyUserStorage, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
             public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -1465,6 +1483,7 @@ public interface IMountService extends IInterface {
         static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
 
         static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
+        static final int TRANSACTION_destroyUserStorage = IBinder.FIRST_CALL_TRANSACTION + 67;
 
         static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
 
@@ -2096,6 +2115,15 @@ public interface IMountService extends IInterface {
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_destroyUserStorage: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String volumeUuid = data.readString();
+                    int userId = data.readInt();
+                    int _flags = data.readInt();
+                    destroyUserStorage(volumeUuid, userId, _flags);
+                    reply.writeNoException();
+                    return true;
+                }
                 case TRANSACTION_mountAppFuse: {
                     data.enforceInterface(DESCRIPTOR);
                     String name = data.readString();
@@ -2434,6 +2462,7 @@ public interface IMountService extends IInterface {
 
     public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
             int flags) throws RemoteException;
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) throws RemoteException;
 
     public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
 }
index 0a8fdd9..f68e227 100644 (file)
@@ -1053,6 +1053,15 @@ public class StorageManager {
     }
 
     /** {@hide} */
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+        try {
+            mMountService.destroyUserStorage(volumeUuid, userId, flags);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
     public boolean isUserKeyUnlocked(int userId) {
         try {
             return mMountService.isUserKeyUnlocked(userId);
index 229a3f4..ec05dae 100644 (file)
@@ -2875,6 +2875,19 @@ class MountService extends IMountService.Stub
     }
 
     @Override
+    public void destroyUserStorage(String volumeUuid, int userId, int flags) {
+        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+        waitForReady();
+
+        try {
+            mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
+                    userId, flags);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
     public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
         try {
             final int uid = Binder.getCallingUid();
index a11ee74..66c1a53 100644 (file)
@@ -174,12 +174,13 @@ public final class Installer extends SystemService {
         mInstaller.execute("destroy_app_profiles", pkgName);
     }
 
-    public void createUserConfig(int userid) throws InstallerException {
-        mInstaller.execute("mkuserconfig", userid);
+    public void createUserData(String uuid, int userId, int userSerial, int flags)
+            throws InstallerException {
+        mInstaller.execute("create_user_data", uuid, userId, userSerial, flags);
     }
 
-    public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
-        mInstaller.execute("rmuser", uuid, userid);
+    public void destroyUserData(String uuid, int userId, int flags) throws InstallerException {
+        mInstaller.execute("destroy_user_data", uuid, userId, flags);
     }
 
     public void markBootComplete(String instructionSet) throws InstallerException {
index 1fb260d..d02e452 100644 (file)
@@ -109,7 +109,6 @@ import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
 import android.app.backup.IBackupManager;
-import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -17382,6 +17381,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
                 return true;
             }
         });
+
+        // Now that we're mostly running, clean up stale users and apps
+        reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
+        reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
     }
 
     @Override
@@ -18614,14 +18617,100 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
     }
 
     /**
+     * Prepare storage areas for given user on all mounted devices.
+     */
+    void prepareUserData(int userId, int userSerial, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
+            }
+        }
+    }
+
+    private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
+            boolean allowRecover) {
+        // Prepare storage and verify that serial numbers are consistent; if
+        // there's a mismatch we need to destroy to avoid leaking data
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+
+            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
+            }
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
+            }
+
+            synchronized (mInstallLock) {
+                mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
+            }
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
+                    + " because we failed to prepare: " + e);
+            destroyUserDataLI(volumeUuid, userId, flags);
+
+            if (allowRecover) {
+                // Try one last time; if we fail again we're really in trouble
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
+            }
+        }
+    }
+
+    /**
+     * Destroy storage areas for given user on all mounted devices.
+     */
+    void destroyUserData(int userId, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                destroyUserDataLI(volumeUuid, userId, flags);
+            }
+        }
+    }
+
+    private void destroyUserDataLI(String volumeUuid, int userId, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            // Clean up app data, profile data, and media data
+            mInstaller.destroyUserData(volumeUuid, userId, flags);
+
+            // Clean up system data
+            if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
+                }
+                if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
+                }
+            }
+
+            // Data with special labels is now gone, so finish the job
+            storage.destroyUserStorage(volumeUuid, userId, flags);
+
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN,
+                    "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
+        }
+    }
+
+    /**
      * Examine all users present on given mounted volume, and destroy data
      * belonging to users that are no longer valid, or whose user ID has been
      * recycled.
      */
     private void reconcileUsers(String volumeUuid) {
-        // TODO: also reconcile DE directories
-        final File[] files = FileUtils
-                .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
+        final List<File> files = new ArrayList<>();
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
         for (File file : files) {
             if (!file.isDirectory()) continue;
 
@@ -18652,11 +18741,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
 
             if (destroyUser) {
                 synchronized (mInstallLock) {
-                    try {
-                        mInstaller.removeUserDataDirs(volumeUuid, userId);
-                    } catch (InstallerException e) {
-                        Slog.w(TAG, "Failed to clean up user dirs", e);
-                    }
+                    destroyUserDataLI(volumeUuid, userId,
+                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                 }
             }
         }
@@ -19451,21 +19537,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
             mSettings.removeUserLPw(userHandle);
             mPendingBroadcasts.remove(userHandle);
             mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
-        }
-        synchronized (mInstallLock) {
-            final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-                final String volumeUuid = vol.getFsUuid();
-                if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
-                try {
-                    mInstaller.removeUserDataDirs(volumeUuid, userHandle);
-                } catch (InstallerException e) {
-                    Slog.w(TAG, "Failed to remove user data", e);
-                }
-            }
-            synchronized (mPackages) {
-                removeUnusedPackagesLILPw(userManager, userHandle);
-            }
+            removeUnusedPackagesLPw(userManager, userHandle);
         }
     }
 
@@ -19474,7 +19546,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
      * that are no longer in use by any other user.
      * @param userHandle the user being removed
      */
-    private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+    private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
         final boolean DEBUG_CLEAN_APKS = false;
         int [] users = userManager.getUserIds();
         Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
@@ -19524,11 +19596,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
     /** Called by UserManagerService */
     void createNewUser(int userHandle) {
         synchronized (mInstallLock) {
-            try {
-                mInstaller.createUserConfig(userHandle);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to create user config", e);
-            }
             mSettings.createNewUserLI(this, mInstaller, userHandle);
         }
         synchronized (mPackages) {
index 60ea254..f9ef078 100644 (file)
@@ -60,7 +60,6 @@ import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -2024,7 +2023,7 @@ public class UserManagerService extends IUserManager.Stub {
             }
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
-            prepareUserStorage(userId, userInfo.serialNumber,
+            mPm.prepareUserData(userId, userInfo.serialNumber,
                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             mPm.createNewUser(userId);
             userInfo.partial = false;
@@ -2264,9 +2263,9 @@ public class UserManagerService extends IUserManager.Stub {
             Slog.i(LOG_TAG,
                 "Destroying key for user " + userHandle + " failed, continuing anyway", e);
         }
+
         // Cleanup package manager settings
         mPm.cleanUpUser(this, userHandle);
-
         // Remove this user from the list
         synchronized (mUsersLock) {
             mUsers.remove(userHandle);
@@ -2286,24 +2285,12 @@ public class UserManagerService extends IUserManager.Stub {
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
         userFile.delete();
         updateUserIds();
-        File userDir = Environment.getUserSystemDirectory(userHandle);
-        File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
-        if (userDir.renameTo(renamedUserDir)) {
-            removeDirectoryRecursive(renamedUserDir);
-        } else {
-            removeDirectoryRecursive(userDir);
-        }
-    }
 
-    private void removeDirectoryRecursive(File parent) {
-        if (parent.isDirectory()) {
-            String[] files = parent.list();
-            for (String filename : files) {
-                File child = new File(parent, filename);
-                removeDirectoryRecursive(child);
-            }
-        }
-        parent.delete();
+        // Now that we've purged all the metadata above, destroy the actual data
+        // on disk; if we battery pull in here we'll finish cleaning up when
+        // reconciling after reboot.
+        mPm.destroyUserData(userHandle,
+                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
     }
 
     private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
@@ -2598,23 +2585,12 @@ public class UserManagerService extends IUserManager.Stub {
     }
 
     /**
-     * Prepare storage areas for given user on all mounted devices.
-     */
-    private void prepareUserStorage(int userId, int userSerial, int flags) {
-        final StorageManager storage = mContext.getSystemService(StorageManager.class);
-        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-            final String volumeUuid = vol.getFsUuid();
-            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
-        }
-    }
-
-    /**
      * Called right before a user is started. This gives us a chance to prepare
      * app storage and apply any user restrictions.
      */
     public void onBeforeStartUser(int userId) {
         final int userSerial = getUserSerialNumber(userId);
-        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE);
 
         if (userId != UserHandle.USER_SYSTEM) {
@@ -2630,7 +2606,7 @@ public class UserManagerService extends IUserManager.Stub {
      */
     public void onBeforeUnlockUser(@UserIdInt int userId) {
         final int userSerial = getUserSerialNumber(userId);
-        prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
     }