summaryrefslogtreecommitdiffstats
path: root/backup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backup.cpp')
-rw-r--r--backup.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/backup.cpp b/backup.cpp
new file mode 100644
index 0000000..ad23b17
--- /dev/null
+++ b/backup.cpp
@@ -0,0 +1,300 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/vfs.h>
+#include <time.h>
+
+#include "cutils/properties.h"
+
+#include "roots.h"
+
+#include "bu.h"
+
+#include "voldclient.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 = lseek(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 = lseek(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 = lseek(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;
+ }
+ }
+
+ 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);
+
+ logmsg("backup complete: rc=%d\n", rc);
+
+ return rc;
+}
+