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