use std::ffi::CStr; use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd}; use libc::{c_char, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH}; use crate::{bfmt_cstr, errno, xopen}; mod unsafe_impl { use std::ffi::CStr; use std::slice; use libc::c_char; #[no_mangle] pub unsafe extern "C" fn read_link(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'; } return r; } #[no_mangle] unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { super::canonical_path(CStr::from_ptr(path), slice::from_raw_parts_mut(buf, bufsz)) } } pub fn __open_fd_impl(path: &CStr, flags: i32, mode: mode_t) -> Option { let fd = xopen(path.as_ptr(), flags, mode); if fd >= 0 { unsafe { Some(OwnedFd::from_raw_fd(fd)) } } else { None } } #[macro_export] macro_rules! open_fd { ($path:expr, $flags:expr) => { crate::__open_fd_impl($path, $flags, 0) }; ($path:expr, $flags:expr, $mode:expr) => { crate::__open_fd_impl($path, $flags, $mode) }; } pub fn readlink(path: &CStr, data: &mut [u8]) -> isize { unsafe { unsafe_impl::read_link(path.as_ptr(), data.as_mut_ptr(), data.len()) } } pub fn fd_path(fd: RawFd, buf: &mut [u8]) -> isize { let mut fd_buf: [u8; 40] = [0; 40]; let fd_path = bfmt_cstr!(&mut fd_buf, "/proc/self/fd/{}", fd); readlink(fd_path, buf) } // Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp pub fn canonical_path(path: &CStr, buf: &mut [u8]) -> isize { if let Some(fd) = open_fd!(path, O_PATH | O_CLOEXEC) { let mut st1: libc::stat; let mut st2: libc::stat; unsafe { st1 = std::mem::zeroed(); if libc::fstat(fd.as_raw_fd(), &mut st1) < 0 { *errno() = ENOENT; return -1; } } let len = fd_path(fd.as_raw_fd(), buf); unsafe { st2 = std::mem::zeroed(); if libc::stat(buf.as_ptr().cast(), &mut st2) < 0 || st2.st_dev != st1.st_dev || st2.st_ino != st1.st_ino { *errno() = ENOENT; return -1; } } return len; } else { *errno() = ENOENT; -1 } } extern "C" { fn strscpy(dst: *mut c_char, src: *const c_char, size: usize) -> usize; } #[no_mangle] pub extern "C" fn mkdirs(path: *const c_char, mode: mode_t) -> i32 { unsafe { let mut buf = [0 as u8; 4096]; let ptr: *mut c_char = buf.as_mut_ptr().cast(); let len = strscpy(ptr, path, buf.len()); let mut curr = &mut buf[1..len]; while let Some(p) = curr.iter().position(|c| *c == b'/') { curr[p] = b'\0'; if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST { return -1; } curr[p] = b'/'; curr = &mut curr[(p + 1)..]; } if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST { return -1; } 0 } }