From 8d2198865697b3fe55474e523c473297df5bbd72 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 2 Dec 2019 04:34:21 -0500 Subject: [PATCH 01/47] Support patching DTB/DTBO partition format --- native/jni/magiskboot/dtb.cpp | 160 +++++++++++------------------- native/jni/magiskboot/dtb.h | 104 +++++++++++++++++++ native/jni/magiskboot/pattern.cpp | 18 ++-- 3 files changed, 174 insertions(+), 108 deletions(-) create mode 100644 native/jni/magiskboot/dtb.h diff --git a/native/jni/magiskboot/dtb.cpp b/native/jni/magiskboot/dtb.cpp index d1641a12c..7080fe753 100644 --- a/native/jni/magiskboot/dtb.cpp +++ b/native/jni/magiskboot/dtb.cpp @@ -1,93 +1,20 @@ #include #include +#include +#include +#include extern "C" { #include } #include -#include -#include -#include #include "magiskboot.h" +#include "dtb.h" using namespace std; -#define DTB_MAGIC "\xd0\x0d\xfe\xed" -#define QCDT_MAGIC "QCDT" -#define DTBH_MAGIC "DTBH" -#define PXADT_MAGIC "PXA-DT" -#define PXA19xx_MAGIC "PXA-19xx" -#define SPRD_MAGIC "SPRD" - -struct qcdt_hdr { - char magic[4]; /* "QCDT" */ - uint32_t version; /* QCDT version */ - uint32_t num_dtbs; /* Number of DTBs */ -} __attribute__((packed)); - -struct qctable_v1 { - uint32_t cpu_info[3]; /* Some CPU info */ - uint32_t offset; /* DTB offset in QCDT */ - uint32_t len; /* DTB size */ -} __attribute__((packed)); - -struct qctable_v2 { - uint32_t cpu_info[4]; /* Some CPU info */ - uint32_t offset; /* DTB offset in QCDT */ - uint32_t len; /* DTB size */ -} __attribute__((packed)); - -struct qctable_v3 { - uint32_t cpu_info[8]; /* Some CPU info */ - uint32_t offset; /* DTB offset in QCDT */ - uint32_t len; /* DTB size */ -} __attribute__((packed)); - -struct dtbh_hdr { - char magic[4]; /* "DTBH" */ - uint32_t version; /* DTBH version */ - uint32_t num_dtbs; /* Number of DTBs */ -} __attribute__((packed)); - -struct bhtable_v2 { - uint32_t cpu_info[5]; /* Some CPU info */ - uint32_t offset; /* DTB offset in DTBH */ - uint32_t len; /* DTB size */ - uint32_t space; /* 0x00000020 */ -} __attribute__((packed)); - -struct pxadt_hdr { - char magic[6]; /* "PXA-DT" */ - uint32_t version; /* PXA-* version */ - uint32_t num_dtbs; /* Number of DTBs */ -} __attribute__((packed)); - -struct pxa19xx_hdr { - char magic[8]; /* "PXA-19xx" */ - uint32_t version; /* PXA-* version */ - uint32_t num_dtbs; /* Number of DTBs */ -} __attribute__((packed)); - -struct pxatable_v1 { - uint32_t cpu_info[2]; /* Some CPU info */ - uint32_t offset; /* DTB offset in PXA-* */ - uint32_t len; /* DTB size */ -} __attribute__((packed)); - -struct sprd_hdr { - char magic[4]; /* "SPRD" */ - uint32_t version; /* SPRD version */ - uint32_t num_dtbs; /* Number of DTBs */ -} __attribute__((packed)); - -struct sprdtable_v1 { - uint32_t cpu_info[3]; /* Some CPU info */ - uint32_t offset; /* DTB offset in SPRD */ - uint32_t len; /* DTB size */ -} __attribute__((packed)); - -struct dtb_blob { +struct fdt_blob { void *fdt; uint32_t offset; uint32_t len; @@ -206,7 +133,7 @@ static void dtb_print(const char *file, bool fstab) { // Loop through all the dtbs int dtb_num = 0; for (int i = 0; i < size; ++i) { - if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) { + if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) { auto fdt = dtb + i; if (fstab) { int node = find_fstab(fdt); @@ -266,19 +193,33 @@ static bool fdt_patch(Iter first, Iter last) { template static int dtb_patch(const Header *hdr, const char *in, const char *out) { - map dtb_map; + map dtb_map; auto buf = reinterpret_cast(hdr); auto tables = reinterpret_cast(hdr + 1); + constexpr bool is_dt_table = std::is_same_v; + + using endian_conv = uint32_t (*)(uint32_t); + endian_conv be_to_le; + endian_conv le_to_be; + if constexpr (is_dt_table) { + be_to_le = fdt32_to_cpu; + le_to_be = cpu_to_fdt32; + } else { + be_to_le = le_to_be = [](uint32_t x) -> auto { return x; }; + } + // Collect all dtbs - for (int i = 0; i < hdr->num_dtbs; ++i) { - if (dtb_map.find(tables[i].offset) == dtb_map.end()) { - auto blob = buf + tables[i].offset; - int size = fdt_totalsize(blob); + auto num_dtb = be_to_le(hdr->num_dtbs); + for (int i = 0; i < num_dtb; ++i) { + auto offset = be_to_le(tables[i].offset); + if (dtb_map.count(offset) == 0) { + auto blob = buf + offset; + uint32_t size = fdt_totalsize(blob); auto fdt = xmalloc(size + 256); memcpy(fdt, blob, size); fdt_open_into(fdt, fdt, size + 256); - dtb_map[tables[i].offset] = { fdt, tables[i].offset }; + dtb_map[offset] = { fdt, offset }; } } if (dtb_map.empty()) @@ -292,18 +233,23 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) { unlink(in); int fd = xopen(out, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + uint32_t total_size = 0; + // Copy headers and tables - xwrite(fd, buf, dtb_map.begin()->first); + total_size += xwrite(fd, buf, dtb_map.begin()->first); // mmap rw to patch table values retroactively auto mmap_sz = lseek(fd, 0, SEEK_CUR); auto addr = (uint8_t *) xmmap(nullptr, mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - // Guess page size using gcd - auto it = dtb_map.begin(); - uint32_t page_size = (it++)->first; - for (; it != dtb_map.end(); ++it) - page_size = binary_gcd(page_size, it->first); + // Guess alignment using gcd + uint32_t align = 1; + if constexpr (!is_dt_table) { + auto it = dtb_map.begin(); + align = (it++)->first; + for (; it != dtb_map.end(); ++it) + align = binary_gcd(align, it->first); + } // Write dtbs for (auto &val : dtb_map) { @@ -311,18 +257,23 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) { auto fdt = val.second.fdt; fdt_pack(fdt); int size = fdt_totalsize(fdt); - xwrite(fd, fdt, size); - val.second.len = do_align(size, page_size); - write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size)); + total_size += xwrite(fd, fdt, size); + val.second.len = do_align(size, align); + write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), align)); + // total_size += align_off(lseek(fd, 0, SEEK_CUR), align); /* Not needed */ free(fdt); } - // Patch tables + // Patch headers + if constexpr (is_dt_table) { + auto hdr_rw = reinterpret_cast
(addr); + hdr_rw->total_size = le_to_be(total_size); + } auto tables_rw = reinterpret_cast(addr + sizeof(Header)); - for (int i = 0; i < hdr->num_dtbs; ++i) { - auto &blob = dtb_map[tables_rw[i].offset]; - tables_rw[i].offset = blob.offset; - tables_rw[i].len = blob.len; + for (int i = 0; i < num_dtb; ++i) { + auto &blob = dtb_map[be_to_le(tables_rw[i].offset)]; + tables_rw[i].offset = le_to_be(blob.offset); + tables_rw[i].len = le_to_be(blob.len); } munmap(addr, mmap_sz); @@ -393,10 +344,19 @@ static int dtb_patch(const char *in, const char *out) { default: return 1; } + } else if (MATCH(DT_TABLE_MAGIC)) { + auto hdr = reinterpret_cast(dtb); + switch (hdr->version) { + case 0: + fprintf(stderr, "DT_TABLE v0\n"); + return dtb_patch(hdr, in, out); + default: + return 1; + } } else { vector fdt_list; for (int i = 0; i < dtb_sz; ++i) { - if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) { + if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) { int len = fdt_totalsize(dtb + i); auto fdt = static_cast(xmalloc(len + 256)); memcpy(fdt, dtb + i, len); diff --git a/native/jni/magiskboot/dtb.h b/native/jni/magiskboot/dtb.h new file mode 100644 index 000000000..a303b5b3d --- /dev/null +++ b/native/jni/magiskboot/dtb.h @@ -0,0 +1,104 @@ +#pragma once + +#include + +#define FDT_MAGIC_STR "\xd0\x0d\xfe\xed" +#define DT_TABLE_MAGIC "\xd7\xb7\xab\x1e" +#define QCDT_MAGIC "QCDT" +#define DTBH_MAGIC "DTBH" +#define PXADT_MAGIC "PXA-DT" +#define PXA19xx_MAGIC "PXA-19xx" +#define SPRD_MAGIC "SPRD" + +struct qcdt_hdr { + char magic[4]; /* "QCDT" */ + uint32_t version; /* QCDT version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct qctable_v1 { + uint32_t cpu_info[3]; /* Some CPU info */ + uint32_t offset; /* DTB offset in QCDT */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +struct qctable_v2 { + uint32_t cpu_info[4]; /* Some CPU info */ + uint32_t offset; /* DTB offset in QCDT */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +struct qctable_v3 { + uint32_t cpu_info[8]; /* Some CPU info */ + uint32_t offset; /* DTB offset in QCDT */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +struct dtbh_hdr { + char magic[4]; /* "DTBH" */ + uint32_t version; /* DTBH version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct bhtable_v2 { + uint32_t cpu_info[5]; /* Some CPU info */ + uint32_t offset; /* DTB offset in DTBH */ + uint32_t len; /* DTB size */ + uint32_t space; /* 0x00000020 */ +} __attribute__((packed)); + +struct pxadt_hdr { + char magic[6]; /* "PXA-DT" */ + uint32_t version; /* PXA-* version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct pxa19xx_hdr { + char magic[8]; /* "PXA-19xx" */ + uint32_t version; /* PXA-* version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct pxatable_v1 { + uint32_t cpu_info[2]; /* Some CPU info */ + uint32_t offset; /* DTB offset in PXA-* */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +struct sprd_hdr { + char magic[4]; /* "SPRD" */ + uint32_t version; /* SPRD version */ + uint32_t num_dtbs; /* Number of DTBs */ +} __attribute__((packed)); + +struct sprdtable_v1 { + uint32_t cpu_info[3]; /* Some CPU info */ + uint32_t offset; /* DTB offset in SPRD */ + uint32_t len; /* DTB size */ +} __attribute__((packed)); + +/* AOSP DTB/DTBO partition layout */ + +struct dt_table_header { + uint32_t magic; /* DT_TABLE_MAGIC */ + uint32_t total_size; /* includes dt_table_header + all dt_table_entry */ + uint32_t header_size; /* sizeof(dt_table_header) */ + + uint32_t dt_entry_size; /* sizeof(dt_table_entry) */ + uint32_t num_dtbs; /* number of dt_table_entry */ + uint32_t dt_entries_offset; /* offset to the first dt_table_entry */ + + uint32_t page_size; /* flash page size we assume */ + uint32_t version; /* DTBO image version */ +} __attribute__((packed)); + +struct dt_table_entry { + uint32_t len; /* DTB size */ + uint32_t offset; + + uint32_t id; + uint32_t rev; + uint32_t flags; + + uint32_t custom[3]; +} __attribute__((packed)); diff --git a/native/jni/magiskboot/pattern.cpp b/native/jni/magiskboot/pattern.cpp index 03bde4879..8a73dcd12 100644 --- a/native/jni/magiskboot/pattern.cpp +++ b/native/jni/magiskboot/pattern.cpp @@ -16,13 +16,14 @@ static int check_verity_pattern(const char *s) { return -1; if (s[skip] == '=') { - while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') ++skip; + while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') + ++skip; } return skip; } static int check_encryption_pattern(const char *s) { - static const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe" }; + constexpr const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe" }; for (auto enc : encrypt_list) { int len = strlen(enc); if (strncmp(s, enc, len) == 0) @@ -33,26 +34,27 @@ static int check_encryption_pattern(const char *s) { char *patch_verity(const void *buf, uint32_t &size, bool inplace) { auto src = static_cast(buf); + auto dest = (char *)(inplace ? buf : xmalloc(size)); int src_size = size; bool found = false; - auto patched = (char *)(inplace ? buf : xmalloc(size)); int write = 0; - for (int read = 0; read < src_size; ++read, ++write) { + for (int read = 0; read < src_size;) { if (int skip; (skip = check_verity_pattern(src + read)) > 0) { fprintf(stderr, "Found pattern [%.*s]\n", skip, src + read); size -= skip; read += skip; found = true; + } else { + dest[write++] = src[read++]; } - patched[write] = src[read]; } - patched[write] = '\0'; + dest[write] = '\0'; if (!found) { if (!inplace) - free(patched); + free(dest); return nullptr; } - return patched; + return dest; } void patch_encryption(void *buf, uint32_t &size) { From 03428329eff5ef92c5d6a1b422d371b411d982e8 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 3 Dec 2019 05:39:39 -0500 Subject: [PATCH 02/47] Add new verity and encryption patterns Close #2118 --- native/jni/magiskboot/pattern.cpp | 32 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/native/jni/magiskboot/pattern.cpp b/native/jni/magiskboot/pattern.cpp index 8a73dcd12..e95a35453 100644 --- a/native/jni/magiskboot/pattern.cpp +++ b/native/jni/magiskboot/pattern.cpp @@ -5,15 +5,16 @@ #include "magiskboot.h" +#define MATCH(p) else if (strncmp(s + skip, p, sizeof(p) - 1) == 0) skip += (sizeof(p) - 1) + static int check_verity_pattern(const char *s) { - int skip = 0; - if (s[0] == ',') ++skip; - if (strncmp(s + skip, "verify", 6) == 0) - skip += 6; - else if (strncmp(s + skip, "avb", 3) == 0) - skip += 3; - else - return -1; + int skip = s[0] == ','; + + if (0) {} + MATCH("verify"); + MATCH("avb"); + MATCH("support_scfs"); + else return -1; if (s[skip] == '=') { while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') @@ -22,14 +23,15 @@ static int check_verity_pattern(const char *s) { return skip; } +#undef MATCH +#define MATCH(p) else if (strncmp(s, p, sizeof(p) - 1) == 0) return (sizeof(p) - 1) + static int check_encryption_pattern(const char *s) { - constexpr const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe" }; - for (auto enc : encrypt_list) { - int len = strlen(enc); - if (strncmp(s, enc, len) == 0) - return len; - } - return -1; + if (0) {} + MATCH("forceencrypt"); + MATCH("forcefdeorfbe"); + MATCH("fileencryption"); + else return -1; } char *patch_verity(const void *buf, uint32_t &size, bool inplace) { From 474d65207ef956cef48aa48223f07a9701673bd8 Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Wed, 27 Nov 2019 18:38:10 +0800 Subject: [PATCH 03/47] Fix MagiskHide unmounting paths under /product Fixes #2107 --- native/jni/magiskhide/hide_policy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/magiskhide/hide_policy.cpp b/native/jni/magiskhide/hide_policy.cpp index 0a20cd75d..b131c0e64 100644 --- a/native/jni/magiskhide/hide_policy.cpp +++ b/native/jni/magiskhide/hide_policy.cpp @@ -67,7 +67,7 @@ void hide_unmount(int pid) { // Unmount dummy skeletons and /sbin links parse_mnt("/proc/self/mounts", [&](mntent *mentry) { - if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(sbin)) + if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(sbin) || TMPFS_MNT(product)) targets.emplace_back(mentry->mnt_dir); return true; }); From 8cc5f096a20a4c6e3989144d161846ab21c0d510 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 5 Dec 2019 16:29:45 -0500 Subject: [PATCH 04/47] Some minor changes --- native/jni/init/early_mount.cpp | 40 +++++++++++++++---------------- native/jni/init/init.h | 1 - native/jni/utils/selinux.cpp | 42 +++++++-------------------------- 3 files changed, 29 insertions(+), 54 deletions(-) diff --git a/native/jni/init/early_mount.cpp b/native/jni/init/early_mount.cpp index 672584ba2..9d7555f66 100644 --- a/native/jni/init/early_mount.cpp +++ b/native/jni/init/early_mount.cpp @@ -20,13 +20,17 @@ struct devinfo { static vector dev_list; +static char partname[32]; +static char fstype[32]; +static char block_dev[64]; + static void parse_device(devinfo *dev, const char *uevent) { dev->partname[0] = '\0'; parse_prop_file(uevent, [=](string_view key, string_view value) -> bool { if (key == "MAJOR") - dev->major = atoi(value.data()); + dev->major = parse_int(value.data()); else if (key == "MINOR") - dev->minor = atoi(value.data()); + dev->minor = parse_int(value.data()); else if (key == "DEVNAME") strcpy(dev->devname, value.data()); else if (key == "PARTNAME") @@ -53,7 +57,7 @@ static void collect_devices() { closedir(dir); } -static dev_t setup_block(const char *partname, char *block_dev = nullptr) { +static dev_t setup_block(char *block_dev = nullptr) { if (dev_list.empty()) collect_devices(); for (;;) { @@ -84,15 +88,7 @@ static bool is_lnk(const char *name) { return S_ISLNK(st.st_mode); } -void BaseInit::cleanup() { - // Unmount in reverse order - for (auto &p : reversed(mount_list)) { - LOGD("Unmount [%s]\n", p.data()); - umount(p.data()); - } -} - -bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) { +static bool read_dt_fstab(cmdline *cmd, const char *name) { char path[128]; int fd; sprintf(path, "%s/fstab/%s/dev", cmd->dt_dir, name); @@ -112,18 +108,14 @@ bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) { return false; } -static char partname[32]; -static char fstype[32]; -static char block_dev[64]; - #define link_root(name) \ if (is_lnk("/system_root" name)) \ cp_afc("/system_root" name, name) #define mount_root(name) \ -if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \ +if (!is_lnk("/" #name) && read_dt_fstab(cmd, #name)) { \ LOGD("Early mount " #name "\n"); \ - setup_block(partname, block_dev); \ + setup_block(block_dev); \ xmkdir("/" #name, 0755); \ xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \ mount_list.emplace_back("/" #name); \ @@ -151,7 +143,7 @@ void SARCompatInit::early_mount() { LOGD("Early mount system_root\n"); sprintf(partname, "system%s", cmd->slot); - setup_block(partname, block_dev); + setup_block(block_dev); xmkdir("/system_root", 0755); if (xmount(block_dev, "/system_root", "ext4", MS_RDONLY, nullptr)) xmount(block_dev, "/system_root", "erofs", MS_RDONLY, nullptr); @@ -213,7 +205,7 @@ void SARInit::early_mount() { LOGD("Early mount system_root\n"); sprintf(partname, "system%s", cmd->slot); - system_dev = setup_block(partname); + system_dev = setup_block(); xmkdir("/system_root", 0755); if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr)) xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr); @@ -245,3 +237,11 @@ void SecondStageInit::early_mount() { switch_root("/system_root"); } + +void BaseInit::cleanup() { + // Unmount in reverse order + for (auto &p : reversed(mount_list)) { + LOGD("Unmount [%s]\n", p.data()); + umount(p.data()); + } +} diff --git a/native/jni/init/init.h b/native/jni/init/init.h index cb28a6ffa..8161100d9 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -55,7 +55,6 @@ protected: raw_data self; virtual void early_mount() = 0; - bool read_dt_fstab(const char *name, char *partname, char *fstype); bool patch_sepolicy(const char *file = "/sepolicy"); public: MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; diff --git a/native/jni/utils/selinux.cpp b/native/jni/utils/selinux.cpp index ed3696191..785b61288 100644 --- a/native/jni/utils/selinux.cpp +++ b/native/jni/utils/selinux.cpp @@ -55,61 +55,37 @@ static int __setcon(const char *ctx) { static int __getfilecon(const char *path, char **ctx) { char buf[1024]; int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); - if (rc < 0) { - errno = -rc; - return -1; - } - *ctx = strdup(buf); + if (rc == 0) + *ctx = strdup(buf); return rc; } static int __lgetfilecon(const char *path, char **ctx) { char buf[1024]; int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); - if (rc < 0) { - errno = -rc; - return -1; - } - *ctx = strdup(buf); + if (rc == 0) + *ctx = strdup(buf); return rc; } static int __fgetfilecon(int fd, char **ctx) { char buf[1024]; int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); - if (rc < 0) { - errno = -rc; - return -1; - } - *ctx = strdup(buf); + if (rc == 0) + *ctx = strdup(buf); return rc; } static int __setfilecon(const char *path, const char *ctx) { - int rc = syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); - if (rc) { - errno = -rc; - return -1; - } - return 0; + return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); } static int __lsetfilecon(const char *path, const char *ctx) { - int rc = syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); - if (rc) { - errno = -rc; - return -1; - } - return 0; + return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); } static int __fsetfilecon(int fd, const char *ctx) { - int rc = syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); - if (rc) { - errno = -rc; - return -1; - } - return 0; + return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); } // Function pointers From 476b61c4c9675b87d027aa43f559c360affbc5c9 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 5 Dec 2019 17:18:23 -0500 Subject: [PATCH 05/47] Support system_root with NVIDIA partition names Fix #2063 --- native/jni/init/early_mount.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/native/jni/init/early_mount.cpp b/native/jni/init/early_mount.cpp index 9d7555f66..c311004c2 100644 --- a/native/jni/init/early_mount.cpp +++ b/native/jni/init/early_mount.cpp @@ -43,7 +43,7 @@ static void parse_device(devinfo *dev, const char *uevent) { static void collect_devices() { char path[128]; struct dirent *entry; - devinfo dev; + devinfo dev{}; DIR *dir = xopendir("/sys/dev/block"); if (dir == nullptr) return; @@ -57,13 +57,14 @@ static void collect_devices() { closedir(dir); } -static dev_t setup_block(char *block_dev = nullptr) { +static int64_t setup_block(char *block_dev = nullptr) { if (dev_list.empty()) collect_devices(); - for (;;) { + xmkdir("/dev", 0755); + + for (int tries = 0; tries < 3; ++tries) { for (auto &dev : dev_list) { if (strcasecmp(dev.partname, partname) == 0) { - xmkdir("/dev", 0755); if (block_dev) { sprintf(block_dev, "/dev/block/%s", dev.devname); xmkdir("/dev/block", 0755); @@ -79,6 +80,9 @@ static dev_t setup_block(char *block_dev = nullptr) { dev_list.clear(); collect_devices(); } + + // The requested partname does not exist + return -1; } static bool is_lnk(const char *name) { @@ -205,7 +209,18 @@ void SARInit::early_mount() { LOGD("Early mount system_root\n"); sprintf(partname, "system%s", cmd->slot); - system_dev = setup_block(); + auto dev = setup_block(); + if (dev < 0) { + // Try NVIDIA naming scheme + strcpy(partname, "APP"); + dev = setup_block(); + if (dev < 0) { + // We don't really know what to do at this point... + LOGE("Cannot find root partition, abort\n"); + exit(1); + } + } + system_dev = dev; xmkdir("/system_root", 0755); if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr)) xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr); From fcd6071c570e609c5f2d75f3f76a1c110a1d8021 Mon Sep 17 00:00:00 2001 From: Hen Ry Date: Thu, 5 Dec 2019 23:21:31 +0100 Subject: [PATCH 06/47] Update de strings --- app/src/main/res/values-de/strings.xml | 51 +++++++++++++++++--------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 57aa1e972..dd9f3eabc 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -8,13 +8,14 @@ Einstellungen Installieren Nicht unterstützte Magisk Version + Diese Version von Magisk Manager unterstützt keine Magisk-Versionen unter %1$s.\n\nDie App verhält sich so, als ob kein Magisk installiert ist. Bitte Magisk so schnell wie möglich aktualisieren. Magisk ist nicht installiert Suche nach Aktualisierungen… Ungültiger Aktualisierungskanal SafetyNet-Status abfragen - Prüfe SafetyNet-Status… + SafetyNet-Status prüfen… SafetyNet-Test erfolgreich SafetyNet API Fehler Die Antwort ist ungültig @@ -23,11 +24,12 @@ Erweiterte Optionen \"force encryption\" beibehalten AVB 2.0/dm-verity beibehalten + Wiederherstellungsmodus Installiert: %1$s Neueste: %1$s Deinstallieren Magisk deinstallieren - Alle Module werden deaktiviert/entfernt. Root wird entfernt, und Ihre Daten werden möglicherweise verschlüsselt, wenn nicht bereits der Fall. + Alle Module werden deaktiviert/entfernt. Root wird entfernt, und deine Daten werden möglicherweise verschlüsselt, wenn es nicht bereits der Fall ist. aktualisieren (Core only Modus aktiviert) @@ -43,6 +45,7 @@ Neustart in das Recovery Neustart in den Bootloader Neustart in den Download-Modus + Neustart in den EDL-Modus Aktualisierung verfügbar @@ -67,6 +70,8 @@ Fortschrittsbenachrichtigungen Download abgeschlossen Fehler beim Herunterladen der Datei + Im übergeordneten Ordner anzeigen + Datei anzeigen Neues Magisk Update verfügbar! Aktualisierung für Magisk Manager verfügbar! @@ -74,71 +79,77 @@ Herunterladen und installieren Nur Zip-Datei herunterladen Direkt installieren (empfohlen) - Installiere in inaktiven Slot (Nach OTA) + In inaktiven Slot installieren (Nach OTA) Dein Gerät wird GEZWUNGEN in den aktuell inaktiven Slot zu starten, nachdem ein Neustart durchgeführt wurde!\nBenutze diese Option nur, nachdem das OTA beendet wurde.\nFortsetzen? Methode auswählen Zusätzliche Einrichtung Auswählen und Patchen einer Datei - Wählen Sie ein Rohabbild (*.img) oder eine ODIN-Tar-Datei (*.tar) aus. + Ein Rohabbild (*.img) oder eine ODIN-Tar-Datei (*.tar) auswählen. Neustart in 5 Sekunden… - Installiere %1$s + %1$s installieren Möchtest du %1$s installieren? Herunterladen Neustart Neustarten, um die Änderungen zu übernehmen Versionshinweise Repo-Cache geleert - DTBO wurde gepatched! Magisk Manager hat dtbo.img gepatched, bitte neustarten Flashing Erledigt! Fehler - Verberge Magisk Manager… - Verbergen von Magisk Manager fehlgeschlagen… - Es wurde keine Anwendung gefunden, um diesen Link zu öffnen... + Magisk Manager verbergen… + Magisk Manager verbergen ist fehlgeschlagen… + Es wurde keine App gefunden, um diesen Link zu öffnen… Warnung Komplette Deinstallation Images wiederherstellen - Wiederherstellen... + Wiederherstellen… Wiederherstellung erfolgreich! Kein Original Backup vorhanden! Lade proprietären Code herunter Magisk Manager ist FOSS und enthält keinen proprietären SafetyNet API Code von Google. Magisk Manager erlauben eine Erweiterung (enthält GoogleApiClient) für SafetyNet-Checks herunterzuladen? Einrichtung fehlgeschlagen Zusätzliche Einrichtung erforderlich - Ihr Gerät benötigt zusätzliche Einrichtung, damit Magisk ordnungsgemäß funktioniert. Es wird eine Magisk-Installations-Zip wird heruntergeladen, fortfahren? + Dein Gerät benötigt eine zusätzliche Einrichtung, damit Magisk ordnungsgemäß funktioniert. Es wird eine Magisk-Installations-Zip heruntergeladen. Fortfahren? Umgebungseinrichtung läuft… + Authentifizieren Allgemein Dunkles Theme Dunkles Theme aktivieren + Download-Verzeichnis + Dateien werden in %1$s gespeichert Repo-Cache leeren Löscht die zwischengespeicherten Informationen des Online-Repos. Erzwingt eine Aktualisierung Magisk Manager verbergen Magisk Manager mit zufälligem Paketnamen neu packen Magisk Manager wiederherstellen - Stellt Magisk Manager mit ursprünglichem Paketnamen wieder her + Magisk Manager mit ursprünglichem Paketnamen wiederherstellen Sprache (Systemstandard) Aktualisierungs-Einstellungen - Prüfe nach Aktualisierungen - Prüfe regelmäßig im Hintergrund nach Aktualisierungen + Auf Aktualisierungen prüfen + Regelmäßig im Hintergrund auf Aktualisierungen prüfen Aktualisierungs-Kanal Stabil Beta Benutzerdefiniert - Gib eine benutzerdefinierte URL ein + Eine benutzerdefinierte URL eingeben Nur Kernfunktionen Aktiviert lediglich die Kernfunktionen, Module werden nicht geladen. MagiskSU und Magisk Hide bleiben weiterhin aktiv Versteckt Magisk vor diversen Entdeckungsmethoden Systemlose Hosts-Datei Systemlose Unterstützung für Werbeblocker - systemless Hosts-Modul hinzugefügt + Systemless Hosts-Modul hinzugefügt + Den gewünschten App-Namen eingeben + Neuer Name + Die App wird unter diesem Namen neu gepackt + Ungültiges Format Apps und ADB Nur Apps Nur ADB @@ -156,6 +167,9 @@ %1$d Sekunden Nach Aktualisierung erneut authentifizieren Superuser-Zugriff nach App-Aktualisierung erneut abfragen + Biometrische Authentifizierung aktivieren + Biometrische Authentifizierung verwenden, um Superuser-Anfragen zu ermöglichen + Nicht unterstütztes Gerät oder keine biometrischen Einstellungen aktiviert Mehrbenutzermodus Nur der Gerätebesitzer @@ -172,6 +186,7 @@ Alle Root-Sitzungen benutzen den global angelegten Namespace Root-Sitzungen erben den Namespace des Abfragenden Jede Root-Sitzung hat ihren isolierten Namespace + Fehler beim Erstellen eines Ordners. Er muss im Stammverzeichnis des Speichers zugänglich und darf keine Datei sein. Superuser-Anfrage @@ -187,7 +202,7 @@ 60 Min. Superuser-Rechte für %1$s gewährt Superuser-Rechte für %1$s verweigert - Keine Applikationen gefunden + Keine Apps gefunden Superuser-Rechte werden für %1$s gewährt Superuser-Rechte werden für %1$s verweigert Benachrichtigungen für %1$s sind aktiviert @@ -205,6 +220,6 @@ Befehl: %1$s - System Applikationen anzeigen + System-Apps anzeigen From 291c718ba2a06222007c173e69b7858a08b2b356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mevl=C3=BCt=20TOP=C3=87U?= Date: Mon, 2 Dec 2019 16:54:58 +0300 Subject: [PATCH 07/47] Update Turkish language Hi, Update Turkish language Merge please Thanks --- app/src/main/res/values-tr/strings.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3ec7b2b94..e8d71b3f3 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -8,6 +8,7 @@ Ayarlar Yükle Desteklenmeyen Magisk Sürümü + Magisk Manager\'ın bu sürümü, %1$s daha düşük Magisk versiyonlarını desteklememektedir.\n\nUygulama hiçbir Magisk kurulu değil gibi davranacak, lütfen en kısa zamanda Magisk\'i yükseltin. Magisk yüklü değil @@ -94,8 +95,7 @@ Ayarları uygulamak için yeniden başlatın Sürüm notları Repo önbelleği temizlendi - - DTBO yamalandı! + DTBO yamalandı! Magisk Manager dtbo.img\'yi yamaladı, lütfen yeniden başlatın Yükleniyor Tamamlandı! @@ -115,6 +115,7 @@ Ek Kurulum Gerekli Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Bu Magisk kurulum zip dosyasını indirecektir, şimdi devam etmek istiyor musunuz? Ortam kurulumu çalışıyor… + Kimlik doğrulaması Genel @@ -166,6 +167,9 @@ %1$d saniye Yükseltmeden sonra yeniden kimlik doğrula Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula + Biyometrik Kimlik Doğrulamayı Etkinleştir + Superuser isteklerine izin vermek için biyometrik kimlik doğrulamayı kullanın + Desteklenmeyen cihaz veya biyometrik ayar etkinleştirilmemiş Çok Kullanıcılı Mod Yalnızca Cihaz Sahibi From 11b7076a435838e3e6dfea7870285e188547a66f Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 5 Dec 2019 17:34:50 -0500 Subject: [PATCH 08/47] Fix broken getxattr calls --- native/jni/utils/selinux.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/native/jni/utils/selinux.cpp b/native/jni/utils/selinux.cpp index 785b61288..f41fd8106 100644 --- a/native/jni/utils/selinux.cpp +++ b/native/jni/utils/selinux.cpp @@ -55,7 +55,7 @@ static int __setcon(const char *ctx) { static int __getfilecon(const char *path, char **ctx) { char buf[1024]; int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); - if (rc == 0) + if (rc >= 0) *ctx = strdup(buf); return rc; } @@ -63,7 +63,7 @@ static int __getfilecon(const char *path, char **ctx) { static int __lgetfilecon(const char *path, char **ctx) { char buf[1024]; int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); - if (rc == 0) + if (rc >= 0) *ctx = strdup(buf); return rc; } @@ -71,7 +71,7 @@ static int __lgetfilecon(const char *path, char **ctx) { static int __fgetfilecon(int fd, char **ctx) { char buf[1024]; int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); - if (rc == 0) + if (rc >= 0) *ctx = strdup(buf); return rc; } From 3e275b7dbad3fb5ca157f1ad7b19eae58f99480b Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 6 Dec 2019 00:30:00 -0500 Subject: [PATCH 09/47] Update a bunch of stuffs --- app/build.gradle | 14 +++++++------- build.gradle | 4 ++-- signing/build.gradle | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8270643fe..8d55ab87b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,7 +74,7 @@ dependencies { implementation "${bindingAdapter}:${vBAdapt}" implementation "${bindingAdapter}-recyclerview:${vBAdapt}" - def vMarkwon = '4.1.2' + def vMarkwon = '4.2.0' implementation "io.noties.markwon:core:${vMarkwon}" implementation "io.noties.markwon:html:${vMarkwon}" implementation "io.noties.markwon:image:${vMarkwon}" @@ -99,7 +99,7 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:${vOkHttp}" implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}" - def vMoshi = '1.9.1' + def vMoshi = '1.9.2' implementation "com.squareup.moshi:moshi:${vMoshi}" def vKotshi = '2.0.2' @@ -111,7 +111,7 @@ dependencies { replacedBy('com.github.topjohnwu:room-runtime') } } - def vRoom = '2.2.1' + def vRoom = '2.2.2' implementation "com.github.topjohnwu:room-runtime:${vRoom}" implementation "androidx.room:room-rxjava2:${vRoom}" kapt "androidx.room:room-compiler:${vRoom}" @@ -124,12 +124,12 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' implementation 'androidx.preference:preference:1.1.0' - implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01' - implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.fragment:fragment-ktx:1.2.0-rc03' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.work:work-runtime:2.2.0' - implementation 'androidx.transition:transition:1.3.0-rc01' + implementation 'androidx.transition:transition:1.3.0-rc02' implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.core:core-ktx:1.1.0' - implementation 'com.google.android.material:material:1.2.0-alpha01' + implementation 'com.google.android.material:material:1.2.0-alpha02' } diff --git a/build.gradle b/build.gradle index 0683ea6b9..7e16c37fa 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ if (configPath.exists()) configPath.withInputStream { is -> props.load(is) } buildscript { - ext.vKotlin = '1.3.50' + ext.vKotlin = '1.3.61' repositories { google() @@ -16,7 +16,7 @@ buildscript { maven { url 'https://kotlin.bintray.com/kotlinx' } } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}" diff --git a/signing/build.gradle b/signing/build.gradle index 6808c87c8..f01c7c8ec 100644 --- a/signing/build.gradle +++ b/signing/build.gradle @@ -34,7 +34,7 @@ repositories { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - def bcVer = '1.63' + def bcVer = '1.64' api "org.bouncycastle:bcprov-jdk15on:${bcVer}" api "org.bouncycastle:bcpkix-jdk15on:${bcVer}" } From 4bb8ad19cf0890238ecf3e8ffa244da3a5230776 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 6 Dec 2019 12:02:34 -0500 Subject: [PATCH 10/47] Small init refactoring --- native/jni/Android.mk | 2 +- native/jni/init/getinfo.cpp | 6 +- native/jni/init/init.cpp | 59 ++++++++++--------- native/jni/init/init.h | 2 +- .../jni/init/{early_mount.cpp => mount.cpp} | 0 5 files changed, 35 insertions(+), 34 deletions(-) rename native/jni/init/{early_mount.cpp => mount.cpp} (100%) diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 378ab401b..266a11def 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -106,7 +106,7 @@ LOCAL_C_INCLUDES := \ LOCAL_SRC_FILES := \ init/init.cpp \ - init/early_mount.cpp \ + init/mount.cpp \ init/rootdir.cpp \ init/getinfo.cpp \ magiskpolicy/api.cpp \ diff --git a/native/jni/init/getinfo.cpp b/native/jni/init/getinfo.cpp index 904aed527..7a082e2c8 100644 --- a/native/jni/init/getinfo.cpp +++ b/native/jni/init/getinfo.cpp @@ -112,7 +112,7 @@ void load_kernel_info(cmdline *cmd) { cmd->slot[0] = '_'; strcpy(cmd->slot + 1, value); } else if (key == "skip_initramfs") { - cmd->system_as_root = true; + cmd->skip_initramfs = true; } else if (key == "androidboot.force_normal_boot") { cmd->force_normal_boot = value[0] == '1'; } else if (key == "androidboot.android_dt_dir") { @@ -143,13 +143,13 @@ void load_kernel_info(cmdline *cmd) { if (recovery_mode) { LOGD("Running in recovery mode, waiting for key...\n"); - cmd->system_as_root = !check_key_combo(); + cmd->skip_initramfs = !check_key_combo(); } if (cmd->dt_dir[0] == '\0') strcpy(cmd->dt_dir, DEFAULT_DT_DIR); - LOGD("system_as_root=[%d]\n", cmd->system_as_root); + LOGD("skip_initramfs=[%d]\n", cmd->skip_initramfs); LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot); LOGD("slot=[%s]\n", cmd->slot); LOGD("dt_dir=[%s]\n", cmd->dt_dir); diff --git a/native/jni/init/init.cpp b/native/jni/init/init.cpp index 90d38cd32..76241e2ac 100644 --- a/native/jni/init/init.cpp +++ b/native/jni/init/init.cpp @@ -75,12 +75,12 @@ static bool unxz(int fd, const uint8_t *buf, size_t size) { xz_crc32_init(); struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26); struct xz_buf b = { - .in = buf, - .in_pos = 0, - .in_size = size, - .out = out, - .out_pos = 0, - .out_size = sizeof(out) + .in = buf, + .in_pos = 0, + .in_size = size, + .out = out, + .out_pos = 0, + .out_size = sizeof(out) }; enum xz_ret ret; do { @@ -147,11 +147,11 @@ class TestInit : public BaseInit { public: TestInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; void start() override { - // Write init tests here + // Place init tests here } }; -static void setup_test(const char *dir) { +[[maybe_unused]] static int test_main(int argc, char *argv[]) { // Log to console cmdline_logging(); log_cb.ex = nop_ex; @@ -167,13 +167,21 @@ static void setup_test(const char *dir) { mounts.emplace_back(me->mnt_dir); return true; }); - for (auto m = mounts.rbegin(); m != mounts.rend(); ++m) - xumount(m->data()); + for (auto &m : reversed(mounts)) + xumount(m.data()); // chroot jail - chdir(dir); + chdir(dirname(argv[0])); chroot("."); chdir("/"); + + cmdline cmd{}; + load_kernel_info(&cmd); + + auto init = make_unique(argv, &cmd); + init->start(); + + return 1; } int main(int argc, char *argv[]) { @@ -184,6 +192,11 @@ int main(int argc, char *argv[]) { return (*init_applet_main[i])(argc, argv); } +#ifdef MAGISK_DEBUG + if (getenv("INIT_TEST") != nullptr) + return test_main(argc, argv); +#endif + if (argc > 1 && strcmp(argv[1], "-x") == 0) { if (strcmp(argv[2], "magisk") == 0) return dump_magisk(argv[3], 0755); @@ -191,34 +204,22 @@ int main(int argc, char *argv[]) { return dump_manager(argv[3], 0644); } + if (getpid() != 1) + return 1; + setup_klog(); + if (argc > 1 && argv[1] == "selinux_setup"sv) { auto init = make_unique(argv); init->start(); } -#ifdef MAGISK_DEBUG - bool run_test = getenv("INIT_TEST") != nullptr; -#else - constexpr bool run_test = false; -#endif - - if (run_test) { - setup_test(dirname(argv[0])); - } else { - if (getpid() != 1) - return 1; - setup_klog(); - } - cmdline cmd{}; load_kernel_info(&cmd); unique_ptr init; - if (run_test) { - init = make_unique(argv, &cmd); - } else if (cmd.force_normal_boot) { + if (cmd.force_normal_boot) { init = make_unique(argv, &cmd); - } else if (cmd.system_as_root) { + } else if (cmd.skip_initramfs) { if (access("/overlay", F_OK) == 0) /* Compatible mode */ init = make_unique(argv, &cmd); else diff --git a/native/jni/init/init.h b/native/jni/init/init.h index 8161100d9..fa7b59783 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -4,7 +4,7 @@ #include struct cmdline { - bool system_as_root; + bool skip_initramfs; bool force_normal_boot; char slot[3]; char dt_dir[128]; diff --git a/native/jni/init/early_mount.cpp b/native/jni/init/mount.cpp similarity index 100% rename from native/jni/init/early_mount.cpp rename to native/jni/init/mount.cpp From d22b9c26b66c0ecea53abdc4729f1014982427b3 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 6 Dec 2019 15:31:49 -0500 Subject: [PATCH 11/47] Pull out common logic --- native/jni/init/rootdir.cpp | 87 ++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index 9b59b34d7..fbd82d94f 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -225,6 +225,48 @@ static void sbin_overlay(const raw_data &self, const raw_data &config) { xsymlink("./magiskinit", "/sbin/supolicy"); } +static void recreate_sbin(const char *mirror) { + int src = xopen(mirror, O_RDONLY | O_CLOEXEC); + int dest = xopen("/sbin", O_RDONLY | O_CLOEXEC); + DIR *fp = fdopendir(src); + char buf[256]; + bool use_bind_mount = true; + for (dirent *entry; (entry = xreaddir(fp));) { + if (entry->d_name == "."sv || entry->d_name == ".."sv) + continue; + struct stat st; + fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW); + if (S_ISLNK(st.st_mode)) { + xreadlinkat(src, entry->d_name, buf, sizeof(buf)); + xsymlinkat(buf, dest, entry->d_name); + } else { + char sbin_path[256]; + sprintf(buf, "%s/%s", mirror, entry->d_name); + sprintf(sbin_path, "/sbin/%s", entry->d_name); + + if (use_bind_mount) { + // Create dummy + if (S_ISDIR(st.st_mode)) + xmkdir(sbin_path, st.st_mode & 0777); + else + close(xopen(sbin_path, O_CREAT | O_WRONLY | O_CLOEXEC, st.st_mode & 0777)); + + if (xmount(buf, sbin_path, nullptr, MS_BIND, nullptr)) { + // Bind mount failed, fallback to symlink + remove(sbin_path); + use_bind_mount = false; + } else { + continue; + } + } + + xsymlink(buf, sbin_path); + } + } + close(src); + close(dest); +} + #define ROOTMIR MIRRDIR "/system_root" #define ROOTBLK BLOCKDIR "/system_root" #define MONOPOLICY "/sepolicy" @@ -268,39 +310,13 @@ void SARBase::patch_rootdir() { xmount(ROOTBLK, ROOTMIR, "erofs", MS_RDONLY, nullptr); // Recreate original sbin structure - int src = xopen(ROOTMIR "/sbin", O_RDONLY | O_CLOEXEC); - int dest = xopen("/sbin", O_RDONLY | O_CLOEXEC); - DIR *fp = fdopendir(src); - struct dirent *entry; - struct stat st; - char buf[256]; - while ((entry = xreaddir(fp))) { - if (entry->d_name == "."sv || entry->d_name == ".."sv) - continue; - fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW); - if (S_ISLNK(st.st_mode)) { - xreadlinkat(src, entry->d_name, buf, sizeof(buf)); - xsymlinkat(buf, dest, entry->d_name); - } else { - char spath[256]; - sprintf(buf, "/sbin/%s", entry->d_name); - sprintf(spath, ROOTMIR "/sbin/%s", entry->d_name); - // Create dummy - if (S_ISDIR(st.st_mode)) - xmkdir(buf, st.st_mode & 0777); - else - close(xopen(buf, O_CREAT | O_WRONLY | O_CLOEXEC, st.st_mode & 0777)); - xmount(spath, buf, nullptr, MS_BIND, nullptr); - } - } - close(src); - close(dest); + recreate_sbin(ROOTMIR "/sbin"); // Patch init raw_data init; file_attr attr; bool redirect = false; - src = xopen("/init", O_RDONLY | O_CLOEXEC); + int src = xopen("/init", O_RDONLY | O_CLOEXEC); fd_full_read(src, init.buf, init.sz); fgetattr(src, &attr); close(src); @@ -320,7 +336,7 @@ void SARBase::patch_rootdir() { } } xmkdir(ROOTOVL, 0); - dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC); + int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC); xwrite(dest, init.buf, init.sz); fsetattr(dest, &attr); close(dest); @@ -483,18 +499,7 @@ int magisk_proxy_main(int argc, char *argv[]) { sbin_overlay(self, config); // Create symlinks pointing back to /root - char path[256]; - int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC); - DIR *dir = xopendir("/root"); - struct dirent *entry; - while((entry = xreaddir(dir))) { - if (entry->d_name == "."sv || entry->d_name == ".."sv) - continue; - sprintf(path, "/root/%s", entry->d_name); - xsymlinkat(path, sbin, entry->d_name); - } - close(sbin); - closedir(dir); + recreate_sbin("/root"); setenv("REMOUNT_ROOT", "1", 1); execv("/sbin/magisk", argv); From 08177c3dd81c3e68ec5a0fc069599607344ee5bb Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 9 Dec 2019 02:44:49 -0500 Subject: [PATCH 12/47] Mount persist partition mirror pre-init --- native/jni/init/init.h | 1 + native/jni/init/mount.cpp | 44 +++++++++++++++++++++++++++++-------- native/jni/init/rootdir.cpp | 7 ++---- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/native/jni/init/init.h b/native/jni/init/init.h index fa7b59783..b759fcaad 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -162,3 +162,4 @@ void load_kernel_info(cmdline *cmd); int dump_magisk(const char *path, mode_t mode); int magisk_proxy_main(int argc, char *argv[]); void setup_klog(); +void mount_sbin(); diff --git a/native/jni/init/mount.cpp b/native/jni/init/mount.cpp index c311004c2..39ca25ef1 100644 --- a/native/jni/init/mount.cpp +++ b/native/jni/init/mount.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "init.h" @@ -42,12 +43,11 @@ static void parse_device(devinfo *dev, const char *uevent) { static void collect_devices() { char path[128]; - struct dirent *entry; devinfo dev{}; DIR *dir = xopendir("/sys/dev/block"); if (dir == nullptr) return; - while ((entry = readdir(dir))) { + for (dirent *entry; (entry = readdir(dir));) { if (entry->d_name == "."sv || entry->d_name == ".."sv) continue; sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name); @@ -57,21 +57,21 @@ static void collect_devices() { closedir(dir); } -static int64_t setup_block(char *block_dev = nullptr) { +static int64_t setup_block(bool write_block = true) { if (dev_list.empty()) collect_devices(); xmkdir("/dev", 0755); + xmkdir("/dev/block", 0755); for (int tries = 0; tries < 3; ++tries) { for (auto &dev : dev_list) { if (strcasecmp(dev.partname, partname) == 0) { - if (block_dev) { + if (write_block) { sprintf(block_dev, "/dev/block/%s", dev.devname); - xmkdir("/dev/block", 0755); } LOGD("Found %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor); dev_t rdev = makedev(dev.major, dev.minor); - mknod(block_dev ? block_dev : "/dev/root", S_IFBLK | 0600, rdev); + mknod(block_dev, S_IFBLK | 0600, rdev); return rdev; } } @@ -119,7 +119,7 @@ if (is_lnk("/system_root" name)) \ #define mount_root(name) \ if (!is_lnk("/" #name) && read_dt_fstab(cmd, #name)) { \ LOGD("Early mount " #name "\n"); \ - setup_block(block_dev); \ + setup_block(); \ xmkdir("/" #name, 0755); \ xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \ mount_list.emplace_back("/" #name); \ @@ -147,7 +147,7 @@ void SARCompatInit::early_mount() { LOGD("Early mount system_root\n"); sprintf(partname, "system%s", cmd->slot); - setup_block(block_dev); + setup_block(); xmkdir("/system_root", 0755); if (xmount(block_dev, "/system_root", "ext4", MS_RDONLY, nullptr)) xmount(block_dev, "/system_root", "erofs", MS_RDONLY, nullptr); @@ -209,7 +209,8 @@ void SARInit::early_mount() { LOGD("Early mount system_root\n"); sprintf(partname, "system%s", cmd->slot); - auto dev = setup_block(); + strcpy(block_dev, "/dev/root"); + auto dev = setup_block(false); if (dev < 0) { // Try NVIDIA naming scheme strcpy(partname, "APP"); @@ -260,3 +261,28 @@ void BaseInit::cleanup() { umount(p.data()); } } + +void mount_sbin() { + LOGD("Mount /sbin tmpfs overlay\n"); + xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755"); + + xmkdir(MAGISKTMP, 0755); + xmkdir(MIRRDIR, 0); + xmkdir(BLOCKDIR, 0); + + // Mount persist partition + strcpy(partname, "persist"); + strcpy(block_dev, BLOCKDIR "/persist"); + const char *mnt_point = MIRRDIR "/persist"; + if (setup_block(false) < 0) { + // Fallback to cache + strcpy(partname, "cache"); + strcpy(block_dev, BLOCKDIR "/cache"); + if (setup_block(false) < 0) + return; + mnt_point = MIRRDIR "/cache"; + xsymlink("./cache", MIRRDIR "/persist"); + } + xmkdir(mnt_point, 0755); + xmount(block_dev, mnt_point, "ext4", 0, nullptr); +} diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index fbd82d94f..47c9dcd6e 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -192,8 +192,7 @@ constexpr const char wrapper[] = ; static void sbin_overlay(const raw_data &self, const raw_data &config) { - LOGD("Mount /sbin tmpfs overlay\n"); - xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755"); + mount_sbin(); // Dump binaries xmkdir(MAGISKTMP, 0755); @@ -302,9 +301,7 @@ void SARBase::patch_rootdir() { sbin_overlay(self, config); // Mount system_root mirror - xmkdir(MIRRDIR, 0); - xmkdir(ROOTMIR, 0); - xmkdir(BLOCKDIR, 0); + xmkdir(ROOTMIR, 0755); mknod(ROOTBLK, S_IFBLK | 0600, system_dev); if (xmount(ROOTBLK, ROOTMIR, "ext4", MS_RDONLY, nullptr)) xmount(ROOTBLK, ROOTMIR, "erofs", MS_RDONLY, nullptr); From 490e6a6f236ba31b3b195a159d57b010702d0405 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 9 Dec 2019 04:14:30 -0500 Subject: [PATCH 13/47] Add new API to load sepolicy rule file --- native/jni/Android.mk | 3 +- .../{magiskpolicy => include}/magiskpolicy.h | 8 +- native/jni/magiskpolicy/api.cpp | 3 +- native/jni/magiskpolicy/magiskpolicy.cpp | 486 ++---------------- native/jni/magiskpolicy/policydb.cpp | 2 +- native/jni/magiskpolicy/rules.cpp | 2 +- native/jni/magiskpolicy/statement.cpp | 425 +++++++++++++++ 7 files changed, 483 insertions(+), 446 deletions(-) rename native/jni/{magiskpolicy => include}/magiskpolicy.h (91%) create mode 100644 native/jni/magiskpolicy/statement.cpp diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 266a11def..0362f30af 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -73,6 +73,7 @@ LOCAL_SRC_FILES := \ magiskpolicy/magiskpolicy.cpp \ magiskpolicy/rules.cpp \ magiskpolicy/policydb.cpp \ + magiskpolicy/statement.cpp \ magiskpolicy/sepolicy.c LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=magiskpolicy_main @@ -97,7 +98,6 @@ ifdef BB_INIT LOCAL_STATIC_LIBRARIES := libsepol libxz libutils LOCAL_C_INCLUDES := \ jni/include \ - jni/magiskpolicy \ $(EXT_PATH)/include \ out \ out/$(TARGET_ARCH_ABI) \ @@ -113,6 +113,7 @@ LOCAL_SRC_FILES := \ magiskpolicy/magiskpolicy.cpp \ magiskpolicy/rules.cpp \ magiskpolicy/policydb.cpp \ + magiskpolicy/statement.cpp \ magiskpolicy/sepolicy.c LOCAL_LDFLAGS := -static diff --git a/native/jni/magiskpolicy/magiskpolicy.h b/native/jni/include/magiskpolicy.h similarity index 91% rename from native/jni/magiskpolicy/magiskpolicy.h rename to native/jni/include/magiskpolicy.h index 0dea4734f..c3927bc40 100644 --- a/native/jni/magiskpolicy/magiskpolicy.h +++ b/native/jni/include/magiskpolicy.h @@ -1,6 +1,3 @@ -/* magiskpolicy.h - Public API for policy patching - */ - #pragma once #include @@ -35,3 +32,8 @@ int sepol_exists(const char *source); // Built in rules void sepol_magisk_rules(); + +// Statement parsing +void parse_statement(const char *statement); +void load_rule_file(const char *file); +void statement_help(); diff --git a/native/jni/magiskpolicy/api.cpp b/native/jni/magiskpolicy/api.cpp index 8fa498ae2..e65a238dd 100644 --- a/native/jni/magiskpolicy/api.cpp +++ b/native/jni/magiskpolicy/api.cpp @@ -1,4 +1,5 @@ -#include "magiskpolicy.h" +#include + #include "sepolicy.h" //#define vprint(fmt, ...) printf(fmt, __VA_ARGS__) diff --git a/native/jni/magiskpolicy/magiskpolicy.cpp b/native/jni/magiskpolicy/magiskpolicy.cpp index d612ee3fb..6a7640f0d 100644 --- a/native/jni/magiskpolicy/magiskpolicy.cpp +++ b/native/jni/magiskpolicy/magiskpolicy.cpp @@ -1,459 +1,59 @@ -/* magiskpolicy.cpp - Main function for policy patching - * - * Includes all the parsing logic for the policy statements - */ - #include #include -#include -#include #include #include #include +#include #include "sepolicy.h" -#include "magiskpolicy.h" -using namespace std; - -static const char *type_msg_1 = -"Type 1:\n" -"\" source_type target_type class perm_set\"\n" -"Rules: allow, deny, auditallow, dontaudit\n"; - -static const char *type_msg_2 = -"Type 2:\n" -"\" source_type target_type class operation xperm_set\"\n" -"Rules: allowxperm, auditallowxperm, dontauditxperm\n" -"* The only supported operation is ioctl\n" -"* The only supported xperm_set format is range ([low-high])\n"; - -static const char *type_msg_3 = -"Type 3:\n" -"\" class\"\n" -"Rules: create, permissive, enforcing\n"; - -static const char *type_msg_4 = -"Type 4:\n" -"\"attradd class attribute\"\n"; - -static const char *type_msg_5 = -"Type 5:\n" -"\" source_type target_type class default_type\"\n" -"Rules: type_transition, type_change, type_member\n"; - -static const char *type_msg_6 = -"Type 6:\n" -"\"name_transition source_type target_type class default_type object_name\"\n"; - - -[[noreturn]] static void statements() { - fprintf(stderr, - "One policy statement should be treated as one parameter;\n" - "this means a full policy statement should be enclosed in quotes;\n" - "multiple policy statements can be provided in a single command\n" - "\n" - "The statements has a format of \" [args...]\"\n" - "Multiple types and permissions can be grouped into collections\n" - "wrapped in curly brackets.\n" - "'*' represents a collection containing all valid matches.\n" - "\n" - "Supported policy statements:\n" - "\n" - "%s\n" - "%s\n" - "%s\n" - "%s\n" - "%s\n" - "%s\n" - "Notes:\n" - "* Type 4 - 6 does not support collections\n" - "* Object classes cannot be collections\n" - "* source_type and target_type can also be attributes\n" - "\n" - "Example: allow { s1 s2 } { t1 t2 } class *\n" - "Will be expanded to:\n" - "\n" - "allow s1 t1 class { all permissions }\n" - "allow s1 t2 class { all permissions }\n" - "allow s2 t1 class { all permissions }\n" - "allow s2 t2 class { all permissions }\n" - "\n", - type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5, type_msg_6); - exit(0); -} +using namespace std::literals; [[noreturn]] static void usage(char *arg0) { fprintf(stderr, - FULL_VER(MagiskPolicy) "\n\n" - "Usage: %s [--options...] [policy statements...]\n" - "\n" - "Options:\n" - " --help show help message for policy statements\n" - " --load FILE load policies from FILE\n" - " --load-split load from preloaded sepolicy or compile\n" - " split policies\n" - " --compile-split compile split cil policies\n" - " --save FILE save policies to FILE\n" - " --live directly apply sepolicy live\n" - " --magisk inject built-in rules for a minimal\n" - " Magisk selinux environment\n" - "\n" - "If neither --load or --compile-split is specified, it will load\n" - "from current live policies (" SELINUX_POLICY ")\n" - "\n", - arg0); +FULL_VER(MagiskPolicy) R"EOF( + +Usage: %s [--options...] [policy statements...] + +Options: + --help show help message for policy statements + --load FILE load policies from FILE + --load-split load from precompiled sepolicy or compile + split policies + --compile-split compile split cil policies + --save FILE save policies to FILE + --live directly apply sepolicy live + --magisk inject built-in rules for a minimal + Magisk selinux environment + --apply FILE apply rules from FILE, read and parsed + line by line as policy statements + +If neither --load or --compile-split is specified, it will load +from current live policies (/sys/fs/selinux/policy) + +)EOF", arg0); exit(1); } -static int parse_bracket(char *tok, char *&stmt, vector *vec) { - if (tok == nullptr || tok[0] != '{') { - // Not in a bracket - vec->push_back(tok); - } else { - if (stmt) - stmt[-1] = ' '; - tok = strchr(tok, '{') + 1; - char *end = strchr(tok, '}'); - if (end == nullptr) // Bracket not closed - return 1; - *end = '\0'; - char *cur; - while ((cur = strtok_r(nullptr, " ", &tok)) != nullptr) - vec->push_back(cur); - stmt = end + 1; - } - return 0; -} - -// Pattern 1: action { source } { target } class { permission } -static int parse_pattern_1(int action, const char *action_str, char *stmt) { - int (*action_func)(const char*, const char*, const char*, const char*); - switch (action) { - case 0: - action_func = sepol_allow; - break; - case 1: - action_func = sepol_deny; - break; - case 2: - action_func = sepol_auditallow; - break; - case 3: - action_func = sepol_dontaudit; - break; - default: - return 1; - } - - int state = 0; - char *cur, *cls; - vector source, target, permission; - while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { - if (cur[0] == '*') cur = ALL; - vector *vec; - switch (state) { - case 0: - vec = &source; - break; - case 1: - vec = ⌖ - break; - case 2: - vec = nullptr; - cls = cur; - break; - case 3: - vec = &permission; - break; - default: - return 1; - } - - if (vec && parse_bracket(cur, stmt, vec)) - return 1; - ++state; - } - if (state != 4 || source.empty() || target.empty() || permission.empty()) - return 1; - - for (auto src : source) - for (auto tgt : target) - for (auto perm : permission) - if (action_func(src, tgt, cls, perm)) - fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, perm); - - return 0; -} - -// Pattern 2: action { source } { target } { class } ioctl range -static int parse_pattern_2(int action, const char *action_str, char *stmt) { - int (*action_func)(const char*, const char*, const char*, const char*); - switch (action) { - case 0: - action_func = sepol_allowxperm; - break; - case 1: - action_func = sepol_auditallowxperm; - break; - case 2: - action_func = sepol_dontauditxperm; - break; - default: - return 1; - } - - int state = 0; - char *cur, *range; - vector source, target, classes; - while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { - if (cur[0] == '*') cur = ALL; - vector *vec; - switch (state) { - case 0: - vec = &source; - break; - case 1: - vec = ⌖ - break; - case 2: - vec = &classes; - break; - case 3: - // Currently only support ioctl - if (strcmp(cur, "ioctl") != 0) - return 1; - vec = nullptr; - break; - case 4: - vec = nullptr; - range = cur; - break; - default: - return 1; - } - - if (vec && parse_bracket(cur, stmt, vec)) - return 1; - ++state; - } - if (state != 5 || source.empty() || target.empty() || classes.empty()) - return 1; - - for (auto src : source) - for (auto tgt : target) - for (auto cls : classes) - if (action_func(src, tgt, cls, range)) - fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, range); - - return 0; -} - -// Pattern 3: action { type } -static int parse_pattern_3(int action, const char *action_str, char* stmt) { - int (*action_func)(const char*); - switch (action) { - case 0: - action_func = sepol_create; - break; - case 1: - action_func = sepol_permissive; - break; - case 2: - action_func = sepol_enforce; - break; - default: - return 1; - } - - char *cur; - vector domains; - while ((cur = strtok_r(nullptr, " {}", &stmt)) != nullptr) { - if (cur[0] == '*') cur = ALL; - domains.push_back(cur); - } - - if (domains.empty()) - return 1; - - for (auto dom : domains) - if (action_func(dom)) - fprintf(stderr, "Error in: %s %s\n", action_str, dom); - - return 0; -} - -// Pattern 4: action { class } { attribute } -static int parse_pattern_4(int action, const char *action_str, char *stmt) { - int (*action_func)(const char*, const char*); - switch (action) { - case 0: - action_func = sepol_attradd; - break; - default: - return 1; - } - - int state = 0; - char *cur; - vector classes, attribute; - while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { - if (cur[0] == '*') cur = ALL; - vector *vec; - switch (state) { - case 0: - vec = &classes; - break; - case 1: - vec = &attribute; - break; - default: - return 1; - } - - if (parse_bracket(cur, stmt, vec)) - return 1; - ++state; - } - if (state != 2 || classes.empty() || attribute.empty()) - return 1; - - for (auto cls : classes) - for (auto attr : attribute) - if (action_func(cls, attr)) - fprintf(stderr, "Error in: %s %s %s\n", action_str, cls, attr); - - return 0; -} - -// Pattern 5: action source target class default -static int parse_pattern_5(int action, const char *action_str, char *stmt) { - int (*action_func)(const char*, const char*, const char*, const char*); - switch (action) { - case 0: - action_func = sepol_typetrans; - break; - case 1: - action_func = sepol_typechange; - break; - case 2: - action_func = sepol_typemember; - break; - default: - return 1; - } - int state = 0; - char *cur; - char *source, *target, *cls, *def; - while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { - switch(state) { - case 0: - source = cur; - break; - case 1: - target = cur; - break; - case 2: - cls = cur; - break; - case 3: - def = cur; - break; - default: - return 1; - } - ++state; - } - if (state < 4) return 1; - if (action_func(source, target, cls, def)) - fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, source, target, cls, def); - return 0; -} - -// Pattern 6: action source target class default filename -static int parse_pattern_6(int action, const char *action_str, char *stmt) { - int state = 0; - char *cur; - char *source, *target, *cls, *def, *filename; - while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { - switch(state) { - case 0: - source = cur; - break; - case 1: - target = cur; - break; - case 2: - cls = cur; - break; - case 3: - def = cur; - break; - case 4: - filename = cur; - break; - default: - return 1; - } - ++state; - } - if (state < 4) return 1; - if (sepol_nametrans(source, target, cls, def, filename)) - fprintf(stderr, "Error in: %s %s %s %s %s %s\n", - action_str, source, target, cls, def, filename); - return 0; -} - -#define add_action(name, type, num) \ -else if (strcmp(name, action) == 0) { \ - if (parse_pattern_##type(num, name, remain)) \ - fprintf(stderr, "Syntax error in '%s'\n\n%s\n", orig.c_str(), type_msg_##type); \ -} - -static void parse_statement(char *statement) { - char *action, *remain; - - // strtok will modify the origin string, duplicate the statement for error messages - string orig(statement); - - action = strtok_r(statement, " ", &remain); - if (remain == nullptr) remain = &action[strlen(action)]; - - if (0) {} - add_action("allow", 1, 0) - add_action("deny", 1, 1) - add_action("auditallow", 1, 2) - add_action("dontaudit", 1, 3) - add_action("allowxperm", 2, 0) - add_action("auditallowxperm", 2, 1) - add_action("dontauditxperm", 2, 2) - add_action("create", 3, 0) - add_action("permissive", 3, 1) - add_action("enforce", 3, 2) - add_action("attradd", 4, 0) - add_action("type_transition", 5, 0) - add_action("type_change", 5, 1) - add_action("type_member", 5, 2) - add_action("name_transition", 6, 0) - else { fprintf(stderr, "Unknown statement: '%s'\n\n", orig.c_str()); } -} - int magiskpolicy_main(int argc, char *argv[]) { cmdline_logging(); - const char *outfile = nullptr; - bool magisk = false, live = false; + const char *out_file = nullptr; + const char *rule_file = nullptr; + bool magisk = false; + bool live = false; if (argc < 2) usage(argv[0]); int i = 1; for (; i < argc; ++i) { // Parse options if (argv[i][0] == '-' && argv[i][1] == '-') { - if (strcmp(argv[i] + 2, "live") == 0) + auto option = argv[i] + 2; + if (option == "live"sv) live = true; - else if (strcmp(argv[i] + 2, "magisk") == 0) + else if (option == "magisk"sv) magisk = true; - else if (strcmp(argv[i] + 2, "load") == 0) { + else if (option == "load"sv) { if (argv[i + 1] == nullptr) usage(argv[0]); if (load_policydb(argv[i + 1])) { @@ -461,23 +61,28 @@ int magiskpolicy_main(int argc, char *argv[]) { return 1; } ++i; - } else if (strcmp(argv[i] + 2, "load-split") == 0) { + } else if (option == "load-split"sv) { if (load_split_cil()) { fprintf(stderr, "Cannot load split cil\n"); return 1; } - } else if (strcmp(argv[i] + 2, "compile-split") == 0) { + } else if (option == "compile-split"sv) { if (compile_split_cil()) { fprintf(stderr, "Cannot compile split cil\n"); return 1; } - } else if (strcmp(argv[i] + 2, "save") == 0) { + } else if (option == "save"sv) { if (argv[i + 1] == nullptr) usage(argv[0]); - outfile = argv[i + 1]; + out_file = argv[i + 1]; ++i; - } else if (strcmp(argv[i] + 2, "help") == 0) { - statements(); + } else if (option == "apply"sv) { + if (argv[i + 1] == nullptr) + usage(argv[0]); + rule_file = argv[i + 1]; + ++i; + } else if (option == "help"sv) { + statement_help(); } else { usage(argv[0]); } @@ -495,6 +100,9 @@ int magiskpolicy_main(int argc, char *argv[]) { if (magisk) sepol_magisk_rules(); + if (rule_file) + load_rule_file(rule_file); + for (; i < argc; ++i) parse_statement(argv[i]); @@ -503,8 +111,8 @@ int magiskpolicy_main(int argc, char *argv[]) { return 1; } - if (outfile && dump_policydb(outfile)) { - fprintf(stderr, "Cannot dump policy to %s\n", outfile); + if (out_file && dump_policydb(out_file)) { + fprintf(stderr, "Cannot dump policy to %s\n", out_file); return 1; } diff --git a/native/jni/magiskpolicy/policydb.cpp b/native/jni/magiskpolicy/policydb.cpp index 723fd3876..2ffaacfad 100644 --- a/native/jni/magiskpolicy/policydb.cpp +++ b/native/jni/magiskpolicy/policydb.cpp @@ -8,8 +8,8 @@ #include #include #include +#include -#include "magiskpolicy.h" #include "sepolicy.h" int load_policydb(const char *file) { diff --git a/native/jni/magiskpolicy/rules.cpp b/native/jni/magiskpolicy/rules.cpp index ffbc83bfc..d930ae69f 100644 --- a/native/jni/magiskpolicy/rules.cpp +++ b/native/jni/magiskpolicy/rules.cpp @@ -1,7 +1,7 @@ #include #include +#include -#include "magiskpolicy.h" #include "sepolicy.h" static void allowSuClient(const char *target) { diff --git a/native/jni/magiskpolicy/statement.cpp b/native/jni/magiskpolicy/statement.cpp new file mode 100644 index 000000000..bf97ae5e4 --- /dev/null +++ b/native/jni/magiskpolicy/statement.cpp @@ -0,0 +1,425 @@ +#include +#include +#include + +#include +#include +#include + +using namespace std; + +static const char *type_msg_1 = +R"EOF(Type 1: +" source_type target_type class perm_set" +Rules: allow, deny, auditallow, dontaudit +)EOF"; + +static const char *type_msg_2 = +R"EOF(Type 2: +" source_type target_type class operation xperm_set" +Rules: allowxperm, auditallowxperm, dontauditxperm +* The only supported operation is ioctl +* The only supported xperm_set format is range ([low-high]) +)EOF"; + +static const char *type_msg_3 = +R"EOF(Type 3: +" class" +Rules: create, permissive, enforcing +)EOF"; + +static const char *type_msg_4 = +R"EOF(Type 4: +"attradd class attribute" +)EOF"; + +static const char *type_msg_5 = +R"EOF(Type 5: +" source_type target_type class default_type" +Rules: type_transition, type_change, type_member +)EOF"; + +static const char *type_msg_6 = +R"EOF(Type 6: +"name_transition source_type target_type class default_type object_name" +)EOF"; + +void statement_help() { + fprintf(stderr, +R"EOF(One policy statement should be treated as one parameter; +this means a full policy statement should be enclosed in quotes. +Multiple policy statements can be provided in a single command. + +The statements has a format of " [args...]" +Multiple types and permissions can be grouped into collections +wrapped in curly brackets. +'*' represents a collection containing all valid matches. + +Supported policy statements: + +%s +%s +%s +%s +%s +%s +Notes: +* Type 4 - 6 does not support collections +* Object classes cannot be collections +* source_type and target_type can also be attributes + +Example: allow { s1 s2 } { t1 t2 } class * +Will be expanded to: + +allow s1 t1 class { all-permissions } +allow s1 t2 class { all-permissions } +allow s2 t1 class { all-permissions } +allow s2 t2 class { all-permissions } + +)EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5, type_msg_6); + exit(0); +} + +static int parse_bracket(char *tok, char *&stmt, vector &vec) { + if (tok == nullptr || tok[0] != '{') { + // Not in a bracket + vec.push_back(tok); + } else { + if (stmt) + stmt[-1] = ' '; + tok = strchr(tok, '{') + 1; + char *end = strchr(tok, '}'); + if (end == nullptr) // Bracket not closed + return 1; + *end = '\0'; + char *cur; + while ((cur = strtok_r(nullptr, " ", &tok)) != nullptr) + vec.push_back(cur); + stmt = end + 1; + } + return 0; +} + +// Pattern 1: action { source } { target } class { permission } +static int parse_pattern_1(int action, const char *action_str, char *stmt) { + int (*action_func)(const char*, const char*, const char*, const char*); + switch (action) { + case 0: + action_func = sepol_allow; + break; + case 1: + action_func = sepol_deny; + break; + case 2: + action_func = sepol_auditallow; + break; + case 3: + action_func = sepol_dontaudit; + break; + default: + return 1; + } + + int state = 0; + char *cur, *cls; + vector source, target, permission; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + vector *vec; + switch (state) { + case 0: + vec = &source; + break; + case 1: + vec = ⌖ + break; + case 2: + vec = nullptr; + cls = cur; + break; + case 3: + vec = &permission; + break; + default: + return 1; + } + + if (vec && parse_bracket(cur, stmt, *vec)) + return 1; + ++state; + } + if (state != 4 || source.empty() || target.empty() || permission.empty()) + return 1; + + for (auto src : source) + for (auto tgt : target) + for (auto perm : permission) + if (action_func(src, tgt, cls, perm)) + LOGW("Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, perm); + + return 0; +} + +// Pattern 2: action { source } { target } { class } ioctl range +static int parse_pattern_2(int action, const char *action_str, char *stmt) { + int (*action_func)(const char*, const char*, const char*, const char*); + switch (action) { + case 0: + action_func = sepol_allowxperm; + break; + case 1: + action_func = sepol_auditallowxperm; + break; + case 2: + action_func = sepol_dontauditxperm; + break; + default: + return 1; + } + + int state = 0; + char *cur, *range; + vector source, target, classes; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + vector *vec; + switch (state) { + case 0: + vec = &source; + break; + case 1: + vec = ⌖ + break; + case 2: + vec = &classes; + break; + case 3: + // Currently only support ioctl + if (strcmp(cur, "ioctl") != 0) + return 1; + vec = nullptr; + break; + case 4: + vec = nullptr; + range = cur; + break; + default: + return 1; + } + + if (vec && parse_bracket(cur, stmt, *vec)) + return 1; + ++state; + } + if (state != 5 || source.empty() || target.empty() || classes.empty()) + return 1; + + for (auto src : source) + for (auto tgt : target) + for (auto cls : classes) + if (action_func(src, tgt, cls, range)) + LOGW("Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, range); + + return 0; +} + +// Pattern 3: action { type } +static int parse_pattern_3(int action, const char *action_str, char* stmt) { + int (*action_func)(const char*); + switch (action) { + case 0: + action_func = sepol_create; + break; + case 1: + action_func = sepol_permissive; + break; + case 2: + action_func = sepol_enforce; + break; + default: + return 1; + } + + char *cur; + vector domains; + while ((cur = strtok_r(nullptr, " {}", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + domains.push_back(cur); + } + + if (domains.empty()) + return 1; + + for (auto dom : domains) + if (action_func(dom)) + LOGW("Error in: %s %s\n", action_str, dom); + + return 0; +} + +// Pattern 4: action { class } { attribute } +static int parse_pattern_4(int action, const char *action_str, char *stmt) { + int (*action_func)(const char*, const char*); + switch (action) { + case 0: + action_func = sepol_attradd; + break; + default: + return 1; + } + + int state = 0; + char *cur; + vector classes, attribute; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + vector *vec; + switch (state) { + case 0: + vec = &classes; + break; + case 1: + vec = &attribute; + break; + default: + return 1; + } + + if (parse_bracket(cur, stmt, *vec)) + return 1; + ++state; + } + if (state != 2 || classes.empty() || attribute.empty()) + return 1; + + for (auto cls : classes) + for (auto attr : attribute) + if (action_func(cls, attr)) + LOGW("Error in: %s %s %s\n", action_str, cls, attr); + + return 0; +} + +// Pattern 5: action source target class default +static int parse_pattern_5(int action, const char *action_str, char *stmt) { + int (*action_func)(const char*, const char*, const char*, const char*); + switch (action) { + case 0: + action_func = sepol_typetrans; + break; + case 1: + action_func = sepol_typechange; + break; + case 2: + action_func = sepol_typemember; + break; + default: + return 1; + } + int state = 0; + char *cur; + char *source, *target, *cls, *def; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + switch(state) { + case 0: + source = cur; + break; + case 1: + target = cur; + break; + case 2: + cls = cur; + break; + case 3: + def = cur; + break; + default: + return 1; + } + ++state; + } + if (state < 4) return 1; + if (action_func(source, target, cls, def)) + LOGW("Error in: %s %s %s %s %s\n", action_str, source, target, cls, def); + return 0; +} + +// Pattern 6: action source target class default filename +static int parse_pattern_6(int action, const char *action_str, char *stmt) { + int state = 0; + char *cur; + char *source, *target, *cls, *def, *filename; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + switch(state) { + case 0: + source = cur; + break; + case 1: + target = cur; + break; + case 2: + cls = cur; + break; + case 3: + def = cur; + break; + case 4: + filename = cur; + break; + default: + return 1; + } + ++state; + } + if (state < 4) return 1; + if (sepol_nametrans(source, target, cls, def, filename)) + LOGW("Error in: %s %s %s %s %s %s\n", action_str, source, target, cls, def, filename); + return 0; +} + +#define add_action(name, type, num) \ +else if (strcmp(name, action) == 0) { \ + if (parse_pattern_##type(num, name, remain)) \ + LOGW("Syntax error in '%s'\n\n%s\n", statement, type_msg_##type); \ +} + +void parse_statement(const char *statement) { + char *action, *remain; + + // strtok will modify strings, duplicate the statement + string stmt(statement); + + action = strtok_r(stmt.data(), " ", &remain); + + if (remain == nullptr) { + LOGE("Syntax error in '%s'\n\n", statement); + return; + } + + if (0) {} + add_action("allow", 1, 0) + add_action("deny", 1, 1) + add_action("auditallow", 1, 2) + add_action("dontaudit", 1, 3) + add_action("allowxperm", 2, 0) + add_action("auditallowxperm", 2, 1) + add_action("dontauditxperm", 2, 2) + add_action("create", 3, 0) + add_action("permissive", 3, 1) + add_action("enforce", 3, 2) + add_action("attradd", 4, 0) + add_action("type_transition", 5, 0) + add_action("type_change", 5, 1) + add_action("type_member", 5, 2) + add_action("name_transition", 6, 0) + else { LOGW("Unknown statement: '%s'\n\n", statement); } +} + +void load_rule_file(const char *file) { + file_readline(file, [](string_view line) -> bool { + if (line.empty() || line[0] == '#') + return true; + parse_statement(line.data()); + return true; + }, true); +} From 8c500709e492b9133e64bb5bed0f81b4d40a8622 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 12 Dec 2019 03:25:48 -0500 Subject: [PATCH 14/47] Remove SAR compatibility mode --- native/jni/init/init.cpp | 5 +---- native/jni/init/init.h | 41 +++++++++++-------------------------- native/jni/init/mount.cpp | 28 ------------------------- native/jni/init/rootdir.cpp | 12 +---------- 4 files changed, 14 insertions(+), 72 deletions(-) diff --git a/native/jni/init/init.cpp b/native/jni/init/init.cpp index 76241e2ac..23a3d6f06 100644 --- a/native/jni/init/init.cpp +++ b/native/jni/init/init.cpp @@ -220,10 +220,7 @@ int main(int argc, char *argv[]) { if (cmd.force_normal_boot) { init = make_unique(argv, &cmd); } else if (cmd.skip_initramfs) { - if (access("/overlay", F_OK) == 0) /* Compatible mode */ - init = make_unique(argv, &cmd); - else - init = make_unique(argv, &cmd); + init = make_unique(argv, &cmd); } else { decompress_ramdisk(); if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0) diff --git a/native/jni/init/init.h b/native/jni/init/init.h index b759fcaad..8251aa891 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -60,20 +60,6 @@ public: MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; }; -class RootFSBase : public MagiskInit { -protected: - int root = -1; - - virtual void setup_rootfs(); -public: - RootFSBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {}; - void start() override { - early_mount(); - setup_rootfs(); - exec_init(); - } -}; - class SARBase : public MagiskInit { protected: raw_data config; @@ -95,7 +81,7 @@ public: * *************/ class ABFirstStageInit : public BaseInit { -protected: +private: void prepare(); public: ABFirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; @@ -106,7 +92,7 @@ public: }; class AFirstStageInit : public BaseInit { -protected: +private: void prepare(); public: AFirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; @@ -139,23 +125,20 @@ public: * Initramfs * **********/ -class RootFSInit : public RootFSBase { +class RootFSInit : public MagiskInit { +private: + int root = -1; + void setup_rootfs(); protected: void early_mount() override; public: - RootFSInit(char *argv[], cmdline *cmd) : RootFSBase(argv, cmd) {}; -}; + RootFSInit(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {}; -/* **************** - * Compat-mode SAR - * ****************/ - -class SARCompatInit : public RootFSBase { -protected: - void early_mount() override; - void setup_rootfs() override; -public: - SARCompatInit(char *argv[], cmdline *cmd) : RootFSBase(argv, cmd) {}; + void start() override { + early_mount(); + setup_rootfs(); + exec_init(); + } }; void load_kernel_info(cmdline *cmd); diff --git a/native/jni/init/mount.cpp b/native/jni/init/mount.cpp index 39ca25ef1..49b7a27e5 100644 --- a/native/jni/init/mount.cpp +++ b/native/jni/init/mount.cpp @@ -112,10 +112,6 @@ static bool read_dt_fstab(cmdline *cmd, const char *name) { return false; } -#define link_root(name) \ -if (is_lnk("/system_root" name)) \ - cp_afc("/system_root" name, name) - #define mount_root(name) \ if (!is_lnk("/" #name) && read_dt_fstab(cmd, #name)) { \ LOGD("Early mount " #name "\n"); \ @@ -138,30 +134,6 @@ void RootFSInit::early_mount() { mount_root(odm); } -void SARCompatInit::early_mount() { - full_read("/init", self.buf, self.sz); - - LOGD("Cleaning rootfs\n"); - root = xopen("/", O_RDONLY | O_CLOEXEC); - frm_rf(root, { ".backup", "overlay", "overlay.d", "proc", "sys" }); - - LOGD("Early mount system_root\n"); - sprintf(partname, "system%s", cmd->slot); - setup_block(); - xmkdir("/system_root", 0755); - if (xmount(block_dev, "/system_root", "ext4", MS_RDONLY, nullptr)) - xmount(block_dev, "/system_root", "erofs", MS_RDONLY, nullptr); - xmkdir("/system", 0755); - xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr); - - link_root("/vendor"); - link_root("/product"); - link_root("/odm"); - mount_root(vendor); - mount_root(product); - mount_root(odm); -} - static void switch_root(const string &path) { LOGD("Switch root to %s\n", path.data()); vector mounts; diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index 47c9dcd6e..27fde186d 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -85,7 +85,7 @@ static void load_overlay_rc(int dirfd) { rewinddir(dir); } -void RootFSBase::setup_rootfs() { +void RootFSInit::setup_rootfs() { if (patch_sepolicy()) { char *addr; size_t size; @@ -141,16 +141,6 @@ void RootFSBase::setup_rootfs() { close(fd); } -void SARCompatInit::setup_rootfs() { - // Clone rootfs - LOGD("Clone root dir from system to rootfs\n"); - int system_root = xopen("/system_root", O_RDONLY | O_CLOEXEC); - clone_dir(system_root, root, false); - close(system_root); - - RootFSBase::setup_rootfs(); -} - bool MagiskInit::patch_sepolicy(const char *file) { bool patch_init = false; From af060b313295ab7b0d23e9e816b97ed37ef03411 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 13 Dec 2019 00:37:06 -0500 Subject: [PATCH 15/47] General QoL changes --- native/jni/core/bootstages.cpp | 50 ++++++++------------ native/jni/core/daemon.cpp | 4 +- native/jni/core/scripting.cpp | 12 ++--- native/jni/init/init.cpp | 45 +++++++++--------- native/jni/init/mount.cpp | 18 ++++---- native/jni/init/rootdir.cpp | 10 ++-- native/jni/magiskboot/bootimg.cpp | 4 +- native/jni/magiskboot/compress.cpp | 66 +++++++++++++-------------- native/jni/magiskboot/compress.h | 4 +- native/jni/magiskboot/ramdisk.cpp | 4 +- native/jni/magiskhide/hide_utils.cpp | 2 +- native/jni/magiskpolicy/policydb.cpp | 2 +- native/jni/magiskpolicy/statement.cpp | 4 +- native/jni/utils/file.cpp | 10 ++-- native/jni/utils/files.h | 41 +++++++++++++---- native/jni/utils/include/stream.h | 63 ++++++++++++------------- native/jni/utils/include/utils.h | 9 ---- native/jni/utils/misc.h | 1 + native/jni/utils/selinux.cpp | 9 ++-- native/jni/utils/stream.cpp | 44 ++++++++++-------- native/jni/utils/xwrap.cpp | 11 ----- native/jni/utils/xwrap.h | 5 ++ 22 files changed, 205 insertions(+), 213 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 9e6f10c97..6c1ba2175 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -131,7 +131,7 @@ void node_entry::create_module_tree(const char *module) { auto full_path = get_path(); snprintf(buf, PATH_MAX, "%s/%s%s", MODULEROOT, module, full_path.c_str()); - unique_ptr dir(xopendir(buf), closedir); + auto dir = xopen_dir(buf); if (!dir) return; @@ -148,7 +148,7 @@ void node_entry::create_module_tree(const char *module) { return; } - for (struct dirent *entry; (entry = xreaddir(dir.get()));) { + for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_name == "."sv || entry->d_name == ".."sv) continue; // Create new node @@ -207,22 +207,18 @@ void node_entry::create_module_tree(const char *module) { } void node_entry::clone_skeleton() { - DIR *dir; - struct dirent *entry; - // Clone the structure auto full_path = get_path(); - snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path.c_str()); - if (!(dir = xopendir(buf))) - return; - while ((entry = xreaddir(dir))) { - if (entry->d_name == "."sv || entry->d_name == ".."sv) - continue; - // Create dummy node - auto dummy = new node_entry(entry->d_name, entry->d_type, IS_DUMMY); - insert(dummy); - } - closedir(dir); + snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path.data()); + if (auto dir = xopen_dir(buf); dir) { + for (dirent *entry; (entry = xreaddir(dir.get()));) { + if (entry->d_name == "."sv || entry->d_name == ".."sv) + continue; + // Create dummy node + auto dummy = new node_entry(entry->d_name, entry->d_type, IS_DUMMY); + insert(dummy); + } + } else { return; } if (status & IS_SKEL) { file_attr attr; @@ -460,9 +456,8 @@ void remove_modules() { LOGI("* Remove all modules and reboot"); chdir(MODULEROOT); rm_rf("lost+found"); - DIR *dir = xopendir("."); - struct dirent *entry; - while ((entry = xreaddir(dir))) { + auto dir = xopen_dir("."); + for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_DIR) { if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv) continue; @@ -471,7 +466,6 @@ void remove_modules() { chdir(".."); } } - closedir(dir); chdir("/"); reboot(); } @@ -479,9 +473,8 @@ void remove_modules() { static void collect_modules() { chdir(MODULEROOT); rm_rf("lost+found"); - DIR *dir = xopendir("."); - struct dirent *entry; - while ((entry = xreaddir(dir))) { + auto dir = xopen_dir("."); + for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_DIR) { if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv) continue; @@ -501,7 +494,6 @@ static void collect_modules() { chdir(".."); } } - closedir(dir); chdir("/"); } @@ -575,15 +567,14 @@ static bool check_data() { } void unlock_blocks() { - DIR *dir; - struct dirent *entry; int fd, dev, OFF = 0; - if (!(dir = xopendir("/dev/block"))) + auto dir = xopen_dir("/dev/block"); + if (!dir) return; - dev = dirfd(dir); + dev = dirfd(dir.get()); - while((entry = readdir(dir))) { + for (dirent *entry; (entry = readdir(dir.get()));) { if (entry->d_type == DT_BLK) { if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0) continue; @@ -592,7 +583,6 @@ void unlock_blocks() { close(fd); } } - closedir(dir); } static bool log_dump = false; diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index d923b1671..c772b3a8a 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -118,10 +118,10 @@ static void main_daemon() { // Unmount pre-init patches if (access(ROOTMNT, F_OK) == 0) { - file_readline(ROOTMNT, [](auto line) -> bool { + file_readline(true, ROOTMNT, [](auto line) -> bool { umount2(line.data(), MNT_DETACH); return true; - }, true); + }); } LOGI(SHOW_VER(Magisk) " daemon started\n"); diff --git a/native/jni/core/scripting.cpp b/native/jni/core/scripting.cpp index ccb4e8da7..070e9c3d2 100644 --- a/native/jni/core/scripting.cpp +++ b/native/jni/core/scripting.cpp @@ -26,15 +26,14 @@ void exec_script(const char *script) { void exec_common_script(const char *stage) { char path[4096]; - DIR *dir; - struct dirent *entry; sprintf(path, SECURE_DIR "/%s.d", stage); - if (!(dir = xopendir(path))) + auto dir = xopen_dir(path); + if (!dir) return; chdir(path); - bool pfs = strcmp(stage, "post-fs-data") == 0; - while ((entry = xreaddir(dir))) { + bool pfs = stage == "post-fs-data"sv; + for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_REG) { if (access(entry->d_name, X_OK) == -1) continue; @@ -50,13 +49,12 @@ void exec_common_script(const char *stage) { } } - closedir(dir); chdir("/"); } void exec_module_script(const char *stage, const vector &module_list) { char path[4096]; - bool pfs = strcmp(stage, "post-fs-data") == 0; + bool pfs = stage == "post-fs-data"sv; for (auto &m : module_list) { const char* module = m.c_str(); sprintf(path, MODULEROOT "/%s/%s.sh", module, stage); diff --git a/native/jni/init/init.cpp b/native/jni/init/init.cpp index 23a3d6f06..394a4dded 100644 --- a/native/jni/init/init.cpp +++ b/native/jni/init/init.cpp @@ -197,10 +197,10 @@ int main(int argc, char *argv[]) { return test_main(argc, argv); #endif - if (argc > 1 && strcmp(argv[1], "-x") == 0) { - if (strcmp(argv[2], "magisk") == 0) + if (argc > 1 && argv[1] == "-x"sv) { + if (argv[2] == "magisk"sv) return dump_magisk(argv[3], 0755); - else if (strcmp(argv[2], "manager") == 0) + else if (argv[2] == "manager"sv) return dump_manager(argv[3], 0644); } @@ -208,27 +208,28 @@ int main(int argc, char *argv[]) { return 1; setup_klog(); - if (argc > 1 && argv[1] == "selinux_setup"sv) { - auto init = make_unique(argv); - init->start(); - } - - cmdline cmd{}; - load_kernel_info(&cmd); - unique_ptr init; - if (cmd.force_normal_boot) { - init = make_unique(argv, &cmd); - } else if (cmd.skip_initramfs) { - init = make_unique(argv, &cmd); + cmdline cmd{}; + + if (argc > 1 && argv[1] == "selinux_setup"sv) { + init = make_unique(argv); } else { - decompress_ramdisk(); - if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0) - init = make_unique(argv, &cmd); - else if (access("/apex", F_OK) == 0) - init = make_unique(argv, &cmd); - else - init = make_unique(argv, &cmd); + // This will also mount /sys and /proc + load_kernel_info(&cmd); + + if (cmd.force_normal_boot) { + init = make_unique(argv, &cmd); + } else if (cmd.skip_initramfs) { + init = make_unique(argv, &cmd); + } else { + decompress_ramdisk(); + if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0) + init = make_unique(argv, &cmd); + else if (access("/apex", F_OK) == 0) + init = make_unique(argv, &cmd); + else + init = make_unique(argv, &cmd); + } } // Run the main routine diff --git a/native/jni/init/mount.cpp b/native/jni/init/mount.cpp index 49b7a27e5..c8bcbc5bf 100644 --- a/native/jni/init/mount.cpp +++ b/native/jni/init/mount.cpp @@ -44,17 +44,15 @@ static void parse_device(devinfo *dev, const char *uevent) { static void collect_devices() { char path[128]; devinfo dev{}; - DIR *dir = xopendir("/sys/dev/block"); - if (dir == nullptr) - return; - for (dirent *entry; (entry = readdir(dir));) { - if (entry->d_name == "."sv || entry->d_name == ".."sv) - continue; - sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name); - parse_device(&dev, path); - dev_list.push_back(dev); + if (auto dir = xopen_dir("/sys/dev/block"); dir) { + for (dirent *entry; (entry = readdir(dir.get()));) { + if (entry->d_name == "."sv || entry->d_name == ".."sv) + continue; + sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name); + parse_device(&dev, path); + dev_list.push_back(dev); + } } - closedir(dir); } static int64_t setup_block(bool write_block = true) { diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index 27fde186d..c1ab38c5b 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -426,17 +426,16 @@ static void patch_fstab(const string &fstab) { #define FSR "/first_stage_ramdisk" void ABFirstStageInit::prepare() { - DIR *dir = xopendir(FSR); + auto dir = xopen_dir(FSR); if (!dir) return; string fstab(FSR "/"); - for (dirent *de; (de = readdir(dir));) { + for (dirent *de; (de = xreaddir(dir.get()));) { if (strstr(de->d_name, "fstab")) { fstab += de->d_name; break; } } - closedir(dir); if (fstab.length() == sizeof(FSR)) return; @@ -453,14 +452,13 @@ void ABFirstStageInit::prepare() { } void AFirstStageInit::prepare() { - DIR *dir = xopendir("/"); - for (dirent *de; (de = readdir(dir));) { + auto dir = xopen_dir("/"); + for (dirent *de; (de = xreaddir(dir.get()));) { if (strstr(de->d_name, "fstab")) { patch_fstab(de->d_name); break; } } - closedir(dir); // Move stuffs for next stage xmkdir("/system", 0755); diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index 93745bb88..3ab44d753 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -22,14 +22,14 @@ uint32_t dyn_img_hdr::j32 = 0; uint64_t dyn_img_hdr::j64 = 0; static void decompress(format_t type, int fd, const void *in, size_t size) { - auto ptr = get_decoder(type, make_stream(fd)); + auto ptr = get_decoder(type, make_unique(fd)); ptr->write(in, size); } static off_t compress(format_t type, int fd, const void *in, size_t size) { auto prev = lseek(fd, 0, SEEK_CUR); { - auto strm = get_encoder(type, make_stream(fd)); + auto strm = get_encoder(type, make_unique(fd)); strm->write(in, size); } auto now = lseek(fd, 0, SEEK_CUR); diff --git a/native/jni/magiskboot/compress.cpp b/native/jni/magiskboot/compress.cpp index 8059aa3d1..ee5bae9b8 100644 --- a/native/jni/magiskboot/compress.cpp +++ b/native/jni/magiskboot/compress.cpp @@ -57,7 +57,7 @@ protected: ENCODE } mode; - gz_strm(mode_t mode, sFILE &&fp) : cpr_stream(std::move(fp)), mode(mode) { + gz_strm(mode_t mode, stream_ptr &&base) : cpr_stream(std::move(base)), mode(mode) { switch(mode) { case DECODE: inflateInit2(&strm, 15 | 16); @@ -99,12 +99,12 @@ private: class gz_decoder : public gz_strm { public: - explicit gz_decoder(sFILE &&fp) : gz_strm(DECODE, std::move(fp)) {}; + explicit gz_decoder(stream_ptr &&base) : gz_strm(DECODE, std::move(base)) {}; }; class gz_encoder : public gz_strm { public: - explicit gz_encoder(sFILE &&fp) : gz_strm(ENCODE, std::move(fp)) {}; + explicit gz_encoder(stream_ptr &&base) : gz_strm(ENCODE, std::move(base)) {}; }; class bz_strm : public cpr_stream { @@ -131,7 +131,7 @@ protected: ENCODE } mode; - bz_strm(mode_t mode, sFILE &&fp) : cpr_stream(std::move(fp)), mode(mode) { + bz_strm(mode_t mode, stream_ptr &&base) : cpr_stream(std::move(base)), mode(mode) { switch(mode) { case DECODE: BZ2_bzDecompressInit(&strm, 0, 0); @@ -173,12 +173,12 @@ private: class bz_decoder : public bz_strm { public: - explicit bz_decoder(sFILE &&fp) : bz_strm(DECODE, std::move(fp)) {}; + explicit bz_decoder(stream_ptr &&base) : bz_strm(DECODE, std::move(base)) {}; }; class bz_encoder : public bz_strm { public: - explicit bz_encoder(sFILE &&fp) : bz_strm(ENCODE, std::move(fp)) {}; + explicit bz_encoder(stream_ptr &&base) : bz_strm(ENCODE, std::move(base)) {}; }; class lzma_strm : public cpr_stream { @@ -199,8 +199,8 @@ protected: ENCODE_LZMA } mode; - lzma_strm(mode_t mode, sFILE &&fp) - : cpr_stream(std::move(fp)), mode(mode), strm(LZMA_STREAM_INIT) { + lzma_strm(mode_t mode, stream_ptr &&base) + : cpr_stream(std::move(base)), mode(mode), strm(LZMA_STREAM_INIT) { lzma_options_lzma opt; // Initialize preset @@ -247,22 +247,22 @@ private: class lzma_decoder : public lzma_strm { public: - explicit lzma_decoder(sFILE &&fp) : lzma_strm(DECODE, std::move(fp)) {} + explicit lzma_decoder(stream_ptr &&base) : lzma_strm(DECODE, std::move(base)) {} }; class xz_encoder : public lzma_strm { public: - explicit xz_encoder(sFILE &&fp) : lzma_strm(ENCODE_XZ, std::move(fp)) {} + explicit xz_encoder(stream_ptr &&base) : lzma_strm(ENCODE_XZ, std::move(base)) {} }; class lzma_encoder : public lzma_strm { public: - explicit lzma_encoder(sFILE &&fp) : lzma_strm(ENCODE_LZMA, std::move(fp)) {} + explicit lzma_encoder(stream_ptr &&base) : lzma_strm(ENCODE_LZMA, std::move(base)) {} }; class LZ4F_decoder : public cpr_stream { public: - explicit LZ4F_decoder(sFILE &&fp) : cpr_stream(std::move(fp)), outbuf(nullptr) { + explicit LZ4F_decoder(stream_ptr &&base) : cpr_stream(std::move(base)), outbuf(nullptr) { LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); } @@ -317,8 +317,8 @@ private: class LZ4F_encoder : public cpr_stream { public: - explicit LZ4F_encoder(sFILE &&fp) - : cpr_stream(std::move(fp)), outbuf(nullptr), outCapacity(0) { + explicit LZ4F_encoder(stream_ptr &&base) + : cpr_stream(std::move(base)), outbuf(nullptr), outCapacity(0) { LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); } @@ -378,8 +378,8 @@ private: class LZ4_decoder : public cpr_stream { public: - explicit LZ4_decoder(sFILE &&fp) - : cpr_stream(std::move(fp)), out_buf(new char[LZ4_UNCOMPRESSED]), + explicit LZ4_decoder(stream_ptr &&base) + : cpr_stream(std::move(base)), out_buf(new char[LZ4_UNCOMPRESSED]), buffer(new char[LZ4_COMPRESSED]), init(false), block_sz(0), buf_off(0) {} ~LZ4_decoder() override { @@ -439,8 +439,8 @@ private: class LZ4_encoder : public cpr_stream { public: - explicit LZ4_encoder(sFILE &&fp) - : cpr_stream(std::move(fp)), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]), + explicit LZ4_encoder(stream_ptr &&base) + : cpr_stream(std::move(base)), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]), init(false), buf_off(0), in_total(0) {} int write(const void *in, size_t size) override { @@ -500,38 +500,38 @@ private: unsigned in_total; }; -stream_ptr get_encoder(format_t type, sFILE &&fp) { +stream_ptr get_encoder(format_t type, stream_ptr &&base) { switch (type) { case XZ: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case LZMA: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case BZIP2: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case LZ4: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case LZ4_LEGACY: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case GZIP: default: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); } } -stream_ptr get_decoder(format_t type, sFILE &&fp) { +stream_ptr get_decoder(format_t type, stream_ptr &&base) { switch (type) { case XZ: case LZMA: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case BZIP2: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case LZ4: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case LZ4_LEGACY: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); case GZIP: default: - return make_unique(std::move(fp)); + return make_unique(std::move(base)); } } @@ -573,7 +573,7 @@ void decompress(char *infile, const char *outfile) { } FILE *out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we"); - strm = get_decoder(type, make_sFILE(out_fp)); + strm = get_decoder(type, make_unique(out_fp)); if (ext) *ext = '.'; } if (strm->write(buf, len) < 0) @@ -614,7 +614,7 @@ void compress(const char *method, const char *infile, const char *outfile) { out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we"); } - auto strm = get_encoder(it->second, make_sFILE(out_fp)); + auto strm = get_encoder(it->second, make_unique(out_fp)); char buf[4096]; size_t len; diff --git a/native/jni/magiskboot/compress.h b/native/jni/magiskboot/compress.h index bb162455f..7a8ab9c23 100644 --- a/native/jni/magiskboot/compress.h +++ b/native/jni/magiskboot/compress.h @@ -4,9 +4,9 @@ #include "format.h" -stream_ptr get_encoder(format_t type, sFILE &&fp); +stream_ptr get_encoder(format_t type, stream_ptr &&base); -stream_ptr get_decoder(format_t type, sFILE &&fp); +stream_ptr get_decoder(format_t type, stream_ptr &&base); void compress(const char *method, const char *infile, const char *outfile); diff --git a/native/jni/magiskboot/ramdisk.cpp b/native/jni/magiskboot/ramdisk.cpp index 798292409..67a65e7da 100644 --- a/native/jni/magiskboot/ramdisk.cpp +++ b/native/jni/magiskboot/ramdisk.cpp @@ -244,7 +244,7 @@ void magisk_cpio::compress() { uint8_t *data; size_t len; - auto strm = make_stream(get_encoder(XZ, make_stream(data, len))); + auto strm = make_stream_fp(get_encoder(XZ, make_unique(data, len))); dump(strm.release()); entries.clear(); @@ -264,7 +264,7 @@ void magisk_cpio::decompress() { char *data; size_t len; { - auto strm = get_decoder(XZ, make_stream(data, len)); + auto strm = get_decoder(XZ, make_unique(data, len)); strm->write(it->second->data, it->second->filesize); } diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index c08a8eacc..b451fa386 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -183,7 +183,7 @@ bool init_list() { // Migrate old hide list into database if (access(LEGACY_LIST, R_OK) == 0) { - file_readline(LEGACY_LIST, [](string_view s) -> bool { + file_readline(true, LEGACY_LIST, [](string_view s) -> bool { add_list(s.data()); return true; }); diff --git a/native/jni/magiskpolicy/policydb.cpp b/native/jni/magiskpolicy/policydb.cpp index 2ffaacfad..6c775b1c2 100644 --- a/native/jni/magiskpolicy/policydb.cpp +++ b/native/jni/magiskpolicy/policydb.cpp @@ -178,7 +178,7 @@ int dump_policydb(const char *file) { size_t len; { - auto fp = make_stream(data, len); + auto fp = make_stream_fp(data, len); struct policy_file pf; policy_file_init(&pf); pf.type = PF_USE_STDIO; diff --git a/native/jni/magiskpolicy/statement.cpp b/native/jni/magiskpolicy/statement.cpp index bf97ae5e4..43f8b8bff 100644 --- a/native/jni/magiskpolicy/statement.cpp +++ b/native/jni/magiskpolicy/statement.cpp @@ -416,10 +416,10 @@ void parse_statement(const char *statement) { } void load_rule_file(const char *file) { - file_readline(file, [](string_view line) -> bool { + file_readline(true, file, [](string_view line) -> bool { if (line.empty() || line[0] == '#') return true; parse_statement(line.data()); return true; - }, true); + }); } diff --git a/native/jni/utils/file.cpp b/native/jni/utils/file.cpp index 42f8463b5..9af4365b3 100644 --- a/native/jni/utils/file.cpp +++ b/native/jni/utils/file.cpp @@ -1,8 +1,4 @@ -/* file.cpp - Contains all files related utilities - */ - #include -#include #include #include #include @@ -359,7 +355,7 @@ void write_zero(int fd, size_t size) { } } -void file_readline(const char *file, const function &fn, bool trim) { +void file_readline(bool trim, const char *file, const std::function &fn) { FILE *fp = xfopen(file, "re"); if (fp == nullptr) return; @@ -384,7 +380,7 @@ void file_readline(const char *file, const function &fn, boo } void parse_prop_file(const char *file, const function &fn) { - file_readline(file, [&](string_view line_view) -> bool { + file_readline(true, file, [&](string_view line_view) -> bool { char *line = (char *) line_view.data(); if (line[0] == '#') return true; @@ -393,7 +389,7 @@ void parse_prop_file(const char *file, const function &fn) { diff --git a/native/jni/utils/files.h b/native/jni/utils/files.h index 26880258b..a53f632c5 100644 --- a/native/jni/utils/files.h +++ b/native/jni/utils/files.h @@ -1,17 +1,16 @@ #pragma once +#include +#include #include #include #include +#include "xwrap.h" + #define do_align(p, a) (((p) + (a) - 1) / (a) * (a)) #define align_off(p, a) (do_align(p, a) - (p)) -using sFILE = std::unique_ptr; -static inline sFILE make_sFILE(FILE *fp = nullptr) { - return sFILE(fp, fclose); -} - struct file_attr { struct stat st; char con[128]; @@ -36,12 +35,15 @@ void clone_attr(const char *source, const char *target); void fd_full_read(int fd, void **buf, size_t *size); void full_read(const char *filename, void **buf, size_t *size); void write_zero(int fd, size_t size); - -void file_readline(const char *file, const std::function &fn, bool trim = false); -void parse_prop_file(const char *file, const std::function - &fn); +void file_readline(bool trim, const char *file, const std::function &fn); +static inline void file_readline(const char *file, + const std::function &fn) { + file_readline(false, file, fn); +} +void parse_prop_file(const char *file, + const std::function &fn); void *__mmap(const char *filename, size_t *size, bool rw); -void frm_rf(int dirfd, std::initializer_list excl = std::initializer_list()); +void frm_rf(int dirfd, std::initializer_list excl = {}); void clone_dir(int src, int dest, bool overwrite = true); void parse_mnt(const char *file, const std::function &fn); @@ -80,3 +82,22 @@ void mmap_rw(const char *filename, B &buf, L &sz) { buf = (B) __mmap(filename, &__sz, true); sz = __sz; } + +using sFILE = std::unique_ptr; +using sDIR = std::unique_ptr; + +static inline sDIR open_dir(const char *path) { + return sDIR(opendir(path), closedir); +} + +static inline sDIR xopen_dir(const char *path) { + return sDIR(xopendir(path), closedir); +} + +static inline sFILE open_file(const char *path, const char *mode) { + return sFILE(fopen(path, mode), fclose); +} + +static inline sFILE xopen_file(const char *path, const char *mode) { + return sFILE(xfopen(path, mode), fclose); +} diff --git a/native/jni/utils/include/stream.h b/native/jni/utils/include/stream.h index a07097fce..4e07dce25 100644 --- a/native/jni/utils/include/stream.h +++ b/native/jni/utils/include/stream.h @@ -5,17 +5,6 @@ #include "../files.h" -class stream; - -using stream_ptr = std::unique_ptr; - -sFILE make_stream(stream_ptr &&strm); - -template -sFILE make_stream(Args &&... args) { - return make_stream(stream_ptr(new T(std::forward(args)...))); -} - class stream { public: virtual int read(void *buf, size_t len); @@ -24,35 +13,22 @@ public: virtual ~stream() = default; }; -// Delegates all operations to the base FILE pointer +using stream_ptr = std::unique_ptr; + +// Delegates all operations to base stream class filter_stream : public stream { public: - filter_stream(sFILE &&fp = make_sFILE()) : fp(std::move(fp)) {} + filter_stream(stream_ptr &&base) : base(std::move(base)) {} int read(void *buf, size_t len) override; int write(const void *buf, size_t len) override; - void set_base(sFILE &&f); - template - void set_base(Args&&... args) { - set_base(make_stream(std::forward(args)...)); - } - protected: - sFILE fp; -}; - -// Handy interface for classes that need custom seek logic -class seekable_stream : public stream { -protected: - size_t _pos = 0; - - off_t seek_pos(off_t off, int whence); - virtual size_t end_pos() = 0; + stream_ptr base; }; // Byte stream that dynamically allocates memory -class byte_stream : public seekable_stream { +class byte_stream : public stream { public: byte_stream(uint8_t *&buf, size_t &len); template @@ -64,10 +40,10 @@ public: private: uint8_t *&_buf; size_t &_len; + size_t _pos = 0; size_t _cap = 0; void resize(size_t new_pos, bool zero = false); - size_t end_pos() final { return _len; } }; // File stream but does not close the file descriptor at any time @@ -81,3 +57,28 @@ public: private: int fd; }; + +/* **************************************** + * Bridge between stream class and C stdio + * ****************************************/ + +// sFILE -> stream_ptr +class fp_stream final : public stream { +public: + fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {} + fp_stream(sFILE &&fp) : fp(std::move(fp)) {} + int read(void *buf, size_t len) override; + int write(const void *buf, size_t len) override; + off_t seek(off_t off, int whence) override; + +private: + sFILE fp; +}; + +// stream_ptr -> sFILE +sFILE make_stream_fp(stream_ptr &&strm); + +template +sFILE make_stream_fp(Args &&... args) { + return make_stream_fp(stream_ptr(new T(std::forward(args)...))); +} diff --git a/native/jni/utils/include/utils.h b/native/jni/utils/include/utils.h index 77638f7d7..e2c71fede 100644 --- a/native/jni/utils/include/utils.h +++ b/native/jni/utils/include/utils.h @@ -1,14 +1,5 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include - #include "../missing.h" #include "../xwrap.h" #include "../files.h" diff --git a/native/jni/utils/misc.h b/native/jni/utils/misc.h index e5cd3721b..ac0d51f99 100644 --- a/native/jni/utils/misc.h +++ b/native/jni/utils/misc.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/native/jni/utils/selinux.cpp b/native/jni/utils/selinux.cpp index f41fd8106..616f1a954 100644 --- a/native/jni/utils/selinux.cpp +++ b/native/jni/utils/selinux.cpp @@ -207,11 +207,10 @@ void restore_rootcon() { setfilecon(MIRRDIR, ROOT_CON); setfilecon(BLOCKDIR, ROOT_CON); - struct dirent *entry; - DIR *dir = xopendir("/sbin"); - int dfd = dirfd(dir); + auto dir = xopen_dir("/sbin"); + int dfd = dirfd(dir.get()); - while ((entry = xreaddir(dir))) { + for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_name == "."sv || entry->d_name == ".."sv) continue; setfilecon_at(dfd, entry->d_name, ROOT_CON); @@ -220,6 +219,4 @@ void restore_rootcon() { setfilecon("/sbin/magisk.bin", MAGISK_CON); setfilecon("/sbin/magisk", MAGISK_CON); setfilecon("/sbin/magiskinit", MAGISK_CON); - - closedir(dir); } diff --git a/native/jni/utils/stream.cpp b/native/jni/utils/stream.cpp index a0b687578..ee80cb0a5 100644 --- a/native/jni/utils/stream.cpp +++ b/native/jni/utils/stream.cpp @@ -23,7 +23,7 @@ static int strm_close(void *v) { return 0; } -sFILE make_stream(stream_ptr &&strm) { +sFILE make_stream_fp(stream_ptr &&strm) { sFILE fp(funopen(strm.release(), strm_read, strm_write, strm_seek, strm_close), fclose); setbuf(fp.get(), nullptr); return fp; @@ -44,29 +44,24 @@ off_t stream::seek(off_t off, int whence) { return -1; } -int filter_stream::read(void *buf, size_t len) { +int fp_stream::read(void *buf, size_t len) { return fread(buf, 1, len, fp.get()); } -int filter_stream::write(const void *buf, size_t len) { +int fp_stream::write(const void *buf, size_t len) { return fwrite(buf, 1, len, fp.get()); } -void filter_stream::set_base(sFILE &&f) { - fp = std::move(f); +off_t fp_stream::seek(off_t off, int whence) { + return fseek(fp.get(), off, whence); } -off_t seekable_stream::seek_pos(off_t off, int whence) { - switch (whence) { - case SEEK_CUR: - return _pos + off; - case SEEK_END: - return end_pos() + off; - case SEEK_SET: - return off; - default: - return -1; - } +int filter_stream::read(void *buf, size_t len) { + return base->read(buf, len); +} + +int filter_stream::write(const void *buf, size_t len) { + return base->write(buf, len); } byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) { @@ -89,9 +84,20 @@ int byte_stream::write(const void *buf, size_t len) { } off_t byte_stream::seek(off_t off, int whence) { - off_t np = seek_pos(off, whence); - if (np < 0) - return -1; + off_t np; + switch (whence) { + case SEEK_CUR: + np = _pos + off; + break; + case SEEK_END: + np = _len + off; + break; + case SEEK_SET: + np = off; + break; + default: + return -1; + } resize(np, true); _pos = np; return np; diff --git a/native/jni/utils/xwrap.cpp b/native/jni/utils/xwrap.cpp index 122cef6a9..29a6db369 100644 --- a/native/jni/utils/xwrap.cpp +++ b/native/jni/utils/xwrap.cpp @@ -1,17 +1,6 @@ -/* xwrap.cpp - wrappers around existing library functions. - * - * Functions with the x prefix are wrappers that either succeed or log the - * error message. They usually have the same arguments and return value - * as the function they wrap. - * - */ - #include -#include #include #include -#include -#include #include #include #include diff --git a/native/jni/utils/xwrap.h b/native/jni/utils/xwrap.h index 106e15b87..ffa4f6536 100644 --- a/native/jni/utils/xwrap.h +++ b/native/jni/utils/xwrap.h @@ -1,5 +1,10 @@ #pragma once +#include +#include +#include +#include + FILE *xfopen(const char *pathname, const char *mode); FILE *xfdopen(int fd, const char *mode); int xopen(const char *pathname, int flags); From 12fda2928051601ebc7fac980fae29a80e7979cc Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 13 Dec 2019 06:05:12 -0500 Subject: [PATCH 16/47] Add support for pre-init custom sepolicy patches Close #1685 --- native/jni/init/init.h | 13 ++++++++++--- native/jni/init/mount.cpp | 22 ++++++++++++++++++---- native/jni/init/rootdir.cpp | 16 ++++++++++++++++ native/jni/magiskpolicy/policydb.cpp | 3 ++- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/native/jni/init/init.h b/native/jni/init/init.h index 8251aa891..746fa0c45 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -3,6 +3,8 @@ #include #include +#include + struct cmdline { bool skip_initramfs; bool force_normal_boot; @@ -45,7 +47,7 @@ protected: virtual void cleanup(); public: BaseInit(char *argv[], cmdline *cmd) : - cmd(cmd), argv(argv), mount_list{"/sys", "/proc", "/dev"} {} + cmd(cmd), argv(argv), mount_list{"/sys", "/proc"} {} virtual ~BaseInit() = default; virtual void start() = 0; }; @@ -53,6 +55,7 @@ public: class MagiskInit : public BaseInit { protected: raw_data self; + const char *persist_dir; virtual void early_mount() = 0; bool patch_sepolicy(const char *file = "/sepolicy"); @@ -68,7 +71,9 @@ protected: void backup_files(); void patch_rootdir(); public: - SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {}; + SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) { + persist_dir = MIRRDIR "/persist/magisk"; + } void start() override { early_mount(); patch_rootdir(); @@ -132,7 +137,9 @@ private: protected: void early_mount() override; public: - RootFSInit(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {}; + RootFSInit(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) { + persist_dir = "/dev/.magisk/mirror/persist/magisk"; + } void start() override { early_mount(); diff --git a/native/jni/init/mount.cpp b/native/jni/init/mount.cpp index c8bcbc5bf..40afba5cd 100644 --- a/native/jni/init/mount.cpp +++ b/native/jni/init/mount.cpp @@ -126,6 +126,13 @@ void RootFSInit::early_mount() { root = xopen("/", O_RDONLY | O_CLOEXEC); rename("/.backup/init", "/init"); + // Mount sbin overlay for persist, but move it and add to cleanup list + mount_sbin(); + xmount("/sbin", "/dev", nullptr, MS_MOVE, nullptr); + mount_list.emplace_back("/dev"); + mount_list.emplace_back("/dev/.magisk/mirror/persist"); + mount_list.emplace_back("/dev/.magisk/mirror/cache"); + mount_root(system); mount_root(vendor); mount_root(product); @@ -169,6 +176,7 @@ void SARInit::early_mount() { // Make dev writable xmkdir("/dev", 0755); xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755"); + mount_list.emplace_back("/dev"); backup_files(); @@ -227,9 +235,11 @@ void SecondStageInit::early_mount() { void BaseInit::cleanup() { // Unmount in reverse order for (auto &p : reversed(mount_list)) { - LOGD("Unmount [%s]\n", p.data()); - umount(p.data()); + if (xumount(p.data()) == 0) + LOGD("Unmount [%s]\n", p.data()); } + mount_list.clear(); + mount_list.shrink_to_fit(); } void mount_sbin() { @@ -248,8 +258,12 @@ void mount_sbin() { // Fallback to cache strcpy(partname, "cache"); strcpy(block_dev, BLOCKDIR "/cache"); - if (setup_block(false) < 0) - return; + if (setup_block(false) < 0) { + // Try NVIDIA's BS + strcpy(partname, "CAC"); + if (setup_block(false) < 0) + return; + } mnt_point = MIRRDIR "/cache"; xsymlink("./cache", MIRRDIR "/persist"); } diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index c1ab38c5b..8c7050f29 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -164,7 +164,23 @@ bool MagiskInit::patch_sepolicy(const char *file) { sepol_magisk_rules(); sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL); + + // Custom rules + if (auto dir = xopen_dir(persist_dir); dir) { + char path[4096]; + for (dirent *entry; (entry = xreaddir(dir.get()));) { + if (entry->d_name == "."sv || entry->d_name == ".."sv) + continue; + snprintf(path, sizeof(path), "%s/%s/sepolicy.rule", persist_dir, entry->d_name); + if (access(path, R_OK) == 0) { + LOGD("Loading custom sepolicy patch: %s\n", path); + load_rule_file(path); + } + } + } + dump_policydb(file); + destroy_policydb(); // Remove OnePlus stupid debug sepolicy and use our own if (access("/sepolicy_debug", F_OK) == 0) { diff --git a/native/jni/magiskpolicy/policydb.cpp b/native/jni/magiskpolicy/policydb.cpp index 6c775b1c2..c446816cc 100644 --- a/native/jni/magiskpolicy/policydb.cpp +++ b/native/jni/magiskpolicy/policydb.cpp @@ -13,6 +13,7 @@ #include "sepolicy.h" int load_policydb(const char *file) { + LOGD("Load policy from: %s\n", file); if (magisk_policydb) destroy_policydb(); @@ -101,7 +102,7 @@ static void load_cil(struct cil_db *db, const char *file) { size_t size; mmap_ro(file, addr, size); cil_add_file(db, (char *) file, addr, size); - LOGD("cil_add[%s]\n", file); + LOGD("cil_add [%s]\n", file); munmap(addr, size); } From 4f4f54a059827adb54905e3cd85c347ce0384fca Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 13 Dec 2019 08:31:24 -0500 Subject: [PATCH 17/47] Remove unused code --- native/jni/init/rootdir.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index 8c7050f29..bc323e5ee 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -101,17 +101,8 @@ void RootFSInit::setup_rootfs() { munmap(addr, size); } - // Handle legacy overlays - int fd = open("/overlay", O_RDONLY | O_CLOEXEC); - if (fd >= 0) { - LOGD("Merge overlay folder\n"); - mv_dir(fd, root); - close(fd); - rmdir("/overlay"); - } - // Handle overlays - fd = open("/overlay.d", O_RDONLY | O_CLOEXEC); + int fd = open("/overlay.d", O_RDONLY | O_CLOEXEC); if (fd >= 0) { LOGD("Merge overlay.d\n"); load_overlay_rc(fd); From b4d0ad971314b4370b5eeb46d52feb8dbfe06903 Mon Sep 17 00:00:00 2001 From: Rom Date: Wed, 11 Dec 2019 16:25:05 +0100 Subject: [PATCH 18/47] Update French translation --- app/src/main/res/values-fr/strings.xml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 68e27a37d..00f7c041d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -8,6 +8,7 @@ Paramètres Installer Version de Magisk non prise en charge + Cette version de Magisk Manager ne supporte pas de version de Magisk inférieure à la %1$s.\n\nL\'application se comportera comme si Magisk n'était pas installé, veuillez mettre à jour Magisk dès que possible. Magisk n’est pas installé. @@ -28,7 +29,7 @@ Dernière version disponible : %1$s Désinstaller Désinstaller Magisk - Tous les modules seront désactivés ou supprimés. Les permissions de super‐utilisateur seront perdues et vos données seront potentiellement chiffrées si elles ne le sont pas déjà. + Tous les modules seront désactivés/supprimés!\nL'enracinement sera retiré!\nVos données seront potentiellement chiffrées si elles ne le sont pas déjà. Mise à jour (Mode « sans modules » activé) @@ -94,7 +95,6 @@ Redémarrer afin d’appliquer les réglages. Notes de version Cache du dépôt effacé - DTBO a été modifié ! Magisk Manager vient de modifier le fichier dtbo.img, veuillez redémarrer. Installation @@ -113,8 +113,9 @@ Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de l’API SafetyNet de Google.Voulez‐vous autoriser Magisk Manager à télécharger une extension (contenant GoogleApiClient) pour les contrôles SafetyNet ? Échec de l’installation Installation additionnelle requise - Votre appareil a besoin d’une configuration supplémentaire pour que Magisk fonctionne correctement. Pour cela, il est nécessaire de télécharger un fichier ZIP d’installation de Magisk. Voulez‐vous le faire maintenant ? + Votre appareil a besoin d’une installation supplémentaire pour que Magisk fonctionne correctement. Celà téléchargera le fichier ZIP d’installation de Magisk. Voulez-vous le faire maintenant ? Démarrer l’installation de l’environnement… + Authentifier Général @@ -144,9 +145,10 @@ Fichier hosts sans système Prise en charge du fichier hosts sans système pour les applications de type AdBlock. Ajout d’un module hosts sans système - Taper le nom de l\'application désirée + + Taper le nom de l'application désirée Nouveau nom - L\'application sera réempacter sous ce nom + L'application sera réempacter sous ce nom Format invalide Applications et ADB Applications uniquement @@ -165,6 +167,9 @@ %1$d secondes Authentifier à nouveau après la mise à niveau Authentifier à nouveau les autorisations super‐utilisateur après une mise à jour de l’application + Activer l'authentification Biométrique + Utiliser l'authentification biométrique pour autoriser les accès super-utilisateur + Périphérique non supporté ou les réglages biométriques ne sont pas actifs Mode multi‐utilisateur Propriétaire de l’appareil uniquement From 404104208f6517421c72e97e7a3d01059c12d0b5 Mon Sep 17 00:00:00 2001 From: JoanVC100 Date: Sat, 7 Dec 2019 00:39:06 +0100 Subject: [PATCH 19/47] Update ca-strings + Fixes --- app/src/main/res/values-ca/strings.xml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index ac9eeaf6e..f35f994b5 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -8,6 +8,7 @@ Configuració Instal·lar Versió de Magisk incompatible + Aquesta versió de Magisk Manager no suporta una versió inferior a la 18.0.\n\nPots actualitzar Magisk manualment o pots instal·lar una versió anterior de la App. Magisk no està instal·lat @@ -80,10 +81,10 @@ Instal·lació directa (Recomanat) Instal·la a la ranura inactiva (Després d\'una OTA) El teu dispositiu serà FORÇAT a arrancar en l\'actual ranura inactiva després del reinici!\nUtilitza aquesta opció NOMÉS quan l\'OTA s\'hagi fet.\nContinuar? - Sel·lecciona un mètode + Selecciona un mètode Instal·lació addicional - Sel·lecciona i arranja un arxiu - Sel·lecciona una imatge crua (*.img) o un ODIN tarfile (*.tar) + Selecciona i arranja un arxiu + Selecciona una imatge crua (*.img) o un ODIN tarfile (*.tar) Reinici en 5 segons… @@ -94,7 +95,6 @@ Reinicia per aplicar els canvis Notes de llançament Memòria cau del repositori netejada - S\'ha arranjat DTBO Magisk Manager ha arranjat dtbo.img. Si us plau, reinicia el telèfon. Arranjament… @@ -110,18 +110,19 @@ Restauració feta! La còpia de seguretat de Stock no existeix! Baixar codi propietari - Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nPot permetre que Magisk Manager baixi una extensió que conté el GoogleApiClient per poder fer la comprobació de SafetyNet? + Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nVot permetre que Magisk Manager baixi una extensió que conté el GoogleApiClient per poder fer la comprovació de SafetyNet? Instal·lació fallida. Es requereix instal·lació addicional - El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instalació ara? + El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instal·lació ara? S\'està executant la configuració de l\'entorn… + Autenticar General Tema fosc Habilitar el tema fosc Directori de baixades - Els arxius es desaràn a %1$s + Els arxius es desaran a %1$s Netejar memòria cau del repositori Neteja l\'informació en memòria cau per als repositoris en línia, força a l\'aplicació a actualitzar-se en línia. Amagar Magisk Manager @@ -139,7 +140,7 @@ Personalitzat Inserta una URL personalitzada Mode nucli de Magisk - Habilitar només les funcions principals, no es carregaran tots els mòduls. MagiskSU y MagiskHide seguirán habilitats + Habilitar només les funcions principals, no es carregaran tots els mòduls. MagiskSU y MagiskHide seguiran habilitats Amagar Magisk de varies deteccions Systemless Hosts Suport per aplicacions tipus Adblock fora de la partició del sistema @@ -148,7 +149,7 @@ Escriu el nom desitjat per l\'App Nou nom Es refarà l\'App amb aquest nom - Format invàl·lid + Format invàlid Aplicacions y ADB Només aplicacions Només ADB @@ -166,6 +167,9 @@ %1$d segons Demanar després d\'una actualització Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada + Activar autenticació biomètrica + Utilitza l\'autenticació biomètrica per permetre solicituds de superusuari + El dispositiu no suporta o no té establerta configuració biomètrica Mode Multiusuari Només Administrador del Dispositiu @@ -182,7 +186,7 @@ Totes les sessions d\'arrel utilitzen el suport Namespace Global Les sessions d\'arrel heretaran les peticiones Namespace Totes les sessions d\'arrel tindran la seva pròpia Namespace - Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu. + Error al crear la carpeta. El directori ha de ser accessible des de el directori arrel i no pot ser un arxiu. Petició de superusuari @@ -216,6 +220,6 @@ Ordre: %1$s - Mostra apps del sistema + Mostra Apps del sistema From 06c42d05c38a395a17147204ac721301adeb7c90 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 15 Dec 2019 21:01:12 -0500 Subject: [PATCH 20/47] Drop image based Magisk support --- scripts/module_installer.sh | 40 +++++++++---------------------------- scripts/util_functions.sh | 17 ---------------- 2 files changed, 9 insertions(+), 48 deletions(-) diff --git a/scripts/module_installer.sh b/scripts/module_installer.sh index 112eb2823..6dbe11d30 100644 --- a/scripts/module_installer.sh +++ b/scripts/module_installer.sh @@ -14,17 +14,12 @@ mkdir -p $TMPDIR ui_print() { echo "$1"; } require_new_magisk() { - ui_print "***********************************" - ui_print " Please install the latest Magisk! " - ui_print "***********************************" + ui_print "*******************************" + ui_print " Please install Magisk v19.0+! " + ui_print "*******************************" exit 1 } -imageless_magisk() { - [ $MAGISK_VER_CODE -gt 18100 ] - return $? -} - ########################################################################################## # Environment ########################################################################################## @@ -38,6 +33,7 @@ mount /data 2>/dev/null if [ -f /data/adb/magisk/util_functions.sh ]; then . /data/adb/magisk/util_functions.sh NVBASE=/data/adb + [ $MAGISK_VER_CODE -gt 18100 ] || require_new_magisk else require_new_magisk fi @@ -65,16 +61,8 @@ unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR > # Load install script . $TMPDIR/install.sh -if imageless_magisk; then - $BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules - MODULEROOT=$NVBASE/$MODDIRNAME -else - $BOOTMODE && IMGNAME=magisk_merge.img || IMGNAME=magisk.img - IMG=$NVBASE/$IMGNAME - request_zip_size_check "$ZIPFILE" - mount_magisk_img - MODULEROOT=$MOUNTPATH -fi +$BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules +MODULEROOT=$NVBASE/$MODDIRNAME MODID=`grep_prop id $TMPDIR/module.prop` MODPATH=$MODULEROOT/$MODID @@ -102,11 +90,7 @@ rm -f $MODPATH/system/placeholder 2>/dev/null [ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh # Auto Mount -if imageless_magisk; then - $SKIPMOUNT && touch $MODPATH/skip_mount -else - $SKIPMOUNT || touch $MODPATH/auto_mount -fi +$SKIPMOUNT && touch $MODPATH/skip_mount # prop files $PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop @@ -115,13 +99,8 @@ $PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop cp -af $TMPDIR/module.prop $MODPATH/module.prop if $BOOTMODE; then # Update info for Magisk Manager - if imageless_magisk; then - mktouch $NVBASE/modules/$MODID/update - cp -af $TMPDIR/module.prop $NVBASE/modules/$MODID/module.prop - else - mktouch /sbin/.magisk/img/$MODID/update - cp -af $TMPDIR/module.prop /sbin/.magisk/img/$MODID/module.prop - fi + mktouch $NVBASE/modules/$MODID/update + cp -af $TMPDIR/module.prop $NVBASE/modules/$MODID/module.prop fi # post-fs-data mode scripts @@ -143,7 +122,6 @@ set_permissions ########################################################################################## cd / -imageless_magisk || unmount_magisk_img $BOOTMODE || recovery_cleanup rm -rf $TMPDIR $MOUNTPATH diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index 25e5fdfe3..e9a5fdb56 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -446,23 +446,6 @@ request_zip_size_check() { reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'` } -################################## -# Backwards Compatibile Functions -################################## - -get_outfd() { setup_flashable; } - -mount_magisk_img() { - $BOOTMODE && MODULE_BASE=modules_update || MODULE_BASE=modules - MODULEPATH=$NVBASE/$MODULE_BASE - mkdir -p $MODULEPATH 2>/dev/null - ln -s $MODULEPATH $MOUNTPATH -} - -unmount_magisk_img() { - rm -f $MOUNTPATH 2>/dev/null -} - boot_actions() { return; } ######## From 695c8bc5d0a4b3d22938e3661f54b9abc8b1b396 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 17 Dec 2019 16:38:12 -0500 Subject: [PATCH 21/47] Detect package name for copying binaries Close #2152 --- native/jni/core/bootstages.cpp | 22 +++++++++++----------- native/jni/core/db.cpp | 27 ++++++++++++++++++++------- native/jni/include/db.h | 3 ++- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 6c1ba2175..c0424a2ea 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -317,13 +317,15 @@ static int bind_mount(const char *from, const char *to, bool log) { static bool magisk_env() { LOGI("* Initializing Magisk environment\n"); + string pkg; + check_manager(&pkg); + + char install_dir[128]; + sprintf(install_dir, "%s/0/%s/install", APP_DATA_DIR, pkg.data()); + // Alternative binaries paths - constexpr const char *alt_bin[] = { - "/cache/data_adb/magisk", "/data/magisk", - "/data/data/com.topjohnwu.magisk/install", - "/data/user_de/0/com.topjohnwu.magisk/install" - }; - for (auto &alt : alt_bin) { + const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", install_dir }; + for (auto alt : alt_bin) { struct stat st; if (lstat(alt, &st) != -1) { if (S_ISLNK(st.st_mode)) { @@ -781,11 +783,9 @@ void boot_complete(int client) { rename(MANAGERAPK, "/data/magisk.apk"); install_apk("/data/magisk.apk"); } else { - // Check whether we have a valid manager installed - db_strings str; - get_db_strings(str, SU_MANAGER); - if (validate_manager(str[SU_MANAGER], 0, nullptr)) { - // There is no manager installed, install the stub + // Check whether we have manager installed + if (!check_manager()) { + // Install stub exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk"); install_apk("/data/magisk.apk"); } diff --git a/native/jni/core/db.cpp b/native/jni/core/db.cpp index c644e0b7e..910362d40 100644 --- a/native/jni/core/db.cpp +++ b/native/jni/core/db.cpp @@ -246,28 +246,41 @@ int get_uid_policy(su_access &su, int uid) { return 0; } -int validate_manager(string &alt_pkg, int userid, struct stat *st) { +bool check_manager(string *pkg) { + db_strings str; + get_db_strings(str, SU_MANAGER); + bool ret = validate_manager(str[SU_MANAGER], 0, nullptr); + if (pkg) { + if (ret) + pkg->swap(str[SU_MANAGER]); + else + *pkg = "xxx"; /* Make sure the return pkg can never exist */ + } + return ret; +} + +bool validate_manager(string &pkg, int userid, struct stat *st) { struct stat tmp_st; if (st == nullptr) st = &tmp_st; // Prefer DE storage char app_path[128]; - sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, alt_pkg.empty() ? "xxx" : alt_pkg.data()); - if (stat(app_path, st)) { + sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, pkg.data()); + if (pkg.empty() || stat(app_path, st)) { // Check the official package name sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, userid); if (stat(app_path, st)) { LOGE("su: cannot find manager"); memset(st, 0, sizeof(*st)); - alt_pkg.clear(); - return 1; + pkg.clear(); + return false; } else { // Switch to official package if exists - alt_pkg = JAVA_PACKAGE_NAME; + pkg = JAVA_PACKAGE_NAME; } } - return 0; + return true; } void exec_sql(int client) { diff --git a/native/jni/include/db.h b/native/jni/include/db.h index 7b5726e0a..29614a249 100644 --- a/native/jni/include/db.h +++ b/native/jni/include/db.h @@ -155,7 +155,8 @@ typedef std::function db_row_cb; int get_db_settings(db_settings &cfg, int key = -1); int get_db_strings(db_strings &str, int key = -1); int get_uid_policy(su_access &su, int uid); -int validate_manager(std::string &alt_pkg, int userid, struct stat *st); +bool check_manager(std::string *pkg = nullptr); +bool validate_manager(std::string &pkg, int userid, struct stat *st); void exec_sql(int client); char *db_exec(const char *sql); char *db_exec(const char *sql, const db_row_cb &fn); From 7668e458904d9cbac591e5a213c749f38b5ea6e6 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 17 Dec 2019 17:15:31 -0500 Subject: [PATCH 22/47] Cleanup legacy code --- native/jni/core/bootstages.cpp | 5 ----- native/jni/core/scripting.cpp | 27 --------------------------- native/jni/include/daemon.h | 1 - 3 files changed, 33 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index c0424a2ea..71a37ba45 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -413,11 +413,6 @@ static bool magisk_env() { } static void prepare_modules() { - const char *legacy_imgs[] = {SECURE_DIR "/magisk.img", SECURE_DIR "/magisk_merge.img"}; - for (auto img : legacy_imgs) { - if (access(img, F_OK) == 0) - migrate_img(img); - } DIR *dir; struct dirent *entry; if ((dir = opendir(MODULEUPGRADE))) { diff --git a/native/jni/core/scripting.cpp b/native/jni/core/scripting.cpp index 070e9c3d2..5e4899847 100644 --- a/native/jni/core/scripting.cpp +++ b/native/jni/core/scripting.cpp @@ -72,33 +72,6 @@ void exec_module_script(const char *stage, const vector &module_list) { } } -constexpr char migrate_script[] = -"MODULEROOT=" MODULEROOT R"EOF( -IMG=%s -MNT=/dev/img_mnt -e2fsck -yf $IMG -mkdir -p $MNT -for num in 0 1 2 3 4 5 6 7; do - losetup /dev/block/loop${num} $IMG || continue - mount -t ext4 /dev/block/loop${num} $MNT - rm -rf $MNT/lost+found $MNT/.core - magisk --clone $MNT $MODULEROOT - umount $MNT - rm -rf $MNT - losetup -d /dev/block/loop${num} - break -done -rm -rf $IMG -)EOF"; - -void migrate_img(const char *img) { - LOGI("* Migrating %s\n", img); - exec_t exec { .pre_exec = set_path }; - char cmds[sizeof(migrate_script) + 128]; - sprintf(cmds, migrate_script, img); - exec_command_sync(exec, "/system/bin/sh", "-c", cmds); -} - constexpr char install_script[] = R"EOF( APK=%s log -t Magisk "apk_install: $APK" diff --git a/native/jni/include/daemon.h b/native/jni/include/daemon.h index 71ac92b9b..c346a809e 100644 --- a/native/jni/include/daemon.h +++ b/native/jni/include/daemon.h @@ -68,7 +68,6 @@ void remove_modules(); void exec_script(const char *script); void exec_common_script(const char *stage); void exec_module_script(const char *stage, const std::vector &module_list); -void migrate_img(const char *img); void install_apk(const char *apk); /************** From 3b9f7885e0e2c4a334848bb1a4b461d1f6fd7cdf Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 21 Dec 2019 05:29:38 -0500 Subject: [PATCH 23/47] Stop using chdir --- native/jni/core/bootstages.cpp | 38 +++++++++++++++++----------------- native/jni/core/scripting.cpp | 15 +++++++------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 71a37ba45..99831fafd 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -451,47 +451,47 @@ static void reboot() { void remove_modules() { LOGI("* Remove all modules and reboot"); - chdir(MODULEROOT); - rm_rf("lost+found"); - auto dir = xopen_dir("."); + auto dir = xopen_dir(MODULEROOT); + int dfd = dirfd(dir.get()); for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_DIR) { if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv) continue; - chdir(entry->d_name); - close(creat("remove", 0644)); - chdir(".."); + + int modfd = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC); + close(xopenat(modfd, "remove", O_RDONLY | O_CREAT | O_CLOEXEC)); + close(modfd); } } - chdir("/"); reboot(); } static void collect_modules() { - chdir(MODULEROOT); - rm_rf("lost+found"); - auto dir = xopen_dir("."); + auto dir = xopen_dir(MODULEROOT); + int dfd = dirfd(dir.get()); for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_DIR) { if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv) continue; - chdir(entry->d_name); - if (access("remove", F_OK) == 0) { - chdir(".."); + + int modfd = xopenat(dfd, entry->d_name, O_RDONLY); + run_finally f([=]{ close(modfd); }); + + if (faccessat(modfd, "remove", F_OK, 0) == 0) { LOGI("%s: remove\n", entry->d_name); - sprintf(buf, "%s/uninstall.sh", entry->d_name); + fd_pathat(modfd, "uninstall.sh", buf, sizeof(buf)); if (access(buf, F_OK) == 0) exec_script(buf); - rm_rf(entry->d_name); + frm_rf(modfd); continue; } - unlink("update"); - if (access("disable", F_OK)) + + unlinkat(modfd, "update", 0); + + if (faccessat(modfd, "disable", F_OK, 0) != 0) module_list.emplace_back(entry->d_name); - chdir(".."); } } - chdir("/"); } static bool load_modules(node_entry *root) { diff --git a/native/jni/core/scripting.cpp b/native/jni/core/scripting.cpp index 5e4899847..3f4888133 100644 --- a/native/jni/core/scripting.cpp +++ b/native/jni/core/scripting.cpp @@ -26,30 +26,31 @@ void exec_script(const char *script) { void exec_common_script(const char *stage) { char path[4096]; - sprintf(path, SECURE_DIR "/%s.d", stage); + char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage); auto dir = xopen_dir(path); if (!dir) return; - chdir(path); + int dfd = dirfd(dir.get()); bool pfs = stage == "post-fs-data"sv; + *(name++) = '/'; + for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_REG) { - if (access(entry->d_name, X_OK) == -1) + if (faccessat(dfd, entry->d_name, X_OK, 0) != 0) continue; LOGI("%s.d: exec [%s]\n", stage, entry->d_name); + strcpy(name, entry->d_name); exec_t exec { .pre_exec = set_path, .fork = pfs ? fork_no_zombie : fork_dont_care }; if (pfs) - exec_command_sync(exec, "/system/bin/sh", entry->d_name); + exec_command_sync(exec, "/system/bin/sh", path); else - exec_command(exec, "/system/bin/sh", entry->d_name); + exec_command(exec, "/system/bin/sh", path); } } - - chdir("/"); } void exec_module_script(const char *stage, const vector &module_list) { From 02761f5f355b0564523892fa1720e6ba1c9e2278 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 21 Dec 2019 06:01:18 -0500 Subject: [PATCH 24/47] Fix French resources Close #2169 --- app/src/main/res/values-fr/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 00f7c041d..0049d56a0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -8,7 +8,7 @@ Paramètres Installer Version de Magisk non prise en charge - Cette version de Magisk Manager ne supporte pas de version de Magisk inférieure à la %1$s.\n\nL\'application se comportera comme si Magisk n'était pas installé, veuillez mettre à jour Magisk dès que possible. + Cette version de Magisk Manager ne supporte pas de version de Magisk inférieure à la %1$s.\n\nL\'application se comportera comme si Magisk n\'était pas installé, veuillez mettre à jour Magisk dès que possible. Magisk n’est pas installé. @@ -29,7 +29,7 @@ Dernière version disponible : %1$s Désinstaller Désinstaller Magisk - Tous les modules seront désactivés/supprimés!\nL'enracinement sera retiré!\nVos données seront potentiellement chiffrées si elles ne le sont pas déjà. + Tous les modules seront désactivés/supprimés!\nL\'enracinement sera retiré!\nVos données seront potentiellement chiffrées si elles ne le sont pas déjà. Mise à jour (Mode « sans modules » activé) @@ -146,9 +146,9 @@ Prise en charge du fichier hosts sans système pour les applications de type AdBlock. Ajout d’un module hosts sans système - Taper le nom de l'application désirée + Taper le nom de l\'application désirée Nouveau nom - L'application sera réempacter sous ce nom + L\'application sera réempacter sous ce nom Format invalide Applications et ADB Applications uniquement @@ -167,8 +167,8 @@ %1$d secondes Authentifier à nouveau après la mise à niveau Authentifier à nouveau les autorisations super‐utilisateur après une mise à jour de l’application - Activer l'authentification Biométrique - Utiliser l'authentification biométrique pour autoriser les accès super-utilisateur + Activer l\'authentification Biométrique + Utiliser l\'authentification biométrique pour autoriser les accès super-utilisateur Périphérique non supporté ou les réglages biométriques ne sont pas actifs Mode multi‐utilisateur From 3beffd84d654342b5a68900d2677972f539ddfb0 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 22 Dec 2019 03:44:07 -0500 Subject: [PATCH 25/47] Copy sepolicy rules to persist every boot --- native/jni/core/bootstages.cpp | 54 +++++++++++++++------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 99831fafd..4533b9f20 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -413,10 +413,9 @@ static bool magisk_env() { } static void prepare_modules() { - DIR *dir; - struct dirent *entry; - if ((dir = opendir(MODULEUPGRADE))) { - while ((entry = xreaddir(dir))) { + // Upgrade modules + if (auto dir = xopen_dir(MODULEUPGRADE); dir) { + for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_DIR) { if (entry->d_name == "."sv || entry->d_name == ".."sv) continue; @@ -429,7 +428,6 @@ static void prepare_modules() { rename(buf2, buf); } } - closedir(dir); rm_rf(MODULEUPGRADE); } bind_mount(MIRRDIR MODULEROOT, MODULEMNT, false); @@ -499,36 +497,47 @@ static bool load_modules(node_entry *root) { bool has_modules = false; for (const auto &m : module_list) { - const auto module = m.c_str(); + const auto module = m.data(); + char *name = buf + snprintf(buf, sizeof(buf), MODULEROOT "/%s/", module); + // Read props - snprintf(buf, PATH_MAX, "%s/%s/system.prop", MODULEROOT, module); + strcpy(name, "system.prop"); if (access(buf, F_OK) == 0) { LOGI("%s: loading [system.prop]\n", module); load_prop_file(buf, false); } + // Copy sepolicy rules + strcpy(name, "sepolicy.rule"); + if (access(MIRRDIR "/persist", F_OK) == 0 && access(buf, F_OK) == 0) { + char *p = buf2 + snprintf(buf2, sizeof(buf2), MIRRDIR "/persist/magisk/%s", module); + xmkdirs(buf2, 0755); + strcpy(p, "/sepolicy.rule"); + cp_afc(buf, buf2); + } + // Check whether skip mounting - snprintf(buf, PATH_MAX, "%s/%s/skip_mount", MODULEROOT, module); + strcpy(name, "skip_mount"); if (access(buf, F_OK) == 0) continue; // Double check whether the system folder exists - snprintf(buf, PATH_MAX, "%s/%s/system", MODULEROOT, module); - if (access(buf, F_OK) == -1) + strcpy(name, "system"); + if (access(buf, F_OK) != 0) continue; // Construct structure has_modules = true; LOGI("%s: constructing magic mount structure\n", module); // If /system/vendor exists in module, create a link outside - snprintf(buf, PATH_MAX, "%s/%s/system/vendor", MODULEROOT, module); + strcpy(name, "system/vendor"); if (node_entry::vendor_root && access(buf, F_OK) == 0) { - snprintf(buf2, PATH_MAX, "%s/%s/vendor", MODULEROOT, module); + snprintf(buf2, sizeof(buf2), "%s/%s/vendor", MODULEROOT, module); unlink(buf2); xsymlink("./system/vendor", buf2); } // If /system/product exists in module, create a link outside - snprintf(buf, PATH_MAX, "%s/%s/system/product", MODULEROOT, module); + strcpy(name, "system/product"); if (node_entry::product_root && access(buf, F_OK) == 0) { - snprintf(buf2, PATH_MAX, "%s/%s/product", MODULEROOT, module); + snprintf(buf2, sizeof(buf2), "%s/%s/product", MODULEROOT, module); unlink(buf2); xsymlink("./system/product", buf2); } @@ -651,22 +660,6 @@ void post_fs_data(int client) { unblock_boot_process(); } -#if 0 - // Increment boot count - int boot_count = 0; - FILE *cf = fopen(BOOTCOUNT, "r"); - if (cf) { - fscanf(cf, "%d", &boot_count); - fclose(cf); - } - boot_count++; - if (boot_count > 2) - creat(DISABLEFILE, 0644); - cf = xfopen(BOOTCOUNT, "w"); - fprintf(cf, "%d", boot_count); - fclose(cf); -#endif - if (!magisk_env()) { LOGE("* Magisk environment setup incomplete, abort\n"); unblock_boot_process(); @@ -747,7 +740,6 @@ void late_start(int client) { auto_start_magiskhide(); - // Run scripts after full patch, most reliable way to run scripts LOGI("* Running service.d scripts\n"); exec_common_script("service"); From b336655a79f3717f91dfdf57345c96f78e012e31 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 27 Dec 2019 17:53:27 +0800 Subject: [PATCH 26/47] Brand new module installer script The new module installer script completely changes the way how module installer zips are structured. More info will come later in docs. The new installer script also supports installing sepolicy.rule to persist partitions in order to make the module work on the next boot. --- scripts/module_installer.sh | 157 +++++++++++++++++++++++------------- scripts/util_functions.sh | 92 ++++++++++++--------- 2 files changed, 154 insertions(+), 95 deletions(-) diff --git a/scripts/module_installer.sh b/scripts/module_installer.sh index 6dbe11d30..bb41d59bc 100644 --- a/scripts/module_installer.sh +++ b/scripts/module_installer.sh @@ -1,12 +1,15 @@ #!/sbin/sh -TMPDIR=/dev/tmp -MOUNTPATH=/dev/magisk_img +################# +# Initialization +################# -# Default permissions umask 022 -# Initial cleanup +# Global vars +TMPDIR=/dev/tmp +PERSISTDIR=/sbin/.magisk/mirror/persist + rm -rf $TMPDIR 2>/dev/null mkdir -p $TMPDIR @@ -20,9 +23,27 @@ require_new_magisk() { exit 1 } -########################################################################################## +is_legacy_script() { + unzip -l "$ZIPFILE" install.sh | grep -q install.sh + return $? +} + +print_modname() { + local len + len=`echo -n $MODNAME | wc -c` + len=$((len + 2)) + local pounds=`printf "%${len}s" | tr ' ' '*'` + ui_print "$pounds" + ui_print " $MODNAME " + ui_print "$pounds" + ui_print "*******************" + ui_print " Powered by Magisk " + ui_print "*******************" +} + +############## # Environment -########################################################################################## +############## OUTFD=$2 ZIPFILE=$3 @@ -30,13 +51,9 @@ ZIPFILE=$3 mount /data 2>/dev/null # Load utility functions -if [ -f /data/adb/magisk/util_functions.sh ]; then - . /data/adb/magisk/util_functions.sh - NVBASE=/data/adb - [ $MAGISK_VER_CODE -gt 18100 ] || require_new_magisk -else - require_new_magisk -fi +[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk +. /data/adb/magisk/util_functions.sh +[ $MAGISK_VER_CODE -gt 18100 ] || require_new_magisk # Preperation for flashable zips setup_flashable @@ -50,80 +67,104 @@ api_level_arch_detect # Setup busybox and binaries $BOOTMODE && boot_actions || recovery_actions -########################################################################################## +############## # Preparation -########################################################################################## +############## -# Extract common files -unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2 - -[ ! -f $TMPDIR/install.sh ] && abort "! Unable to extract zip file!" -# Load install script -. $TMPDIR/install.sh +# Extract prop file +unzip -o "$ZIPFILE" module.prop -d $TMPDIR >&2 +[ ! -f $TMPDIR/module.prop ] && abort "! Unable to extract zip file!" $BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules MODULEROOT=$NVBASE/$MODDIRNAME - MODID=`grep_prop id $TMPDIR/module.prop` MODPATH=$MODULEROOT/$MODID - -print_modname - -ui_print "******************************" -ui_print "Powered by Magisk (@topjohnwu)" -ui_print "******************************" - -########################################################################################## -# Install -########################################################################################## +MODNAME=`grep_prop name $TMPDIR/module.prop` # Create mod paths rm -rf $MODPATH 2>/dev/null mkdir -p $MODPATH -on_install +########## +# Install +########## -# Remove placeholder -rm -f $MODPATH/system/placeholder 2>/dev/null +if is_legacy_script; then + unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2 -# Custom uninstaller -[ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh + # Load install script + . $TMPDIR/install.sh -# Auto Mount -$SKIPMOUNT && touch $MODPATH/skip_mount + # Callbacks + print_modname + on_install -# prop files -$PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop + # Custom uninstaller + [ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh -# Module info -cp -af $TMPDIR/module.prop $MODPATH/module.prop -if $BOOTMODE; then - # Update info for Magisk Manager - mktouch $NVBASE/modules/$MODID/update - cp -af $TMPDIR/module.prop $NVBASE/modules/$MODID/module.prop + # Skip mount + $SKIPMOUNT && touch $MODPATH/skip_mount + + # prop file + $PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop + + # Module info + cp -af $TMPDIR/module.prop $MODPATH/module.prop + + # post-fs-data scripts + $POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh + + # service scripts + $LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh + + ui_print "- Setting permissions" + set_permissions +else + print_modname + + ui_print "- Extracting module files" + unzip -o "$ZIPFILE" -d $MODPATH >&2 + + # Default permissions + set_perm_recursive $MODPATH 0 0 0755 0644 + + # Load customization script + [ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh fi -# post-fs-data mode scripts -$POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh - -# service mode scripts -$LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh - # Handle replace folders for TARGET in $REPLACE; do + ui_print "- Replace target: $TARGET" mktouch $MODPATH$TARGET/.replace done -ui_print "- Setting permissions" -set_permissions +if $BOOTMODE; then + # Update info for Magisk Manager + mktouch $NVBASE/modules/$MODID/update + cp -af $MODPATH/module.prop $NVBASE/modules/$MODID/module.prop +fi -########################################################################################## +# Copy over custom sepolicy rules +if [ -f $MODPATH/sepolicy.rule -a -e $PERSISTDIR ]; then + ui_print "- Installing custom sepolicy patch" + PERSISTMOD=$PERSISTDIR/magisk/$MODID + mkdir -p $PERSISTMOD + cp -af $MODPATH/sepolicy.rule $PERSISTMOD/sepolicy.rule +fi + +# Remove stuffs that don't belong to modules +rm -rf \ +$MODPATH/system/placeholder $MODPATH/customize.sh \ +$MODPATH/META-INF $MODPATH/README.md $MODPATH/.git* \ +2>/dev/null + +############## # Finalizing -########################################################################################## +############## cd / $BOOTMODE || recovery_cleanup -rm -rf $TMPDIR $MOUNTPATH +rm -rf $TMPDIR ui_print "- Done" exit 0 diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index e9a5fdb56..dd7e5610c 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -5,27 +5,8 @@ # ######################################### -########## -# Presets -########## - #MAGISK_VERSION_STUB -# Detect whether in boot mode -[ -z $BOOTMODE ] && BOOTMODE=false -$BOOTMODE || ps | grep zygote | grep -qv grep && BOOTMODE=true -$BOOTMODE || ps -A 2>/dev/null | grep zygote | grep -qv grep && BOOTMODE=true - -# Presets -MAGISKTMP=/sbin/.magisk -NVBASE=/data/adb -[ -z $TMPDIR ] && TMPDIR=/dev/tmp - -# Bootsigner related stuff -BOOTSIGNERCLASS=a.a -BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK \$BOOTSIGNERCLASS" -BOOTSIGNED=false - ################### # Helper Functions ################### @@ -128,15 +109,15 @@ recovery_actions() { } recovery_cleanup() { - export PATH=$OLD_PATH - [ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB - [ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE - [ -z $OLD_LD_CFG ] || export LD_CONFIG_FILE=$OLD_LD_CFG ui_print "- Unmounting partitions" umount -l /system_root 2>/dev/null umount -l /system 2>/dev/null umount -l /vendor 2>/dev/null umount -l /dev/random 2>/dev/null + export PATH=$OLD_PATH + [ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB + [ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE + [ -z $OLD_LD_CFG ] || export LD_CONFIG_FILE=$OLD_LD_CFG } ####################### @@ -165,19 +146,29 @@ find_block() { return 1 } -mount_part() { - $BOOTMODE && return +# mount_name +mount_name() { local PART=$1 - local POINT=/${PART} + local POINT=$2 + local FLAG=$3 [ -L $POINT ] && rm -f $POINT - mkdir $POINT 2>/dev/null + mkdir -p $POINT 2>/dev/null is_mounted $POINT && return - ui_print "- Mounting $PART" - mount -o ro $POINT 2>/dev/null + ui_print "- Mounting $POINT" + # First try mounting with fstab + mount $FLAG $POINT 2>/dev/null if ! is_mounted $POINT; then - local BLOCK=`find_block $PART$SLOT` - mount -o ro $BLOCK $POINT + local BLOCK=`find_block $PART` + mount $FLAG $BLOCK $POINT fi +} + +mount_ro_ensure() { + # We handle ro partitions only in recovery + $BOOTMODE && return + local PART=$1$SLOT + local POINT=/$1 + mount_name $PART $POINT '-o ro' is_mounted $POINT || abort "! Cannot mount $POINT" } @@ -190,7 +181,8 @@ mount_partitions() { fi [ -z $SLOT ] || ui_print "- Current boot slot: $SLOT" - mount_part system + # Mount ro partitions + mount_ro_ensure system if [ -f /system/init.rc ]; then SYSTEM_ROOT=true [ -L /system_root ] && rm -f /system_root @@ -201,8 +193,20 @@ mount_partitions() { grep ' / ' /proc/mounts | grep -qv 'rootfs' || grep -q ' /system_root ' /proc/mounts \ && SYSTEM_ROOT=true || SYSTEM_ROOT=false fi - [ -L /system/vendor ] && mount_part vendor + [ -L /system/vendor ] && mount_ro_ensure vendor $SYSTEM_ROOT && ui_print "- Device is system-as-root" + + # Persist partitions for module install in recovery + if ! $BOOTMODE && [ ! -z $PERSISTDIR ]; then + # Try to mount persist + PERSISTDIR=/persist + mount_name persist /persist + if ! is_mounted /persist; then + # Fallback to cache + mount_name cache /cache + is_mounted /cache && PERSISTDIR=/cache || PERSISTDIR= + fi + fi } get_flags() { @@ -293,8 +297,8 @@ patch_dtbo_image() { return 1 } +# Common installation script for flash_script.sh and addon.d.sh patch_boot_image() { - # Common installation script for flash_script.sh (updater-script) and addon.d.sh SOURCEDMODE=true cd $MAGISKBIN @@ -448,8 +452,22 @@ request_zip_size_check() { boot_actions() { return; } -######## -# Setup -######## +########## +# Presets +########## + +# Detect whether in boot mode +[ -z $BOOTMODE ] && ps | grep zygote | grep -qv grep && BOOTMODE=true +[ -z $BOOTMODE ] && ps -A 2>/dev/null | grep zygote | grep -qv grep && BOOTMODE=true +[ -z $BOOTMODE ] && BOOTMODE=false + +MAGISKTMP=/sbin/.magisk +NVBASE=/data/adb +[ -z $TMPDIR ] && TMPDIR=/dev/tmp + +# Bootsigner related stuff +BOOTSIGNERCLASS=a.a +BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK \$BOOTSIGNERCLASS" +BOOTSIGNED=false resolve_vars From 33d62d7f2110a673cc8b72344a08243ff98be648 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 27 Dec 2019 19:03:45 +0800 Subject: [PATCH 27/47] Handle sepolicy.rule when disable/remove in app --- .../topjohnwu/magisk/model/entity/module/Module.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt index 8017dea4d..1da190682 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt @@ -13,17 +13,21 @@ class Module(path: String) : BaseModule() { override var versionCode: Int = -1 override var description: String = "" - private val removeFile: SuFile = SuFile(path, "remove") - private val disableFile: SuFile = SuFile(path, "disable") - private val updateFile: SuFile = SuFile(path, "update") + private val removeFile = SuFile(path, "remove") + private val disableFile = SuFile(path, "disable") + private val updateFile = SuFile(path, "update") + private val ruleFile = SuFile(path, "sepolicy.rule") val updated: Boolean = updateFile.exists() var enable: Boolean = !disableFile.exists() set(enable) { + val dir = "$PERSIST/$id" field = if (enable) { + Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit() disableFile.delete() } else { + Shell.su("rm -rf $dir").submit() !disableFile.createNewFile() } } @@ -31,8 +35,10 @@ class Module(path: String) : BaseModule() { var remove: Boolean = removeFile.exists() set(remove) { field = if (remove) { + Shell.su("rm -rf $PERSIST/$id").submit() removeFile.createNewFile() } else { + Shell.su("cp -af $ruleFile $PERSIST/$id").submit() !removeFile.delete() } } @@ -54,6 +60,8 @@ class Module(path: String) : BaseModule() { companion object { + private const val PERSIST = "/sbin/.magisk/mirror/persist/magisk" + @WorkerThread fun loadModules(): List { val moduleList = mutableListOf() From 7fa2625a03115e5b314c96a6cbf3f90274069889 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 27 Dec 2019 20:37:33 +0800 Subject: [PATCH 28/47] Fix strings --- app/src/main/res/values-ca/strings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index f35f994b5..a34923690 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -8,7 +8,6 @@ Configuració Instal·lar Versió de Magisk incompatible - Aquesta versió de Magisk Manager no suporta una versió inferior a la 18.0.\n\nPots actualitzar Magisk manualment o pots instal·lar una versió anterior de la App. Magisk no està instal·lat From 025b0605069015a492b94c011bf606328cd80449 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Dec 2019 02:33:35 +0800 Subject: [PATCH 29/47] Exclude META-INF in unzip --- scripts/module_installer.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/module_installer.sh b/scripts/module_installer.sh index bb41d59bc..8b9af0ee2 100644 --- a/scripts/module_installer.sh +++ b/scripts/module_installer.sh @@ -123,7 +123,7 @@ else print_modname ui_print "- Extracting module files" - unzip -o "$ZIPFILE" -d $MODPATH >&2 + unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2 # Default permissions set_perm_recursive $MODPATH 0 0 0755 0644 @@ -155,8 +155,7 @@ fi # Remove stuffs that don't belong to modules rm -rf \ $MODPATH/system/placeholder $MODPATH/customize.sh \ -$MODPATH/META-INF $MODPATH/README.md $MODPATH/.git* \ -2>/dev/null +$MODPATH/README.md $MODPATH/.git* 2>/dev/null ############## # Finalizing From d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4 Mon Sep 17 00:00:00 2001 From: Zackptg5 <5107713+Zackptg5@users.noreply.github.com> Date: Sat, 28 Dec 2019 00:47:05 -0500 Subject: [PATCH 30/47] Ignore twrp fstabs --- native/jni/magiskboot/ramdisk.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/native/jni/magiskboot/ramdisk.cpp b/native/jni/magiskboot/ramdisk.cpp index 67a65e7da..c6811abd9 100644 --- a/native/jni/magiskboot/ramdisk.cpp +++ b/native/jni/magiskboot/ramdisk.cpp @@ -47,8 +47,11 @@ void magisk_cpio::patch() { for (auto it = entries.begin(); it != entries.end();) { auto cur = it++; bool fstab = (!keepverity || !keepforceencrypt) && - !str_starts(cur->first, ".backup") && - str_contains(cur->first, "fstab") && S_ISREG(cur->second->mode); + S_ISREG(cur->second->mode) && + !str_starts(cur->first, ".backup") && + !str_contains(cur->first, "twrp") && + !str_contains(cur->first, "recovery") && + str_contains(cur->first, "fstab"); if (!keepverity) { if (fstab) { fprintf(stderr, "Found fstab file [%s]\n", cur->first.data()); From b2cb2b8b75b8a6a2ebe976ee1bb263209b987604 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Dec 2019 21:27:55 +0800 Subject: [PATCH 31/47] Reduce socket name length Some detectors simply ban long abstract sockets --- native/jni/init/rootdir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index bc323e5ee..af4f59f38 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -24,7 +24,7 @@ static void patch_socket_name(const char *path) { mmap_rw(path, buf, size); for (int i = 0; i < size; ++i) { if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) { - gen_rand_str(buf + i, sizeof(MAIN_SOCKET)); + gen_rand_str(buf + i, 16); i += sizeof(MAIN_SOCKET); } } From 7f667fed1826712fcdcce698fd3a55d90c540ddd Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 29 Dec 2019 00:45:49 +0800 Subject: [PATCH 32/47] Allow customize.sh to skip unzip Close #2184 --- scripts/module_installer.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/module_installer.sh b/scripts/module_installer.sh index 8b9af0ee2..5a6b15f94 100644 --- a/scripts/module_installer.sh +++ b/scripts/module_installer.sh @@ -122,11 +122,15 @@ if is_legacy_script; then else print_modname - ui_print "- Extracting module files" - unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2 + unzip -o "$ZIPFILE" customize.sh -d $MODPATH >&2 - # Default permissions - set_perm_recursive $MODPATH 0 0 0755 0644 + if ! grep -q '^SKIPUNZIP=' $MODPATH/customize.sh 2>/dev/null; then + ui_print "- Extracting module files" + unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2 + + # Default permissions + set_perm_recursive $MODPATH 0 0 0755 0644 + fi # Load customization script [ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh From eb26e628897a1fe0a9fd3962fbd0b639fd0fdb00 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 29 Dec 2019 03:28:03 +0800 Subject: [PATCH 33/47] Update documentation --- docs/README.md | 6 +- docs/details.md | 2 +- docs/guides.md | 186 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 147 insertions(+), 47 deletions(-) diff --git a/docs/README.md b/docs/README.md index 6d1732db5..48a05ebf0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ # Magisk Documentation -(Updated on 2019.9.19) +(Updated on 2019.12.29) - [Installation](install.md) - [Tutorials](tutorials.md) @@ -8,7 +8,7 @@ The following sections are for developers -- [Magisk Details](details.md) -- [Magisk Tools](tools.md) - [Developer Guides](guides.md) +- [Magisk Tools](tools.md) +- [Internal Details](details.md) - [Deployment](deploy.md) diff --git a/docs/details.md b/docs/details.md index 5f47273f9..c8d18e1e3 100644 --- a/docs/details.md +++ b/docs/details.md @@ -1,4 +1,4 @@ -# Magisk Details +# Internal Details ## File Structure ### Paths in "sbin tmpfs overlay" One of Magisk's breakthrough designs is sbin tmpfs overlay. It is required to support system-as-root devices, and also is the key to hiding Magisk from detection. All Magisk binaries, applets, mirrors, and other trivial stuffs are all located in the `tmpfs` mounted on `/sbin`. MagiskHide can just simply unmount `/sbin` and the bind mounts to hide all modifications easily. diff --git a/docs/guides.md b/docs/guides.md index 14388fc14..9b6b6fd1b 100644 --- a/docs/guides.md +++ b/docs/guides.md @@ -1,9 +1,7 @@ # Developer Guides -Please read through [Magisk Details](details.md) before reading the following guides. If you are developing a module, pay extra attention to the [Magic Mount](details.md#magic-mount) section. - ## Magisk Modules -A Magisk module is a folder placed in `/data/adb/modules` with a structure below: +A Magisk module is a folder placed in `/data/adb/modules` with the structure below: ``` /data/adb/modules @@ -14,40 +12,38 @@ A Magisk module is a folder placed in `/data/adb/modules` with a structure below │ │ │ │ *** Module Identity *** │ │ -│   ├── module.prop <--- This files stores the metadata of the module +│   ├── module.prop <--- This file stores the metadata of the module │ │ -│ │ *** Status files *** +│ │ *** Main Contents *** │ │ -│   ├── skip_mount <--- If exists, Magisk will NOT mount your files +│   ├── system <--- This folder will be mounted if skip_mount does not exists +│   │   ├── ... +│   │   ├── ... +│   │   └── ... +│ │ +│ │ *** Status Flags *** +│ │ +│   ├── skip_mount <--- If exists, Magisk will NOT mount your system folder │   ├── disable <--- If exists, the module will be disabled │   ├── remove <--- If exists, the module will be removed next reboot │ │ -│ │ *** Scripts *** +│ │ *** Optional Files *** │ │ │   ├── post-fs-data.sh <--- This script will be executed in post-fs-data │   ├── service.sh <--- This script will be executed in late_start service | ├── uninstall.sh <--- This script will be executed when Magisk removes your module -│ │ -│ │ *** Resetprop Files *** -│ │ -│   ├── system.prop <--- This file will be loaded as system properties by resetprop -│ │ -│ │ *** Module contents *** -│ │ -│   ├── system <--- This folder will be Magic Mounted if skip_mount does not exists -│   │   ├── . -│   │   ├── . -│   │   └── . +│   ├── system.prop <--- Properties in this file will be loaded as system properties by resetprop +│   ├── sepolicy.rule <--- Additional custom sepolicy rules to be patched │   │ -│ │ *** Auto Generated by Magisk *** +│ │ *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY *** │   │ │   ├── vendor <--- A symlink to $MODID/system/vendor │   ├── product <--- A symlink to $MODID/system/product │ │ -│ │ *** Others *** +│ │ *** Any additional files / folders are allowed *** │ │ -│   ├── . <--- Any additional files / folders are allowed -│   └── . +│   ├── ... +│   └── ... | ├── another_module │   ├── . @@ -55,9 +51,11 @@ A Magisk module is a folder placed in `/data/adb/modules` with a structure below ├── . ├── . ``` -As long as a folder follows the structure above, it will be recognized as a module. -Here is the **strict** format of `module.prop`: + +#### module.prop + +This is the **strict** format of `module.prop` ``` id= @@ -71,27 +69,134 @@ description= ex: ✓ `a_module`, ✓ `a.module`, ✓ `module-101`, ✗ `a module`, ✗ `1_module`, ✗ `-a-module`
This is the **unique identifier** of your module. You should not change it once published. - `versionCode` has to be an **integer**. This is used to compare versions -- Others that isn't mentioned above can be any **single line** string. +- Others that weren't mentioned above can be any **single line** string. + +#### Shell scripts (`*.sh`) +Please read the [Boot Scripts](#boot-scripts) section to understand the difference between `post-fs-data.sh` and `service.sh`. For most module developers, `service.sh` should be good enough if you just need to run a boot script. + +In all scripts of your module, please use `MODDIR=${0%/*}` to get your module's base directory path; do **NOT** hardcode your module path in scripts. + +#### system.prop +This file follows the same format as `build.prop`. Each line comprises of `[key]=[value]`. + +#### sepolicy.rule +If your module requires some additional sepolicy patches, please add those rules into this file. The module installer script and Magisk's daemon will make sure this file is copied to somewhere `magiskinit` can read pre-init to ensure these rules are injected properly. + +Each line in this file will be treated as a policy statement. For more details how a policy statement is formated, please check [magiskpolicy](tools.md#magiskpolicy)'s documentation. + +#### The `system` folder +All files you want Magisk to replace/inject for you should be placed in this folder. Please read through the [Magic Mount](details.md#magic-mount) section to understand how Magisk mount your files. ## Magisk Module Installer -The official Magisk Module installer is hosted **[here](https://github.com/topjohnwu/magisk-module-installer)**. -That repo is a starting point for creating a Magisk module installer zip. Here are some details: +A Magisk Module Installer is a Magisk Module packaged in a zip file that can be flashed in Magisk Manager or custom recoveries such as TWRP. An installer have the same file structure as a Magisk module (please check the previous section for more info). The simplest Magisk Module Installer is just a Magisk Module packed in a zip file, with addition to the following files: -- You will found out that `META-INF/com/google/android/update-binary` is a dummy file. If you are creating a module locally for personal usage or testing, download the latest installer [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and replace `update-binary` with the script -- The next thing to do is to modify `module.prop` to provide information about your module -- Finally, modify `install.sh` to fit your need. The script is heavily documented so you should know what to do. -- (Optional) If you want to run custom uninstallation logic when your module is removed, add a new file `uninstall.sh` to the root of the installer -- **Windows users aware!!** The line endings of all text files should be in the **Unix format**. Please use advanced text editors like Sublime, Atom, Notepad++ etc. to edit **ALL** text files, **NEVER** use Windows Notepad. +- `update-binary`: Download the latest [module_installer.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and rename/copy that script as `update-binary` +- `updater-script`: This file should only contain the string `#MAGISK` + +By default, `update-binary` will check/setup the environment, load utility functions, extract the module installer zip to where your module will be installed, and finally do some trivial tasks and cleanups, which should cover most simple modules' needs. + +``` +module.zip +│ +├── META-INF +│   └── com +│      └── google +│         └── android +│            ├── update-binary <--- The module_installer.sh you downloaded +│            └── updater-script <--- Should only contain the string "#MAGISK" +│ +├── customize.sh <--- (Optional, more details later) +│ This script will be sourced by update-binary +├── ... +├── ... /* The rest of module's files */ +| +``` + +#### Customization + +If you need to customize the module installation process, optionally you can create a new script in the installer called `customize.sh`. This script will be *sourced* (not executed!) by `update-binary` after all files are extracted and default permissions and secontext are applied. This is very useful if your module includes different files based on ABI, or you need to set special permissions/secontext for some of your files (e.g. files in `/system/bin`). + +If you need even more customization and prefer to do everything on your own, declare `SKIPUNZIP=1` in `customize.sh` to skip the extraction and applying default permissions/secontext steps. Be aware that by doing so, your `customize.sh` will then be responsible to install everything by itself. + +#### `customize.sh` Environment + +Magisk's internal busybox's path `$BBPATH` is added in the front of `PATH`. The following variables and shell functions are available for convenience: + +##### Variables +- `MAGISK_VER` (string): the version string of current installed Magisk (e.g. `v20.0`) +- `MAGISK_VER_CODE` (int): the version code of current installed Magisk (e.g. `20000`) +- `BOOTMODE` (bool): `true` if the module is being installed in Magisk Manager +- `MODPATH` (path): the path where your module files should be installed +- `TMPDIR` (path): a place where you can temporarily store files +- `ZIPFILE` (path): your module's installation zip +- `ARCH` (string): the CPU architecture of the device. Value is either `arm`, `arm64`, `x86`, or `x64` +- `IS64BIT` (bool): `true` if `$ARCH` is either `arm64` or `x64` +- `API` (int): the API level (Android version) of the device (e.g. `21` for Android 5.0) + +##### Functions + +``` +ui_print + print to console + Avoid using 'echo' as it will not display in custom recovery's console + +abort + print error message to console and terminate installation + Avoid using 'exit' as it will skip the termination cleanup steps + +set_perm [context] + if [context] is not set, the default is "u:object_r:system_file:s0" + this function is a shorthand for the following commands: + chown owner.group target + chmod permission target + chcon context target + +set_perm_recursive [context] + if [context] is not set, the default is "u:object_r:system_file:s0" + for all files in , it will call: + set_perm file owner group filepermission context + for all directories in (including itself), it will call: + set_perm dir owner group dirpermission context +``` + +##### Easy Replace +You can declare a list of folders you want to directly replace in the variable name `REPLACE`. The module installer script will pickup this variable and create `.replace` files for you. An example declaration: + +``` +REPLACE=" +/system/app/YouTube +/system/app/Bloatware +" +``` +The list above will result in the following files being created: `$MODPATH/system/app/YouTube/.replace` and `$MODPATH/system/app/Bloatware/.replace` + +#### Notes + +- When your module is downloaded with Magisk Manager, `update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) to ensure all installer uses up-to-date scripts. **DO NOT** try to add any custom logic in `update-binary` as it is pointless. +- Due to historical reasons, **DO NOT** add a file named `install.sh` in your module installer. That specific file was previously used and will be treated differently. +- **DO NOT** call `exit` at the end of `customize.sh`. The module installer would want to do finalizations. ## Submit Modules -You can submit a module to **Magisk-Module-Repo** so users can download your module directly in Magisk Manager. Before submitting, follow the instructions in the previous section to create a valid installer for your module. You can then create a request for submission via this link: [submission](https://github.com/Magisk-Modules-Repo/submission). -- When your module is downloaded with Magisk Manager, `META-INF/com/google/android/update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) to make sure all installation uses the latest scripts. -- Since `update-binary` will be replaced, this means that all modules in the repo are expected to follow how the installation framework works: the installation framework will load your `install.sh` script and run the corresponding callbacks. -- This also means that you should NOT add custom logic in `update-binary` as they will simply be ignored. +You can submit a module to **Magisk-Module-Repo** so users can download your module directly in Magisk Manager. + +- Follow the instructions in the previous section to create a valid installer for your module. +- Create `README.md` (filename should be exactly the same) containing all info for your module. If you are not familiar with the Markdown syntax, the [Markdown Cheat Sheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) will be handy. +- Create a repository with your personal GitHub account, and upload your module installer to the repo +- Create a request for submission via this link: [submission](https://github.com/Magisk-Modules-Repo/submission) + +## Module Tricks + +#### Remove Files +How to remove a file systemless-ly? To actually make the file *disappear* is complicated (possible, not worth the effort). Replacing it with a dummy file should be good enough! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file. + +#### Remove Folders +Same as mentioned above, actually making the folder *disappear* is not worth the effort. Replacing it with an empty folder should be good enough! A handy trick for module developers is to add the folder you want to remove into the `REPLACE` list within `customize.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`. + ## Boot Scripts + In Magisk, you can run boot scripts in 2 different modes: **post-fs-data** and **late_start service** mode. - post-fs-data mode @@ -118,9 +223,10 @@ In Magisk, there are also 2 kinds of scripts: **general scripts** and **module s - Will NOT be executed when **Core-Only** mode is enabled (all modules are disabled) - Modules require boot scripts should **ONLY** use module scripts instead of general scripts -Magisk's internal busybox's path `$BBPATH` is always prepended in `PATH`. This means all commands you call in scripts are always using busybox unless the applet is not included. This makes sure that your script always run in a predictable environment and always have the full suite of commands regardless of which Android version it is running on. +Magisk's internal busybox's path `$BBPATH` is added in the front of `PATH`. This means all commands you call in scripts are always using busybox unless the applet is not included. This makes sure that your script always run in a predictable environment and always have the full suite of commands regardless of which Android version it is running on. ## Root Directory Overlay System + Since `/` is read-only in system-as-root devices, Magisk provides an overlay system, allowing developers to patch files / add new rc scripts. Additional files shall be placed in the `overlay.d` folder in the ramdisk, and they will have the following restrictions: - All `*.rc` files in `overlay.d` will be read and concatenated *AFTER* `init.rc` @@ -130,9 +236,3 @@ e.g. you can replace `/res/random.png` by adding the file `overlay.d/res/random. e.g. `overlay.d/new_file` will be ignored if `/new_file` does not exist - Additional files in `overlay.d/sbin` is allowed as they will be copied into Magisk's sbin overlay.
e.g. `overlay.d/sbin/libfoo.ko` will be copied to `/sbin/libfoo.ko`. - -## Remove Files -How to remove a file systemless-ly? To actually make the file *disappear* is complicated (possible, not worth the effort). Replacing it with a dummy file should be good enough! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file. - -## Remove Folders -Same as mentioned above, actually making the folder *disappear* is not worth the effort. Replacing it with an empty folder should be good enough! A handy trick for module developers is to add the folder you want to remove into the `REPLACE` list within `install.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`. From 46ba7262325958aad56f6cac9032350046c1b753 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 29 Dec 2019 15:16:04 +0800 Subject: [PATCH 34/47] Match exact "SKIPUNZIP=1" to prevent confusion --- scripts/module_installer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/module_installer.sh b/scripts/module_installer.sh index 5a6b15f94..29240cda4 100644 --- a/scripts/module_installer.sh +++ b/scripts/module_installer.sh @@ -124,7 +124,7 @@ else unzip -o "$ZIPFILE" customize.sh -d $MODPATH >&2 - if ! grep -q '^SKIPUNZIP=' $MODPATH/customize.sh 2>/dev/null; then + if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null; then ui_print "- Extracting module files" unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2 From 78c64d39ecc7f57105172b0f30dbbecd21a0c7d2 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 30 Dec 2019 13:04:39 +0800 Subject: [PATCH 35/47] Add split command to magiskboot Allow splitting image.*-dtb files to kernel and dtb --- native/jni/magiskboot/bootimg.cpp | 50 +++++++++++++++++++++++------- native/jni/magiskboot/magiskboot.h | 1 + native/jni/magiskboot/main.cpp | 24 +++++++------- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index 3ab44d753..cf067e5ce 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -282,33 +282,61 @@ void boot_img::parse_image(uint8_t *addr) { fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt2name[r_fmt]); } -void boot_img::find_kernel_dtb() { - const int eof = static_cast(hdr->kernel_size()); - for (int i = 0; i < eof - (int) sizeof(fdt_header); ++i) { - auto fdt_hdr = reinterpret_cast(kernel + i); +static int find_dtb_offset(uint8_t *buf, int sz) { + for (int off = 0; off < sz - (int) sizeof(fdt_header); ++off) { + auto fdt_hdr = reinterpret_cast(buf + off); if (fdt32_to_cpu(fdt_hdr->magic) != FDT_MAGIC) continue; // Check that fdt_header.totalsize does not overflow kernel image size uint32_t totalsize = fdt32_to_cpu(fdt_hdr->totalsize); - if (totalsize + i > eof) + if (totalsize + off > sz) continue; // Check that fdt_header.off_dt_struct does not overflow kernel image size uint32_t off_dt_struct = fdt32_to_cpu(fdt_hdr->off_dt_struct); - if (off_dt_struct + i > eof) + if (off_dt_struct + off > sz) continue; // Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE - auto fdt_node_hdr = reinterpret_cast(kernel + i + off_dt_struct); + auto fdt_node_hdr = reinterpret_cast(buf + off + off_dt_struct); if (fdt32_to_cpu(fdt_node_hdr->tag) != FDT_BEGIN_NODE) continue; - kernel_dtb = kernel + i; - kernel_dt_size = eof - i; - hdr->kernel_size() = i; + return off; + } + return -1; +} + +void boot_img::find_kernel_dtb() { + if (int off = find_dtb_offset(kernel, hdr->kernel_size()); off > 0) { + kernel_dtb = kernel + off; + kernel_dt_size = hdr->kernel_size() - off; + hdr->kernel_size() = off; fprintf(stderr, "KERNEL_DTB [%u]\n", kernel_dt_size); - break; + } +} + +int split_image_dtb(const char *filename) { + uint8_t *buf; + size_t sz; + mmap_ro(filename, buf, sz); + run_finally f([=]{ munmap(buf, sz); }); + + if (int off = find_dtb_offset(buf, sz); off > 0) { + format_t fmt = check_fmt(buf, sz); + if (COMPRESSED(fmt)) { + int fd = creat(KERNEL_FILE, 0644); + decompress(fmt, fd, buf, off); + close(fd); + } else { + dump(buf, off, KERNEL_FILE); + } + dump(buf + off, sz - off, KER_DTB_FILE); + return 0; + } else { + fprintf(stderr, "Cannot find DTB in %s\n", filename); + return 1; } } diff --git a/native/jni/magiskboot/magiskboot.h b/native/jni/magiskboot/magiskboot.h index 580018e49..912b3657e 100644 --- a/native/jni/magiskboot/magiskboot.h +++ b/native/jni/magiskboot/magiskboot.h @@ -14,6 +14,7 @@ int unpack(const char *image, bool nodecomp = false, bool hdr = false); void repack(const char* src_img, const char* out_img, bool nocomp = false); +int split_image_dtb(const char *filename); int hexpatch(const char *image, const char *from, const char *to); int cpio_commands(int argc, char *argv[]); int dtb_commands(int argc, char *argv[]); diff --git a/native/jni/magiskboot/main.cpp b/native/jni/magiskboot/main.cpp index 2c5a89c5d..825d4d255 100644 --- a/native/jni/magiskboot/main.cpp +++ b/native/jni/magiskboot/main.cpp @@ -16,7 +16,7 @@ using namespace std; static void usage(char *arg0) { fprintf(stderr, -FULL_VER(MagiskBoot) R"EOF( - Boot Image Modification Tool +FULL_VER(MagiskBoot) R"EOF( - Boot Image Modification Tool Usage: %s [args...] @@ -84,6 +84,15 @@ Supported actions: If [OUT] is not specified, it will directly output to Configure with env variables: KEEPVERITY TWOSTAGEINIT + split + Split image.*-dtb into kernel + kernel_dtb + + sha1 + Print the SHA1 checksum for + + cleanup + Cleanup the current working directory + compress[=method] [outfile] Compress with [method] (default: gzip), optionally to [outfile] /[outfile] can be '-' to be STDIN/STDOUT @@ -102,16 +111,7 @@ Supported actions: for (auto &it : name2fmt) fprintf(stderr, "%s ", it.first.data()); - fprintf(stderr, R"EOF( - - sha1 - Print the SHA1 checksum for - - cleanup - Cleanup the current working directory - -)EOF"); - + fprintf(stderr, "\n\n"); exit(1); } @@ -147,6 +147,8 @@ int main(int argc, char *argv[]) { printf("%02x", i); printf("\n"); munmap(buf, size); + } else if (argc > 2 && action == "split") { + return split_image_dtb(argv[2]); } else if (argc > 2 && action == "unpack") { int idx = 2; bool nodecomp = false; From 2db1e5cb74d6b6c4f578a0adbd9937ad7f72561b Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 30 Dec 2019 13:21:39 +0800 Subject: [PATCH 36/47] Minor module related fixes --- native/jni/core/bootstages.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 4533b9f20..9cc14fd47 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -414,7 +414,7 @@ static bool magisk_env() { static void prepare_modules() { // Upgrade modules - if (auto dir = xopen_dir(MODULEUPGRADE); dir) { + if (auto dir = open_dir(MODULEUPGRADE); dir) { for (dirent *entry; (entry = xreaddir(dir.get()));) { if (entry->d_type == DT_DIR) { if (entry->d_name == "."sv || entry->d_name == ".."sv) @@ -481,6 +481,7 @@ static void collect_modules() { if (access(buf, F_OK) == 0) exec_script(buf); frm_rf(modfd); + unlinkat(dfd, entry->d_name, AT_REMOVEDIR); continue; } From 3049a81c3bfb981018df744b4f8ce34023be2248 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 1 Jan 2020 14:02:44 +0800 Subject: [PATCH 37/47] Update several scripts - Update backup format as we might be patching multiple partitions - Update uninstaller to remove files in persist (sepolicy.rule) - Better handling for dtb/dtbo partition patching --- .../topjohnwu/magisk/tasks/MagiskInstaller.kt | 6 +- .../com/topjohnwu/magisk/ui/SplashActivity.kt | 2 +- .../com/topjohnwu/magisk/utils/RootInit.kt | 11 ++- app/src/main/res/raw/utils.sh | 57 ++++++------ scripts/addon.d.sh | 3 +- scripts/boot_patch.sh | 23 ++--- scripts/flash_script.sh | 5 +- scripts/magisk_uninstaller.sh | 30 +++--- scripts/util_functions.sh | 93 ++++++++++++------- 9 files changed, 129 insertions(+), 101 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt index e6de79548..19598e116 100644 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt @@ -287,8 +287,10 @@ abstract class MagiskInstaller { protected fun flashBoot(): Boolean { if (!"direct_install $installDir $srcBoot".sh().isSuccess) return false - if (!Info.keepVerity) - "patch_dtbo_image".sh() + arrayOf( + "(KEEPVERITY=${Info.keepVerity} patch_dtb_partitions)", + "run_migrations" + ).sh() return true } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index eabbd9c00..bdc63cff5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -49,7 +49,7 @@ open class SplashActivity : Activity() { // Setup shortcuts Shortcuts.setup(this) - Shell.su("mm_patch_dtbo").submit { + Shell.su("mm_patch_dtb").submit { if (it.isSuccess) Notifications.dtboPatched(this) } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt index c7c01a1d1..bf06c33e4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt @@ -29,11 +29,12 @@ class RootInit : Shell.Initializer() { job.add(context.rawResource(R.raw.nonroot_utils)) } - job.add("mount_partitions", - "get_flags", - "run_migrations", - "export BOOTMODE=true") - .exec() + job.add( + "mount_partitions", + "get_flags", + "run_migrations", + "export BOOTMODE=true" + ).exec() Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() diff --git a/app/src/main/res/raw/utils.sh b/app/src/main/res/raw/utils.sh index 79ca207b4..6bbeb7b35 100644 --- a/app/src/main/res/raw/utils.sh +++ b/app/src/main/res/raw/utils.sh @@ -17,16 +17,6 @@ fix_env() { cd / } -run_migrations() { - # Move the stock backups - if [ -f /data/magisk/stock_boot* ]; then - mv /data/magisk/stock_boot* /data 2>/dev/null - fi - if [ -f /data/adb/magisk/stock_boot* ]; then - mv /data/adb/magisk/stock_boot* /data 2>/dev/null - fi -} - direct_install() { rm -rf $MAGISKBIN/* 2>/dev/null mkdir -p $MAGISKBIN 2>/dev/null @@ -43,30 +33,43 @@ direct_install() { return 0 } -mm_patch_dtbo() { - $KEEPVERITY && return 1 || patch_dtbo_image +mm_patch_dtb() { + local result=1 + local PATCHED=$TMPDIR/dt.patched + for name in dtb dtbo; do + local IMAGE=`find_block $name$SLOT` + if [ ! -z $IMAGE ]; then + if $MAGISKBIN/magiskboot dtb $IMAGE patch $PATCHED; then + result=0 + if [ ! -z $SHA1 ]; then + # Backup stuffs + mkdir /data/magisk_backup_${SHA1} 2>/dev/null + cat $IMAGE | gzip -9 > /data/magisk_backup_${SHA1}/${name}.img.gz + fi + cat $PATCHED /dev/zero > $IMAGE + rm -f $PATCHED + fi + fi + done + return $result } restore_imgs() { - local SHA1=`grep_prop SHA1 /sbin/.magisk/config` - [ -z $SHA1 ] && local SHA1=`cat /.backup/.sha1` [ -z $SHA1 ] && return 1 - local STOCKBOOT=/data/stock_boot_${SHA1}.img.gz - local STOCKDTBO=/data/stock_dtbo.img.gz - [ -f $STOCKBOOT ] || return 1 + local BACKUPDIR=/data/magisk_backup_$SHA1 + [ -d $BACKUPDIR ] || return 1 get_flags find_boot_image - find_dtbo_image - if [ -f $STOCKDTBO -a -b "$DTBOIMAGE" ]; then - flash_image $STOCKDTBO $DTBOIMAGE - fi - if [ -f $STOCKBOOT -a -b "$BOOTIMAGE" ]; then - flash_image $STOCKBOOT $BOOTIMAGE - return 0 - fi - return 1 + for name in dtb dtbo; do + [ -f $BACKUPDIR/${name}.img.gz ] || continue + local IMAGE=`find_block $name$SLOT` + [ -z $IMAGE ] && continue + flash_image $BACKUPDIR/${name}.img.gz $IMAGE + done + [ -f $BACKUPDIR/boot.img.gz ] || return 1 + flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE } post_ota() { @@ -119,3 +122,5 @@ force_pm_install() { [ "$VERIFY" -eq 1 ] && settings put global package_verifier_enable 1 return $res } + +SHA1=`grep_prop SHA1 /sbin/.magisk/config` diff --git a/scripts/addon.d.sh b/scripts/addon.d.sh index 8aae989b6..dc01b2d46 100644 --- a/scripts/addon.d.sh +++ b/scripts/addon.d.sh @@ -70,9 +70,8 @@ main() { remove_system_su find_manager_apk - patch_boot_image + install_magisk - cd / # Cleanups $BOOTMODE || recovery_cleanup rm -rf $TMPDIR diff --git a/scripts/boot_patch.sh b/scripts/boot_patch.sh index 148e22aca..b139aae05 100644 --- a/scripts/boot_patch.sh +++ b/scripts/boot_patch.sh @@ -13,20 +13,15 @@ # # File name Type Description # -# boot_patch.sh script A script to patch boot. Expect path to boot image as parameter. +# boot_patch.sh script A script to patch boot image for Magisk. # (this file) The script will use binaries and files in its same directory # to complete the patching process -# util_functions.sh script A script which hosts all functions requires for this script +# util_functions.sh script A script which hosts all functions required for this script # to work properly -# magiskinit binary The binary to replace /init, which has the magisk binary embedded -# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk, -# and patch the ramdisk for Magisk support -# chromeos folder This folder should store all the utilities and keys to sign -# (optional) a chromeos device. Used for Pixel C -# -# If the script is not running as root, then the input boot image should be a stock image -# or have a backup included in ramdisk internally, since we cannot access the stock boot -# image placed under /data we've created when previously installed +# magiskinit binary The binary to replace /init; magisk binary embedded +# magiskboot binary A tool to manipulate boot images +# chromeos folder This folder includes all the utilities and keys to sign +# (optional) chromeos boot images. Currently only used for Pixel C # ########################################################################################## ########################################################################################## @@ -104,10 +99,8 @@ fi case $((STATUS & 3)) in 0 ) # Stock boot ui_print "- Stock boot image detected" - ui_print "- Backing up stock boot image" SHA1=`./magiskboot sha1 "$BOOTIMAGE" 2>/dev/null` - STOCKDUMP=stock_boot_${SHA1}.img.gz - ./magiskboot compress "$BOOTIMAGE" $STOCKDUMP + cat $BOOTIMAGE > stock_boot.img cp -af ramdisk.cpio ramdisk.cpio.orig 2>/dev/null ;; 1 ) # Magisk patched @@ -158,7 +151,7 @@ rm -f ramdisk.cpio.orig config ########################################################################################## for dt in dtb kernel_dtb extra recovery_dtbo; do - [ -f $dt ] && ./magiskboot dtb $dt patch && ui_print "- Patching fstab in $dt" + [ -f $dt ] && ./magiskboot dtb $dt patch && ui_print "- Patch fstab in $dt" done if [ -f kernel ]; then diff --git a/scripts/flash_script.sh b/scripts/flash_script.sh index 6b8f6bbcc..0cdcfaf14 100644 --- a/scripts/flash_script.sh +++ b/scripts/flash_script.sh @@ -53,7 +53,7 @@ ui_print "- Target image: $BOOTIMAGE" # Detect version and architecture api_level_arch_detect -[ $API -lt 17 ] && abort "! Magisk is only for Android 4.2 and above" +[ $API -lt 17 ] && abort "! Magisk only support Android 4.2 and above" ui_print "- Device platform: $ARCH" @@ -108,9 +108,8 @@ $BOOTMODE || recovery_actions # Boot/DTBO Patching ########################################################################################## -patch_boot_image +install_magisk -cd / # Cleanups $BOOTMODE || recovery_cleanup rm -rf $TMPDIR diff --git a/scripts/magisk_uninstaller.sh b/scripts/magisk_uninstaller.sh index 0fb59b9ff..02b89edd6 100644 --- a/scripts/magisk_uninstaller.sh +++ b/scripts/magisk_uninstaller.sh @@ -15,6 +15,7 @@ TMPDIR=/dev/tmp INSTALLER=$TMPDIR/install CHROMEDIR=$INSTALLER/chromeos +PERSISTDIR=/sbin/.magisk/mirror/persist # Default permissions umask 022 @@ -50,6 +51,7 @@ chmod -R 755 $MAGISKBIN check_data $DATA_DE || abort "! Cannot access /data, please uninstall with Magisk Manager" $BOOTMODE || recovery_actions +run_migrations ########################################################################################## # Uninstall @@ -57,7 +59,6 @@ $BOOTMODE || recovery_actions get_flags find_boot_image -find_dtbo_image [ -e $BOOTIMAGE ] || abort "! Unable to detect boot image" ui_print "- Found target image: $BOOTIMAGE" @@ -96,16 +97,18 @@ case $((STATUS & 3)) in 1 ) # Magisk patched ui_print "- Magisk patched image detected" # Find SHA1 of stock boot image - [ -z $SHA1 ] && SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null` - STOCKBOOT=/data/stock_boot_${SHA1}.img.gz - STOCKDTBO=/data/stock_dtbo.img.gz - if [ -f $STOCKBOOT ]; then + SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null` + BACKUPDIR=/data/magisk_backup_$SHA1 + if [ -d $BACKUPDIR ]; then ui_print "- Restoring stock boot image" - flash_image $STOCKBOOT $BOOTIMAGE - if [ -f $STOCKDTBO -a -b "$DTBOIMAGE" ]; then - ui_print "- Restoring stock dtbo image" - flash_image $STOCKDTBO $DTBOIMAGE - fi + flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE + for name in dtb dtbo; do + [ -f $BACKUPDIR/${name}.img.gz ] || continue + IMAGE=`find_block $name$SLOT` + [ -z $IMAGE ] && continue + ui_print "- Restoring stock $name image" + flash_image $BACKUPDIR/${name}.img.gz $IMAGE + done else ui_print "! Boot image backup unavailable" ui_print "- Restoring ramdisk with internal backup" @@ -128,9 +131,10 @@ case $((STATUS & 3)) in esac ui_print "- Removing Magisk files" -rm -rf /cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \ - /data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* \ - /data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* 2>/dev/null +rm -rf \ +/cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \ +/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* \ +/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* $PERSISTDIR/magisk 2>/dev/null if [ -f /system/addon.d/99-magisk.sh ]; then mount -o rw,remount /system diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index dd7e5610c..935c8aaca 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -110,8 +110,8 @@ recovery_actions() { recovery_cleanup() { ui_print "- Unmounting partitions" - umount -l /system_root 2>/dev/null umount -l /system 2>/dev/null + umount -l /system_root 2>/dev/null umount -l /vendor 2>/dev/null umount -l /dev/random 2>/dev/null export PATH=$OLD_PATH @@ -196,7 +196,7 @@ mount_partitions() { [ -L /system/vendor ] && mount_ro_ensure vendor $SYSTEM_ROOT && ui_print "- Device is system-as-root" - # Persist partitions for module install in recovery + # Mount persist partition in recovery if ! $BOOTMODE && [ ! -z $PERSISTDIR ]; then # Try to mount persist PERSISTDIR=/persist @@ -276,30 +276,29 @@ flash_image() { return 0 } -find_dtbo_image() { - DTBOIMAGE=`find_block dtbo$SLOT` -} - -patch_dtbo_image() { - find_dtbo_image - if [ ! -z $DTBOIMAGE ]; then - ui_print "- DTBO image: $DTBOIMAGE" - local PATCHED=$TMPDIR/dtbo - if $MAGISKBIN/magiskboot dtb $DTBOIMAGE patch $PATCHED; then - ui_print "- Backing up stock DTBO image" - $MAGISKBIN/magiskboot compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz - ui_print "- Patching DTBO to remove avb-verity" - cat $PATCHED /dev/zero > $DTBOIMAGE - rm -f $PATCHED - return 0 +patch_dtb_partitions() { + local result=1 + cd $MAGISKBIN + for name in dtb dtbo; do + local IMAGE=`find_block $name$SLOT` + if [ ! -z $IMAGE ]; then + ui_print "- $name image: $IMAGE" + if ./magiskboot dtb $IMAGE patch dt.patched; then + result=0 + ui_print "- Backing up stock $name image" + cat $IMAGE > stock_${name}.img + ui_print "- Flashing patched $name" + cat dt.patched /dev/zero > $IMAGE + rm -f dt.patched + fi fi - fi - return 1 + done + cd / + return $result } # Common installation script for flash_script.sh and addon.d.sh -patch_boot_image() { - SOURCEDMODE=true +install_magisk() { cd $MAGISKBIN eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true @@ -308,6 +307,7 @@ patch_boot_image() { $IS64BIT && mv -f magiskinit64 magiskinit 2>/dev/null || rm -f magiskinit64 # Source the boot patcher + SOURCEDMODE=true . ./boot_patch.sh "$BOOTIMAGE" ui_print "- Flashing new boot image" @@ -322,18 +322,8 @@ patch_boot_image() { ./magiskboot cleanup rm -f new-boot.img - if [ -f stock_boot* ]; then - rm -f /data/stock_boot* 2>/dev/null - $DATA && mv stock_boot* /data - fi - - # Patch DTBO together with boot image - $KEEPVERITY || patch_dtbo_image - - if [ -f stock_dtbo* ]; then - rm -f /data/stock_dtbo* 2>/dev/null - $DATA && mv stock_dtbo* /data - fi + patch_dtb_partitions + run_migrations } sign_chromeos() { @@ -415,6 +405,41 @@ find_manager_apk() { [ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner" } +run_migrations() { + local LOCSHA1 + local TARGET + # Legacy app installation + local BACKUP=/data/adb/magisk/stock_boot*.gz + if [ -f $BACKUP ]; then + cp $BACKUP /data + rm -f $BACKUP + fi + + # Legacy backup + for gz in /data/stock_boot*.gz; do + [ -f $gz ] || break + LOCSHA1=`basename $gz | sed -e 's/stock_boot_//' -e 's/.img.gz//'` + [ -z $LOCSHA1 ] && break + mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null + mv $gz /data/magisk_backup_${LOCSHA1}/boot.img.gz + done + + # Stock backups + LOCSHA1=$SHA1 + for name in boot dtb dtbo; do + BACKUP=/data/adb/magisk/stock_${name}.img + [ -f $BACKUP ] || continue + if [ $name = 'boot' ]; then + LOCSHA1=`$MAGISKBIN/magiskboot sha1 $BACKUP` + mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null + fi + TARGET=/data/magisk_backup_${LOCSHA1}/${name}.img + cp $BACKUP $TARGET + rm -f $BACKUP + gzip -9f $TARGET + done +} + ################# # Module Related ################# From 1b4a3d2d9f3d0c04cc353d6aef1a30ad2240ce4d Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 1 Jan 2020 14:19:24 +0800 Subject: [PATCH 38/47] More precise env detection in non-root --- app/src/main/res/raw/nonroot_utils.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/raw/nonroot_utils.sh b/app/src/main/res/raw/nonroot_utils.sh index 7c8ae1812..88a5db68b 100644 --- a/app/src/main/res/raw/nonroot_utils.sh +++ b/app/src/main/res/raw/nonroot_utils.sh @@ -1,6 +1,7 @@ mount_partitions() { [ "`getprop ro.build.ab_update`" = "true" ] && SLOT=`getprop ro.boot.slot_suffix` - [ "`getprop ro.build.system_root_image`" = "true" ] && SYSTEM_ROOT=true || SYSTEM_ROOT=false + # Check whether non rootfs root dir exists + grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true || SYSTEM_ROOT=false } get_flags() { From 1aeb6315ff8a34516a31622cff541f5a847c6677 Mon Sep 17 00:00:00 2001 From: Davy Defaud Date: Sat, 28 Dec 2019 17:22:10 +0100 Subject: [PATCH 39/47] Spelling & typographical fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - spelling fixes : complête → complète - typographical fixes : thin spaces before exclamation and interrogation marks - rewording for a better French translation --- stub/src/main/res/values-fr/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stub/src/main/res/values-fr/strings.xml b/stub/src/main/res/values-fr/strings.xml index dc928c8e9..6a0633dfa 100644 --- a/stub/src/main/res/values-fr/strings.xml +++ b/stub/src/main/res/values-fr/strings.xml @@ -1,5 +1,5 @@ - Mettre à jour vers la version complête de Magisk Manager pour finir l\'installation. Télécharger et installer? - Veuillez vous connecter à Internet! Une mise à niveau complête vers le Gestionnaire Magisk est requise. + Une mise à niveau de Magisk Manager en version complète est nécessaire afin de terminer l’installation. Souhaitez‑vous procéder à son téléchargement et son installation ? + Veuillez vous connecter à Internet ! Une mise à niveau complète de Magisk Manager est requise. From b07b528e2a18abde9e90c3b7ded76c777a7c5669 Mon Sep 17 00:00:00 2001 From: Davy Defaud Date: Sat, 28 Dec 2019 17:35:13 +0100 Subject: [PATCH 40/47] Add missing translation for "dling" string --- stub/src/main/res/values-fr/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/stub/src/main/res/values-fr/strings.xml b/stub/src/main/res/values-fr/strings.xml index 6a0633dfa..e74fa8e20 100644 --- a/stub/src/main/res/values-fr/strings.xml +++ b/stub/src/main/res/values-fr/strings.xml @@ -2,4 +2,5 @@ Une mise à niveau de Magisk Manager en version complète est nécessaire afin de terminer l’installation. Souhaitez‑vous procéder à son téléchargement et son installation ? Veuillez vous connecter à Internet ! Une mise à niveau complète de Magisk Manager est requise. + Téléchargement en cours From 71b05b18a0b279b623df3916dadf4e16281b7df4 Mon Sep 17 00:00:00 2001 From: Davy Defaud Date: Sat, 28 Dec 2019 22:00:58 +0100 Subject: [PATCH 41/47] Spelling, typographical and wording fixes - spelling fixes - typographical fixes : thin spaces before exclamation and interrogation marks, true apostrophes instead of single quotes, non-breaking spaces to avoid orphan words, etc. - rewording for a better French translation - fix various misinterpretation --- app/src/main/res/values-fr/strings.xml | 244 ++++++++++++------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0049d56a0..bd848c767 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -3,221 +3,221 @@ Modules Téléchargements - Super‐utilisateur + Super‑utilisateur Journal Paramètres Installer - Version de Magisk non prise en charge - Cette version de Magisk Manager ne supporte pas de version de Magisk inférieure à la %1$s.\n\nL\'application se comportera comme si Magisk n\'était pas installé, veuillez mettre à jour Magisk dès que possible. + Version de Magisk non prise en charge + Cette version de Magisk Manager ne prend pas en charge les versions de Magisk inférieures à %1$s.\n\nL’application se comportera comme si Magisk n’était pas installé, veuillez mettre à jour Magisk dès que possible. - Magisk n’est pas installé. - Vérification des mises à jour… - Canal de mise à jour invalide - Appuyez pour lancer le contrôle SafetyNet. + Magisk n’est pas installé + Vérification des mises à jour… + Canal de mise à jour invalide + Appuyez pour lancer le contrôle SafetyNet Vérification de l’état de SafetyNet… - Contrôle SafetyNet passé avec succès + Contrôle SafetyNet réussi Erreur de l’API SafetyNet - La réponse est incorrecte. - Magisk est à jour - Magisk Manager est à jour + La réponse est incorrecte + Magisk est à jour + Magisk Manager est à jour Paramètres avancés Conserver le chiffrement forcé Conserver AVB 2.0/dm-verity - Mode Récupération - Version actuellement installée : %1$s - Dernière version disponible : %1$s + Mode récupération + Version actuellement installée : %1$s + Dernière version disponible : %1$s Désinstaller Désinstaller Magisk - Tous les modules seront désactivés/supprimés!\nL\'enracinement sera retiré!\nVos données seront potentiellement chiffrées si elles ne le sont pas déjà. - Mise à jour - (Mode « sans modules » activé) + Tous les modules seront désactivés ou supprimés !\nL’accès super‑utiliateur sera perdu !\nVos données seront potentiellement chiffrées si elles ne le sont pas déjà. + Mise à jour + (mode « sans modules » activé) (aucune information transmise) - Aucun module trouvé. - Le module sera mis à jour au prochain redémarrage. - Le module sera supprimé au prochain redémarrage. - Le module ne sera pas supprimé au prochain redémarrage. - Le module sera désactivé au prochain redémarrage. - Le module sera activé au prochain redémarrage. + Aucun module trouvé + Le module sera mis à jour au prochain redémarrage ! + Le module sera supprimé au prochain redémarrage ! + Le module ne sera pas supprimé au prochain redémarrage ! + Le module sera désactivé au prochain redémarrage ! + Le module sera activé au prochain redémarrage ! Créé par %1$s Redémarrer en mode récupération Redémarrer en mode amorçage Redémarrer en mode téléchargement - Redémarrer en mode EDL + Redémarrer en mode secours (EDL) Mise à jour disponible Installé Non installé - Mis à jour le : %1$s - Mode de tri : - par nom - par date décroissante + Mis à jour le : %1$s + Mode de tri + alphabétique + antichronologique Enregistrer le journal Actualiser Effacer le journal maintenant - Journal effacé avec succès. + Le journal a bien été effacé. - Journal + Journal des modifications - Mises à jour de Magisk - Progression des notifications + Mises à jour de Magisk + Notifications de progression Téléchargement terminé - Erreur de téléchargement du fichier - Afficher dans le dossier parent - Afficher fichier - Une mise à jour de Magisk est disponible ! - Une mise à jour de Magisk Manager est disponible ! + Erreur lors du téléchargement du fichier + Afficher le dossier parent + Afficher le fichier + Une mise à jour de Magisk est disponible ! + Une mise à jour de Magisk Manager est disponible ! Appuyez pour le télécharger et l’installer. - Télécharger le ZIP uniquement + Télécharger le ZIP sans l’installer Installation directe (recommandée) - Installer dans l’espace inactif (après mise à jour OTA) - Votre appareil sera réamorcé à partir de l’espace actuellement inactif après un redémarrage!\nN’utilisez cette option qu’uniquement après que la mise à jour OTA ait été effectuée.\nVoulez-vous continuer? + Installer dans l’espace inactif (après mise à jour OTA) + Votre appareil sera obligatoirement réamorcé à partir de l’espace (slot) actuellement inactif après son redémarrage !\nN’utilisez cette option uniquement après que la mise à jour OTA a été effectuée.\nVoulez‑vous continuer ? Sélectionnez la méthode - Installation additionnelle - Sélectionner et Patcher un Fichier - Sélectionnez une image brute (*.img) ou un fichier tar ODIN (*.tar) - Redémarrage dans 5 secondes… + Configuration supplémentaire + Sélectionnez le fichier cible du correctif + Sélectionnez une image brute (*.img) ou une archive TAR ODIN (*.tar) + Redémarrage dans 5 secondes… Installer %1$s - Voulez‐vous installer %1$s maintenant ? + Voulez‑vous installer %1$s maintenant ? Télécharger Redémarrer - Redémarrer afin d’appliquer les réglages. + Redémarrer afin d’appliquer les paramètres Notes de version Cache du dépôt effacé - DTBO a été modifié ! - Magisk Manager vient de modifier le fichier dtbo.img, veuillez redémarrer. - Installation - Terminé! - Échec - Masquer Magisk Manager… - Le masquage de Magisk Manager a échoué. - Aucune application permettant d’ouvrir le lien n’a été trouvée. + L’arborescence matérielle (DTBO) a été modifiée ! + Magisk Manager a modifié le fichier dtbo.img. Veuillez redémarrer. + Écriture dans la mémoire Flash… + Terminé ! + Erreur + Masquer Magisk Manager… + Le masquage de Magisk Manager a échoué. + Aucune application permettant d’ouvrir le lien n’a été trouvée Avertissement - Désinstallation terminée + Désinstallation complète Restauration des images Restauration… Restauration terminée ! - La sauvegarde par défaut n’existe pas ! + La sauvegarde par défaut n’existe pas ! Téléchargement de code propriétaire - Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de l’API SafetyNet de Google.Voulez‐vous autoriser Magisk Manager à télécharger une extension (contenant GoogleApiClient) pour les contrôles SafetyNet ? + Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de l’API SafetyNet de Google. Autorisez‑vous Magisk Manager à télécharger une extension (contenant GoogleApiClient) pour les contrôles SafetyNet ? Échec de l’installation - Installation additionnelle requise - Votre appareil a besoin d’une installation supplémentaire pour que Magisk fonctionne correctement. Celà téléchargera le fichier ZIP d’installation de Magisk. Voulez-vous le faire maintenant ? - Démarrer l’installation de l’environnement… - Authentifier + Installation supplémentaire requise + Votre appareil a besoin d’une installation supplémentaire afin que Magisk fonctionne correctement. Un fichier ZIP d’installation pour Magisk devra être téléchargé. Voulez‑vous faire cette installation maintenant ? + Installation de l’environnement en cours… + Authentification Général Thème sombre - Activer le thème sombre. - Emplacement téléchargement + Activer le thème sombre + Répertoire de téléchargement Les fichiers seront sauvegardés dans %1$s Effacer le cache du dépôt - Effacer les informations de cache pour les dépôts en ligne. Cela force l’application à récupérer ses informations en ligne. - Masquer Magisk Manager - Réempaqueter Magisk Manager avec un nom de paquet aléatoire. - Restaurer Magisk Manager - Restaurer Magisk Manager avec son nom de paquet d’origine + Effacer les informations en cache concerant les dépôts en ligne. Ceci force l’application à télécharger des informations à jour. + Masquer Magisk Manager + Réempaqueter Magisk Manager avec des noms de paquet et d’application aléatoires. + Restaurer Magisk Manager + Restaurer Magisk Manager avec ses noms de paquet et d’application d’origine Langue - (système par défaut) - Mise à jour des réglages - Vérification des mises à jour - Vérifier périodiquement en tâche de fond l’existence d’une mise à jour. - Canal de mise à jour + (langue par défaut du système) + Paramètres de mise à jour + Vérifier les mises à jour + Vérifier périodiquement en tâche de fond l’existence d’une mise à jour + Canal de mise à jour Stable Bêta Personnalisé - Insérez une URL personnalisée - Mode Magisk Core uniquement - Active uniquement les fonctionnalités de base. MagiskSU et MagiskHide resteront activés, mais aucun module ne sera chargé. + Saisissez une URL personnalisée + Mode « sans modules » uniquement + Activer uniquement les fonctionnalités de base. MagiskSU et MagiskHide resteront activés, mais aucun module ne sera chargé. Rendre Magisk invisible à diverses formes de détection. - Fichier hosts sans système - Prise en charge du fichier hosts sans système pour les applications de type AdBlock. - Ajout d’un module hosts sans système + Fichier hosts hors partition système + Utilisation d’un fichier hosts hors de la partition système pour les applications de blocage de publicité. + Ajout d’un module pour fichier hosts hors système - Taper le nom de l\'application désirée + Saisissez le nom de l’application désiré Nouveau nom - L\'application sera réempacter sous ce nom - Format invalide + L’application sera réempaquetée sous ce nom + Format incorrect Applications et ADB Applications uniquement ADB uniquement Désactivé - 10 secondes - 15 secondes - 20 secondes - 30 secondes - 45 secondes - 60 secondes - Accès super‐utilisateur + 10 secondes + 15 secondes + 20 secondes + 30 secondes + 45 secondes + 60 secondes + Accès super‑utilisateur Réponse automatique Délai d’expiration de la demande - Notification super‐utilisateur - %1$d secondes - Authentifier à nouveau après la mise à niveau - Authentifier à nouveau les autorisations super‐utilisateur après une mise à jour de l’application - Activer l\'authentification Biométrique - Utiliser l\'authentification biométrique pour autoriser les accès super-utilisateur - Périphérique non supporté ou les réglages biométriques ne sont pas actifs + Notification super‑utilisateur + %1$d secondes + S’authentifier à nouveau après la mise à niveau + Redemander une authentification pour autoriser l’accès en super‑utilisateur après une mise à jour de l’application + Activer l’authentification biométrique + Utiliser l’authentification biométrique pour autoriser les accès en super‑utilisateur + L’appareil n’est pas pris en charge ou alors aucun paramètre biométrique n’est activé - Mode multi‐utilisateur + Mode multi‑utilisateur Propriétaire de l’appareil uniquement Géré par le propriétaire de l’appareil Indépendant de l’utilisateur - Seul le propriétaire a un accès super‐utilisateur. - Seul le propriétaire peut gérer l’accès super‐utilisateur et recevoir les demandes de requêtes. - Chaque utilisateur a ses propres règles super‐utilisateur séparées. + Seul le propriétaire possède un accès super‑utilisateur. + Seul le propriétaire peut gérer l’accès super‑utilisateur et recevoir les demandes d’accès. + Chaque utilisateur a ses propres règles d’accès super‑utilisateur séparées. - Mode d’espace de noms du montage + Mode d’espace de noms du montage Espace de noms global - Hériter de l’espace de noms + Hériter de l’espace de noms Espace de noms isolé - Toutes les sessions super‐utilisateur utilisent l’espace de noms global du montage. - Les sessions super‐utilisateur hériteront de l’espace de noms de leur demandeur. - Chaque session super‐utilisateur aura son propre espace de noms isolé. - Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier. + Toutes les sessions super‑utilisateur utilisent l’espace de noms global du montage. + Les sessions super‑utilisateur hériteront de l’espace de noms de leur demandeur. + Chaque session super‑utilisateur aura son propre espace de noms isolé. + Erreur lors de la création du dossier. Ce dernier doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier. - Requête super‐utilisateur + Demande d’accès super‑utilisateur Refuser Demander Accepter - Autoriser l’accès complet à votre appareil.\nRefusez si vous n’êtes pas sûr ! + Autoriser l’accès complet à votre appareil.\nRefusez si vous n’êtes pas sûr(e) ! Toujours - Une fois + Une fois 10 min 20 min 30 min 60 min - %1$s a obtenu les droits super‐utilisateur - %1$s n’a pas obtenu les droits super‐utilisateur + %1$s a obtenu les droits de super‑utilisateur + %1$s s’est vu refuser les droits de super‑utilisateur Aucune application trouvée - Les droits super‐utilisateur de %1$s sont accordés - Les droits super‐utilisateur de %1$s sont refusés - Les notifications pour %1$s sont activées - Les notifications pour %1$s sont désactivées - La journalisation pour %1$s est activée - La journalisation pour %1$s est désactivée - Annuler ? - Confirmez‐vous l’annulation des droits pour %1$s ? + Les droits de super‑utilisateur sont accordés à %1$s + Les droits de super‑utilisateur sont refusés à %1$s + Les notifications sont activées pour %1$s + Les notifications sont désactivées pour %1$s + La journalisation est activée pour %1$s + La journalisation est désactivée pour %1$s + Révoquer ? + Confirmez‑vous la révocation des droits accordés à %1$s ? Toast Aucun - PID : %1$d - UID cible : %1$d - Commande : %1$s + PID : %1$d + UID cible : %1$d + Commande : %1$s Afficher les applications système From e88b1cc443e219b80919c5709cfd725e376b42c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=9F=E6=AD=A6=2E=E5=B0=BC=E5=BE=B7=E9=9C=8D=E6=A0=BC?= =?UTF-8?q?=2E=E9=BE=8D?= <7708801314520.tony@gmail.com> Date: Wed, 1 Jan 2020 15:05:07 +0800 Subject: [PATCH 42/47] =?UTF-8?q?=E7=B9=81=E9=AB=94=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=AD=97=E4=B8=B2=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values-zh-rTW/strings.xml | 2 +- stub/src/main/res/values-zh-rTW/strings.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 46361b5ed..5bd6cccd9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -167,7 +167,7 @@ %1$d 秒 更新後重新驗證 應用程式更新後,重新驗證超級使用者的請求 - + 啟用生物特徵驗證 多重使用者模式 僅限裝置擁有者 由裝置擁有者管理 diff --git a/stub/src/main/res/values-zh-rTW/strings.xml b/stub/src/main/res/values-zh-rTW/strings.xml index 84bb1988f..d44406e77 100644 --- a/stub/src/main/res/values-zh-rTW/strings.xml +++ b/stub/src/main/res/values-zh-rTW/strings.xml @@ -1,4 +1,5 @@ 需要升級到完整版 Magisk Manager。是否下載並安裝? 請連上網路!升級到完整版 Magisk Manager 是必須的。 + 下載中 From 3c2041933f38536f7454c2c4903878027ff5b09b Mon Sep 17 00:00:00 2001 From: nikk gitanes Date: Tue, 24 Dec 2019 22:58:10 +0300 Subject: [PATCH 43/47] Remote focus fixes (classic UI) --- app/src/main/res/layout/fragment_modules.xml | 4 +++- app/src/main/res/layout/item_console.xml | 1 + app/src/main/res/layout/item_hide_app.xml | 2 ++ app/src/main/res/layout/item_hide_process.xml | 2 ++ app/src/main/res/layout/item_module.xml | 4 ++++ app/src/main/res/layout/item_page_magisk_log.xml | 5 ++++- app/src/main/res/layout/item_repo.xml | 2 ++ 7 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/fragment_modules.xml b/app/src/main/res/layout/fragment_modules.xml index 6c6b8f6da..7439ca496 100644 --- a/app/src/main/res/layout/fragment_modules.xml +++ b/app/src/main/res/layout/fragment_modules.xml @@ -59,10 +59,12 @@ android:layout_gravity="bottom|center_horizontal" android:layout_margin="@dimen/fab_padding" android:onClick="@{() -> viewModel.fabPressed()}" + android:focusable="true" + android:clickable="true" app:fabSize="normal" app:layout_behavior="com.google.android.material.floatingactionbutton.FloatingActionButton$Behavior" app:srcCompat="@drawable/ic_add" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_console.xml b/app/src/main/res/layout/item_console.xml index b0c4d4fb7..8fa6c0b93 100644 --- a/app/src/main/res/layout/item_console.xml +++ b/app/src/main/res/layout/item_console.xml @@ -16,6 +16,7 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_repo.xml b/app/src/main/res/layout/item_repo.xml index 6e876e5ea..efada5183 100644 --- a/app/src/main/res/layout/item_repo.xml +++ b/app/src/main/res/layout/item_repo.xml @@ -105,6 +105,8 @@ Date: Thu, 2 Jan 2020 02:19:56 +0800 Subject: [PATCH 44/47] Make sure shell command won't block --- .../main/java/com/topjohnwu/magisk/Config.kt | 3 +++ .../main/java/com/topjohnwu/magisk/Info.kt | 18 +++++++++++++++++ .../magisk/data/database/PolicyDao.kt | 2 +- .../magisk/data/database/SettingsDao.kt | 2 +- .../magisk/data/database/StringDao.kt | 2 +- .../com/topjohnwu/magisk/ui/SplashActivity.kt | 20 ++++++++++++------- .../com/topjohnwu/magisk/utils/RootInit.kt | 9 --------- 7 files changed, 37 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/Config.kt b/app/src/main/java/com/topjohnwu/magisk/Config.kt index 845521236..22a1ef47b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Config.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Config.kt @@ -48,6 +48,7 @@ object Config : PreferenceModel, DBConfig { const val REPO_ORDER = "repo_order" const val SHOW_SYSTEM_APP = "show_system" const val DOWNLOAD_PATH = "download_path" + const val BOOT_ID = "boot_id" // system state const val MAGISKHIDE = "magiskhide" @@ -105,6 +106,8 @@ object Config : PreferenceModel, DBConfig { } else Value.DEFAULT_CHANNEL + var bootId by preference(Key.BOOT_ID, "") + var downloadPath by preference(Key.DOWNLOAD_PATH, Environment.DIRECTORY_DOWNLOADS) var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE) diff --git a/app/src/main/java/com/topjohnwu/magisk/Info.kt b/app/src/main/java/com/topjohnwu/magisk/Info.kt index fd1120ee3..b95834dc0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Info.kt @@ -8,6 +8,8 @@ import com.topjohnwu.magisk.utils.CachedValue import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils +import java.io.FileInputStream +import java.io.IOException val isRunningAsStub get() = Info.stub != null @@ -32,6 +34,22 @@ object Info { } } + val isNewReboot by lazy { + try { + FileInputStream("/proc/sys/kernel/random/boot_id").bufferedReader().use { + val id = it.readLine() + if (id != Config.bootId) { + Config.bootId = id + true + } else { + false + } + } + } catch (e: IOException) { + false + } + } + private fun loadState() = runCatching { val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0] val code = ShellUtils.fastCmd("magisk -V").toInt() diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/PolicyDao.kt b/app/src/main/java/com/topjohnwu/magisk/data/database/PolicyDao.kt index ff73b6cb7..653b12e7a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/database/PolicyDao.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/database/PolicyDao.kt @@ -3,8 +3,8 @@ package com.topjohnwu.magisk.data.database import android.content.Context import android.content.pm.PackageManager import com.topjohnwu.magisk.Const -import com.topjohnwu.magisk.data.database.magiskdb.Delete import com.topjohnwu.magisk.data.database.magiskdb.BaseDao +import com.topjohnwu.magisk.data.database.magiskdb.Delete import com.topjohnwu.magisk.data.database.magiskdb.Replace import com.topjohnwu.magisk.data.database.magiskdb.Select import com.topjohnwu.magisk.extensions.now diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/SettingsDao.kt b/app/src/main/java/com/topjohnwu/magisk/data/database/SettingsDao.kt index c2b4a2701..fdc00d520 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/database/SettingsDao.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/database/SettingsDao.kt @@ -1,7 +1,7 @@ package com.topjohnwu.magisk.data.database -import com.topjohnwu.magisk.data.database.magiskdb.Delete import com.topjohnwu.magisk.data.database.magiskdb.BaseDao +import com.topjohnwu.magisk.data.database.magiskdb.Delete import com.topjohnwu.magisk.data.database.magiskdb.Replace import com.topjohnwu.magisk.data.database.magiskdb.Select diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/StringDao.kt b/app/src/main/java/com/topjohnwu/magisk/data/database/StringDao.kt index 92b8c458b..24eab0564 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/database/StringDao.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/database/StringDao.kt @@ -1,7 +1,7 @@ package com.topjohnwu.magisk.data.database -import com.topjohnwu.magisk.data.database.magiskdb.Delete import com.topjohnwu.magisk.data.database.magiskdb.BaseDao +import com.topjohnwu.magisk.data.database.magiskdb.Delete import com.topjohnwu.magisk.data.database.magiskdb.Replace import com.topjohnwu.magisk.data.database.magiskdb.Select diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index bdc63cff5..3a4555976 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -3,14 +3,12 @@ package com.topjohnwu.magisk.ui import android.app.Activity import android.content.Context import android.os.Bundle -import com.topjohnwu.magisk.BuildConfig -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.intent +import com.topjohnwu.magisk.* import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts -import com.topjohnwu.magisk.wrap import com.topjohnwu.superuser.Shell +import com.topjohnwu.superuser.ShellUtils open class SplashActivity : Activity() { @@ -37,6 +35,10 @@ open class SplashActivity : Activity() { } } + Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() + Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() + Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() + // Set default configs Config.initialize() @@ -49,9 +51,13 @@ open class SplashActivity : Activity() { // Setup shortcuts Shortcuts.setup(this) - Shell.su("mm_patch_dtb").submit { - if (it.isSuccess) - Notifications.dtboPatched(this) + if (Info.isNewReboot) { + val shell = Shell.newInstance() + shell.newJob().add("mm_patch_dtb").submit { + if (it.isSuccess) + Notifications.dtboPatched(this) + shell.close() + } } DONE = true diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt index bf06c33e4..92d5a2729 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt @@ -2,12 +2,10 @@ package com.topjohnwu.magisk.utils import android.content.Context import com.topjohnwu.magisk.Const -import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.rawResource import com.topjohnwu.magisk.wrap import com.topjohnwu.superuser.Shell -import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.io.SuFile class RootInit : Shell.Initializer() { @@ -17,9 +15,6 @@ class RootInit : Shell.Initializer() { } fun init(context: Context, shell: Shell): Boolean { - // Invalidate env state if shell is recreated - Info.envRef.invalidate() - val job = shell.newJob() if (shell.isRoot) { job.add(context.rawResource(R.raw.util_functions)) @@ -36,10 +31,6 @@ class RootInit : Shell.Initializer() { "export BOOTMODE=true" ).exec() - Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() - Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() - Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() - return true } } From e0d02a61a9c3e86fdb7b849e0dd767f5490e84a1 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 2 Jan 2020 12:09:36 +0800 Subject: [PATCH 45/47] Add v7.5.0 changelog --- app/src/main/res/raw/changelog.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/raw/changelog.md b/app/src/main/res/raw/changelog.md index cfb6136f2..f3091bd65 100644 --- a/app/src/main/res/raw/changelog.md +++ b/app/src/main/res/raw/changelog.md @@ -1,5 +1,4 @@ -# v7.4.0 -- Hide Magisk Manager with stub APKs on Android 9.0+. Not all devices will be supported, please refer to Magisk v20.1 release notes. -- Allow customizing app name when hiding Magisk Manager -- Generate random keys to sign the hidden Magisk Manager to prevent signature detections -- Fix fingerprint UI infinite loop +# v7.5.0 +- Support new communication method (ContentProvider) +- Fix several issues with hidden stub APK +- Support using BiometricPrompt (face unlock) From bd438ca2884eab67b3ff886699fee6c5832b2138 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 2 Jan 2020 13:45:08 +0800 Subject: [PATCH 46/47] Update docs --- docs/README.md | 2 +- docs/tools.md | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/README.md b/docs/README.md index 48a05ebf0..dadc177ef 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ # Magisk Documentation -(Updated on 2019.12.29) +(Updated on 2020.1.2) - [Installation](install.md) - [Tutorials](tutorials.md) diff --git a/docs/tools.md b/docs/tools.md index 0c2499f7a..2ff9e2d0b 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -119,20 +119,22 @@ Usage: magiskpolicy [--options...] [policy statements...] Options: --help show help message for policy statements --load FILE load policies from FILE - --load-split load from preloaded sepolicy or compile + --load-split load from precompiled sepolicy or compile split policies --compile-split compile split cil policies --save FILE save policies to FILE --live directly apply sepolicy live --magisk inject built-in rules for a minimal Magisk selinux environment + --apply FILE apply rules from FILE, read and parsed + line by line as policy statements If neither --load or --compile-split is specified, it will load from current live policies (/sys/fs/selinux/policy) One policy statement should be treated as one parameter; -this means a full policy statement should be enclosed in quotes; -multiple policy statements can be provided in a single command +this means a full policy statement should be enclosed in quotes. +Multiple policy statements can be provided in a single command. The statements has a format of " [args...]" Multiple types and permissions can be grouped into collections @@ -173,10 +175,10 @@ Notes: Example: allow { s1 s2 } { t1 t2 } class * Will be expanded to: -allow s1 t1 class { all permissions } -allow s1 t2 class { all permissions } -allow s2 t1 class { all permissions } -allow s2 t2 class { all permissions } +allow s1 t1 class { all-permissions } +allow s1 t2 class { all-permissions } +allow s2 t1 class { all-permissions } +allow s2 t2 class { all-permissions } ``` @@ -202,7 +204,6 @@ Advanced Options (Internal APIs): --clone-attr SRC DEST clone permission, owner, and selinux context --clone SRC DEST clone SRC to DEST --sqlite SQL exec SQL commands to Magisk database - --use-broadcast use broadcast for su logging and notify Supported init triggers: post-fs-data, service, boot-complete @@ -269,5 +270,4 @@ Actions: ls Print the current hide list exec CMDs... Execute commands in isolated mount namespace and do all hide unmounts - test Run process monitor test ``` From 15bd2da824d6ff3b24fa5ddfda666521e1f1f3f5 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 2 Jan 2020 13:46:59 +0800 Subject: [PATCH 47/47] Update magiskboot docs --- docs/tools.md | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/docs/tools.md b/docs/tools.md index 2ff9e2d0b..484bf04a6 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -25,9 +25,11 @@ The concept of `magiskboot` is to make boot image modification simpler. For unpa Usage: magiskboot [args...] Supported actions: - unpack [-h] + unpack [-n] [-h] Unpack to, if available, kernel, kernel_dtb, ramdisk.cpio, second, dtb, extra, and recovery_dtbo into current directory. + If '-n' is provided, it will not attempt to decompress kernel or + ramdisk.cpio from their original formats. If '-h' is provided, it will dump header info to 'header', which will be parsed when repacking. Return values: @@ -45,7 +47,7 @@ Supported actions: cpio [commands...] Do cpio commands to (modifications are done directly) - Each command is a single argument, use quotes if necessary + Each command is a single argument, add quotes for each command Supported commands: exists ENTRY Return 0 if ENTRY exists, else return 1 @@ -65,8 +67,9 @@ Supported actions: Test the current cpio's patch status Return values: 0:stock 1:Magisk 2:unsupported (phh, SuperSU, Xposed) - patch KEEPVERITY KEEPFORCEENCRYPT - Ramdisk patches. KEEP**** are boolean values + patch + Apply ramdisk patches. Configure settings with env variables: + KEEPVERITY KEEPFORCEENCRYPT backup ORIG Create ramdisk backups from ORIG restore @@ -74,17 +77,25 @@ Supported actions: sha1 Print stock boot SHA1 if previously backed up in ramdisk - dtb- - Do dtb related cmds to (modifications are done directly) - Supported commands: - dump - Dump all contents from dtb for debugging - test - Check if fstab has verity/avb flags - Return values: - 0:flag exists 1:no flags - patch + dtb [args...] + Do dtb related actions to + Supported actions: + print [-f] + Print all contents of dtb for debugging + Specify [-f] to only print fstab nodes + patch [OUT] Search for fstab and remove verity/avb + If [OUT] is not specified, it will directly output to + Configure with env variables: KEEPVERITY TWOSTAGEINIT + + split + Split image.*-dtb into kernel + kernel_dtb + + sha1 + Print the SHA1 checksum for + + cleanup + Cleanup the current working directory compress[=method] [outfile] Compress with [method] (default: gzip), optionally to [outfile] @@ -95,12 +106,6 @@ Supported actions: Detect method and decompress , optionally to [outfile] /[outfile] can be '-' to be STDIN/STDOUT Supported methods: bzip2 gzip lz4 lz4_legacy lzma xz - - sha1 - Print the SHA1 checksum for - - cleanup - Cleanup the current working directory ``` ### magiskinit