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