Use nix for libc functions

This commit is contained in:
topjohnwu
2025-09-08 23:59:29 -07:00
parent 17082af438
commit ef4e230258
31 changed files with 457 additions and 475 deletions

26
native/src/Cargo.lock generated
View File

@@ -60,6 +60,7 @@ dependencies = [
"cxx", "cxx",
"cxx-gen", "cxx-gen",
"libc", "libc",
"nix",
"num-derive", "num-derive",
"num-traits", "num-traits",
"thiserror", "thiserror",
@@ -92,6 +93,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@@ -167,6 +174,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.47" version = "4.5.47"
@@ -634,6 +647,7 @@ dependencies = [
"cxx", "cxx",
"cxx-gen", "cxx-gen",
"derive", "derive",
"nix",
"num-derive", "num-derive",
"num-traits", "num-traits",
"pb-rs", "pb-rs",
@@ -713,6 +727,18 @@ dependencies = [
"adler2", "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]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"

View File

@@ -32,6 +32,7 @@ bzip2 = { version = "0.6.0" }
zopfli = "0.8.2" zopfli = "0.8.2"
lz4 = "1.28.1" lz4 = "1.28.1"
lzma-rust2 = { version = "0.13.0", default-features = false, features = ["xz", "std", "encoder", "optimization"] } lzma-rust2 = { version = "0.13.0", default-features = false, features = ["xz", "std", "encoder", "optimization"] }
nix = "0.30.1"
# Rust crypto crates are tied together # Rust crypto crates are tied together
sha1 = "0.11.0-rc.2" sha1 = "0.11.0-rc.2"

View File

@@ -23,3 +23,4 @@ bytemuck = { workspace = true }
num-traits = { workspace = true } num-traits = { workspace = true }
num-derive = { workspace = true } num-derive = { workspace = true }
const_format = { workspace = true } const_format = { workspace = true }
nix = { workspace = true, features = ["fs"] }

View File

@@ -1,5 +1,6 @@
use cxx::{ExternType, type_id}; use cxx::{ExternType, type_id};
use libc::c_char; use libc::c_char;
use nix::NixPath;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::{Ordering, min}; use std::cmp::{Ordering, min};
use std::ffi::{CStr, FromBytesWithNulError, OsStr}; use std::ffi::{CStr, FromBytesWithNulError, OsStr};
@@ -367,6 +368,26 @@ impl AsRef<Utf8CStr> 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<T, F>(&self, f: F) -> nix::Result<T>
where
F: FnOnce(&CStr) -> T,
{
Ok(f(self.as_cstr()))
}
}
// Notice that we only implement ExternType on Utf8CStr *reference* // Notice that we only implement ExternType on Utf8CStr *reference*
unsafe impl ExternType for &Utf8CStr { unsafe impl ExternType for &Utf8CStr {
type Id = type_id!("Utf8CStr"); type Id = type_id!("Utf8CStr");

View File

@@ -6,9 +6,6 @@ use std::mem::ManuallyDrop;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::os::fd::{BorrowedFd, FromRawFd, OwnedFd, RawFd}; 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::ffi::{FnBoolStr, FnBoolStrStr};
use crate::files::map_file_at; use crate::files::map_file_at;
pub(crate) use crate::xwrap::*; pub(crate) use crate::xwrap::*;
@@ -16,6 +13,9 @@ use crate::{
BufReadExt, Directory, LoggedResult, ResultExt, Utf8CStr, clone_attr, cstr, fclone_attr, BufReadExt, Directory, LoggedResult, ResultExt, Utf8CStr, clone_attr, cstr, fclone_attr,
map_fd, map_file, slice_from_ptr, 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(no_mangle)]
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { 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) { 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)) BufReader::new(file).for_each_prop(|key, value| f.call(key, value))
} }
} }

View File

@@ -3,16 +3,16 @@ use crate::{
FsPathBuilder, LibcReturn, LoggedResult, OsError, OsResult, Utf8CStr, Utf8CStrBuf, cstr, errno, FsPathBuilder, LibcReturn, LoggedResult, OsError, OsResult, Utf8CStr, Utf8CStrBuf, cstr, errno,
fd_path, fd_set_attr, 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::fs::File;
use std::marker::PhantomData; use std::ops::Deref;
use std::ops::{Deref, DerefMut}; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd};
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use std::ptr::NonNull; use std::ptr::NonNull;
use std::{mem, slice}; use std::slice;
pub struct DirEntry<'a> { pub struct DirEntry<'a> {
dir: BorrowedDirectory<'a>, dir: &'a Directory,
entry: NonNull<dirent>, entry: NonNull<dirent>,
d_name_len: usize, d_name_len: usize,
} }
@@ -23,6 +23,7 @@ impl DirEntry<'_> {
} }
pub fn name(&self) -> &Utf8CStr { pub fn name(&self) -> &Utf8CStr {
// SAFETY: Utf8CStr is already validated in Directory::read
unsafe { unsafe {
Utf8CStr::from_bytes_unchecked(slice::from_raw_parts( Utf8CStr::from_bytes_unchecked(slice::from_raw_parts(
self.d_name.as_ptr().cast(), self.d_name.as_ptr().cast(),
@@ -64,7 +65,11 @@ impl DirEntry<'_> {
} }
pub fn unlink(&self) -> OsResult<'_, ()> { 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) self.dir.unlink_at(self.name(), flag)
} }
@@ -75,7 +80,7 @@ impl DirEntry<'_> {
pub fn open_as_dir(&self) -> OsResult<'_, Directory> { pub fn open_as_dir(&self) -> OsResult<'_, Directory> {
if !self.is_dir() { if !self.is_dir() {
return Err(OsError::new( return Err(OsError::new(
libc::ENOTDIR, Errno::ENOTDIR,
"fdopendir", "fdopendir",
Some(self.name()), Some(self.name()),
None, None,
@@ -84,10 +89,10 @@ impl DirEntry<'_> {
self.dir.open_as_dir_at(self.name()) 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() { if self.is_dir() {
return Err(OsError::new( return Err(OsError::new(
libc::EISDIR, Errno::EISDIR,
"open_as_file", "open_as_file",
Some(self.name()), Some(self.name()),
None, None,
@@ -95,6 +100,14 @@ impl DirEntry<'_> {
} }
self.dir.open_as_file_at(self.name(), flags, 0) 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<'_> { impl Deref for DirEntry<'_> {
@@ -110,30 +123,6 @@ pub struct Directory {
inner: NonNull<libc::DIR>, inner: NonNull<libc::DIR>,
} }
#[repr(transparent)]
pub struct BorrowedDirectory<'a> {
inner: NonNull<libc::DIR>,
phantom: PhantomData<&'a Directory>,
}
impl Deref for BorrowedDirectory<'_> {
type Target = Directory;
fn deref(&self) -> &Directory {
// SAFETY: layout of NonNull<libc::DIR> 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<libc::DIR> 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 { pub enum WalkResult {
Continue, Continue,
Abort, Abort,
@@ -141,19 +130,14 @@ pub enum WalkResult {
} }
impl Directory { impl Directory {
fn borrow(&self) -> BorrowedDirectory<'_> { fn open_at<'a>(&self, name: &'a Utf8CStr, flags: OFlag, mode: mode_t) -> OsResult<'a, OwnedFd> {
BorrowedDirectory { nix::fcntl::openat(
inner: self.inner, self,
phantom: PhantomData, name,
} flags | OFlag::O_CLOEXEC,
} Mode::from_bits_truncate(mode),
)
fn openat<'a>(&self, name: &'a Utf8CStr, flags: i32, mode: u32) -> OsResult<'a, OwnedFd> { .into_os_result("openat", Some(name), None)
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 path_at(&self, name: &Utf8CStr, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> { fn path_at(&self, name: &Utf8CStr, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
@@ -193,7 +177,7 @@ impl Directory {
self.read() self.read()
} else { } else {
let e = DirEntry { let e = DirEntry {
dir: self.borrow(), dir: self,
entry: NonNull::from(entry), entry: NonNull::from(entry),
d_name_len: name.as_bytes_with_nul().len(), 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> { 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)) Directory::try_from(fd).map_err(|e| e.set_args(Some(name), None))
} }
pub fn open_as_file_at<'a>( pub fn open_as_file_at<'a>(
&self, &self,
name: &'a Utf8CStr, name: &'a Utf8CStr,
flags: i32, flags: OFlag,
mode: u32, mode: mode_t,
) -> OsResult<'a, File> { ) -> OsResult<'a, File> {
let fd = self.openat(name, flags, mode)?; let fd = self.open_at(name, flags, mode)?;
Ok(File::from(fd)) Ok(File::from(fd))
} }
@@ -241,14 +225,10 @@ impl Directory {
} }
pub fn mkdir_at<'a>(&self, name: &'a Utf8CStr, mode: mode_t) -> OsResult<'a, ()> { pub fn mkdir_at<'a>(&self, name: &'a Utf8CStr, mode: mode_t) -> OsResult<'a, ()> {
unsafe { match nix::sys::stat::mkdirat(self, name, Mode::from_bits_truncate(mode)) {
if libc::mkdirat(self.as_raw_fd(), name.as_ptr(), mode as mode_t) < 0 Ok(_) | Err(Errno::EEXIST) => Ok(()),
&& *errno() != EEXIST Err(e) => Err(OsError::new(e, "mkdirat", Some(name), None)),
{
return Err(OsError::last_os_error("mkdirat", Some(name), None));
}
} }
Ok(())
} }
// ln -s target self/name // ln -s target self/name
@@ -257,44 +237,36 @@ impl Directory {
name: &'a Utf8CStr, name: &'a Utf8CStr,
target: &'a Utf8CStr, target: &'a Utf8CStr,
) -> OsResult<'a, ()> { ) -> OsResult<'a, ()> {
unsafe { nix::unistd::symlinkat(target, self, name).check_os_err(
libc::symlinkat(target.as_ptr(), self.as_raw_fd(), name.as_ptr()).check_os_err( "symlinkat",
"symlinkat", Some(target),
Some(target), Some(name),
Some(name), )
)
}
} }
pub fn unlink_at<'a>(&self, name: &'a Utf8CStr, flag: i32) -> OsResult<'a, ()> { pub fn unlink_at<'a>(&self, name: &'a Utf8CStr, flag: UnlinkatFlags) -> OsResult<'a, ()> {
unsafe { nix::unistd::unlinkat(self, name, flag).check_os_err("unlinkat", Some(name), None)
libc::unlinkat(self.as_raw_fd(), name.as_ptr(), flag).check_os_err(
"unlinkat",
Some(name),
None,
)?;
}
Ok(())
} }
pub fn contains_path(&self, path: &Utf8CStr) -> bool { pub fn contains_path(&self, path: &Utf8CStr) -> bool {
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall // WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2. // does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
// Use fstatat to check the existence of a file instead. // Use fstatat to check the existence of a file instead.
unsafe { nix::sys::stat::fstatat(self, path, AtFlags::AT_SYMLINK_NOFOLLOW).is_ok()
let mut st: libc::stat = mem::zeroed();
libc::fstatat(
self.as_raw_fd(),
path.as_ptr(),
&mut st,
libc::AT_SYMLINK_NOFOLLOW,
) == 0
}
} }
pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> { pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
fd_path(self.as_raw_fd(), buf) 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. // High-level helper methods, composed of multiple operations.
@@ -328,7 +300,6 @@ impl Directory {
} }
pub fn move_into(&mut self, dir: &Directory) -> LoggedResult<()> { pub fn move_into(&mut self, dir: &Directory) -> LoggedResult<()> {
let dir_fd = self.as_raw_fd();
while let Some(ref e) = self.read()? { while let Some(ref e) = self.read()? {
if e.is_dir() && dir.contains_path(e.name()) { if e.is_dir() && dir.contains_path(e.name()) {
// Destination folder exists, needs recursive move // Destination folder exists, needs recursive move
@@ -337,16 +308,7 @@ impl Directory {
src.move_into(&dest)?; src.move_into(&dest)?;
return Ok(e.unlink()?); return Ok(e.unlink()?);
} }
e.rename_to(dir, e.name())?;
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)?;
}
} }
Ok(()) Ok(())
} }
@@ -422,9 +384,12 @@ impl Directory {
src.copy_into_impl(&dest, buf)?; src.copy_into_impl(&dest, buf)?;
fd_set_attr(dest.as_raw_fd(), &attr)?; fd_set_attr(dest.as_raw_fd(), &attr)?;
} else if e.is_file() { } else if e.is_file() {
let mut src = e.open_as_file(O_RDONLY)?; let mut src = e.open_as_file(OFlag::O_RDONLY)?;
let mut dest = let mut dest = dest_dir.open_as_file_at(
dest_dir.open_as_file_at(e.name(), O_WRONLY | O_CREAT | O_TRUNC, 0o777)?; e.name(),
OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC,
0o777,
)?;
std::io::copy(&mut src, &mut dest)?; std::io::copy(&mut src, &mut dest)?;
fd_set_attr(dest.as_raw_fd(), &attr)?; fd_set_attr(dest.as_raw_fd(), &attr)?;
} else if e.is_symlink() { } else if e.is_symlink() {
@@ -442,7 +407,6 @@ impl Directory {
dest_dir: &Directory, dest_dir: &Directory,
buf: &mut dyn Utf8CStrBuf, buf: &mut dyn Utf8CStrBuf,
) -> LoggedResult<()> { ) -> LoggedResult<()> {
let dir_fd = self.as_raw_fd();
while let Some(ref e) = self.read()? { while let Some(ref e) = self.read()? {
if e.is_dir() { if e.is_dir() {
dest_dir.mkdir_at(e.name(), 0o777)?; dest_dir.mkdir_at(e.name(), 0o777)?;
@@ -453,16 +417,8 @@ impl Directory {
src.link_into_impl(&dest, buf)?; src.link_into_impl(&dest, buf)?;
fd_set_attr(dest.as_raw_fd(), &attr)?; fd_set_attr(dest.as_raw_fd(), &attr)?;
} else { } else {
unsafe { nix::unistd::linkat(e.dir, e.name(), dest_dir, e.name(), AtFlags::empty())
libc::linkat(
dir_fd,
e.d_name.as_ptr(),
dest_dir.as_raw_fd(),
e.d_name.as_ptr(),
0,
)
.check_os_err("linkat", Some(e.name()), None)?; .check_os_err("linkat", Some(e.name()), None)?;
}
} }
} }
Ok(()) Ok(())

View File

@@ -3,20 +3,24 @@ use crate::{
cstr, errno, error, cstr, errno, error,
}; };
use bytemuck::{Pod, bytes_of, bytes_of_mut}; use bytemuck::{Pod, bytes_of, bytes_of_mut};
use libc::{ use libc::{c_uint, makedev, mode_t};
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 mem::MaybeUninit; use mem::MaybeUninit;
use nix::{
errno::Errno,
fcntl::{AT_FDCWD, OFlag},
sys::stat::{FchmodatFlags, Mode},
unistd::AccessFlags,
};
use num_traits::AsPrimitive; use num_traits::AsPrimitive;
use std::cmp::min; use std::cmp::min;
use std::ffi::CStr; use std::ffi::CStr;
use std::fmt::Display; use std::fmt::Display;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write}; use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
use std::ops::Deref;
use std::os::fd::{AsFd, BorrowedFd}; use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::ffi::OsStrExt; 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::path::Path;
use std::{io, mem, ptr, slice}; use std::{io, mem, ptr, slice};
@@ -139,15 +143,12 @@ impl FileOrStd {
} }
} }
fn open_fd(path: &Utf8CStr, flags: i32, mode: mode_t) -> OsResult<'_, OwnedFd> { fn open_fd(path: &Utf8CStr, flags: OFlag, mode: mode_t) -> OsResult<'_, OwnedFd> {
unsafe { nix::fcntl::open(path, flags, Mode::from_bits_truncate(mode)).into_os_result(
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).into_os_result( "open",
"open", Some(path),
Some(path), None,
None, )
)?;
Ok(OwnedFd::from_raw_fd(fd))
}
} }
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> { pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
@@ -225,29 +226,24 @@ impl Utf8CStr {
unsafe { mem::transmute(self) } 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)?)) Ok(File::from(open_fd(self, flags, 0)?))
} }
pub fn create(&self, flags: i32, mode: mode_t) -> OsResult<'_, File> { pub fn create(&self, flags: OFlag, mode: mode_t) -> OsResult<'_, File> {
Ok(File::from(open_fd(self, O_CREAT | flags, mode)?)) Ok(File::from(open_fd(self, OFlag::O_CREAT | flags, mode)?))
} }
pub fn exists(&self) -> bool { pub fn exists(&self) -> bool {
unsafe { nix::sys::stat::lstat(self).is_ok()
let mut st: stat = mem::zeroed();
libc::lstat(self.as_ptr(), &mut st) == 0
}
} }
pub fn rename_to<'a>(&'a self, name: &'a Utf8CStr) -> OsResult<'a, ()> { pub fn rename_to<'a>(&'a self, name: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe { nix::fcntl::renameat(AT_FDCWD, self, AT_FDCWD, name).check_os_err(
libc::rename(self.as_ptr(), name.as_ptr()).check_os_err( "rename",
"rename", Some(self),
Some(self), Some(name),
Some(name), )
)
}
} }
pub fn remove(&self) -> OsResult<'_, ()> { pub fn remove(&self) -> OsResult<'_, ()> {
@@ -267,73 +263,74 @@ impl Utf8CStr {
} }
pub fn mkdir(&self, mode: mode_t) -> OsResult<'_, ()> { pub fn mkdir(&self, mode: mode_t) -> OsResult<'_, ()> {
unsafe { match nix::unistd::mkdir(self, Mode::from_bits_truncate(mode)) {
if libc::mkdir(self.as_ptr(), mode) < 0 { Ok(_) | Err(Errno::EEXIST) => Ok(()),
if *errno() == EEXIST { Err(e) => Err(OsError::new(e, "mkdir", Some(self), None)),
libc::chmod(self.as_ptr(), mode).check_os_err("chmod", Some(self), None)?;
} else {
return Err(OsError::last_os_error("mkdir", Some(self), None));
}
}
} }
Ok(())
} }
// Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp // Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp
pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> { pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
let fd = self.open(O_PATH | O_CLOEXEC)?; let fd = self.open(OFlag::O_PATH | OFlag::O_CLOEXEC)?;
let mut st1: libc::stat;
let mut st2: libc::stat;
let mut skip_check = false; let mut skip_check = false;
unsafe {
st1 = mem::zeroed(); let st1 = match nix::sys::stat::fstat(&fd) {
if libc::fstat(fd.as_raw_fd(), &mut st1) < 0 { Ok(st) => st,
Err(_) => {
// This will only fail on Linux < 3.6 // This will only fail on Linux < 3.6
skip_check = true; skip_check = true;
unsafe { mem::zeroed() }
} }
} };
fd_path(fd.as_raw_fd(), buf)?; fd_path(fd.as_raw_fd(), buf)?;
unsafe {
st2 = mem::zeroed(); let st2 = nix::sys::stat::stat(buf.as_cstr()).into_os_result("stat", Some(self), None)?;
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) {
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));
*errno() = ENOENT;
return Err(OsError::last_os_error("realpath", Some(self), None));
}
} }
Ok(()) Ok(())
} }
pub fn get_attr(&self) -> OsResult<'_, FileAttr> { pub fn get_attr(&self) -> OsResult<'_, FileAttr> {
let mut attr = FileAttr::new(); #[allow(unused_mut)]
unsafe { let mut attr = FileAttr {
libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?; st: nix::sys::stat::lstat(self).into_os_result("lstat", Some(self), None)?,
#[cfg(feature = "selinux")] #[cfg(feature = "selinux")]
self.get_secontext(&mut attr.con)?; con: cstr::buf::new(),
} };
#[cfg(feature = "selinux")]
self.get_secontext(&mut attr.con)?;
Ok(attr) Ok(attr)
} }
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> { pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
unsafe { if !attr.is_symlink()
if !attr.is_symlink() && libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()) < 0 && let Err(e) = nix::sys::stat::fchmodat(
{ AT_FDCWD,
let self_attr = self.get_attr()?; self,
if !self_attr.is_symlink() { Mode::from_bits_truncate((attr.st.st_mode & 0o777).as_()),
return Err(OsError::last_os_error("chmod", Some(self), None)); 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( libc::lchown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).check_os_err(
"lchown", "lchown",
Some(self), Some(self),
None, None,
)?; )?;
}
#[cfg(feature = "selinux")] #[cfg(feature = "selinux")]
if !attr.con.is_empty() { if !attr.con.is_empty() {
self.set_secontext(&attr.con)?; self.set_secontext(&attr.con)?;
}
} }
Ok(()) Ok(())
} }
@@ -388,17 +385,19 @@ impl Utf8CStr {
// ln -s target self // ln -s target self
pub fn create_symlink_to<'a>(&'a self, target: &'a Utf8CStr) -> OsResult<'a, ()> { pub fn create_symlink_to<'a>(&'a self, target: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe { nix::unistd::symlinkat(target, AT_FDCWD, self).check_os_err(
libc::symlink(target.as_ptr(), self.as_ptr()).check_os_err( "symlink",
"symlink", Some(target),
Some(target), Some(self),
Some(self), )
)
}
} }
pub fn mkfifo(&self, mode: mode_t) -> OsResult<'_, ()> { 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<()> { pub fn remove_all(&self) -> LoggedResult<()> {
let attr = self.get_attr()?; let attr = self.get_attr()?;
if attr.is_dir() { 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()?; dir.remove_all()?;
} }
Ok(self.remove()?) Ok(self.remove()?)
@@ -431,12 +430,7 @@ impl Utf8CStr {
break; break;
}; };
path.append_path(s); path.append_path(s);
path.mkdir(mode)?;
unsafe {
if libc::mkdir(path.as_ptr(), mode) < 0 && *errno() != EEXIST {
return Err(OsError::last_os_error("mkdir", Some(&path), None))?;
}
}
} }
*errno() = 0; *errno() = 0;
@@ -454,8 +448,11 @@ impl Utf8CStr {
// It's OK if remove failed // It's OK if remove failed
path.remove().ok(); path.remove().ok();
if attr.is_file() { if attr.is_file() {
let mut src = self.open(O_RDONLY | O_CLOEXEC)?; let mut src = self.open(OFlag::O_RDONLY | OFlag::O_CLOEXEC)?;
let mut dest = path.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o777)?; 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)?; std::io::copy(&mut src, &mut dest)?;
} else if attr.is_symlink() { } else if attr.is_symlink() {
let mut buf = cstr::buf::default(); let mut buf = cstr::buf::default();
@@ -512,37 +509,41 @@ impl Utf8CStr {
impl FsPathFollow { impl FsPathFollow {
pub fn exists(&self) -> bool { 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> { pub fn get_attr(&self) -> OsResult<'_, FileAttr> {
let mut attr = FileAttr::new(); #[allow(unused_mut)]
unsafe { let mut attr = FileAttr {
libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?; st: nix::sys::stat::stat(self.deref()).into_os_result("lstat", Some(self), None)?,
#[cfg(feature = "selinux")] #[cfg(feature = "selinux")]
self.get_secontext(&mut attr.con)?; con: cstr::buf::new(),
} };
#[cfg(feature = "selinux")]
self.get_secontext(&mut attr.con)?;
Ok(attr) Ok(attr)
} }
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> { 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 { 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( libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).check_os_err(
"chown", "chown",
Some(self), Some(self),
None, None,
)?; )?;
}
#[cfg(feature = "selinux")] #[cfg(feature = "selinux")]
if !attr.con.is_empty() { if !attr.con.is_empty() {
self.set_secontext(&attr.con)?; self.set_secontext(&attr.con)?;
}
} }
Ok(()) Ok(())
} }
@@ -751,7 +752,7 @@ unsafe extern "C" {
// We mark the returned slice static because it is valid until explicitly unmapped // 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]> { 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>( pub(crate) fn map_file_at<'a>(
@@ -765,17 +766,9 @@ pub(crate) fn map_file_at<'a>(
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
const BLKGETSIZE64: u32 = 0x80041272; const BLKGETSIZE64: u32 = 0x80041272;
let flag = if rw { O_RDWR } else { O_RDONLY }; let flag = if rw { OFlag::O_RDWR } else { OFlag::O_RDONLY };
let fd = unsafe { let fd = nix::fcntl::openat(dirfd, path, flag | OFlag::O_CLOEXEC, Mode::empty())
OwnedFd::from_raw_fd( .into_os_result("openat", Some(path), None)?;
libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flag | O_CLOEXEC).into_os_result(
"openat",
Some(path),
None,
)?,
)
};
let attr = fd_get_attr(fd.as_raw_fd())?; let attr = fd_get_attr(fd.as_raw_fd())?;
let sz = if attr.is_block_device() { let sz = if attr.is_block_device() {
let mut sz = 0_u64; let mut sz = 0_u64;
@@ -881,7 +874,7 @@ fn parse_mount_info_line(line: &str) -> Option<MountInfo> {
pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> { pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
let mut res = vec![]; let mut res = vec![];
let mut path = format!("/proc/{pid}/mountinfo"); 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| { BufReader::new(file).for_each_line(|line| {
parse_mount_info_line(line) parse_mount_info_line(line)
.map(|info| res.push(info)) .map(|info| res.push(info))
@@ -890,19 +883,3 @@ pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
} }
res 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]),
})
}
}

View File

@@ -2,6 +2,7 @@
pub use const_format; pub use const_format;
pub use libc; pub use libc;
pub use nix;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
pub use cstr::{ pub use cstr::{

View File

@@ -1,9 +1,10 @@
use crate::logging::Formatter; 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::fmt::Display;
use std::panic::Location; use std::panic::Location;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::{fmt, io};
// Error handling throughout the Rust codebase in Magisk: // Error handling throughout the Rust codebase in Magisk:
// //
@@ -217,7 +218,7 @@ where
{ {
type Value; type Value;
fn into_result(self) -> Result<Self::Value, i32>; fn check_err(self) -> nix::Result<Self::Value>;
fn into_os_result<'a>( fn into_os_result<'a>(
self, self,
@@ -225,7 +226,7 @@ where
arg1: Option<&'a str>, arg1: Option<&'a str>,
arg2: Option<&'a str>, arg2: Option<&'a str>,
) -> OsResult<'a, Self::Value> { ) -> OsResult<'a, Self::Value> {
self.into_result() self.check_err()
.map_err(|e| OsError::new(e, name, arg1, arg2)) .map_err(|e| OsError::new(e, name, arg1, arg2))
} }
@@ -235,16 +236,10 @@ where
arg1: Option<&'a str>, arg1: Option<&'a str>,
arg2: Option<&'a str>, arg2: Option<&'a str>,
) -> OsResult<'a, ()> { ) -> OsResult<'a, ()> {
self.into_result() self.check_err()
.map(|_| ()) .map(|_| ())
.map_err(|e| OsError::new(e, name, arg1, arg2)) .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 { macro_rules! impl_libc_return {
@@ -253,9 +248,9 @@ macro_rules! impl_libc_return {
type Value = Self; type Value = Self;
#[inline(always)] #[inline(always)]
fn into_result(self) -> Result<Self::Value, i32> { fn check_err(self) -> nix::Result<Self::Value> {
if self < 0 { if self < 0 {
Err(*errno()) Err(Errno::last())
} else { } else {
Ok(self) Ok(self)
} }
@@ -270,14 +265,23 @@ impl<T> LibcReturn for *mut T {
type Value = NonNull<T>; type Value = NonNull<T>;
#[inline(always)] #[inline(always)]
fn into_result(self) -> Result<Self::Value, i32> { fn check_err(self) -> nix::Result<Self::Value> {
NonNull::new(self).ok_or_else(|| *errno()) NonNull::new(self).ok_or_else(Errno::last)
}
}
impl<T> LibcReturn for nix::Result<T> {
type Value = T;
#[inline(always)]
fn check_err(self) -> Self {
self
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct OsError<'a> { pub struct OsError<'a> {
code: i32, errno: Errno,
name: &'static str, name: &'static str,
arg1: Option<&'a str>, arg1: Option<&'a str>,
arg2: Option<&'a str>, arg2: Option<&'a str>,
@@ -285,13 +289,13 @@ pub struct OsError<'a> {
impl OsError<'_> { impl OsError<'_> {
pub fn new<'a>( pub fn new<'a>(
code: i32, errno: Errno,
name: &'static str, name: &'static str,
arg1: Option<&'a str>, arg1: Option<&'a str>,
arg2: Option<&'a str>, arg2: Option<&'a str>,
) -> OsError<'a> { ) -> OsError<'a> {
OsError { OsError {
code, errno,
name, name,
arg1, arg1,
arg2, arg2,
@@ -303,33 +307,28 @@ impl OsError<'_> {
arg1: Option<&'a str>, arg1: Option<&'a str>,
arg2: Option<&'a str>, arg2: Option<&'a str>,
) -> OsError<'a> { ) -> 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> { pub fn set_args<'a>(self, arg1: Option<&'a str>, arg2: Option<&'a str>) -> OsError<'a> {
Self::new(self.code, self.name, arg1, arg2) Self::new(self.errno, self.name, arg1, arg2)
}
fn as_io_error(&self) -> io::Error {
io::Error::from_raw_os_error(self.code)
} }
} }
impl Display for OsError<'_> { impl Display for OsError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let error = self.as_io_error();
if self.name.is_empty() { if self.name.is_empty() {
write!(f, "{error:#}") write!(f, "{}", self.errno)
} else { } else {
match (self.arg1, self.arg2) { match (self.arg1, self.arg2) {
(Some(arg1), Some(arg2)) => { (Some(arg1), Some(arg2)) => {
write!(f, "{} '{}' '{}': {:#}", self.name, arg1, arg2, error) write!(f, "{} '{arg1}' '{arg2}': {}", self.name, self.errno)
} }
(Some(arg1), None) => { (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)
} }
} }
} }

View File

@@ -1,9 +1,7 @@
// Functions in this file are only for exporting to C++, DO NOT USE IN RUST // Functions in this file are only for exporting to C++, DO NOT USE IN RUST
use crate::cxx_extern::readlinkat; use crate::cxx_extern::readlinkat;
use crate::{ use crate::{Directory, LibcReturn, ResultExt, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut};
BorrowedDirectory, 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 libc::{c_char, c_uint, c_ulong, c_void, dev_t, mode_t, off_t, sockaddr, socklen_t};
use std::ffi::CStr; use std::ffi::CStr;
use std::fs::File; use std::fs::File;
@@ -185,7 +183,7 @@ extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
unsafe extern "C" fn xreaddir(mut dir: BorrowedDirectory) -> *mut libc::dirent { unsafe extern "C" fn xreaddir(mut dir: ManuallyDrop<Directory>) -> *mut libc::dirent {
dir.read() dir.read()
.log() .log()
.ok() .ok()

View File

@@ -8,7 +8,7 @@ use crate::sign::{sha1_hash, sign_boot_image};
use argh::{CommandInfo, EarlyExit, FromArgs, SubCommand}; use argh::{CommandInfo, EarlyExit, FromArgs, SubCommand};
use base::{ use base::{
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, PositionalArgParser, ResultExt, Utf8CStr, 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::ffi::c_char;
use std::io::{Seek, SeekFrom, Write}; use std::io::{Seek, SeekFrom, Write};
@@ -333,7 +333,7 @@ fn sign_cmd(
let sig = sign_boot_image(img.payload(), name, cert, key)?; let sig = sign_boot_image(img.payload(), name, cert, key)?;
let tail_off = img.tail_off(); let tail_off = img.tail_off();
drop(img); 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.seek(SeekFrom::Start(tail_off))?;
fd.write_all(&sig)?; fd.write_all(&sig)?;
let current = fd.stream_position()?; let current = fd.stream_position()?;

View File

@@ -1,7 +1,7 @@
use crate::ffi::{FileFormat, check_fmt}; use crate::ffi::{FileFormat, check_fmt};
use base::libc::{O_RDONLY, O_TRUNC, O_WRONLY};
use base::{ use base::{
Chunker, FileOrStd, LoggedResult, ReadExt, ResultExt, Utf8CStr, Utf8CString, WriteExt, log_err, Chunker, FileOrStd, LoggedResult, ReadExt, ResultExt, Utf8CStr, Utf8CString, WriteExt, log_err,
nix::fcntl::OFlag,
}; };
use bzip2::{Compression as BzCompression, read::BzDecoder, write::BzEncoder}; use bzip2::{Compression as BzCompression, read::BzDecoder, write::BzEncoder};
use flate2::{Compression as GzCompression, read::MultiGzDecoder, write::GzEncoder}; 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 { let input = if in_std {
FileOrStd::StdIn FileOrStd::StdIn
} else { } else {
FileOrStd::File(infile.open(O_RDONLY)?) FileOrStd::File(infile.open(OFlag::O_RDONLY)?)
}; };
// First read some bytes for format detection // First read some bytes for format detection
@@ -316,7 +316,7 @@ pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> L
if outfile == "-" { if outfile == "-" {
FileOrStd::StdOut FileOrStd::StdOut
} else { } 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 { } else if in_std {
FileOrStd::StdOut FileOrStd::StdOut
@@ -332,7 +332,7 @@ pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> L
rm_in = true; rm_in = true;
eprintln!("Decompressing to [{outfile}]"); 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())); 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 { let input = if in_std {
FileOrStd::StdIn FileOrStd::StdIn
} else { } else {
FileOrStd::File(infile.open(O_RDONLY)?) FileOrStd::File(infile.open(OFlag::O_RDONLY)?)
}; };
let output = if let Some(outfile) = outfile { let output = if let Some(outfile) = outfile {
if outfile == "-" { if outfile == "-" {
FileOrStd::StdOut FileOrStd::StdOut
} else { } 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 { } else if in_std {
FileOrStd::StdOut FileOrStd::StdOut
@@ -374,7 +374,7 @@ pub(crate) fn compress_cmd(
outfile.write_str(method.ext()).ok(); outfile.write_str(method.ext()).ok();
eprintln!("Compressing to [{outfile}]"); eprintln!("Compressing to [{outfile}]");
rm_in = true; 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) FileOrStd::File(outfile)
}; };

View File

@@ -14,20 +14,19 @@ use bytemuck::{Pod, Zeroable, from_bytes};
use num_traits::cast::AsPrimitive; use num_traits::cast::AsPrimitive;
use size::{Base, Size, Style}; 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::check_env;
use crate::compress::{get_decoder, get_encoder}; use crate::compress::{get_decoder, get_encoder};
use crate::ffi::FileFormat; use crate::ffi::FileFormat;
use crate::patch::{patch_encryption, patch_verity}; 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)] #[derive(FromArgs)]
struct CpioCommand { struct CpioCommand {
@@ -349,7 +348,10 @@ impl Cpio {
match entry.mode & S_IFMT { match entry.mode & S_IFMT {
S_IFDIR => out.mkdir(mode)?, S_IFDIR => out.mkdir(mode)?,
S_IFREG => { 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)?; file.write_all(&entry.data)?;
} }
S_IFLNK => { S_IFLNK => {
@@ -402,7 +404,8 @@ impl Cpio {
let mode = if attr.is_file() || attr.is_symlink() { let mode = if attr.is_file() || attr.is_symlink() {
rdevmajor = 0; rdevmajor = 0;
rdevminor = 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 mode | S_IFREG
} else { } else {
rdevmajor = major(attr.st.st_rdev.as_()).as_(); rdevmajor = major(attr.st.st_rdev.as_()).as_();

View File

@@ -26,3 +26,4 @@ bytemuck = { workspace = true, features = ["derive"] }
thiserror = { workspace = true } thiserror = { workspace = true }
bit-set = { workspace = true } bit-set = { workspace = true }
argh = { workspace = true } argh = { workspace = true }
nix = { workspace = true, features = ["fs", "poll", "signal"] }

View File

@@ -18,11 +18,11 @@ use crate::socket::IpcWrite;
use crate::su::SuInfo; use crate::su::SuInfo;
use crate::zygisk::ZygiskState; use crate::zygisk::ZygiskState;
use base::const_format::concatcp; use base::const_format::concatcp;
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
use base::{ use base::{
AtomicArc, BufReadExt, FsPathBuilder, ReadExt, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt, AtomicArc, BufReadExt, FsPathBuilder, ReadExt, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
cstr, error, info, libc, cstr, error, info, libc,
}; };
use nix::fcntl::OFlag;
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::fs::File; use std::fs::File;
use std::io::{BufReader, Write}; use std::io::{BufReader, Write};
@@ -304,7 +304,9 @@ pub fn daemon_entry() {
unsafe { libc::setsid() }; unsafe { libc::setsid() };
// Make sure the current context is magisk // 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); let con = cstr!(MAGISK_PROC_CON);
current.write_all(con.as_bytes_with_nul()).log_ok(); current.write_all(con.as_bytes_with_nul()).log_ok();
} }
@@ -323,7 +325,7 @@ pub fn daemon_entry() {
.join_path(magisk_tmp) .join_path(magisk_tmp)
.join_path(MAIN_CONFIG); .join_path(MAIN_CONFIG);
let mut is_recovery = false; 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| { BufReader::new(main_config).for_each_prop(|key, val| {
if key == "RECOVERYMODE" { if key == "RECOVERYMODE" {
is_recovery = val == "true"; is_recovery = val == "true";
@@ -335,7 +337,7 @@ pub fn daemon_entry() {
tmp_path.truncate(magisk_tmp.len()); tmp_path.truncate(magisk_tmp.len());
let mut sdk_int = -1; 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| { BufReader::new(build_prop).for_each_prop(|key, val| {
if key == "ro.build.version.sdk" { if key == "ro.build.version.sdk" {
sdk_int = val.parse::<i32>().unwrap_or(-1); sdk_int = val.parse::<i32>().unwrap_or(-1);
@@ -370,7 +372,7 @@ pub fn daemon_entry() {
// Cleanup pre-init mounts // Cleanup pre-init mounts
tmp_path.append_path(ROOTMNT); 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| { BufReader::new(mount_list).for_each_line(|line| {
line.truncate(line.trim_end().len()); line.truncate(line.trim_end().len());
let item = Utf8CStr::from_string(line); let item = Utf8CStr::from_string(line);
@@ -407,7 +409,7 @@ fn switch_cgroup(cgroup: &str, pid: i32) {
if !buf.exists() { if !buf.exists() {
return; 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.clear();
buf.write_fmt(format_args!("{pid}")).ok(); buf.write_fmt(format_args!("{pid}")).ok();
file.write_all(buf.as_bytes()).log_ok(); file.write_all(buf.as_bytes()).log_ok();
@@ -415,7 +417,7 @@ fn switch_cgroup(cgroup: &str, pid: i32) {
} }
fn check_data() -> bool { 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; let mut mnt = false;
BufReader::new(file).for_each_line(|line| { BufReader::new(file).for_each_line(|line| {
if line.contains(" /data ") && !line.contains("tmpfs") { if line.contains(" /data ") && !line.contains("tmpfs") {

View File

@@ -4,13 +4,14 @@ use crate::ffi::{ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp};
use crate::mount::setup_module_mount; use crate::mount::setup_module_mount;
use crate::resetprop::load_prop_file; use crate::resetprop::load_prop_file;
use base::{ use base::{
DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResult, ResultExt, DirEntry, Directory, FsPathBuilder, LoggedResult, OsResult, ResultExt, SilentLogExt, Utf8CStr,
SilentLogExt, Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc, raw_cstr,
info, libc, raw_cstr, warn, 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::collections::BTreeMap;
use std::os::fd::{AsRawFd, IntoRawFd}; use std::os::fd::IntoRawFd;
use std::path::{Component, Path}; use std::path::{Component, Path};
use std::ptr; use std::ptr;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@@ -51,7 +52,7 @@ fn mount_dummy<'a>(
if is_dir { if is_dir {
dest.mkdir(0o000)?; dest.mkdir(0o000)?;
} else { } 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); bind_mount(reason, src, dest, false);
Ok(()) Ok(())
@@ -563,7 +564,6 @@ fn inject_zygisk_bins(name: &str, system: &mut FsNode) {
fn upgrade_modules() -> LoggedResult<()> { fn upgrade_modules() -> LoggedResult<()> {
let mut upgrade = Directory::open(cstr!(MODULEUPGRADE)).silent()?; let mut upgrade = Directory::open(cstr!(MODULEUPGRADE)).silent()?;
let ufd = upgrade.as_raw_fd();
let root = Directory::open(cstr!(MODULEROOT))?; let root = Directory::open(cstr!(MODULEROOT))?;
while let Some(e) = upgrade.read()? { while let Some(e) = upgrade.read()? {
if !e.is_dir() { 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 // If the old module is disabled, we need to also disable the new one
disable = module.contains_path(cstr!("disable")); disable = module.contains_path(cstr!("disable"));
module.remove_all()?; module.remove_all()?;
root.unlink_at(module_name, AT_REMOVEDIR)?; root.unlink_at(module_name, UnlinkatFlags::RemoveDir)?;
} }
info!("Upgrade / New module: {module_name}"); info!("Upgrade / New module: {module_name}");
unsafe { e.rename_to(&root, module_name)?;
libc::renameat(
ufd,
module_name.as_ptr(),
root.as_raw_fd(),
module_name.as_ptr(),
)
.check_os_err("renameat", Some(module_name), None)?;
}
if disable { if disable {
let path = cstr::buf::default() let path = cstr::buf::default()
.join_path(module_name) .join_path(module_name)
.join_path("disable"); .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()?; upgrade.remove_all()?;
@@ -614,7 +610,11 @@ fn for_each_module(mut func: impl FnMut(&DirEntry) -> LoggedResult<()>) -> Logge
pub fn disable_modules() { pub fn disable_modules() {
for_each_module(|e| { for_each_module(|e| {
let dir = e.open_as_dir()?; 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(()) Ok(())
}) })
.log_ok(); .log_ok();
@@ -656,7 +656,8 @@ fn collect_modules(zygisk_enabled: bool, open_zygisk: bool) -> Vec<ModuleInfo> {
e.unlink()?; e.unlink()?;
return Ok(()); return Ok(());
} }
dir.unlink_at(cstr!("update"), 0).ok(); dir.unlink_at(cstr!("update"), UnlinkatFlags::NoRemoveDir)
.ok();
if dir.contains_path(cstr!("disable")) { if dir.contains_path(cstr!("disable")) {
return Ok(()); return Ok(());
} }
@@ -673,7 +674,7 @@ fn collect_modules(zygisk_enabled: bool, open_zygisk: bool) -> Vec<ModuleInfo> {
} }
fn open_fd_safe(dir: &Directory, name: &Utf8CStr) -> i32 { 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() .log()
.map(IntoRawFd::into_raw_fd) .map(IntoRawFd::into_raw_fd)
.unwrap_or(-1) .unwrap_or(-1)
@@ -702,7 +703,8 @@ fn collect_modules(zygisk_enabled: bool, open_zygisk: bool) -> Vec<ModuleInfo> {
{ {
z64 = open_fd_safe(&dir, cstr!("zygisk/riscv64.so")); 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 { } else {
// Ignore zygisk modules when zygisk is not enabled // Ignore zygisk modules when zygisk is not enabled

View File

@@ -200,9 +200,8 @@ pub fn find_preinit_device() -> String {
libc::S_IFBLK | 0o600, libc::S_IFBLK | 0o600,
info.device as dev_t, info.device as dev_t,
) )
.check_io_err() .check_os_err("mknod", Some(dev_path), None)
.log() .log_ok();
.ok();
} }
} }
} }

View File

@@ -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::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 crate::ffi::{DbEntryKey, get_magisk_tmp, install_apk, uninstall_pkg};
use base::WalkResult::{Abort, Continue, Skip}; use base::WalkResult::{Abort, Continue, Skip};
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
use base::{ use base::{
BufReadExt, Directory, FsPathBuilder, LoggedResult, ReadExt, ResultExt, Utf8CStrBuf, BufReadExt, Directory, FsPathBuilder, LoggedResult, ReadExt, ResultExt, Utf8CStrBuf,
Utf8CString, cstr, error, fd_get_attr, warn, Utf8CString, cstr, error, fd_get_attr, warn,
}; };
use bit_set::BitSet; use bit_set::BitSet;
use nix::fcntl::OFlag;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
@@ -238,7 +238,7 @@ impl ManagerInfo {
.join_path("dyn") .join_path("dyn")
.join_path("current.apk"); .join_path("current.apk");
let uid: i32; 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) => { Ok(mut fd) => {
uid = fd_get_attr(fd.as_raw_fd()) uid = fd_get_attr(fd.as_raw_fd())
.map(|attr| attr.st.st_uid as i32) .map(|attr| attr.st.st_uid as i32)
@@ -270,7 +270,7 @@ impl ManagerInfo {
return Status::NotInstalled; 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), Ok(mut fd) => read_certificate(&mut fd, -1),
Err(_) => return Status::NotInstalled, Err(_) => return Status::NotInstalled,
}; };
@@ -293,7 +293,7 @@ impl ManagerInfo {
return Status::NotInstalled; 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), Ok(mut fd) => read_certificate(&mut fd, MAGISK_VER_CODE),
Err(_) => return Status::NotInstalled, Err(_) => return Status::NotInstalled,
}; };
@@ -317,8 +317,10 @@ impl ManagerInfo {
let tmp_apk = cstr!("/data/stub.apk"); let tmp_apk = cstr!("/data/stub.apk");
let result: LoggedResult<()> = try { let result: LoggedResult<()> = try {
{ {
let mut tmp_apk_file = let mut tmp_apk_file = tmp_apk.create(
tmp_apk.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o600)?; OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC | OFlag::O_CLOEXEC,
0o600,
)?;
io::copy(stub_fd, &mut tmp_apk_file)?; io::copy(stub_fd, &mut tmp_apk_file)?;
} }
// Seek the fd back to start // Seek the fd back to start
@@ -445,7 +447,7 @@ impl MagiskD {
.join_path(get_magisk_tmp()) .join_path(get_magisk_tmp())
.join_path("stub.apk"); .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); info.trusted_cert = read_certificate(&mut fd, MAGISK_VER_CODE);
// Seek the fd back to start // Seek the fd back to start
fd.seek(SeekFrom::Start(0)).log_ok(); fd.seek(SeekFrom::Start(0)).log_ok();

View File

@@ -6,8 +6,9 @@ use argh::{EarlyExit, FromArgs, MissingRequirements};
use base::libc::PROP_VALUE_MAX; use base::libc::PROP_VALUE_MAX;
use base::{ use base::{
BufReadExt, CmdArgs, EarlyExitExt, LogLevel, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, 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::collections::BTreeMap;
use std::ffi::c_char; use std::ffi::c_char;
use std::io::BufReader; use std::io::BufReader;
@@ -211,7 +212,7 @@ impl ResetProp {
} }
fn load_file(&self, file: &Utf8CStr) -> LoggedResult<()> { 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 key = cstr::buf::dynamic(128);
let mut val = cstr::buf::dynamic(128); let mut val = cstr::buf::dynamic(128);
BufReader::new(fd).for_each_prop(|k, v| { BufReader::new(fd).for_each_prop(|k, v| {

View File

@@ -1,3 +1,5 @@
use nix::fcntl::OFlag;
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
use std::io::Read; use std::io::Read;
use std::{ use std::{
fs::File, fs::File,
@@ -5,14 +7,11 @@ use std::{
os::fd::FromRawFd, os::fd::FromRawFd,
}; };
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
use crate::resetprop::PropReader; use crate::resetprop::PropReader;
use crate::resetprop::proto::persistent_properties::{ use crate::resetprop::proto::persistent_properties::{
PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord, PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord,
}; };
use base::const_format::concatcp; use base::const_format::concatcp;
use base::libc::{O_CLOEXEC, O_RDONLY};
use base::{ use base::{
Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentLogExt, Utf8CStr, Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentLogExt, Utf8CStr,
Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, log_err, Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, log_err,
@@ -46,7 +45,7 @@ fn file_get_prop(name: &Utf8CStr) -> LoggedResult<String> {
let path = cstr::buf::default() let path = cstr::buf::default()
.join_path(PERSIST_PROP_DIR) .join_path(PERSIST_PROP_DIR)
.join_path(name); .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); debug!("resetprop: read prop from [{}]", path);
let mut s = String::new(); let mut s = String::new();
file.read_to_string(&mut s)?; file.read_to_string(&mut s)?;

View File

@@ -1,7 +1,7 @@
use crate::consts::{DATABIN, LOG_PIPE, MAGISK_LOG_CON, MAGISKDB, MODULEROOT, SECURE_DIR}; use crate::consts::{DATABIN, LOG_PIPE, MAGISK_LOG_CON, MAGISKDB, MODULEROOT, SECURE_DIR};
use crate::ffi::get_magisk_tmp; use crate::ffi::get_magisk_tmp;
use base::libc::{O_CLOEXEC, O_WRONLY};
use base::{Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, libc}; use base::{Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, libc};
use nix::fcntl::OFlag;
use std::io::Write; use std::io::Write;
const UNLABEL_CON: &Utf8CStr = cstr!("u:object_r:unlabeled:s0"); 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() { pub(crate) fn restorecon() {
if let Ok(mut file) = cstr!("/sys/fs/selinux/context") if let Ok(mut file) = cstr!("/sys/fs/selinux/context")
.open(O_WRONLY | O_CLOEXEC) .open(OFlag::O_WRONLY | OFlag::O_CLOEXEC)
.log() .log()
&& file.write_all(ADB_CON.as_bytes_with_nul()).is_ok() && file.write_all(ADB_CON.as_bytes_with_nul()).is_ok()
{ {

View File

@@ -6,11 +6,16 @@ use crate::ffi::{SuPolicy, SuRequest, get_magisk_tmp};
use crate::socket::IpcRead; use crate::socket::IpcRead;
use ExtraVal::{Bool, Int, IntList, Str}; use ExtraVal::{Bool, Int, IntList, Str};
use base::{ 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 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> { struct Extra<'a> {
key: &'static str, key: &'static str,
@@ -171,7 +176,7 @@ impl SuAppContext<'_> {
attr.st.st_mode = 0o600; attr.st.st_mode = 0o600;
attr.st.st_uid = self.info.mgr_uid.as_(); attr.st.st_uid = self.info.mgr_uid.as_();
attr.st.st_gid = 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.mkfifo(0o600)?;
fifo.set_attr(&attr)?; fifo.set_attr(&attr)?;
@@ -193,20 +198,12 @@ impl SuAppContext<'_> {
self.exec_cmd("request", &extras, false); self.exec_cmd("request", &extras, false);
// Open with O_RDWR to prevent FIFO open block // 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 // Wait for data input for at most 70 seconds
let mut pfd = PollFd { nix::poll::poll(&mut pfd, PollTimeout::try_from(70 * 1000).unwrap())
fd: fd.as_raw_fd(), .check_os_err("poll", None, None)?;
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))?;
}
fd fd
}; };

View File

@@ -1,21 +1,20 @@
use base::libc::ssize_t; use base::{FileOrStd, LibcReturn, LoggedResult, OsResult, libc, warn};
use base::{
LibcReturn, LoggedResult, OsResult, PipeFd, ReadExt, ResultExt, error, libc, log_err,
make_pipe, warn,
};
use bytemuck::{Pod, Zeroable};
use libc::{ use libc::{
O_CLOEXEC, POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, c_int, cfmakeraw,
TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, cfmakeraw, close, poll, pollfd, raise, sigaddset, ssize_t, tcgetattr, tcsetattr, termios, winsize,
sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_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::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::mem::{ManuallyDrop, MaybeUninit}; 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::sync::atomic::{AtomicBool, Ordering};
use std::{ffi::c_int, ptr::null_mut};
static mut OLD_STDIN: Option<termios> = None; static mut OLD_STDIN: Option<termios> = None;
static SHOULD_USE_SPLICE: AtomicBool = AtomicBool::new(true); static SHOULD_USE_SPLICE: AtomicBool = AtomicBool::new(true);
@@ -97,13 +96,13 @@ fn pump_via_copy(infd: RawFd, outfd: RawFd) -> LoggedResult<()> {
Ok(()) 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) { if !SHOULD_USE_SPLICE.load(Ordering::Acquire) {
return pump_via_copy(infd, outfd); return pump_via_copy(infd, outfd);
} }
// The pipe capacity is by default 16 pages, let's just use 65536 // 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 // If splice failed, stop using splice and fallback to userspace copy
SHOULD_USE_SPLICE.store(false, Ordering::Release); SHOULD_USE_SPLICE.store(false, Ordering::Release);
return pump_via_copy(infd, outfd); return pump_via_copy(infd, outfd);
@@ -111,85 +110,70 @@ fn pump_via_splice(infd: RawFd, outfd: RawFd, pipe: &PipeFd) -> LoggedResult<()>
if len == 0 { if len == 0 {
return Ok(()); 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(()) Ok(())
} }
#[derive(Copy, Clone)] fn pump_tty_impl(infd: OwnedFd, raw_out: RawFd) -> LoggedResult<()> {
#[repr(transparent)] let mut set = SigSet::empty();
struct SignalFdInfo(signalfd_siginfo); set.add(Signal::SIGWINCH);
unsafe impl Zeroable for SignalFdInfo {} set.thread_block()
unsafe impl Pod for SignalFdInfo {} .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) { let raw_in = infd.as_raw_fd();
set_stdin_raw(); let raw_sig = signal_fd.as_raw_fd();
let signal_fd = unsafe { let mut poll_fds = Vec::with_capacity(3);
let mut mask: sigset_t = std::mem::zeroed(); poll_fds.push(PollFd::new(infd.as_fd(), PollFlags::POLLIN));
sigemptyset(&mut mask); poll_fds.push(PollFd::new(signal_fd.as_fd(), PollFlags::POLLIN));
sigaddset(&mut mask, SIGWINCH); if raw_out >= 0 {
sigprocmask(SIG_BLOCK, &mask, null_mut()) poll_fds.push(PollFd::new(
.check_os_err("sigprocmask", None, None) FileOrStd::StdIn.as_file().as_fd(),
.log_ok(); PollFlags::POLLIN,
signalfd(-1, &mask, SFD_CLOEXEC) ));
}; }
resize_pty(outfd); // Any flag in this list indicates stop polling
let stop_flags = PollFlags::POLLERR | PollFlags::POLLHUP | PollFlags::POLLNVAL;
let mut pfds = [ // Open a pipe to bypass userspace copy with splice
pollfd { let pipe_fd = nix::unistd::pipe2(OFlag::O_CLOEXEC).into_os_result("pipe2", None, None)?;
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;
};
'poll: loop { 'poll: loop {
let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) }; // Wait for event
nix::poll::poll(&mut poll_fds, PollTimeout::NONE).check_os_err("poll", None, None)?;
if ready < 0 { for pfd in &poll_fds {
error!("poll error"); if pfd.all().unwrap_or(false) {
break; let raw_fd = pfd.as_fd().as_raw_fd();
} if raw_fd == STDIN_FILENO {
pump_via_splice(STDIN_FILENO, raw_out, &pipe_fd)?;
for pfd in &pfds { } else if raw_fd == raw_in {
if pfd.revents & POLLIN != 0 { pump_via_splice(raw_in, STDOUT_FILENO, &pipe_fd)?;
let res = if pfd.fd == STDIN_FILENO { } else if raw_fd == raw_sig {
pump_via_splice(STDIN_FILENO, outfd, &pipe_fd) resize_pty(raw_out);
} else if pfd.fd == infd { signal_fd.read_signal()?;
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;
} }
} else if pfd.revents != 0 && pfd.fd == infd { } else if pfd
unsafe { close(pfd.fd) }; .revents()
.unwrap_or(PollFlags::POLLHUP)
.intersects(stop_flags)
{
// If revents is None or contains any err_flags, stop polling
break 'poll; 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(); restore_stdin();
unsafe { raise(SIGWINCH) }; nix::sys::signal::raise(Signal::SIGWINCH).ok();
} }

View File

@@ -3,11 +3,12 @@ use crate::daemon::{MagiskD, to_user_id};
use crate::ffi::{ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, update_deny_flags}; use crate::ffi::{ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, update_deny_flags};
use crate::resetprop::{get_prop, set_prop}; use crate::resetprop::{get_prop, set_prop};
use crate::socket::{IpcRead, UnixSocketExt}; use crate::socket::{IpcRead, UnixSocketExt};
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO}; use base::libc::STDOUT_FILENO;
use base::{ use base::{
Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, WriteExt, cstr, fork_dont_care, Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, WriteExt, cstr, fork_dont_care,
libc, log_err, raw_cstr, warn, libc, log_err, raw_cstr, warn,
}; };
use nix::fcntl::OFlag;
use std::fmt::Write; use std::fmt::Write;
use std::os::fd::{AsRawFd, OwnedFd, RawFd}; use std::os::fd::{AsRawFd, OwnedFd, RawFd};
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
@@ -228,7 +229,7 @@ impl MagiskD {
.join_path("zygisk"); .join_path("zygisk");
// Create the unloaded marker file // Create the unloaded marker file
if let Ok(dir) = Directory::open(&path) { 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() .log()
.ok(); .ok();
} }
@@ -244,7 +245,7 @@ impl MagiskD {
let dir = cstr::buf::default() let dir = cstr::buf::default()
.join_path(MODULEROOT) .join_path(MODULEROOT)
.join_path(&module.name); .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()])?; client.send_fds(&[fd.as_raw_fd()])?;
Ok(()) Ok(())
} }

View File

@@ -130,7 +130,7 @@ impl MagiskInit {
null(), null(),
) )
} }
.check_io_err()?; .check_err()?;
self.mount_list.push("/proc".to_string()); self.mount_list.push("/proc".to_string());
} }
if !cstr!("/sys/block").exists() { if !cstr!("/sys/block").exists() {
@@ -144,7 +144,7 @@ impl MagiskInit {
null(), null(),
) )
} }
.check_io_err()?; .check_err()?;
self.mount_list.push("/sys".to_string()); self.mount_list.push("/sys".to_string());
} }

View File

@@ -1,8 +1,9 @@
use base::nix::fcntl::OFlag;
use base::{ use base::{
LOGGER, LogLevel, Logger, SilentLogExt, Utf8CStr, cstr, LOGGER, LogLevel, Logger, SilentLogExt, Utf8CStr, cstr,
libc::{ libc::{
O_CLOEXEC, O_RDWR, O_WRONLY, S_IFCHR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, SYS_dup3, O_CLOEXEC, S_IFCHR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, SYS_dup3, makedev, mknod,
makedev, mknod, syscall, syscall,
}, },
raw_cstr, raw_cstr,
}; };
@@ -19,10 +20,14 @@ static mut KMSG: RawFd = -1;
pub fn setup_klog() { pub fn setup_klog() {
unsafe { unsafe {
// Shut down first 3 fds // 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() { if fd.is_err() {
mknod(raw_cstr!("/null"), S_IFCHR | 0o666, makedev(1, 3)); 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(); cstr!("/null").remove().ok();
} }
if let Ok(ref fd) = fd { if let Ok(ref fd) = fd {
@@ -32,17 +37,23 @@ pub fn setup_klog() {
} }
// Then open kmsg fd // 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() { if fd.is_err() {
mknod(raw_cstr!("/kmsg"), S_IFCHR | 0o666, makedev(1, 11)); 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(); cstr!("/kmsg").remove().ok();
} }
KMSG = fd.map(|fd| fd.into_raw_fd()).unwrap_or(-1); KMSG = fd.map(|fd| fd.into_raw_fd()).unwrap_or(-1);
} }
// Disable kmsg rate limiting // 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(); writeln!(rate, "on").ok();
} }

View File

@@ -44,7 +44,7 @@ pub(crate) fn switch_root(path: &Utf8CStr) {
mounts.insert(info.target); mounts.insert(info.target);
} }
unsafe { unsafe {
chdir(path.as_ptr()).check_io_err()?; chdir(path.as_ptr()).check_err()?;
path.move_mount_to(cstr!("/"))?; path.move_mount_to(cstr!("/"))?;
chroot(raw_cstr!(".")); chroot(raw_cstr!("."));
} }
@@ -88,7 +88,7 @@ impl MagiskInit {
raw_cstr!("mode=755").cast(), raw_cstr!("mode=755").cast(),
) )
} }
.check_io_err() .check_err()
.log_ok(); .log_ok();
cstr!("/init").copy_to(cstr!("/data/magiskinit")).log_ok(); cstr!("/init").copy_to(cstr!("/data/magiskinit")).log_ok();
@@ -107,7 +107,7 @@ impl MagiskInit {
} }
unsafe { unsafe {
execve(raw_cstr!("/init"), self.argv.cast(), environ.cast()) execve(raw_cstr!("/init"), self.argv.cast(), environ.cast())
.check_io_err() .check_err()
.log_ok(); .log_ok();
exit(1); exit(1);
} }

View File

@@ -1,6 +1,6 @@
use crate::consts::{ROOTMNT, ROOTOVL}; use crate::consts::{ROOTMNT, ROOTOVL};
use crate::ffi::MagiskInit; use crate::ffi::MagiskInit;
use base::libc::{O_CREAT, O_RDONLY, O_WRONLY}; use base::nix::fcntl::OFlag;
use base::{ use base::{
BufReadExt, Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CString, BufReadExt, Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, Utf8CString,
clone_attr, cstr, debug, clone_attr, cstr, debug,
@@ -44,7 +44,7 @@ pub struct OverlayAttr(Utf8CString, Utf8CString);
impl MagiskInit { impl MagiskInit {
pub(crate) fn parse_config_file(&mut self) { 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); let mut reader = BufReader::new(fd);
reader.for_each_prop(|key, val| { reader.for_each_prop(|key, val| {
if key == "PREINITDEVICE" { if key == "PREINITDEVICE" {
@@ -95,7 +95,7 @@ impl MagiskInit {
let mut mount_list = String::new(); let mut mount_list = String::new();
self.mount_impl(cstr!(ROOTOVL), dest, &mut mount_list) self.mount_impl(cstr!(ROOTOVL), dest, &mut mount_list)
.log_ok(); .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(); fd.write(mount_list.as_bytes()).log_ok();
} }
} }

View File

@@ -1,6 +1,7 @@
use crate::consts::{PREINITMIRR, SELINUXMOCK}; use crate::consts::{PREINITMIRR, SELINUXMOCK};
use crate::ffi::{MagiskInit, preload_ack, preload_lib, preload_policy, split_plat_cil}; use crate::ffi::{MagiskInit, preload_ack, preload_lib, preload_policy, split_plat_cil};
use base::const_format::concatcp; use base::const_format::concatcp;
use base::nix::fcntl::OFlag;
use base::{ use base::{
BytesExt, LibcReturn, LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, debug, error, info, BytesExt, LibcReturn, LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, debug, error, info,
libc, raw_cstr, libc, raw_cstr,
@@ -54,7 +55,7 @@ fn mock_fifo(target: &Utf8CStr, mock: &Utf8CStr) -> LoggedResult<()> {
fn mock_file(target: &Utf8CStr, mock: &Utf8CStr) -> LoggedResult<()> { fn mock_file(target: &Utf8CStr, mock: &Utf8CStr) -> LoggedResult<()> {
debug!("Hijack [{}]", target); debug!("Hijack [{}]", target);
drop(mock.create(libc::O_RDONLY, 0o666)?); drop(mock.create(OFlag::O_RDONLY, 0o666)?);
mock.bind_mount_to(target, false).log() mock.bind_mount_to(target, false).log()
} }
@@ -93,7 +94,9 @@ impl MagiskInit {
let rule_file = cstr!(concatcp!("/data/", PREINITMIRR, "/sepolicy.rule")); let rule_file = cstr!(concatcp!("/data/", PREINITMIRR, "/sepolicy.rule"));
if rule_file.exists() { if rule_file.exists() {
debug!("Loading custom sepolicy patch: [{}]", rule_file); 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 // Step 0: determine strategy
@@ -153,7 +156,7 @@ impl MagiskInit {
0, 0,
ptr::null(), ptr::null(),
) )
.check_io_err()?; .check_err()?;
} }
} }
@@ -165,7 +168,7 @@ impl MagiskInit {
if !policy_ver.exists() { if !policy_ver.exists() {
// The file does not exist, create one // 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 // 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)?; mock_fifo(SELINUX_REQPROT, MOCK_REQPROT)?;
// This will unblock init at selinux_android_load_policy() -> set_policy_index(). // 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()?; policy_ver.unmount()?;
@@ -218,7 +221,7 @@ impl MagiskInit {
match strat { match strat {
SePatchStrategy::LdPreload => { SePatchStrategy::LdPreload => {
// This open will block until preload.so finish writing the sepolicy // 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()); let mut sepol = SePolicy::from_file(preload_policy());
@@ -237,14 +240,14 @@ impl MagiskInit {
} }
SePatchStrategy::SelinuxFs => { SePatchStrategy::SelinuxFs => {
// This open will block until init calls security_getenforce(). // 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); self.cleanup_and_load(&rules);
// security_getenforce was called, read from real and redirect to mock // security_getenforce was called, read from real and redirect to mock
let mut data = vec![]; let mut data = vec![];
SELINUX_ENFORCE SELINUX_ENFORCE
.open(libc::O_RDONLY)? .open(OFlag::O_RDONLY)?
.read_to_end(&mut data)?; .read_to_end(&mut data)?;
mock_enforce.write_all(&data)?; mock_enforce.write_all(&data)?;
} }
@@ -266,10 +269,10 @@ impl MagiskInit {
// init is blocked on checkreqprot, write to the real node first, then // init is blocked on checkreqprot, write to the real node first, then
// unblock init by opening the mock FIFO. // unblock init by opening the mock FIFO.
SELINUX_REQPROT SELINUX_REQPROT
.open(libc::O_WRONLY)? .open(OFlag::O_WRONLY)?
.write_all("0".as_bytes())?; .write_all("0".as_bytes())?;
let mut v = vec![]; 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)?;
} }
} }

View File

@@ -1,8 +1,6 @@
use crate::ffi::MagiskInit; use crate::ffi::MagiskInit;
use base::{ use base::nix::fcntl::OFlag;
LoggedResult, MappedFile, MutBytesExt, ResultExt, cstr, debug, error, use base::{LoggedResult, MappedFile, MutBytesExt, ResultExt, cstr, debug, error};
libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_WRONLY},
};
use std::io::Write; use std::io::Write;
pub(crate) fn hexpatch_init_for_second_stage(writable: bool) { 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 dest = cstr!("/data/init");
let _: LoggedResult<()> = try { 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())?; fd.write_all(init.as_ref())?;
} }
let attr = src.follow_link().get_attr()?; let attr = src.follow_link().get_attr()?;
@@ -77,7 +75,7 @@ impl MagiskInit {
.log_ok(); .log_ok();
debug!("Symlink /first_stage_ramdisk/storage/self/primary -> /system/system/bin/init"); debug!("Symlink /first_stage_ramdisk/storage/self/primary -> /system/system/bin/init");
cstr!("/first_stage_ramdisk/sdcard") 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(); .log_ok();
} else { } else {
cstr!("/storage/self").mkdirs(0o755).log_ok(); cstr!("/storage/self").mkdirs(0o755).log_ok();

View File

@@ -4,8 +4,7 @@ use std::{iter::Peekable, vec::IntoIter};
use crate::SePolicy; use crate::SePolicy;
use crate::ffi::Xperm; use crate::ffi::Xperm;
use base::libc::{O_CLOEXEC, O_RDONLY}; use base::{BufReadExt, LoggedResult, Utf8CStr, error, nix::fcntl::OFlag, warn};
use base::{BufReadExt, LoggedResult, Utf8CStr, error, warn};
pub enum Token<'a> { pub enum Token<'a> {
AL, AL,
@@ -279,7 +278,7 @@ impl SePolicy {
pub fn load_rule_file(&mut self, filename: &Utf8CStr) { pub fn load_rule_file(&mut self, filename: &Utf8CStr) {
let result: LoggedResult<()> = try { 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); let mut reader = BufReader::new(file);
self.load_rules_from_reader(&mut reader); self.load_rules_from_reader(&mut reader);
}; };