Home | History | Annotate | Download | only in rawbu
      1 // Copyright 2009 The Android Open Source Project
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <stdarg.h>
      6 #include <string.h>
      7 #include <unistd.h>
      8 #include <fcntl.h>
      9 #include <time.h>
     10 #include <dirent.h>
     11 #include <errno.h>
     12 #include <assert.h>
     13 #include <ctype.h>
     14 #include <utime.h>
     15 #include <sys/stat.h>
     16 #include <sys/types.h>
     17 #include <stdint.h>
     18 
     19 #include <cutils/properties.h>
     20 
     21 #include <private/android_filesystem_config.h>
     22 
     23 #ifndef PATH_MAX
     24 #define PATH_MAX 4096
     25 #endif
     26 
     27 // First version.
     28 #define FILE_VERSION_1 0xffff0001
     29 
     30 // Introduces backup all option to header.
     31 #define FILE_VERSION_2 0xffff0002
     32 
     33 #define FILE_VERSION FILE_VERSION_2
     34 
     35 namespace android {
     36 
     37 static char nameBuffer[PATH_MAX];
     38 static struct stat statBuffer;
     39 
     40 static char copyBuffer[8192];
     41 static char *backupFilePath = NULL;
     42 
     43 static uint32_t inputFileVersion;
     44 
     45 static int opt_backupAll;
     46 
     47 #define SPECIAL_NO_TOUCH 0
     48 #define SPECIAL_NO_BACKUP 1
     49 
     50 struct special_dir {
     51     const char* path;
     52     int type;
     53 };
     54 
     55 /* Directory paths that we will not backup/restore */
     56 static const struct special_dir SKIP_PATHS[] = {
     57     { "/data/misc", SPECIAL_NO_TOUCH },
     58     { "/data/system/batterystats.bin", SPECIAL_NO_TOUCH },
     59     { "/data/system/location", SPECIAL_NO_TOUCH },
     60     { "/data/dalvik-cache", SPECIAL_NO_BACKUP },
     61     { NULL, 0 },
     62 };
     63 
     64 /* This is just copied from the shell's built-in wipe command. */
     65 static int wipe (const char *path)
     66 {
     67     DIR *dir;
     68     struct dirent *de;
     69     int ret;
     70     int i;
     71 
     72     dir = opendir(path);
     73 
     74     if (dir == NULL) {
     75         fprintf (stderr, "Error opendir'ing %s: %s\n",
     76                     path, strerror(errno));
     77         return 0;
     78     }
     79 
     80     char *filenameOffset;
     81 
     82     strcpy(nameBuffer, path);
     83     strcat(nameBuffer, "/");
     84 
     85     filenameOffset = nameBuffer + strlen(nameBuffer);
     86 
     87     for (;;) {
     88         de = readdir(dir);
     89 
     90         if (de == NULL) {
     91             break;
     92         }
     93 
     94         if (0 == strcmp(de->d_name, ".")
     95                 || 0 == strcmp(de->d_name, "..")
     96                 || 0 == strcmp(de->d_name, "lost+found")
     97         ) {
     98             continue;
     99         }
    100 
    101         strcpy(filenameOffset, de->d_name);
    102         bool noBackup = false;
    103 
    104         /* See if this is a path we should skip. */
    105         for (i = 0; SKIP_PATHS[i].path; i++) {
    106             if (strcmp(SKIP_PATHS[i].path, nameBuffer) == 0) {
    107                 if (opt_backupAll || SKIP_PATHS[i].type == SPECIAL_NO_BACKUP) {
    108                     // In this case we didn't back up the directory --
    109                     // we do want to wipe its contents, but not the
    110                     // directory itself, since the restore file won't
    111                     // contain the directory.
    112                     noBackup = true;
    113                 }
    114                 break;
    115             }
    116         }
    117 
    118         if (!noBackup && SKIP_PATHS[i].path != NULL) {
    119             // This is a SPECIAL_NO_TOUCH directory.
    120             continue;
    121         }
    122 
    123         ret = lstat (nameBuffer, &statBuffer);
    124 
    125         if (ret != 0) {
    126             fprintf(stderr, "warning -- stat() error on '%s': %s\n",
    127                     nameBuffer, strerror(errno));
    128             continue;
    129         }
    130 
    131         if(S_ISDIR(statBuffer.st_mode)) {
    132             char *newpath;
    133 
    134             newpath = strdup(nameBuffer);
    135             if (wipe(newpath) == 0) {
    136                 free(newpath);
    137                 closedir(dir);
    138                 return 0;
    139             }
    140 
    141             if (!noBackup) {
    142                 ret = rmdir(newpath);
    143                 if (ret != 0) {
    144                     fprintf(stderr, "warning -- rmdir() error on '%s': %s\n",
    145                         newpath, strerror(errno));
    146                 }
    147             }
    148 
    149             free(newpath);
    150 
    151             strcpy(nameBuffer, path);
    152             strcat(nameBuffer, "/");
    153 
    154         } else {
    155             // Don't delete the backup file
    156             if (backupFilePath && strcmp(backupFilePath, nameBuffer) == 0) {
    157                 continue;
    158             }
    159             ret = unlink(nameBuffer);
    160 
    161             if (ret != 0) {
    162                 fprintf(stderr, "warning -- unlink() error on '%s': %s\n",
    163                     nameBuffer, strerror(errno));
    164             }
    165         }
    166     }
    167 
    168     closedir(dir);
    169 
    170     return 1;
    171 }
    172 
    173 static int write_int32(FILE* fh, int32_t val)
    174 {
    175     int res = fwrite(&val, 1, sizeof(val), fh);
    176     if (res != sizeof(val)) {
    177         fprintf(stderr, "unable to write int32 (%d bytes): %s\n", res, strerror(errno));
    178         return 0;
    179     }
    180 
    181     return 1;
    182 }
    183 
    184 static int write_int64(FILE* fh, int64_t val)
    185 {
    186     int res = fwrite(&val, 1, sizeof(val), fh);
    187     if (res != sizeof(val)) {
    188         fprintf(stderr, "unable to write int64 (%d bytes): %s\n", res, strerror(errno));
    189         return 0;
    190     }
    191 
    192     return 1;
    193 }
    194 
    195 static int copy_file(FILE* dest, FILE* src, off_t size, const char* destName,
    196         const char* srcName)
    197 {
    198     errno = 0;
    199 
    200     off_t origSize = size;
    201 
    202     while (size > 0) {
    203         int amt = size > (off_t)sizeof(copyBuffer) ? sizeof(copyBuffer) : (int)size;
    204         int readLen = fread(copyBuffer, 1, amt, src);
    205         if (readLen <= 0) {
    206             if (srcName != NULL) {
    207                 fprintf(stderr, "unable to read source (%d of %ld bytes) file '%s': %s\n",
    208                     amt, origSize, srcName, errno != 0 ? strerror(errno) : "unexpected EOF");
    209             } else {
    210                 fprintf(stderr, "unable to read buffer (%d of %ld bytes): %s\n",
    211                     amt, origSize, errno != 0 ? strerror(errno) : "unexpected EOF");
    212             }
    213             return 0;
    214         }
    215         int writeLen = fwrite(copyBuffer, 1, readLen, dest);
    216         if (writeLen != readLen) {
    217             if (destName != NULL) {
    218                 fprintf(stderr, "unable to write file (%d of %d bytes) '%s': '%s'\n",
    219                     writeLen, readLen, destName, strerror(errno));
    220             } else {
    221                 fprintf(stderr, "unable to write buffer (%d of %d bytes): '%s'\n",
    222                     writeLen, readLen, strerror(errno));
    223             }
    224             return 0;
    225         }
    226         size -= readLen;
    227     }
    228     return 1;
    229 }
    230 
    231 #define TYPE_END 0
    232 #define TYPE_DIR 1
    233 #define TYPE_FILE 2
    234 
    235 static int write_header(FILE* fh, int type, const char* path, const struct stat* st)
    236 {
    237     int pathLen = strlen(path);
    238     if (!write_int32(fh, type)) return 0;
    239     if (!write_int32(fh, pathLen)) return 0;
    240     if (fwrite(path, 1, pathLen, fh) != (size_t)pathLen) {
    241         fprintf(stderr, "unable to write: %s\n", strerror(errno));
    242         return 0;
    243     }
    244 
    245     if (!write_int32(fh, st->st_uid)) return 0;
    246     if (!write_int32(fh, st->st_gid)) return 0;
    247     if (!write_int32(fh, st->st_mode)) return 0;
    248     if (!write_int64(fh, ((int64_t)st->st_atime)*1000*1000*1000)) return 0;
    249     if (!write_int64(fh, ((int64_t)st->st_mtime)*1000*1000*1000)) return 0;
    250     if (!write_int64(fh, ((int64_t)st->st_ctime)*1000*1000*1000)) return 0;
    251 
    252     return 1;
    253 }
    254 
    255 static int backup_dir(FILE* fh, const char* srcPath)
    256 {
    257     DIR *dir;
    258     struct dirent *de;
    259     char* fullPath = NULL;
    260     int srcLen = strlen(srcPath);
    261     int result = 1;
    262     int i;
    263 
    264     dir = opendir(srcPath);
    265 
    266     if (dir == NULL) {
    267         fprintf (stderr, "error opendir'ing '%s': %s\n",
    268                     srcPath, strerror(errno));
    269         return 0;
    270     }
    271 
    272     for (;;) {
    273         de = readdir(dir);
    274 
    275         if (de == NULL) {
    276             break;
    277         }
    278 
    279         if (0 == strcmp(de->d_name, ".")
    280                 || 0 == strcmp(de->d_name, "..")
    281                 || 0 == strcmp(de->d_name, "lost+found")
    282         ) {
    283             continue;
    284         }
    285 
    286         if (fullPath != NULL) {
    287             free(fullPath);
    288         }
    289         fullPath = (char*)malloc(srcLen + strlen(de->d_name) + 2);
    290         strcpy(fullPath, srcPath);
    291         fullPath[srcLen] = '/';
    292         strcpy(fullPath+srcLen+1, de->d_name);
    293 
    294         /* See if this is a path we should skip. */
    295         if (!opt_backupAll) {
    296             for (i = 0; SKIP_PATHS[i].path; i++) {
    297                 if (strcmp(SKIP_PATHS[i].path, fullPath) == 0) {
    298                     break;
    299                 }
    300             }
    301             if (SKIP_PATHS[i].path != NULL) {
    302                 continue;
    303             }
    304         }
    305 
    306         int ret = lstat(fullPath, &statBuffer);
    307 
    308         if (ret != 0) {
    309             fprintf(stderr, "stat() error on '%s': %s\n",
    310                     fullPath, strerror(errno));
    311             result = 0;
    312             goto done;
    313         }
    314 
    315         if(S_ISDIR(statBuffer.st_mode)) {
    316             printf("Saving dir %s...\n", fullPath);
    317 
    318             if (write_header(fh, TYPE_DIR, fullPath, &statBuffer) == 0) {
    319                 result = 0;
    320                 goto done;
    321             }
    322             if (backup_dir(fh, fullPath) == 0) {
    323                 result = 0;
    324                 goto done;
    325             }
    326         } else if (S_ISREG(statBuffer.st_mode)) {
    327             // Skip the backup file
    328             if (backupFilePath && strcmp(fullPath, backupFilePath) == 0) {
    329                 printf("Skipping backup file %s...\n", backupFilePath);
    330                 continue;
    331             } else {
    332                 printf("Saving file %s...\n", fullPath);
    333             }
    334             if (write_header(fh, TYPE_FILE, fullPath, &statBuffer) == 0) {
    335                 result = 0;
    336                 goto done;
    337             }
    338 
    339             off_t size = statBuffer.st_size;
    340             if (!write_int64(fh, size)) {
    341                 result = 0;
    342                 goto done;
    343             }
    344 
    345             FILE* src = fopen(fullPath, "r");
    346             if (src == NULL) {
    347                 fprintf(stderr, "unable to open source file '%s': %s\n",
    348                     fullPath, strerror(errno));
    349                 result = 0;
    350                 goto done;
    351             }
    352 
    353             int copyres = copy_file(fh, src, size, NULL, fullPath);
    354             fclose(src);
    355             if (!copyres) {
    356                 result = 0;
    357                 goto done;
    358             }
    359         }
    360     }
    361 
    362 done:
    363     if (fullPath != NULL) {
    364         free(fullPath);
    365     }
    366 
    367     closedir(dir);
    368 
    369     return result;
    370 }
    371 
    372 static int backup_data(const char* destPath)
    373 {
    374     int res = -1;
    375 
    376     FILE* fh = fopen(destPath, "w");
    377     if (fh == NULL) {
    378         fprintf(stderr, "unable to open destination '%s': %s\n",
    379                 destPath, strerror(errno));
    380         return -1;
    381     }
    382 
    383     printf("Backing up /data to %s...\n", destPath);
    384 
    385     // The path that shouldn't be backed up
    386     backupFilePath = strdup(destPath);
    387 
    388     if (!write_int32(fh, FILE_VERSION)) goto done;
    389     if (!write_int32(fh, opt_backupAll)) goto done;
    390     if (!backup_dir(fh, "/data")) goto done;
    391     if (!write_int32(fh, 0)) goto done;
    392 
    393     res = 0;
    394 
    395 done:
    396     if (fflush(fh) != 0) {
    397         fprintf(stderr, "error flushing destination '%s': %s\n",
    398             destPath, strerror(errno));
    399         res = -1;
    400         goto donedone;
    401     }
    402     if (fsync(fileno(fh)) != 0) {
    403         fprintf(stderr, "error syncing destination '%s': %s\n",
    404             destPath, strerror(errno));
    405         res = -1;
    406         goto donedone;
    407     }
    408     fclose(fh);
    409     sync();
    410 
    411 donedone:
    412     return res;
    413 }
    414 
    415 static int32_t read_int32(FILE* fh, int32_t defVal)
    416 {
    417     int32_t val;
    418     if (fread(&val, 1, sizeof(val), fh) != sizeof(val)) {
    419         fprintf(stderr, "unable to read: %s\n", strerror(errno));
    420         return defVal;
    421     }
    422 
    423     return val;
    424 }
    425 
    426 static int64_t read_int64(FILE* fh, int64_t defVal)
    427 {
    428     int64_t val;
    429     if (fread(&val, 1, sizeof(val), fh) != sizeof(val)) {
    430         fprintf(stderr, "unable to read: %s\n", strerror(errno));
    431         return defVal;
    432     }
    433 
    434     return val;
    435 }
    436 
    437 static int read_header(FILE* fh, int* type, char** path, struct stat* st)
    438 {
    439     *type = read_int32(fh, -1);
    440     if (*type == TYPE_END) {
    441         return 1;
    442     }
    443 
    444     if (*type < 0) {
    445         fprintf(stderr, "bad token %d in restore file\n", *type);
    446         return 0;
    447     }
    448 
    449     int32_t pathLen = read_int32(fh, -1);
    450     if (pathLen <= 0) {
    451         fprintf(stderr, "bad path length %d in restore file\n", pathLen);
    452         return 0;
    453     }
    454     char* readPath = (char*)malloc(pathLen+1);
    455     if (fread(readPath, 1, pathLen, fh) != (size_t)pathLen) {
    456         fprintf(stderr, "truncated path in restore file\n");
    457         free(readPath);
    458         return 0;
    459     }
    460     readPath[pathLen] = 0;
    461     *path = readPath;
    462 
    463     st->st_uid = read_int32(fh, -1);
    464     if (st->st_uid == (uid_t)-1) {
    465         fprintf(stderr, "bad uid in restore file at '%s'\n", readPath);
    466         return 0;
    467     }
    468     st->st_gid = read_int32(fh, -1);
    469     if (st->st_gid == (gid_t)-1) {
    470         fprintf(stderr, "bad gid in restore file at '%s'\n", readPath);
    471         return 0;
    472     }
    473     st->st_mode = read_int32(fh, -1);
    474     if (st->st_mode == (mode_t)-1) {
    475         fprintf(stderr, "bad mode in restore file at '%s'\n", readPath);
    476         return 0;
    477     }
    478     int64_t ltime = read_int64(fh, -1);
    479     if (ltime < 0) {
    480         fprintf(stderr, "bad atime in restore file at '%s'\n", readPath);
    481         return 0;
    482     }
    483     st->st_atime = (time_t)(ltime/1000/1000/1000);
    484     ltime = read_int64(fh, -1);
    485     if (ltime < 0) {
    486         fprintf(stderr, "bad mtime in restore file at '%s'\n", readPath);
    487         return 0;
    488     }
    489     st->st_mtime = (time_t)(ltime/1000/1000/1000);
    490     ltime = read_int64(fh, -1);
    491     if (ltime < 0) {
    492         fprintf(stderr, "bad ctime in restore file at '%s'\n", readPath);
    493         return 0;
    494     }
    495     st->st_ctime = (time_t)(ltime/1000/1000/1000);
    496 
    497     st->st_mode &= (S_IRWXU|S_IRWXG|S_IRWXO);
    498 
    499     return 1;
    500 }
    501 
    502 static int restore_data(const char* srcPath)
    503 {
    504     int res = -1;
    505 
    506     FILE* fh = fopen(srcPath, "r");
    507     if (fh == NULL) {
    508         fprintf(stderr, "Unable to open source '%s': %s\n",
    509                 srcPath, strerror(errno));
    510         return -1;
    511     }
    512 
    513     inputFileVersion = read_int32(fh, 0);
    514     if (inputFileVersion < FILE_VERSION_1 || inputFileVersion > FILE_VERSION) {
    515         fprintf(stderr, "Restore file has bad version: 0x%x\n", inputFileVersion);
    516         goto done;
    517     }
    518 
    519     if (inputFileVersion >= FILE_VERSION_2) {
    520         opt_backupAll = read_int32(fh, 0);
    521     } else {
    522         opt_backupAll = 0;
    523     }
    524 
    525     // The path that shouldn't be deleted
    526     backupFilePath = strdup(srcPath);
    527 
    528     printf("Wiping contents of /data...\n");
    529     if (!wipe("/data")) {
    530         goto done;
    531     }
    532 
    533     printf("Restoring from %s to /data...\n", srcPath);
    534 
    535     while (1) {
    536         int type;
    537         char* path = NULL;
    538         if (read_header(fh, &type, &path, &statBuffer) == 0) {
    539             goto done;
    540         }
    541         if (type == 0) {
    542             break;
    543         }
    544 
    545         const char* typeName = "?";
    546 
    547         if (type == TYPE_DIR) {
    548             typeName = "dir";
    549 
    550             printf("Restoring dir %s...\n", path);
    551 
    552             if (mkdir(path, statBuffer.st_mode) != 0) {
    553                 if (errno != EEXIST) {
    554                     fprintf(stderr, "unable to create directory '%s': %s\n",
    555                         path, strerror(errno));
    556                     free(path);
    557                     goto done;
    558                 }
    559             }
    560 
    561         } else if (type == TYPE_FILE) {
    562             typeName = "file";
    563             off_t size = read_int64(fh, -1);
    564             if (size < 0) {
    565                 fprintf(stderr, "bad file size %ld in restore file\n", size);
    566                 free(path);
    567                 goto done;
    568             }
    569 
    570             printf("Restoring file %s...\n", path);
    571 
    572             FILE* dest = fopen(path, "w");
    573             if (dest == NULL) {
    574                 fprintf(stderr, "unable to open destination file '%s': %s\n",
    575                     path, strerror(errno));
    576                 free(path);
    577                 goto done;
    578             }
    579 
    580             int copyres = copy_file(dest, fh, size, path, NULL);
    581             fclose(dest);
    582             if (!copyres) {
    583                 free(path);
    584                 goto done;
    585             }
    586 
    587         } else {
    588             fprintf(stderr, "unknown node type %d\n", type);
    589             goto done;
    590         }
    591 
    592         // Do this even for directories, since the dir may have already existed
    593         // so we need to make sure it gets the correct mode.
    594         if (chmod(path, statBuffer.st_mode&(S_IRWXU|S_IRWXG|S_IRWXO)) != 0) {
    595             fprintf(stderr, "unable to chmod destination %s '%s' to 0x%x: %s\n",
    596                 typeName, path, statBuffer.st_mode, strerror(errno));
    597             free(path);
    598             goto done;
    599         }
    600 
    601         if (chown(path, statBuffer.st_uid, statBuffer.st_gid) != 0) {
    602             fprintf(stderr, "unable to chown destination %s '%s' to uid %d / gid %d: %s\n",
    603                 typeName, path, (int)statBuffer.st_uid, (int)statBuffer.st_gid, strerror(errno));
    604             free(path);
    605             goto done;
    606         }
    607 
    608         struct utimbuf timbuf;
    609         timbuf.actime = statBuffer.st_atime;
    610         timbuf.modtime = statBuffer.st_mtime;
    611         if (utime(path, &timbuf) != 0) {
    612             fprintf(stderr, "unable to utime destination %s '%s': %s\n",
    613                 typeName, path, strerror(errno));
    614             free(path);
    615             goto done;
    616         }
    617 
    618 
    619         free(path);
    620     }
    621 
    622     res = 0;
    623 
    624 done:
    625     fclose(fh);
    626 
    627     return res;
    628 }
    629 
    630 static void show_help(const char *cmd)
    631 {
    632     fprintf(stderr,"Usage: %s COMMAND [options] [backup-file-path]\n", cmd);
    633 
    634     fprintf(stderr, "commands are:\n"
    635                     "  help            Show this help text.\n"
    636                     "  backup          Perform a backup of /data.\n"
    637                     "  restore         Perform a restore of /data.\n");
    638     fprintf(stderr, "options include:\n"
    639                     "  -h              Show this help text.\n"
    640                     "  -a              Backup all files.\n");
    641     fprintf(stderr, "\n backup-file-path Defaults to /sdcard/backup.dat .\n"
    642                     "                  On devices that emulate the sdcard, you will need to\n"
    643                     "                  explicitly specify the directory it is mapped to,\n"
    644                     "                  to avoid recursive backup or deletion of the backup file\n"
    645                     "                  during restore.\n\n"
    646                     "                  Eg. /data/media/0/backup.dat\n");
    647     fprintf(stderr, "\nThe %s command allows you to perform low-level\n"
    648                     "backup and restore of the /data partition.  This is\n"
    649                     "where all user data is kept, allowing for a fairly\n"
    650                     "complete restore of a device's state.  Note that\n"
    651                     "because this is low-level, it will only work across\n"
    652                     "builds of the same (or very similar) device software.\n",
    653                     cmd);
    654 }
    655 
    656 } /* namespace android */
    657 
    658 int main (int argc, char **argv)
    659 {
    660     int restore = 0;
    661 
    662     if (getuid() != AID_ROOT) {
    663         fprintf(stderr, "error -- %s must run as root\n", argv[0]);
    664         exit(-1);
    665     }
    666 
    667     if (argc < 2) {
    668         fprintf(stderr, "No command specified.\n");
    669         android::show_help(argv[0]);
    670         exit(-1);
    671     }
    672 
    673     if (0 == strcmp(argv[1], "restore")) {
    674         restore = 1;
    675     } else if (0 == strcmp(argv[1], "help")) {
    676         android::show_help(argv[0]);
    677         exit(0);
    678     } else if (0 != strcmp(argv[1], "backup")) {
    679         fprintf(stderr, "Unknown command: %s\n", argv[1]);
    680         android::show_help(argv[0]);
    681         exit(-1);
    682     }
    683 
    684     android::opt_backupAll = 0;
    685 
    686     optind = 2;
    687 
    688     for (;;) {
    689         int ret;
    690 
    691         ret = getopt(argc, argv, "ah");
    692 
    693         if (ret < 0) {
    694             break;
    695         }
    696 
    697         switch(ret) {
    698             case 'a':
    699                 android::opt_backupAll = 1;
    700                 if (restore) fprintf(stderr, "Warning: -a option ignored on restore\n");
    701                 break;
    702             case 'h':
    703                 android::show_help(argv[0]);
    704                 exit(0);
    705             break;
    706 
    707             default:
    708                 fprintf(stderr,"Unrecognized Option\n");
    709                 android::show_help(argv[0]);
    710                 exit(-1);
    711             break;
    712         }
    713     }
    714 
    715     const char* backupFile = "/sdcard/backup.dat";
    716 
    717     if (argc > optind) {
    718         backupFile = argv[optind];
    719         optind++;
    720         if (argc != optind) {
    721             fprintf(stderr, "Too many arguments\n");
    722             android::show_help(argv[0]);
    723             exit(-1);
    724         }
    725     }
    726 
    727     printf("Stopping system...\n");
    728     property_set("ctl.stop", "runtime");
    729     property_set("ctl.stop", "zygote");
    730     sleep(1);
    731 
    732     int res;
    733     if (restore) {
    734         res = android::restore_data(backupFile);
    735         if (res != 0) {
    736             // Don't restart system, since the data partition is hosed.
    737             return res;
    738         }
    739         printf("Restore complete!  Restarting system, cross your fingers...\n");
    740     } else {
    741         res = android::backup_data(backupFile);
    742         if (res == 0) {
    743             printf("Backup complete!  Restarting system...\n");
    744         } else {
    745             printf("Restarting system...\n");
    746         }
    747     }
    748 
    749     property_set("ctl.start", "zygote");
    750     property_set("ctl.start", "runtime");
    751 }
    752