blob: c4fa7656f9967bd062267e5e6329dd9e3402f973 [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>
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 }
Dianne Hackborn738b7582010-03-18 17:45:15 -0700321 } else if (S_ISREG(statBuffer.st_mode)) {
Dianne Hackborne4bfb782010-03-11 21:43:34 -0800322 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}