From ef4e23025816c73b394b05a98e7d45e43dbf96eb Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 8 Sep 2025 23:59:29 -0700 Subject: [PATCH] Use nix for libc functions --- native/src/Cargo.lock | 26 +++ native/src/Cargo.toml | 1 + native/src/base/Cargo.toml | 1 + native/src/base/cstr.rs | 21 +++ native/src/base/cxx_extern.rs | 8 +- native/src/base/dir.rs | 172 ++++++++----------- native/src/base/files.rs | 237 ++++++++++++--------------- native/src/base/lib.rs | 1 + native/src/base/result.rs | 57 ++++--- native/src/base/xwrap.rs | 6 +- native/src/boot/cli.rs | 4 +- native/src/boot/compress.rs | 14 +- native/src/boot/cpio.rs | 27 +-- native/src/core/Cargo.toml | 1 + native/src/core/daemon.rs | 16 +- native/src/core/module.rs | 46 +++--- native/src/core/mount.rs | 5 +- native/src/core/package.rs | 16 +- native/src/core/resetprop/cli.rs | 5 +- native/src/core/resetprop/persist.rs | 7 +- native/src/core/selinux.rs | 4 +- native/src/core/su/connect.rs | 29 ++-- native/src/core/su/pts.rs | 142 +++++++--------- native/src/core/zygisk/daemon.rs | 7 +- native/src/init/init.rs | 4 +- native/src/init/logging.rs | 25 ++- native/src/init/mount.rs | 6 +- native/src/init/rootdir.rs | 6 +- native/src/init/selinux.rs | 23 +-- native/src/init/twostage.rs | 10 +- native/src/sepolicy/statement.rs | 5 +- 31 files changed, 457 insertions(+), 475 deletions(-) diff --git a/native/src/Cargo.lock b/native/src/Cargo.lock index 8fb184602..8d15e957f 100644 --- a/native/src/Cargo.lock +++ b/native/src/Cargo.lock @@ -60,6 +60,7 @@ dependencies = [ "cxx", "cxx-gen", "libc", + "nix", "num-derive", "num-traits", "thiserror", @@ -92,6 +93,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + [[package]] name = "block-buffer" version = "0.10.4" @@ -167,6 +174,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clap" version = "4.5.47" @@ -634,6 +647,7 @@ dependencies = [ "cxx", "cxx-gen", "derive", + "nix", "num-derive", "num-traits", "pb-rs", @@ -713,6 +727,18 @@ dependencies = [ "adler2", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" diff --git a/native/src/Cargo.toml b/native/src/Cargo.toml index f09a349c1..4890c64d2 100644 --- a/native/src/Cargo.toml +++ b/native/src/Cargo.toml @@ -32,6 +32,7 @@ bzip2 = { version = "0.6.0" } zopfli = "0.8.2" lz4 = "1.28.1" lzma-rust2 = { version = "0.13.0", default-features = false, features = ["xz", "std", "encoder", "optimization"] } +nix = "0.30.1" # Rust crypto crates are tied together sha1 = "0.11.0-rc.2" diff --git a/native/src/base/Cargo.toml b/native/src/base/Cargo.toml index 5a1c843d5..14223a3aa 100644 --- a/native/src/base/Cargo.toml +++ b/native/src/base/Cargo.toml @@ -23,3 +23,4 @@ bytemuck = { workspace = true } num-traits = { workspace = true } num-derive = { workspace = true } const_format = { workspace = true } +nix = { workspace = true, features = ["fs"] } \ No newline at end of file diff --git a/native/src/base/cstr.rs b/native/src/base/cstr.rs index 5152d18f4..e208cb3f3 100644 --- a/native/src/base/cstr.rs +++ b/native/src/base/cstr.rs @@ -1,5 +1,6 @@ use cxx::{ExternType, type_id}; use libc::c_char; +use nix::NixPath; use std::borrow::Borrow; use std::cmp::{Ordering, min}; use std::ffi::{CStr, FromBytesWithNulError, OsStr}; @@ -367,6 +368,26 @@ impl AsRef for Utf8CStr { } } +impl NixPath for Utf8CStr { + #[inline(always)] + fn is_empty(&self) -> bool { + self.as_str().is_empty() + } + + #[inline(always)] + fn len(&self) -> usize { + self.as_str().len() + } + + #[inline(always)] + fn with_nix_path(&self, f: F) -> nix::Result + where + F: FnOnce(&CStr) -> T, + { + Ok(f(self.as_cstr())) + } +} + // Notice that we only implement ExternType on Utf8CStr *reference* unsafe impl ExternType for &Utf8CStr { type Id = type_id!("Utf8CStr"); diff --git a/native/src/base/cxx_extern.rs b/native/src/base/cxx_extern.rs index 0c45fcb0d..d7a833c49 100644 --- a/native/src/base/cxx_extern.rs +++ b/native/src/base/cxx_extern.rs @@ -6,9 +6,6 @@ use std::mem::ManuallyDrop; use std::ops::DerefMut; use std::os::fd::{BorrowedFd, FromRawFd, OwnedFd, RawFd}; -use cfg_if::cfg_if; -use libc::{O_RDONLY, c_char, mode_t}; - use crate::ffi::{FnBoolStr, FnBoolStrStr}; use crate::files::map_file_at; pub(crate) use crate::xwrap::*; @@ -16,6 +13,9 @@ use crate::{ BufReadExt, Directory, LoggedResult, ResultExt, Utf8CStr, clone_attr, cstr, fclone_attr, map_fd, map_file, slice_from_ptr, }; +use cfg_if::cfg_if; +use libc::{c_char, mode_t}; +use nix::fcntl::OFlag; #[unsafe(no_mangle)] unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { @@ -178,7 +178,7 @@ unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize { } pub(crate) fn parse_prop_file_rs(name: &Utf8CStr, f: &FnBoolStrStr) { - if let Ok(file) = name.open(O_RDONLY) { + if let Ok(file) = name.open(OFlag::O_RDONLY) { BufReader::new(file).for_each_prop(|key, value| f.call(key, value)) } } diff --git a/native/src/base/dir.rs b/native/src/base/dir.rs index 4babf6500..60908db3e 100644 --- a/native/src/base/dir.rs +++ b/native/src/base/dir.rs @@ -3,16 +3,16 @@ use crate::{ FsPathBuilder, LibcReturn, LoggedResult, OsError, OsResult, Utf8CStr, Utf8CStrBuf, cstr, errno, fd_path, fd_set_attr, }; -use libc::{EEXIST, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, dirent, mode_t}; +use libc::{dirent, mode_t}; +use nix::{errno::Errno, fcntl::AtFlags, fcntl::OFlag, sys::stat::Mode, unistd::UnlinkatFlags}; use std::fs::File; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use std::ops::Deref; +use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd}; use std::ptr::NonNull; -use std::{mem, slice}; +use std::slice; pub struct DirEntry<'a> { - dir: BorrowedDirectory<'a>, + dir: &'a Directory, entry: NonNull, d_name_len: usize, } @@ -23,6 +23,7 @@ impl DirEntry<'_> { } pub fn name(&self) -> &Utf8CStr { + // SAFETY: Utf8CStr is already validated in Directory::read unsafe { Utf8CStr::from_bytes_unchecked(slice::from_raw_parts( self.d_name.as_ptr().cast(), @@ -64,7 +65,11 @@ impl DirEntry<'_> { } pub fn unlink(&self) -> OsResult<'_, ()> { - let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 }; + let flag = if self.is_dir() { + UnlinkatFlags::RemoveDir + } else { + UnlinkatFlags::NoRemoveDir + }; self.dir.unlink_at(self.name(), flag) } @@ -75,7 +80,7 @@ impl DirEntry<'_> { pub fn open_as_dir(&self) -> OsResult<'_, Directory> { if !self.is_dir() { return Err(OsError::new( - libc::ENOTDIR, + Errno::ENOTDIR, "fdopendir", Some(self.name()), None, @@ -84,10 +89,10 @@ impl DirEntry<'_> { self.dir.open_as_dir_at(self.name()) } - pub fn open_as_file(&self, flags: i32) -> OsResult<'_, File> { + pub fn open_as_file(&self, flags: OFlag) -> OsResult<'_, File> { if self.is_dir() { return Err(OsError::new( - libc::EISDIR, + Errno::EISDIR, "open_as_file", Some(self.name()), None, @@ -95,6 +100,14 @@ impl DirEntry<'_> { } self.dir.open_as_file_at(self.name(), flags, 0) } + + pub fn rename_to<'a, 'entry: 'a>( + &'entry self, + new_dir: impl AsFd, + path: &'a Utf8CStr, + ) -> OsResult<'a, ()> { + self.dir.rename_at(self.name(), new_dir, path) + } } impl Deref for DirEntry<'_> { @@ -110,30 +123,6 @@ pub struct Directory { inner: NonNull, } -#[repr(transparent)] -pub struct BorrowedDirectory<'a> { - inner: NonNull, - phantom: PhantomData<&'a Directory>, -} - -impl Deref for BorrowedDirectory<'_> { - type Target = Directory; - - fn deref(&self) -> &Directory { - // SAFETY: layout of NonNull is the same as Directory - // SAFETY: the lifetime of the raw pointer is tracked in the PhantomData - unsafe { mem::transmute(&self.inner) } - } -} - -impl DerefMut for BorrowedDirectory<'_> { - fn deref_mut(&mut self) -> &mut Directory { - // SAFETY: layout of NonNull is the same as Directory - // SAFETY: the lifetime of the raw pointer is tracked in the PhantomData - unsafe { mem::transmute(&mut self.inner) } - } -} - pub enum WalkResult { Continue, Abort, @@ -141,19 +130,14 @@ pub enum WalkResult { } impl Directory { - fn borrow(&self) -> BorrowedDirectory<'_> { - BorrowedDirectory { - inner: self.inner, - phantom: PhantomData, - } - } - - fn openat<'a>(&self, name: &'a Utf8CStr, flags: i32, mode: u32) -> OsResult<'a, OwnedFd> { - unsafe { - libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode) - .into_os_result("openat", Some(name), None) - .map(|fd| OwnedFd::from_raw_fd(fd)) - } + fn open_at<'a>(&self, name: &'a Utf8CStr, flags: OFlag, mode: mode_t) -> OsResult<'a, OwnedFd> { + nix::fcntl::openat( + self, + name, + flags | OFlag::O_CLOEXEC, + Mode::from_bits_truncate(mode), + ) + .into_os_result("openat", Some(name), None) } fn path_at(&self, name: &Utf8CStr, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> { @@ -193,7 +177,7 @@ impl Directory { self.read() } else { let e = DirEntry { - dir: self.borrow(), + dir: self, entry: NonNull::from(entry), d_name_len: name.as_bytes_with_nul().len(), }; @@ -207,17 +191,17 @@ impl Directory { } pub fn open_as_dir_at<'a>(&self, name: &'a Utf8CStr) -> OsResult<'a, Directory> { - let fd = self.openat(name, O_RDONLY, 0)?; + let fd = self.open_at(name, OFlag::O_RDONLY, 0)?; Directory::try_from(fd).map_err(|e| e.set_args(Some(name), None)) } pub fn open_as_file_at<'a>( &self, name: &'a Utf8CStr, - flags: i32, - mode: u32, + flags: OFlag, + mode: mode_t, ) -> OsResult<'a, File> { - let fd = self.openat(name, flags, mode)?; + let fd = self.open_at(name, flags, mode)?; Ok(File::from(fd)) } @@ -241,14 +225,10 @@ impl Directory { } pub fn mkdir_at<'a>(&self, name: &'a Utf8CStr, mode: mode_t) -> OsResult<'a, ()> { - unsafe { - if libc::mkdirat(self.as_raw_fd(), name.as_ptr(), mode as mode_t) < 0 - && *errno() != EEXIST - { - return Err(OsError::last_os_error("mkdirat", Some(name), None)); - } + match nix::sys::stat::mkdirat(self, name, Mode::from_bits_truncate(mode)) { + Ok(_) | Err(Errno::EEXIST) => Ok(()), + Err(e) => Err(OsError::new(e, "mkdirat", Some(name), None)), } - Ok(()) } // ln -s target self/name @@ -257,44 +237,36 @@ impl Directory { name: &'a Utf8CStr, target: &'a Utf8CStr, ) -> OsResult<'a, ()> { - unsafe { - libc::symlinkat(target.as_ptr(), self.as_raw_fd(), name.as_ptr()).check_os_err( - "symlinkat", - Some(target), - Some(name), - ) - } + nix::unistd::symlinkat(target, self, name).check_os_err( + "symlinkat", + Some(target), + Some(name), + ) } - pub fn unlink_at<'a>(&self, name: &'a Utf8CStr, flag: i32) -> OsResult<'a, ()> { - unsafe { - libc::unlinkat(self.as_raw_fd(), name.as_ptr(), flag).check_os_err( - "unlinkat", - Some(name), - None, - )?; - } - Ok(()) + pub fn unlink_at<'a>(&self, name: &'a Utf8CStr, flag: UnlinkatFlags) -> OsResult<'a, ()> { + nix::unistd::unlinkat(self, name, flag).check_os_err("unlinkat", Some(name), None) } pub fn contains_path(&self, path: &Utf8CStr) -> bool { // WARNING: Using faccessat is incorrect, because the raw linux kernel syscall // does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2. // Use fstatat to check the existence of a file instead. - unsafe { - let mut st: libc::stat = mem::zeroed(); - libc::fstatat( - self.as_raw_fd(), - path.as_ptr(), - &mut st, - libc::AT_SYMLINK_NOFOLLOW, - ) == 0 - } + nix::sys::stat::fstatat(self, path, AtFlags::AT_SYMLINK_NOFOLLOW).is_ok() } pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> { fd_path(self.as_raw_fd(), buf) } + + pub fn rename_at<'a>( + &self, + old: &'a Utf8CStr, + new_dir: impl AsFd, + new: &'a Utf8CStr, + ) -> OsResult<'a, ()> { + nix::fcntl::renameat(self, old, new_dir, new).check_os_err("renameat", Some(old), Some(new)) + } } // High-level helper methods, composed of multiple operations. @@ -328,7 +300,6 @@ impl Directory { } pub fn move_into(&mut self, dir: &Directory) -> LoggedResult<()> { - let dir_fd = self.as_raw_fd(); while let Some(ref e) = self.read()? { if e.is_dir() && dir.contains_path(e.name()) { // Destination folder exists, needs recursive move @@ -337,16 +308,7 @@ impl Directory { src.move_into(&dest)?; return Ok(e.unlink()?); } - - unsafe { - libc::renameat( - dir_fd, - e.d_name.as_ptr(), - dir.as_raw_fd(), - e.d_name.as_ptr(), - ) - .check_os_err("renameat", Some(e.name()), None)?; - } + e.rename_to(dir, e.name())?; } Ok(()) } @@ -422,9 +384,12 @@ impl Directory { src.copy_into_impl(&dest, buf)?; fd_set_attr(dest.as_raw_fd(), &attr)?; } else if e.is_file() { - let mut src = e.open_as_file(O_RDONLY)?; - let mut dest = - dest_dir.open_as_file_at(e.name(), O_WRONLY | O_CREAT | O_TRUNC, 0o777)?; + let mut src = e.open_as_file(OFlag::O_RDONLY)?; + let mut dest = dest_dir.open_as_file_at( + e.name(), + OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC, + 0o777, + )?; std::io::copy(&mut src, &mut dest)?; fd_set_attr(dest.as_raw_fd(), &attr)?; } else if e.is_symlink() { @@ -442,7 +407,6 @@ impl Directory { dest_dir: &Directory, buf: &mut dyn Utf8CStrBuf, ) -> LoggedResult<()> { - let dir_fd = self.as_raw_fd(); while let Some(ref e) = self.read()? { if e.is_dir() { dest_dir.mkdir_at(e.name(), 0o777)?; @@ -453,16 +417,8 @@ impl Directory { src.link_into_impl(&dest, buf)?; fd_set_attr(dest.as_raw_fd(), &attr)?; } else { - unsafe { - libc::linkat( - dir_fd, - e.d_name.as_ptr(), - dest_dir.as_raw_fd(), - e.d_name.as_ptr(), - 0, - ) + nix::unistd::linkat(e.dir, e.name(), dest_dir, e.name(), AtFlags::empty()) .check_os_err("linkat", Some(e.name()), None)?; - } } } Ok(()) diff --git a/native/src/base/files.rs b/native/src/base/files.rs index 2a24ce12c..2966d2ddb 100644 --- a/native/src/base/files.rs +++ b/native/src/base/files.rs @@ -3,20 +3,24 @@ use crate::{ cstr, errno, error, }; use bytemuck::{Pod, bytes_of, bytes_of_mut}; -use libc::{ - EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, c_uint, - makedev, mode_t, stat, -}; +use libc::{c_uint, makedev, mode_t}; use mem::MaybeUninit; +use nix::{ + errno::Errno, + fcntl::{AT_FDCWD, OFlag}, + sys::stat::{FchmodatFlags, Mode}, + unistd::AccessFlags, +}; use num_traits::AsPrimitive; use std::cmp::min; use std::ffi::CStr; use std::fmt::Display; use std::fs::File; use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write}; +use std::ops::Deref; use std::os::fd::{AsFd, BorrowedFd}; use std::os::unix::ffi::OsStrExt; -use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd}; +use std::os::unix::io::{AsRawFd, OwnedFd, RawFd}; use std::path::Path; use std::{io, mem, ptr, slice}; @@ -139,15 +143,12 @@ impl FileOrStd { } } -fn open_fd(path: &Utf8CStr, flags: i32, mode: mode_t) -> OsResult<'_, OwnedFd> { - unsafe { - let fd = libc::open(path.as_ptr(), flags, mode as c_uint).into_os_result( - "open", - Some(path), - None, - )?; - Ok(OwnedFd::from_raw_fd(fd)) - } +fn open_fd(path: &Utf8CStr, flags: OFlag, mode: mode_t) -> OsResult<'_, OwnedFd> { + nix::fcntl::open(path, flags, Mode::from_bits_truncate(mode)).into_os_result( + "open", + Some(path), + None, + ) } pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> { @@ -225,29 +226,24 @@ impl Utf8CStr { unsafe { mem::transmute(self) } } - pub fn open(&self, flags: i32) -> OsResult<'_, File> { + pub fn open(&self, flags: OFlag) -> OsResult<'_, File> { Ok(File::from(open_fd(self, flags, 0)?)) } - pub fn create(&self, flags: i32, mode: mode_t) -> OsResult<'_, File> { - Ok(File::from(open_fd(self, O_CREAT | flags, mode)?)) + pub fn create(&self, flags: OFlag, mode: mode_t) -> OsResult<'_, File> { + Ok(File::from(open_fd(self, OFlag::O_CREAT | flags, mode)?)) } pub fn exists(&self) -> bool { - unsafe { - let mut st: stat = mem::zeroed(); - libc::lstat(self.as_ptr(), &mut st) == 0 - } + nix::sys::stat::lstat(self).is_ok() } pub fn rename_to<'a>(&'a self, name: &'a Utf8CStr) -> OsResult<'a, ()> { - unsafe { - libc::rename(self.as_ptr(), name.as_ptr()).check_os_err( - "rename", - Some(self), - Some(name), - ) - } + nix::fcntl::renameat(AT_FDCWD, self, AT_FDCWD, name).check_os_err( + "rename", + Some(self), + Some(name), + ) } pub fn remove(&self) -> OsResult<'_, ()> { @@ -267,73 +263,74 @@ impl Utf8CStr { } pub fn mkdir(&self, mode: mode_t) -> OsResult<'_, ()> { - unsafe { - if libc::mkdir(self.as_ptr(), mode) < 0 { - if *errno() == EEXIST { - libc::chmod(self.as_ptr(), mode).check_os_err("chmod", Some(self), None)?; - } else { - return Err(OsError::last_os_error("mkdir", Some(self), None)); - } - } + match nix::unistd::mkdir(self, Mode::from_bits_truncate(mode)) { + Ok(_) | Err(Errno::EEXIST) => Ok(()), + Err(e) => Err(OsError::new(e, "mkdir", Some(self), None)), } - Ok(()) } // Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> { - let fd = self.open(O_PATH | O_CLOEXEC)?; - let mut st1: libc::stat; - let mut st2: libc::stat; + let fd = self.open(OFlag::O_PATH | OFlag::O_CLOEXEC)?; let mut skip_check = false; - unsafe { - st1 = mem::zeroed(); - if libc::fstat(fd.as_raw_fd(), &mut st1) < 0 { + + let st1 = match nix::sys::stat::fstat(&fd) { + Ok(st) => st, + Err(_) => { // This will only fail on Linux < 3.6 skip_check = true; + unsafe { mem::zeroed() } } - } + }; + fd_path(fd.as_raw_fd(), buf)?; - unsafe { - st2 = mem::zeroed(); - libc::stat(buf.as_ptr(), &mut st2).check_os_err("stat", Some(self), None)?; - if !skip_check && (st2.st_dev != st1.st_dev || st2.st_ino != st1.st_ino) { - *errno() = ENOENT; - return Err(OsError::last_os_error("realpath", Some(self), None)); - } + + let st2 = nix::sys::stat::stat(buf.as_cstr()).into_os_result("stat", Some(self), None)?; + if !skip_check && (st2.st_dev != st1.st_dev || st2.st_ino != st1.st_ino) { + return Err(OsError::new(Errno::ENOENT, "realpath", Some(self), None)); } Ok(()) } pub fn get_attr(&self) -> OsResult<'_, FileAttr> { - let mut attr = FileAttr::new(); - unsafe { - libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?; - + #[allow(unused_mut)] + let mut attr = FileAttr { + st: nix::sys::stat::lstat(self).into_os_result("lstat", Some(self), None)?, #[cfg(feature = "selinux")] - self.get_secontext(&mut attr.con)?; - } + con: cstr::buf::new(), + }; + #[cfg(feature = "selinux")] + self.get_secontext(&mut attr.con)?; Ok(attr) } pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> { - unsafe { - if !attr.is_symlink() && libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()) < 0 - { - let self_attr = self.get_attr()?; - if !self_attr.is_symlink() { - return Err(OsError::last_os_error("chmod", Some(self), None)); - } + if !attr.is_symlink() + && let Err(e) = nix::sys::stat::fchmodat( + AT_FDCWD, + self, + Mode::from_bits_truncate((attr.st.st_mode & 0o777).as_()), + FchmodatFlags::FollowSymlink, + ) + { + // Double check if self is symlink before reporting error + let self_attr = self.get_attr()?; + if !self_attr.is_symlink() { + return Err(OsError::new(e, "chmod", Some(self), None)); } + } + + unsafe { libc::lchown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).check_os_err( "lchown", Some(self), None, )?; + } - #[cfg(feature = "selinux")] - if !attr.con.is_empty() { - self.set_secontext(&attr.con)?; - } + #[cfg(feature = "selinux")] + if !attr.con.is_empty() { + self.set_secontext(&attr.con)?; } Ok(()) } @@ -388,17 +385,19 @@ impl Utf8CStr { // ln -s target self pub fn create_symlink_to<'a>(&'a self, target: &'a Utf8CStr) -> OsResult<'a, ()> { - unsafe { - libc::symlink(target.as_ptr(), self.as_ptr()).check_os_err( - "symlink", - Some(target), - Some(self), - ) - } + nix::unistd::symlinkat(target, AT_FDCWD, self).check_os_err( + "symlink", + Some(target), + Some(self), + ) } pub fn mkfifo(&self, mode: mode_t) -> OsResult<'_, ()> { - unsafe { libc::mkfifo(self.as_ptr(), mode).check_os_err("mkfifo", Some(self), None) } + nix::unistd::mkfifo(self, Mode::from_bits_truncate(mode)).check_os_err( + "mkfifo", + Some(self), + None, + ) } } @@ -408,7 +407,7 @@ impl Utf8CStr { pub fn remove_all(&self) -> LoggedResult<()> { let attr = self.get_attr()?; if attr.is_dir() { - let dir = Directory::try_from(open_fd(self, O_RDONLY | O_CLOEXEC, 0)?)?; + let dir = Directory::open(self)?; dir.remove_all()?; } Ok(self.remove()?) @@ -431,12 +430,7 @@ impl Utf8CStr { break; }; path.append_path(s); - - unsafe { - if libc::mkdir(path.as_ptr(), mode) < 0 && *errno() != EEXIST { - return Err(OsError::last_os_error("mkdir", Some(&path), None))?; - } - } + path.mkdir(mode)?; } *errno() = 0; @@ -454,8 +448,11 @@ impl Utf8CStr { // It's OK if remove failed path.remove().ok(); if attr.is_file() { - let mut src = self.open(O_RDONLY | O_CLOEXEC)?; - let mut dest = path.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o777)?; + let mut src = self.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC)?; + let mut dest = path.create( + OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC | OFlag::O_CLOEXEC, + 0o777, + )?; std::io::copy(&mut src, &mut dest)?; } else if attr.is_symlink() { let mut buf = cstr::buf::default(); @@ -512,37 +509,41 @@ impl Utf8CStr { impl FsPathFollow { pub fn exists(&self) -> bool { - unsafe { libc::access(self.as_ptr(), F_OK) == 0 } + nix::unistd::access(self.deref(), AccessFlags::F_OK).is_ok() } pub fn get_attr(&self) -> OsResult<'_, FileAttr> { - let mut attr = FileAttr::new(); - unsafe { - libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?; - + #[allow(unused_mut)] + let mut attr = FileAttr { + st: nix::sys::stat::stat(self.deref()).into_os_result("lstat", Some(self), None)?, #[cfg(feature = "selinux")] - self.get_secontext(&mut attr.con)?; - } + con: cstr::buf::new(), + }; + #[cfg(feature = "selinux")] + self.get_secontext(&mut attr.con)?; Ok(attr) } pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> { + nix::sys::stat::fchmodat( + AT_FDCWD, + self.deref(), + Mode::from_bits_truncate((attr.st.st_mode & 0o777).as_()), + FchmodatFlags::FollowSymlink, + ) + .check_os_err("chmod", Some(self), None)?; + unsafe { - libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).check_os_err( - "chmod", - Some(self), - None, - )?; libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).check_os_err( "chown", Some(self), None, )?; + } - #[cfg(feature = "selinux")] - if !attr.con.is_empty() { - self.set_secontext(&attr.con)?; - } + #[cfg(feature = "selinux")] + if !attr.con.is_empty() { + self.set_secontext(&attr.con)?; } Ok(()) } @@ -751,7 +752,7 @@ unsafe extern "C" { // We mark the returned slice static because it is valid until explicitly unmapped pub(crate) fn map_file(path: &Utf8CStr, rw: bool) -> OsResult<'_, &'static mut [u8]> { - unsafe { map_file_at(BorrowedFd::borrow_raw(libc::AT_FDCWD), path, rw) } + map_file_at(AT_FDCWD, path, rw) } pub(crate) fn map_file_at<'a>( @@ -765,17 +766,9 @@ pub(crate) fn map_file_at<'a>( #[cfg(target_pointer_width = "32")] const BLKGETSIZE64: u32 = 0x80041272; - let flag = if rw { O_RDWR } else { O_RDONLY }; - let fd = unsafe { - OwnedFd::from_raw_fd( - libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flag | O_CLOEXEC).into_os_result( - "openat", - Some(path), - None, - )?, - ) - }; - + let flag = if rw { OFlag::O_RDWR } else { OFlag::O_RDONLY }; + let fd = nix::fcntl::openat(dirfd, path, flag | OFlag::O_CLOEXEC, Mode::empty()) + .into_os_result("openat", Some(path), None)?; let attr = fd_get_attr(fd.as_raw_fd())?; let sz = if attr.is_block_device() { let mut sz = 0_u64; @@ -881,7 +874,7 @@ fn parse_mount_info_line(line: &str) -> Option { pub fn parse_mount_info(pid: &str) -> Vec { let mut res = vec![]; let mut path = format!("/proc/{pid}/mountinfo"); - if let Ok(file) = Utf8CStr::from_string(&mut path).open(O_RDONLY | O_CLOEXEC) { + if let Ok(file) = Utf8CStr::from_string(&mut path).open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { BufReader::new(file).for_each_line(|line| { parse_mount_info_line(line) .map(|info| res.push(info)) @@ -890,19 +883,3 @@ pub fn parse_mount_info(pid: &str) -> Vec { } res } - -pub struct PipeFd { - pub read: OwnedFd, - pub write: OwnedFd, -} - -pub fn make_pipe(flags: i32) -> OsResult<'static, PipeFd> { - let mut pipefd: [RawFd; 2] = [0; 2]; - unsafe { - libc::pipe2(pipefd.as_mut_ptr(), flags).check_os_err("pipe2", None, None)?; - Ok(PipeFd { - read: OwnedFd::from_raw_fd(pipefd[0]), - write: OwnedFd::from_raw_fd(pipefd[1]), - }) - } -} diff --git a/native/src/base/lib.rs b/native/src/base/lib.rs index 1edff31b4..2197726f3 100644 --- a/native/src/base/lib.rs +++ b/native/src/base/lib.rs @@ -2,6 +2,7 @@ pub use const_format; pub use libc; +pub use nix; use num_traits::FromPrimitive; pub use cstr::{ diff --git a/native/src/base/result.rs b/native/src/base/result.rs index 4470981de..e6cae9cb9 100644 --- a/native/src/base/result.rs +++ b/native/src/base/result.rs @@ -1,9 +1,10 @@ use crate::logging::Formatter; -use crate::{LogLevel, errno, log_with_args, log_with_formatter}; +use crate::{LogLevel, log_with_args, log_with_formatter}; +use nix::errno::Errno; +use std::fmt; use std::fmt::Display; use std::panic::Location; use std::ptr::NonNull; -use std::{fmt, io}; // Error handling throughout the Rust codebase in Magisk: // @@ -217,7 +218,7 @@ where { type Value; - fn into_result(self) -> Result; + fn check_err(self) -> nix::Result; fn into_os_result<'a>( self, @@ -225,7 +226,7 @@ where arg1: Option<&'a str>, arg2: Option<&'a str>, ) -> OsResult<'a, Self::Value> { - self.into_result() + self.check_err() .map_err(|e| OsError::new(e, name, arg1, arg2)) } @@ -235,16 +236,10 @@ where arg1: Option<&'a str>, arg2: Option<&'a str>, ) -> OsResult<'a, ()> { - self.into_result() + self.check_err() .map(|_| ()) .map_err(|e| OsError::new(e, name, arg1, arg2)) } - - fn check_io_err(self) -> io::Result<()> { - self.into_result() - .map(|_| ()) - .map_err(io::Error::from_raw_os_error) - } } macro_rules! impl_libc_return { @@ -253,9 +248,9 @@ macro_rules! impl_libc_return { type Value = Self; #[inline(always)] - fn into_result(self) -> Result { + fn check_err(self) -> nix::Result { if self < 0 { - Err(*errno()) + Err(Errno::last()) } else { Ok(self) } @@ -270,14 +265,23 @@ impl LibcReturn for *mut T { type Value = NonNull; #[inline(always)] - fn into_result(self) -> Result { - NonNull::new(self).ok_or_else(|| *errno()) + fn check_err(self) -> nix::Result { + NonNull::new(self).ok_or_else(Errno::last) + } +} + +impl LibcReturn for nix::Result { + type Value = T; + + #[inline(always)] + fn check_err(self) -> Self { + self } } #[derive(Debug)] pub struct OsError<'a> { - code: i32, + errno: Errno, name: &'static str, arg1: Option<&'a str>, arg2: Option<&'a str>, @@ -285,13 +289,13 @@ pub struct OsError<'a> { impl OsError<'_> { pub fn new<'a>( - code: i32, + errno: Errno, name: &'static str, arg1: Option<&'a str>, arg2: Option<&'a str>, ) -> OsError<'a> { OsError { - code, + errno, name, arg1, arg2, @@ -303,33 +307,28 @@ impl OsError<'_> { arg1: Option<&'a str>, arg2: Option<&'a str>, ) -> OsError<'a> { - Self::new(*errno(), name, arg1, arg2) + Self::new(Errno::last(), name, arg1, arg2) } pub fn set_args<'a>(self, arg1: Option<&'a str>, arg2: Option<&'a str>) -> OsError<'a> { - Self::new(self.code, self.name, arg1, arg2) - } - - fn as_io_error(&self) -> io::Error { - io::Error::from_raw_os_error(self.code) + Self::new(self.errno, self.name, arg1, arg2) } } impl Display for OsError<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let error = self.as_io_error(); if self.name.is_empty() { - write!(f, "{error:#}") + write!(f, "{}", self.errno) } else { match (self.arg1, self.arg2) { (Some(arg1), Some(arg2)) => { - write!(f, "{} '{}' '{}': {:#}", self.name, arg1, arg2, error) + write!(f, "{} '{arg1}' '{arg2}': {}", self.name, self.errno) } (Some(arg1), None) => { - write!(f, "{} '{}': {:#}", self.name, arg1, error) + write!(f, "{} '{arg1}': {}", self.name, self.errno) } _ => { - write!(f, "{}: {:#}", self.name, error) + write!(f, "{}: {}", self.name, self.errno) } } } diff --git a/native/src/base/xwrap.rs b/native/src/base/xwrap.rs index 97819941a..b5b6c4f7a 100644 --- a/native/src/base/xwrap.rs +++ b/native/src/base/xwrap.rs @@ -1,9 +1,7 @@ // Functions in this file are only for exporting to C++, DO NOT USE IN RUST use crate::cxx_extern::readlinkat; -use crate::{ - BorrowedDirectory, LibcReturn, ResultExt, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut, -}; +use crate::{Directory, LibcReturn, ResultExt, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut}; use libc::{c_char, c_uint, c_ulong, c_void, dev_t, mode_t, off_t, sockaddr, socklen_t}; use std::ffi::CStr; use std::fs::File; @@ -185,7 +183,7 @@ extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR { } #[unsafe(no_mangle)] -unsafe extern "C" fn xreaddir(mut dir: BorrowedDirectory) -> *mut libc::dirent { +unsafe extern "C" fn xreaddir(mut dir: ManuallyDrop) -> *mut libc::dirent { dir.read() .log() .ok() diff --git a/native/src/boot/cli.rs b/native/src/boot/cli.rs index b66d65832..104779523 100644 --- a/native/src/boot/cli.rs +++ b/native/src/boot/cli.rs @@ -8,7 +8,7 @@ use crate::sign::{sha1_hash, sign_boot_image}; use argh::{CommandInfo, EarlyExit, FromArgs, SubCommand}; use base::{ CmdArgs, EarlyExitExt, LoggedResult, MappedFile, PositionalArgParser, ResultExt, Utf8CStr, - Utf8CString, WriteExt, cmdline_logging, cstr, libc, libc::umask, log_err, + Utf8CString, WriteExt, cmdline_logging, cstr, libc::umask, log_err, nix::fcntl::OFlag, }; use std::ffi::c_char; use std::io::{Seek, SeekFrom, Write}; @@ -333,7 +333,7 @@ fn sign_cmd( let sig = sign_boot_image(img.payload(), name, cert, key)?; let tail_off = img.tail_off(); drop(img); - let mut fd = image.open(libc::O_WRONLY | libc::O_CLOEXEC)?; + let mut fd = image.open(OFlag::O_WRONLY | OFlag::O_CLOEXEC)?; fd.seek(SeekFrom::Start(tail_off))?; fd.write_all(&sig)?; let current = fd.stream_position()?; diff --git a/native/src/boot/compress.rs b/native/src/boot/compress.rs index c9667ed6a..c3143a85a 100644 --- a/native/src/boot/compress.rs +++ b/native/src/boot/compress.rs @@ -1,7 +1,7 @@ use crate::ffi::{FileFormat, check_fmt}; -use base::libc::{O_RDONLY, O_TRUNC, O_WRONLY}; use base::{ Chunker, FileOrStd, LoggedResult, ReadExt, ResultExt, Utf8CStr, Utf8CString, WriteExt, log_err, + nix::fcntl::OFlag, }; use bzip2::{Compression as BzCompression, read::BzDecoder, write::BzEncoder}; use flate2::{Compression as GzCompression, read::MultiGzDecoder, write::GzEncoder}; @@ -293,7 +293,7 @@ pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> L let input = if in_std { FileOrStd::StdIn } else { - FileOrStd::File(infile.open(O_RDONLY)?) + FileOrStd::File(infile.open(OFlag::O_RDONLY)?) }; // First read some bytes for format detection @@ -316,7 +316,7 @@ pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> L if outfile == "-" { FileOrStd::StdOut } else { - FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?) + FileOrStd::File(outfile.create(OFlag::O_WRONLY | OFlag::O_TRUNC, 0o644)?) } } else if in_std { FileOrStd::StdOut @@ -332,7 +332,7 @@ pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> L rm_in = true; eprintln!("Decompressing to [{outfile}]"); - FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?) + FileOrStd::File(outfile.create(OFlag::O_WRONLY | OFlag::O_TRUNC, 0o644)?) }; let mut decoder = get_decoder(format, Cursor::new(buf).chain(input.as_file())); @@ -356,14 +356,14 @@ pub(crate) fn compress_cmd( let input = if in_std { FileOrStd::StdIn } else { - FileOrStd::File(infile.open(O_RDONLY)?) + FileOrStd::File(infile.open(OFlag::O_RDONLY)?) }; let output = if let Some(outfile) = outfile { if outfile == "-" { FileOrStd::StdOut } else { - FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?) + FileOrStd::File(outfile.create(OFlag::O_WRONLY | OFlag::O_TRUNC, 0o644)?) } } else if in_std { FileOrStd::StdOut @@ -374,7 +374,7 @@ pub(crate) fn compress_cmd( outfile.write_str(method.ext()).ok(); eprintln!("Compressing to [{outfile}]"); rm_in = true; - let outfile = outfile.create(O_WRONLY | O_TRUNC, 0o644)?; + let outfile = outfile.create(OFlag::O_WRONLY | OFlag::O_TRUNC, 0o644)?; FileOrStd::File(outfile) }; diff --git a/native/src/boot/cpio.rs b/native/src/boot/cpio.rs index f4949bf63..afc00ad75 100644 --- a/native/src/boot/cpio.rs +++ b/native/src/boot/cpio.rs @@ -14,20 +14,19 @@ use bytemuck::{Pod, Zeroable, from_bytes}; use num_traits::cast::AsPrimitive; use size::{Base, Size, Style}; -use base::libc::{ - O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, - S_IFREG, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR, - dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, -}; -use base::{ - BytesExt, EarlyExitExt, LoggedResult, MappedFile, OptionExt, ResultExt, Utf8CStr, Utf8CStrBuf, - WriteExt, cstr, log_err, -}; - use crate::check_env; use crate::compress::{get_decoder, get_encoder}; use crate::ffi::FileFormat; use crate::patch::{patch_encryption, patch_verity}; +use base::libc::{ + S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, + S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR, dev_t, gid_t, major, makedev, minor, mknod, + mode_t, uid_t, +}; +use base::{ + BytesExt, EarlyExitExt, LoggedResult, MappedFile, OptionExt, ResultExt, Utf8CStr, Utf8CStrBuf, + WriteExt, cstr, log_err, nix::fcntl::OFlag, +}; #[derive(FromArgs)] struct CpioCommand { @@ -349,7 +348,10 @@ impl Cpio { match entry.mode & S_IFMT { S_IFDIR => out.mkdir(mode)?, S_IFREG => { - let mut file = out.create(O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, mode)?; + let mut file = out.create( + OFlag::O_CREAT | OFlag::O_TRUNC | OFlag::O_WRONLY | OFlag::O_CLOEXEC, + mode, + )?; file.write_all(&entry.data)?; } S_IFLNK => { @@ -402,7 +404,8 @@ impl Cpio { let mode = if attr.is_file() || attr.is_symlink() { rdevmajor = 0; rdevminor = 0; - file.open(O_RDONLY | O_CLOEXEC)?.read_to_end(&mut content)?; + file.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC)? + .read_to_end(&mut content)?; mode | S_IFREG } else { rdevmajor = major(attr.st.st_rdev.as_()).as_(); diff --git a/native/src/core/Cargo.toml b/native/src/core/Cargo.toml index 240efb1fe..6f3cc70a9 100644 --- a/native/src/core/Cargo.toml +++ b/native/src/core/Cargo.toml @@ -26,3 +26,4 @@ bytemuck = { workspace = true, features = ["derive"] } thiserror = { workspace = true } bit-set = { workspace = true } argh = { workspace = true } +nix = { workspace = true, features = ["fs", "poll", "signal"] } diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index ac73c32a9..a4379269b 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -18,11 +18,11 @@ use crate::socket::IpcWrite; use crate::su::SuInfo; use crate::zygisk::ZygiskState; use base::const_format::concatcp; -use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY}; use base::{ AtomicArc, BufReadExt, FsPathBuilder, ReadExt, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt, cstr, error, info, libc, }; +use nix::fcntl::OFlag; use std::fmt::Write as FmtWrite; use std::fs::File; use std::io::{BufReader, Write}; @@ -304,7 +304,9 @@ pub fn daemon_entry() { unsafe { libc::setsid() }; // Make sure the current context is magisk - if let Ok(mut current) = cstr!("/proc/self/attr/current").open(O_WRONLY | O_CLOEXEC) { + if let Ok(mut current) = + cstr!("/proc/self/attr/current").open(OFlag::O_WRONLY | OFlag::O_CLOEXEC) + { let con = cstr!(MAGISK_PROC_CON); current.write_all(con.as_bytes_with_nul()).log_ok(); } @@ -323,7 +325,7 @@ pub fn daemon_entry() { .join_path(magisk_tmp) .join_path(MAIN_CONFIG); let mut is_recovery = false; - if let Ok(main_config) = tmp_path.open(O_RDONLY | O_CLOEXEC) { + if let Ok(main_config) = tmp_path.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { BufReader::new(main_config).for_each_prop(|key, val| { if key == "RECOVERYMODE" { is_recovery = val == "true"; @@ -335,7 +337,7 @@ pub fn daemon_entry() { tmp_path.truncate(magisk_tmp.len()); let mut sdk_int = -1; - if let Ok(build_prop) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) { + if let Ok(build_prop) = cstr!("/system/build.prop").open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { BufReader::new(build_prop).for_each_prop(|key, val| { if key == "ro.build.version.sdk" { sdk_int = val.parse::().unwrap_or(-1); @@ -370,7 +372,7 @@ pub fn daemon_entry() { // Cleanup pre-init mounts tmp_path.append_path(ROOTMNT); - if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) { + if let Ok(mount_list) = tmp_path.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { BufReader::new(mount_list).for_each_line(|line| { line.truncate(line.trim_end().len()); let item = Utf8CStr::from_string(line); @@ -407,7 +409,7 @@ fn switch_cgroup(cgroup: &str, pid: i32) { if !buf.exists() { return; } - if let Ok(mut file) = buf.open(O_WRONLY | O_APPEND | O_CLOEXEC) { + if let Ok(mut file) = buf.open(OFlag::O_WRONLY | OFlag::O_APPEND | OFlag::O_CLOEXEC) { buf.clear(); buf.write_fmt(format_args!("{pid}")).ok(); file.write_all(buf.as_bytes()).log_ok(); @@ -415,7 +417,7 @@ fn switch_cgroup(cgroup: &str, pid: i32) { } fn check_data() -> bool { - if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) { + if let Ok(file) = cstr!("/proc/mounts").open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { let mut mnt = false; BufReader::new(file).for_each_line(|line| { if line.contains(" /data ") && !line.contains("tmpfs") { diff --git a/native/src/core/module.rs b/native/src/core/module.rs index 8cf586be7..ad8126356 100644 --- a/native/src/core/module.rs +++ b/native/src/core/module.rs @@ -4,13 +4,14 @@ use crate::ffi::{ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp}; use crate::mount::setup_module_mount; use crate::resetprop::load_prop_file; use base::{ - DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResult, ResultExt, - SilentLogExt, Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, - info, libc, raw_cstr, warn, + DirEntry, Directory, FsPathBuilder, LoggedResult, OsResult, ResultExt, SilentLogExt, Utf8CStr, + Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc, raw_cstr, + warn, }; -use libc::{AT_REMOVEDIR, MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY}; +use libc::MS_RDONLY; +use nix::{fcntl::OFlag, unistd::UnlinkatFlags}; use std::collections::BTreeMap; -use std::os::fd::{AsRawFd, IntoRawFd}; +use std::os::fd::IntoRawFd; use std::path::{Component, Path}; use std::ptr; use std::sync::atomic::Ordering; @@ -51,7 +52,7 @@ fn mount_dummy<'a>( if is_dir { dest.mkdir(0o000)?; } else { - dest.create(O_CREAT | O_RDONLY | O_CLOEXEC, 0o000)?; + dest.create(OFlag::O_CREAT | OFlag::O_RDONLY | OFlag::O_CLOEXEC, 0o000)?; } bind_mount(reason, src, dest, false); Ok(()) @@ -563,7 +564,6 @@ fn inject_zygisk_bins(name: &str, system: &mut FsNode) { fn upgrade_modules() -> LoggedResult<()> { let mut upgrade = Directory::open(cstr!(MODULEUPGRADE)).silent()?; - let ufd = upgrade.as_raw_fd(); let root = Directory::open(cstr!(MODULEROOT))?; while let Some(e) = upgrade.read()? { if !e.is_dir() { @@ -577,23 +577,19 @@ fn upgrade_modules() -> LoggedResult<()> { // If the old module is disabled, we need to also disable the new one disable = module.contains_path(cstr!("disable")); module.remove_all()?; - root.unlink_at(module_name, AT_REMOVEDIR)?; + root.unlink_at(module_name, UnlinkatFlags::RemoveDir)?; } info!("Upgrade / New module: {module_name}"); - unsafe { - libc::renameat( - ufd, - module_name.as_ptr(), - root.as_raw_fd(), - module_name.as_ptr(), - ) - .check_os_err("renameat", Some(module_name), None)?; - } + e.rename_to(&root, module_name)?; if disable { let path = cstr::buf::default() .join_path(module_name) .join_path("disable"); - let _ = root.open_as_file_at(&path, O_RDONLY | O_CREAT | O_CLOEXEC, 0)?; + let _ = root.open_as_file_at( + &path, + OFlag::O_RDONLY | OFlag::O_CREAT | OFlag::O_CLOEXEC, + 0, + )?; } } upgrade.remove_all()?; @@ -614,7 +610,11 @@ fn for_each_module(mut func: impl FnMut(&DirEntry) -> LoggedResult<()>) -> Logge pub fn disable_modules() { for_each_module(|e| { let dir = e.open_as_dir()?; - dir.open_as_file_at(cstr!("disable"), O_RDONLY | O_CREAT | O_CLOEXEC, 0)?; + dir.open_as_file_at( + cstr!("disable"), + OFlag::O_RDONLY | OFlag::O_CREAT | OFlag::O_CLOEXEC, + 0, + )?; Ok(()) }) .log_ok(); @@ -656,7 +656,8 @@ fn collect_modules(zygisk_enabled: bool, open_zygisk: bool) -> Vec { e.unlink()?; return Ok(()); } - dir.unlink_at(cstr!("update"), 0).ok(); + dir.unlink_at(cstr!("update"), UnlinkatFlags::NoRemoveDir) + .ok(); if dir.contains_path(cstr!("disable")) { return Ok(()); } @@ -673,7 +674,7 @@ fn collect_modules(zygisk_enabled: bool, open_zygisk: bool) -> Vec { } fn open_fd_safe(dir: &Directory, name: &Utf8CStr) -> i32 { - dir.open_as_file_at(name, O_RDONLY | O_CLOEXEC, 0) + dir.open_as_file_at(name, OFlag::O_RDONLY | OFlag::O_CLOEXEC, 0) .log() .map(IntoRawFd::into_raw_fd) .unwrap_or(-1) @@ -702,7 +703,8 @@ fn collect_modules(zygisk_enabled: bool, open_zygisk: bool) -> Vec { { z64 = open_fd_safe(&dir, cstr!("zygisk/riscv64.so")); } - dir.unlink_at(cstr!("zygisk/unloaded"), 0).ok(); + dir.unlink_at(cstr!("zygisk/unloaded"), UnlinkatFlags::NoRemoveDir) + .ok(); } } else { // Ignore zygisk modules when zygisk is not enabled diff --git a/native/src/core/mount.rs b/native/src/core/mount.rs index 52842d882..e26cdfc44 100644 --- a/native/src/core/mount.rs +++ b/native/src/core/mount.rs @@ -200,9 +200,8 @@ pub fn find_preinit_device() -> String { libc::S_IFBLK | 0o600, info.device as dev_t, ) - .check_io_err() - .log() - .ok(); + .check_os_err("mknod", Some(dev_path), None) + .log_ok(); } } } diff --git a/native/src/core/package.rs b/native/src/core/package.rs index df6202ec1..cca959280 100644 --- a/native/src/core/package.rs +++ b/native/src/core/package.rs @@ -2,12 +2,12 @@ use crate::consts::{APP_PACKAGE_NAME, MAGISK_VER_CODE}; use crate::daemon::{AID_APP_END, AID_APP_START, AID_USER_OFFSET, MagiskD, to_app_id}; use crate::ffi::{DbEntryKey, get_magisk_tmp, install_apk, uninstall_pkg}; use base::WalkResult::{Abort, Continue, Skip}; -use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY}; use base::{ BufReadExt, Directory, FsPathBuilder, LoggedResult, ReadExt, ResultExt, Utf8CStrBuf, Utf8CString, cstr, error, fd_get_attr, warn, }; use bit_set::BitSet; +use nix::fcntl::OFlag; use std::collections::BTreeMap; use std::fs::File; use std::io; @@ -238,7 +238,7 @@ impl ManagerInfo { .join_path("dyn") .join_path("current.apk"); let uid: i32; - let cert = match apk.open(O_RDONLY | O_CLOEXEC) { + let cert = match apk.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { Ok(mut fd) => { uid = fd_get_attr(fd.as_raw_fd()) .map(|attr| attr.st.st_uid as i32) @@ -270,7 +270,7 @@ impl ManagerInfo { return Status::NotInstalled; }; - let cert = match apk.open(O_RDONLY | O_CLOEXEC) { + let cert = match apk.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { Ok(mut fd) => read_certificate(&mut fd, -1), Err(_) => return Status::NotInstalled, }; @@ -293,7 +293,7 @@ impl ManagerInfo { return Status::NotInstalled; }; - let cert = match apk.open(O_RDONLY | O_CLOEXEC) { + let cert = match apk.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { Ok(mut fd) => read_certificate(&mut fd, MAGISK_VER_CODE), Err(_) => return Status::NotInstalled, }; @@ -317,8 +317,10 @@ impl ManagerInfo { let tmp_apk = cstr!("/data/stub.apk"); let result: LoggedResult<()> = try { { - let mut tmp_apk_file = - tmp_apk.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o600)?; + let mut tmp_apk_file = tmp_apk.create( + OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC | OFlag::O_CLOEXEC, + 0o600, + )?; io::copy(stub_fd, &mut tmp_apk_file)?; } // Seek the fd back to start @@ -445,7 +447,7 @@ impl MagiskD { .join_path(get_magisk_tmp()) .join_path("stub.apk"); - if let Ok(mut fd) = apk.open(O_RDONLY | O_CLOEXEC) { + if let Ok(mut fd) = apk.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC) { info.trusted_cert = read_certificate(&mut fd, MAGISK_VER_CODE); // Seek the fd back to start fd.seek(SeekFrom::Start(0)).log_ok(); diff --git a/native/src/core/resetprop/cli.rs b/native/src/core/resetprop/cli.rs index c6244a986..015a8f067 100644 --- a/native/src/core/resetprop/cli.rs +++ b/native/src/core/resetprop/cli.rs @@ -6,8 +6,9 @@ use argh::{EarlyExit, FromArgs, MissingRequirements}; use base::libc::PROP_VALUE_MAX; use base::{ BufReadExt, CmdArgs, EarlyExitExt, LogLevel, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, - Utf8CString, cstr, debug, libc, log_err, set_log_level_state, + Utf8CString, cstr, debug, log_err, set_log_level_state, }; +use nix::fcntl::OFlag; use std::collections::BTreeMap; use std::ffi::c_char; use std::io::BufReader; @@ -211,7 +212,7 @@ impl ResetProp { } fn load_file(&self, file: &Utf8CStr) -> LoggedResult<()> { - let fd = file.open(libc::O_RDONLY | libc::O_CLOEXEC)?; + let fd = file.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC)?; let mut key = cstr::buf::dynamic(128); let mut val = cstr::buf::dynamic(128); BufReader::new(fd).for_each_prop(|k, v| { diff --git a/native/src/core/resetprop/persist.rs b/native/src/core/resetprop/persist.rs index 0ef386795..e7526b254 100644 --- a/native/src/core/resetprop/persist.rs +++ b/native/src/core/resetprop/persist.rs @@ -1,3 +1,5 @@ +use nix::fcntl::OFlag; +use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use std::io::Read; use std::{ fs::File, @@ -5,14 +7,11 @@ use std::{ os::fd::FromRawFd, }; -use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; - use crate::resetprop::PropReader; use crate::resetprop::proto::persistent_properties::{ PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord, }; use base::const_format::concatcp; -use base::libc::{O_CLOEXEC, O_RDONLY}; use base::{ Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentLogExt, Utf8CStr, Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, log_err, @@ -46,7 +45,7 @@ fn file_get_prop(name: &Utf8CStr) -> LoggedResult { let path = cstr::buf::default() .join_path(PERSIST_PROP_DIR) .join_path(name); - let mut file = path.open(O_RDONLY | O_CLOEXEC).silent()?; + let mut file = path.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC).silent()?; debug!("resetprop: read prop from [{}]", path); let mut s = String::new(); file.read_to_string(&mut s)?; diff --git a/native/src/core/selinux.rs b/native/src/core/selinux.rs index 5122ed23a..6fcc87f24 100644 --- a/native/src/core/selinux.rs +++ b/native/src/core/selinux.rs @@ -1,7 +1,7 @@ use crate::consts::{DATABIN, LOG_PIPE, MAGISK_LOG_CON, MAGISKDB, MODULEROOT, SECURE_DIR}; use crate::ffi::get_magisk_tmp; -use base::libc::{O_CLOEXEC, O_WRONLY}; use base::{Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, libc}; +use nix::fcntl::OFlag; use std::io::Write; const UNLABEL_CON: &Utf8CStr = cstr!("u:object_r:unlabeled:s0"); @@ -53,7 +53,7 @@ fn restore_syscon(path: &mut dyn Utf8CStrBuf) -> LoggedResult<()> { pub(crate) fn restorecon() { if let Ok(mut file) = cstr!("/sys/fs/selinux/context") - .open(O_WRONLY | O_CLOEXEC) + .open(OFlag::O_WRONLY | OFlag::O_CLOEXEC) .log() && file.write_all(ADB_CON.as_bytes_with_nul()).is_ok() { diff --git a/native/src/core/su/connect.rs b/native/src/core/su/connect.rs index 93a33fede..8b848d060 100644 --- a/native/src/core/su/connect.rs +++ b/native/src/core/su/connect.rs @@ -6,11 +6,16 @@ use crate::ffi::{SuPolicy, SuRequest, get_magisk_tmp}; use crate::socket::IpcRead; use ExtraVal::{Bool, Int, IntList, Str}; use base::{ - BytesExt, FileAttr, LibcReturn, LoggedResult, OsError, ResultExt, cstr, fork_dont_care, libc, + BytesExt, FileAttr, LibcReturn, LoggedResult, ResultExt, Utf8CStrBuf, cstr, fork_dont_care, + libc, +}; +use nix::{ + fcntl::OFlag, + poll::{PollFd, PollFlags, PollTimeout}, }; -use libc::pollfd as PollFd; use num_traits::AsPrimitive; -use std::{fmt::Write, fs::File, os::fd::AsRawFd, process::Command, process::exit}; +use std::os::fd::AsFd; +use std::{fmt::Write, fs::File, process::Command, process::exit}; struct Extra<'a> { key: &'static str, @@ -171,7 +176,7 @@ impl SuAppContext<'_> { attr.st.st_mode = 0o600; attr.st.st_uid = self.info.mgr_uid.as_(); attr.st.st_gid = self.info.mgr_uid.as_(); - attr.con.write_str(MAGISK_FILE_CON).ok(); + attr.con.push_str(MAGISK_FILE_CON); fifo.mkfifo(0o600)?; fifo.set_attr(&attr)?; @@ -193,20 +198,12 @@ impl SuAppContext<'_> { self.exec_cmd("request", &extras, false); // Open with O_RDWR to prevent FIFO open block - let fd = fifo.open(libc::O_RDWR | libc::O_CLOEXEC)?; + let fd = fifo.open(OFlag::O_RDWR | OFlag::O_CLOEXEC)?; + let mut pfd = [PollFd::new(fd.as_fd(), PollFlags::POLLIN)]; // Wait for data input for at most 70 seconds - let mut pfd = PollFd { - fd: fd.as_raw_fd(), - events: libc::POLLIN, - revents: 0, - }; - if unsafe { libc::poll(&mut pfd, 1, 70 * 1000).into_os_result("poll", None, None)? } - == 0 - { - Err(OsError::new(libc::ETIMEDOUT, "poll", None, None))?; - } - + nix::poll::poll(&mut pfd, PollTimeout::try_from(70 * 1000).unwrap()) + .check_os_err("poll", None, None)?; fd }; diff --git a/native/src/core/su/pts.rs b/native/src/core/su/pts.rs index 86d7a80c3..31acb0247 100644 --- a/native/src/core/su/pts.rs +++ b/native/src/core/su/pts.rs @@ -1,21 +1,20 @@ -use base::libc::ssize_t; -use base::{ - LibcReturn, LoggedResult, OsResult, PipeFd, ReadExt, ResultExt, error, libc, log_err, - make_pipe, warn, -}; -use bytemuck::{Pod, Zeroable}; +use base::{FileOrStd, LibcReturn, LoggedResult, OsResult, libc, warn}; use libc::{ - O_CLOEXEC, POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, - TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, cfmakeraw, close, poll, pollfd, raise, sigaddset, - sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, tcgetattr, tcsetattr, termios, - winsize, + STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, c_int, cfmakeraw, + ssize_t, tcgetattr, tcsetattr, termios, winsize, +}; +use nix::{ + fcntl::OFlag, + poll::{PollFd, PollFlags, PollTimeout}, + sys::signal::{SigSet, Signal}, + sys::signalfd::{SfdFlags, SignalFd}, }; use std::fs::File; use std::io::{Read, Write}; use std::mem::{ManuallyDrop, MaybeUninit}; -use std::os::fd::{AsRawFd, FromRawFd, RawFd}; +use std::os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd}; +use std::ptr::null_mut; use std::sync::atomic::{AtomicBool, Ordering}; -use std::{ffi::c_int, ptr::null_mut}; static mut OLD_STDIN: Option = None; static SHOULD_USE_SPLICE: AtomicBool = AtomicBool::new(true); @@ -97,13 +96,13 @@ fn pump_via_copy(infd: RawFd, outfd: RawFd) -> LoggedResult<()> { Ok(()) } -fn pump_via_splice(infd: RawFd, outfd: RawFd, pipe: &PipeFd) -> LoggedResult<()> { +fn pump_via_splice(infd: RawFd, outfd: RawFd, pipe: &(OwnedFd, OwnedFd)) -> LoggedResult<()> { if !SHOULD_USE_SPLICE.load(Ordering::Acquire) { return pump_via_copy(infd, outfd); } // The pipe capacity is by default 16 pages, let's just use 65536 - let Ok(len) = splice(infd, pipe.write.as_raw_fd(), 65536_usize, 0) else { + let Ok(len) = splice(infd, pipe.1.as_raw_fd(), 65536_usize, 0) else { // If splice failed, stop using splice and fallback to userspace copy SHOULD_USE_SPLICE.store(false, Ordering::Release); return pump_via_copy(infd, outfd); @@ -111,85 +110,70 @@ fn pump_via_splice(infd: RawFd, outfd: RawFd, pipe: &PipeFd) -> LoggedResult<()> if len == 0 { return Ok(()); } - splice(pipe.read.as_raw_fd(), outfd, len as usize, 0)?; + splice(pipe.0.as_raw_fd(), outfd, len as usize, 0)?; Ok(()) } -#[derive(Copy, Clone)] -#[repr(transparent)] -struct SignalFdInfo(signalfd_siginfo); -unsafe impl Zeroable for SignalFdInfo {} -unsafe impl Pod for SignalFdInfo {} +fn pump_tty_impl(infd: OwnedFd, raw_out: RawFd) -> LoggedResult<()> { + let mut set = SigSet::empty(); + set.add(Signal::SIGWINCH); + set.thread_block() + .check_os_err("pthread_sigmask", None, None)?; + let signal_fd = + SignalFd::with_flags(&set, SfdFlags::SFD_CLOEXEC).into_os_result("signalfd", None, None)?; -pub fn pump_tty(infd: i32, outfd: i32) { - set_stdin_raw(); + let raw_in = infd.as_raw_fd(); + let raw_sig = signal_fd.as_raw_fd(); - let signal_fd = unsafe { - let mut mask: sigset_t = std::mem::zeroed(); - sigemptyset(&mut mask); - sigaddset(&mut mask, SIGWINCH); - sigprocmask(SIG_BLOCK, &mask, null_mut()) - .check_os_err("sigprocmask", None, None) - .log_ok(); - signalfd(-1, &mask, SFD_CLOEXEC) - }; + let mut poll_fds = Vec::with_capacity(3); + poll_fds.push(PollFd::new(infd.as_fd(), PollFlags::POLLIN)); + poll_fds.push(PollFd::new(signal_fd.as_fd(), PollFlags::POLLIN)); + if raw_out >= 0 { + poll_fds.push(PollFd::new( + FileOrStd::StdIn.as_file().as_fd(), + PollFlags::POLLIN, + )); + } - resize_pty(outfd); + // Any flag in this list indicates stop polling + let stop_flags = PollFlags::POLLERR | PollFlags::POLLHUP | PollFlags::POLLNVAL; - let mut pfds = [ - pollfd { - fd: if outfd > 0 { STDIN_FILENO } else { -1 }, - events: POLLIN, - revents: 0, - }, - pollfd { - fd: infd, - events: POLLIN, - revents: 0, - }, - pollfd { - fd: signal_fd, - events: POLLIN, - revents: 0, - }, - ]; - - let Ok(pipe_fd) = make_pipe(O_CLOEXEC).log() else { - return; - }; + // Open a pipe to bypass userspace copy with splice + let pipe_fd = nix::unistd::pipe2(OFlag::O_CLOEXEC).into_os_result("pipe2", None, None)?; 'poll: loop { - let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) }; - - if ready < 0 { - error!("poll error"); - break; - } - - for pfd in &pfds { - if pfd.revents & POLLIN != 0 { - let res = if pfd.fd == STDIN_FILENO { - pump_via_splice(STDIN_FILENO, outfd, &pipe_fd) - } else if pfd.fd == infd { - pump_via_splice(infd, STDOUT_FILENO, &pipe_fd) - } else if pfd.fd == signal_fd { - resize_pty(outfd); - let mut info = SignalFdInfo::zeroed(); - let mut fd = ManuallyDrop::new(unsafe { File::from_raw_fd(signal_fd) }); - fd.read_pod(&mut info).log() - } else { - log_err!() - }; - if res.is_err() { - break 'poll; + // Wait for event + nix::poll::poll(&mut poll_fds, PollTimeout::NONE).check_os_err("poll", None, None)?; + for pfd in &poll_fds { + if pfd.all().unwrap_or(false) { + let raw_fd = pfd.as_fd().as_raw_fd(); + if raw_fd == STDIN_FILENO { + pump_via_splice(STDIN_FILENO, raw_out, &pipe_fd)?; + } else if raw_fd == raw_in { + pump_via_splice(raw_in, STDOUT_FILENO, &pipe_fd)?; + } else if raw_fd == raw_sig { + resize_pty(raw_out); + signal_fd.read_signal()?; } - } else if pfd.revents != 0 && pfd.fd == infd { - unsafe { close(pfd.fd) }; + } else if pfd + .revents() + .unwrap_or(PollFlags::POLLHUP) + .intersects(stop_flags) + { + // If revents is None or contains any err_flags, stop polling break 'poll; } } } + Ok(()) +} + +pub fn pump_tty(infd: i32, outfd: i32) { + set_stdin_raw(); + + let infd = unsafe { OwnedFd::from_raw_fd(infd) }; + pump_tty_impl(infd, outfd).ok(); restore_stdin(); - unsafe { raise(SIGWINCH) }; + nix::sys::signal::raise(Signal::SIGWINCH).ok(); } diff --git a/native/src/core/zygisk/daemon.rs b/native/src/core/zygisk/daemon.rs index a469b345b..5096d05c8 100644 --- a/native/src/core/zygisk/daemon.rs +++ b/native/src/core/zygisk/daemon.rs @@ -3,11 +3,12 @@ use crate::daemon::{MagiskD, to_user_id}; use crate::ffi::{ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, update_deny_flags}; use crate::resetprop::{get_prop, set_prop}; use crate::socket::{IpcRead, UnixSocketExt}; -use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO}; +use base::libc::STDOUT_FILENO; use base::{ Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, WriteExt, cstr, fork_dont_care, libc, log_err, raw_cstr, warn, }; +use nix::fcntl::OFlag; use std::fmt::Write; use std::os::fd::{AsRawFd, OwnedFd, RawFd}; use std::os::unix::net::UnixStream; @@ -228,7 +229,7 @@ impl MagiskD { .join_path("zygisk"); // Create the unloaded marker file if let Ok(dir) = Directory::open(&path) { - dir.open_as_file_at(cstr!("unloaded"), O_CREAT | O_RDONLY, 0o644) + dir.open_as_file_at(cstr!("unloaded"), OFlag::O_CREAT | OFlag::O_RDONLY, 0o644) .log() .ok(); } @@ -244,7 +245,7 @@ impl MagiskD { let dir = cstr::buf::default() .join_path(MODULEROOT) .join_path(&module.name); - let fd = dir.open(O_RDONLY | O_CLOEXEC)?; + let fd = dir.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC)?; client.send_fds(&[fd.as_raw_fd()])?; Ok(()) } diff --git a/native/src/init/init.rs b/native/src/init/init.rs index 08df1169e..151f40679 100644 --- a/native/src/init/init.rs +++ b/native/src/init/init.rs @@ -130,7 +130,7 @@ impl MagiskInit { null(), ) } - .check_io_err()?; + .check_err()?; self.mount_list.push("/proc".to_string()); } if !cstr!("/sys/block").exists() { @@ -144,7 +144,7 @@ impl MagiskInit { null(), ) } - .check_io_err()?; + .check_err()?; self.mount_list.push("/sys".to_string()); } diff --git a/native/src/init/logging.rs b/native/src/init/logging.rs index e53d1f4e2..e5c661a5a 100644 --- a/native/src/init/logging.rs +++ b/native/src/init/logging.rs @@ -1,8 +1,9 @@ +use base::nix::fcntl::OFlag; use base::{ LOGGER, LogLevel, Logger, SilentLogExt, Utf8CStr, cstr, libc::{ - O_CLOEXEC, O_RDWR, O_WRONLY, S_IFCHR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, SYS_dup3, - makedev, mknod, syscall, + O_CLOEXEC, S_IFCHR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, SYS_dup3, makedev, mknod, + syscall, }, raw_cstr, }; @@ -19,10 +20,14 @@ static mut KMSG: RawFd = -1; pub fn setup_klog() { unsafe { // Shut down first 3 fds - let mut fd = cstr!("/dev/null").open(O_RDWR | O_CLOEXEC).silent(); + let mut fd = cstr!("/dev/null") + .open(OFlag::O_RDWR | OFlag::O_CLOEXEC) + .silent(); if fd.is_err() { mknod(raw_cstr!("/null"), S_IFCHR | 0o666, makedev(1, 3)); - fd = cstr!("/null").open(O_RDWR | O_CLOEXEC).silent(); + fd = cstr!("/null") + .open(OFlag::O_RDWR | OFlag::O_CLOEXEC) + .silent(); cstr!("/null").remove().ok(); } if let Ok(ref fd) = fd { @@ -32,17 +37,23 @@ pub fn setup_klog() { } // Then open kmsg fd - let mut fd = cstr!("/dev/kmsg").open(O_WRONLY | O_CLOEXEC).silent(); + let mut fd = cstr!("/dev/kmsg") + .open(OFlag::O_WRONLY | OFlag::O_CLOEXEC) + .silent(); if fd.is_err() { mknod(raw_cstr!("/kmsg"), S_IFCHR | 0o666, makedev(1, 11)); - fd = cstr!("/kmsg").open(O_WRONLY | O_CLOEXEC).silent(); + fd = cstr!("/kmsg") + .open(OFlag::O_WRONLY | OFlag::O_CLOEXEC) + .silent(); cstr!("/kmsg").remove().ok(); } KMSG = fd.map(|fd| fd.into_raw_fd()).unwrap_or(-1); } // Disable kmsg rate limiting - if let Ok(mut rate) = cstr!("/proc/sys/kernel/printk_devkmsg").open(O_WRONLY | O_CLOEXEC) { + if let Ok(mut rate) = + cstr!("/proc/sys/kernel/printk_devkmsg").open(OFlag::O_WRONLY | OFlag::O_CLOEXEC) + { writeln!(rate, "on").ok(); } diff --git a/native/src/init/mount.rs b/native/src/init/mount.rs index 878e212f8..39ecf5087 100644 --- a/native/src/init/mount.rs +++ b/native/src/init/mount.rs @@ -44,7 +44,7 @@ pub(crate) fn switch_root(path: &Utf8CStr) { mounts.insert(info.target); } unsafe { - chdir(path.as_ptr()).check_io_err()?; + chdir(path.as_ptr()).check_err()?; path.move_mount_to(cstr!("/"))?; chroot(raw_cstr!(".")); } @@ -88,7 +88,7 @@ impl MagiskInit { raw_cstr!("mode=755").cast(), ) } - .check_io_err() + .check_err() .log_ok(); cstr!("/init").copy_to(cstr!("/data/magiskinit")).log_ok(); @@ -107,7 +107,7 @@ impl MagiskInit { } unsafe { execve(raw_cstr!("/init"), self.argv.cast(), environ.cast()) - .check_io_err() + .check_err() .log_ok(); exit(1); } diff --git a/native/src/init/rootdir.rs b/native/src/init/rootdir.rs index 242ba0081..dc679bfee 100644 --- a/native/src/init/rootdir.rs +++ b/native/src/init/rootdir.rs @@ -1,6 +1,6 @@ use crate::consts::{ROOTMNT, ROOTOVL}; use crate::ffi::MagiskInit; -use base::libc::{O_CREAT, O_RDONLY, O_WRONLY}; +use base::nix::fcntl::OFlag; use base::{ BufReadExt, Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CString, clone_attr, cstr, debug, @@ -44,7 +44,7 @@ pub struct OverlayAttr(Utf8CString, Utf8CString); impl MagiskInit { pub(crate) fn parse_config_file(&mut self) { - if let Ok(fd) = cstr!("/data/.backup/.magisk").open(O_RDONLY) { + if let Ok(fd) = cstr!("/data/.backup/.magisk").open(OFlag::O_RDONLY) { let mut reader = BufReader::new(fd); reader.for_each_prop(|key, val| { if key == "PREINITDEVICE" { @@ -95,7 +95,7 @@ impl MagiskInit { let mut mount_list = String::new(); self.mount_impl(cstr!(ROOTOVL), dest, &mut mount_list) .log_ok(); - if let Ok(mut fd) = cstr!(ROOTMNT).create(O_CREAT | O_WRONLY, 0) { + if let Ok(mut fd) = cstr!(ROOTMNT).create(OFlag::O_CREAT | OFlag::O_WRONLY, 0) { fd.write(mount_list.as_bytes()).log_ok(); } } diff --git a/native/src/init/selinux.rs b/native/src/init/selinux.rs index 84514fa54..9e58dcdee 100644 --- a/native/src/init/selinux.rs +++ b/native/src/init/selinux.rs @@ -1,6 +1,7 @@ use crate::consts::{PREINITMIRR, SELINUXMOCK}; use crate::ffi::{MagiskInit, preload_ack, preload_lib, preload_policy, split_plat_cil}; use base::const_format::concatcp; +use base::nix::fcntl::OFlag; use base::{ BytesExt, LibcReturn, LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, debug, error, info, libc, raw_cstr, @@ -54,7 +55,7 @@ fn mock_fifo(target: &Utf8CStr, mock: &Utf8CStr) -> LoggedResult<()> { fn mock_file(target: &Utf8CStr, mock: &Utf8CStr) -> LoggedResult<()> { debug!("Hijack [{}]", target); - drop(mock.create(libc::O_RDONLY, 0o666)?); + drop(mock.create(OFlag::O_RDONLY, 0o666)?); mock.bind_mount_to(target, false).log() } @@ -93,7 +94,9 @@ impl MagiskInit { let rule_file = cstr!(concatcp!("/data/", PREINITMIRR, "/sepolicy.rule")); if rule_file.exists() { debug!("Loading custom sepolicy patch: [{}]", rule_file); - rule_file.open(libc::O_RDONLY)?.read_to_string(&mut rules)?; + rule_file + .open(OFlag::O_RDONLY)? + .read_to_string(&mut rules)?; } // Step 0: determine strategy @@ -153,7 +156,7 @@ impl MagiskInit { 0, ptr::null(), ) - .check_io_err()?; + .check_err()?; } } @@ -165,7 +168,7 @@ impl MagiskInit { if !policy_ver.exists() { // The file does not exist, create one - drop(policy_ver.create(libc::O_RDONLY, 0o666)?); + drop(policy_ver.create(OFlag::O_RDONLY, 0o666)?); } // The only purpose of this is to block init's control flow after it mounts @@ -204,7 +207,7 @@ impl MagiskInit { mock_fifo(SELINUX_REQPROT, MOCK_REQPROT)?; // This will unblock init at selinux_android_load_policy() -> set_policy_index(). - drop(MOCK_VERSION.open(libc::O_WRONLY)?); + drop(MOCK_VERSION.open(OFlag::O_WRONLY)?); policy_ver.unmount()?; @@ -218,7 +221,7 @@ impl MagiskInit { match strat { SePatchStrategy::LdPreload => { // This open will block until preload.so finish writing the sepolicy - let mut ack_fd = preload_ack().open(libc::O_WRONLY)?; + let mut ack_fd = preload_ack().open(OFlag::O_WRONLY)?; let mut sepol = SePolicy::from_file(preload_policy()); @@ -237,14 +240,14 @@ impl MagiskInit { } SePatchStrategy::SelinuxFs => { // This open will block until init calls security_getenforce(). - let mut mock_enforce = MOCK_ENFORCE.open(libc::O_WRONLY)?; + let mut mock_enforce = MOCK_ENFORCE.open(OFlag::O_WRONLY)?; self.cleanup_and_load(&rules); // security_getenforce was called, read from real and redirect to mock let mut data = vec![]; SELINUX_ENFORCE - .open(libc::O_RDONLY)? + .open(OFlag::O_RDONLY)? .read_to_end(&mut data)?; mock_enforce.write_all(&data)?; } @@ -266,10 +269,10 @@ impl MagiskInit { // init is blocked on checkreqprot, write to the real node first, then // unblock init by opening the mock FIFO. SELINUX_REQPROT - .open(libc::O_WRONLY)? + .open(OFlag::O_WRONLY)? .write_all("0".as_bytes())?; let mut v = vec![]; - MOCK_REQPROT.open(libc::O_RDONLY)?.read_to_end(&mut v)?; + MOCK_REQPROT.open(OFlag::O_RDONLY)?.read_to_end(&mut v)?; } } diff --git a/native/src/init/twostage.rs b/native/src/init/twostage.rs index 4677195e2..0f2ad7775 100644 --- a/native/src/init/twostage.rs +++ b/native/src/init/twostage.rs @@ -1,8 +1,6 @@ use crate::ffi::MagiskInit; -use base::{ - LoggedResult, MappedFile, MutBytesExt, ResultExt, cstr, debug, error, - libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_WRONLY}, -}; +use base::nix::fcntl::OFlag; +use base::{LoggedResult, MappedFile, MutBytesExt, ResultExt, cstr, debug, error}; use std::io::Write; pub(crate) fn hexpatch_init_for_second_stage(writable: bool) { @@ -32,7 +30,7 @@ pub(crate) fn hexpatch_init_for_second_stage(writable: bool) { let dest = cstr!("/data/init"); let _: LoggedResult<()> = try { { - let mut fd = dest.create(O_CREAT | O_WRONLY, 0)?; + let mut fd = dest.create(OFlag::O_CREAT | OFlag::O_WRONLY, 0)?; fd.write_all(init.as_ref())?; } let attr = src.follow_link().get_attr()?; @@ -77,7 +75,7 @@ impl MagiskInit { .log_ok(); debug!("Symlink /first_stage_ramdisk/storage/self/primary -> /system/system/bin/init"); cstr!("/first_stage_ramdisk/sdcard") - .create(O_RDONLY | O_CREAT | O_CLOEXEC, 0) + .create(OFlag::O_RDONLY | OFlag::O_CREAT | OFlag::O_CLOEXEC, 0) .log_ok(); } else { cstr!("/storage/self").mkdirs(0o755).log_ok(); diff --git a/native/src/sepolicy/statement.rs b/native/src/sepolicy/statement.rs index d3f25f17b..9eee0ff11 100644 --- a/native/src/sepolicy/statement.rs +++ b/native/src/sepolicy/statement.rs @@ -4,8 +4,7 @@ use std::{iter::Peekable, vec::IntoIter}; use crate::SePolicy; use crate::ffi::Xperm; -use base::libc::{O_CLOEXEC, O_RDONLY}; -use base::{BufReadExt, LoggedResult, Utf8CStr, error, warn}; +use base::{BufReadExt, LoggedResult, Utf8CStr, error, nix::fcntl::OFlag, warn}; pub enum Token<'a> { AL, @@ -279,7 +278,7 @@ impl SePolicy { pub fn load_rule_file(&mut self, filename: &Utf8CStr) { let result: LoggedResult<()> = try { - let file = filename.open(O_RDONLY | O_CLOEXEC)?; + let file = filename.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC)?; let mut reader = BufReader::new(file); self.load_rules_from_reader(&mut reader); };