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