Make FsPath a trait

This commit is contained in:
topjohnwu
2025-04-21 18:00:43 -07:00
committed by John Wu
parent c34c7838bb
commit f3fef7bfe4
21 changed files with 191 additions and 229 deletions

View File

@@ -11,8 +11,7 @@ use std::str::Utf8Error;
use std::{fmt, mem, slice, str};
use thiserror::Error;
use crate::slice_from_ptr_mut;
use crate::{FsPath, FsPathMnt, slice_from_ptr_mut};
// Utf8CStr types are UTF-8 validated and null terminated strings.
//
// Several Utf8CStr types:
@@ -415,32 +414,6 @@ const_assert_eq!(align_of::<&Utf8CStr>(), align_of::<[usize; 2]>());
// File system path extensions types
#[repr(transparent)]
pub struct FsPath(Utf8CStr);
impl FsPath {
#[inline(always)]
pub fn from<T: AsRef<Utf8CStr> + ?Sized>(value: &T) -> &FsPath {
unsafe { mem::transmute(value.as_ref()) }
}
#[inline(always)]
pub const fn __from_utfcstr(value: &Utf8CStr) -> &FsPath {
unsafe { mem::transmute(value) }
}
pub fn follow_link(&self) -> &FsPathFollow {
unsafe { mem::transmute(self) }
}
}
impl AsUtf8CStr for FsPath {
#[inline(always)]
fn as_utf8_cstr(&self) -> &Utf8CStr {
&self.0
}
}
#[repr(transparent)]
pub struct FsPathFollow(Utf8CStr);
@@ -502,11 +475,10 @@ impl<const N: usize> FsPathBuf<'_, N> {
}
}
impl<const N: usize> Deref for FsPathBuf<'_, N> {
type Target = FsPath;
fn deref(&self) -> &FsPath {
FsPath::from(self.0.deref())
impl<const N: usize> AsUtf8CStr for FsPathBuf<'_, N> {
#[inline(always)]
fn as_utf8_cstr(&self) -> &Utf8CStr {
&self.0
}
}
@@ -525,11 +497,11 @@ macro_rules! impl_cstr_deref {
}
impl_cstr_deref!(
(FsPath,)
(FsPathFollow,)
(Utf8CStrBufRef<'_>,)
(Utf8CStrBufArr<N>, const N: usize)
(Utf8CString,)
(FsPathFollow,)
(FsPathBuf<'_, N>, const N: usize)
);
// impl<T: Deref<Target = Utf8CStr>> BoilerPlate for T { ... }
@@ -612,12 +584,11 @@ macro_rules! impl_cstr_misc {
impl_cstr_misc!(
(Utf8CStr,)
(FsPath,)
(FsPathFollow,)
(FsPathBuf<'_, N>, const N: usize)
(Utf8CStrBufRef<'_>,)
(Utf8CStrBufArr<N>, const N: usize)
(Utf8CString,)
(FsPathFollow,)
(FsPathBuf<'_, N>, const N: usize)
);
fn copy_cstr_truncate(dest: &mut [u8], src: &[u8]) -> usize {
@@ -703,6 +674,23 @@ impl_cstr_buf_write!(
(Utf8CString,)
);
// impl<T: Deref<Target = Utf8CStr>> FsPath for T {}
// impl<T: Deref<Target = Utf8CStr>> FsPathMnt for T {}
macro_rules! impl_fs_path {
($( ($t:ty, $($g:tt)*) )*) => {$(
impl<$($g)*> FsPath for $t {}
impl<$($g)*> FsPathMnt for $t {}
)*}
}
impl_fs_path!(
(&Utf8CStr,)
(Utf8CStrBufRef<'_>,)
(Utf8CStrBufArr<N>, const N: usize)
(Utf8CString,)
(FsPathBuf<'_, N>, const N: usize)
);
#[macro_export]
macro_rules! cstr {
($str:expr) => {{
@@ -718,8 +706,3 @@ macro_rules! cstr {
macro_rules! raw_cstr {
($str:expr) => {{ $crate::cstr!($str).as_ptr() }};
}
#[macro_export]
macro_rules! path {
($str:expr) => {{ $crate::FsPath::__from_utfcstr($crate::cstr!($str)) }};
}

View File

@@ -23,11 +23,11 @@ pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
unsafe {
match Utf8CStr::from_ptr(path) {
Ok(p) => {
Ok(path) => {
let mut buf = cstr_buf::wrap_ptr(buf, bufsz);
FsPath::from(p)
.realpath(&mut buf)
.map_or(-1, |_| buf.len() as isize)
path.realpath(&mut buf)
.log_cxx()
.map_or(-1_isize, |_| buf.len() as isize)
}
Err(_) => -1,
}
@@ -38,7 +38,7 @@ unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: us
unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
unsafe {
match Utf8CStr::from_ptr(path) {
Ok(p) => FsPath::from(p).mkdirs(mode).map_or(-1, |_| 0),
Ok(path) => path.mkdirs(mode).map_or(-1, |_| 0),
Err(_) => -1,
}
}
@@ -48,7 +48,7 @@ unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
unsafe {
match Utf8CStr::from_ptr(path) {
Ok(p) => FsPath::from(p).remove_all().is_ok(),
Ok(path) => path.remove_all().is_ok(),
Err(_) => false,
}
}
@@ -114,8 +114,6 @@ unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) ->
unsafe {
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().is_ok();
}
}
@@ -128,8 +126,6 @@ unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) ->
unsafe {
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().is_ok();
}
}
@@ -142,8 +138,6 @@ unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char)
unsafe {
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().is_ok();
}
}
@@ -156,8 +150,6 @@ unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char)
unsafe {
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().is_ok();
}
}

View File

@@ -1,7 +1,7 @@
use crate::cxx_extern::readlinkat;
use crate::{
FileAttr, FsPathBuf, LibcReturn, OsError, OsResult, OsResultStatic, Utf8CStr, Utf8CStrBuf,
cstr_buf, errno, fd_path, fd_set_attr,
FileAttr, FsPath, FsPathBuf, LibcReturn, OsError, OsResult, OsResultStatic, Utf8CStr,
Utf8CStrBuf, cstr_buf, errno, fd_path, fd_set_attr,
};
use libc::{EEXIST, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, dirent, mode_t};
use std::fs::File;

View File

@@ -1,6 +1,6 @@
use crate::{
Directory, FsPath, FsPathBuf, FsPathFollow, LibcReturn, OsError, OsResult, OsResultStatic,
Utf8CStr, Utf8CStrBuf, cstr_buf, errno, error,
Directory, FsPathBuf, FsPathFollow, LibcReturn, OsError, OsResult, OsResultStatic, Utf8CStr,
Utf8CStrBuf, cstr_buf, errno, error,
};
use bytemuck::{Pod, bytes_of, bytes_of_mut};
use libc::{
@@ -13,6 +13,7 @@ use std::cmp::min;
use std::ffi::CStr;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
use std::ops::Deref;
use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
@@ -193,23 +194,27 @@ impl FileAttr {
const XATTR_NAME_SELINUX: &CStr = c"security.selinux";
impl FsPath {
pub fn open(&self, flags: i32) -> OsResult<File> {
pub trait FsPath: Deref<Target = Utf8CStr> {
fn follow_link(&self) -> &FsPathFollow {
unsafe { mem::transmute(self.deref()) }
}
fn open(&self, flags: i32) -> OsResult<File> {
Ok(File::from(open_fd!(self, flags)?))
}
pub fn create(&self, flags: i32, mode: mode_t) -> OsResult<File> {
fn create(&self, flags: i32, mode: mode_t) -> OsResult<File> {
Ok(File::from(open_fd!(self, O_CREAT | flags, mode)?))
}
pub fn exists(&self) -> bool {
fn exists(&self) -> bool {
unsafe {
let mut st: stat = mem::zeroed();
libc::lstat(self.as_ptr(), &mut st) == 0
}
}
pub fn rename_to<'a>(&'a self, name: &'a FsPath) -> OsResult<'a, ()> {
fn rename_to<'a>(&'a self, name: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe {
libc::rename(self.as_ptr(), name.as_ptr()).check_os_err(
"rename",
@@ -219,11 +224,11 @@ impl FsPath {
}
}
pub fn remove(&self) -> OsResult<()> {
fn remove(&self) -> OsResult<()> {
unsafe { libc::remove(self.as_ptr()).check_os_err("remove", Some(self), None) }
}
pub fn remove_all(&self) -> OsResultStatic<()> {
fn remove_all(&self) -> OsResultStatic<()> {
let attr = self.get_attr()?;
if attr.is_dir() {
let mut dir = Directory::try_from(open_fd!(self, O_RDONLY | O_CLOEXEC)?)?;
@@ -233,7 +238,7 @@ impl FsPath {
}
#[allow(clippy::unnecessary_cast)]
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
buf.clear();
unsafe {
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
@@ -244,7 +249,7 @@ impl FsPath {
Ok(())
}
pub fn mkdir(&self, mode: mode_t) -> OsResult<()> {
fn mkdir(&self, mode: mode_t) -> OsResult<()> {
unsafe {
if libc::mkdir(self.as_ptr(), mode) < 0 {
if *errno() == EEXIST {
@@ -257,7 +262,7 @@ impl FsPath {
Ok(())
}
pub fn mkdirs(&self, mode: mode_t) -> OsResultStatic<()> {
fn mkdirs(&self, mode: mode_t) -> OsResultStatic<()> {
if self.is_empty() {
return Ok(());
}
@@ -282,7 +287,7 @@ impl FsPath {
}
// Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp
pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
let fd = open_fd!(self, O_PATH | O_CLOEXEC)?;
let mut st1: libc::stat;
let mut st2: libc::stat;
@@ -306,7 +311,7 @@ impl FsPath {
Ok(())
}
pub fn get_attr(&self) -> OsResult<FileAttr> {
fn get_attr(&self) -> OsResult<FileAttr> {
let mut attr = FileAttr::new();
unsafe {
libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?;
@@ -317,7 +322,7 @@ impl FsPath {
Ok(attr)
}
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
unsafe {
if !attr.is_symlink() {
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).check_os_err(
@@ -340,7 +345,7 @@ impl FsPath {
Ok(())
}
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
unsafe {
let sz = libc::lgetxattr(
self.as_ptr(),
@@ -360,7 +365,7 @@ impl FsPath {
Ok(())
}
pub fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe {
libc::lsetxattr(
self.as_ptr(),
@@ -373,7 +378,7 @@ impl FsPath {
}
}
pub fn copy_to(&self, path: &FsPath) -> OsResultStatic<()> {
fn copy_to(&self, path: &Utf8CStr) -> OsResultStatic<()> {
let attr = self.get_attr()?;
if attr.is_dir() {
path.mkdir(0o777)?;
@@ -403,7 +408,7 @@ impl FsPath {
Ok(())
}
pub fn move_to(&self, path: &FsPath) -> OsResultStatic<()> {
fn move_to(&self, path: &Utf8CStr) -> OsResultStatic<()> {
if path.exists() {
let attr = path.get_attr()?;
if attr.is_dir() {
@@ -418,7 +423,7 @@ impl FsPath {
Ok(())
}
pub fn parent(&self, buf: &mut dyn Utf8CStrBuf) -> bool {
fn parent(&self, buf: &mut dyn Utf8CStrBuf) -> bool {
buf.clear();
if let Some(parent) = Path::new(self.as_str()).parent() {
let bytes = parent.as_os_str().as_bytes();
@@ -432,7 +437,7 @@ impl FsPath {
}
// ln self path
pub fn link_to(&self, path: &FsPath) -> OsResultStatic<()> {
fn link_to(&self, path: &Utf8CStr) -> OsResultStatic<()> {
let attr = self.get_attr()?;
if attr.is_dir() {
path.mkdir(0o777)?;
@@ -453,7 +458,7 @@ impl FsPath {
}
// ln -s target self
pub fn create_symlink_to<'a>(&'a self, target: &'a FsPath) -> OsResult<'a, ()> {
fn create_symlink_to<'a>(&'a self, target: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe {
libc::symlink(target.as_ptr(), self.as_ptr()).check_os_err(
"symlink",
@@ -463,17 +468,21 @@ impl FsPath {
}
}
pub fn mkfifo(&self, mode: mode_t) -> OsResult<()> {
fn mkfifo(&self, mode: mode_t) -> OsResult<()> {
unsafe { libc::mkfifo(self.as_ptr(), mode).check_os_err("mkfifo", Some(self), None) }
}
}
impl FsPathFollow {
pub fn exists(&self) -> bool {
impl FsPath for FsPathFollow {
fn follow_link(&self) -> &FsPathFollow {
self
}
fn exists(&self) -> bool {
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
}
pub fn get_attr(&self) -> OsResult<FileAttr> {
fn get_attr(&self) -> OsResult<FileAttr> {
let mut attr = FileAttr::new();
unsafe {
libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?;
@@ -484,7 +493,7 @@ impl FsPathFollow {
Ok(attr)
}
pub fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
fn set_attr<'a>(&'a self, attr: &'a FileAttr) -> OsResult<'a, ()> {
unsafe {
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).check_os_err(
"chmod",
@@ -505,7 +514,7 @@ impl FsPathFollow {
Ok(())
}
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
unsafe {
let sz = libc::getxattr(
self.as_ptr(),
@@ -525,7 +534,7 @@ impl FsPathFollow {
Ok(())
}
pub fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
fn set_secontext<'a>(&'a self, con: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe {
libc::setxattr(
self.as_ptr(),
@@ -595,8 +604,8 @@ pub fn fd_set_secontext(fd: RawFd, con: &Utf8CStr) -> OsResult<()> {
}
}
pub fn clone_attr<'a>(a: &'a FsPath, b: &'a FsPath) -> OsResult<'a, ()> {
let attr = a.get_attr()?;
pub fn clone_attr<'a>(a: &'a Utf8CStr, b: &'a Utf8CStr) -> OsResult<'a, ()> {
let attr = a.get_attr().map_err(|e| e.set_args(Some(a), None))?;
b.set_attr(&attr).map_err(|e| e.set_args(Some(b), None))
}

View File

@@ -13,6 +13,7 @@ pub use ffi::fork_dont_care;
pub use files::*;
pub use logging::*;
pub use misc::*;
pub use mount::*;
pub use result::*;
mod cstr;

View File

@@ -1,9 +1,10 @@
use crate::{FsPath, LibcReturn, OsResult, Utf8CStr};
use crate::{LibcReturn, OsResult, Utf8CStr};
use libc::c_ulong;
use std::ops::Deref;
use std::ptr;
impl FsPath {
pub fn bind_mount_to<'a>(&'a self, path: &'a FsPath) -> OsResult<'a, ()> {
pub trait FsPathMnt: Deref<Target = Utf8CStr> {
fn bind_mount_to<'a>(&'a self, path: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe {
libc::mount(
self.as_ptr(),
@@ -16,7 +17,7 @@ impl FsPath {
}
}
pub fn remount_with_flags(&self, flags: c_ulong) -> OsResult<()> {
fn remount_with_flags(&self, flags: c_ulong) -> OsResult<()> {
unsafe {
libc::mount(
ptr::null(),
@@ -29,7 +30,7 @@ impl FsPath {
}
}
pub fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<()> {
fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<()> {
unsafe {
libc::mount(
ptr::null(),
@@ -42,7 +43,7 @@ impl FsPath {
}
}
pub fn move_mount_to<'a>(&'a self, path: &'a FsPath) -> OsResult<'a, ()> {
fn move_mount_to<'a>(&'a self, path: &'a Utf8CStr) -> OsResult<'a, ()> {
unsafe {
libc::mount(
self.as_ptr(),
@@ -55,13 +56,13 @@ impl FsPath {
}
}
pub fn unmount(&self) -> OsResult<()> {
fn unmount(&self) -> OsResult<()> {
unsafe {
libc::umount2(self.as_ptr(), libc::MNT_DETACH).check_os_err("unmount", Some(self), None)
}
}
pub fn set_mount_private(&self, recursive: bool) -> OsResult<()> {
fn set_mount_private(&self, recursive: bool) -> OsResult<()> {
let flag = if recursive { libc::MS_REC } else { 0 };
unsafe {
libc::mount(

View File

@@ -29,10 +29,9 @@ fn ptr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
unsafe {
match Utf8CStr::from_ptr(path) {
Ok(p) => {
Ok(path) => {
let mut buf = cstr_buf::wrap_ptr(buf, bufsz);
FsPath::from(p)
.realpath(&mut buf)
path.realpath(&mut buf)
.log_cxx()
.map_or(-1, |_| buf.len() as isize)
}
@@ -45,10 +44,9 @@ unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize)
unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
unsafe {
match Utf8CStr::from_ptr(path) {
Ok(p) => {
Ok(path) => {
let mut buf = cstr_buf::wrap_ptr(buf, bufsz);
FsPath::from(p)
.read_link(&mut buf)
path.read_link(&mut buf)
.log_cxx()
.map_or(-1, |_| buf.len() as isize)
}
@@ -343,7 +341,7 @@ unsafe extern "C" fn xrename(oldname: *const c_char, newname: *const c_char) ->
unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
unsafe {
match Utf8CStr::from_ptr(path) {
Ok(p) => FsPath::from(p).mkdir(mode).log_cxx().map_or(-1, |_| 0),
Ok(path) => path.mkdir(mode).log_cxx().map_or(-1, |_| 0),
Err(_) => -1,
}
}
@@ -353,7 +351,7 @@ unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
unsafe {
match Utf8CStr::from_ptr(path) {
Ok(p) => FsPath::from(p).mkdirs(mode).log_cxx().map_or(-1, |_| 0),
Ok(path) => path.mkdirs(mode).log_cxx().map_or(-1, |_| 0),
Err(_) => -1,
}
}