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