android_mt6572_jiabo/bootable/recovery/backup.cpp
2025-09-05 16:56:03 +08:00

300 lines
8.3 KiB
C++

#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 = 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;
}
}
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;
}