mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-29 22:14:27 +02:00
Provide richer error messages
Make sure most syscall/libc calls results are mapped to OsResult that can produce more detailed error messages.
This commit is contained in:
parent
c1e061603b
commit
7bd901273c
@ -1,6 +1,5 @@
|
|||||||
// 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 std::io;
|
|
||||||
use std::os::fd::{BorrowedFd, OwnedFd, RawFd};
|
use std::os::fd::{BorrowedFd, OwnedFd, RawFd};
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
@ -9,8 +8,8 @@ use libc::{c_char, mode_t};
|
|||||||
use crate::files::map_file_at;
|
use crate::files::map_file_at;
|
||||||
pub(crate) use crate::xwrap::*;
|
pub(crate) use crate::xwrap::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
CxxResultExt, Directory, FsPath, Utf8CStr, clone_attr, cstr, cstr_buf, fclone_attr, fd_path,
|
CxxResultExt, Directory, FsPath, OsResultStatic, Utf8CStr, clone_attr, cstr, cstr_buf,
|
||||||
map_fd, map_file, slice_from_ptr,
|
fclone_attr, fd_path, map_fd, map_file, slice_from_ptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
||||||
@ -57,7 +56,7 @@ unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
|||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
||||||
fn inner(fd: OwnedFd) -> io::Result<()> {
|
fn inner(fd: OwnedFd) -> OsResultStatic<()> {
|
||||||
Directory::try_from(fd)?.remove_all()
|
Directory::try_from(fd)?.remove_all()
|
||||||
}
|
}
|
||||||
inner(fd).is_ok()
|
inner(fd).is_ok()
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use crate::cxx_extern::readlinkat_for_cxx;
|
use crate::cxx_extern::readlinkat_for_cxx;
|
||||||
use crate::{
|
use crate::{
|
||||||
FileAttr, FsPath, LibcReturn, Utf8CStr, Utf8CStrBuf, cstr, cstr_buf, errno, fd_path,
|
FileAttr, FsPath, LibcReturn, OsError, OsResult, OsResultStatic, Utf8CStr, Utf8CStrBuf, cstr,
|
||||||
fd_set_attr,
|
cstr_buf, errno, fd_path, fd_set_attr,
|
||||||
};
|
};
|
||||||
use libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, dirent};
|
use libc::{EEXIST, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, dirent, mode_t};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||||
use std::{io, mem, slice};
|
use std::{mem, slice};
|
||||||
|
|
||||||
pub struct DirEntry<'a> {
|
pub struct DirEntry<'a> {
|
||||||
dir: &'a Directory,
|
dir: &'a Directory,
|
||||||
@ -26,7 +26,12 @@ impl DirEntry<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
#[inline(always)]
|
||||||
|
fn utf8_name(&self) -> Option<&str> {
|
||||||
|
self.name().to_str().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||||
self.dir.path(buf)?;
|
self.dir.path(buf)?;
|
||||||
buf.push_str("/");
|
buf.push_str("/");
|
||||||
buf.push_lossy(self.name().to_bytes());
|
buf.push_lossy(self.name().to_bytes());
|
||||||
@ -61,15 +66,19 @@ impl DirEntry<'_> {
|
|||||||
self.d_type == libc::DT_SOCK
|
self.d_type == libc::DT_SOCK
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unlink(&self) -> io::Result<()> {
|
pub fn unlink(&self) -> OsResult<()> {
|
||||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err(
|
||||||
|
"unlinkat",
|
||||||
|
self.utf8_name(),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = readlinkat_for_cxx(
|
let r = readlinkat_for_cxx(
|
||||||
@ -78,52 +87,66 @@ impl DirEntry<'_> {
|
|||||||
buf.as_mut_ptr().cast(),
|
buf.as_mut_ptr().cast(),
|
||||||
buf.capacity(),
|
buf.capacity(),
|
||||||
)
|
)
|
||||||
.check_os_err()? as usize;
|
.as_os_result("readlinkat", self.utf8_name(), None)? as usize;
|
||||||
buf.set_len(r);
|
buf.set_len(r);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
pub fn open_as_dir(&self) -> OsResult<Directory> {
|
||||||
unsafe { self.dir.open_raw_fd(self.name(), flags, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
|
||||||
if !self.is_dir() {
|
if !self.is_dir() {
|
||||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
return Err(OsError::with_os_error(
|
||||||
|
libc::ENOTDIR,
|
||||||
|
"fdopendir",
|
||||||
|
self.utf8_name(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
self.dir.openat_as_dir(self.name())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
pub fn open_as_file(&self, flags: i32) -> OsResult<File> {
|
||||||
if self.is_dir() {
|
if self.is_dir() {
|
||||||
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
return Err(OsError::with_os_error(
|
||||||
|
libc::EISDIR,
|
||||||
|
"open_as_file",
|
||||||
|
self.utf8_name(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
self.dir.openat_as_file(self.name(), flags, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
pub fn get_attr(&self) -> OsResult<FileAttr> {
|
||||||
let mut path = cstr_buf::default();
|
let mut path = cstr_buf::default();
|
||||||
self.path(&mut path)?;
|
self.path(&mut path)?;
|
||||||
FsPath::from(&path).get_attr()
|
FsPath::from(&path)
|
||||||
|
.get_attr()
|
||||||
|
.map_err(|e| e.set_args(self.utf8_name(), None))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
pub fn set_attr(&self, attr: &FileAttr) -> OsResult<()> {
|
||||||
let mut path = cstr_buf::default();
|
let mut path = cstr_buf::default();
|
||||||
self.path(&mut path)?;
|
self.path(&mut path)?;
|
||||||
FsPath::from(&path).set_attr(attr)
|
FsPath::from(&path)
|
||||||
|
.set_attr(attr)
|
||||||
|
.map_err(|e| e.set_args(self.utf8_name(), None))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||||
let mut path = cstr_buf::default();
|
let mut path = cstr_buf::default();
|
||||||
self.path(&mut path)?;
|
self.path(&mut path)?;
|
||||||
FsPath::from(&path).get_secontext(con)
|
FsPath::from(&path)
|
||||||
|
.get_secontext(con)
|
||||||
|
.map_err(|e| e.set_args(self.utf8_name(), None))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
pub fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||||
let mut path = cstr_buf::default();
|
let mut path = cstr_buf::default();
|
||||||
self.path(&mut path)?;
|
self.path(&mut path)?;
|
||||||
FsPath::from(&path).set_secontext(con)
|
FsPath::from(&path)
|
||||||
|
.set_secontext(con)
|
||||||
|
.map_err(|e| e.set_args(self.utf8_name(), Some(con)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,17 +169,18 @@ pub enum WalkResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Directory {
|
impl Directory {
|
||||||
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
pub fn open(path: &Utf8CStr) -> OsResult<Directory> {
|
||||||
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
let dirp = unsafe { libc::opendir(path.as_ptr()) };
|
||||||
|
let dirp = dirp.as_os_result("opendir", Some(path), None)?;
|
||||||
Ok(Directory { dirp })
|
Ok(Directory { dirp })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self) -> io::Result<Option<DirEntry<'_>>> {
|
pub fn read(&mut self) -> OsResult<'static, Option<DirEntry>> {
|
||||||
*errno() = 0;
|
*errno() = 0;
|
||||||
let e = unsafe { libc::readdir(self.dirp) };
|
let e = unsafe { libc::readdir(self.dirp) };
|
||||||
if e.is_null() {
|
if e.is_null() {
|
||||||
return if *errno() != 0 {
|
return if *errno() != 0 {
|
||||||
Err(io::Error::last_os_error())
|
Err(OsError::last_os_error("readdir", None, None))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
};
|
};
|
||||||
@ -182,17 +206,33 @@ impl Directory {
|
|||||||
unsafe { libc::rewinddir(self.dirp) }
|
unsafe { libc::rewinddir(self.dirp) }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn open_raw_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
fn openat<'a>(&self, name: &'a CStr, flags: i32, mode: u32) -> OsResult<'a, OwnedFd> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode)
|
||||||
|
.as_os_result("openat", name.to_str().ok(), None)
|
||||||
|
.map(|fd| OwnedFd::from_raw_fd(fd))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_fd(&self, name: &Utf8CStr, flags: i32, mode: i32) -> io::Result<OwnedFd> {
|
pub fn openat_as_dir<'a>(&self, name: &'a CStr) -> OsResult<'a, Directory> {
|
||||||
|
let fd = self.openat(name, O_RDONLY, 0)?;
|
||||||
|
Directory::try_from(fd).map_err(|e| e.set_args(name.to_str().ok(), None))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn openat_as_file<'a>(&self, name: &'a CStr, flags: i32, mode: u32) -> OsResult<'a, File> {
|
||||||
|
let fd = self.openat(name, flags, mode)?;
|
||||||
|
Ok(File::from(fd))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mkdirat<'a>(&self, name: &'a CStr, mode: u32) -> OsResult<'a, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.open_raw_fd(name.as_cstr(), flags, mode)
|
if libc::mkdirat(self.as_raw_fd(), name.as_ptr(), mode as mode_t) < 0
|
||||||
.map(|fd| OwnedFd::from_raw_fd(fd))
|
&& *errno() != EEXIST
|
||||||
|
{
|
||||||
|
return Err(OsError::last_os_error("mkdirat", name.to_str().ok(), None));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_path(&self, path: &CStr) -> bool {
|
pub fn contains_path(&self, path: &CStr) -> bool {
|
||||||
@ -210,25 +250,25 @@ impl Directory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||||
fd_path(self.as_raw_fd(), buf)
|
fd_path(self.as_raw_fd(), buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
pub fn post_order_walk<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> io::Result<WalkResult> {
|
) -> OsResultStatic<WalkResult> {
|
||||||
self.post_order_walk_impl(&mut f)
|
self.post_order_walk_impl(&mut f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
pub fn pre_order_walk<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> io::Result<WalkResult> {
|
) -> OsResultStatic<WalkResult> {
|
||||||
self.pre_order_walk_impl(&mut f)
|
self.pre_order_walk_impl(&mut f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_all(&mut self) -> io::Result<()> {
|
pub fn remove_all(&mut self) -> OsResultStatic<()> {
|
||||||
self.post_order_walk(|e| {
|
self.post_order_walk(|e| {
|
||||||
e.unlink()?;
|
e.unlink()?;
|
||||||
Ok(WalkResult::Continue)
|
Ok(WalkResult::Continue)
|
||||||
@ -236,60 +276,47 @@ impl Directory {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
pub fn copy_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||||
while let Some(ref e) = self.read()? {
|
while let Some(ref e) = self.read()? {
|
||||||
let attr = e.get_attr()?;
|
let attr = e.get_attr()?;
|
||||||
let new_entry = DirEntry {
|
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
if e.is_dir() {
|
if e.is_dir() {
|
||||||
unsafe {
|
dir.mkdirat(e.name(), 0o777)?;
|
||||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
|
||||||
}
|
|
||||||
let mut src = e.open_as_dir()?;
|
let mut src = e.open_as_dir()?;
|
||||||
let dest = new_entry.open_as_dir()?;
|
let dest = dir.openat_as_dir(e.name())?;
|
||||||
src.copy_into(&dest)?;
|
src.copy_into(&dest)?;
|
||||||
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(O_RDONLY)?;
|
||||||
let mut dest = unsafe {
|
let mut dest = dir.openat_as_file(e.name(), O_WRONLY | O_CREAT | O_TRUNC, 0o777)?;
|
||||||
File::from_raw_fd(dir.open_raw_fd(
|
|
||||||
e.name(),
|
|
||||||
O_WRONLY | O_CREAT | 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() {
|
||||||
let mut path = cstr_buf::default();
|
let mut target = cstr_buf::default();
|
||||||
e.read_link(&mut path)?;
|
e.read_link(&mut target)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
libc::symlinkat(target.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
||||||
.as_os_err()?;
|
.check_os_err("symlinkat", Some(&target), e.utf8_name())?;
|
||||||
}
|
}
|
||||||
|
let new_entry = DirEntry {
|
||||||
|
dir,
|
||||||
|
entry: e.entry,
|
||||||
|
d_name_len: e.d_name_len,
|
||||||
|
};
|
||||||
new_entry.set_attr(&attr)?;
|
new_entry.set_attr(&attr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
pub fn move_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||||
let dir_fd = self.as_raw_fd();
|
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
|
||||||
let mut src = e.open_as_dir()?;
|
let mut src = e.open_as_dir()?;
|
||||||
let new_entry = DirEntry {
|
let dest = dir.openat_as_dir(e.name())?;
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
let dest = new_entry.open_as_dir()?;
|
|
||||||
src.move_into(&dest)?;
|
src.move_into(&dest)?;
|
||||||
return e.unlink();
|
return Ok(e.unlink()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -299,27 +326,20 @@ impl Directory {
|
|||||||
dir.as_raw_fd(),
|
dir.as_raw_fd(),
|
||||||
e.d_name.as_ptr(),
|
e.d_name.as_ptr(),
|
||||||
)
|
)
|
||||||
.as_os_err()?;
|
.check_os_err("renameat", e.utf8_name(), None)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
pub fn link_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||||
let dir_fd = self.as_raw_fd();
|
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() {
|
||||||
unsafe {
|
dir.mkdirat(e.name(), 0o777)?;
|
||||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
|
||||||
}
|
|
||||||
let attr = e.get_attr()?;
|
let attr = e.get_attr()?;
|
||||||
let new_entry = DirEntry {
|
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
let mut src = e.open_as_dir()?;
|
let mut src = e.open_as_dir()?;
|
||||||
let dest = new_entry.open_as_dir()?;
|
let dest = dir.openat_as_dir(e.name())?;
|
||||||
src.link_into(&dest)?;
|
src.link_into(&dest)?;
|
||||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||||
} else {
|
} else {
|
||||||
@ -331,7 +351,7 @@ impl Directory {
|
|||||||
e.d_name.as_ptr(),
|
e.d_name.as_ptr(),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.as_os_err()?;
|
.check_os_err("linkat", e.utf8_name(), None)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -340,10 +360,10 @@ impl Directory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Directory {
|
impl Directory {
|
||||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
fn post_order_walk_impl<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: &mut F,
|
f: &mut F,
|
||||||
) -> io::Result<WalkResult> {
|
) -> OsResultStatic<WalkResult> {
|
||||||
use WalkResult::*;
|
use WalkResult::*;
|
||||||
loop {
|
loop {
|
||||||
match self.read()? {
|
match self.read()? {
|
||||||
@ -365,10 +385,10 @@ impl Directory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: &mut F,
|
f: &mut F,
|
||||||
) -> io::Result<WalkResult> {
|
) -> OsResultStatic<WalkResult> {
|
||||||
use WalkResult::*;
|
use WalkResult::*;
|
||||||
loop {
|
loop {
|
||||||
match self.read()? {
|
match self.read()? {
|
||||||
@ -391,10 +411,11 @@ impl Directory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<OwnedFd> for Directory {
|
impl TryFrom<OwnedFd> for Directory {
|
||||||
type Error = io::Error;
|
type Error = OsError<'static>;
|
||||||
|
|
||||||
fn try_from(fd: OwnedFd) -> io::Result<Self> {
|
fn try_from(fd: OwnedFd) -> OsResult<'static, Self> {
|
||||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) }.check_os_err()?;
|
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) };
|
||||||
|
let dirp = dirp.as_os_result("fdopendir", None, None)?;
|
||||||
Ok(Directory { dirp })
|
Ok(Directory { dirp })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Directory, FsPath, FsPathBuf, FsPathFollow, LibcReturn, Utf8CStr, Utf8CStrBuf, cstr_buf, errno,
|
Directory, FsPath, FsPathBuf, FsPathFollow, LibcReturn, OsError, OsResult, OsResultStatic,
|
||||||
error,
|
Utf8CStr, Utf8CStrBuf, cstr_buf, errno, error,
|
||||||
};
|
};
|
||||||
use bytemuck::{Pod, bytes_of, bytes_of_mut};
|
use bytemuck::{Pod, bytes_of, bytes_of_mut};
|
||||||
use libc::{
|
use libc::{
|
||||||
@ -19,28 +19,6 @@ use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{io, mem, ptr, slice};
|
use std::{io, mem, ptr, slice};
|
||||||
|
|
||||||
pub fn __open_fd_impl(path: &Utf8CStr, flags: i32, mode: mode_t) -> io::Result<OwnedFd> {
|
|
||||||
unsafe {
|
|
||||||
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).check_os_err()?;
|
|
||||||
Ok(OwnedFd::from_raw_fd(fd))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! open_fd {
|
|
||||||
($path:expr, $flags:expr) => {
|
|
||||||
$crate::__open_fd_impl($path, $flags, 0)
|
|
||||||
};
|
|
||||||
($path:expr, $flags:expr, $mode:expr) => {
|
|
||||||
$crate::__open_fd_impl($path, $flags, $mode)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
let path = FsPathBuf::default().join("/proc/self/fd").join_fmt(fd);
|
|
||||||
path.read_link(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ReadExt {
|
pub trait ReadExt {
|
||||||
fn skip(&mut self, len: usize) -> io::Result<()>;
|
fn skip(&mut self, len: usize) -> io::Result<()>;
|
||||||
fn read_pod<F: Pod>(&mut self, data: &mut F) -> io::Result<()>;
|
fn read_pod<F: Pod>(&mut self, data: &mut F) -> io::Result<()>;
|
||||||
@ -137,6 +115,32 @@ impl<T: Write> WriteExt for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn __open_fd_impl(path: &Utf8CStr, flags: i32, mode: mode_t) -> OsResult<OwnedFd> {
|
||||||
|
unsafe {
|
||||||
|
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).as_os_result(
|
||||||
|
"open",
|
||||||
|
Some(path),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
Ok(OwnedFd::from_raw_fd(fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! open_fd {
|
||||||
|
($path:expr, $flags:expr) => {
|
||||||
|
$crate::__open_fd_impl($path, $flags, 0)
|
||||||
|
};
|
||||||
|
($path:expr, $flags:expr, $mode:expr) => {
|
||||||
|
$crate::__open_fd_impl($path, $flags, $mode)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||||
|
let path = FsPathBuf::default().join("/proc/self/fd").join_fmt(fd);
|
||||||
|
path.read_link(buf).map_err(|e| e.set_args(None, None))
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FileAttr {
|
pub struct FileAttr {
|
||||||
pub st: libc::stat,
|
pub st: libc::stat,
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
@ -194,11 +198,11 @@ impl FsPath {
|
|||||||
unsafe { mem::transmute(self) }
|
unsafe { mem::transmute(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&self, flags: i32) -> io::Result<File> {
|
pub fn open(&self, flags: i32) -> OsResult<File> {
|
||||||
Ok(File::from(open_fd!(self, flags)?))
|
Ok(File::from(open_fd!(self, flags)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(&self, flags: i32, mode: mode_t) -> io::Result<File> {
|
pub fn create(&self, flags: i32, mode: mode_t) -> OsResult<File> {
|
||||||
Ok(File::from(open_fd!(self, O_CREAT | flags, mode)?))
|
Ok(File::from(open_fd!(self, O_CREAT | flags, mode)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,75 +213,80 @@ impl FsPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename_to<T: AsRef<Utf8CStr>>(&self, name: T) -> io::Result<()> {
|
pub fn rename_to<'a>(&'a self, name: &'a FsPath) -> OsResult<'a, ()> {
|
||||||
unsafe { libc::rename(self.as_ptr(), name.as_ref().as_ptr()).as_os_err() }
|
unsafe {
|
||||||
|
libc::rename(self.as_ptr(), name.as_ptr()).check_os_err(
|
||||||
|
"rename",
|
||||||
|
Some(self),
|
||||||
|
Some(name),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&self) -> io::Result<()> {
|
pub fn remove(&self) -> OsResult<()> {
|
||||||
unsafe { libc::remove(self.as_ptr()).as_os_err() }
|
unsafe { libc::remove(self.as_ptr()).check_os_err("remove", Some(self), None) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_all(&self) -> io::Result<()> {
|
pub fn remove_all(&self) -> OsResultStatic<()> {
|
||||||
let attr = self.get_attr()?;
|
let attr = self.get_attr()?;
|
||||||
if attr.is_dir() {
|
if attr.is_dir() {
|
||||||
let mut dir = Directory::try_from(open_fd!(self, O_RDONLY | O_CLOEXEC)?)?;
|
let mut dir = Directory::try_from(open_fd!(self, O_RDONLY | O_CLOEXEC)?)?;
|
||||||
dir.remove_all()?;
|
dir.remove_all()?;
|
||||||
}
|
}
|
||||||
self.remove()
|
Ok(self.remove()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unnecessary_cast)]
|
#[allow(clippy::unnecessary_cast)]
|
||||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
|
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
|
||||||
.check_os_err()? as isize;
|
.as_os_result("readlink", Some(self), None)? as isize;
|
||||||
*(buf.as_mut_ptr().offset(r) as *mut u8) = b'\0';
|
*(buf.as_mut_ptr().offset(r) as *mut u8) = b'\0';
|
||||||
buf.set_len(r as usize);
|
buf.set_len(r as usize);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mkdir(&self, mode: mode_t) -> io::Result<()> {
|
pub fn mkdir(&self, mode: mode_t) -> OsResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if libc::mkdir(self.as_ptr(), mode) < 0 {
|
if libc::mkdir(self.as_ptr(), mode) < 0 {
|
||||||
if *errno() == EEXIST {
|
if *errno() == EEXIST {
|
||||||
libc::chmod(self.as_ptr(), mode).as_os_err()?;
|
libc::chmod(self.as_ptr(), mode).check_os_err("chmod", Some(self), None)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(OsError::last_os_error("mkdir", Some(self), None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mkdirs(&self, mode: mode_t) -> io::Result<()> {
|
pub fn mkdirs(&self, mode: mode_t) -> OsResultStatic<()> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut arr = cstr_buf::default();
|
|
||||||
arr.push_str(self);
|
let mut path = FsPathBuf::default();
|
||||||
let mut off = 1;
|
let mut components = self.split('/').filter(|s| !s.is_empty());
|
||||||
unsafe {
|
loop {
|
||||||
let buf = arr.as_bytes_mut();
|
let Some(s) = components.next() else {
|
||||||
while let Some(p) = buf[off..].iter().position(|c| *c == b'/') {
|
break;
|
||||||
buf[off + p] = b'\0';
|
};
|
||||||
if libc::mkdir(buf.as_ptr().cast(), mode) < 0 && *errno() != EEXIST {
|
path = path.join(s);
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
|
unsafe {
|
||||||
|
if libc::mkdir(path.as_ptr(), mode) < 0 && *errno() != EEXIST {
|
||||||
|
return Err(OsError::last_os_error("mkdir", Some(&path), None))?;
|
||||||
}
|
}
|
||||||
buf[off + p] = b'/';
|
|
||||||
off += p + 1;
|
|
||||||
}
|
|
||||||
if libc::mkdir(buf.as_ptr().cast(), mode) < 0 && *errno() != EEXIST {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*errno() = 0;
|
*errno() = 0;
|
||||||
Ok(())
|
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) -> io::Result<()> {
|
pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||||
let fd = open_fd!(self, O_PATH | O_CLOEXEC)?;
|
let fd = open_fd!(self, O_PATH | O_CLOEXEC)?;
|
||||||
let mut st1: libc::stat;
|
let mut st1: libc::stat;
|
||||||
let mut st2: libc::stat;
|
let mut st2: libc::stat;
|
||||||
@ -292,19 +301,19 @@ impl FsPath {
|
|||||||
fd_path(fd.as_raw_fd(), buf)?;
|
fd_path(fd.as_raw_fd(), buf)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
st2 = mem::zeroed();
|
st2 = mem::zeroed();
|
||||||
libc::stat(buf.as_ptr(), &mut st2).as_os_err()?;
|
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) {
|
||||||
*errno() = ENOENT;
|
*errno() = ENOENT;
|
||||||
return Err(io::Error::last_os_error());
|
return Err(OsError::last_os_error("realpath", Some(self), None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
pub fn get_attr(&self) -> OsResult<FileAttr> {
|
||||||
let mut attr = FileAttr::new();
|
let mut attr = FileAttr::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::lstat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?;
|
||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
self.get_secontext(&mut attr.con)?;
|
self.get_secontext(&mut attr.con)?;
|
||||||
@ -312,12 +321,20 @@ impl FsPath {
|
|||||||
Ok(attr)
|
Ok(attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if !attr.is_symlink() {
|
if !attr.is_symlink() {
|
||||||
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).check_os_err(
|
||||||
|
"chmod",
|
||||||
|
Some(self),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
libc::lchown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
libc::lchown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).check_os_err(
|
||||||
|
"lchown",
|
||||||
|
Some(self),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
if !attr.con.is_empty() {
|
if !attr.con.is_empty() {
|
||||||
@ -327,7 +344,7 @@ impl FsPath {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let sz = libc::lgetxattr(
|
let sz = libc::lgetxattr(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
@ -338,7 +355,7 @@ impl FsPath {
|
|||||||
if sz < 1 {
|
if sz < 1 {
|
||||||
con.clear();
|
con.clear();
|
||||||
if *errno() != libc::ENODATA {
|
if *errno() != libc::ENODATA {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(OsError::last_os_error("lgetxattr", Some(self), None));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
con.set_len((sz - 1) as usize);
|
con.set_len((sz - 1) as usize);
|
||||||
@ -347,7 +364,7 @@ impl FsPath {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
pub fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::lsetxattr(
|
libc::lsetxattr(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
@ -356,11 +373,11 @@ impl FsPath {
|
|||||||
con.len() + 1,
|
con.len() + 1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_os_err("lsetxattr", Some(self), Some(con))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_to(&self, path: &FsPath) -> io::Result<()> {
|
pub fn copy_to(&self, path: &FsPath) -> OsResultStatic<()> {
|
||||||
let attr = self.get_attr()?;
|
let attr = self.get_attr()?;
|
||||||
if attr.is_dir() {
|
if attr.is_dir() {
|
||||||
path.mkdir(0o777)?;
|
path.mkdir(0o777)?;
|
||||||
@ -378,7 +395,11 @@ impl FsPath {
|
|||||||
let mut buf = cstr_buf::default();
|
let mut buf = cstr_buf::default();
|
||||||
self.read_link(&mut buf)?;
|
self.read_link(&mut buf)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::symlink(buf.as_ptr(), path.as_ptr()).as_os_err()?;
|
libc::symlink(buf.as_ptr(), path.as_ptr()).check_os_err(
|
||||||
|
"symlink",
|
||||||
|
Some(&buf),
|
||||||
|
Some(path),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,7 +407,7 @@ impl FsPath {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_to(&self, path: &FsPath) -> io::Result<()> {
|
pub fn move_to(&self, path: &FsPath) -> OsResultStatic<()> {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
let attr = path.get_attr()?;
|
let attr = path.get_attr()?;
|
||||||
if attr.is_dir() {
|
if attr.is_dir() {
|
||||||
@ -397,7 +418,8 @@ impl FsPath {
|
|||||||
path.remove()?;
|
path.remove()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.rename_to(path)
|
self.rename_to(path)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent(&self, buf: &mut dyn Utf8CStrBuf) -> bool {
|
pub fn parent(&self, buf: &mut dyn Utf8CStrBuf) -> bool {
|
||||||
@ -414,26 +436,39 @@ impl FsPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ln self path
|
// ln self path
|
||||||
pub fn link_to(&self, path: &FsPath) -> io::Result<()> {
|
pub fn link_to(&self, path: &FsPath) -> OsResultStatic<()> {
|
||||||
let attr = self.get_attr()?;
|
let attr = self.get_attr()?;
|
||||||
if attr.is_dir() {
|
if attr.is_dir() {
|
||||||
path.mkdir(0o777)?;
|
path.mkdir(0o777)?;
|
||||||
path.set_attr(&attr)?;
|
path.set_attr(&attr)?;
|
||||||
let mut src = Directory::open(self)?;
|
let mut src = Directory::open(self)?;
|
||||||
let dest = Directory::open(path)?;
|
let dest = Directory::open(path)?;
|
||||||
src.link_into(&dest)
|
Ok(src.link_into(&dest)?)
|
||||||
} else {
|
} else {
|
||||||
unsafe { libc::link(self.as_ptr(), path.as_ptr()).as_os_err() }
|
unsafe {
|
||||||
|
libc::link(self.as_ptr(), path.as_ptr()).check_os_err(
|
||||||
|
"link",
|
||||||
|
Some(self),
|
||||||
|
Some(path),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ln -s target self
|
// ln -s target self
|
||||||
pub fn create_symlink_to(&self, target: &FsPath) -> io::Result<()> {
|
pub fn create_symlink_to<'a>(&'a self, target: &'a FsPath) -> OsResult<'a, ()> {
|
||||||
unsafe { libc::symlink(target.as_ptr(), self.as_ptr()).as_os_err() }
|
unsafe {
|
||||||
|
libc::symlink(target.as_ptr(), self.as_ptr()).check_os_err(
|
||||||
|
"symlink",
|
||||||
|
Some(target),
|
||||||
|
Some(self),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mkfifo(&self, mode: mode_t) -> io::Result<()> {
|
pub fn mkfifo(&self, mode: mode_t) -> OsResult<()> {
|
||||||
unsafe { libc::mkfifo(self.as_ptr(), mode).as_os_err() }
|
unsafe { libc::mkfifo(self.as_ptr(), mode).check_os_err("mkfifo", Some(self), None) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,10 +477,10 @@ impl FsPathFollow {
|
|||||||
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
pub fn get_attr(&self) -> OsResult<FileAttr> {
|
||||||
let mut attr = FileAttr::new();
|
let mut attr = FileAttr::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::stat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?;
|
||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
self.get_secontext(&mut attr.con)?;
|
self.get_secontext(&mut attr.con)?;
|
||||||
@ -453,10 +488,18 @@ impl FsPathFollow {
|
|||||||
Ok(attr)
|
Ok(attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).check_os_err(
|
||||||
libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_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")]
|
#[cfg(feature = "selinux")]
|
||||||
if !attr.con.is_empty() {
|
if !attr.con.is_empty() {
|
||||||
@ -466,7 +509,7 @@ impl FsPathFollow {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let sz = libc::getxattr(
|
let sz = libc::getxattr(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
@ -477,7 +520,7 @@ impl FsPathFollow {
|
|||||||
if sz < 1 {
|
if sz < 1 {
|
||||||
con.clear();
|
con.clear();
|
||||||
if *errno() != libc::ENODATA {
|
if *errno() != libc::ENODATA {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(OsError::last_os_error("getxattr", Some(self), None));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
con.set_len((sz - 1) as usize);
|
con.set_len((sz - 1) as usize);
|
||||||
@ -486,7 +529,7 @@ impl FsPathFollow {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
pub fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::setxattr(
|
libc::setxattr(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
@ -495,86 +538,97 @@ impl FsPathFollow {
|
|||||||
con.len() + 1,
|
con.len() + 1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_os_err("setxattr", Some(self), Some(con))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
pub fn fd_get_attr(fd: RawFd) -> OsResult<'static, FileAttr> {
|
||||||
let mut attr = FileAttr::new();
|
let mut attr = FileAttr::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::fstat(fd, &mut attr.st).as_os_err()?;
|
libc::fstat(fd, &mut attr.st).check_os_err("fstat", None, None)?;
|
||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
{
|
fd_get_secontext(fd, &mut attr.con)?;
|
||||||
let sz = libc::fgetxattr(
|
|
||||||
fd,
|
|
||||||
XATTR_NAME_SELINUX.as_ptr(),
|
|
||||||
attr.con.as_mut_ptr().cast(),
|
|
||||||
attr.con.capacity(),
|
|
||||||
);
|
|
||||||
if sz < 1 {
|
|
||||||
if *errno() != libc::ENODATA {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
attr.con.set_len((sz - 1) as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(attr)
|
Ok(attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> io::Result<()> {
|
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> OsResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::fchmod(fd, (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
libc::fchmod(fd, (attr.st.st_mode & 0o777).as_()).check_os_err("fchmod", None, None)?;
|
||||||
libc::fchown(fd, attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
libc::fchown(fd, attr.st.st_uid, attr.st.st_gid).check_os_err("fchown", None, None)?;
|
||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
if !attr.con.is_empty() {
|
if !attr.con.is_empty() {
|
||||||
libc::fsetxattr(
|
fd_set_secontext(fd, &attr.con)?;
|
||||||
fd,
|
|
||||||
XATTR_NAME_SELINUX.as_ptr(),
|
|
||||||
attr.con.as_ptr().cast(),
|
|
||||||
attr.con.len() + 1,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_attr(a: &FsPath, b: &FsPath) -> io::Result<()> {
|
pub fn fd_get_secontext(fd: RawFd, con: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||||
let attr = a.get_attr()?;
|
unsafe {
|
||||||
b.set_attr(&attr)
|
let sz = libc::fgetxattr(
|
||||||
|
fd,
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_mut_ptr().cast(),
|
||||||
|
con.capacity(),
|
||||||
|
);
|
||||||
|
if sz < 1 {
|
||||||
|
if *errno() != libc::ENODATA {
|
||||||
|
return Err(OsError::last_os_error("fgetxattr", None, None));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
con.set_len((sz - 1) as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fclone_attr(a: RawFd, b: RawFd) -> io::Result<()> {
|
pub fn fd_set_secontext(fd: RawFd, con: &Utf8CStr) -> OsResult<()> {
|
||||||
|
unsafe {
|
||||||
|
libc::fsetxattr(
|
||||||
|
fd,
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_ptr().cast(),
|
||||||
|
con.len() + 1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.check_os_err("fsetxattr", Some(con), None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone_attr<'a>(a: &'a FsPath, b: &'a FsPath) -> OsResult<'a, ()> {
|
||||||
|
let attr = a.get_attr()?;
|
||||||
|
b.set_attr(&attr).map_err(|e| e.set_args(Some(b), None))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fclone_attr(a: RawFd, b: RawFd) -> OsResult<'static, ()> {
|
||||||
let attr = fd_get_attr(a)?;
|
let attr = fd_get_attr(a)?;
|
||||||
fd_set_attr(b, &attr)
|
fd_set_attr(b, &attr).map_err(|e| e.set_args(None, None))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MappedFile(&'static mut [u8]);
|
pub struct MappedFile(&'static mut [u8]);
|
||||||
|
|
||||||
impl MappedFile {
|
impl MappedFile {
|
||||||
pub fn open(path: &Utf8CStr) -> io::Result<MappedFile> {
|
pub fn open(path: &Utf8CStr) -> OsResult<MappedFile> {
|
||||||
Ok(MappedFile(map_file(path, false)?))
|
Ok(MappedFile(map_file(path, false)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_rw(path: &Utf8CStr) -> io::Result<MappedFile> {
|
pub fn open_rw(path: &Utf8CStr) -> OsResult<MappedFile> {
|
||||||
Ok(MappedFile(map_file(path, true)?))
|
Ok(MappedFile(map_file(path, true)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openat<T: AsFd>(dir: &T, path: &Utf8CStr) -> io::Result<MappedFile> {
|
pub fn openat<'a, T: AsFd>(dir: &T, path: &'a Utf8CStr) -> OsResult<'a, MappedFile> {
|
||||||
Ok(MappedFile(map_file_at(dir.as_fd(), path, false)?))
|
Ok(MappedFile(map_file_at(dir.as_fd(), path, false)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openat_rw<T: AsFd>(dir: &T, path: &Utf8CStr) -> io::Result<MappedFile> {
|
pub fn openat_rw<'a, T: AsFd>(dir: &T, path: &'a Utf8CStr) -> OsResult<'a, MappedFile> {
|
||||||
Ok(MappedFile(map_file_at(dir.as_fd(), path, true)?))
|
Ok(MappedFile(map_file_at(dir.as_fd(), path, true)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<MappedFile> {
|
pub fn create(fd: BorrowedFd, sz: usize, rw: bool) -> OsResult<MappedFile> {
|
||||||
Ok(MappedFile(map_fd(fd, sz, rw)?))
|
Ok(MappedFile(map_fd(fd, sz, rw)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -605,15 +659,15 @@ 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) -> io::Result<&'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) }
|
unsafe { map_file_at(BorrowedFd::borrow_raw(libc::AT_FDCWD), path, rw) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn map_file_at(
|
pub(crate) fn map_file_at<'a>(
|
||||||
dirfd: BorrowedFd,
|
dirfd: BorrowedFd,
|
||||||
path: &Utf8CStr,
|
path: &'a Utf8CStr,
|
||||||
rw: bool,
|
rw: bool,
|
||||||
) -> io::Result<&'static mut [u8]> {
|
) -> OsResult<'a, &'static mut [u8]> {
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
const BLKGETSIZE64: u32 = 0x80081272;
|
const BLKGETSIZE64: u32 = 0x80081272;
|
||||||
|
|
||||||
@ -623,23 +677,29 @@ pub(crate) fn map_file_at(
|
|||||||
let flag = if rw { O_RDWR } else { O_RDONLY };
|
let flag = if rw { O_RDWR } else { O_RDONLY };
|
||||||
let fd = unsafe {
|
let fd = unsafe {
|
||||||
OwnedFd::from_raw_fd(
|
OwnedFd::from_raw_fd(
|
||||||
libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flag | O_CLOEXEC).check_os_err()?,
|
libc::openat(dirfd.as_raw_fd(), path.as_ptr(), flag | O_CLOEXEC).as_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;
|
||||||
unsafe { ioctl(fd.as_raw_fd(), BLKGETSIZE64, &mut sz) }.as_os_err()?;
|
unsafe {
|
||||||
|
ioctl(fd.as_raw_fd(), BLKGETSIZE64, &mut sz).check_os_err("ioctl", Some(path), None)?;
|
||||||
|
}
|
||||||
sz
|
sz
|
||||||
} else {
|
} else {
|
||||||
attr.st.st_size as u64
|
attr.st.st_size as u64
|
||||||
};
|
};
|
||||||
|
|
||||||
map_fd(fd.as_fd(), sz as usize, rw)
|
map_fd(fd.as_fd(), sz as usize, rw).map_err(|e| e.set_args(Some(path), None))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<&'static mut [u8]> {
|
pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> OsResult<'static, &'static mut [u8]> {
|
||||||
let flag = if rw {
|
let flag = if rw {
|
||||||
libc::MAP_SHARED
|
libc::MAP_SHARED
|
||||||
} else {
|
} else {
|
||||||
@ -655,7 +715,7 @@ pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<&'static
|
|||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
if ptr == libc::MAP_FAILED {
|
if ptr == libc::MAP_FAILED {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(OsError::last_os_error("mmap", None, None));
|
||||||
}
|
}
|
||||||
Ok(slice::from_raw_parts_mut(ptr.cast(), sz))
|
Ok(slice::from_raw_parts_mut(ptr.cast(), sz))
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use std::mem::ManuallyDrop;
|
|||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||||
use std::{fmt, io, slice, str};
|
use std::{fmt, slice, str};
|
||||||
|
|
||||||
pub fn errno() -> &'static mut i32 {
|
pub fn errno() -> &'static mut i32 {
|
||||||
unsafe { &mut *libc::__errno() }
|
unsafe { &mut *libc::__errno() }
|
||||||
@ -37,52 +37,6 @@ pub unsafe fn slice_from_ptr_mut<'a, T>(buf: *mut T, len: usize) -> &'a mut [T]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check libc return value and map to Result
|
|
||||||
pub trait LibcReturn
|
|
||||||
where
|
|
||||||
Self: Copy,
|
|
||||||
{
|
|
||||||
fn is_error(&self) -> bool;
|
|
||||||
fn check_os_err(self) -> io::Result<Self> {
|
|
||||||
if self.is_error() {
|
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
} else {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn as_os_err(self) -> io::Result<()> {
|
|
||||||
self.check_os_err()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_libc_return {
|
|
||||||
($($t:ty)*) => ($(
|
|
||||||
impl LibcReturn for $t {
|
|
||||||
#[inline]
|
|
||||||
fn is_error(&self) -> bool {
|
|
||||||
*self < 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_libc_return! { i8 i16 i32 i64 isize }
|
|
||||||
|
|
||||||
impl<T> LibcReturn for *const T {
|
|
||||||
#[inline]
|
|
||||||
fn is_error(&self) -> bool {
|
|
||||||
self.is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> LibcReturn for *mut T {
|
|
||||||
#[inline]
|
|
||||||
fn is_error(&self) -> bool {
|
|
||||||
self.is_null()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait BytesExt {
|
pub trait BytesExt {
|
||||||
fn find(&self, needle: &[u8]) -> Option<usize>;
|
fn find(&self, needle: &[u8]) -> Option<usize>;
|
||||||
fn contains(&self, needle: &[u8]) -> bool {
|
fn contains(&self, needle: &[u8]) -> bool {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::{FsPath, LibcReturn, Utf8CStr};
|
use crate::{FsPath, LibcReturn, OsResult, Utf8CStr};
|
||||||
use libc::c_ulong;
|
use libc::c_ulong;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
impl FsPath {
|
impl FsPath {
|
||||||
pub fn bind_mount_to(&self, path: &FsPath) -> std::io::Result<()> {
|
pub fn bind_mount_to<'a>(&'a self, path: &'a FsPath) -> OsResult<'a, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::mount(
|
libc::mount(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
@ -12,11 +12,11 @@ impl FsPath {
|
|||||||
libc::MS_BIND,
|
libc::MS_BIND,
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_os_err("bind_mount", Some(self), Some(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remount_with_flags(&self, flags: c_ulong) -> std::io::Result<()> {
|
pub fn remount_with_flags(&self, flags: c_ulong) -> OsResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::mount(
|
libc::mount(
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
@ -25,11 +25,11 @@ impl FsPath {
|
|||||||
libc::MS_BIND | libc::MS_REMOUNT | flags,
|
libc::MS_BIND | libc::MS_REMOUNT | flags,
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_os_err("remount", Some(self), None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remount_with_data(&self, data: &Utf8CStr) -> std::io::Result<()> {
|
pub fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::mount(
|
libc::mount(
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
@ -38,11 +38,11 @@ impl FsPath {
|
|||||||
libc::MS_REMOUNT,
|
libc::MS_REMOUNT,
|
||||||
data.as_ptr().cast(),
|
data.as_ptr().cast(),
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_os_err("remount", Some(self), None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_mount_to(&self, path: &FsPath) -> std::io::Result<()> {
|
pub fn move_mount_to<'a>(&'a self, path: &'a FsPath) -> OsResult<'a, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::mount(
|
libc::mount(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
@ -51,15 +51,17 @@ impl FsPath {
|
|||||||
libc::MS_MOVE,
|
libc::MS_MOVE,
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_os_err("move_mount", Some(self), Some(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmount(&self) -> std::io::Result<()> {
|
pub fn unmount(&self) -> OsResult<()> {
|
||||||
unsafe { libc::umount2(self.as_ptr(), libc::MNT_DETACH).as_os_err() }
|
unsafe {
|
||||||
|
libc::umount2(self.as_ptr(), libc::MNT_DETACH).check_os_err("unmount", Some(self), None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mount_private(&self, recursive: bool) -> std::io::Result<()> {
|
pub fn set_mount_private(&self, recursive: bool) -> OsResult<()> {
|
||||||
let flag = if recursive { libc::MS_REC } else { 0 };
|
let flag = if recursive { libc::MS_REC } else { 0 };
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::mount(
|
libc::mount(
|
||||||
@ -69,7 +71,7 @@ impl FsPath {
|
|||||||
libc::MS_PRIVATE | flag,
|
libc::MS_PRIVATE | flag,
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_os_err("set_mount_private", Some(self), None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::fmt;
|
use crate::logging::Formatter;
|
||||||
|
use crate::{LogLevel, errno, log_with_args, log_with_formatter};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::panic::Location;
|
use std::panic::Location;
|
||||||
|
use std::{fmt, io};
|
||||||
use crate::logging::Formatter;
|
use thiserror::Error;
|
||||||
use crate::{LogLevel, log_with_args, log_with_formatter};
|
|
||||||
|
|
||||||
// Error handling throughout the Rust codebase in Magisk:
|
// Error handling throughout the Rust codebase in Magisk:
|
||||||
//
|
//
|
||||||
@ -205,3 +205,191 @@ impl<T: Display> From<T> for LoggedError {
|
|||||||
LoggedError::default()
|
LoggedError::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check libc return value and map to Result
|
||||||
|
pub trait LibcReturn
|
||||||
|
where
|
||||||
|
Self: Copy,
|
||||||
|
{
|
||||||
|
fn is_error(&self) -> bool;
|
||||||
|
|
||||||
|
fn as_os_result<'a>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
arg1: Option<&'a str>,
|
||||||
|
arg2: Option<&'a str>,
|
||||||
|
) -> OsResult<'a, Self> {
|
||||||
|
if self.is_error() {
|
||||||
|
Err(OsError::last_os_error(name, arg1, arg2))
|
||||||
|
} else {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_os_err<'a>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
arg1: Option<&'a str>,
|
||||||
|
arg2: Option<&'a str>,
|
||||||
|
) -> OsResult<'a, ()> {
|
||||||
|
self.as_os_result(name, arg1, arg2)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_io_err(self) -> io::Result<()> {
|
||||||
|
if self.is_error() {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_libc_return {
|
||||||
|
($($t:ty)*) => ($(
|
||||||
|
impl LibcReturn for $t {
|
||||||
|
#[inline]
|
||||||
|
fn is_error(&self) -> bool {
|
||||||
|
*self < 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_libc_return! { i8 i16 i32 i64 isize }
|
||||||
|
|
||||||
|
impl<T> LibcReturn for *const T {
|
||||||
|
#[inline]
|
||||||
|
fn is_error(&self) -> bool {
|
||||||
|
self.is_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LibcReturn for *mut T {
|
||||||
|
#[inline]
|
||||||
|
fn is_error(&self) -> bool {
|
||||||
|
self.is_null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum OwnableStr<'a> {
|
||||||
|
None,
|
||||||
|
Borrowed(&'a str),
|
||||||
|
Owned(Box<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnableStr<'_> {
|
||||||
|
fn into_owned(self) -> OwnableStr<'static> {
|
||||||
|
match self {
|
||||||
|
OwnableStr::None => OwnableStr::None,
|
||||||
|
OwnableStr::Borrowed(s) => OwnableStr::Owned(Box::from(s)),
|
||||||
|
OwnableStr::Owned(s) => OwnableStr::Owned(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ok(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
OwnableStr::None => None,
|
||||||
|
OwnableStr::Borrowed(s) => Some(*s),
|
||||||
|
OwnableStr::Owned(s) => Some(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Option<&'a str>> for OwnableStr<'a> {
|
||||||
|
fn from(value: Option<&'a str>) -> Self {
|
||||||
|
value.map(OwnableStr::Borrowed).unwrap_or(OwnableStr::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OsError<'a> {
|
||||||
|
code: i32,
|
||||||
|
name: &'static str,
|
||||||
|
arg1: OwnableStr<'a>,
|
||||||
|
arg2: OwnableStr<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OsError<'_> {
|
||||||
|
pub fn with_os_error<'a>(
|
||||||
|
code: i32,
|
||||||
|
name: &'static str,
|
||||||
|
arg1: Option<&'a str>,
|
||||||
|
arg2: Option<&'a str>,
|
||||||
|
) -> OsError<'a> {
|
||||||
|
OsError {
|
||||||
|
code,
|
||||||
|
name,
|
||||||
|
arg1: OwnableStr::from(arg1),
|
||||||
|
arg2: OwnableStr::from(arg2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_os_error<'a>(
|
||||||
|
name: &'static str,
|
||||||
|
arg1: Option<&'a str>,
|
||||||
|
arg2: Option<&'a str>,
|
||||||
|
) -> OsError<'a> {
|
||||||
|
Self::with_os_error(*errno(), name, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_args<'a>(self, arg1: Option<&'a str>, arg2: Option<&'a str>) -> OsError<'a> {
|
||||||
|
Self::with_os_error(self.code, self.name, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> OsError<'static> {
|
||||||
|
OsError {
|
||||||
|
code: *errno(),
|
||||||
|
name: self.name,
|
||||||
|
arg1: self.arg1.into_owned(),
|
||||||
|
arg2: self.arg2.into_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_io_error(&self) -> io::Error {
|
||||||
|
io::Error::from_raw_os_error(self.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
} else {
|
||||||
|
match (self.arg1.ok(), self.arg2.ok()) {
|
||||||
|
(Some(arg1), Some(arg2)) => {
|
||||||
|
write!(f, "{} '{}' '{}': {:#}", self.name, arg1, arg2, error)
|
||||||
|
}
|
||||||
|
(Some(arg1), None) => {
|
||||||
|
write!(f, "{} '{}': {:#}", self.name, arg1, error)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
write!(f, "{}: {:#}", self.name, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for OsError<'_> {}
|
||||||
|
|
||||||
|
pub type OsResult<'a, T> = Result<T, OsError<'a>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum OsErrorStatic {
|
||||||
|
#[error(transparent)]
|
||||||
|
Os(OsError<'static>),
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert non-static OsError to static
|
||||||
|
impl<'a> From<OsError<'a>> for OsErrorStatic {
|
||||||
|
fn from(value: OsError<'a>) -> Self {
|
||||||
|
OsErrorStatic::Os(value.into_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type OsResultStatic<T> = Result<T, OsErrorStatic>;
|
||||||
|
@ -7,8 +7,8 @@ use num_traits::AsPrimitive;
|
|||||||
|
|
||||||
use base::libc::{c_uint, dev_t};
|
use base::libc::{c_uint, dev_t};
|
||||||
use base::{
|
use base::{
|
||||||
FsPath, FsPathBuf, LibcReturn, LoggedResult, MountInfo, ResultExt, Utf8CStr, cstr, cstr_buf,
|
FsPath, FsPathBuf, LibcReturn, LoggedResult, MountInfo, ResultExt, Utf8CStr, cstr, debug, info,
|
||||||
debug, info, libc, parse_mount_info, path, warn,
|
libc, parse_mount_info, path, warn,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
||||||
@ -42,16 +42,12 @@ pub fn setup_mounts() {
|
|||||||
let target = Utf8CStr::from_string(&mut target);
|
let target = Utf8CStr::from_string(&mut target);
|
||||||
let mut preinit_dir = resolve_preinit_dir(target);
|
let mut preinit_dir = resolve_preinit_dir(target);
|
||||||
let preinit_dir = Utf8CStr::from_string(&mut preinit_dir);
|
let preinit_dir = Utf8CStr::from_string(&mut preinit_dir);
|
||||||
|
let preinit_dir = FsPath::from(preinit_dir);
|
||||||
let r: LoggedResult<()> = try {
|
let r: LoggedResult<()> = try {
|
||||||
FsPath::from(preinit_dir).mkdir(0o700)?;
|
preinit_dir.mkdir(0o700)?;
|
||||||
let mut buf = cstr_buf::default();
|
mnt_path.mkdirs(0o755)?;
|
||||||
if mnt_path.parent(&mut buf) {
|
|
||||||
FsPath::from(&buf).mkdirs(0o755)?;
|
|
||||||
}
|
|
||||||
mnt_path.remove().ok();
|
mnt_path.remove().ok();
|
||||||
unsafe {
|
mnt_path.create_symlink_to(preinit_dir)?;
|
||||||
libc::symlink(preinit_dir.as_ptr(), mnt_path.as_ptr()).as_os_err()?
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if r.is_ok() {
|
if r.is_ok() {
|
||||||
linked = true;
|
linked = true;
|
||||||
@ -187,17 +183,10 @@ pub fn find_preinit_device() -> String {
|
|||||||
let preinit_dir = FsPath::from(Utf8CStr::from_string(&mut preinit_dir));
|
let preinit_dir = FsPath::from(Utf8CStr::from_string(&mut preinit_dir));
|
||||||
let _: LoggedResult<()> = try {
|
let _: LoggedResult<()> = try {
|
||||||
preinit_dir.mkdirs(0o700)?;
|
preinit_dir.mkdirs(0o700)?;
|
||||||
let mut buf = cstr_buf::default();
|
mirror_dir.mkdirs(0o755)?;
|
||||||
if mirror_dir.parent(&mut buf) {
|
mirror_dir.unmount().ok();
|
||||||
FsPath::from(&buf).mkdirs(0o755)?;
|
mirror_dir.remove().ok();
|
||||||
}
|
mirror_dir.create_symlink_to(preinit_dir)?;
|
||||||
unsafe {
|
|
||||||
libc::umount2(mirror_dir.as_ptr(), libc::MNT_DETACH)
|
|
||||||
.as_os_err()
|
|
||||||
.ok(); // ignore error
|
|
||||||
mirror_dir.remove().ok();
|
|
||||||
libc::symlink(preinit_dir.as_ptr(), mirror_dir.as_ptr()).as_os_err()?;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if std::env::var_os("MAKEDEV").is_some() {
|
if std::env::var_os("MAKEDEV").is_some() {
|
||||||
mirror_dir.clear();
|
mirror_dir.clear();
|
||||||
@ -208,7 +197,7 @@ pub fn find_preinit_device() -> String {
|
|||||||
libc::S_IFBLK | 0o600,
|
libc::S_IFBLK | 0o600,
|
||||||
info.device as dev_t,
|
info.device as dev_t,
|
||||||
)
|
)
|
||||||
.as_os_err()
|
.check_io_err()
|
||||||
.log()
|
.log()
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
|||||||
res.log().unwrap_or(vec![])
|
res.log().unwrap_or(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_apk_path(pkg: &str, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
fn find_apk_path(pkg: &str, buf: &mut dyn Utf8CStrBuf) -> LoggedResult<()> {
|
||||||
Directory::open(cstr!("/data/app"))?.pre_order_walk(|e| {
|
Directory::open(cstr!("/data/app"))?.pre_order_walk(|e| {
|
||||||
if !e.is_dir() {
|
if !e.is_dir() {
|
||||||
return Ok(Skip);
|
return Ok(Skip);
|
||||||
|
@ -84,13 +84,14 @@ fn file_set_prop(name: &Utf8CStr, value: Option<&Utf8CStr>) -> LoggedResult<()>
|
|||||||
.join("prop.XXXXXX");
|
.join("prop.XXXXXX");
|
||||||
{
|
{
|
||||||
let mut f = unsafe {
|
let mut f = unsafe {
|
||||||
let fd = mkstemp(tmp.as_mut_ptr()).check_os_err()?;
|
mkstemp(tmp.as_mut_ptr())
|
||||||
File::from_raw_fd(fd)
|
.as_os_result("mkstemp", None, None)
|
||||||
|
.map(|fd| File::from_raw_fd(fd))?
|
||||||
};
|
};
|
||||||
f.write_all(value.as_bytes())?;
|
f.write_all(value.as_bytes())?;
|
||||||
}
|
}
|
||||||
debug!("resetprop: write prop to [{}]", tmp);
|
debug!("resetprop: write prop to [{}]", tmp);
|
||||||
tmp.rename_to(path)?
|
tmp.rename_to(&path)?
|
||||||
} else {
|
} else {
|
||||||
path.remove().silent()?;
|
path.remove().silent()?;
|
||||||
debug!("resetprop: unlink [{}]", path);
|
debug!("resetprop: unlink [{}]", path);
|
||||||
@ -113,14 +114,15 @@ fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> {
|
|||||||
let mut tmp = FsPathBuf::default().join(concatcp!(PERSIST_PROP, ".XXXXXX"));
|
let mut tmp = FsPathBuf::default().join(concatcp!(PERSIST_PROP, ".XXXXXX"));
|
||||||
{
|
{
|
||||||
let f = unsafe {
|
let f = unsafe {
|
||||||
let fd = mkstemp(tmp.as_mut_ptr()).check_os_err()?;
|
mkstemp(tmp.as_mut_ptr())
|
||||||
File::from_raw_fd(fd)
|
.as_os_result("mkstemp", None, None)
|
||||||
|
.map(|fd| File::from_raw_fd(fd))?
|
||||||
};
|
};
|
||||||
debug!("resetprop: encode with protobuf [{}]", tmp);
|
debug!("resetprop: encode with protobuf [{}]", tmp);
|
||||||
props.write_message(&mut Writer::new(BufWriter::new(f)))?;
|
props.write_message(&mut Writer::new(BufWriter::new(f)))?;
|
||||||
}
|
}
|
||||||
clone_attr(path!(PERSIST_PROP), &tmp)?;
|
clone_attr(path!(PERSIST_PROP), &tmp)?;
|
||||||
tmp.rename_to(cstr!(PERSIST_PROP))?;
|
tmp.rename_to(path!(PERSIST_PROP))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ impl MagiskD {
|
|||||||
.join("zygisk");
|
.join("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_fd(cstr!("unloaded"), O_CREAT | O_RDONLY, 0o644)
|
dir.openat_as_file(cstr!("unloaded").as_cstr(), O_CREAT | O_RDONLY, 0o644)
|
||||||
.log()
|
.log()
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ impl MagiskInit {
|
|||||||
null(),
|
null(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.as_os_err()?;
|
.check_io_err()?;
|
||||||
self.mount_list.push("/proc".to_string());
|
self.mount_list.push("/proc".to_string());
|
||||||
}
|
}
|
||||||
if !path!("/sys/block").exists() {
|
if !path!("/sys/block").exists() {
|
||||||
@ -144,7 +144,7 @@ impl MagiskInit {
|
|||||||
null(),
|
null(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.as_os_err()?;
|
.check_io_err()?;
|
||||||
self.mount_list.push("/sys".to_string());
|
self.mount_list.push("/sys".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub(crate) fn switch_root(path: &Utf8CStr) {
|
|||||||
mounts.insert(info.target);
|
mounts.insert(info.target);
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
chdir(path.as_ptr()).as_os_err()?;
|
chdir(path.as_ptr()).check_io_err()?;
|
||||||
FsPath::from(path).move_mount_to(path!("/"))?;
|
FsPath::from(path).move_mount_to(path!("/"))?;
|
||||||
chroot(raw_cstr!("."));
|
chroot(raw_cstr!("."));
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ impl MagiskInit {
|
|||||||
raw_cstr!("mode=755").cast(),
|
raw_cstr!("mode=755").cast(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.as_os_err()
|
.check_io_err()
|
||||||
.log_ok();
|
.log_ok();
|
||||||
|
|
||||||
path!("/init").copy_to(path!("/data/magiskinit")).log_ok();
|
path!("/init").copy_to(path!("/data/magiskinit")).log_ok();
|
||||||
@ -108,7 +108,7 @@ impl MagiskInit {
|
|||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
execve(raw_cstr!("/init"), self.argv.cast(), environ.cast())
|
execve(raw_cstr!("/init"), self.argv.cast(), environ.cast())
|
||||||
.as_os_err()
|
.check_io_err()
|
||||||
.log_ok();
|
.log_ok();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ impl MagiskInit {
|
|||||||
0,
|
0,
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
)
|
)
|
||||||
.as_os_err()?;
|
.check_io_err()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ impl MagiskInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new process waiting for init operations
|
// Create a new process waiting for init operations
|
||||||
let pid = unsafe { libc::fork().check_os_err()? };
|
let pid = unsafe { libc::fork() };
|
||||||
if pid != 0 {
|
if pid != 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user