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