blob: 96b8d572fae21a3ff227134f1b0a1d4179be64aa [file] [log] [blame]
/*
* Copyright (C) 2019 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <cutils/log.h>
#include <cutils/properties.h>
#include <selinux/label.h>
#include <fs_mgr.h>
#include "roots.h"
#include "bu.h"
#define PATHNAME_RC "/tmp/burc"
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;
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 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_mount_point(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);
ui_print(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 rc = 1;
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);
load_volume_table();
if (!strcmp(opname, "backup")) {
ui_print("Backup in progress...");
rc = do_backup(argc - optidx, &argv[optidx]);
} else if (!strcmp(opname, "restore")) {
ui_print("Restore in progress...");
rc = do_restore(argc - optidx, &argv[optidx]);
} else {
logmsg("Unknown operation %s\n", opname);
rc = 1;
}
close(adb_ofd);
close(adb_ifd);
sleep(1);
logmsg("bu exiting\n");
do_exit(rc);
return rc;
}