diff options
Diffstat (limited to 'bu.cpp')
-rw-r--r-- | bu.cpp | 394 |
1 files changed, 394 insertions, 0 deletions
@@ -0,0 +1,394 @@ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.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 <cutils/properties.h> +#include <cutils/log.h> + +#include <selinux/label.h> + +#include "roots.h" + +#include "bu.h" + +#include "messagesocket.h" + +#include "voldclient.h" + +#define PATHNAME_RC "/tmp/burc" + +#define PATHNAME_XCOMP_ENABLE "/sys/fs/xcomp/enable" + +using namespace std; + +using namespace android; + +struct selabel_handle *sehandle; + +int adb_ifd; +int adb_ofd; +TAR* tar; +gzFile gzf; + +char* hash_name; +size_t hash_datalen; +SHA_CTX sha_ctx; +MD5_CTX md5_ctx; + +static MessageSocket ms; + +void +ui_print(const char* format, ...) { + char buffer[256]; + + va_list ap; + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + fputs(buffer, stdout); +} + +void logmsg(const char *fmt, ...) +{ + char msg[1024]; + FILE* fp; + va_list ap; + + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + fp = fopen("/tmp/bu.log", "a"); + if (fp) { + fprintf(fp, "[%d] %s", getpid(), msg); + fclose(fp); + } +} + +static int xcomp_enable_get(void) +{ + int val = 0; + int fd; + char buf[12+1+1]; + + fd = open(PATHNAME_XCOMP_ENABLE, O_RDONLY); + if (fd < 0) + return 0; + memset(buf, 0, sizeof(buf)); + if (read(fd, buf, sizeof(buf)) > 0) { + val = atoi(buf); + } + close(fd); + return val; +} + +static void xcomp_enable_set(int val) +{ + int fd; + char buf[12+1+1]; + int len; + + fd = open(PATHNAME_XCOMP_ENABLE, O_RDWR); + if (fd < 0) + return; + len = sprintf(buf, "%d\n", val); + write(fd, buf, len); + close(fd); +} + +static partspec partlist[MAX_PART]; +static partspec* curpart; + +int part_add(const char* name) +{ + Volume* vol = NULL; + char* path = NULL; + int i; + + path = (char*)malloc(1+strlen(name)+1); + sprintf(path, "/%s", name); + vol = volume_for_path(path); + if (vol == NULL || vol->fs_type == NULL) { + logmsg("missing vol info for %s, ignoring\n", name); + goto err; + } + + for (i = 0; i < MAX_PART; ++i) { + if (partlist[i].name == NULL) { + partlist[i].name = strdup(name); + partlist[i].path = path; + partlist[i].vol = vol; + logmsg("part_add: i=%d, name=%s, path=%s\n", i, name, path); + return 0; + } + if (strcmp(partlist[i].name, name) == 0) { + logmsg("duplicate partition %s, ignoring\n", name); + goto err; + } + } + +err: + free(path); + return -1; +} + +partspec* part_get(int i) +{ + if (i >= 0 && i < MAX_PART) { + if (partlist[i].name != NULL) { + return &partlist[i]; + } + } + return NULL; +} + +partspec* part_find(const char* name) +{ + for (int i = 0; i < MAX_PART; ++i) { + if (partlist[i].name && !strcmp(name, partlist[i].name)) { + return &partlist[i]; + } + } + return NULL; +} + +void part_set(partspec* part) +{ + curpart = part; + curpart->off = 0; +} + +int update_progress(uint64_t off) +{ + static time_t last_time = 0; + static int last_pct = 0; + if (curpart) { + curpart->off += off; + time_t now = time(NULL); + int pct = min(100, (int)((uint64_t)100*curpart->off/curpart->used)); + if (now != last_time && pct != last_pct) { + char msg[256]; + sprintf(msg, "%s: %d%% complete", curpart->name, pct); + ms.Show(msg); + last_time = now; + last_pct = pct; + } + } + return 0; +} + +static int tar_cb_open(const char* path, int mode, ...) +{ + errno = EINVAL; + return -1; +} + +static int tar_cb_close(int fd) +{ + return 0; +} + +static ssize_t tar_cb_read(int fd, void* buf, size_t len) +{ + ssize_t nread; + nread = ::read(fd, buf, len); + if (nread > 0 && hash_name) { + SHA1_Update(&sha_ctx, (u_char*)buf, nread); + MD5_Update(&md5_ctx, buf, nread); + hash_datalen += nread; + } + update_progress(nread); + return nread; +} + +static ssize_t tar_cb_write(int fd, const void* buf, size_t len) +{ + ssize_t written = 0; + + if (hash_name) { + SHA1_Update(&sha_ctx, (u_char*)buf, len); + MD5_Update(&md5_ctx, buf, len); + hash_datalen += len; + } + + while (len > 0) { + ssize_t n = ::write(fd, buf, len); + if (n < 0) { + logmsg("tar_cb_write: error: n=%d\n", n); + return n; + } + if (n == 0) + break; + buf = (const char *)buf + n; + len -= n; + written += n; + } + update_progress(written); + return written; +} + +static tartype_t tar_io = { + tar_cb_open, + tar_cb_close, + tar_cb_read, + tar_cb_write +}; + +static ssize_t tar_gz_cb_read(int fd, void* buf, size_t len) +{ + int nread; + nread = gzread(gzf, buf, len); + if (nread > 0 && hash_name) { + SHA1_Update(&sha_ctx, (u_char*)buf, nread); + MD5_Update(&md5_ctx, buf, nread); + hash_datalen += nread; + } + update_progress(nread); + return nread; +} + +static ssize_t tar_gz_cb_write(int fd, const void* buf, size_t len) +{ + ssize_t written = 0; + + if (hash_name) { + SHA1_Update(&sha_ctx, (u_char*)buf, len); + MD5_Update(&md5_ctx, buf, len); + hash_datalen += len; + } + + while (len > 0) { + ssize_t n = gzwrite(gzf, buf, len); + if (n < 0) { + logmsg("tar_gz_cb_write: error: n=%d\n", n); + return n; + } + if (n == 0) + break; + buf = (const char *)buf + n; + len -= n; + written += n; + } + update_progress(written); + return written; +} + +static tartype_t tar_io_gz = { + tar_cb_open, + tar_cb_close, + tar_gz_cb_read, + tar_gz_cb_write +}; + +int create_tar(int fd, const char* compress, const char* mode) +{ + int rc = -1; + + SHA1_Init(&sha_ctx); + MD5_Init(&md5_ctx); + + if (!compress || strcasecmp(compress, "none") == 0) { + rc = tar_fdopen(&tar, fd, "foobar", &tar_io, + 0, /* oflags: unused */ + 0, /* mode: unused */ + TAR_GNU | TAR_STORE_SELINUX /* options */); + } + else if (strcasecmp(compress, "gzip") == 0) { + gzf = gzdopen(fd, mode); + if (gzf != NULL) { + rc = tar_fdopen(&tar, 0, "foobar", &tar_io_gz, + 0, /* oflags: unused */ + 0, /* mode: unused */ + TAR_GNU | TAR_STORE_SELINUX /* options */); + } + } + return rc; +} + +static void do_exit(int rc) +{ + char rcstr[80]; + int len; + len = sprintf(rcstr, "%d\n", rc); + + unlink(PATHNAME_RC); + int fd = open(PATHNAME_RC, O_RDWR|O_CREAT, 0644); + write(fd, rcstr, len); + close(fd); + exit(rc); +} + +int main(int argc, char **argv) +{ + int n; + int rc = 1; + int xcomp_enable; + + const char* logfile = "/tmp/recovery.log"; + adb_ifd = dup(STDIN_FILENO); + adb_ofd = dup(STDOUT_FILENO); + freopen(logfile, "a", stdout); setbuf(stdout, NULL); + freopen(logfile, "a", stderr); setbuf(stderr, NULL); + + logmsg("bu: invoked with %d args\n", argc); + + if (argc < 2) { + logmsg("Not enough args (%d)\n", argc); + do_exit(1); + } + + // progname args... + int optidx = 1; + const char* opname = argv[optidx++]; + + struct selinux_opt seopts[] = { + { SELABEL_OPT_PATH, "/file_contexts" } + }; + sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); + + xcomp_enable = xcomp_enable_get(); + xcomp_enable_set(0); + + load_volume_table(); + vdc = new VoldClient(); + vdc->start(); + + ms.ClientInit(); + + if (!strcmp(opname, "backup")) { + ms.Show("Backup in progress..."); + rc = do_backup(argc-optidx, &argv[optidx]); + } + else if (!strcmp(opname, "restore")) { + ms.Show("Restore in progress..."); + rc = do_restore(argc-optidx, &argv[optidx]); + } + else { + logmsg("Unknown operation %s\n", opname); + rc = 1; + } + + ms.Dismiss(); + + xcomp_enable_set(xcomp_enable); + + close(adb_ofd); + close(adb_ifd); + + sleep(1); + + logmsg("bu exiting\n"); + + do_exit(rc); + + return rc; +} |