• 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 = nullptr;
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     { nullptr, 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 == nullptr) {
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 == nullptr) {
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 != nullptr) {
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 
write_int32(FILE * fh,int32_t val)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 
write_int64(FILE * fh,int64_t val)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 
copy_file(FILE * dest,FILE * src,off_t size,const char * destName,const char * srcName)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 != nullptr) {
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 != nullptr) {
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 
write_header(FILE * fh,int type,const char * path,const struct stat * st)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 
backup_dir(FILE * fh,const char * srcPath)255 static int backup_dir(FILE* fh, const char* srcPath)
256 {
257     DIR *dir;
258     struct dirent *de;
259     char* fullPath = nullptr;
260     int srcLen = strlen(srcPath);
261     int result = 1;
262     int i;
263 
264     dir = opendir(srcPath);
265 
266     if (dir == nullptr) {
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 == nullptr) {
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 != nullptr) {
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 != nullptr) {
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 == nullptr) {
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, nullptr, fullPath);
354             fclose(src);
355             if (!copyres) {
356                 result = 0;
357                 goto done;
358             }
359         }
360     }
361 
362 done:
363     if (fullPath != nullptr) {
364         free(fullPath);
365     }
366 
367     closedir(dir);
368 
369     return result;
370 }
371 
backup_data(const char * destPath)372 static int backup_data(const char* destPath)
373 {
374     int res = -1;
375 
376     FILE* fh = fopen(destPath, "w");
377     if (fh == nullptr) {
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 
read_int32(FILE * fh,int32_t defVal)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 
read_int64(FILE * fh,int64_t defVal)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 
read_header(FILE * fh,int * type,char ** path,struct stat * st)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 
restore_data(const char * srcPath)502 static int restore_data(const char* srcPath)
503 {
504     int res = -1;
505 
506     FILE* fh = fopen(srcPath, "r");
507     if (fh == nullptr) {
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 = nullptr;
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 == nullptr) {
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, nullptr);
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 
show_help(const char * cmd)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 
main(int argc,char ** argv)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