Add basic cpio features

This commit is contained in:
topjohnwu 2017-03-08 00:54:23 +08:00
parent a4ce9f6f05
commit 1e3bcfc8cd
8 changed files with 367 additions and 75 deletions

View File

@ -9,7 +9,7 @@ LOCAL_C_INCLUDES := \
jni/ndk-compression/lz4/lib/ \ jni/ndk-compression/lz4/lib/ \
jni/ndk-compression/bzip2/ jni/ndk-compression/bzip2/
LOCAL_SRC_FILES := main.c unpack.c repack.c hexpatch.c parseimg.c compress.c utils.c LOCAL_SRC_FILES := main.c unpack.c repack.c hexpatch.c parseimg.c compress.c utils.c cpio.c
LOCAL_CFLAGS += -DZLIB_CONST LOCAL_CFLAGS += -DZLIB_CONST
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)

158
jni/magiskboot/cpio.c Normal file
View File

@ -0,0 +1,158 @@
#include "magiskboot.h"
#include "cpio.h"
static uint32_t x8u(char *hex) {
uint32_t val, inpos = 8, outpos;
char pattern[6];
while (*hex == '0') {
hex++;
if (!--inpos) return 0;
}
// Because scanf gratuitously treats %*X differently than printf does.
sprintf(pattern, "%%%dx%%n", inpos);
sscanf(hex, pattern, &val, &outpos);
if (inpos != outpos) error(1, "bad cpio header");
return val;
}
static void cpio_vec_insert(vector *v, cpio_file *n) {
cpio_file *f, *t;
int shift = 0;
// Insert in alphabet order
vec_for_each(v, f) {
if (shift) {
vec_entry(v)[_i] = t;
t = f;
continue;
}
t = f;
if (strcmp(f->filename, n->filename) == 0) {
// Replace, then all is done
vec_entry(v)[_i] = n;
return;
} else if (strcmp(f->filename, n->filename) > 0) {
// Insert, then start shifting
vec_entry(v)[_i] = n;
t = f;
shift = 1;
}
}
vec_push_back(v, t);
}
// Parse cpio file to a vector of cpio_file
void parse_cpio(const char *filename, vector *v) {
printf("\nLoading cpio: [%s]\n\n", filename);
int fd = open(filename, O_RDONLY);
if (fd < 0)
error(1, "Cannot open %s", filename);
cpio_newc_header header;
cpio_file *f;
while(read(fd, &header, 110) == 110) {
f = calloc(sizeof(*f), 1);
// f->ino = x8u(header.ino);
f->mode = x8u(header.mode);
f->uid = x8u(header.uid);
f->gid = x8u(header.gid);
// f->nlink = x8u(header.nlink);
// f->mtime = x8u(header.mtime);
f->filesize = x8u(header.filesize);
// f->devmajor = x8u(header.devmajor);
// f->devminor = x8u(header.devminor);
// f->rdevmajor = x8u(header.rdevmajor);
// f->rdevminor = x8u(header.rdevminor);
f->namesize = x8u(header.namesize);
// f->check = x8u(header.check);
f->filename = malloc(f->namesize);
read(fd, f->filename, f->namesize);
file_align(fd, 4, 0);
if (f->filesize) {
f->data = malloc(f->filesize);
read(fd, f->data, f->filesize);
file_align(fd, 4, 0);
}
vec_push_back(v, f);
}
close(fd);
}
void dump_cpio(const char *filename, vector *v) {
printf("\nDump cpio: [%s]\n\n", filename);
int fd = open_new(filename);
unsigned inode = 300000;
char header[111];
cpio_file *f;
vec_for_each(v, f) {
if (f->remove) continue;
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
inode++, // f->ino
f->mode,
f->uid,
f->gid,
1, // f->nlink
0, // f->mtime
f->filesize,
0, // f->devmajor
0, // f->devminor
0, // f->rdevmajor
0, // f->rdevminor
f->namesize,
0 // f->check
);
write(fd, header, 110);
write(fd, f->filename, f->namesize);
file_align(fd, 4, 1);
if (f->filesize) {
write(fd, f->data, f->filesize);
file_align(fd, 4, 1);
}
}
}
void cpio_vec_destroy(vector *v) {
// Free each cpio_file
cpio_file *f;
vec_for_each(v, f) {
free(f->filename);
free(f->data);
free(f);
}
vec_destroy(v);
}
void cpio_rm(int recursive, const char *entry, vector *v) {
cpio_file *f;
vec_for_each(v, f) {
if ((recursive && strncmp(f->filename, entry, strlen(entry)) == 0)
|| (strcmp(f->filename, entry) == 0) ) {
f->remove = 1;
}
}
}
void cpio_mkdir(mode_t mode, const char *entry, vector *v) {
cpio_file *f = calloc(sizeof(*f), 1);
f->mode = S_IFDIR | mode;
f->namesize = strlen(entry) + 1;
f->filename = malloc(f->namesize);
memcpy(f->filename, entry, f->namesize);
cpio_vec_insert(v, f);
}
void cpio_add(mode_t mode, const char *entry, const char *filename, vector *v) {
int fd = open(filename, O_RDONLY);
if (fd < 0)
error(1, "Cannot open %s", filename);
cpio_file *f = calloc(sizeof(*f), 1);
f->mode = S_IFREG | mode;
f->namesize = strlen(entry) + 1;
f->filename = malloc(f->namesize);
memcpy(f->filename, entry, f->namesize);
f->filesize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
f->data = malloc(f->filesize);
read(fd, f->data, f->filesize);
cpio_vec_insert(v, f);
}

42
jni/magiskboot/cpio.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef _CPIO_H_
#define _CPIO_H_
#include <stdint.h>
typedef struct cpio_file {
// uint32_t ino;
uint32_t mode;
uint32_t uid;
uint32_t gid;
// uint32_t nlink;
// uint32_t mtime;
uint32_t filesize;
// uint32_t devmajor;
// uint32_t devminor;
// uint32_t rdevmajor;
// uint32_t rdevminor;
uint32_t namesize;
// uint32_t check;
char *filename;
void *data;
int remove;
} cpio_file;
typedef struct cpio_newc_header {
char magic[6];
char ino[8];
char mode[8];
char uid[8];
char gid[8];
char nlink[8];
char mtime[8];
char filesize[8];
char devmajor[8];
char devminor[8];
char rdevmajor[8];
char rdevminor[8];
char namesize[8];
char check[8];
} cpio_newc_header;
#endif

View File

@ -1,27 +1,11 @@
#include "magiskboot.h" #include "magiskboot.h"
static int hex2int(char c) { static void hex2byte(const char *hex, unsigned char *str) {
int first = c / 16 - 3; char high, low;
int second = c % 16; for (int i = 0, length = strlen(hex); i < length; i += 2) {
int result = first * 10 + second; high = toupper(hex[i]) - '0';
if(result > 9) result--; low = toupper(hex[i + 1]) - '0';
return result; str[i / 2] = ((high > 9 ? high - 7 : high) << 4) + (low > 9 ? low - 7 : low);
}
static unsigned hex2ascii(char c, char d) {
int high = hex2int(c) * 16;
int low = hex2int(d);
return high + low;
}
static void hexstr2str(const char *hex, unsigned char *str) {
char buf = 0;
for(int i = 0, length = strlen(hex); i < length; ++i){
if(i % 2){
str[i / 2] = hex2ascii(buf, hex[i]);
} else{
buf = hex[i];
}
} }
} }
@ -32,8 +16,8 @@ void hexpatch(const char *image, const char *from, const char *to) {
mmap_rw(image, &file, &filesize); mmap_rw(image, &file, &filesize);
pattern = malloc(patternsize); pattern = malloc(patternsize);
patch = malloc(patchsize); patch = malloc(patchsize);
hexstr2str(from, pattern); hex2byte(from, pattern);
hexstr2str(to, patch); hex2byte(to, patch);
for (size_t i = 0; i < filesize - patternsize; ++i) { for (size_t i = 0; i < filesize - patternsize; ++i) {
if (memcmp(file + i, pattern, patternsize) == 0) { if (memcmp(file + i, pattern, patternsize) == 0) {
printf("Pattern %s found!\nPatching to %s\n", from, to); printf("Pattern %s found!\nPatching to %s\n", from, to);

View File

@ -3,7 +3,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h>
#include <unistd.h> #include <unistd.h>
#include <ctype.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/sendfile.h> #include <sys/sendfile.h>
@ -48,12 +50,37 @@ typedef enum {
QCDT, QCDT,
} file_t; } file_t;
typedef enum {
NONE,
RM,
MKDIR,
ADD
} command_t;
extern char *SUP_EXT_LIST[SUP_NUM]; extern char *SUP_EXT_LIST[SUP_NUM];
extern file_t SUP_TYPE_LIST[SUP_NUM]; extern file_t SUP_TYPE_LIST[SUP_NUM];
// Cannot declare in header, but place a copy here for convenience // Cannot declare in header, but place a copy here for convenience
// char *SUP_EXT_LIST[SUP_NUM] = { "gz", "xz", "lzma", "bz2", "lz4" }; // char *SUP_EXT_LIST[SUP_NUM] = { "gz", "xz", "lzma", "bz2", "lz4" };
// file_t SUP_TYPE_LIST[SUP_NUM] = { GZIP, XZ, LZMA, BZIP2, LZ4 }; // file_t SUP_TYPE_LIST[SUP_NUM] = { GZIP, XZ, LZMA, BZIP2, LZ4 };
// Vector
typedef struct vector {
size_t size;
size_t cap;
void **data;
} vector;
void vec_init(vector *v);
void vec_push_back(vector *v, void *p);
void vec_destroy(vector *v);
#define vec_size(v) (v)->size
#define vec_cap(v) (v)->cap
#define vec_entry(v) (v)->data
// vec_for_each(vector *v, void *e)
#define vec_for_each(v, e) \
e = (v)->data[0]; \
for (size_t _i = 0; _i < (v)->size; ++_i, e = (v)->data[_i])
// Global variables // Global variables
extern unsigned char *kernel, *ramdisk, *second, *dtb; extern unsigned char *kernel, *ramdisk, *second, *dtb;
extern boot_img_hdr hdr; extern boot_img_hdr hdr;
@ -62,9 +89,8 @@ extern int mtk_kernel, mtk_ramdisk;
// Main entries // Main entries
void unpack(const char *image); void unpack(const char *image);
void repack(const char *image); void repack(const char* orig_image, const char* out_image);
void hexpatch(const char *image, const char *from, const char *to); void hexpatch(const char *image, const char *from, const char *to);
void cpio(const char *filename);
void error(int rc, const char *msg, ...); void error(int rc, const char *msg, ...);
void parse_img(unsigned char *orig, size_t size); void parse_img(unsigned char *orig, size_t size);
void cleanup(); void cleanup();
@ -79,12 +105,20 @@ void comp_file(const char *method, const char *from);
int decomp(file_t type, const char *to, const unsigned char *from, size_t size); int decomp(file_t type, const char *to, const unsigned char *from, size_t size);
void decomp_file(char *from); void decomp_file(char *from);
// CPIO
void parse_cpio(const char *filename, vector *v);
void dump_cpio(const char *filename, vector *v);
void cpio_vec_destroy(vector *v);
void cpio_rm(int recursive, const char *entry, vector *v);
void cpio_mkdir(mode_t mode, const char *entry, vector *v);
void cpio_add(mode_t mode, const char *entry, const char *filename, vector *v);
// Utils // Utils
void mmap_ro(const char *filename, unsigned char **buf, size_t *size); void mmap_ro(const char *filename, unsigned char **buf, size_t *size);
void mmap_rw(const char *filename, unsigned char **buf, size_t *size); void mmap_rw(const char *filename, unsigned char **buf, size_t *size);
file_t check_type(const unsigned char *buf); file_t check_type(const unsigned char *buf);
void mem_align(size_t *pos, size_t align); void mem_align(size_t *pos, size_t align);
void file_align(int fd, size_t align); void file_align(int fd, size_t align, int out);
int open_new(const char *filename); int open_new(const char *filename);
void print_info(); void print_info();

View File

@ -6,22 +6,36 @@
static void usage(char *arg0) { static void usage(char *arg0) {
fprintf(stderr, "\n"); fprintf(stderr, "\n");
fprintf(stderr, "%s --unpack <bootimage>\n", arg0); fprintf(stderr, "%s --unpack <bootimg>\n", arg0);
fprintf(stderr, " Unpack <bootimage> to kernel, ramdisk.cpio, (second), (dtb) into the\n current directory\n\n"); fprintf(stderr, " Unpack <bootimg> to kernel, ramdisk.cpio, (second), (dtb) into the\n current directory\n\n");
fprintf(stderr, "%s --repack <bootimage>\n", arg0);
fprintf(stderr, "%s --repack <origbootimg> [outbootimg]\n", arg0);
fprintf(stderr, " Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n"); fprintf(stderr, " Repack kernel, ramdisk.cpio[.ext], second, dtb... from current directory\n");
fprintf(stderr, " to new-image.img. <bootimage> is the original boot image you've just unpacked.\n"); fprintf(stderr, " to [outbootimg], or new-boot.img if not specified.\n");
fprintf(stderr, " If file ramdisk.cpio exists, it will auto re-compress with the same method\n"); fprintf(stderr, " It will compress ramdisk.cpio with the same method used in <origbootimg>\n");
fprintf(stderr, " used in <bootimage>, or it will attempt to find ramdisk.cpio.[ext], and repack\n"); fprintf(stderr, " if exists, or attempt to find ramdisk.cpio.[ext], and repack\n");
fprintf(stderr, " directly with the compressed file\n\n"); fprintf(stderr, " directly with the compressed ramdisk file\n\n");
fprintf(stderr, "%s --hexpatch <file> <hexpattern1> <hexpattern2>\n", arg0); fprintf(stderr, "%s --hexpatch <file> <hexpattern1> <hexpattern2>\n", arg0);
fprintf(stderr, " Search <hexpattern1> in <file>, and replace with <hexpattern2>\n\n"); fprintf(stderr, " Search <hexpattern1> in <file>, and replace with <hexpattern2>\n\n");
fprintf(stderr, "%s --cpio-<cmd> <incpio> <outcpio> [flags...] [params...]\n", arg0);
fprintf(stderr, " Do cpio related cmds to <incpio> and output to <outcpio>\n Supported commands:\n");
fprintf(stderr, " --cpio-rm [-r] <entry>\n Remove entry from cpio, flag -r to remove recursively\n");
fprintf(stderr, " --cpio-mkdir <mode> <entry>\n Create directory as an <entry>\n");
fprintf(stderr, " --cpio-add <mode> <entry> <infile>\n Add <infile> as an <entry>; will replace <entry> if already exists\n");
fprintf(stderr, " e.g: the command to add /tmp/tmpfile as entry init.magisk.rc:\n");
fprintf(stderr, " %s --cpio-add <incpio> <outcpio> 0750 init.magisk.rc /tmp/tmpfile\n\n", arg0);
fprintf(stderr, "%s --compress[=method] <file>\n", arg0); fprintf(stderr, "%s --compress[=method] <file>\n", arg0);
fprintf(stderr, " Compress <file> with [method], or gzip if not specified.\n Supported methods: " SUP_LIST "\n\n"); fprintf(stderr, " Compress <file> with [method], or gzip if not specified.\n Supported methods: " SUP_LIST "\n\n");
fprintf(stderr, "%s --decompress <file>\n", arg0); fprintf(stderr, "%s --decompress <file>\n", arg0);
fprintf(stderr, " Auto check file type, and decompress <file> accordingly\n Supported methods: " SUP_LIST "\n\n"); fprintf(stderr, " Auto check file type, and decompress <file> accordingly\n Supported methods: " SUP_LIST "\n\n");
fprintf(stderr, "%s --cleanup\n", arg0); fprintf(stderr, "%s --cleanup\n", arg0);
fprintf(stderr, " Cleanup the current working directory\n\n"); fprintf(stderr, " Cleanup the current working directory\n\n");
exit(1); exit(1);
} }
@ -36,33 +50,67 @@ void error(int rc, const char *msg, ...) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
printf("MagiskBoot (by topjohnwu) - Boot Image Modification Tool\n"); printf("MagiskBoot (by topjohnwu) - Boot Image Modification Tool\n");
if (argc < 3) {
if (strcmp(argv[1], "--cleanup") == 0) {
cleanup();
} else {
usage(argv[0]);
}
} else {
if (strcmp(argv[1], "--unpack") == 0) {
unpack(argv[2]);
} else if (strcmp(argv[1], "--repack") == 0) {
repack(argv[2]);
} else if (strcmp(argv[1], "--hexpatch") == 0) {
if (argc < 5)
usage(argv[0]);
hexpatch(argv[2], argv[3], argv[4]);
} else if (strcmp(argv[1], "--decompress") == 0) {
decomp_file(argv[2]);
} else if (strstr(argv[1], "--compress") != NULL) {
char *method;
method = strchr(argv[1], '=');
if (method == NULL) method = "gzip";
else method++;
comp_file(method, argv[2]);
} else {
usage(argv[0]);
}
}
return 0;
if (argc > 1 && strcmp(argv[1], "--cleanup") == 0) {
cleanup();
} else if (argc > 2 && strcmp(argv[1], "--unpack") == 0) {
unpack(argv[2]);
} else if (argc > 2 && strcmp(argv[1], "--repack") == 0) {
repack(argv[2], argc > 3 ? argv[3] : NEW_BOOT);
} else if (argc > 2 && strcmp(argv[1], "--decompress") == 0) {
decomp_file(argv[2]);
} else if (argc > 2 && strncmp(argv[1], "--compress", 10) == 0) {
char *method;
method = strchr(argv[1], '=');
if (method == NULL) method = "gzip";
else method++;
comp_file(method, argv[2]);
} else if (argc > 4 && strcmp(argv[1], "--hexpatch") == 0) {
hexpatch(argv[2], argv[3], argv[4]);
} else if (argc > 4 && strncmp(argv[1], "--cpio", 6) == 0) {
int recursive = 0;
char *command;
command_t cmd;
command = strchr(argv[1] + 2, '-');
if (command == NULL) usage(argv[0]);
else command++;
if (strcmp(command, "rm") == 0) {
cmd = RM;
if (argc > 5 && strcmp(argv[4], "-r") == 0) {
recursive = 1;
argv[4] = argv[5];
argc--;
}
} else if (argc > 5 && strcmp(command, "mkdir") == 0) {
cmd = MKDIR;
} else if (argc > 6 && strcmp(command, "add") == 0) {
cmd = ADD;
} else {
cmd = NONE;
usage(argv[0]);
}
vector v;
vec_init(&v);
parse_cpio(argv[2], &v);
switch(cmd) {
case RM:
cpio_rm(recursive, argv[4], &v);
break;
case MKDIR:
cpio_mkdir(strtoul(argv[4], NULL, 8), argv[5], &v);
break;
case ADD:
cpio_add(strtoul(argv[4], NULL, 8), argv[5], argv[6], &v);
break;
default:
// Never happen
break;
}
dump_cpio(argv[3], &v);
cpio_vec_destroy(&v);
} else {
usage(argv[0]);
}
return 0;
} }

View File

@ -20,20 +20,20 @@ static void restore_buf(const void *buf, size_t size, int fd) {
} }
} }
void repack(const char* image) { void repack(const char* orig_image, const char* out_image) {
size_t size; size_t size;
unsigned char *orig; unsigned char *orig;
char name[PATH_MAX]; char name[PATH_MAX];
// Load original image // Load original image
mmap_ro(image, &orig, &size); mmap_ro(orig_image, &orig, &size);
// Parse original image // Parse original image
printf("\nParsing boot image: [%s]\n\n", image); printf("\nParsing boot image: [%s]\n\n", orig_image);
parse_img(orig, size); parse_img(orig, size);
// Create new image // Create new image
int fd = open_new(NEW_BOOT); int fd = open_new(out_image);
// Set all sizes to 0 // Set all sizes to 0
hdr.kernel_size = 0; hdr.kernel_size = 0;
@ -51,7 +51,7 @@ void repack(const char* image) {
hdr.kernel_size += 512; hdr.kernel_size += 512;
} }
hdr.kernel_size += restore(KERNEL_FILE, fd); hdr.kernel_size += restore(KERNEL_FILE, fd);
file_align(fd, hdr.page_size); file_align(fd, hdr.page_size, 1);
// Restore ramdisk // Restore ramdisk
if (mtk_ramdisk) { if (mtk_ramdisk) {
@ -90,22 +90,22 @@ void repack(const char* image) {
if (!found) if (!found)
error(1, "No ramdisk exists!"); error(1, "No ramdisk exists!");
hdr.ramdisk_size += restore(name, fd); hdr.ramdisk_size += restore(name, fd);
file_align(fd, hdr.page_size); file_align(fd, hdr.page_size, 1);
// Restore second // Restore second
if (access(SECOND_FILE, R_OK) == 0) { if (access(SECOND_FILE, R_OK) == 0) {
hdr.second_size += restore(SECOND_FILE, fd); hdr.second_size += restore(SECOND_FILE, fd);
file_align(fd, hdr.page_size); file_align(fd, hdr.page_size, 1);
} }
// Restore dtb // Restore dtb
if (access(DTB_FILE, R_OK) == 0) { if (access(DTB_FILE, R_OK) == 0) {
hdr.dt_size += restore(DTB_FILE, fd); hdr.dt_size += restore(DTB_FILE, fd);
file_align(fd, hdr.page_size); file_align(fd, hdr.page_size, 1);
} }
// Write header back // Write header back
printf("\nRepack to boot image: [%s]\n\n", NEW_BOOT); printf("\nRepack to boot image: [%s]\n\n", out_image);
print_info(); print_info();
lseek(fd, 0, SEEK_SET); lseek(fd, 0, SEEK_SET);
write(fd, &hdr, sizeof(hdr)); write(fd, &hdr, sizeof(hdr));

View File

@ -62,13 +62,15 @@ void mem_align(size_t *pos, size_t align) {
} }
} }
void file_align(int fd, size_t align) { void file_align(int fd, size_t align, int out) {
size_t pos = lseek(fd, 0, SEEK_CUR); size_t pos = lseek(fd, 0, SEEK_CUR);
size_t mask = align - 1; size_t mask = align - 1;
if (pos & mask) { if (pos & mask) {
pos += align - (pos & mask); pos += align - (pos & mask);
ftruncate(fd, pos); if (out) {
lseek(fd, 0, SEEK_END); ftruncate(fd, pos);
}
lseek(fd, pos, SEEK_SET);
} }
} }
@ -138,3 +140,27 @@ void cleanup() {
unlink(name); unlink(name);
} }
} }
void vec_init(vector *v) {
vec_size(v) = 0;
vec_cap(v) = 1;
vec_entry(v) = malloc(sizeof(void*));
}
void vec_push_back(vector *v, void *p) {
if (v == NULL) return;
if (vec_size(v) == vec_cap(v)) {
vec_cap(v) *= 2;
vec_entry(v) = realloc(vec_entry(v), sizeof(void*) * vec_cap(v));
}
vec_entry(v)[vec_size(v)] = p;
++vec_size(v);
}
void vec_destroy(vector *v) {
// Will not free each entry!
// Manually free each entry, then call this function
vec_size(v) = 0;
vec_cap(v) = 0;
free(v->data);
}