Reduce CString allocations in std as much as possible
Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
This commit is contained in:
parent
33d351972a
commit
86974b83af
13 changed files with 398 additions and 287 deletions
|
@ -11,3 +11,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
pub mod alloc;
|
pub mod alloc;
|
||||||
|
pub mod small_c_string;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
58
library/std/src/sys/common/small_c_string.rs
Normal file
58
library/std/src/sys/common/small_c_string.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use crate::ffi::{CStr, CString};
|
||||||
|
use crate::mem::MaybeUninit;
|
||||||
|
use crate::path::Path;
|
||||||
|
use crate::slice;
|
||||||
|
use crate::{io, ptr};
|
||||||
|
|
||||||
|
// Make sure to stay under 4096 so the compiler doesn't insert a probe frame:
|
||||||
|
// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
|
||||||
|
#[cfg(not(target_os = "espidf"))]
|
||||||
|
const MAX_STACK_ALLOCATION: usize = 384;
|
||||||
|
#[cfg(target_os = "espidf")]
|
||||||
|
const MAX_STACK_ALLOCATION: usize = 32;
|
||||||
|
|
||||||
|
const NUL_ERR: io::Error =
|
||||||
|
io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte");
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn run_path_with_cstr<T, F>(path: &Path, f: F) -> io::Result<T>
|
||||||
|
where
|
||||||
|
F: FnOnce(&CStr) -> io::Result<T>,
|
||||||
|
{
|
||||||
|
run_with_cstr(path.as_os_str().bytes(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn run_with_cstr<T, F>(bytes: &[u8], f: F) -> io::Result<T>
|
||||||
|
where
|
||||||
|
F: FnOnce(&CStr) -> io::Result<T>,
|
||||||
|
{
|
||||||
|
if bytes.len() >= MAX_STACK_ALLOCATION {
|
||||||
|
return run_with_cstr_allocating(bytes, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
|
||||||
|
let buf_ptr = buf.as_mut_ptr() as *mut u8;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
|
||||||
|
buf_ptr.add(bytes.len()).write(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) {
|
||||||
|
Ok(s) => f(s),
|
||||||
|
Err(_) => Err(NUL_ERR),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[inline(never)]
|
||||||
|
fn run_with_cstr_allocating<T, F>(bytes: &[u8], f: F) -> io::Result<T>
|
||||||
|
where
|
||||||
|
F: FnOnce(&CStr) -> io::Result<T>,
|
||||||
|
{
|
||||||
|
match CString::new(bytes) {
|
||||||
|
Ok(s) => f(&s),
|
||||||
|
Err(_) => Err(NUL_ERR),
|
||||||
|
}
|
||||||
|
}
|
66
library/std/src/sys/common/tests.rs
Normal file
66
library/std/src/sys/common/tests.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use crate::ffi::CString;
|
||||||
|
use crate::hint::black_box;
|
||||||
|
use crate::path::Path;
|
||||||
|
use crate::sys::common::small_c_string::run_path_with_cstr;
|
||||||
|
use core::iter::repeat;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_allocation_works() {
|
||||||
|
let path = Path::new("abc");
|
||||||
|
let result = run_path_with_cstr(path, |p| {
|
||||||
|
assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap());
|
||||||
|
Ok(42)
|
||||||
|
});
|
||||||
|
assert_eq!(result.unwrap(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_allocation_fails() {
|
||||||
|
let path = Path::new("ab\0");
|
||||||
|
assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heap_allocation_works() {
|
||||||
|
let path = repeat("a").take(384).collect::<String>();
|
||||||
|
let path = Path::new(&path);
|
||||||
|
let result = run_path_with_cstr(path, |p| {
|
||||||
|
assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap());
|
||||||
|
Ok(42)
|
||||||
|
});
|
||||||
|
assert_eq!(result.unwrap(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heap_allocation_fails() {
|
||||||
|
let mut path = repeat("a").take(384).collect::<String>();
|
||||||
|
path.push('\0');
|
||||||
|
let path = Path::new(&path);
|
||||||
|
assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_stack_path_alloc(b: &mut test::Bencher) {
|
||||||
|
let path = repeat("a").take(383).collect::<String>();
|
||||||
|
let p = Path::new(&path);
|
||||||
|
b.iter(|| {
|
||||||
|
run_path_with_cstr(p, |cstr| {
|
||||||
|
black_box(cstr);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_heap_path_alloc(b: &mut test::Bencher) {
|
||||||
|
let path = repeat("a").take(384).collect::<String>();
|
||||||
|
let p = Path::new(&path);
|
||||||
|
b.iter(|| {
|
||||||
|
run_path_with_cstr(p, |cstr| {
|
||||||
|
black_box(cstr);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::convert::TryFrom;
|
||||||
use crate::ffi::{CStr, CString, OsString};
|
use crate::ffi::{CStr, CString, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::hash::{Hash, Hasher};
|
use crate::hash::{Hash, Hasher};
|
||||||
|
@ -5,6 +6,7 @@ use crate::io::{self, Error, ErrorKind};
|
||||||
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
|
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
|
||||||
use crate::os::unix::ffi::OsStrExt;
|
use crate::os::unix::ffi::OsStrExt;
|
||||||
use crate::path::{Path, PathBuf};
|
use crate::path::{Path, PathBuf};
|
||||||
|
use crate::sys::common::small_c_string::run_path_with_cstr;
|
||||||
use crate::sys::cvt;
|
use crate::sys::cvt;
|
||||||
use crate::sys::hermit::abi;
|
use crate::sys::hermit::abi;
|
||||||
use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
|
use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
|
||||||
|
@ -15,10 +17,6 @@ use crate::sys::unsupported;
|
||||||
pub use crate::sys_common::fs::{copy, try_exists};
|
pub use crate::sys_common::fs::{copy, try_exists};
|
||||||
//pub use crate::sys_common::fs::remove_dir_all;
|
//pub use crate::sys_common::fs::remove_dir_all;
|
||||||
|
|
||||||
fn cstr(path: &Path) -> io::Result<CString> {
|
|
||||||
Ok(CString::new(path.as_os_str().as_bytes())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File(FileDesc);
|
pub struct File(FileDesc);
|
||||||
|
|
||||||
|
@ -272,8 +270,7 @@ impl OpenOptions {
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
||||||
let path = cstr(path)?;
|
run_path_with_cstr(path, |path| File::open_c(&path, opts))
|
||||||
File::open_c(&path, opts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
|
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
|
||||||
|
@ -373,9 +370,7 @@ pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unlink(path: &Path) -> io::Result<()> {
|
pub fn unlink(path: &Path) -> io::Result<()> {
|
||||||
let name = cstr(path)?;
|
run_path_with_cstr(path, |path| cvt(unsafe { abi::unlink(path.as_ptr()) }).map(|_| ()))
|
||||||
let _ = unsafe { cvt(abi::unlink(name.as_ptr()))? };
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
|
pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
#![allow(missing_debug_implementations)]
|
#![allow(missing_debug_implementations)]
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(unix)] {
|
if #[cfg(unix)] {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use super::unsupported;
|
use super::unsupported;
|
||||||
|
use crate::convert::TryFrom;
|
||||||
use crate::error::Error as StdError;
|
use crate::error::Error as StdError;
|
||||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
use crate::ffi::{CStr, CString, OsStr, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
|
@ -9,6 +10,7 @@ use crate::os::{
|
||||||
};
|
};
|
||||||
use crate::path::{self, PathBuf};
|
use crate::path::{self, PathBuf};
|
||||||
use crate::sync::RwLock;
|
use crate::sync::RwLock;
|
||||||
|
use crate::sys::common::small_c_string::run_with_cstr;
|
||||||
use crate::vec;
|
use crate::vec;
|
||||||
|
|
||||||
use super::{error, itron, memchr};
|
use super::{error, itron, memchr};
|
||||||
|
@ -139,35 +141,33 @@ pub fn env() -> Env {
|
||||||
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
||||||
// environment variables with a nul byte can't be set, so their value is
|
// environment variables with a nul byte can't be set, so their value is
|
||||||
// always None as well
|
// always None as well
|
||||||
let k = CString::new(k.as_bytes()).ok()?;
|
let s = run_with_cstr(k.as_bytes(), |k| {
|
||||||
unsafe {
|
|
||||||
let _guard = ENV_LOCK.read();
|
let _guard = ENV_LOCK.read();
|
||||||
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
|
Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char)
|
||||||
if s.is_null() {
|
})
|
||||||
None
|
.ok()?;
|
||||||
} else {
|
|
||||||
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
|
if s.is_null() {
|
||||||
}
|
None
|
||||||
|
} else {
|
||||||
|
Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
||||||
let k = CString::new(k.as_bytes())?;
|
run_with_cstr(k.as_bytes(), |k| {
|
||||||
let v = CString::new(v.as_bytes())?;
|
run_with_cstr(v.as_bytes(), |v| {
|
||||||
|
let _guard = ENV_LOCK.write();
|
||||||
unsafe {
|
cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
|
||||||
let _guard = ENV_LOCK.write();
|
})
|
||||||
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
||||||
let nbuf = CString::new(n.as_bytes())?;
|
run_with_cstr(n.as_bytes(), |nbuf| {
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let _guard = ENV_LOCK.write();
|
let _guard = ENV_LOCK.write();
|
||||||
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
|
cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
|
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::os::unix::prelude::*;
|
use crate::os::unix::prelude::*;
|
||||||
|
|
||||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
use crate::ffi::{CStr, OsStr, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
|
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
|
@ -8,6 +8,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
|
||||||
use crate::path::{Path, PathBuf};
|
use crate::path::{Path, PathBuf};
|
||||||
use crate::ptr;
|
use crate::ptr;
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
|
use crate::sys::common::small_c_string::run_path_with_cstr;
|
||||||
use crate::sys::fd::FileDesc;
|
use crate::sys::fd::FileDesc;
|
||||||
use crate::sys::time::SystemTime;
|
use crate::sys::time::SystemTime;
|
||||||
use crate::sys::{cvt, cvt_r};
|
use crate::sys::{cvt, cvt_r};
|
||||||
|
@ -260,7 +261,7 @@ pub struct DirEntry {
|
||||||
// We need to store an owned copy of the entry name on platforms that use
|
// We need to store an owned copy of the entry name on platforms that use
|
||||||
// readdir() (not readdir_r()), because a) struct dirent may use a flexible
|
// readdir() (not readdir_r()), because a) struct dirent may use a flexible
|
||||||
// array to store the name, b) it lives only until the next readdir() call.
|
// array to store the name, b) it lives only until the next readdir() call.
|
||||||
name: CString,
|
name: crate::ffi::CString,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define a minimal subset of fields we need from `dirent64`, especially since
|
// Define a minimal subset of fields we need from `dirent64`, especially since
|
||||||
|
@ -900,8 +901,7 @@ impl OpenOptions {
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
||||||
let path = cstr(path)?;
|
run_path_with_cstr(path, |path| File::open_c(path, opts))
|
||||||
File::open_c(&path, opts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
|
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
|
||||||
|
@ -1114,9 +1114,7 @@ impl DirBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
|
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
|
||||||
let p = cstr(p)?;
|
run_path_with_cstr(p, |p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ()))
|
||||||
cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mode(&mut self, mode: u32) {
|
pub fn set_mode(&mut self, mode: u32) {
|
||||||
|
@ -1124,10 +1122,6 @@ impl DirBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cstr(path: &Path) -> io::Result<CString> {
|
|
||||||
Ok(CString::new(path.as_os_str().as_bytes())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsInner<FileDesc> for File {
|
impl AsInner<FileDesc> for File {
|
||||||
fn as_inner(&self) -> &FileDesc {
|
fn as_inner(&self) -> &FileDesc {
|
||||||
&self.0
|
&self.0
|
||||||
|
@ -1273,173 +1267,170 @@ impl fmt::Debug for File {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
|
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
|
||||||
let root = p.to_path_buf();
|
let ptr = run_path_with_cstr(path, |p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
|
||||||
let p = cstr(p)?;
|
if ptr.is_null() {
|
||||||
unsafe {
|
Err(Error::last_os_error())
|
||||||
let ptr = libc::opendir(p.as_ptr());
|
} else {
|
||||||
if ptr.is_null() {
|
let root = path.to_path_buf();
|
||||||
Err(Error::last_os_error())
|
let inner = InnerReadDir { dirp: Dir(ptr), root };
|
||||||
} else {
|
Ok(ReadDir {
|
||||||
let inner = InnerReadDir { dirp: Dir(ptr), root };
|
inner: Arc::new(inner),
|
||||||
Ok(ReadDir {
|
#[cfg(not(any(
|
||||||
inner: Arc::new(inner),
|
target_os = "android",
|
||||||
#[cfg(not(any(
|
target_os = "linux",
|
||||||
target_os = "android",
|
target_os = "solaris",
|
||||||
target_os = "linux",
|
target_os = "illumos",
|
||||||
target_os = "solaris",
|
target_os = "fuchsia",
|
||||||
target_os = "illumos",
|
target_os = "redox",
|
||||||
target_os = "fuchsia",
|
)))]
|
||||||
target_os = "redox",
|
end_of_stream: false,
|
||||||
)))]
|
})
|
||||||
end_of_stream: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unlink(p: &Path) -> io::Result<()> {
|
pub fn unlink(p: &Path) -> io::Result<()> {
|
||||||
let p = cstr(p)?;
|
run_path_with_cstr(p, |p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ()))
|
||||||
cvt(unsafe { libc::unlink(p.as_ptr()) })?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
|
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
|
||||||
let old = cstr(old)?;
|
run_path_with_cstr(old, |old| {
|
||||||
let new = cstr(new)?;
|
run_path_with_cstr(new, |new| {
|
||||||
cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
|
cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ())
|
||||||
Ok(())
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
|
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
|
||||||
let p = cstr(p)?;
|
run_path_with_cstr(p, |p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()))
|
||||||
cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rmdir(p: &Path) -> io::Result<()> {
|
pub fn rmdir(p: &Path) -> io::Result<()> {
|
||||||
let p = cstr(p)?;
|
run_path_with_cstr(p, |p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ()))
|
||||||
cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
|
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
|
||||||
let c_path = cstr(p)?;
|
run_path_with_cstr(p, |c_path| {
|
||||||
let p = c_path.as_ptr();
|
let p = c_path.as_ptr();
|
||||||
|
|
||||||
let mut buf = Vec::with_capacity(256);
|
let mut buf = Vec::with_capacity(256);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let buf_read =
|
let buf_read =
|
||||||
cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
|
cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })?
|
||||||
|
as usize;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
buf.set_len(buf_read);
|
buf.set_len(buf_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf_read != buf.capacity() {
|
||||||
|
buf.shrink_to_fit();
|
||||||
|
|
||||||
|
return Ok(PathBuf::from(OsString::from_vec(buf)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the internal buffer resizing logic of `Vec` by requiring
|
||||||
|
// more space than the current capacity. The length is guaranteed to be
|
||||||
|
// the same as the capacity due to the if statement above.
|
||||||
|
buf.reserve(1);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
if buf_read != buf.capacity() {
|
|
||||||
buf.shrink_to_fit();
|
|
||||||
|
|
||||||
return Ok(PathBuf::from(OsString::from_vec(buf)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger the internal buffer resizing logic of `Vec` by requiring
|
|
||||||
// more space than the current capacity. The length is guaranteed to be
|
|
||||||
// the same as the capacity due to the if statement above.
|
|
||||||
buf.reserve(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
|
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
|
||||||
let original = cstr(original)?;
|
run_path_with_cstr(original, |original| {
|
||||||
let link = cstr(link)?;
|
run_path_with_cstr(link, |link| {
|
||||||
cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?;
|
cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ())
|
||||||
Ok(())
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
|
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
|
||||||
let original = cstr(original)?;
|
run_path_with_cstr(original, |original| {
|
||||||
let link = cstr(link)?;
|
run_path_with_cstr(link, |link| {
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] {
|
if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] {
|
||||||
// VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
|
// VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
|
||||||
// it implementation-defined whether `link` follows symlinks, so rely on the
|
// it implementation-defined whether `link` follows symlinks, so rely on the
|
||||||
// `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
|
// `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
|
||||||
// Android has `linkat` on newer versions, but we happen to know `link`
|
// Android has `linkat` on newer versions, but we happen to know `link`
|
||||||
// always has the correct behavior, so it's here as well.
|
// always has the correct behavior, so it's here as well.
|
||||||
cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
|
cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
|
||||||
} else if #[cfg(target_os = "macos")] {
|
} else if #[cfg(target_os = "macos")] {
|
||||||
// On MacOS, older versions (<=10.9) lack support for linkat while newer
|
// On MacOS, older versions (<=10.9) lack support for linkat while newer
|
||||||
// versions have it. We want to use linkat if it is available, so we use weak!
|
// versions have it. We want to use linkat if it is available, so we use weak!
|
||||||
// to check. `linkat` is preferable to `link` because it gives us a flag to
|
// to check. `linkat` is preferable to `link` because it gives us a flag to
|
||||||
// specify how symlinks should be handled. We pass 0 as the flags argument,
|
// specify how symlinks should be handled. We pass 0 as the flags argument,
|
||||||
// meaning it shouldn't follow symlinks.
|
// meaning it shouldn't follow symlinks.
|
||||||
weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
|
weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
|
||||||
|
|
||||||
if let Some(f) = linkat.get() {
|
if let Some(f) = linkat.get() {
|
||||||
cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
|
cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
|
||||||
} else {
|
} else {
|
||||||
cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
|
cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Where we can, use `linkat` instead of `link`; see the comment above
|
// Where we can, use `linkat` instead of `link`; see the comment above
|
||||||
// this one for details on why.
|
// this one for details on why.
|
||||||
cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
|
cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stat(p: &Path) -> io::Result<FileAttr> {
|
pub fn stat(p: &Path) -> io::Result<FileAttr> {
|
||||||
let p = cstr(p)?;
|
run_path_with_cstr(p, |p| {
|
||||||
|
cfg_has_statx! {
|
||||||
cfg_has_statx! {
|
if let Some(ret) = unsafe { try_statx(
|
||||||
if let Some(ret) = unsafe { try_statx(
|
libc::AT_FDCWD,
|
||||||
libc::AT_FDCWD,
|
p.as_ptr(),
|
||||||
p.as_ptr(),
|
libc::AT_STATX_SYNC_AS_STAT,
|
||||||
libc::AT_STATX_SYNC_AS_STAT,
|
libc::STATX_ALL,
|
||||||
libc::STATX_ALL,
|
) } {
|
||||||
) } {
|
return ret;
|
||||||
return ret;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut stat: stat64 = unsafe { mem::zeroed() };
|
let mut stat: stat64 = unsafe { mem::zeroed() };
|
||||||
cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
|
cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
|
||||||
Ok(FileAttr::from_stat64(stat))
|
Ok(FileAttr::from_stat64(stat))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
|
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
|
||||||
let p = cstr(p)?;
|
run_path_with_cstr(p, |p| {
|
||||||
|
cfg_has_statx! {
|
||||||
cfg_has_statx! {
|
if let Some(ret) = unsafe { try_statx(
|
||||||
if let Some(ret) = unsafe { try_statx(
|
libc::AT_FDCWD,
|
||||||
libc::AT_FDCWD,
|
p.as_ptr(),
|
||||||
p.as_ptr(),
|
libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
|
||||||
libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
|
libc::STATX_ALL,
|
||||||
libc::STATX_ALL,
|
) } {
|
||||||
) } {
|
return ret;
|
||||||
return ret;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut stat: stat64 = unsafe { mem::zeroed() };
|
let mut stat: stat64 = unsafe { mem::zeroed() };
|
||||||
cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
|
cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
|
||||||
Ok(FileAttr::from_stat64(stat))
|
Ok(FileAttr::from_stat64(stat))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
|
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
|
||||||
let path = CString::new(p.as_os_str().as_bytes())?;
|
let r = run_path_with_cstr(p, |path| unsafe {
|
||||||
let buf;
|
Ok(libc::realpath(path.as_ptr(), ptr::null_mut()))
|
||||||
unsafe {
|
})?;
|
||||||
let r = libc::realpath(path.as_ptr(), ptr::null_mut());
|
if r.is_null() {
|
||||||
if r.is_null() {
|
return Err(io::Error::last_os_error());
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
buf = CStr::from_ptr(r).to_bytes().to_vec();
|
|
||||||
libc::free(r as *mut _);
|
|
||||||
}
|
}
|
||||||
Ok(PathBuf::from(OsString::from_vec(buf)))
|
Ok(PathBuf::from(OsString::from_vec(unsafe {
|
||||||
|
let buf = CStr::from_ptr(r).to_bytes().to_vec();
|
||||||
|
libc::free(r as *mut _);
|
||||||
|
buf
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
|
fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
|
||||||
|
@ -1589,9 +1580,9 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||||
// Opportunistically attempt to create a copy-on-write clone of `from`
|
// Opportunistically attempt to create a copy-on-write clone of `from`
|
||||||
// using `fclonefileat`.
|
// using `fclonefileat`.
|
||||||
if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
|
if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
|
||||||
let to = cstr(to)?;
|
let clonefile_result = run_path_with_cstr(to, |to| {
|
||||||
let clonefile_result =
|
cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) })
|
||||||
cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
|
});
|
||||||
match clonefile_result {
|
match clonefile_result {
|
||||||
Ok(_) => return Ok(reader_metadata.len()),
|
Ok(_) => return Ok(reader_metadata.len()),
|
||||||
Err(err) => match err.raw_os_error() {
|
Err(err) => match err.raw_os_error() {
|
||||||
|
@ -1635,9 +1626,10 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
|
pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
|
||||||
let path = cstr(path)?;
|
run_path_with_cstr(path, |path| {
|
||||||
cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
|
cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
|
||||||
Ok(())
|
.map(|_| ())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
|
pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
|
||||||
|
@ -1646,16 +1638,15 @@ pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
|
pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
|
||||||
let path = cstr(path)?;
|
run_path_with_cstr(path, |path| {
|
||||||
cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
|
cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
|
||||||
Ok(())
|
.map(|_| ())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
|
#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
|
||||||
pub fn chroot(dir: &Path) -> io::Result<()> {
|
pub fn chroot(dir: &Path) -> io::Result<()> {
|
||||||
let dir = cstr(dir)?;
|
run_path_with_cstr(dir, |dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ()))
|
||||||
cvt(unsafe { libc::chroot(dir.as_ptr()) })?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use remove_dir_impl::remove_dir_all;
|
pub use remove_dir_impl::remove_dir_all;
|
||||||
|
@ -1669,13 +1660,14 @@ mod remove_dir_impl {
|
||||||
// Modern implementation using openat(), unlinkat() and fdopendir()
|
// Modern implementation using openat(), unlinkat() and fdopendir()
|
||||||
#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))]
|
#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))]
|
||||||
mod remove_dir_impl {
|
mod remove_dir_impl {
|
||||||
use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir};
|
use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir};
|
||||||
use crate::ffi::CStr;
|
use crate::ffi::CStr;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
||||||
use crate::os::unix::prelude::{OwnedFd, RawFd};
|
use crate::os::unix::prelude::{OwnedFd, RawFd};
|
||||||
use crate::path::{Path, PathBuf};
|
use crate::path::{Path, PathBuf};
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
|
use crate::sys::common::small_c_string::run_path_with_cstr;
|
||||||
use crate::sys::{cvt, cvt_r};
|
use crate::sys::{cvt, cvt_r};
|
||||||
|
|
||||||
#[cfg(not(all(target_os = "macos", not(target_arch = "aarch64")),))]
|
#[cfg(not(all(target_os = "macos", not(target_arch = "aarch64")),))]
|
||||||
|
@ -1842,7 +1834,7 @@ mod remove_dir_impl {
|
||||||
if attr.file_type().is_symlink() {
|
if attr.file_type().is_symlink() {
|
||||||
crate::fs::remove_file(p)
|
crate::fs::remove_file(p)
|
||||||
} else {
|
} else {
|
||||||
remove_dir_all_recursive(None, &cstr(p)?)
|
run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ mod tests;
|
||||||
|
|
||||||
use crate::os::unix::prelude::*;
|
use crate::os::unix::prelude::*;
|
||||||
|
|
||||||
|
use crate::convert::TryFrom;
|
||||||
use crate::error::Error as StdError;
|
use crate::error::Error as StdError;
|
||||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
use crate::ffi::{CStr, CString, OsStr, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
|
@ -18,6 +19,7 @@ use crate::ptr;
|
||||||
use crate::slice;
|
use crate::slice;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
use crate::sync::{PoisonError, RwLock};
|
use crate::sync::{PoisonError, RwLock};
|
||||||
|
use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
|
||||||
use crate::sys::cvt;
|
use crate::sys::cvt;
|
||||||
use crate::sys::fd;
|
use crate::sys::fd;
|
||||||
use crate::sys::memchr;
|
use crate::sys::memchr;
|
||||||
|
@ -170,12 +172,8 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
|
||||||
|
|
||||||
#[cfg(not(target_os = "espidf"))]
|
#[cfg(not(target_os = "espidf"))]
|
||||||
pub fn chdir(p: &path::Path) -> io::Result<()> {
|
pub fn chdir(p: &path::Path) -> io::Result<()> {
|
||||||
let p: &OsStr = p.as_ref();
|
let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?;
|
||||||
let p = CString::new(p.as_bytes())?;
|
if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) }
|
||||||
if unsafe { libc::chdir(p.as_ptr()) } != 0 {
|
|
||||||
return Err(io::Error::last_os_error());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SplitPaths<'a> {
|
pub struct SplitPaths<'a> {
|
||||||
|
@ -548,35 +546,32 @@ pub fn env() -> Env {
|
||||||
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
||||||
// environment variables with a nul byte can't be set, so their value is
|
// environment variables with a nul byte can't be set, so their value is
|
||||||
// always None as well
|
// always None as well
|
||||||
let k = CString::new(k.as_bytes()).ok()?;
|
let s = run_with_cstr(k.as_bytes(), |k| {
|
||||||
unsafe {
|
|
||||||
let _guard = env_read_lock();
|
let _guard = env_read_lock();
|
||||||
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
|
Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char)
|
||||||
if s.is_null() {
|
})
|
||||||
None
|
.ok()?;
|
||||||
} else {
|
if s.is_null() {
|
||||||
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
|
None
|
||||||
}
|
} else {
|
||||||
|
Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
||||||
let k = CString::new(k.as_bytes())?;
|
run_with_cstr(k.as_bytes(), |k| {
|
||||||
let v = CString::new(v.as_bytes())?;
|
run_with_cstr(v.as_bytes(), |v| {
|
||||||
|
let _guard = ENV_LOCK.write();
|
||||||
unsafe {
|
cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
|
||||||
let _guard = ENV_LOCK.write();
|
})
|
||||||
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
||||||
let nbuf = CString::new(n.as_bytes())?;
|
run_with_cstr(n.as_bytes(), |nbuf| {
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let _guard = ENV_LOCK.write();
|
let _guard = ENV_LOCK.write();
|
||||||
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
|
cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "espidf"))]
|
#[cfg(not(target_os = "espidf"))]
|
||||||
|
|
|
@ -154,9 +154,8 @@ impl Thread {
|
||||||
|
|
||||||
#[cfg(target_os = "netbsd")]
|
#[cfg(target_os = "netbsd")]
|
||||||
pub fn set_name(name: &CStr) {
|
pub fn set_name(name: &CStr) {
|
||||||
use crate::ffi::CString;
|
|
||||||
let cname = CString::new(&b"%s"[..]).unwrap();
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let cname = CStr::from_bytes_with_nul_unchecked(b"%s\0".as_slice());
|
||||||
libc::pthread_setname_np(
|
libc::pthread_setname_np(
|
||||||
libc::pthread_self(),
|
libc::pthread_self(),
|
||||||
cname.as_ptr(),
|
cname.as_ptr(),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
use super::fd::WasiFd;
|
use super::fd::WasiFd;
|
||||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
use crate::ffi::{CStr, OsStr, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
|
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
|
||||||
use crate::iter;
|
use crate::iter;
|
||||||
|
@ -12,6 +12,7 @@ use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd
|
||||||
use crate::path::{Path, PathBuf};
|
use crate::path::{Path, PathBuf};
|
||||||
use crate::ptr;
|
use crate::ptr;
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
|
use crate::sys::common::small_c_string::run_path_with_cstr;
|
||||||
use crate::sys::time::SystemTime;
|
use crate::sys::time::SystemTime;
|
||||||
use crate::sys::unsupported;
|
use crate::sys::unsupported;
|
||||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||||
|
@ -694,51 +695,52 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
||||||
/// Note that this can fail if `p` doesn't look like it can be opened relative
|
/// Note that this can fail if `p` doesn't look like it can be opened relative
|
||||||
/// to any pre-opened file descriptor.
|
/// to any pre-opened file descriptor.
|
||||||
fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
|
fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
|
||||||
let p = CString::new(p.as_os_str().as_bytes())?;
|
run_path_with_cstr(p, |p| {
|
||||||
let mut buf = Vec::<u8>::with_capacity(512);
|
let mut buf = Vec::<u8>::with_capacity(512);
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut relative_path = buf.as_ptr().cast();
|
let mut relative_path = buf.as_ptr().cast();
|
||||||
let mut abs_prefix = ptr::null();
|
let mut abs_prefix = ptr::null();
|
||||||
let fd = __wasilibc_find_relpath(
|
let fd = __wasilibc_find_relpath(
|
||||||
p.as_ptr(),
|
p.as_ptr(),
|
||||||
&mut abs_prefix,
|
&mut abs_prefix,
|
||||||
&mut relative_path,
|
&mut relative_path,
|
||||||
buf.capacity(),
|
buf.capacity(),
|
||||||
);
|
|
||||||
if fd == -1 {
|
|
||||||
if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
|
|
||||||
// Trigger the internal buffer resizing logic of `Vec` by requiring
|
|
||||||
// more space than the current capacity.
|
|
||||||
let cap = buf.capacity();
|
|
||||||
buf.set_len(cap);
|
|
||||||
buf.reserve(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let msg = format!(
|
|
||||||
"failed to find a pre-opened file descriptor \
|
|
||||||
through which {:?} could be opened",
|
|
||||||
p
|
|
||||||
);
|
);
|
||||||
return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
|
if fd == -1 {
|
||||||
|
if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
|
||||||
|
// Trigger the internal buffer resizing logic of `Vec` by requiring
|
||||||
|
// more space than the current capacity.
|
||||||
|
let cap = buf.capacity();
|
||||||
|
buf.set_len(cap);
|
||||||
|
buf.reserve(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let msg = format!(
|
||||||
|
"failed to find a pre-opened file descriptor \
|
||||||
|
through which {:?} could be opened",
|
||||||
|
p
|
||||||
|
);
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
|
||||||
|
}
|
||||||
|
let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
|
||||||
|
|
||||||
|
return Ok((
|
||||||
|
ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
|
||||||
|
PathBuf::from(OsString::from_vec(relative)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
|
|
||||||
|
|
||||||
return Ok((
|
|
||||||
ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
|
|
||||||
PathBuf::from(OsString::from_vec(relative)),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn __wasilibc_find_relpath(
|
pub fn __wasilibc_find_relpath(
|
||||||
path: *const libc::c_char,
|
path: *const libc::c_char,
|
||||||
abs_prefix: *mut *const libc::c_char,
|
abs_prefix: *mut *const libc::c_char,
|
||||||
relative_path: *mut *const libc::c_char,
|
relative_path: *mut *const libc::c_char,
|
||||||
relative_path_len: libc::size_t,
|
relative_path_len: libc::size_t,
|
||||||
) -> libc::c_int;
|
) -> libc::c_int;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
|
pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
use crate::any::Any;
|
use crate::any::Any;
|
||||||
use crate::error::Error as StdError;
|
use crate::error::Error as StdError;
|
||||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
use crate::ffi::{CStr, OsStr, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::marker::PhantomData;
|
use crate::marker::PhantomData;
|
||||||
use crate::os::wasi::prelude::*;
|
use crate::os::wasi::prelude::*;
|
||||||
use crate::path::{self, PathBuf};
|
use crate::path::{self, PathBuf};
|
||||||
use crate::str;
|
use crate::str;
|
||||||
|
use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr};
|
||||||
use crate::sys::memchr;
|
use crate::sys::memchr;
|
||||||
use crate::sys::unsupported;
|
use crate::sys::unsupported;
|
||||||
use crate::vec;
|
use crate::vec;
|
||||||
|
@ -77,13 +78,10 @@ pub fn getcwd() -> io::Result<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chdir(p: &path::Path) -> io::Result<()> {
|
pub fn chdir(p: &path::Path) -> io::Result<()> {
|
||||||
let p: &OsStr = p.as_ref();
|
let result = run_path_with_cstr(p, |p| unsafe { Ok(libc::chdir(p.as_ptr())) })?;
|
||||||
let p = CString::new(p.as_bytes())?;
|
match result == (0 as libc::c_int) {
|
||||||
unsafe {
|
true => Ok(()),
|
||||||
match libc::chdir(p.as_ptr()) == (0 as libc::c_int) {
|
false => Err(io::Error::last_os_error()),
|
||||||
true => Ok(()),
|
|
||||||
false => Err(io::Error::last_os_error()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,35 +174,32 @@ pub fn env() -> Env {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
pub fn getenv(k: &OsStr) -> Option<OsString> {
|
||||||
let k = CString::new(k.as_bytes()).ok()?;
|
let s = run_with_cstr(k.as_bytes(), |k| unsafe {
|
||||||
unsafe {
|
|
||||||
let _guard = env_lock();
|
let _guard = env_lock();
|
||||||
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
|
Ok(libc::getenv(k.as_ptr()) as *const libc::c_char)
|
||||||
if s.is_null() {
|
})
|
||||||
None
|
.ok()?;
|
||||||
} else {
|
if s.is_null() {
|
||||||
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
|
None
|
||||||
}
|
} else {
|
||||||
|
Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
||||||
let k = CString::new(k.as_bytes())?;
|
run_with_cstr(k.as_bytes(), |k| {
|
||||||
let v = CString::new(v.as_bytes())?;
|
run_with_cstr(v.as_bytes(), |v| unsafe {
|
||||||
|
let _guard = env_lock();
|
||||||
unsafe {
|
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
|
||||||
let _guard = env_lock();
|
})
|
||||||
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
||||||
let nbuf = CString::new(n.as_bytes())?;
|
run_with_cstr(n.as_bytes(), |nbuf| unsafe {
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let _guard = env_lock();
|
let _guard = env_lock();
|
||||||
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
|
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn temp_dir() -> PathBuf {
|
pub fn temp_dir() -> PathBuf {
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use crate::cmp;
|
use crate::cmp;
|
||||||
use crate::ffi::CString;
|
use crate::convert::{TryFrom, TryInto};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
|
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
|
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
|
||||||
use crate::ptr;
|
use crate::ptr;
|
||||||
|
use crate::sys::common::small_c_string::run_with_cstr;
|
||||||
use crate::sys::net::netc as c;
|
use crate::sys::net::netc as c;
|
||||||
use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket};
|
use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket};
|
||||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||||
|
@ -197,14 +198,15 @@ impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
|
||||||
fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> {
|
fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> {
|
||||||
init();
|
init();
|
||||||
|
|
||||||
let c_host = CString::new(host)?;
|
run_with_cstr(host.as_bytes(), |c_host| {
|
||||||
let mut hints: c::addrinfo = unsafe { mem::zeroed() };
|
let mut hints: c::addrinfo = unsafe { mem::zeroed() };
|
||||||
hints.ai_socktype = c::SOCK_STREAM;
|
hints.ai_socktype = c::SOCK_STREAM;
|
||||||
let mut res = ptr::null_mut();
|
let mut res = ptr::null_mut();
|
||||||
unsafe {
|
unsafe {
|
||||||
cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res))
|
cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res))
|
||||||
.map(|_| LookupHost { original: res, cur: res, port })
|
.map(|_| LookupHost { original: res, cur: res, port })
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ LL | let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode a
|
||||||
= note: inside closure at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC
|
= note: inside closure at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC
|
||||||
= note: inside `std::sys::PLATFORM::cvt_r::<i32, [closure@std::sys::PLATFORM::fs::File::open_c::{closure#0}]>` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC
|
= note: inside `std::sys::PLATFORM::cvt_r::<i32, [closure@std::sys::PLATFORM::fs::File::open_c::{closure#0}]>` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC
|
||||||
= note: inside `std::sys::PLATFORM::fs::File::open_c` at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC
|
= note: inside `std::sys::PLATFORM::fs::File::open_c` at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC
|
||||||
|
= note: inside closure at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC
|
||||||
|
= note: inside `std::sys::PLATFORM::small_c_string::run_with_cstr::<std::sys::PLATFORM::fs::File, [closure@std::sys::PLATFORM::fs::File::open::{closure#0}]>` at RUSTLIB/std/src/sys/PLATFORM/small_c_string.rs:LL:CC
|
||||||
|
= note: inside `std::sys::PLATFORM::small_c_string::run_path_with_cstr::<std::sys::PLATFORM::fs::File, [closure@std::sys::PLATFORM::fs::File::open::{closure#0}]>` at RUSTLIB/std/src/sys/PLATFORM/small_c_string.rs:LL:CC
|
||||||
= note: inside `std::sys::PLATFORM::fs::File::open` at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC
|
= note: inside `std::sys::PLATFORM::fs::File::open` at RUSTLIB/std/src/sys/PLATFORM/fs.rs:LL:CC
|
||||||
= note: inside `std::fs::OpenOptions::_open` at RUSTLIB/std/src/fs.rs:LL:CC
|
= note: inside `std::fs::OpenOptions::_open` at RUSTLIB/std/src/fs.rs:LL:CC
|
||||||
= note: inside `std::fs::OpenOptions::open::<&std::path::Path>` at RUSTLIB/std/src/fs.rs:LL:CC
|
= note: inside `std::fs::OpenOptions::open::<&std::path::Path>` at RUSTLIB/std/src/fs.rs:LL:CC
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue