mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-22 16:07: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"
|
name = "base"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cxx",
|
"cxx",
|
||||||
"cxx-gen",
|
"cxx-gen",
|
||||||
|
@ -14,3 +14,4 @@ cxx = { workspace = true }
|
|||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
cfg-if = { workspace = true }
|
cfg-if = { workspace = true }
|
||||||
thiserror = { 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;
|
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) {
|
void mv_path(const char *src, const char *dest) {
|
||||||
file_attr attr;
|
file_attr attr;
|
||||||
getattr(src, &attr);
|
getattr(src, &attr);
|
||||||
|
@ -55,12 +55,13 @@ extern "C" {
|
|||||||
|
|
||||||
int mkdirs(const char *path, mode_t mode);
|
int mkdirs(const char *path, mode_t mode);
|
||||||
ssize_t canonical_path(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
|
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"
|
} // extern "C"
|
||||||
|
|
||||||
using rust::fd_path;
|
using rust::fd_path;
|
||||||
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
|
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_path(const char *src, const char *dest);
|
||||||
void mv_dir(int src, int dest);
|
void mv_dir(int src, int dest);
|
||||||
void cp_afc(const char *src, const char *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(FILE *fp, const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||||
void parse_prop_file(const char *file,
|
void parse_prop_file(const char *file,
|
||||||
const std::function<bool(std::string_view, std::string_view)> &fn);
|
const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||||
void frm_rf(int dirfd);
|
|
||||||
void clone_dir(int src, int dest);
|
void clone_dir(int src, int dest);
|
||||||
std::vector<mount_info> parse_mount_info(const char *pid);
|
std::vector<mount_info> parse_mount_info(const char *pid);
|
||||||
std::string find_apk_path(const char *pkg);
|
std::string find_apk_path(const char *pkg);
|
||||||
|
@ -1,53 +1,25 @@
|
|||||||
use mem::MaybeUninit;
|
use mem::MaybeUninit;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::fs::File;
|
||||||
use std::io::{BufRead, Read, Seek, SeekFrom, Write};
|
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::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 {
|
pub fn __open_fd_impl(path: &CStr, flags: i32, mode: mode_t) -> io::Result<OwnedFd> {
|
||||||
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> {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let fd = libc::open(path.as_ptr(), flags, mode as c_uint);
|
let fd = libc::open(path.as_ptr(), flags, mode as c_uint);
|
||||||
if fd >= 0 {
|
if fd >= 0 {
|
||||||
Some(OwnedFd::from_raw_fd(fd))
|
Ok(OwnedFd::from_raw_fd(fd))
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(io::Error::last_os_error())
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,78 +34,74 @@ macro_rules! open_fd {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
pub unsafe fn readlink_unsafe(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||||
macro_rules! xopen_fd {
|
let r = libc::readlink(path, buf.cast(), bufsz - 1);
|
||||||
($path:expr, $flags:expr) => {
|
if r >= 0 {
|
||||||
$crate::__xopen_fd_impl($path, $flags, 0)
|
*buf.offset(r) = b'\0';
|
||||||
};
|
}
|
||||||
($path:expr, $flags:expr, $mode:expr) => {
|
r
|
||||||
$crate::__xopen_fd_impl($path, $flags, $mode)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readlink(path: &CStr, data: &mut [u8]) -> isize {
|
pub fn readlink(path: &CStr, data: &mut [u8]) -> io::Result<usize> {
|
||||||
unsafe { unsafe_impl::readlink(path.as_ptr(), data.as_mut_ptr(), data.len()) }
|
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 {
|
pub fn fd_path(fd: RawFd, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
let mut fd_buf: [u8; 40] = [0; 40];
|
let mut fd_buf = [0_u8; 40];
|
||||||
let fd_path = bfmt_cstr!(&mut fd_buf, "/proc/self/fd/{}", fd);
|
let fd_path = bfmt_cstr!(&mut fd_buf, "/proc/self/fd/{}", fd);
|
||||||
readlink(fd_path, buf)
|
readlink(fd_path, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(path: &CStr, buf: &mut [u8]) -> isize {
|
pub fn realpath(path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
if let Some(fd) = open_fd!(path, O_PATH | O_CLOEXEC) {
|
let fd = open_fd!(path, O_PATH | O_CLOEXEC)?;
|
||||||
let mut st1: libc::stat;
|
let mut st1: libc::stat;
|
||||||
let mut st2: libc::stat;
|
let mut st2: libc::stat;
|
||||||
let mut skip_check = false;
|
let mut skip_check = false;
|
||||||
unsafe {
|
unsafe {
|
||||||
st1 = mem::zeroed();
|
st1 = mem::zeroed();
|
||||||
if libc::fstat(fd.as_raw_fd(), &mut st1) < 0 {
|
if libc::fstat(fd.as_raw_fd(), &mut st1) < 0 {
|
||||||
// This shall only fail on Linux < 3.6
|
// This shall only fail on Linux < 3.6
|
||||||
skip_check = true;
|
skip_check = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
len
|
|
||||||
} else {
|
|
||||||
*errno() = ENOENT;
|
|
||||||
-1
|
|
||||||
}
|
}
|
||||||
|
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 Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(len)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
pub fn mkdirs(path: &CStr, mode: mode_t) -> io::Result<()> {
|
||||||
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 {
|
|
||||||
let mut buf = [0_u8; 4096];
|
let mut buf = [0_u8; 4096];
|
||||||
let ptr: *mut c_char = buf.as_mut_ptr().cast();
|
let len = copy_cstr(&mut buf, path);
|
||||||
let len = strscpy(ptr, path, buf.len());
|
let buf = &mut buf[..len];
|
||||||
let mut curr = &mut buf[1..len];
|
let mut off = 1;
|
||||||
while let Some(p) = curr.iter().position(|c| *c == b'/') {
|
unsafe {
|
||||||
curr[p] = b'\0';
|
while let Some(p) = buf[off..].iter().position(|c| *c == b'/') {
|
||||||
if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST {
|
buf[off + p] = b'\0';
|
||||||
return -1;
|
if libc::mkdir(buf.as_ptr().cast(), mode) < 0 && *errno() != EEXIST {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
curr[p] = b'/';
|
|
||||||
curr = &mut curr[(p + 1)..];
|
|
||||||
}
|
}
|
||||||
if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST {
|
*errno() = 0;
|
||||||
return -1;
|
Ok(())
|
||||||
}
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ReadExt {
|
pub trait ReadExt {
|
||||||
@ -221,3 +189,276 @@ impl<T: Write> WriteExt for T {
|
|||||||
Ok(())
|
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;
|
pub use libc;
|
||||||
|
|
||||||
|
use cxx_extern::*;
|
||||||
pub use files::*;
|
pub use files::*;
|
||||||
pub use logging::*;
|
pub use logging::*;
|
||||||
pub use misc::*;
|
pub use misc::*;
|
||||||
pub use xwrap::*;
|
pub use xwrap::*;
|
||||||
|
|
||||||
|
mod cxx_extern;
|
||||||
mod files;
|
mod files;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod misc;
|
mod misc;
|
||||||
@ -34,6 +36,7 @@ pub mod ffi {
|
|||||||
#[namespace = "rust"]
|
#[namespace = "rust"]
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32;
|
fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32;
|
||||||
|
#[rust_name = "fd_path_for_cxx"]
|
||||||
fn fd_path(fd: i32, buf: &mut [u8]) -> isize;
|
fn fd_path(fd: i32, buf: &mut [u8]) -> isize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,21 @@ use std::{fmt, slice};
|
|||||||
|
|
||||||
use thiserror::Error;
|
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);
|
let len = min(src.len(), dest.len() - 1);
|
||||||
dest[..len].copy_from_slice(&src[..len]);
|
dest[..len].copy_from_slice(&src[..len]);
|
||||||
dest[len] = b'\0';
|
dest[len] = b'\0';
|
||||||
len
|
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> {
|
struct BufFmtWriter<'a> {
|
||||||
buf: &'a mut [u8],
|
buf: &'a mut [u8],
|
||||||
used: usize,
|
used: usize,
|
||||||
@ -31,7 +39,7 @@ impl<'a> fmt::Write for BufFmtWriter<'a> {
|
|||||||
// Silent truncate
|
// Silent truncate
|
||||||
return Ok(());
|
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
|
// Silent truncate
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -57,6 +65,7 @@ macro_rules! bfmt {
|
|||||||
macro_rules! bfmt_cstr {
|
macro_rules! bfmt_cstr {
|
||||||
($buf:expr, $($args:tt)*) => {{
|
($buf:expr, $($args:tt)*) => {{
|
||||||
let len = $crate::fmt_to_buf($buf, format_args!($($args)*));
|
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)]) }
|
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'),
|
!$str.bytes().any(|b| b == b'\0'),
|
||||||
"cstr argument contains embedded NUL bytes",
|
"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::os::unix::io::RawFd;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use libc::{
|
use libc::{
|
||||||
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
||||||
ssize_t, SYS_dup3,
|
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 {
|
mod unsafe_impl {
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use libc::{c_char, nfds_t, off_t, pollfd};
|
use libc::{c_char, nfds_t, off_t, pollfd};
|
||||||
|
|
||||||
use crate::unsafe_impl::readlink;
|
use crate::{
|
||||||
use crate::{perror, ptr_to_str, slice_from_ptr, slice_from_ptr_mut};
|
perror, ptr_to_str, readlink, readlink_unsafe, slice_from_ptr, slice_from_ptr_mut,
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||||
@ -36,12 +40,15 @@ mod unsafe_impl {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
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]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
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 {
|
if r < 0 {
|
||||||
perror!("readlink");
|
perror!("readlink");
|
||||||
}
|
}
|
||||||
@ -572,11 +579,10 @@ pub unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
pub unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
||||||
let r = mkdirs(path, mode);
|
mkdirs(CStr::from_ptr(path), mode)
|
||||||
if r < 0 {
|
.with_context(|| format!("mkdirs {}", ptr_to_str(path)))
|
||||||
perror!("mkdirs {}", ptr_to_str(path));
|
.log()
|
||||||
}
|
.map_or(-1, |_| 0)
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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) }
|
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]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
pub unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
||||||
let r = libc::mknod(pathname, mode, dev);
|
let r = libc::mknod(pathname, mode, dev);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user