mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-26 16:00:52 +00:00
Introduce FsPathFollow
Make sure all operations of FsPath do not follow symlinks, and provide a way to explicitly switch over to a set of operations that DO follow symlinks by FsPath::follow_link.
This commit is contained in:
parent
c64de35375
commit
6ff82c4e86
@ -501,6 +501,25 @@ impl DerefMut for FsPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct FsPathFollow(Utf8CStr);
|
||||||
|
|
||||||
|
impl Deref for FsPathFollow {
|
||||||
|
type Target = Utf8CStr;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn deref(&self) -> &Utf8CStr {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for FsPathFollow {
|
||||||
|
#[inline(always)]
|
||||||
|
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Utf8CStrBufOwned<const N: usize> {
|
enum Utf8CStrBufOwned<const N: usize> {
|
||||||
Dynamic(Utf8CString),
|
Dynamic(Utf8CString),
|
||||||
Fixed(Utf8CStrBufArr<N>),
|
Fixed(Utf8CStrBufArr<N>),
|
||||||
@ -667,6 +686,7 @@ macro_rules! impl_str {
|
|||||||
impl_str!(
|
impl_str!(
|
||||||
(Utf8CStr,)
|
(Utf8CStr,)
|
||||||
(FsPath,)
|
(FsPath,)
|
||||||
|
(FsPathFollow,)
|
||||||
(FsPathBuf<N>, const N: usize)
|
(FsPathBuf<N>, const N: usize)
|
||||||
(Utf8CStrBufRef<'_>,)
|
(Utf8CStrBufRef<'_>,)
|
||||||
(Utf8CStrBufArr<N>, const N: usize)
|
(Utf8CStrBufArr<N>, const N: usize)
|
||||||
|
420
native/src/base/dir.rs
Normal file
420
native/src/base/dir.rs
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
use crate::cxx_extern::readlinkat_for_cxx;
|
||||||
|
use crate::{
|
||||||
|
cstr, cstr_buf, errno, fd_path, fd_set_attr, FileAttr, FsPath, LibcReturn, Utf8CStr,
|
||||||
|
Utf8CStrBuf,
|
||||||
|
};
|
||||||
|
use libc::{dirent, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||||
|
use std::{io, mem, slice};
|
||||||
|
|
||||||
|
pub struct DirEntry<'a> {
|
||||||
|
dir: &'a Directory,
|
||||||
|
entry: &'a dirent,
|
||||||
|
d_name_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirEntry<'_> {
|
||||||
|
pub fn name(&self) -> &CStr {
|
||||||
|
unsafe {
|
||||||
|
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
||||||
|
self.d_name.as_ptr().cast(),
|
||||||
|
self.d_name_len,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
self.dir.path(buf)?;
|
||||||
|
buf.push_str("/");
|
||||||
|
buf.push_lossy(self.name().to_bytes());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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_symlink(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_LNK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_block_device(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_BLK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_char_device(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_CHR
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_fifo(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_FIFO
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_socket(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_SOCK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlink(&self) -> io::Result<()> {
|
||||||
|
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||||
|
unsafe {
|
||||||
|
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
buf.clear();
|
||||||
|
unsafe {
|
||||||
|
let r = readlinkat_for_cxx(
|
||||||
|
self.dir.as_raw_fd(),
|
||||||
|
self.d_name.as_ptr(),
|
||||||
|
buf.as_mut_ptr().cast(),
|
||||||
|
buf.capacity(),
|
||||||
|
)
|
||||||
|
.check_os_err()? as usize;
|
||||||
|
buf.set_len(r);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
||||||
|
unsafe { self.dir.open_raw_fd(self.name(), flags, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
||||||
|
if !self.is_dir() {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
||||||
|
}
|
||||||
|
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
||||||
|
if self.is_dir() {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
||||||
|
}
|
||||||
|
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).get_attr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).set_attr(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).get_secontext(con)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).set_secontext(con)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for DirEntry<'_> {
|
||||||
|
type Target = dirent;
|
||||||
|
|
||||||
|
fn deref(&self) -> &dirent {
|
||||||
|
self.entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Directory {
|
||||||
|
dirp: *mut libc::DIR,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum WalkResult {
|
||||||
|
Continue,
|
||||||
|
Abort,
|
||||||
|
Skip,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
||||||
|
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
||||||
|
Ok(Directory { dirp })
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
if d_name == cstr!(".") || d_name == cstr!("..") {
|
||||||
|
self.read()
|
||||||
|
} else {
|
||||||
|
let e = DirEntry {
|
||||||
|
dir: self,
|
||||||
|
entry,
|
||||||
|
d_name_len: d_name.to_bytes_with_nul().len(),
|
||||||
|
};
|
||||||
|
Ok(Some(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rewind(&mut self) {
|
||||||
|
unsafe { libc::rewinddir(self.dirp) }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn open_raw_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
||||||
|
unsafe {
|
||||||
|
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_fd(&self, name: &Utf8CStr, flags: i32, mode: i32) -> io::Result<OwnedFd> {
|
||||||
|
unsafe {
|
||||||
|
self.open_raw_fd(name.as_cstr(), flags, mode)
|
||||||
|
.map(|fd| OwnedFd::from_raw_fd(fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_path(&self, path: &CStr) -> bool {
|
||||||
|
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
||||||
|
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
||||||
|
// Use fstatat to check the existence of a file instead.
|
||||||
|
unsafe {
|
||||||
|
let mut st: libc::stat = mem::zeroed();
|
||||||
|
libc::fstatat(
|
||||||
|
self.as_raw_fd(),
|
||||||
|
path.as_ptr(),
|
||||||
|
&mut st,
|
||||||
|
libc::AT_SYMLINK_NOFOLLOW,
|
||||||
|
) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||||
|
while let Some(ref e) = self.read()? {
|
||||||
|
let attr = e.get_attr()?;
|
||||||
|
let new_entry = DirEntry {
|
||||||
|
dir,
|
||||||
|
entry: e.entry,
|
||||||
|
d_name_len: e.d_name_len,
|
||||||
|
};
|
||||||
|
if e.is_dir() {
|
||||||
|
unsafe {
|
||||||
|
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||||
|
}
|
||||||
|
let mut src = e.open_as_dir()?;
|
||||||
|
let dest = new_entry.open_as_dir()?;
|
||||||
|
src.copy_into(&dest)?;
|
||||||
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||||
|
} else if e.is_file() {
|
||||||
|
let mut src = e.open_as_file(O_RDONLY)?;
|
||||||
|
let mut dest = unsafe {
|
||||||
|
File::from_raw_fd(dir.open_raw_fd(
|
||||||
|
e.name(),
|
||||||
|
O_WRONLY | O_CREAT | O_TRUNC,
|
||||||
|
0o777,
|
||||||
|
)?)
|
||||||
|
};
|
||||||
|
std::io::copy(&mut src, &mut dest)?;
|
||||||
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||||
|
} else if e.is_symlink() {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
e.read_link(&mut path)?;
|
||||||
|
unsafe {
|
||||||
|
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
||||||
|
.as_os_err()?;
|
||||||
|
}
|
||||||
|
new_entry.set_attr(&attr)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||||
|
let dir_fd = self.as_raw_fd();
|
||||||
|
while let Some(ref e) = self.read()? {
|
||||||
|
if e.is_dir() && dir.contains_path(e.name()) {
|
||||||
|
// Destination folder exists, needs recursive move
|
||||||
|
let mut src = e.open_as_dir()?;
|
||||||
|
let new_entry = DirEntry {
|
||||||
|
dir,
|
||||||
|
entry: e.entry,
|
||||||
|
d_name_len: e.d_name_len,
|
||||||
|
};
|
||||||
|
let dest = new_entry.open_as_dir()?;
|
||||||
|
src.move_into(&dest)?;
|
||||||
|
return e.unlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libc::renameat(
|
||||||
|
dir_fd,
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
dir.as_raw_fd(),
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
)
|
||||||
|
.as_os_err()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||||
|
let dir_fd = self.as_raw_fd();
|
||||||
|
while let Some(ref e) = self.read()? {
|
||||||
|
if e.is_dir() {
|
||||||
|
unsafe {
|
||||||
|
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||||
|
}
|
||||||
|
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 dest = new_entry.open_as_dir()?;
|
||||||
|
src.link_into(&dest)?;
|
||||||
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
libc::linkat(
|
||||||
|
dir_fd,
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
dir.as_raw_fd(),
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.as_os_err()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 => 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()) }.check_os_err()?;
|
||||||
|
Ok(Directory { dirp })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Directory {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
unsafe { libc::dirfd(self.dirp) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsFd for Directory {
|
||||||
|
fn as_fd(&self) -> BorrowedFd {
|
||||||
|
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Directory {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
libc::closedir(self.dirp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
use crate::cxx_extern::readlinkat_for_cxx;
|
use crate::{
|
||||||
use crate::{cstr, cstr_buf, errno, error, FsPath, FsPathBuf, LibcReturn, Utf8CStr, Utf8CStrBuf};
|
cstr_buf, errno, error, Directory, FsPath, FsPathBuf, FsPathFollow, LibcReturn, Utf8CStr,
|
||||||
|
Utf8CStrBuf,
|
||||||
|
};
|
||||||
use bytemuck::{bytes_of, bytes_of_mut, Pod};
|
use bytemuck::{bytes_of, bytes_of_mut, Pod};
|
||||||
use libc::{
|
use libc::{
|
||||||
c_uint, dirent, makedev, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
c_uint, makedev, mode_t, stat, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
||||||
O_RDWR, O_TRUNC, O_WRONLY,
|
O_RDWR, O_TRUNC, O_WRONLY,
|
||||||
};
|
};
|
||||||
use mem::MaybeUninit;
|
use mem::MaybeUninit;
|
||||||
@ -11,8 +13,7 @@ use std::cmp::min;
|
|||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
||||||
use std::ops::Deref;
|
use std::os::fd::{AsFd, BorrowedFd};
|
||||||
use std::os::fd::{AsFd, BorrowedFd, IntoRawFd};
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@ -186,418 +187,13 @@ impl FileAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const XATTR_NAME_SELINUX: &[u8] = b"security.selinux\0";
|
const XATTR_NAME_SELINUX: &CStr = c"security.selinux";
|
||||||
|
|
||||||
pub struct DirEntry<'a> {
|
|
||||||
dir: &'a Directory,
|
|
||||||
entry: &'a dirent,
|
|
||||||
d_name_len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirEntry<'_> {
|
|
||||||
pub fn name(&self) -> &CStr {
|
|
||||||
unsafe {
|
|
||||||
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
|
||||||
self.d_name.as_ptr().cast(),
|
|
||||||
self.d_name_len,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
self.dir.path(buf)?;
|
|
||||||
buf.push_str("/");
|
|
||||||
buf.push_lossy(self.name().to_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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_symlink(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_LNK
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_block_device(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_BLK
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_char_device(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_CHR
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_fifo(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_FIFO
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_socket(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_SOCK
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unlink(&self) -> io::Result<()> {
|
|
||||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
|
||||||
unsafe {
|
|
||||||
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
buf.clear();
|
|
||||||
unsafe {
|
|
||||||
let r = readlinkat_for_cxx(
|
|
||||||
self.dir.as_raw_fd(),
|
|
||||||
self.d_name.as_ptr(),
|
|
||||||
buf.as_mut_ptr().cast(),
|
|
||||||
buf.capacity(),
|
|
||||||
)
|
|
||||||
.check_os_err()? as usize;
|
|
||||||
buf.set_len(r);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
|
||||||
unsafe { self.dir.open_raw_fd(self.name(), flags, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
|
||||||
if !self.is_dir() {
|
|
||||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
|
||||||
}
|
|
||||||
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
|
||||||
if self.is_dir() {
|
|
||||||
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
|
||||||
}
|
|
||||||
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
|
||||||
let mut path = cstr_buf::default();
|
|
||||||
self.path(&mut path)?;
|
|
||||||
FsPath::from(&path).get_attr()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
|
||||||
let mut path = cstr_buf::default();
|
|
||||||
self.path(&mut path)?;
|
|
||||||
FsPath::from(&path).set_attr(attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
let mut path = cstr_buf::default();
|
|
||||||
self.path(&mut path)?;
|
|
||||||
FsPath::from(&path).get_secontext(con)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
|
||||||
let mut path = cstr_buf::default();
|
|
||||||
self.path(&mut path)?;
|
|
||||||
FsPath::from(&path).set_secontext(con)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for DirEntry<'_> {
|
|
||||||
type Target = dirent;
|
|
||||||
|
|
||||||
fn deref(&self) -> &dirent {
|
|
||||||
self.entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Directory {
|
|
||||||
dirp: *mut libc::DIR,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum WalkResult {
|
|
||||||
Continue,
|
|
||||||
Abort,
|
|
||||||
Skip,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Directory {
|
|
||||||
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
|
||||||
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
|
||||||
Ok(Directory { dirp })
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
if d_name == cstr!(".") || d_name == cstr!("..") {
|
|
||||||
self.read()
|
|
||||||
} else {
|
|
||||||
let e = DirEntry {
|
|
||||||
dir: self,
|
|
||||||
entry,
|
|
||||||
d_name_len: d_name.to_bytes_with_nul().len(),
|
|
||||||
};
|
|
||||||
Ok(Some(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rewind(&mut self) {
|
|
||||||
unsafe { libc::rewinddir(self.dirp) }
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn open_raw_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
|
||||||
unsafe {
|
|
||||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_fd(&self, name: &Utf8CStr, flags: i32, mode: i32) -> io::Result<OwnedFd> {
|
|
||||||
unsafe {
|
|
||||||
self.open_raw_fd(name.as_cstr(), flags, mode)
|
|
||||||
.map(|fd| OwnedFd::from_raw_fd(fd))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_path(&self, path: &CStr) -> bool {
|
|
||||||
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
|
||||||
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
|
||||||
// Use fstatat to check the existence of a file instead.
|
|
||||||
unsafe {
|
|
||||||
let mut st: libc::stat = mem::zeroed();
|
|
||||||
libc::fstatat(
|
|
||||||
self.as_raw_fd(),
|
|
||||||
path.as_ptr(),
|
|
||||||
&mut st,
|
|
||||||
libc::AT_SYMLINK_NOFOLLOW,
|
|
||||||
) == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
|
||||||
while let Some(ref e) = self.read()? {
|
|
||||||
let attr = e.get_attr()?;
|
|
||||||
let new_entry = DirEntry {
|
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
if e.is_dir() {
|
|
||||||
unsafe {
|
|
||||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
|
||||||
}
|
|
||||||
let mut src = e.open_as_dir()?;
|
|
||||||
let dest = new_entry.open_as_dir()?;
|
|
||||||
src.copy_into(&dest)?;
|
|
||||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
||||||
} else if e.is_file() {
|
|
||||||
let mut src = e.open_as_file(O_RDONLY)?;
|
|
||||||
let mut dest = unsafe {
|
|
||||||
File::from_raw_fd(dir.open_raw_fd(
|
|
||||||
e.name(),
|
|
||||||
O_WRONLY | O_CREAT | O_TRUNC,
|
|
||||||
0o777,
|
|
||||||
)?)
|
|
||||||
};
|
|
||||||
std::io::copy(&mut src, &mut dest)?;
|
|
||||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
||||||
} else if e.is_symlink() {
|
|
||||||
let mut path = cstr_buf::default();
|
|
||||||
e.read_link(&mut path)?;
|
|
||||||
unsafe {
|
|
||||||
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
|
||||||
new_entry.set_attr(&attr)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
|
||||||
let dir_fd = self.as_raw_fd();
|
|
||||||
while let Some(ref e) = self.read()? {
|
|
||||||
if e.is_dir() && dir.contains_path(e.name()) {
|
|
||||||
// Destination folder exists, needs recursive move
|
|
||||||
let mut src = e.open_as_dir()?;
|
|
||||||
let new_entry = DirEntry {
|
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
let dest = new_entry.open_as_dir()?;
|
|
||||||
src.move_into(&dest)?;
|
|
||||||
return e.unlink();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
libc::renameat(
|
|
||||||
dir_fd,
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
dir.as_raw_fd(),
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
)
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
|
||||||
let dir_fd = self.as_raw_fd();
|
|
||||||
while let Some(ref e) = self.read()? {
|
|
||||||
if e.is_dir() {
|
|
||||||
unsafe {
|
|
||||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
|
||||||
}
|
|
||||||
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 dest = new_entry.open_as_dir()?;
|
|
||||||
src.link_into(&dest)?;
|
|
||||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
libc::linkat(
|
|
||||||
dir_fd,
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
dir.as_raw_fd(),
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 => 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()) }.check_os_err()?;
|
|
||||||
Ok(Directory { dirp })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRawFd for Directory {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
unsafe { libc::dirfd(self.dirp) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsFd for Directory {
|
|
||||||
fn as_fd(&self) -> BorrowedFd {
|
|
||||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Directory {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
libc::closedir(self.dirp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FsPath {
|
impl FsPath {
|
||||||
|
pub fn follow_link(&self) -> &FsPathFollow {
|
||||||
|
unsafe { mem::transmute(self) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn open(&self, flags: i32) -> io::Result<File> {
|
pub fn open(&self, flags: i32) -> io::Result<File> {
|
||||||
Ok(File::from(open_fd!(self, flags)?))
|
Ok(File::from(open_fd!(self, flags)?))
|
||||||
}
|
}
|
||||||
@ -607,7 +203,10 @@ impl FsPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn exists(&self) -> bool {
|
pub fn exists(&self) -> bool {
|
||||||
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
unsafe {
|
||||||
|
let mut st: stat = mem::zeroed();
|
||||||
|
libc::lstat(self.as_ptr(), &mut st) == 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename_to<T: AsRef<Utf8CStr>>(&self, name: T) -> io::Result<()> {
|
pub fn rename_to<T: AsRef<Utf8CStr>>(&self, name: T) -> io::Result<()> {
|
||||||
@ -732,7 +331,7 @@ impl FsPath {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let sz = libc::lgetxattr(
|
let sz = libc::lgetxattr(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
con.as_mut_ptr().cast(),
|
con.as_mut_ptr().cast(),
|
||||||
con.capacity(),
|
con.capacity(),
|
||||||
);
|
);
|
||||||
@ -752,7 +351,7 @@ impl FsPath {
|
|||||||
unsafe {
|
unsafe {
|
||||||
libc::lsetxattr(
|
libc::lsetxattr(
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
con.as_ptr().cast(),
|
con.as_ptr().cast(),
|
||||||
con.len() + 1,
|
con.len() + 1,
|
||||||
0,
|
0,
|
||||||
@ -832,6 +431,69 @@ impl FsPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FsPathFollow {
|
||||||
|
pub fn exists(&self) -> bool {
|
||||||
|
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||||
|
let mut attr = FileAttr::new();
|
||||||
|
unsafe {
|
||||||
|
libc::stat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
||||||
|
|
||||||
|
#[cfg(feature = "selinux")]
|
||||||
|
self.get_secontext(&mut attr.con)?;
|
||||||
|
}
|
||||||
|
Ok(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
||||||
|
libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
||||||
|
|
||||||
|
#[cfg(feature = "selinux")]
|
||||||
|
if !attr.con.is_empty() {
|
||||||
|
self.set_secontext(&attr.con)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let sz = libc::getxattr(
|
||||||
|
self.as_ptr(),
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_mut_ptr().cast(),
|
||||||
|
con.capacity(),
|
||||||
|
);
|
||||||
|
if sz < 1 {
|
||||||
|
con.clear();
|
||||||
|
if *errno() != libc::ENODATA {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
con.set_len((sz - 1) as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
libc::setxattr(
|
||||||
|
self.as_ptr(),
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_ptr().cast(),
|
||||||
|
con.len() + 1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.as_os_err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
||||||
let mut attr = FileAttr::new();
|
let mut attr = FileAttr::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -841,7 +503,7 @@ pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
|||||||
{
|
{
|
||||||
let sz = libc::fgetxattr(
|
let sz = libc::fgetxattr(
|
||||||
fd,
|
fd,
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
attr.con.as_mut_ptr().cast(),
|
attr.con.as_mut_ptr().cast(),
|
||||||
attr.con.capacity(),
|
attr.con.capacity(),
|
||||||
);
|
);
|
||||||
@ -866,7 +528,7 @@ pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> io::Result<()> {
|
|||||||
if !attr.con.is_empty() {
|
if !attr.con.is_empty() {
|
||||||
libc::fsetxattr(
|
libc::fsetxattr(
|
||||||
fd,
|
fd,
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
attr.con.as_ptr().cast(),
|
attr.con.as_ptr().cast(),
|
||||||
attr.con.len() + 1,
|
attr.con.len() + 1,
|
||||||
0,
|
0,
|
||||||
|
@ -8,6 +8,7 @@ use num_traits::FromPrimitive;
|
|||||||
|
|
||||||
pub use cstr::*;
|
pub use cstr::*;
|
||||||
use cxx_extern::*;
|
use cxx_extern::*;
|
||||||
|
pub use dir::*;
|
||||||
pub use ffi::fork_dont_care;
|
pub use ffi::fork_dont_care;
|
||||||
pub use files::*;
|
pub use files::*;
|
||||||
pub use logging::*;
|
pub use logging::*;
|
||||||
@ -16,6 +17,7 @@ pub use result::*;
|
|||||||
|
|
||||||
mod cstr;
|
mod cstr;
|
||||||
mod cxx_extern;
|
mod cxx_extern;
|
||||||
|
mod dir;
|
||||||
mod files;
|
mod files;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
@ -2,7 +2,7 @@ use crate::ffi::MagiskInit;
|
|||||||
use base::{
|
use base::{
|
||||||
clone_attr, cstr, debug, error, info,
|
clone_attr, cstr, debug, error, info,
|
||||||
libc::{
|
libc::{
|
||||||
fstatat, mount, stat, statfs, umount2, AT_SYMLINK_NOFOLLOW, MNT_DETACH, MS_BIND, O_CLOEXEC,
|
mount, statfs, umount2, MNT_DETACH, MS_BIND, O_CLOEXEC,
|
||||||
O_CREAT, O_RDONLY, O_WRONLY, TMPFS_MAGIC,
|
O_CREAT, O_RDONLY, O_WRONLY, TMPFS_MAGIC,
|
||||||
},
|
},
|
||||||
path, raw_cstr, LibcReturn, MappedFile, MutBytesExt, ResultExt,
|
path, raw_cstr, LibcReturn, MappedFile, MutBytesExt, ResultExt,
|
||||||
@ -14,16 +14,7 @@ impl MagiskInit {
|
|||||||
info!("First Stage Init");
|
info!("First Stage Init");
|
||||||
self.prepare_data();
|
self.prepare_data();
|
||||||
|
|
||||||
if unsafe {
|
if !path!("/sdcard").exists() && !path!("/first_stage_ramdisk/sdcard").exists() {
|
||||||
let mut st: stat = std::mem::zeroed();
|
|
||||||
fstatat(-1, raw_cstr!("/sdcard"), &mut st, AT_SYMLINK_NOFOLLOW) != 0
|
|
||||||
&& fstatat(
|
|
||||||
-1,
|
|
||||||
raw_cstr!("/first_stage_ramdisk/sdcard"),
|
|
||||||
&mut st,
|
|
||||||
AT_SYMLINK_NOFOLLOW,
|
|
||||||
) != 0
|
|
||||||
} {
|
|
||||||
if self.config.force_normal_boot {
|
if self.config.force_normal_boot {
|
||||||
path!("/first_stage_ramdisk/storage/self")
|
path!("/first_stage_ramdisk/storage/self")
|
||||||
.mkdirs(0o755)
|
.mkdirs(0o755)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user