mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-12 08:32:07 +00:00
Use nix for libc functions
This commit is contained in:
@@ -23,3 +23,4 @@ bytemuck = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
num-derive = { workspace = true }
|
||||
const_format = { workspace = true }
|
||||
nix = { workspace = true, features = ["fs"] }
|
||||
@@ -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<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*
|
||||
unsafe impl ExternType for &Utf8CStr {
|
||||
type Id = type_id!("Utf8CStr");
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<dirent>,
|
||||
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<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 {
|
||||
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(())
|
||||
|
||||
@@ -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<MountInfo> {
|
||||
pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
|
||||
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<MountInfo> {
|
||||
}
|
||||
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]),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
pub use const_format;
|
||||
pub use libc;
|
||||
pub use nix;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
pub use cstr::{
|
||||
|
||||
@@ -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<Self::Value, i32>;
|
||||
fn check_err(self) -> nix::Result<Self::Value>;
|
||||
|
||||
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<Self::Value, i32> {
|
||||
fn check_err(self) -> nix::Result<Self::Value> {
|
||||
if self < 0 {
|
||||
Err(*errno())
|
||||
Err(Errno::last())
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
@@ -270,14 +265,23 @@ impl<T> LibcReturn for *mut T {
|
||||
type Value = NonNull<T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_result(self) -> Result<Self::Value, i32> {
|
||||
NonNull::new(self).ok_or_else(|| *errno())
|
||||
fn check_err(self) -> nix::Result<Self::Value> {
|
||||
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)]
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Directory>) -> *mut libc::dirent {
|
||||
dir.read()
|
||||
.log()
|
||||
.ok()
|
||||
|
||||
Reference in New Issue
Block a user