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