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