From b6b34f761214158562fd81b1a4cca5a9c343e551 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 18 Feb 2025 15:39:59 -0800 Subject: [PATCH] Fix overlay.d context preservation --- native/src/base/cstr.rs | 34 +++++-- native/src/base/files.rs | 85 ++++++++++------ native/src/core/package.rs | 2 +- native/src/core/resetprop/persist.rs | 2 +- native/src/include/consts.rs | 2 + native/src/init/init.rs | 1 + native/src/init/lib.rs | 11 ++- native/src/init/rootdir.cpp | 120 +++++++++------------- native/src/init/rootdir.rs | 142 +++++++++++++-------------- native/src/init/selinux.cpp | 2 +- 10 files changed, 208 insertions(+), 193 deletions(-) diff --git a/native/src/base/cstr.rs b/native/src/base/cstr.rs index 5746a1dff..89fa18eab 100644 --- a/native/src/base/cstr.rs +++ b/native/src/base/cstr.rs @@ -1,14 +1,14 @@ +use cxx::{type_id, ExternType}; +use libc::c_char; +use std::borrow::Borrow; use std::cmp::min; use std::ffi::{CStr, FromBytesWithNulError, OsStr}; -use std::fmt::{Arguments, Debug, Display, Formatter, Write}; +use std::fmt::{Debug, Display, Formatter, Write}; use std::ops::{Deref, DerefMut}; use std::os::unix::ffi::OsStrExt; use std::path::{Path, PathBuf}; use std::str::Utf8Error; use std::{fmt, mem, slice, str}; - -use cxx::{type_id, ExternType}; -use libc::c_char; use thiserror::Error; use crate::slice_from_ptr_mut; @@ -263,6 +263,12 @@ impl From for Utf8CString { } } +impl Borrow for Utf8CString { + fn borrow(&self) -> &Utf8CStr { + self.deref() + } +} + // UTF-8 validated + null terminated reference to buffer pub struct Utf8CStrBufRef<'a> { used: usize, @@ -359,7 +365,7 @@ impl Utf8CStr { } #[inline(always)] - pub unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr { + pub const unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr { mem::transmute(buf) } @@ -432,6 +438,16 @@ impl DerefMut for Utf8CStr { } } +impl ToOwned for Utf8CStr { + type Owned = Utf8CString; + + fn to_owned(&self) -> Utf8CString { + let mut s = Utf8CString::with_capacity(self.len() + 1); + s.push_str(self.as_str()); + s + } +} + // Notice that we only implement ExternType on Utf8CStr *reference* unsafe impl ExternType for &Utf8CStr { type Id = type_id!("rust::Utf8CStr"); @@ -535,7 +551,8 @@ impl FsPathBuf { fn inner(buf: &mut dyn Utf8CStrBuf, path: &str) { if path.starts_with('/') { buf.clear(); - } else { + } + if !buf.is_empty() && !buf.ends_with('/') { buf.push_str("/"); } buf.push_str(path); @@ -545,10 +562,7 @@ impl FsPathBuf { } pub fn join_fmt(mut self, name: T) -> Self { - fn inner(buf: &mut dyn Utf8CStrBuf, path: Arguments) { - buf.write_fmt(path).ok(); - } - inner(self.0.deref_mut(), format_args!("/{}", name)); + self.0.write_fmt(format_args!("/{}", name)).ok(); self } } diff --git a/native/src/base/files.rs b/native/src/base/files.rs index e45f69795..9c384ce7c 100644 --- a/native/src/base/files.rs +++ b/native/src/base/files.rs @@ -1,8 +1,5 @@ use crate::cxx_extern::readlinkat_for_cxx; -use crate::{ - cstr, cstr_buf, errno, error, FsPath, FsPathBuf, LibcReturn, Utf8CStr, Utf8CStrBuf, - Utf8CStrBufArr, -}; +use crate::{cstr, cstr_buf, errno, error, FsPath, FsPathBuf, LibcReturn, Utf8CStr, Utf8CStrBuf}; use bytemuck::{bytes_of, bytes_of_mut, Pod}; use libc::{ c_uint, dirent, makedev, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY, @@ -142,7 +139,7 @@ impl WriteExt for T { pub struct FileAttr { pub st: libc::stat, #[cfg(feature = "selinux")] - pub con: Utf8CStrBufArr<128>, + pub con: crate::Utf8CStrBufArr<128>, } impl FileAttr { @@ -150,7 +147,7 @@ impl FileAttr { FileAttr { st: unsafe { mem::zeroed() }, #[cfg(feature = "selinux")] - con: Utf8CStrBufArr::new(), + con: crate::Utf8CStrBufArr::new(), } } @@ -189,7 +186,6 @@ impl FileAttr { } } -#[cfg(feature = "selinux")] const XATTR_NAME_SELINUX: &[u8] = b"security.selinux\0"; pub struct DirEntry<'a> { @@ -199,7 +195,7 @@ pub struct DirEntry<'a> { } impl DirEntry<'_> { - pub fn d_name(&self) -> &CStr { + pub fn name(&self) -> &CStr { unsafe { CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts( self.d_name.as_ptr().cast(), @@ -211,7 +207,7 @@ impl DirEntry<'_> { pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> { self.dir.path(buf)?; buf.push_str("/"); - buf.push_lossy(self.d_name().to_bytes()); + buf.push_lossy(self.name().to_bytes()); Ok(()) } @@ -267,7 +263,7 @@ impl DirEntry<'_> { } unsafe fn open_fd(&self, flags: i32) -> io::Result { - self.dir.open_raw_fd(self.d_name(), flags, 0) + self.dir.open_raw_fd(self.name(), flags, 0) } pub fn open_as_dir(&self) -> io::Result { @@ -295,6 +291,18 @@ impl DirEntry<'_> { self.path(&mut path)?; FsPath::from(&path).set_attr(attr) } + + pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> { + let mut path = cstr_buf::default(); + self.path(&mut path)?; + FsPath::from(&path).get_secontext(con) + } + + pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> { + let mut path = cstr_buf::default(); + self.path(&mut path)?; + FsPath::from(&path).set_secontext(con) + } } impl Deref for DirEntry<'_> { @@ -424,7 +432,7 @@ impl Directory { let mut src = e.open_as_file(O_RDONLY)?; let mut dest = unsafe { File::from_raw_fd(dir.open_raw_fd( - e.d_name(), + e.name(), O_WRONLY | O_CREAT | O_TRUNC, 0o777, )?) @@ -447,7 +455,7 @@ impl Directory { pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> { let dir_fd = self.as_raw_fd(); while let Some(ref e) = self.read()? { - if e.is_dir() && dir.contains_path(e.d_name()) { + if e.is_dir() && dir.contains_path(e.name()) { // Destination folder exists, needs recursive move let mut src = e.open_as_dir()?; let new_entry = DirEntry { @@ -621,7 +629,7 @@ impl FsPath { pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> { buf.clear(); unsafe { - let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr().cast(), buf.capacity() - 1) + let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1) .check_os_err()? as isize; *(buf.as_mut_ptr().offset(r) as *mut u8) = b'\0'; buf.set_len(r as usize); @@ -698,16 +706,7 @@ impl FsPath { libc::lstat(self.as_ptr(), &mut attr.st).as_os_err()?; #[cfg(feature = "selinux")] - { - let sz = libc::lgetxattr( - self.as_ptr(), - XATTR_NAME_SELINUX.as_ptr().cast(), - attr.con.as_mut_ptr().cast(), - attr.con.capacity(), - ) - .check_os_err()?; - attr.con.set_len((sz - 1) as usize); - } + self.get_secontext(&mut attr.con)?; } Ok(attr) } @@ -721,19 +720,43 @@ impl FsPath { #[cfg(feature = "selinux")] if !attr.con.is_empty() { - libc::lsetxattr( - self.as_ptr(), - XATTR_NAME_SELINUX.as_ptr().cast(), - attr.con.as_ptr().cast(), - attr.con.len() + 1, - 0, - ) - .as_os_err()?; + self.set_secontext(&attr.con)?; } } Ok(()) } + pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> { + unsafe { + let sz = libc::lgetxattr( + self.as_ptr(), + XATTR_NAME_SELINUX.as_ptr().cast(), + con.as_mut_ptr().cast(), + con.capacity(), + ) + .check_os_err()?; + if sz < 1 { + con.clear(); + } else { + con.set_len((sz - 1) as usize); + } + } + Ok(()) + } + + pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> { + unsafe { + libc::lsetxattr( + self.as_ptr(), + XATTR_NAME_SELINUX.as_ptr().cast(), + con.as_ptr().cast(), + con.len() + 1, + 0, + ) + .as_os_err() + } + } + pub fn copy_to(&self, path: &FsPath) -> io::Result<()> { let attr = self.get_attr()?; if attr.is_dir() { diff --git a/native/src/core/package.rs b/native/src/core/package.rs index 9cd59fd21..361b723a4 100644 --- a/native/src/core/package.rs +++ b/native/src/core/package.rs @@ -157,7 +157,7 @@ fn find_apk_path(pkg: &str, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> { if !e.is_dir() { return Ok(Skip); } - let name_bytes = e.d_name().to_bytes(); + let name_bytes = e.name().to_bytes(); if name_bytes.starts_with(pkg.as_bytes()) && name_bytes[pkg.len()] == b'-' { // Found the APK path, we can abort now e.path(buf)?; diff --git a/native/src/core/resetprop/persist.rs b/native/src/core/resetprop/persist.rs index fc3aad034..7600770df 100644 --- a/native/src/core/resetprop/persist.rs +++ b/native/src/core/resetprop/persist.rs @@ -162,7 +162,7 @@ pub fn persist_get_props(mut prop_cb: Pin<&mut PropCb>) { let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?; dir.pre_order_walk(|e| { if e.is_file() { - if let Ok(name) = Utf8CStr::from_cstr(e.d_name()) { + if let Ok(name) = Utf8CStr::from_cstr(e.name()) { if let Ok(mut value) = file_get_prop(name) { prop_cb.exec(name, Utf8CStr::from_string(&mut value)); } diff --git a/native/src/include/consts.rs b/native/src/include/consts.rs index 7853d06c4..029d1e0f0 100644 --- a/native/src/include/consts.rs +++ b/native/src/include/consts.rs @@ -25,3 +25,5 @@ pub const MODULEMNT: &str = concatcp!(INTERNAL_DIR, "/modules"); pub const WORKERDIR: &str = concatcp!(INTERNAL_DIR, "/worker"); pub const DEVICEDIR: &str = concatcp!(INTERNAL_DIR, "/device"); pub const PREINITDEV: &str = concatcp!(DEVICEDIR, "/preinit"); +pub const ROOTOVL: &str = concatcp!(INTERNAL_DIR, "/rootdir"); +pub const ROOTMNT: &str = concatcp!(ROOTOVL, "/.mount_list"); diff --git a/native/src/init/init.rs b/native/src/init/init.rs index accd868ac..5bb0defe6 100644 --- a/native/src/init/init.rs +++ b/native/src/init/init.rs @@ -18,6 +18,7 @@ impl MagiskInit { Self { preinit_dev: String::new(), mount_list: Vec::new(), + overlay_con: Vec::new(), argv, config: BootConfig { skip_initramfs: false, diff --git a/native/src/init/lib.rs b/native/src/init/lib.rs index a14f45c7b..4a598a966 100644 --- a/native/src/init/lib.rs +++ b/native/src/init/lib.rs @@ -7,8 +7,10 @@ use logging::setup_klog; // Has to be pub so all symbols in that crate is included pub use magiskpolicy; use mount::{is_device_mounted, switch_root}; -use rootdir::{collect_overlay_contexts, inject_magisk_rc, reset_overlay_contexts}; +use rootdir::{inject_magisk_rc, OverlayAttr}; +#[path = "../include/consts.rs"] +mod consts; mod getinfo; mod init; mod logging; @@ -43,6 +45,7 @@ pub mod ffi { mount_list: Vec, argv: *mut *mut c_char, config: BootConfig, + overlay_con: Vec, } unsafe extern "C++" { @@ -62,8 +65,6 @@ pub mod ffi { fn inject_magisk_rc(fd: i32, tmp_dir: Utf8CStrRef); fn switch_root(path: Utf8CStrRef); fn is_device_mounted(dev: u64, target: Pin<&mut CxxString>) -> bool; - fn collect_overlay_contexts(src: Utf8CStrRef); - fn reset_overlay_contexts(); } // BootConfig @@ -78,8 +79,11 @@ pub mod ffi { // MagiskInit extern "Rust" { + type OverlayAttr; fn patch_sepolicy(self: &MagiskInit, src: Utf8CStrRef, out: Utf8CStrRef); fn parse_config_file(self: &mut MagiskInit); + fn mount_overlay(self: &mut MagiskInit, dest: Utf8CStrRef); + fn restore_overlay_contexts(self: &MagiskInit); } unsafe extern "C++" { // Used in Rust @@ -93,5 +97,6 @@ pub mod ffi { fn mount_preinit_dir(self: &MagiskInit); unsafe fn find_block(self: &MagiskInit, partname: *const c_char) -> u64; fn hijack_sepolicy(self: &mut MagiskInit) -> bool; + unsafe fn patch_fissiond(self: &mut MagiskInit, tmp_path: *const c_char); } } diff --git a/native/src/init/rootdir.cpp b/native/src/init/rootdir.cpp index 93446614f..7add415b6 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -13,7 +13,6 @@ using namespace std; static vector rc_list; -static string magic_mount_list; #define NEW_INITRC_DIR "/system/etc/init/hw" #define INIT_RC "init.rc" @@ -43,33 +42,10 @@ static bool unxz(out_stream &strm, rust::Slice bytes) { return true; } -static void magic_mount(const string &sdir, const string &ddir = "") { - auto dir = xopen_dir(sdir.data()); - if (!dir) return; - for (dirent *entry; (entry = xreaddir(dir.get()));) { - string src = sdir + "/" + entry->d_name; - string dest = ddir + "/" + entry->d_name; - if (access(dest.data(), F_OK) == 0) { - if (entry->d_type == DT_DIR) { - // Recursive - magic_mount(src, dest); - } else { - LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data()); - struct stat st; - xstat(dest.data(), &st); - chmod(src.data(), st.st_mode & 0777); - chown(src.data(), st.st_uid, st.st_gid); - xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr); - magic_mount_list += dest; - magic_mount_list += '\n'; - } - } - } -} - -static void patch_rc_scripts(const char *src_path, const char *tmp_path, bool writable) { +// When return true, run patch_fissiond +static bool patch_rc_scripts(const char *src_path, const char *tmp_path, bool writable) { auto src_dir = xopen_dir(src_path); - if (!src_dir) return; + if (!src_dir) return false; int src_fd = dirfd(src_dir.get()); // If writable, directly modify the file in src_path, or else add to rootfs overlay @@ -81,17 +57,17 @@ static void patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr xmkdirs(buf, 0755); return xopen_dir(buf); }(); - if (!dest_dir) return; + if (!dest_dir) return false; int dest_fd = dirfd(dest_dir.get()); // First patch init.rc { auto src = xopen_file(xopenat(src_fd, INIT_RC, O_RDONLY | O_CLOEXEC, 0), "re"); - if (!src) return; + if (!src) return false; if (writable) unlinkat(src_fd, INIT_RC, 0); auto dest = xopen_file( xopenat(dest_fd, INIT_RC, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we"); - if (!dest) return; + if (!dest) return false; LOGD("Patching " INIT_RC " in %s\n", src_path); file_readline(false, src.get(), [&dest](string_view line) -> bool { // Do not start vaultkeeper @@ -157,42 +133,46 @@ static void patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr fclone_attr(fileno(src.get()), fileno(dest.get())); } - if (faccessat(src_fd, "init.fission_host.rc", F_OK, 0) == 0) { + return faccessat(src_fd, "init.fission_host.rc", F_OK, 0) == 0; +} + +void MagiskInit::patch_fissiond(const char *tmp_path) noexcept { + { + LOGD("Patching fissiond\n"); + mmap_data fissiond("/system/bin/fissiond", false); + for (size_t off : fissiond.patch( + "ro.build.system.fission_single_os", + "ro.build.system.xxxxxxxxxxxxxxxxx")) { - LOGD("Patching fissiond\n"); - mmap_data fissiond("/system/bin/fissiond", false); - for (size_t off : fissiond.patch("ro.build.system.fission_single_os", "ro.build.system.xxxxxxxxxxxxxxxxx")) { - LOGD("Patch @ %08zX [ro.build.system.fission_single_os] -> [ro.build.system.xxxxxxxxxxxxxxxxx]\n", off); - } - mkdirs(ROOTOVL "/system/bin", 0755); - if (auto target_fissiond = xopen_file(ROOTOVL "/system/bin/fissiond", "we")) { - fwrite(fissiond.buf(), 1, fissiond.sz(), target_fissiond.get()); - clone_attr("/system/bin/fissiond", ROOTOVL "/system/bin/fissiond"); - } + LOGD("Patch @ %08zX [ro.build.system.fission_single_os] -> " + "[ro.build.system.xxxxxxxxxxxxxxxxx]\n", off); } - LOGD("hijack isolated\n"); - auto hijack = xopen_file("/sys/devices/system/cpu/isolated", "re"); - mkfifo(INTLROOT "/isolated", 0777); - xmount(INTLROOT "/isolated", "/sys/devices/system/cpu/isolated", nullptr, MS_BIND, nullptr); - if (!xfork()) { - auto dest = xopen_file(INTLROOT "/isolated", "we"); - LOGD("hijacked isolated\n"); - xumount2("/sys/devices/system/cpu/isolated", MNT_DETACH); - unlink(INTLROOT "/isolated"); - string content; - full_read(fileno(hijack.get()), content); - { - string target = "/dev/cells/cell2"s + tmp_path; - xmkdirs(target.data(), 0); - xmount(tmp_path, target.data(), nullptr, MS_BIND | MS_REC,nullptr); - magic_mount(ROOTOVL, "/dev/cells/cell2"); - auto mount = xopen_file(ROOTMNT, "w"); - fwrite(magic_mount_list.data(), 1, magic_mount_list.length(), mount.get()); - } - fprintf(dest.get(), "%s", content.data()); - exit(0); + mkdirs(ROOTOVL "/system/bin", 0755); + if (auto target_fissiond = xopen_file(ROOTOVL "/system/bin/fissiond", "we")) { + fwrite(fissiond.buf(), 1, fissiond.sz(), target_fissiond.get()); + clone_attr("/system/bin/fissiond", ROOTOVL "/system/bin/fissiond"); } } + LOGD("hijack isolated\n"); + auto hijack = xopen_file("/sys/devices/system/cpu/isolated", "re"); + mkfifo(INTLROOT "/isolated", 0777); + xmount(INTLROOT "/isolated", "/sys/devices/system/cpu/isolated", nullptr, MS_BIND, nullptr); + if (!xfork()) { + auto dest = xopen_file(INTLROOT "/isolated", "we"); + LOGD("hijacked isolated\n"); + xumount2("/sys/devices/system/cpu/isolated", MNT_DETACH); + unlink(INTLROOT "/isolated"); + string content; + full_read(fileno(hijack.get()), content); + { + string target = "/dev/cells/cell2"s + tmp_path; + xmkdirs(target.data(), 0); + xmount(tmp_path, target.data(), nullptr, MS_BIND | MS_REC, nullptr); + mount_overlay("/dev/cells/cell2"); + } + fprintf(dest.get(), "%s", content.data()); + exit(0); + } } static void load_overlay_rc(const char *overlay) { @@ -337,18 +317,18 @@ void MagiskInit::patch_ro_root() noexcept { } // Patch init.rc + bool p; if (access(NEW_INITRC_DIR "/" INIT_RC, F_OK) == 0) { // Android 11's new init.rc - patch_rc_scripts(NEW_INITRC_DIR, tmp_dir.data(), false); + p = patch_rc_scripts(NEW_INITRC_DIR, tmp_dir.data(), false); } else { - patch_rc_scripts("/", tmp_dir.data(), false); + p = patch_rc_scripts("/", tmp_dir.data(), false); } + if (p) patch_fissiond(tmp_dir.data()); // Extract overlay archives extract_files(false); - rust::collect_overlay_contexts(ROOTOVL); - // Oculus Go will use a special sepolicy if unlocked if (access("/sepolicy.unlocked", F_OK) == 0) { patch_sepolicy("/sepolicy.unlocked", ROOTOVL "/sepolicy.unlocked"); @@ -361,10 +341,7 @@ void MagiskInit::patch_ro_root() noexcept { unlink("init-ld"); // Mount rootdir - magic_mount(ROOTOVL); - int dest = xopen(ROOTMNT, O_WRONLY | O_CREAT, 0); - write(dest, magic_mount_list.data(), magic_mount_list.length()); - close(dest); + mount_overlay("/"); chdir("/"); } @@ -388,7 +365,8 @@ void MagiskInit::patch_rw_root() noexcept { rm_rf("/.backup"); // Patch init.rc - patch_rc_scripts("/", "/sbin", true); + if (patch_rc_scripts("/", "/sbin", true)) + patch_fissiond("/sbin"); bool treble; { diff --git a/native/src/init/rootdir.rs b/native/src/init/rootdir.rs index 80ed1b436..92244d56a 100644 --- a/native/src/init/rootdir.rs +++ b/native/src/init/rootdir.rs @@ -1,8 +1,9 @@ +use crate::consts::{ROOTMNT, ROOTOVL}; use crate::ffi::MagiskInit; -use base::libc::O_RDONLY; +use base::libc::{O_CREAT, O_RDONLY, O_WRONLY}; use base::{ - cstr_buf, debug, libc, path, BufReadExt, Directory, LibcReturn, LoggedResult, ResultExt, - Utf8CStr, Utf8CStrBuf, Utf8CStrBufArr, WalkResult, + clone_attr, cstr, cstr_buf, debug, libc, path, BufReadExt, Directory, FsPath, FsPathBuf, + LibcReturn, LoggedResult, ResultExt, Utf8CStr, Utf8CString, }; use std::io::BufReader; use std::{ @@ -10,80 +11,9 @@ use std::{ io::Write, mem, os::fd::{FromRawFd, RawFd}, - sync::OnceLock, + ptr, }; -pub static OVERLAY_ATTRS: OnceLock> = OnceLock::new(); - -const XATTR_NAME_SELINUX: &[u8] = b"security.selinux\0"; - -fn get_context(path: &str, con: &mut Utf8CStrBufArr) -> std::io::Result<()> { - unsafe { - let sz = libc::lgetxattr( - path.as_ptr().cast(), - XATTR_NAME_SELINUX.as_ptr().cast(), - con.as_mut_ptr().cast(), - con.capacity(), - ) - .check_os_err()?; - con.set_len((sz - 1) as usize); - } - Ok(()) -} - -fn set_context(path: &str, con: &str) -> std::io::Result<()> { - unsafe { - libc::lsetxattr( - path.as_ptr().cast(), - XATTR_NAME_SELINUX.as_ptr().cast(), - con.as_ptr().cast(), - con.len() + 1, - 0, - ) - .as_os_err() - } -} - -pub fn collect_overlay_contexts(src: &Utf8CStr) { - OVERLAY_ATTRS - .get_or_try_init(|| -> LoggedResult<_> { - let mut contexts = vec![]; - let mut con = cstr_buf::default(); - let mut path = cstr_buf::default(); - let mut src = Directory::open(src)?; - src.path(&mut path)?; - let src_len = path.len(); - src.post_order_walk(|f| { - f.path(&mut path)?; - - let path = &path[src_len..]; - if get_context(path, &mut con) - .log_with_msg(|w| w.write_fmt(format_args!("collect context {}", path))) - .is_ok() - { - debug!("collect context: {:?} -> {:?}", path, con); - contexts.push((path.to_string(), con.to_string())); - } - - Ok(WalkResult::Continue) - })?; - Ok(contexts) - }) - .ok(); -} - -pub fn reset_overlay_contexts() { - OVERLAY_ATTRS.get().map(|attrs| { - for (path, con) in attrs.iter() { - debug!("set context: {} -> {}", path, con); - set_context(path, con) - .log_with_msg(|w| w.write_fmt(format_args!("reset context {}", path))) - .ok(); - } - Some(()) - }); -} - pub fn inject_magisk_rc(fd: RawFd, tmp_dir: &Utf8CStr) { debug!("Injecting magisk rc"); @@ -114,6 +44,8 @@ on property:init.svc.zygote=stopped mem::forget(file) } +pub struct OverlayAttr(Utf8CString, Utf8CString); + impl MagiskInit { pub(crate) fn parse_config_file(&mut self) { if let Ok(fd) = path!("/data/.backup/.magisk").open(O_RDONLY) { @@ -127,4 +59,64 @@ impl MagiskInit { }) } } + + fn mount_impl( + &mut self, + src_dir: &Utf8CStr, + dest_dir: &Utf8CStr, + mount_list: &mut String, + ) -> LoggedResult<()> { + let mut dir = Directory::open(src_dir)?; + let mut con = cstr_buf::default(); + loop { + match &dir.read()? { + None => return Ok(()), + Some(e) => { + let name = e.name().to_str()?; + let src = FsPathBuf::new_dynamic(256).join(src_dir).join(name); + let dest = FsPathBuf::new_dynamic(256).join(dest_dir).join(name); + if dest.exists() { + if e.is_dir() { + // Recursive + self.mount_impl(&src, &dest, mount_list)?; + } else { + debug!("Mount [{}] -> [{}]", src, dest); + clone_attr(&dest, &src)?; + dest.get_secontext(&mut con)?; + unsafe { + libc::mount( + src.as_ptr(), + dest.as_ptr(), + ptr::null(), + libc::MS_BIND, + ptr::null(), + ) + .as_os_err()?; + }; + self.overlay_con + .push(OverlayAttr(dest.to_owned(), con.to_owned())); + mount_list.push_str(dest.as_str()); + mount_list.push('\n'); + } + } + } + } + } + } + + pub(crate) fn mount_overlay(&mut self, dest: &Utf8CStr) { + let mut mount_list = String::new(); + self.mount_impl(cstr!(ROOTOVL), dest, &mut mount_list) + .log_ok(); + if let Ok(mut fd) = path!(ROOTMNT).create(O_CREAT | O_WRONLY, 0) { + fd.write(mount_list.as_bytes()).log_ok(); + } + } + + pub(crate) fn restore_overlay_contexts(&self) { + self.overlay_con.iter().for_each(|attr| { + let OverlayAttr(path, con) = attr; + FsPath::from(path).set_secontext(con).log_ok(); + }) + } } diff --git a/native/src/init/selinux.cpp b/native/src/init/selinux.cpp index ddee912ca..91373bd4b 100644 --- a/native/src/init/selinux.cpp +++ b/native/src/init/selinux.cpp @@ -111,7 +111,7 @@ bool MagiskInit::hijack_sepolicy() noexcept { sepol.to_file(SELINUX_LOAD); // restore mounted files' context after sepolicy loaded - rust::reset_overlay_contexts(); + restore_overlay_contexts(); // Write to the enforce node ONLY after sepolicy is loaded. We need to make sure // the actual init process is blocked until sepolicy is loaded, or else