#include #include #include #include #include #include #include #include #include #include #include "cutils/properties.h" #include "roots.h" #include "bu.h" #include "voldclient.h" #include "messagesocket.h" using namespace android; static int append_sod(const char* opt_hash) { const char* key; char value[PROPERTY_VALUE_MAX]; int len; char buf[PROP_LINE_LEN]; char sodbuf[PROP_LINE_LEN*10]; char* p = sodbuf; key = "hash.name"; strcpy(value, opt_hash); p += sprintf(p, "%s=%s\n", key, value); key = "ro.product.device"; property_get(key, value, ""); p += sprintf(p, "%s=%s\n", key, value); for (int i = 0; i < MAX_PART; ++i) { partspec* part = part_get(i); if (!part) break; if (!volume_is_mountable(part->vol) || volume_is_readonly(part->vol) || volume_is_verity(part->vol)) { int fd = open(part->vol->blk_device, O_RDONLY); part->size = part->used = lseek64(fd, 0, SEEK_END); close(fd); } else { if (ensure_path_mounted(part->path) == 0) { struct statfs stfs; memset(&stfs, 0, sizeof(stfs)); if (statfs(part->path, &stfs) == 0) { part->size = (stfs.f_blocks) * stfs.f_bsize; part->used = (stfs.f_blocks - stfs.f_bfree) * stfs.f_bsize; } else { logmsg("Failed to statfs %s: %s\n", part->path, strerror(errno)); } ensure_path_unmounted(part->path); } else { int fd = open(part->vol->blk_device, O_RDONLY); part->size = part->used = lseek64(fd, 0, SEEK_END); close(fd); } } p += sprintf(p, "fs.%s.size=%llu\n", part->name, part->size); p += sprintf(p, "fs.%s.used=%llu\n", part->name, part->used); } int rc = tar_append_file_contents(tar, "SOD", 0600, getuid(), getgid(), sodbuf, p-sodbuf); return rc; } static int append_eod(const char* opt_hash) { char buf[PROP_LINE_LEN]; char eodbuf[PROP_LINE_LEN*10]; char* p = eodbuf; int n; p += sprintf(p, "hash.datalen=%u\n", hash_datalen); unsigned char digest[HASH_MAX_LENGTH]; char hexdigest[HASH_MAX_STRING_LENGTH]; if (!strcasecmp(opt_hash, "sha1")) { SHA1_Final(digest, &sha_ctx); for (n = 0; n < SHA_DIGEST_LENGTH; ++n) { sprintf(hexdigest+2*n, "%02x", digest[n]); } p += sprintf(p, "hash.value=%s\n", hexdigest); } else { // default to md5 MD5_Final(digest, &md5_ctx); for (n = 0; n < MD5_DIGEST_LENGTH; ++n) { sprintf(hexdigest+2*n, "%02x", digest[n]); } p += sprintf(p, "hash.value=%s\n", hexdigest); } int rc = tar_append_file_contents(tar, "EOD", 0600, getuid(), getgid(), eodbuf, p-eodbuf); return rc; } static int do_backup_tree(const String8& path) { int rc = 0; bool path_is_data = !strcmp(path.string(), "/data"); DIR *dp; dp = opendir(path.string()); struct dirent *de; while ((de = readdir(dp)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { continue; } if (path_is_data && !strcmp(de->d_name, "media") && vdc->isEmulatedStorage()) { logmsg("do_backup_tree: skipping datamedia\n"); continue; } struct stat st; String8 filepath(path); filepath += "/"; filepath += de->d_name; memset(&st, 0, sizeof(st)); rc = lstat(filepath.string(), &st); if (rc != 0) { logmsg("do_backup_tree: path=%s, lstat failed, rc=%d\n", path.string(), rc); break; } if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) { logmsg("do_backup_tree: path=%s, ignoring special file\n", path.string()); continue; } if (S_ISDIR(st.st_mode)) { rc = tar_append_file(tar, filepath.string(), filepath.string()); if (rc != 0) { logmsg("do_backup_tree: path=%s, tar_append_file failed, rc=%d\n", path.string(), rc); break; } rc = do_backup_tree(filepath); if (rc != 0) { logmsg("do_backup_tree: path=%s, recursion failed, rc=%d\n", path.string(), rc); break; } } else { rc = tar_append_file(tar, filepath.string(), filepath.string()); if (rc != 0) { logmsg("do_backup_tree: path=%s, tar_append_file failed, rc=%d\n", path.string(), rc); break; } } } closedir(dp); return rc; } static int tar_append_device_contents(TAR* t, const char* devname, const char* savename) { struct stat st; memset(&st, 0, sizeof(st)); if (lstat(devname, &st) != 0) { logmsg("tar_append_device_contents: lstat %s failed\n", devname); return -1; } st.st_mode = 0644 | S_IFREG; int fd = open(devname, O_RDONLY); if (fd < 0) { logmsg("tar_append_device_contents: open %s failed\n", devname); return -1; } st.st_size = lseek64(fd, 0, SEEK_END); close(fd); th_set_from_stat(t, &st); th_set_path(t, savename); if (th_write(t) != 0) { logmsg("tar_append_device_contents: th_write failed\n"); return -1; } if (tar_append_regfile(t, devname) != 0) { logmsg("tar_append_device_contents: tar_append_regfile %s failed\n", devname); return -1; } return 0; } int do_backup(int argc, char **argv) { int rc = 1; int n; int i; int len; int written; const char* opt_compress = "gzip"; const char* opt_hash = "md5"; int optidx = 0; while (optidx < argc && argv[optidx][0] == '-' && argv[optidx][1] == '-') { char* optname = &argv[optidx][2]; ++optidx; char* optval = strchr(optname, '='); if (optval) { *optval = '\0'; ++optval; } else { if (optidx >= argc) { logmsg("No argument to --%s\n", optname); return -1; } optval = argv[optidx]; ++optidx; } if (!strcmp(optname, "compress")) { opt_compress = optval; logmsg("do_backup: compress=%s\n", opt_compress); } else if (!strcmp(optname, "hash")) { opt_hash = optval; logmsg("do_backup: hash=%s\n", opt_hash); } else { logmsg("do_backup: invalid option name \"%s\"\n", optname); return -1; } } for (n = optidx; n < argc; ++n) { const char* partname = argv[n]; if (*partname == '-') ++partname; if (part_add(partname) != 0) { logmsg("Failed to add partition %s\n", partname); return -1; } } MessageSocket ms; ms.ClientInit(); ms.Show("Backup in progress..."); rc = create_tar(adb_ofd, opt_compress, "w"); if (rc != 0) { logmsg("do_backup: cannot open tar stream\n"); return rc; } append_sod(opt_hash); hash_name = strdup(opt_hash); for (i = 0; i < MAX_PART; ++i) { partspec* curpart = part_get(i); if (!curpart) break; part_set(curpart); if (!volume_is_mountable(curpart->vol) || volume_is_readonly(curpart->vol) || volume_is_verity(curpart->vol)) { rc = tar_append_device_contents(tar, curpart->vol->blk_device, curpart->name); } else { if (ensure_path_mounted(curpart->path) != 0) { rc = tar_append_device_contents(tar, curpart->vol->blk_device, curpart->name); if (rc != 0) { logmsg("do_backup: cannot backup %s\n", curpart->path); continue; } } String8 path(curpart->path); rc = do_backup_tree(path); ensure_path_unmounted(curpart->path); } } free(hash_name); hash_name = NULL; append_eod(opt_hash); tar_append_eof(tar); if (opt_compress) gzflush(gzf, Z_FINISH); ms.Dismiss(); logmsg("backup complete: rc=%d\n", rc); return rc; }