Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.server.pm;
     18 
     19 import android.content.Context;
     20 import android.content.pm.UserInfo;
     21 import android.os.Environment;
     22 import android.os.FileUtils;
     23 import android.os.storage.StorageManager;
     24 import android.os.storage.VolumeInfo;
     25 import android.system.ErrnoException;
     26 import android.system.Os;
     27 import android.system.OsConstants;
     28 import android.util.Log;
     29 import android.util.Slog;
     30 import android.util.SparseArray;
     31 
     32 import com.android.internal.annotations.VisibleForTesting;
     33 
     34 import java.io.File;
     35 import java.io.IOException;
     36 import java.nio.charset.StandardCharsets;
     37 import java.util.ArrayList;
     38 import java.util.Collections;
     39 import java.util.List;
     40 import java.util.Objects;
     41 import java.util.Set;
     42 
     43 import static com.android.server.pm.PackageManagerService.logCriticalInfo;
     44 
     45 /**
     46  * Helper class for preparing and destroying user storage
     47  */
     48 class UserDataPreparer {
     49     private static final String TAG = "UserDataPreparer";
     50     private static final String XATTR_SERIAL = "user.serial";
     51 
     52     private final Object mInstallLock;
     53     private final Context mContext;
     54     private final boolean mOnlyCore;
     55     private final Installer mInstaller;
     56 
     57     UserDataPreparer(Installer installer, Object installLock, Context context, boolean onlyCore) {
     58         mInstallLock = installLock;
     59         mContext = context;
     60         mOnlyCore = onlyCore;
     61         mInstaller = installer;
     62     }
     63 
     64     /**
     65      * Prepare storage areas for given user on all mounted devices.
     66      */
     67     void prepareUserData(int userId, int userSerial, int flags) {
     68         synchronized (mInstallLock) {
     69             final StorageManager storage = mContext.getSystemService(StorageManager.class);
     70             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
     71                 final String volumeUuid = vol.getFsUuid();
     72                 prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
     73             }
     74         }
     75     }
     76 
     77     private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
     78             boolean allowRecover) {
     79         // Prepare storage and verify that serial numbers are consistent; if
     80         // there's a mismatch we need to destroy to avoid leaking data
     81         final StorageManager storage = mContext.getSystemService(StorageManager.class);
     82         try {
     83             storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
     84 
     85             if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && !mOnlyCore) {
     86                 enforceSerialNumber(getDataUserDeDirectory(volumeUuid, userId), userSerial);
     87                 if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
     88                     enforceSerialNumber(getDataSystemDeDirectory(userId), userSerial);
     89                 }
     90             }
     91             if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
     92                 enforceSerialNumber(getDataUserCeDirectory(volumeUuid, userId), userSerial);
     93                 if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
     94                     enforceSerialNumber(getDataSystemCeDirectory(userId), userSerial);
     95                 }
     96             }
     97 
     98             mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
     99         } catch (Exception e) {
    100             logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
    101                     + " because we failed to prepare: " + e);
    102             destroyUserDataLI(volumeUuid, userId,
    103                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
    104 
    105             if (allowRecover) {
    106                 // Try one last time; if we fail again we're really in trouble
    107                 prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
    108             }
    109         }
    110     }
    111 
    112     /**
    113      * Destroy storage areas for given user on all mounted devices.
    114      */
    115     void destroyUserData(int userId, int flags) {
    116         synchronized (mInstallLock) {
    117             final StorageManager storage = mContext.getSystemService(StorageManager.class);
    118             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
    119                 final String volumeUuid = vol.getFsUuid();
    120                 destroyUserDataLI(volumeUuid, userId, flags);
    121             }
    122         }
    123     }
    124 
    125     void destroyUserDataLI(String volumeUuid, int userId, int flags) {
    126         final StorageManager storage = mContext.getSystemService(StorageManager.class);
    127         try {
    128             // Clean up app data, profile data, and media data
    129             mInstaller.destroyUserData(volumeUuid, userId, flags);
    130 
    131             // Clean up system data
    132             if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
    133                 if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
    134                     FileUtils.deleteContentsAndDir(getUserSystemDirectory(userId));
    135                     FileUtils.deleteContentsAndDir(getDataSystemDeDirectory(userId));
    136                     FileUtils.deleteContentsAndDir(getDataMiscDeDirectory(userId));
    137                 }
    138                 if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
    139                     FileUtils.deleteContentsAndDir(getDataSystemCeDirectory(userId));
    140                     FileUtils.deleteContentsAndDir(getDataMiscCeDirectory(userId));
    141                 }
    142             }
    143 
    144             // Data with special labels is now gone, so finish the job
    145             storage.destroyUserStorage(volumeUuid, userId, flags);
    146 
    147         } catch (Exception e) {
    148             logCriticalInfo(Log.WARN,
    149                     "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
    150         }
    151     }
    152 
    153     /**
    154      * Examine all users present on given mounted volume, and destroy data
    155      * belonging to users that are no longer valid, or whose user ID has been
    156      * recycled.
    157      */
    158     void reconcileUsers(String volumeUuid, List<UserInfo> validUsersList) {
    159         final List<File> files = new ArrayList<>();
    160         Collections.addAll(files, FileUtils
    161                 .listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
    162         Collections.addAll(files, FileUtils
    163                 .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
    164         Collections.addAll(files, FileUtils
    165                 .listFilesOrEmpty(Environment.getDataSystemDeDirectory()));
    166         Collections.addAll(files, FileUtils
    167                 .listFilesOrEmpty(Environment.getDataSystemCeDirectory()));
    168         Collections.addAll(files, FileUtils
    169                 .listFilesOrEmpty(Environment.getDataMiscCeDirectory()));
    170         reconcileUsers(volumeUuid, validUsersList, files);
    171     }
    172 
    173     @VisibleForTesting
    174     void reconcileUsers(String volumeUuid, List<UserInfo> validUsersList, List<File> files) {
    175         final int userCount = validUsersList.size();
    176         SparseArray<UserInfo> users = new SparseArray<>(userCount);
    177         for (int i = 0; i < userCount; i++) {
    178             UserInfo user = validUsersList.get(i);
    179             users.put(user.id, user);
    180         }
    181         for (File file : files) {
    182             if (!file.isDirectory()) {
    183                 continue;
    184             }
    185 
    186             final int userId;
    187             final UserInfo info;
    188             try {
    189                 userId = Integer.parseInt(file.getName());
    190                 info = users.get(userId);
    191             } catch (NumberFormatException e) {
    192                 Slog.w(TAG, "Invalid user directory " + file);
    193                 continue;
    194             }
    195 
    196             boolean destroyUser = false;
    197             if (info == null) {
    198                 logCriticalInfo(Log.WARN, "Destroying user directory " + file
    199                         + " because no matching user was found");
    200                 destroyUser = true;
    201             } else if (!mOnlyCore) {
    202                 try {
    203                     enforceSerialNumber(file, info.serialNumber);
    204                 } catch (IOException e) {
    205                     logCriticalInfo(Log.WARN, "Destroying user directory " + file
    206                             + " because we failed to enforce serial number: " + e);
    207                     destroyUser = true;
    208                 }
    209             }
    210 
    211             if (destroyUser) {
    212                 synchronized (mInstallLock) {
    213                     destroyUserDataLI(volumeUuid, userId,
    214                             StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
    215                 }
    216             }
    217         }
    218     }
    219 
    220     @VisibleForTesting
    221     protected File getDataMiscCeDirectory(int userId) {
    222         return Environment.getDataMiscCeDirectory(userId);
    223     }
    224 
    225     @VisibleForTesting
    226     protected File getDataSystemCeDirectory(int userId) {
    227         return Environment.getDataSystemCeDirectory(userId);
    228     }
    229 
    230     @VisibleForTesting
    231     protected File getDataMiscDeDirectory(int userId) {
    232         return Environment.getDataMiscDeDirectory(userId);
    233     }
    234 
    235     @VisibleForTesting
    236     protected File getUserSystemDirectory(int userId) {
    237         return Environment.getUserSystemDirectory(userId);
    238     }
    239 
    240     @VisibleForTesting
    241     protected File getDataUserCeDirectory(String volumeUuid, int userId) {
    242         return Environment.getDataUserCeDirectory(volumeUuid, userId);
    243     }
    244 
    245     @VisibleForTesting
    246     protected File getDataSystemDeDirectory(int userId) {
    247         return Environment.getDataSystemDeDirectory(userId);
    248     }
    249 
    250     @VisibleForTesting
    251     protected File getDataUserDeDirectory(String volumeUuid, int userId) {
    252         return Environment.getDataUserDeDirectory(volumeUuid, userId);
    253     }
    254 
    255     @VisibleForTesting
    256     protected boolean isFileEncryptedEmulatedOnly() {
    257         return StorageManager.isFileEncryptedEmulatedOnly();
    258     }
    259 
    260     /**
    261      * Enforce that serial number stored in user directory inode matches the
    262      * given expected value. Gracefully sets the serial number if currently
    263      * undefined.
    264      *
    265      * @throws IOException when problem extracting serial number, or serial
    266      *             number is mismatched.
    267      */
    268     void enforceSerialNumber(File file, int serialNumber) throws IOException {
    269         if (isFileEncryptedEmulatedOnly()) {
    270             // When we're emulating FBE, the directory may have been chmod
    271             // 000'ed, meaning we can't read the serial number to enforce it;
    272             // instead of destroying the user, just log a warning.
    273             Slog.w(TAG, "Device is emulating FBE; assuming current serial number is valid");
    274             return;
    275         }
    276 
    277         final int foundSerial = getSerialNumber(file);
    278         Slog.v(TAG, "Found " + file + " with serial number " + foundSerial);
    279 
    280         if (foundSerial == -1) {
    281             Slog.d(TAG, "Serial number missing on " + file + "; assuming current is valid");
    282             try {
    283                 setSerialNumber(file, serialNumber);
    284             } catch (IOException e) {
    285                 Slog.w(TAG, "Failed to set serial number on " + file, e);
    286             }
    287 
    288         } else if (foundSerial != serialNumber) {
    289             throw new IOException("Found serial number " + foundSerial
    290                     + " doesn't match expected " + serialNumber);
    291         }
    292     }
    293 
    294     /**
    295      * Set serial number stored in user directory inode.
    296      *
    297      * @throws IOException if serial number was already set
    298      */
    299     private static void setSerialNumber(File file, int serialNumber) throws IOException {
    300         try {
    301             final byte[] buf = Integer.toString(serialNumber).getBytes(StandardCharsets.UTF_8);
    302             Os.setxattr(file.getAbsolutePath(), XATTR_SERIAL, buf, OsConstants.XATTR_CREATE);
    303         } catch (ErrnoException e) {
    304             throw e.rethrowAsIOException();
    305         }
    306     }
    307 
    308     /**
    309      * Return serial number stored in user directory inode.
    310      *
    311      * @return parsed serial number, or -1 if not set
    312      */
    313     @VisibleForTesting
    314     static int getSerialNumber(File file) throws IOException {
    315         try {
    316             final byte[] buf = Os.getxattr(file.getAbsolutePath(), XATTR_SERIAL);
    317             final String serial = new String(buf);
    318             try {
    319                 return Integer.parseInt(serial);
    320             } catch (NumberFormatException e) {
    321                 throw new IOException("Bad serial number: " + serial);
    322             }
    323         } catch (ErrnoException e) {
    324             if (e.errno == OsConstants.ENODATA) {
    325                 return -1;
    326             } else {
    327                 throw e.rethrowAsIOException();
    328             }
    329         }
    330     }
    331 
    332 }
    333