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.backup.utils; 18 19 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME; 20 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION; 21 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_MANIFEST_PACKAGE_NAME; 22 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_OLD_VERSION; 23 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_POLICY_ALLOW_APKS; 24 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT; 25 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY; 26 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED; 27 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK; 28 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE; 29 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE; 30 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH; 31 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE; 32 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION; 33 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT; 34 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH; 35 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER; 36 37 import static com.android.server.backup.BackupManagerService.DEBUG; 38 import static com.android.server.backup.BackupManagerService.MORE_DEBUG; 39 import static com.android.server.backup.BackupManagerService.TAG; 40 import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME; 41 import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION; 42 import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME; 43 import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN; 44 import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; 45 46 import android.app.backup.BackupAgent; 47 import android.app.backup.BackupManagerMonitor; 48 import android.app.backup.FullBackup; 49 import android.app.backup.IBackupManagerMonitor; 50 import android.content.pm.ApplicationInfo; 51 import android.content.pm.PackageInfo; 52 import android.content.pm.PackageManager; 53 import android.content.pm.PackageManagerInternal; 54 import android.content.pm.Signature; 55 import android.os.Bundle; 56 import android.os.UserHandle; 57 import android.util.Slog; 58 59 import com.android.server.backup.FileMetadata; 60 import com.android.server.backup.restore.RestorePolicy; 61 62 import java.io.ByteArrayInputStream; 63 import java.io.DataInputStream; 64 import java.io.IOException; 65 import java.io.InputStream; 66 67 /** 68 * Utility methods to read backup tar file. 69 */ 70 public class TarBackupReader { 71 private static final int TAR_HEADER_OFFSET_TYPE_CHAR = 156; 72 private static final int TAR_HEADER_LENGTH_PATH = 100; 73 private static final int TAR_HEADER_OFFSET_PATH = 0; 74 private static final int TAR_HEADER_LENGTH_PATH_PREFIX = 155; 75 private static final int TAR_HEADER_OFFSET_PATH_PREFIX = 345; 76 private static final int TAR_HEADER_LENGTH_MODE = 8; 77 private static final int TAR_HEADER_OFFSET_MODE = 100; 78 private static final int TAR_HEADER_LENGTH_MODTIME = 12; 79 private static final int TAR_HEADER_OFFSET_MODTIME = 136; 80 private static final int TAR_HEADER_LENGTH_FILESIZE = 12; 81 private static final int TAR_HEADER_OFFSET_FILESIZE = 124; 82 private static final int TAR_HEADER_LONG_RADIX = 8; 83 84 private final InputStream mInputStream; 85 private final BytesReadListener mBytesReadListener; 86 87 private IBackupManagerMonitor mMonitor; 88 89 // Widget blob to be restored out-of-band. 90 private byte[] mWidgetData = null; 91 TarBackupReader(InputStream inputStream, BytesReadListener bytesReadListener, IBackupManagerMonitor monitor)92 public TarBackupReader(InputStream inputStream, BytesReadListener bytesReadListener, 93 IBackupManagerMonitor monitor) { 94 mInputStream = inputStream; 95 mBytesReadListener = bytesReadListener; 96 mMonitor = monitor; 97 } 98 99 /** 100 * Consumes a tar file header block [sequence] and accumulates the relevant metadata. 101 */ readTarHeaders()102 public FileMetadata readTarHeaders() throws IOException { 103 byte[] block = new byte[512]; 104 FileMetadata info = null; 105 106 boolean gotHeader = readTarHeader(block); 107 if (gotHeader) { 108 try { 109 // okay, presume we're okay, and extract the various metadata 110 info = new FileMetadata(); 111 info.size = extractRadix(block, 112 TAR_HEADER_OFFSET_FILESIZE, 113 TAR_HEADER_LENGTH_FILESIZE, 114 TAR_HEADER_LONG_RADIX); 115 info.mtime = extractRadix(block, 116 TAR_HEADER_OFFSET_MODTIME, 117 TAR_HEADER_LENGTH_MODTIME, 118 TAR_HEADER_LONG_RADIX); 119 info.mode = extractRadix(block, 120 TAR_HEADER_OFFSET_MODE, 121 TAR_HEADER_LENGTH_MODE, 122 TAR_HEADER_LONG_RADIX); 123 124 info.path = extractString(block, 125 TAR_HEADER_OFFSET_PATH_PREFIX, 126 TAR_HEADER_LENGTH_PATH_PREFIX); 127 String path = extractString(block, 128 TAR_HEADER_OFFSET_PATH, 129 TAR_HEADER_LENGTH_PATH); 130 if (path.length() > 0) { 131 if (info.path.length() > 0) { 132 info.path += '/'; 133 } 134 info.path += path; 135 } 136 137 // tar link indicator field: 1 byte at offset 156 in the header. 138 int typeChar = block[TAR_HEADER_OFFSET_TYPE_CHAR]; 139 if (typeChar == 'x') { 140 // pax extended header, so we need to read that 141 gotHeader = readPaxExtendedHeader(info); 142 if (gotHeader) { 143 // and after a pax extended header comes another real header -- read 144 // that to find the real file type 145 gotHeader = readTarHeader(block); 146 } 147 if (!gotHeader) { 148 throw new IOException("Bad or missing pax header"); 149 } 150 151 typeChar = block[TAR_HEADER_OFFSET_TYPE_CHAR]; 152 } 153 154 switch (typeChar) { 155 case '0': 156 info.type = BackupAgent.TYPE_FILE; 157 break; 158 case '5': { 159 info.type = BackupAgent.TYPE_DIRECTORY; 160 if (info.size != 0) { 161 Slog.w(TAG, "Directory entry with nonzero size in header"); 162 info.size = 0; 163 } 164 break; 165 } 166 case 0: { 167 // presume EOF 168 if (MORE_DEBUG) { 169 Slog.w(TAG, "Saw type=0 in tar header block, info=" + info); 170 } 171 return null; 172 } 173 default: { 174 Slog.e(TAG, "Unknown tar entity type: " + typeChar); 175 throw new IOException("Unknown entity type " + typeChar); 176 } 177 } 178 179 // Parse out the path 180 // 181 // first: apps/shared/unrecognized 182 if (FullBackup.SHARED_PREFIX.regionMatches(0, 183 info.path, 0, FullBackup.SHARED_PREFIX.length())) { 184 // File in shared storage. !!! TODO: implement this. 185 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); 186 info.packageName = SHARED_BACKUP_AGENT_PACKAGE; 187 info.domain = FullBackup.SHARED_STORAGE_TOKEN; 188 if (DEBUG) { 189 Slog.i(TAG, "File in shared storage: " + info.path); 190 } 191 } else if (FullBackup.APPS_PREFIX.regionMatches(0, 192 info.path, 0, FullBackup.APPS_PREFIX.length())) { 193 // App content! Parse out the package name and domain 194 195 // strip the apps/ prefix 196 info.path = info.path.substring(FullBackup.APPS_PREFIX.length()); 197 198 // extract the package name 199 int slash = info.path.indexOf('/'); 200 if (slash < 0) { 201 throw new IOException("Illegal semantic path in " + info.path); 202 } 203 info.packageName = info.path.substring(0, slash); 204 info.path = info.path.substring(slash + 1); 205 206 // if it's a manifest or metadata payload we're done, otherwise parse 207 // out the domain into which the file will be restored 208 if (!info.path.equals(BACKUP_MANIFEST_FILENAME) && 209 !info.path.equals(BACKUP_METADATA_FILENAME)) { 210 slash = info.path.indexOf('/'); 211 if (slash < 0) { 212 throw new IOException("Illegal semantic path in non-manifest " 213 + info.path); 214 } 215 info.domain = info.path.substring(0, slash); 216 info.path = info.path.substring(slash + 1); 217 } 218 } 219 } catch (IOException e) { 220 if (DEBUG) { 221 Slog.e(TAG, "Parse error in header: " + e.getMessage()); 222 if (MORE_DEBUG) { 223 hexLog(block); 224 } 225 } 226 throw e; 227 } 228 } 229 return info; 230 } 231 232 /** 233 * Tries to read exactly the given number of bytes into a buffer at the stated offset. 234 * 235 * @param in - input stream to read bytes from.. 236 * @param buffer - where to write bytes to. 237 * @param offset - offset in buffer to write bytes to. 238 * @param size - number of bytes to read. 239 * @return number of bytes actually read. 240 * @throws IOException in case of an error. 241 */ readExactly(InputStream in, byte[] buffer, int offset, int size)242 private static int readExactly(InputStream in, byte[] buffer, int offset, int size) 243 throws IOException { 244 if (size <= 0) { 245 throw new IllegalArgumentException("size must be > 0"); 246 } 247 if (MORE_DEBUG) { 248 Slog.i(TAG, " ... readExactly(" + size + ") called"); 249 } 250 int soFar = 0; 251 while (soFar < size) { 252 int nRead = in.read(buffer, offset + soFar, size - soFar); 253 if (nRead <= 0) { 254 if (MORE_DEBUG) { 255 Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar); 256 } 257 break; 258 } 259 soFar += nRead; 260 if (MORE_DEBUG) { 261 Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soFar)); 262 } 263 } 264 return soFar; 265 } 266 267 /** 268 * Reads app manifest, filling version and hasApk fields in the metadata, and returns array of 269 * signatures. 270 * 271 * @param info - file metadata. 272 * @return array of signatures or null, in case of an error. 273 * @throws IOException in case of an error. 274 */ readAppManifestAndReturnSignatures(FileMetadata info)275 public Signature[] readAppManifestAndReturnSignatures(FileMetadata info) 276 throws IOException { 277 // Fail on suspiciously large manifest files 278 if (info.size > 64 * 1024) { 279 throw new IOException("Restore manifest too big; corrupt? size=" + info.size); 280 } 281 282 byte[] buffer = new byte[(int) info.size]; 283 if (MORE_DEBUG) { 284 Slog.i(TAG, 285 " readAppManifestAndReturnSignatures() looking for " + info.size + " bytes"); 286 } 287 if (readExactly(mInputStream, buffer, 0, (int) info.size) == info.size) { 288 mBytesReadListener.onBytesRead(info.size); 289 } else { 290 throw new IOException("Unexpected EOF in manifest"); 291 } 292 293 String[] str = new String[1]; 294 int offset = 0; 295 296 try { 297 offset = extractLine(buffer, offset, str); 298 int version = Integer.parseInt(str[0]); 299 if (version == BACKUP_MANIFEST_VERSION) { 300 offset = extractLine(buffer, offset, str); 301 String manifestPackage = str[0]; 302 // TODO: handle <original-package> 303 if (manifestPackage.equals(info.packageName)) { 304 offset = extractLine(buffer, offset, str); 305 info.version = Integer.parseInt(str[0]); // app version 306 offset = extractLine(buffer, offset, str); 307 // This is the platform version, which we don't use, but we parse it 308 // as a safety against corruption in the manifest. 309 Integer.parseInt(str[0]); 310 offset = extractLine(buffer, offset, str); 311 info.installerPackageName = (str[0].length() > 0) ? str[0] : null; 312 offset = extractLine(buffer, offset, str); 313 info.hasApk = str[0].equals("1"); 314 offset = extractLine(buffer, offset, str); 315 int numSigs = Integer.parseInt(str[0]); 316 if (numSigs > 0) { 317 Signature[] sigs = new Signature[numSigs]; 318 for (int i = 0; i < numSigs; i++) { 319 offset = extractLine(buffer, offset, str); 320 sigs[i] = new Signature(str[0]); 321 } 322 return sigs; 323 } else { 324 Slog.i(TAG, "Missing signature on backed-up package " + info.packageName); 325 mMonitor = BackupManagerMonitorUtils.monitorEvent( 326 mMonitor, 327 LOG_EVENT_ID_MISSING_SIGNATURE, 328 null, 329 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 330 BackupManagerMonitorUtils.putMonitoringExtra(null, 331 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); 332 } 333 } else { 334 Slog.i(TAG, "Expected package " + info.packageName 335 + " but restore manifest claims " + manifestPackage); 336 Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, 337 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 338 monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra( 339 monitoringExtras, 340 EXTRA_LOG_MANIFEST_PACKAGE_NAME, manifestPackage); 341 mMonitor = BackupManagerMonitorUtils.monitorEvent( 342 mMonitor, 343 LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE, 344 null, 345 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 346 monitoringExtras); 347 } 348 } else { 349 Slog.i(TAG, "Unknown restore manifest version " + version 350 + " for package " + info.packageName); 351 Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, 352 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 353 monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, 354 EXTRA_LOG_EVENT_PACKAGE_VERSION, version); 355 mMonitor = BackupManagerMonitorUtils.monitorEvent( 356 mMonitor, 357 BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION, 358 null, 359 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 360 monitoringExtras); 361 362 } 363 } catch (NumberFormatException e) { 364 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName); 365 mMonitor = BackupManagerMonitorUtils.monitorEvent( 366 mMonitor, 367 BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST, 368 null, 369 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 370 BackupManagerMonitorUtils.putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, 371 info.packageName)); 372 } catch (IllegalArgumentException e) { 373 Slog.w(TAG, e.getMessage()); 374 } 375 376 return null; 377 } 378 379 /** 380 * Chooses restore policy. 381 * 382 * @param packageManager - PackageManager instance. 383 * @param allowApks - allow restore set to include apks. 384 * @param info - file metadata. 385 * @param signatures - array of signatures parsed from backup file. 386 * @param userId - ID of the user for which restore is performed. 387 * @return a restore policy constant. 388 */ chooseRestorePolicy(PackageManager packageManager, boolean allowApks, FileMetadata info, Signature[] signatures, PackageManagerInternal pmi, int userId)389 public RestorePolicy chooseRestorePolicy(PackageManager packageManager, 390 boolean allowApks, FileMetadata info, Signature[] signatures, 391 PackageManagerInternal pmi, int userId) { 392 if (signatures == null) { 393 return RestorePolicy.IGNORE; 394 } 395 396 RestorePolicy policy = RestorePolicy.IGNORE; 397 398 // Okay, got the manifest info we need... 399 try { 400 PackageInfo pkgInfo = packageManager.getPackageInfoAsUser( 401 info.packageName, PackageManager.GET_SIGNING_CERTIFICATES, userId); 402 // Fall through to IGNORE if the app explicitly disallows backup 403 final int flags = pkgInfo.applicationInfo.flags; 404 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { 405 // Restore system-uid-space packages only if they have 406 // defined a custom backup agent 407 if (!UserHandle.isCore(pkgInfo.applicationInfo.uid) 408 || (pkgInfo.applicationInfo.backupAgentName != null)) { 409 // Verify signatures against any installed version; if they 410 // don't match, then we fall though and ignore the data. The 411 // signatureMatch() method explicitly ignores the signature 412 // check for packages installed on the system partition, because 413 // such packages are signed with the platform cert instead of 414 // the app developer's cert, so they're different on every 415 // device. 416 if (AppBackupUtils.signaturesMatch(signatures, pkgInfo, pmi)) { 417 if ((pkgInfo.applicationInfo.flags 418 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) { 419 Slog.i(TAG, "Package has restoreAnyVersion; taking data"); 420 mMonitor = BackupManagerMonitorUtils.monitorEvent( 421 mMonitor, 422 LOG_EVENT_ID_RESTORE_ANY_VERSION, 423 pkgInfo, 424 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 425 null); 426 policy = RestorePolicy.ACCEPT; 427 } else if (pkgInfo.getLongVersionCode() >= info.version) { 428 Slog.i(TAG, "Sig + version match; taking data"); 429 policy = RestorePolicy.ACCEPT; 430 mMonitor = BackupManagerMonitorUtils.monitorEvent( 431 mMonitor, 432 LOG_EVENT_ID_VERSIONS_MATCH, 433 pkgInfo, 434 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 435 null); 436 } else { 437 // The data is from a newer version of the app than 438 // is presently installed. That means we can only 439 // use it if the matching apk is also supplied. 440 if (allowApks) { 441 Slog.i(TAG, "Data version " + info.version 442 + " is newer than installed " 443 + "version " 444 + pkgInfo.getLongVersionCode() 445 + " - requiring apk"); 446 policy = RestorePolicy.ACCEPT_IF_APK; 447 } else { 448 Slog.i(TAG, "Data requires newer version " 449 + info.version + "; ignoring"); 450 mMonitor = BackupManagerMonitorUtils 451 .monitorEvent(mMonitor, 452 LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER, 453 pkgInfo, 454 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 455 BackupManagerMonitorUtils 456 .putMonitoringExtra( 457 null, 458 EXTRA_LOG_OLD_VERSION, 459 info.version)); 460 461 policy = RestorePolicy.IGNORE; 462 } 463 } 464 } else { 465 Slog.w(TAG, "Restore manifest signatures do not match " 466 + "installed application for " 467 + info.packageName); 468 mMonitor = BackupManagerMonitorUtils.monitorEvent( 469 mMonitor, 470 LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH, 471 pkgInfo, 472 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 473 null); 474 } 475 } else { 476 Slog.w(TAG, "Package " + info.packageName 477 + " is system level with no agent"); 478 mMonitor = BackupManagerMonitorUtils.monitorEvent( 479 mMonitor, 480 LOG_EVENT_ID_SYSTEM_APP_NO_AGENT, 481 pkgInfo, 482 LOG_EVENT_CATEGORY_AGENT, 483 null); 484 } 485 } else { 486 if (DEBUG) { 487 Slog.i(TAG, 488 "Restore manifest from " + info.packageName + " but allowBackup=false"); 489 } 490 mMonitor = BackupManagerMonitorUtils.monitorEvent( 491 mMonitor, 492 LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE, 493 pkgInfo, 494 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 495 null); 496 } 497 } catch (PackageManager.NameNotFoundException e) { 498 // Okay, the target app isn't installed. We can process 499 // the restore properly only if the dataset provides the 500 // apk file and we can successfully install it. 501 if (allowApks) { 502 if (DEBUG) { 503 Slog.i(TAG, "Package " + info.packageName 504 + " not installed; requiring apk in dataset"); 505 } 506 policy = RestorePolicy.ACCEPT_IF_APK; 507 } else { 508 policy = RestorePolicy.IGNORE; 509 } 510 Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra( 511 null, 512 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 513 monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra( 514 monitoringExtras, 515 EXTRA_LOG_POLICY_ALLOW_APKS, allowApks); 516 mMonitor = BackupManagerMonitorUtils.monitorEvent( 517 mMonitor, 518 LOG_EVENT_ID_APK_NOT_INSTALLED, 519 null, 520 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 521 monitoringExtras); 522 } 523 524 if (policy == RestorePolicy.ACCEPT_IF_APK && !info.hasApk) { 525 Slog.i(TAG, "Cannot restore package " + info.packageName 526 + " without the matching .apk"); 527 mMonitor = BackupManagerMonitorUtils.monitorEvent( 528 mMonitor, 529 LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK, 530 null, 531 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 532 BackupManagerMonitorUtils.putMonitoringExtra(null, 533 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName)); 534 } 535 536 return policy; 537 } 538 539 // Given an actual file content size, consume the post-content padding mandated 540 // by the tar format. skipTarPadding(long size)541 public void skipTarPadding(long size) throws IOException { 542 long partial = (size + 512) % 512; 543 if (partial > 0) { 544 final int needed = 512 - (int) partial; 545 if (MORE_DEBUG) { 546 Slog.i(TAG, "Skipping tar padding: " + needed + " bytes"); 547 } 548 byte[] buffer = new byte[needed]; 549 if (readExactly(mInputStream, buffer, 0, needed) == needed) { 550 mBytesReadListener.onBytesRead(needed); 551 } else { 552 throw new IOException("Unexpected EOF in padding"); 553 } 554 } 555 } 556 557 /** 558 * Read a widget metadata file, returning the restored blob. 559 */ readMetadata(FileMetadata info)560 public void readMetadata(FileMetadata info) throws IOException { 561 // Fail on suspiciously large widget dump files 562 if (info.size > 64 * 1024) { 563 throw new IOException("Metadata too big; corrupt? size=" + info.size); 564 } 565 566 byte[] buffer = new byte[(int) info.size]; 567 if (readExactly(mInputStream, buffer, 0, (int) info.size) == info.size) { 568 mBytesReadListener.onBytesRead(info.size); 569 } else { 570 throw new IOException("Unexpected EOF in widget data"); 571 } 572 573 String[] str = new String[1]; 574 int offset = extractLine(buffer, 0, str); 575 int version = Integer.parseInt(str[0]); 576 if (version == BACKUP_MANIFEST_VERSION) { 577 offset = extractLine(buffer, offset, str); 578 final String pkg = str[0]; 579 if (info.packageName.equals(pkg)) { 580 // Data checks out -- the rest of the buffer is a concatenation of 581 // binary blobs as described in the comment at writeAppWidgetData() 582 ByteArrayInputStream bin = new ByteArrayInputStream(buffer, 583 offset, buffer.length - offset); 584 DataInputStream in = new DataInputStream(bin); 585 while (bin.available() > 0) { 586 int token = in.readInt(); 587 int size = in.readInt(); 588 if (size > 64 * 1024) { 589 throw new IOException("Datum " + Integer.toHexString(token) 590 + " too big; corrupt? size=" + info.size); 591 } 592 switch (token) { 593 case BACKUP_WIDGET_METADATA_TOKEN: { 594 if (MORE_DEBUG) { 595 Slog.i(TAG, "Got widget metadata for " + info.packageName); 596 } 597 mWidgetData = new byte[size]; 598 in.read(mWidgetData); 599 break; 600 } 601 default: { 602 if (DEBUG) { 603 Slog.i(TAG, "Ignoring metadata blob " + Integer.toHexString(token) 604 + " for " + info.packageName); 605 } 606 in.skipBytes(size); 607 break; 608 } 609 } 610 } 611 } else { 612 Slog.w(TAG, 613 "Metadata mismatch: package " + info.packageName + " but widget data for " 614 + pkg); 615 616 Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null, 617 EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName); 618 monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, 619 BackupManagerMonitor.EXTRA_LOG_WIDGET_PACKAGE_NAME, pkg); 620 mMonitor = BackupManagerMonitorUtils.monitorEvent( 621 mMonitor, 622 BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH, 623 null, 624 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 625 monitoringExtras); 626 } 627 } else { 628 Slog.w(TAG, "Unsupported metadata version " + version); 629 630 Bundle monitoringExtras = BackupManagerMonitorUtils 631 .putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, 632 info.packageName); 633 monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras, 634 EXTRA_LOG_EVENT_PACKAGE_VERSION, version); 635 mMonitor = BackupManagerMonitorUtils.monitorEvent( 636 mMonitor, 637 BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION, 638 null, 639 LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, 640 monitoringExtras); 641 } 642 } 643 644 /** 645 * Builds a line from a byte buffer starting at 'offset'. 646 * 647 * @param buffer - where to read a line from. 648 * @param offset - offset in buffer to read a line from. 649 * @param outStr - an output parameter, the result will be put in outStr. 650 * @return the index of the next unconsumed data in the buffer. 651 * @throws IOException in case of an error. 652 */ extractLine(byte[] buffer, int offset, String[] outStr)653 private static int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException { 654 final int end = buffer.length; 655 if (offset >= end) { 656 throw new IOException("Incomplete data"); 657 } 658 659 int pos; 660 for (pos = offset; pos < end; pos++) { 661 byte c = buffer[pos]; 662 // at LF we declare end of line, and return the next char as the 663 // starting point for the next time through 664 if (c == '\n') { 665 break; 666 } 667 } 668 outStr[0] = new String(buffer, offset, pos - offset); 669 pos++; // may be pointing an extra byte past the end but that's okay 670 return pos; 671 } 672 readTarHeader(byte[] block)673 private boolean readTarHeader(byte[] block) throws IOException { 674 final int got = readExactly(mInputStream, block, 0, 512); 675 if (got == 0) { 676 return false; // Clean EOF 677 } 678 if (got < 512) { 679 throw new IOException("Unable to read full block header"); 680 } 681 mBytesReadListener.onBytesRead(512); 682 return true; 683 } 684 685 // overwrites 'info' fields based on the pax extended header readPaxExtendedHeader(FileMetadata info)686 private boolean readPaxExtendedHeader(FileMetadata info) 687 throws IOException { 688 // We should never see a pax extended header larger than this 689 if (info.size > 32 * 1024) { 690 Slog.w(TAG, "Suspiciously large pax header size " + info.size + " - aborting"); 691 throw new IOException("Sanity failure: pax header size " + info.size); 692 } 693 694 // read whole blocks, not just the content size 695 int numBlocks = (int) ((info.size + 511) >> 9); 696 byte[] data = new byte[numBlocks * 512]; 697 if (readExactly(mInputStream, data, 0, data.length) < data.length) { 698 throw new IOException("Unable to read full pax header"); 699 } 700 mBytesReadListener.onBytesRead(data.length); 701 702 final int contentSize = (int) info.size; 703 int offset = 0; 704 do { 705 // extract the line at 'offset' 706 int eol = offset + 1; 707 while (eol < contentSize && data[eol] != ' ') { 708 eol++; 709 } 710 if (eol >= contentSize) { 711 // error: we just hit EOD looking for the end of the size field 712 throw new IOException("Invalid pax data"); 713 } 714 // eol points to the space between the count and the key 715 int linelen = (int) extractRadix(data, offset, eol - offset, 10); 716 int key = eol + 1; // start of key=value 717 eol = offset + linelen - 1; // trailing LF 718 int value; 719 for (value = key + 1; data[value] != '=' && value <= eol; value++) { 720 ; 721 } 722 if (value > eol) { 723 throw new IOException("Invalid pax declaration"); 724 } 725 726 // pax requires that key/value strings be in UTF-8 727 String keyStr = new String(data, key, value - key, "UTF-8"); 728 // -1 to strip the trailing LF 729 String valStr = new String(data, value + 1, eol - value - 1, "UTF-8"); 730 731 if ("path".equals(keyStr)) { 732 info.path = valStr; 733 } else if ("size".equals(keyStr)) { 734 info.size = Long.parseLong(valStr); 735 } else { 736 if (DEBUG) { 737 Slog.i(TAG, "Unhandled pax key: " + key); 738 } 739 } 740 741 offset += linelen; 742 } while (offset < contentSize); 743 744 return true; 745 } 746 extractRadix(byte[] data, int offset, int maxChars, int radix)747 private static long extractRadix(byte[] data, int offset, int maxChars, int radix) 748 throws IOException { 749 long value = 0; 750 final int end = offset + maxChars; 751 for (int i = offset; i < end; i++) { 752 final byte b = data[i]; 753 // Numeric fields in tar can terminate with either NUL or SPC 754 if (b == 0 || b == ' ') { 755 break; 756 } 757 if (b < '0' || b > ('0' + radix - 1)) { 758 throw new IOException("Invalid number in header: '" + (char) b 759 + "' for radix " + radix); 760 } 761 value = radix * value + (b - '0'); 762 } 763 return value; 764 } 765 extractString(byte[] data, int offset, int maxChars)766 private static String extractString(byte[] data, int offset, int maxChars) throws IOException { 767 final int end = offset + maxChars; 768 int eos = offset; 769 // tar string fields terminate early with a NUL 770 while (eos < end && data[eos] != 0) { 771 eos++; 772 } 773 return new String(data, offset, eos - offset, "US-ASCII"); 774 } 775 hexLog(byte[] block)776 private static void hexLog(byte[] block) { 777 int offset = 0; 778 int todo = block.length; 779 StringBuilder buf = new StringBuilder(64); 780 while (todo > 0) { 781 buf.append(String.format("%04x ", offset)); 782 int numThisLine = (todo > 16) ? 16 : todo; 783 for (int i = 0; i < numThisLine; i++) { 784 buf.append(String.format("%02x ", block[offset + i])); 785 } 786 Slog.i("hexdump", buf.toString()); 787 buf.setLength(0); 788 todo -= numThisLine; 789 offset += numThisLine; 790 } 791 } 792 getMonitor()793 public IBackupManagerMonitor getMonitor() { 794 return mMonitor; 795 } 796 getWidgetData()797 public byte[] getWidgetData() { 798 return mWidgetData; 799 } 800 } 801