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