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