Read fstab from device tree

In previous versions, magiskinit will not early mount if /sepolicy is detected. However on OP5/5T latest betas, the devices are fully trebelized,
but for some reason the file /sepolicy still exists, making magiskinit think it is NOT a treble device and doesn't work properly.

So to properly fix this issue, I will have to use the "official" way - check fstab in device trees. Any block mentioned in the fstab in device trees
are supposed to be early mounted. Currently magiskinit will only mount system and vendor even if other partitions exists in the dtb fstab, since other
partitions are not used to construct sepolicy (currently).

These changes can also fix #373, since we dynamically detect PARTNAME from device trees.
This commit is contained in:
topjohnwu 2018-07-18 00:45:10 +08:00
parent 3c1aca114f
commit adf95ce3a0

View File

@ -55,12 +55,20 @@
#define VLOG(fmt, ...) #define VLOG(fmt, ...)
#endif #endif
#define DEFAULT_DT_DIR "/proc/device-tree/firmware/android"
extern policydb_t *policydb; extern policydb_t *policydb;
int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, NULL }; int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, NULL };
struct cmdline { struct cmdline {
char skip_initramfs; char skip_initramfs;
char slot[3]; char slot[3];
char dt_dir[128];
};
struct early_mnt {
char system[32];
char vendor[32];
}; };
struct device { struct device {
@ -76,8 +84,6 @@ static void parse_cmdline(struct cmdline *cmd) {
memset(cmd, 0, sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd));
char cmdline[4096]; char cmdline[4096];
mkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, NULL);
int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC); int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0'; cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0';
close(fd); close(fd);
@ -89,33 +95,38 @@ static void parse_cmdline(struct cmdline *cmd) {
sscanf(tok, "androidboot.slot=%c", cmd->slot + 1); sscanf(tok, "androidboot.slot=%c", cmd->slot + 1);
} else if (strcmp(tok, "skip_initramfs") == 0) { } else if (strcmp(tok, "skip_initramfs") == 0) {
cmd->skip_initramfs = 1; cmd->skip_initramfs = 1;
} else if (strncmp(tok, "androidboot.android_dt_dir", 26) == 0) {
sscanf(tok, "androidboot.android_dt_dir=%s", cmd->dt_dir);
} }
} }
VLOG("cmdline: skip_initramfs[%d] slot[%s]\n", cmd->skip_initramfs, cmd->slot); if (cmd->dt_dir[0] == '\0')
strcpy(cmd->dt_dir, DEFAULT_DT_DIR);
VLOG("cmdline: skip_initramfs[%d] slot[%s] dt_dir[%s]\n", cmd->skip_initramfs, cmd->slot, cmd->dt_dir);
} }
static void parse_device(struct device *dev, char *uevent) { static void parse_device(struct device *dev, const char *uevent) {
dev->partname[0] = '\0'; dev->partname[0] = '\0';
char *tok; FILE *fp = xfopen(uevent, "r");
tok = strtok(uevent, "\n"); char buf[64];
while (tok != NULL) { while (fgets(buf, sizeof(buf), fp)) {
if (strncmp(tok, "MAJOR", 5) == 0) { if (strncmp(buf, "MAJOR", 5) == 0) {
sscanf(tok, "MAJOR=%ld", (long*) &dev->major); sscanf(buf, "MAJOR=%ld", (long*) &dev->major);
} else if (strncmp(tok, "MINOR", 5) == 0) { } else if (strncmp(buf, "MINOR", 5) == 0) {
sscanf(tok, "MINOR=%ld", (long*) &dev->minor); sscanf(buf, "MINOR=%ld", (long*) &dev->minor);
} else if (strncmp(tok, "DEVNAME", 7) == 0) { } else if (strncmp(buf, "DEVNAME", 7) == 0) {
sscanf(tok, "DEVNAME=%s", dev->devname); sscanf(buf, "DEVNAME=%s", dev->devname);
} else if (strncmp(tok, "PARTNAME", 8) == 0) { } else if (strncmp(buf, "PARTNAME", 8) == 0) {
sscanf(tok, "PARTNAME=%s", dev->partname); sscanf(buf, "PARTNAME=%s", dev->partname);
} }
tok = strtok(NULL, "\n");
} }
fclose(fp);
VLOG("%s [%s] (%u, %u)\n", dev->devname, dev->partname, (unsigned) dev->major, (unsigned) dev->minor); VLOG("%s [%s] (%u, %u)\n", dev->devname, dev->partname, (unsigned) dev->major, (unsigned) dev->minor);
} }
static int setup_block(struct device *dev, const char *partname) { static int setup_block(struct device *dev, const char *partname) {
char buffer[1024], path[128]; char path[128];
struct dirent *entry; struct dirent *entry;
DIR *dir = opendir("/sys/dev/block"); DIR *dir = opendir("/sys/dev/block");
if (dir == NULL) if (dir == NULL)
@ -124,14 +135,10 @@ static int setup_block(struct device *dev, const char *partname) {
while ((entry = readdir(dir))) { while ((entry = readdir(dir))) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue; continue;
snprintf(path, sizeof(path), "/sys/dev/block/%s/uevent", entry->d_name); sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
int fd = open(path, O_RDONLY | O_CLOEXEC); parse_device(dev, path);
ssize_t size = read(fd, buffer, sizeof(buffer));
buffer[size] = '\0';
close(fd);
parse_device(dev, buffer);
if (strcasecmp(dev->partname, partname) == 0) { if (strcasecmp(dev->partname, partname) == 0) {
snprintf(dev->path, sizeof(dev->path), "/dev/block/%s", dev->devname); sprintf(dev->path, "/dev/block/%s", dev->devname);
found = 1; found = 1;
break; break;
} }
@ -147,6 +154,35 @@ static int setup_block(struct device *dev, const char *partname) {
return 0; return 0;
} }
static void get_partname(const struct cmdline *cmd, struct early_mnt *mnt) {
char buf[128];
memset(mnt, 0, sizeof(mnt));
if (cmd->skip_initramfs) {
// System root, we have to early mount system
sprintf(mnt->system, "system%s", cmd->slot);
} else {
sprintf(buf, "%s/fstab/system/dev", cmd->dt_dir);
if (access(buf, F_OK) == 0) {
// Early mount system
int fd = open(buf, O_RDONLY | O_CLOEXEC);
read(fd, buf, sizeof(buf));
close(fd);
sprintf(mnt->system, "%s%s", strrchr(buf, '/') + 1, cmd->slot);
}
}
sprintf(buf, "%s/fstab/vendor/dev", cmd->dt_dir);
if (access(buf, F_OK) == 0) {
// Early mount system
int fd = open(buf, O_RDONLY | O_CLOEXEC);
read(fd, buf, sizeof(buf));
close(fd);
sprintf(mnt->vendor, "%s%s", strrchr(buf, '/') + 1, cmd->slot);
}
VLOG("system=[%s] vendor=[%s]\n", mnt->system, mnt->vendor);
}
static int strend(const char *s1, const char *s2) { static int strend(const char *s1, const char *s2) {
size_t l1 = strlen(s1); size_t l1 = strlen(s1);
size_t l2 = strlen(s2); size_t l2 = strlen(s2);
@ -181,7 +217,7 @@ static int compile_cil() {
char plat[10]; char plat[10];
int fd = open(SPLIT_NONPLAT_VER, O_RDONLY | O_CLOEXEC); int fd = open(SPLIT_NONPLAT_VER, O_RDONLY | O_CLOEXEC);
plat[read(fd, plat, sizeof(plat)) - 1] = '\0'; plat[read(fd, plat, sizeof(plat)) - 1] = '\0';
snprintf(path, sizeof(path), SPLIT_PLAT_MAPPING, plat); sprintf(path, SPLIT_PLAT_MAPPING, plat);
mmap_ro(path, &addr, &size); mmap_ro(path, &addr, &size);
VLOG("cil_add[%s]\n", path); VLOG("cil_add[%s]\n", path);
cil_add_file(db, path, addr, size); cil_add_file(db, path, addr, size);
@ -194,7 +230,7 @@ static int compile_cil() {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue; continue;
if (strend(entry->d_name, ".cil") == 0) { if (strend(entry->d_name, ".cil") == 0) {
snprintf(path, sizeof(path), NONPLAT_POLICY_DIR "%s", entry->d_name); sprintf(path, NONPLAT_POLICY_DIR "%s", entry->d_name);
mmap_ro(path, &addr, &size); mmap_ro(path, &addr, &size);
VLOG("cil_add[%s]\n", path); VLOG("cil_add[%s]\n", path);
cil_add_file(db, path, addr, size); cil_add_file(db, path, addr, size);
@ -216,7 +252,7 @@ static int verify_precompiled() {
DIR *dir; DIR *dir;
struct dirent *entry; struct dirent *entry;
int fd; int fd;
char sys_sha[70], ven_sha[70]; char sys_sha[64], ven_sha[64];
// init the strings with different value // init the strings with different value
sys_sha[0] = 0; sys_sha[0] = 0;
@ -228,7 +264,7 @@ static int verify_precompiled() {
continue; continue;
if (strend(entry->d_name, ".sha256") == 0) { if (strend(entry->d_name, ".sha256") == 0) {
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC); fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
ven_sha[read(fd, ven_sha, sizeof(ven_sha)) - 1] = '\0'; read(fd, ven_sha, sizeof(ven_sha));
close(fd); close(fd);
break; break;
} }
@ -240,25 +276,29 @@ static int verify_precompiled() {
continue; continue;
if (strend(entry->d_name, ".sha256") == 0) { if (strend(entry->d_name, ".sha256") == 0) {
fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC); fd = openat(dirfd(dir), entry->d_name, O_RDONLY | O_CLOEXEC);
sys_sha[read(fd, sys_sha, sizeof(sys_sha)) - 1] = '\0'; read(fd, sys_sha, sizeof(sys_sha));
close(fd); close(fd);
break; break;
} }
} }
closedir(dir); closedir(dir);
VLOG("sys_sha[%s]\nven_sha[%s]\n", sys_sha, ven_sha); VLOG("sys_sha[%.*s]\nven_sha[%.*s]\n", sizeof(sys_sha), sys_sha, sizeof(ven_sha), ven_sha);
return strcmp(sys_sha, ven_sha) == 0; return memcmp(sys_sha, ven_sha, sizeof(sys_sha)) == 0;
} }
static int patch_sepolicy() { static int patch_sepolicy() {
if (access("/sepolicy", R_OK) == 0) int init_patch = 0;
load_policydb("/sepolicy"); if (access(SPLIT_PRECOMPILE, R_OK) == 0 && verify_precompiled()) {
else if (access(SPLIT_PRECOMPILE, R_OK) == 0 && verify_precompiled()) init_patch = 1;
load_policydb(SPLIT_PRECOMPILE); load_policydb(SPLIT_PRECOMPILE);
else if (access(SPLIT_PLAT_CIL, R_OK) == 0) } else if (access(SPLIT_PLAT_CIL, R_OK) == 0) {
init_patch = 1;
compile_cil(); compile_cil();
else } else if (access("/sepolicy", R_OK) == 0) {
load_policydb("/sepolicy");
} else {
return 1; return 1;
}
sepol_magisk_rules(); sepol_magisk_rules();
dump_policydb("/sepolicy"); dump_policydb("/sepolicy");
@ -269,6 +309,20 @@ static int patch_sepolicy() {
link("/sepolicy", "/sepolicy_debug"); link("/sepolicy", "/sepolicy_debug");
} }
if (init_patch) {
// Force init to load /sepolicy
void *addr;
size_t size;
mmap_rw("/init", &addr, &size);
for (int i = 0; i < size; ++i) {
if (memcmp(addr + i, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL) - 1) == 0) {
memcpy(addr + i + sizeof(SPLIT_PLAT_CIL) - 4, "xxx", 3);
break;
}
}
munmap(addr, size);
}
return 0; return 0;
} }
@ -370,14 +424,24 @@ int main(int argc, char *argv[]) {
// Backup self // Backup self
rename("/init", "/init.bak"); rename("/init", "/init.bak");
// Communicate with kernel using procfs and sysfs
mkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, NULL);
mkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, NULL);
struct cmdline cmd; struct cmdline cmd;
parse_cmdline(&cmd); parse_cmdline(&cmd);
/* ***********
* Initialize
* ***********/
int root = open("/", O_RDONLY | O_CLOEXEC); int root = open("/", O_RDONLY | O_CLOEXEC);
if (cmd.skip_initramfs) { if (cmd.skip_initramfs) {
// Clear rootfs // Clear rootfs
excl_list = (char *[]) { "overlay", ".backup", "proc", "init.bak", NULL }; excl_list = (char *[]) { "overlay", ".backup", "proc", "sys", "init.bak", NULL };
frm_rf(root); frm_rf(root);
} else if (access("/ramdisk.cpio.xz", R_OK) == 0) { } else if (access("/ramdisk.cpio.xz", R_OK) == 0) {
// High compression mode // High compression mode
@ -391,7 +455,7 @@ int main(int argc, char *argv[]) {
struct vector v; struct vector v;
vec_init(&v); vec_init(&v);
parse_cpio(&v, "/ramdisk.cpio"); parse_cpio(&v, "/ramdisk.cpio");
excl_list = (char *[]) { "overlay", ".backup", NULL }; excl_list = (char *[]) { "overlay", ".backup", "proc", "sys", "init.bak", NULL };
frm_rf(root); frm_rf(root);
chdir("/"); chdir("/");
cpio_extract_all(&v); cpio_extract_all(&v);
@ -408,20 +472,13 @@ int main(int argc, char *argv[]) {
int mnt_system = 0; int mnt_system = 0;
int mnt_vendor = 0; int mnt_vendor = 0;
// If skip_initramfs or using split policies, we need early mount struct early_mnt mnt;
if (cmd.skip_initramfs || access("/sepolicy", R_OK) != 0) { get_partname(&cmd, &mnt);
char partname[32];
struct device dev; struct device dev;
// Mount sysfs
mkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, NULL);
// Mount system
snprintf(partname, sizeof(partname), "system%s", cmd.slot);
setup_block(&dev, partname);
if (cmd.skip_initramfs) { if (cmd.skip_initramfs) {
mkdir("/system_root", 0755); setup_block(&dev, mnt.system);
xmkdir("/system_root", 0755);
xmount(dev.path, "/system_root", "ext4", MS_RDONLY, NULL); xmount(dev.path, "/system_root", "ext4", MS_RDONLY, NULL);
int system_root = open("/system_root", O_RDONLY | O_CLOEXEC); int system_root = open("/system_root", O_RDONLY | O_CLOEXEC);
@ -430,35 +487,26 @@ int main(int argc, char *argv[]) {
clone_dir(system_root, root); clone_dir(system_root, root);
close(system_root); close(system_root);
mkdir("/system", 0755); xmkdir("/system", 0755);
xmount("/system_root/system", "/system", NULL, MS_BIND, NULL); xmount("/system_root/system", "/system", NULL, MS_BIND, NULL);
} else { } else if (mnt.system[0]) {
setup_block(&dev, mnt.system);
xmount(dev.path, "/system", "ext4", MS_RDONLY, NULL); xmount(dev.path, "/system", "ext4", MS_RDONLY, NULL);
mnt_system = 1; mnt_system = 1;
} }
// Mount vendor if (mnt.vendor[0]) {
snprintf(partname, sizeof(partname), "vendor%s", cmd.slot); setup_block(&dev, mnt.vendor);
if (setup_block(&dev, partname) == 0) {
xmount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL); xmount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL);
mnt_vendor = 1; mnt_vendor = 1;
} }
// Force init to load monolithic sepolicy /* ****************
void *addr; * Ramdisk Patches
size_t size; * ****************/
mmap_rw("/init", &addr, &size);
for (int i = 0; i < size; ++i) {
if (memcmp(addr + i, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL) - 1) == 0) {
memcpy(addr + i + sizeof(SPLIT_PLAT_CIL) - 4, "xxx", 3);
break;
}
}
munmap(addr, size);
}
// Only patch rootfs if not intended to run in recovery // Only patch rootfs if not intended to run in recovery
if (access("/etc/recovery.fstab", F_OK) != 0) { if (access("/sbin/recovery", F_OK) != 0) {
// Handle ramdisk overlays // Handle ramdisk overlays
int fd = open("/overlay", O_RDONLY | O_CLOEXEC); int fd = open("/overlay", O_RDONLY | O_CLOEXEC);
if (fd >= 0) { if (fd >= 0) {