Move more I/O operations into Rust

This commit is contained in:
topjohnwu 2023-09-26 20:18:37 -07:00
parent 5d07d0b964
commit a55d570213
13 changed files with 470 additions and 257 deletions

1
native/src/Cargo.lock generated
View File

@ -75,6 +75,7 @@ dependencies = [
"cxx",
"cxx-gen",
"libc",
"num-traits",
"thiserror",
]

View File

@ -16,3 +16,4 @@ cfg-if = { workspace = true }
thiserror = { workspace = true }
argh = { workspace = true }
bytemuck = { workspace = true }
num-traits = { workspace = true }

View File

@ -7,6 +7,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/xattr.h>
extern "C" {
@ -90,6 +91,16 @@ int mkfifo(const char *path, mode_t mode) {
return mknod(path, (mode & ~S_IFMT) | S_IFIFO, 0);
}
[[gnu::weak]]
int fsetxattr(int fd, const char *name, const void *value, size_t size, int flags) {
return syscall(__NR_fsetxattr, fd, name, value, size, flags);
}
[[gnu::weak]]
int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) {
return syscall(__NR_lsetxattr, path, name, value, size, flags);
}
#define SPLIT_64(v) (unsigned)((v) & 0xFFFFFFFF), (unsigned)((v) >> 32)
#if defined(__arm__)

View File

@ -3,12 +3,15 @@
use std::io;
use std::os::fd::{BorrowedFd, OwnedFd, RawFd};
use cfg_if::cfg_if;
use cxx::private::c_char;
use libc::mode_t;
use crate::logging::CxxResultExt;
pub(crate) use crate::xwrap::*;
use crate::{fd_path, map_fd, map_file, Directory, FsPath, Utf8CStr, Utf8CStrSlice};
use crate::{
clone_attr, fclone_attr, fd_path, map_fd, map_file, Directory, FsPath, Utf8CStr, Utf8CStrSlice,
};
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
let mut buf = Utf8CStrSlice::from(buf);
@ -69,3 +72,102 @@ pub(crate) fn map_fd_for_cxx(fd: RawFd, sz: usize, rw: bool) -> &'static mut [u8
.unwrap_or(&mut [])
}
}
pub(crate) unsafe fn readlinkat_for_cxx(
dirfd: RawFd,
path: *const c_char,
buf: *mut u8,
bufsz: usize,
) -> isize {
// readlinkat() may fail on x86 platform, returning random value
// instead of number of bytes placed in buf (length of link)
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
libc::memset(buf.cast(), 0, bufsz);
let mut r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
if r > 0 {
r = libc::strlen(buf.cast()) as isize;
}
} else {
let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
if r >= 0 {
*buf.offset(r) = b'\0';
}
}
}
r
}
#[export_name = "cp_afc"]
unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
if let Ok(src) = Utf8CStr::from_ptr(src) {
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
let src = FsPath::from(src);
let dest = FsPath::from(dest);
return src
.copy_to(dest)
.log_cxx_with_msg(|w| {
w.write_fmt(format_args!("cp_afc {} -> {} failed", src, dest))
})
.is_ok();
}
}
false
}
#[export_name = "mv_path"]
unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
if let Ok(src) = Utf8CStr::from_ptr(src) {
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
let src = FsPath::from(src);
let dest = FsPath::from(dest);
return src
.move_to(dest)
.log_cxx_with_msg(|w| {
w.write_fmt(format_args!("mv_path {} -> {} failed", src, dest))
})
.is_ok();
}
}
false
}
#[export_name = "link_path"]
unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
if let Ok(src) = Utf8CStr::from_ptr(src) {
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
let src = FsPath::from(src);
let dest = FsPath::from(dest);
return src
.link_to(dest)
.log_cxx_with_msg(|w| {
w.write_fmt(format_args!("link_path {} -> {} failed", src, dest))
})
.is_ok();
}
}
false
}
#[export_name = "clone_attr"]
unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
if let Ok(src) = Utf8CStr::from_ptr(src) {
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
let src = FsPath::from(src);
let dest = FsPath::from(dest);
return clone_attr(src, dest)
.log_cxx_with_msg(|w| {
w.write_fmt(format_args!("clone_attr {} -> {} failed", src, dest))
})
.is_ok();
}
}
false
}
#[export_name = "fclone_attr"]
unsafe extern "C" fn fclone_attr_for_cxx(a: RawFd, b: RawFd) -> bool {
fclone_attr(a, b)
.log_cxx_with_msg(|w| w.write_str("fclone_attr failed"))
.is_ok()
}

View File

@ -20,191 +20,6 @@ int fd_pathat(int dirfd, const char *name, char *path, size_t size) {
return 0;
}
void mv_path(const char *src, const char *dest) {
file_attr attr;
getattr(src, &attr);
if (S_ISDIR(attr.st.st_mode)) {
if (access(dest, F_OK) != 0) {
xmkdirs(dest, 0);
setattr(dest, &attr);
}
mv_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
} else{
xrename(src, dest);
}
rmdir(src);
}
void mv_dir(int src, int dest) {
auto dir = xopen_dir(src);
run_finally f([=]{ close(dest); });
for (dirent *entry; (entry = xreaddir(dir.get()));) {
switch (entry->d_type) {
case DT_DIR:
if (xfaccessat(dest, entry->d_name, F_OK, 0) == 0) {
// Destination folder exists, needs recursive move
int newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
int newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
mv_dir(newsrc, newdest);
unlinkat(src, entry->d_name, AT_REMOVEDIR);
break;
}
// Else fall through
case DT_LNK:
case DT_REG:
renameat(src, entry->d_name, dest, entry->d_name);
break;
}
}
}
void cp_afc(const char *src, const char *dest) {
file_attr a;
getattr(src, &a);
if (S_ISDIR(a.st.st_mode)) {
xmkdirs(dest, 0);
clone_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
} else{
unlink(dest);
if (S_ISREG(a.st.st_mode)) {
int sfd = xopen(src, O_RDONLY | O_CLOEXEC);
int dfd = xopen(dest, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
xsendfile(dfd, sfd, nullptr, a.st.st_size);
close(sfd);
close(dfd);
} else if (S_ISLNK(a.st.st_mode)) {
char buf[4096];
xreadlink(src, buf, sizeof(buf));
xsymlink(buf, dest);
}
}
setattr(dest, &a);
}
void clone_dir(int src, int dest) {
auto dir = xopen_dir(src);
run_finally f([&]{ close(dest); });
for (dirent *entry; (entry = xreaddir(dir.get()));) {
file_attr a;
getattrat(src, entry->d_name, &a);
switch (entry->d_type) {
case DT_DIR: {
xmkdirat(dest, entry->d_name, 0);
setattrat(dest, entry->d_name, &a);
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
int dst = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
clone_dir(sfd, dst);
break;
}
case DT_REG: {
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
int dfd = xopenat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
xsendfile(dfd, sfd, nullptr, a.st.st_size);
fsetattr(dfd, &a);
close(dfd);
close(sfd);
break;
}
case DT_LNK: {
char buf[4096];
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
xsymlinkat(buf, dest, entry->d_name);
setattrat(dest, entry->d_name, &a);
break;
}
}
}
}
void link_path(const char *src, const char *dest) {
link_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
}
void link_dir(int src, int dest) {
auto dir = xopen_dir(src);
run_finally f([&]{ close(dest); });
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) {
file_attr a;
getattrat(src, entry->d_name, &a);
xmkdirat(dest, entry->d_name, 0);
setattrat(dest, entry->d_name, &a);
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
int dfd = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
link_dir(sfd, dfd);
} else {
xlinkat(src, entry->d_name, dest, entry->d_name, 0);
}
}
}
int getattr(const char *path, file_attr *a) {
if (xlstat(path, &a->st) == -1)
return -1;
char *con;
if (lgetfilecon(path, &con) == -1)
return -1;
strcpy(a->con, con);
freecon(con);
return 0;
}
int getattrat(int dirfd, const char *name, file_attr *a) {
char path[4096];
fd_pathat(dirfd, name, path, sizeof(path));
return getattr(path, a);
}
int fgetattr(int fd, file_attr *a) {
if (xfstat(fd, &a->st) < 0)
return -1;
char *con;
if (fgetfilecon(fd, &con) < 0)
return -1;
strcpy(a->con, con);
freecon(con);
return 0;
}
int setattr(const char *path, file_attr *a) {
if (chmod(path, a->st.st_mode & 0777) < 0)
return -1;
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
return -1;
if (a->con[0] && lsetfilecon(path, a->con) < 0)
return -1;
return 0;
}
int setattrat(int dirfd, const char *name, file_attr *a) {
char path[4096];
fd_pathat(dirfd, name, path, sizeof(path));
return setattr(path, a);
}
int fsetattr(int fd, file_attr *a) {
if (fchmod(fd, a->st.st_mode & 0777) < 0)
return -1;
if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0)
return -1;
if (a->con[0] && fsetfilecon(fd, a->con) < 0)
return -1;
return 0;
}
void clone_attr(const char *src, const char *dest) {
file_attr a;
getattr(src, &a);
setattr(dest, &a);
}
void fclone_attr(int src, int dest) {
file_attr a;
fgetattr(src, &a);
fsetattr(dest, &a);
}
void full_read(int fd, string &str) {
char buf[4096];
for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;)

View File

@ -20,11 +20,6 @@ static inline T align_padding(T v, int a) {
return align_to(v, a) - v;
}
struct file_attr {
struct stat st;
char con[128];
};
struct mount_info {
unsigned int id;
unsigned int parent;
@ -58,27 +53,19 @@ 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);
void cp_afc(const char *src, const char *dest);
void mv_path(const char *src, const char *dest);
void link_path(const char *src, const char *dest);
void clone_attr(const char *src, const char *dest);
void fclone_attr(int src, int dest);
} // extern "C"
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
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);
void link_path(const char *src, const char *dest);
void link_dir(int src, int dest);
static inline ssize_t realpath(
const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz) {
return canonical_path(path, buf, bufsiz);
}
int getattr(const char *path, file_attr *a);
int getattrat(int dirfd, const char *name, file_attr *a);
int fgetattr(int fd, file_attr *a);
int setattr(const char *path, file_attr *a);
int setattrat(int dirfd, const char *name, file_attr *a);
int fsetattr(int fd, file_attr *a);
void fclone_attr(int src, int dest);
void clone_attr(const char *src, const char *dest);
void full_read(int fd, std::string &str);
void full_read(const char *filename, std::string &str);
std::string full_read(int fd);
@ -90,7 +77,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 clone_dir(int src, int dest);
std::vector<mount_info> parse_mount_info(const char *pid);
std::string resolve_preinit_dir(const char *base_dir);

View File

@ -8,11 +8,17 @@ use std::os::android::fs::MetadataExt;
use std::os::fd::{AsFd, BorrowedFd, IntoRawFd};
use std::os::unix::fs::FileTypeExt;
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::sync::atomic::{AtomicBool, Ordering};
use std::{io, mem, ptr, slice};
use bytemuck::{bytes_of_mut, Pod};
use libc::{c_uint, dirent, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_PATH, O_RDONLY, O_RDWR};
use libc::{
c_uint, dirent, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY, O_RDWR,
O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFREG,
};
use num_traits::AsPrimitive;
use crate::cxx_extern::readlinkat_for_cxx;
use crate::{
copy_cstr, cstr, errno, error, FsPath, FsPathBuf, LibcReturn, Utf8CStr, Utf8CStrArr,
Utf8CStrBuf,
@ -132,6 +138,18 @@ impl<T: Write> WriteExt for T {
}
}
pub struct FileAttr {
pub st: libc::stat,
pub con: Utf8CStrArr<128>,
}
const XATTR_NAME_SELINUX: &[u8] = b"security.selinux\0";
static SELINUX_ENABLED: AtomicBool = AtomicBool::new(false);
pub fn enable_selinux() {
SELINUX_ENABLED.store(true, Ordering::Relaxed);
}
pub struct DirEntry<'a> {
dir: &'a Directory,
entry: &'a dirent,
@ -175,34 +193,49 @@ impl DirEntry<'_> {
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> {
self.dir.open_fd(self.d_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 {
let fd = libc::openat(
self.dir.as_raw_fd(),
self.d_name.as_ptr(),
O_RDONLY | O_CLOEXEC,
)
.check_os_err()?;
Directory::try_from(OwnedFd::from_raw_fd(fd))
}
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 {
let fd = libc::openat(
self.dir.as_raw_fd(),
self.d_name.as_ptr(),
flags | O_CLOEXEC,
)
.check_os_err()?;
Ok(File::from_raw_fd(fd))
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
}
pub fn get_attr(&self) -> io::Result<FileAttr> {
let mut path = Utf8CStrArr::default();
self.path(&mut path)?;
FsPath::from(path).get_attr()
}
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
let mut path = Utf8CStrArr::default();
self.path(&mut path)?;
FsPath::from(path).set_attr(attr)
}
}
@ -261,6 +294,25 @@ impl Directory {
unsafe { libc::rewinddir(self.dirp) }
}
unsafe fn open_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
}
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)
}
@ -286,6 +338,108 @@ impl Directory {
})?;
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_fd(
e.d_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_lnk() {
let mut path = Utf8CStrArr::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.d_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 {
@ -410,6 +564,19 @@ impl FsPath {
Ok(())
}
pub fn mkdir(&self, mode: mode_t) -> io::Result<()> {
unsafe {
if libc::mkdir(self.as_ptr(), mode) < 0 {
if *errno() == EEXIST {
libc::chmod(self.as_ptr(), mode).as_os_err()?;
} else {
return Err(io::Error::last_os_error());
}
}
}
Ok(())
}
pub fn mkdirs(&self, mode: mode_t) -> io::Result<()> {
let mut buf = [0_u8; 4096];
let len = copy_cstr(&mut buf, self);
@ -456,6 +623,151 @@ impl FsPath {
}
Ok(())
}
pub fn get_attr(&self) -> io::Result<FileAttr> {
let mut attr: FileAttr;
unsafe {
attr = FileAttr {
st: mem::zeroed(),
con: Utf8CStrArr::new(),
};
libc::lstat(self.as_ptr(), &mut attr.st).as_os_err()?;
if SELINUX_ENABLED.load(Ordering::Relaxed) {
let sz = libc::lgetxattr(
self.as_ptr(),
XATTR_NAME_SELINUX.as_ptr().cast(),
attr.con.as_mut_ptr().cast(),
attr.con.capacity(),
)
.check_os_err()?;
attr.con.set_len((sz - 1) as usize);
}
}
Ok(attr)
}
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
unsafe {
if (attr.st.st_mode & libc::S_IFMT as c_uint) != S_IFLNK.as_() {
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
}
libc::lchown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_os_err()?;
if !attr.con.is_empty() {
libc::lsetxattr(
self.as_ptr(),
XATTR_NAME_SELINUX.as_ptr().cast(),
attr.con.as_ptr().cast(),
attr.con.len() + 1,
0,
)
.as_os_err()?;
}
}
Ok(())
}
pub fn copy_to(&self, path: &FsPath) -> io::Result<()> {
let attr = self.get_attr()?;
if (attr.st.st_mode & libc::S_IFMT as c_uint) == S_IFDIR.as_() {
path.mkdir(0o777)?;
let mut src = Directory::open(self)?;
let dest = Directory::open(path)?;
src.copy_into(&dest)?;
} else {
// It's OK if remove failed
path.remove().ok();
if (attr.st.st_mode & libc::S_IFMT as c_uint) == S_IFREG.as_() {
let mut src = self.open(O_RDONLY | O_CLOEXEC)?;
let mut dest = path.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o777)?;
std::io::copy(&mut src, &mut dest)?;
} else if (attr.st.st_mode & libc::S_IFMT as c_uint) == S_IFLNK.as_() {
let mut buf = Utf8CStrArr::default();
self.read_link(&mut buf)?;
unsafe {
libc::symlink(buf.as_ptr(), path.as_ptr()).as_os_err()?;
}
}
}
path.set_attr(&attr)?;
Ok(())
}
pub fn move_to(&self, path: &FsPath) -> io::Result<()> {
if path.exists() {
let attr = path.get_attr()?;
if (attr.st.st_mode & libc::S_IFMT as c_uint) == S_IFDIR.as_() {
let mut src = Directory::open(self)?;
let dest = Directory::open(path)?;
return src.move_into(&dest);
} else {
path.remove()?;
}
}
self.rename_to(path)
}
pub fn link_to(&self, path: &FsPath) -> io::Result<()> {
let attr = self.get_attr()?;
if (attr.st.st_mode & libc::S_IFMT as c_uint) == S_IFDIR.as_() {
path.mkdir(0o777)?;
path.set_attr(&attr)?;
let mut src = Directory::open(self)?;
let dest = Directory::open(path)?;
src.link_into(&dest)
} else {
unsafe { libc::link(self.as_ptr(), path.as_ptr()).as_os_err() }
}
}
}
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
let mut attr: FileAttr;
unsafe {
attr = FileAttr {
st: mem::zeroed(),
con: Utf8CStrArr::new(),
};
libc::fstat(fd, &mut attr.st).as_os_err()?;
if SELINUX_ENABLED.load(Ordering::Relaxed) {
let sz = libc::fgetxattr(
fd,
XATTR_NAME_SELINUX.as_ptr().cast(),
attr.con.as_mut_ptr().cast(),
attr.con.capacity(),
)
.check_os_err()?;
attr.con.set_len((sz - 1) as usize);
}
}
Ok(attr)
}
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> io::Result<()> {
unsafe {
libc::fchmod(fd, (attr.st.st_mode & 0o777).as_()).as_os_err()?;
libc::fchown(fd, attr.st.st_uid, attr.st.st_gid).as_os_err()?;
if !attr.con.is_empty() {
libc::fsetxattr(
fd,
XATTR_NAME_SELINUX.as_ptr().cast(),
attr.con.as_ptr().cast(),
attr.con.len() + 1,
0,
)
.as_os_err()?;
}
}
Ok(())
}
pub fn clone_attr(a: &FsPath, b: &FsPath) -> io::Result<()> {
let attr = a.get_attr()?;
b.set_attr(&attr)
}
pub fn fclone_attr(a: RawFd, b: RawFd) -> io::Result<()> {
let attr = fd_get_attr(a)?;
fd_set_attr(b, &attr)
}
pub struct MappedFile(&'static mut [u8]);
@ -494,13 +806,13 @@ impl Drop for MappedFile {
}
}
// 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]> {
extern "C" {
// Don't use the declaration from the libc crate as request should be u32 not i32
fn ioctl(fd: RawFd, request: u32, ...) -> i32;
}
// 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]> {
#[cfg(target_pointer_width = "64")]
const BLKGETSIZE64: u32 = 0x80081272;

View File

@ -51,5 +51,6 @@ pub mod ffi {
fn map_file_for_cxx(path: &[u8], rw: bool) -> &'static mut [u8];
#[cxx_name = "map_fd"]
fn map_fd_for_cxx(fd: i32, sz: usize, rw: bool) -> &'static mut [u8];
fn enable_selinux();
}
}

View File

@ -103,6 +103,7 @@ void setfilecon_at(int dirfd, const char *name, const char *con) {
}
void enable_selinux() {
rust::enable_selinux();
setcon = __setcon;
getfilecon = __getfilecon;
lgetfilecon = __lgetfilecon;

View File

@ -4,12 +4,12 @@ use std::ffi::CStr;
use std::os::unix::io::RawFd;
use std::ptr;
use cfg_if::cfg_if;
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::cxx_extern::readlinkat_for_cxx;
use crate::{cstr, errno, raw_cstr, CxxResultExt, FsPath, Utf8CStr, Utf8CStrSlice};
fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str {
@ -96,24 +96,10 @@ unsafe extern "C" fn xreadlinkat(
buf: *mut u8,
bufsz: usize,
) -> isize {
// readlinkat() may fail on x86 platform, returning random value
// instead of number of bytes placed in buf (length of link)
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
libc::memset(buf.cast(), 0, bufsz);
let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
let r = readlinkat_for_cxx(dirfd, path, buf, bufsz);
if r < 0 {
perror!("readlinkat {}", ptr_to_str(path))
}
} else {
let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
if r < 0 {
perror!("readlinkat {}", ptr_to_str(path))
} else {
*buf.offset(r) = b'\0';
}
}
}
r
}

View File

@ -23,9 +23,6 @@ pub mod ffi {
type PropCb;
unsafe fn get_prop_rs(name: *const c_char, persist: bool) -> String;
unsafe fn prop_cb_exec(cb: Pin<&mut PropCb>, name: *const c_char, value: *const c_char);
include!("../base/files.hpp");
unsafe fn clone_attr(src: *const c_char, dst: *const c_char);
}
extern "Rust" {

View File

@ -164,11 +164,11 @@ void module_node::mount() {
void tmpfs_node::mount() {
string src = mirror_path();
const string &dest = node_path();
file_attr a{};
const char *src_path;
if (access(src.data(), F_OK) == 0)
getattr(src.data(), &a);
src_path = src.data();
else
getattr(parent()->node_path().data(), &a);
src_path = parent()->node_path().data();
if (!isa<tmpfs_node>(parent())) {
auto worker_dir = MAGISKTMP + "/" WORKERDIR + dest;
mkdirs(worker_dir.data(), 0);
@ -177,7 +177,7 @@ void tmpfs_node::mount() {
// We don't need another layer of tmpfs if parent is tmpfs
mkdir(dest.data(), 0);
}
setattr(dest.data(), &a);
clone_attr(src_path, dest.data());
dir_node::mount();
}

View File

@ -12,11 +12,11 @@ use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
use base::libc::{O_CLOEXEC, O_RDONLY};
use base::{
cstr, debug, libc::mkstemp, raw_cstr, Directory, FsPath, FsPathBuf, LibcReturn, LoggedError,
clone_attr, cstr, debug, libc::mkstemp, Directory, FsPath, FsPathBuf, LibcReturn, LoggedError,
LoggedResult, MappedFile, Utf8CStr, Utf8CStrArr, WalkResult,
};
use crate::ffi::{clone_attr, prop_cb_exec, PropCb};
use crate::ffi::{prop_cb_exec, PropCb};
use crate::resetprop::proto::persistent_properties::{
mod_PersistentProperties::PersistentPropertyRecord, PersistentProperties,
};
@ -140,7 +140,7 @@ fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> {
debug!("resetprop: encode with protobuf [{}]", tmp);
props.write_message(&mut Writer::new(BufWriter::new(f)))?;
}
unsafe { clone_attr(raw_cstr!(PERSIST_PROP!()), tmp.as_ptr()) };
clone_attr(FsPath::from(cstr!(PERSIST_PROP!())), &tmp)?;
tmp.rename_to(cstr!(PERSIST_PROP!()))?;
Ok(())
}