• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 namespace android {
35 
36 static char nameBuffer[PATH_MAX];
37 static struct stat statBuffer;
38 
39 static char copyBuffer[8192];
40 
41 static uint32_t inputFileVersion;
42 
43 static int opt_backupAll;
44 
45 #define SPECIAL_NO_TOUCH 0
46 #define SPECIAL_NO_BACKUP 1
47 
48 struct special_dir {
49     const char* path;
50     int type;
51 };
52 
53 /* Directory paths that we will not backup/restore */
54 static 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. */
wipe(const char * path)63 static 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 
write_int32(FILE * fh,int32_t val)168 static 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 
write_int64(FILE * fh,int64_t val)179 static 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 
copy_file(FILE * dest,FILE * src,off_t size,const char * destName,const char * srcName)190 static 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 
write_header(FILE * fh,int type,const char * path,const struct stat * st)230 static 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 
backup_dir(FILE * fh,const char * srcPath)250 static 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 if (S_ISREG(statBuffer.st_mode)) {
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 
352 done:
353     if (fullPath != NULL) {
354         free(fullPath);
355     }
356 
357     closedir(dir);
358 
359     return result;
360 }
361 
backup_data(const char * destPath)362 static 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 
382 done:
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 
398 donedone:
399     return res;
400 }
401 
read_int32(FILE * fh,int32_t defVal)402 static 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 
read_int64(FILE * fh,int64_t defVal)413 static 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 
read_header(FILE * fh,int * type,char ** path,struct stat * st)424 static 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 
restore_data(const char * srcPath)489 static 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 
608 done:
609     fclose(fh);
610 
611     return res;
612 }
613 
show_help(const char * cmd)614 static 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 
main(int argc,char ** argv)636 int 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