mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 07:57:39 +00:00
Introduce directory traversal
This commit is contained in:
parent
e13775ec2c
commit
40f25f4d56
1
native/src/Cargo.lock
generated
1
native/src/Cargo.lock
generated
@ -27,6 +27,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
name = "base"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"cxx",
|
||||
"cxx-gen",
|
||||
|
@ -14,3 +14,4 @@ cxx = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
cfg-if = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
anyhow = { workspace = true }
|
39
native/src/base/cxx_extern.rs
Normal file
39
native/src/base/cxx_extern.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// Functions listed here are just to export to C++
|
||||
|
||||
use crate::{fd_path, mkdirs, realpath, rm_rf, slice_from_ptr_mut, Directory, ResultExt};
|
||||
use anyhow::Context;
|
||||
use cxx::private::c_char;
|
||||
use libc::mode_t;
|
||||
use std::ffi::CStr;
|
||||
use std::io;
|
||||
use std::os::fd::{OwnedFd, RawFd};
|
||||
|
||||
pub fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
||||
fd_path(fd, buf)
|
||||
.context("fd_path failed")
|
||||
.log()
|
||||
.map_or(-1_isize, |v| v as isize)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
realpath(CStr::from_ptr(path), slice_from_ptr_mut(buf, bufsz)).map_or(-1, |v| v as isize)
|
||||
}
|
||||
|
||||
#[export_name = "mkdirs"]
|
||||
unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
|
||||
mkdirs(CStr::from_ptr(path), mode).map_or(-1, |_| 0)
|
||||
}
|
||||
|
||||
#[export_name = "rm_rf"]
|
||||
unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
||||
rm_rf(CStr::from_ptr(path)).map_or(false, |_| true)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
||||
fn inner(fd: OwnedFd) -> io::Result<()> {
|
||||
Directory::try_from(fd)?.remove_all()
|
||||
}
|
||||
inner(fd).map_or(false, |_| true)
|
||||
}
|
@ -62,23 +62,6 @@ static walk_result pre_order_walk(int dirfd, const Func &fn) {
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
static void remove_at(int dirfd, struct dirent *entry) {
|
||||
unlinkat(dirfd, entry->d_name, entry->d_type == DT_DIR ? AT_REMOVEDIR : 0);
|
||||
}
|
||||
|
||||
void rm_rf(const char *path) {
|
||||
struct stat st;
|
||||
if (lstat(path, &st) < 0)
|
||||
return;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
frm_rf(xopen(path, O_RDONLY | O_CLOEXEC));
|
||||
remove(path);
|
||||
}
|
||||
|
||||
void frm_rf(int dirfd) {
|
||||
post_order_walk(dirfd, remove_at);
|
||||
}
|
||||
|
||||
void mv_path(const char *src, const char *dest) {
|
||||
file_attr attr;
|
||||
getattr(src, &attr);
|
||||
|
@ -55,12 +55,13 @@ extern "C" {
|
||||
|
||||
int mkdirs(const char *path, mode_t mode);
|
||||
ssize_t canonical_path(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
|
||||
bool rm_rf(const char *path);
|
||||
bool frm_rf(int dirfd);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
using rust::fd_path;
|
||||
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
|
||||
void rm_rf(const char *path);
|
||||
void mv_path(const char *src, const char *dest);
|
||||
void mv_dir(int src, int dest);
|
||||
void cp_afc(const char *src, const char *dest);
|
||||
@ -89,7 +90,6 @@ void file_readline(const char *file, const std::function<bool(std::string_view)>
|
||||
void parse_prop_file(FILE *fp, const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||
void parse_prop_file(const char *file,
|
||||
const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||
void frm_rf(int dirfd);
|
||||
void clone_dir(int src, int dest);
|
||||
std::vector<mount_info> parse_mount_info(const char *pid);
|
||||
std::string find_apk_path(const char *pkg);
|
||||
|
@ -1,53 +1,25 @@
|
||||
use mem::MaybeUninit;
|
||||
use std::cmp::min;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, Read, Seek, SeekFrom, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::os::fd::{AsFd, BorrowedFd, IntoRawFd};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||
use std::{io, mem};
|
||||
use std::{io, mem, slice};
|
||||
|
||||
use libc::{c_char, c_uint, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH};
|
||||
use libc::{c_char, c_uint, dirent, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH, O_RDONLY};
|
||||
|
||||
use crate::{bfmt_cstr, errno, error, xopen};
|
||||
use crate::{bfmt_cstr, copy_cstr, cstr, errno, error};
|
||||
|
||||
pub mod unsafe_impl {
|
||||
use std::ffi::CStr;
|
||||
|
||||
use libc::c_char;
|
||||
|
||||
use crate::slice_from_ptr_mut;
|
||||
|
||||
pub unsafe fn readlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
let r = libc::readlink(path, buf.cast(), bufsz - 1);
|
||||
if r >= 0 {
|
||||
*buf.offset(r) = b'\0';
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
super::realpath(CStr::from_ptr(path), slice_from_ptr_mut(buf, bufsz))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn __open_fd_impl(path: &CStr, flags: i32, mode: mode_t) -> Option<OwnedFd> {
|
||||
pub fn __open_fd_impl(path: &CStr, flags: i32, mode: mode_t) -> io::Result<OwnedFd> {
|
||||
unsafe {
|
||||
let fd = libc::open(path.as_ptr(), flags, mode as c_uint);
|
||||
if fd >= 0 {
|
||||
Some(OwnedFd::from_raw_fd(fd))
|
||||
Ok(OwnedFd::from_raw_fd(fd))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn __xopen_fd_impl(path: &CStr, flags: i32, mode: mode_t) -> Option<OwnedFd> {
|
||||
unsafe {
|
||||
let fd = xopen(path.as_ptr(), flags, mode);
|
||||
if fd >= 0 {
|
||||
Some(OwnedFd::from_raw_fd(fd))
|
||||
} else {
|
||||
None
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,29 +34,31 @@ macro_rules! open_fd {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! xopen_fd {
|
||||
($path:expr, $flags:expr) => {
|
||||
$crate::__xopen_fd_impl($path, $flags, 0)
|
||||
};
|
||||
($path:expr, $flags:expr, $mode:expr) => {
|
||||
$crate::__xopen_fd_impl($path, $flags, $mode)
|
||||
};
|
||||
pub unsafe fn readlink_unsafe(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
let r = libc::readlink(path, buf.cast(), bufsz - 1);
|
||||
if r >= 0 {
|
||||
*buf.offset(r) = b'\0';
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub fn readlink(path: &CStr, data: &mut [u8]) -> isize {
|
||||
unsafe { unsafe_impl::readlink(path.as_ptr(), data.as_mut_ptr(), data.len()) }
|
||||
pub fn readlink(path: &CStr, data: &mut [u8]) -> io::Result<usize> {
|
||||
let r = unsafe { readlink_unsafe(path.as_ptr(), data.as_mut_ptr(), data.len()) };
|
||||
if r < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(r as usize)
|
||||
}
|
||||
|
||||
pub fn fd_path(fd: RawFd, buf: &mut [u8]) -> isize {
|
||||
let mut fd_buf: [u8; 40] = [0; 40];
|
||||
pub fn fd_path(fd: RawFd, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let mut fd_buf = [0_u8; 40];
|
||||
let fd_path = bfmt_cstr!(&mut fd_buf, "/proc/self/fd/{}", fd);
|
||||
readlink(fd_path, buf)
|
||||
}
|
||||
|
||||
// Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp
|
||||
pub fn realpath(path: &CStr, buf: &mut [u8]) -> isize {
|
||||
if let Some(fd) = open_fd!(path, O_PATH | O_CLOEXEC) {
|
||||
pub fn realpath(path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let fd = open_fd!(path, O_PATH | O_CLOEXEC)?;
|
||||
let mut st1: libc::stat;
|
||||
let mut st2: libc::stat;
|
||||
let mut skip_check = false;
|
||||
@ -95,45 +69,39 @@ pub fn realpath(path: &CStr, buf: &mut [u8]) -> isize {
|
||||
skip_check = true;
|
||||
}
|
||||
}
|
||||
let len = fd_path(fd.as_raw_fd(), buf);
|
||||
let len = fd_path(fd.as_raw_fd(), buf)?;
|
||||
unsafe {
|
||||
st2 = mem::zeroed();
|
||||
if libc::stat(buf.as_ptr().cast(), &mut st2) < 0
|
||||
|| (!skip_check && (st2.st_dev != st1.st_dev || st2.st_ino != st1.st_ino))
|
||||
{
|
||||
*errno() = ENOENT;
|
||||
return -1;
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
len
|
||||
} else {
|
||||
*errno() = ENOENT;
|
||||
-1
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn strscpy(dst: *mut c_char, src: *const c_char, size: usize) -> usize;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
||||
pub fn mkdirs(path: &CStr, mode: mode_t) -> io::Result<()> {
|
||||
let mut buf = [0_u8; 4096];
|
||||
let ptr: *mut c_char = buf.as_mut_ptr().cast();
|
||||
let len = strscpy(ptr, path, buf.len());
|
||||
let mut curr = &mut buf[1..len];
|
||||
while let Some(p) = curr.iter().position(|c| *c == b'/') {
|
||||
curr[p] = b'\0';
|
||||
if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST {
|
||||
return -1;
|
||||
let len = copy_cstr(&mut buf, path);
|
||||
let buf = &mut buf[..len];
|
||||
let mut off = 1;
|
||||
unsafe {
|
||||
while let Some(p) = buf[off..].iter().position(|c| *c == b'/') {
|
||||
buf[off + p] = b'\0';
|
||||
if libc::mkdir(buf.as_ptr().cast(), mode) < 0 && *errno() != EEXIST {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
curr[p] = b'/';
|
||||
curr = &mut curr[(p + 1)..];
|
||||
buf[off + p] = b'/';
|
||||
off += p + 1;
|
||||
}
|
||||
if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST {
|
||||
return -1;
|
||||
if libc::mkdir(buf.as_ptr().cast(), mode) < 0 && *errno() != EEXIST {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
0
|
||||
}
|
||||
*errno() = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait ReadExt {
|
||||
@ -221,3 +189,276 @@ impl<T: Write> WriteExt for T {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DirEntry<'a> {
|
||||
dir: &'a Directory<'a>,
|
||||
entry: &'a dirent,
|
||||
name_len: usize,
|
||||
}
|
||||
|
||||
impl DirEntry<'_> {
|
||||
pub fn d_name(&self) -> &CStr {
|
||||
unsafe {
|
||||
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
||||
self.d_name.as_ptr().cast(),
|
||||
self.name_len,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let mut len = self.dir.path(buf)?;
|
||||
buf[len] = b'/';
|
||||
len += 1;
|
||||
len += copy_cstr(&mut buf[len..], self.d_name());
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.d_type == libc::DT_DIR
|
||||
}
|
||||
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.d_type == libc::DT_REG
|
||||
}
|
||||
|
||||
pub fn is_lnk(&self) -> bool {
|
||||
self.d_type == libc::DT_LNK
|
||||
}
|
||||
|
||||
pub fn unlink(&self) -> io::Result<()> {
|
||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||
unsafe {
|
||||
if libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
||||
if !self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
||||
}
|
||||
unsafe {
|
||||
let fd = libc::openat(
|
||||
self.dir.as_raw_fd(),
|
||||
self.d_name.as_ptr(),
|
||||
O_RDONLY | O_CLOEXEC,
|
||||
);
|
||||
if fd < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Directory::try_from(OwnedFd::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
||||
if self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
||||
}
|
||||
unsafe {
|
||||
let fd = libc::openat(
|
||||
self.dir.as_raw_fd(),
|
||||
self.d_name.as_ptr(),
|
||||
flags | O_CLOEXEC,
|
||||
);
|
||||
if fd < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(File::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DirEntry<'_> {
|
||||
type Target = dirent;
|
||||
|
||||
fn deref(&self) -> &dirent {
|
||||
self.entry
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Directory<'a> {
|
||||
dirp: *mut libc::DIR,
|
||||
_phantom: PhantomData<&'a libc::DIR>,
|
||||
}
|
||||
|
||||
pub enum WalkResult {
|
||||
Continue,
|
||||
Abort,
|
||||
Skip,
|
||||
}
|
||||
|
||||
impl<'a> Directory<'a> {
|
||||
pub fn open(path: &CStr) -> io::Result<Directory> {
|
||||
let dirp = unsafe { libc::opendir(path.as_ptr()) };
|
||||
if dirp.is_null() {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(Directory {
|
||||
dirp,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> io::Result<Option<DirEntry<'_>>> {
|
||||
*errno() = 0;
|
||||
let e = unsafe { libc::readdir(self.dirp) };
|
||||
if e.is_null() {
|
||||
return if *errno() != 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
// Skip both "." and ".."
|
||||
unsafe {
|
||||
let entry = &*e;
|
||||
let d_name = CStr::from_ptr(entry.d_name.as_ptr());
|
||||
return if d_name == cstr!(".") || d_name == cstr!("..") {
|
||||
self.read()
|
||||
} else {
|
||||
let e = DirEntry {
|
||||
dir: self,
|
||||
entry,
|
||||
name_len: d_name.to_bytes().len(),
|
||||
};
|
||||
Ok(Some(e))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self) {
|
||||
unsafe { libc::rewinddir(self.dirp) }
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
fd_path(self.as_raw_fd(), buf)
|
||||
}
|
||||
|
||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
self.post_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
self.pre_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) -> io::Result<()> {
|
||||
self.post_order_walk(|e| {
|
||||
e.unlink()?;
|
||||
Ok(WalkResult::Continue)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Directory<'_> {
|
||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
None => return Ok(Continue),
|
||||
Some(ref e) => {
|
||||
if e.is_dir() {
|
||||
let mut dir = e.open_as_dir()?;
|
||||
if let Abort = dir.post_order_walk_impl(f)? {
|
||||
return Ok(Abort);
|
||||
}
|
||||
}
|
||||
match f(e)? {
|
||||
Abort => return Ok(Abort),
|
||||
Skip => return Ok(Continue),
|
||||
Continue => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
None => return Ok(Continue),
|
||||
Some(ref e) => match f(e)? {
|
||||
Abort => return Ok(Abort),
|
||||
Skip => return Ok(Continue),
|
||||
Continue => {
|
||||
if e.is_dir() {
|
||||
let mut dir = e.open_as_dir()?;
|
||||
if let Abort = dir.pre_order_walk_impl(f)? {
|
||||
return Ok(Abort);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OwnedFd> for Directory<'_> {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(fd: OwnedFd) -> io::Result<Self> {
|
||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) };
|
||||
if dirp.is_null() {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(Directory {
|
||||
dirp,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Directory<'_> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
unsafe { libc::dirfd(self.dirp) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsFd for Directory<'a> {
|
||||
fn as_fd(&self) -> BorrowedFd<'a> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Directory<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
libc::closedir(self.dirp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rm_rf(path: &CStr) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut stat: libc::stat = mem::zeroed();
|
||||
if libc::lstat(path.as_ptr(), &mut stat) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if (stat.st_mode & libc::S_IFMT as u32) == libc::S_IFDIR as u32 {
|
||||
let mut dir = Directory::open(path)?;
|
||||
dir.remove_all()?;
|
||||
}
|
||||
if libc::remove(path.as_ptr()) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -4,11 +4,13 @@
|
||||
|
||||
pub use libc;
|
||||
|
||||
use cxx_extern::*;
|
||||
pub use files::*;
|
||||
pub use logging::*;
|
||||
pub use misc::*;
|
||||
pub use xwrap::*;
|
||||
|
||||
mod cxx_extern;
|
||||
mod files;
|
||||
mod logging;
|
||||
mod misc;
|
||||
@ -34,6 +36,7 @@ pub mod ffi {
|
||||
#[namespace = "rust"]
|
||||
extern "Rust" {
|
||||
fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32;
|
||||
#[rust_name = "fd_path_for_cxx"]
|
||||
fn fd_path(fd: i32, buf: &mut [u8]) -> isize;
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,21 @@ use std::{fmt, slice};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
pub fn copy_str(dest: &mut [u8], src: &[u8]) -> usize {
|
||||
pub fn copy_str<T: AsRef<[u8]>>(dest: &mut [u8], src: T) -> usize {
|
||||
let src = src.as_ref();
|
||||
let len = min(src.len(), dest.len() - 1);
|
||||
dest[..len].copy_from_slice(&src[..len]);
|
||||
dest[len] = b'\0';
|
||||
len
|
||||
}
|
||||
|
||||
pub fn copy_cstr(dest: &mut [u8], src: &CStr) -> usize {
|
||||
let src = src.to_bytes_with_nul();
|
||||
let len = min(src.len(), dest.len());
|
||||
dest[..len].copy_from_slice(&src[..len]);
|
||||
len
|
||||
}
|
||||
|
||||
struct BufFmtWriter<'a> {
|
||||
buf: &'a mut [u8],
|
||||
used: usize,
|
||||
@ -31,7 +39,7 @@ impl<'a> fmt::Write for BufFmtWriter<'a> {
|
||||
// Silent truncate
|
||||
return Ok(());
|
||||
}
|
||||
self.used += copy_str(&mut self.buf[self.used..], s.as_bytes());
|
||||
self.used += copy_str(&mut self.buf[self.used..], s);
|
||||
// Silent truncate
|
||||
Ok(())
|
||||
}
|
||||
@ -57,6 +65,7 @@ macro_rules! bfmt {
|
||||
macro_rules! bfmt_cstr {
|
||||
($buf:expr, $($args:tt)*) => {{
|
||||
let len = $crate::fmt_to_buf($buf, format_args!($($args)*));
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&$buf[..(len + 1)]) }
|
||||
}};
|
||||
}
|
||||
@ -70,7 +79,10 @@ macro_rules! cstr {
|
||||
!$str.bytes().any(|b| b == b'\0'),
|
||||
"cstr argument contains embedded NUL bytes",
|
||||
);
|
||||
unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes()) }
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
std::ffi::CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes())
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -2,22 +2,26 @@ use std::ffi::CStr;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
|
||||
use anyhow::Context;
|
||||
use libc::{
|
||||
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
||||
ssize_t, SYS_dup3,
|
||||
};
|
||||
|
||||
use crate::{cstr, errno, error, mkdirs, perror, ptr_to_str, raw_cstr, realpath};
|
||||
use crate::{cstr, errno, error, mkdirs, perror, ptr_to_str, raw_cstr, ResultExt};
|
||||
|
||||
mod unsafe_impl {
|
||||
use std::ffi::CStr;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use anyhow::Context;
|
||||
use cfg_if::cfg_if;
|
||||
use libc::{c_char, nfds_t, off_t, pollfd};
|
||||
|
||||
use crate::unsafe_impl::readlink;
|
||||
use crate::{perror, ptr_to_str, slice_from_ptr, slice_from_ptr_mut};
|
||||
use crate::{
|
||||
perror, ptr_to_str, readlink, readlink_unsafe, slice_from_ptr, slice_from_ptr_mut,
|
||||
ResultExt,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||
@ -36,12 +40,15 @@ mod unsafe_impl {
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
super::xrealpath(CStr::from_ptr(path), slice_from_ptr_mut(buf, bufsz))
|
||||
readlink(CStr::from_ptr(path), slice_from_ptr_mut(buf, bufsz))
|
||||
.with_context(|| format!("realpath {} failed", ptr_to_str(path)))
|
||||
.log()
|
||||
.map_or(-1, |v| v as isize)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
let r = readlink(path, buf, bufsz);
|
||||
let r = readlink_unsafe(path, buf, bufsz);
|
||||
if r < 0 {
|
||||
perror!("readlink");
|
||||
}
|
||||
@ -572,11 +579,10 @@ pub unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
||||
let r = mkdirs(path, mode);
|
||||
if r < 0 {
|
||||
perror!("mkdirs {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
mkdirs(CStr::from_ptr(path), mode)
|
||||
.with_context(|| format!("mkdirs {}", ptr_to_str(path)))
|
||||
.log()
|
||||
.map_or(-1, |_| 0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -629,14 +635,6 @@ pub fn xpoll(fds: &mut [pollfd], timeout: i32) -> i32 {
|
||||
unsafe { unsafe_impl::xpoll(fds.as_mut_ptr(), fds.len() as nfds_t, timeout) }
|
||||
}
|
||||
|
||||
pub fn xrealpath(path: &CStr, buf: &mut [u8]) -> isize {
|
||||
let r = realpath(path, buf);
|
||||
if r < 0 {
|
||||
perror!("realpath {}", path.to_str().unwrap_or(""))
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
||||
let r = libc::mknod(pathname, mode, dev);
|
||||
|
Loading…
x
Reference in New Issue
Block a user