Skip to content

Commit f121b86

Browse files
committed
Store the path in io::Error without extra allocations.
We need to allocate for doing a syscall on a path anyway, so this just keeps that allocation around in the io::Error if the syscall fails.
1 parent 543ab99 commit f121b86

File tree

5 files changed

+184
-38
lines changed

5 files changed

+184
-38
lines changed

library/std/src/io/error.rs

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
#![allow(unreachable_patterns)] // For empty OsWithPath on some platforms.
2+
13
#[cfg(test)]
24
mod tests;
35

4-
use crate::convert::From;
6+
use crate::convert::{From, TryInto};
57
use crate::error;
68
use crate::fmt;
79
use crate::result;
@@ -68,9 +70,19 @@ impl fmt::Debug for Error {
6870

6971
enum Repr {
7072
Os(i32),
73+
74+
// i16 to make sure the whole Repr remains two words in size.
75+
// All known error codes fit in a i16.
76+
// If for some reason it wouldn't fit, we just don't add the path and use
77+
// Os(code) instead.
78+
#[allow(dead_code)]
79+
OsWithPath(i16, sys::fs::OsPathBuf),
80+
7181
Simple(ErrorKind),
82+
7283
// &str is a fat pointer, but &&str is a thin pointer.
7384
SimpleMessage(ErrorKind, &'static &'static str),
85+
7486
Custom(Box<Custom>),
7587
}
7688

@@ -337,6 +349,16 @@ impl Error {
337349
Error { repr: Repr::Os(code) }
338350
}
339351

352+
#[allow(dead_code)]
353+
pub(crate) fn with_path(self, path: sys::fs::OsPathBuf) -> Self {
354+
if let Repr::Os(code) = self.repr {
355+
if let Ok(code) = code.try_into() {
356+
return Self { repr: Repr::OsWithPath(code, path) };
357+
}
358+
}
359+
self
360+
}
361+
340362
/// Returns the OS error that this error represents (if any).
341363
///
342364
/// If this [`Error`] was constructed via [`last_os_error`] or
@@ -371,6 +393,7 @@ impl Error {
371393
pub fn raw_os_error(&self) -> Option<i32> {
372394
match self.repr {
373395
Repr::Os(i) => Some(i),
396+
Repr::OsWithPath(i, _) => Some(i.into()),
374397
Repr::Custom(..) => None,
375398
Repr::Simple(..) => None,
376399
Repr::SimpleMessage(..) => None,
@@ -409,6 +432,7 @@ impl Error {
409432
pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> {
410433
match self.repr {
411434
Repr::Os(..) => None,
435+
Repr::OsWithPath(..) => None,
412436
Repr::Simple(..) => None,
413437
Repr::SimpleMessage(..) => None,
414438
Repr::Custom(ref c) => Some(&*c.error),
@@ -482,6 +506,7 @@ impl Error {
482506
pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> {
483507
match self.repr {
484508
Repr::Os(..) => None,
509+
Repr::OsWithPath(..) => None,
485510
Repr::Simple(..) => None,
486511
Repr::SimpleMessage(..) => None,
487512
Repr::Custom(ref mut c) => Some(&mut *c.error),
@@ -520,6 +545,7 @@ impl Error {
520545
pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
521546
match self.repr {
522547
Repr::Os(..) => None,
548+
Repr::OsWithPath(..) => None,
523549
Repr::Simple(..) => None,
524550
Repr::SimpleMessage(..) => None,
525551
Repr::Custom(c) => Some(c.error),
@@ -549,6 +575,7 @@ impl Error {
549575
pub fn kind(&self) -> ErrorKind {
550576
match self.repr {
551577
Repr::Os(code) => sys::decode_error_kind(code),
578+
Repr::OsWithPath(code, _) => sys::decode_error_kind(code.into()),
552579
Repr::Custom(ref c) => c.kind,
553580
Repr::Simple(kind) => kind,
554581
Repr::SimpleMessage(kind, _) => kind,
@@ -565,6 +592,13 @@ impl fmt::Debug for Repr {
565592
.field("kind", &sys::decode_error_kind(code))
566593
.field("message", &sys::os::error_string(code))
567594
.finish(),
595+
Repr::OsWithPath(code, ref path) => fmt
596+
.debug_struct("Os")
597+
.field("code", &code)
598+
.field("kind", &sys::decode_error_kind(code.into()))
599+
.field("message", &sys::os::error_string(code.into()))
600+
.field("path", path)
601+
.finish(),
568602
Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
569603
Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
570604
Repr::SimpleMessage(kind, &message) => {
@@ -582,6 +616,9 @@ impl fmt::Display for Error {
582616
let detail = sys::os::error_string(code);
583617
write!(fmt, "{} (os error {})", detail, code)
584618
}
619+
Repr::OsWithPath(code, _) => {
620+
fmt::Display::fmt(&Self::from_raw_os_error(code.into()), fmt)
621+
}
585622
Repr::Custom(ref c) => c.error.fmt(fmt),
586623
Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
587624
Repr::SimpleMessage(_, &msg) => msg.fmt(fmt),
@@ -594,7 +631,7 @@ impl error::Error for Error {
594631
#[allow(deprecated, deprecated_in_future)]
595632
fn description(&self) -> &str {
596633
match self.repr {
597-
Repr::Os(..) | Repr::Simple(..) => self.kind().as_str(),
634+
Repr::Os(..) | Repr::OsWithPath(..) | Repr::Simple(..) => self.kind().as_str(),
598635
Repr::SimpleMessage(_, &msg) => msg,
599636
Repr::Custom(ref c) => c.error.description(),
600637
}
@@ -604,6 +641,7 @@ impl error::Error for Error {
604641
fn cause(&self) -> Option<&dyn error::Error> {
605642
match self.repr {
606643
Repr::Os(..) => None,
644+
Repr::OsWithPath(..) => None,
607645
Repr::Simple(..) => None,
608646
Repr::SimpleMessage(..) => None,
609647
Repr::Custom(ref c) => c.error.cause(),
@@ -613,6 +651,7 @@ impl error::Error for Error {
613651
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
614652
match self.repr {
615653
Repr::Os(..) => None,
654+
Repr::OsWithPath(..) => None,
616655
Repr::Simple(..) => None,
617656
Repr::SimpleMessage(..) => None,
618657
Repr::Custom(ref c) => c.error.source(),

library/std/src/sys/unix/fs.rs

+38-35
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod ospathbuf;
2+
13
use crate::os::unix::prelude::*;
24

35
use crate::ffi::{CStr, CString, OsStr, OsString};
@@ -50,6 +52,8 @@ use libc::{
5052

5153
pub use crate::sys_common::fs::{remove_dir_all, try_exists};
5254

55+
pub use ospathbuf::OsPathBuf;
56+
5357
pub struct File(FileDesc);
5458

5559
// FIXME: This should be available on Linux with all `target_env`.
@@ -729,8 +733,8 @@ impl OpenOptions {
729733

730734
impl File {
731735
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
732-
let path = cstr(path)?;
733-
File::open_c(&path, opts)
736+
let path = OsPathBuf::new(path)?;
737+
File::open_c(&path, opts).map_err(|e| e.with_path(path))
734738
}
735739

736740
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
@@ -898,8 +902,8 @@ impl DirBuilder {
898902
}
899903

900904
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
901-
let p = cstr(p)?;
902-
cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
905+
let p = OsPathBuf::new(p)?;
906+
cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map_err(|e| e.with_path(p))?;
903907
Ok(())
904908
}
905909

@@ -908,10 +912,6 @@ impl DirBuilder {
908912
}
909913
}
910914

911-
fn cstr(path: &Path) -> io::Result<CString> {
912-
Ok(CString::new(path.as_os_str().as_bytes())?)
913-
}
914-
915915
impl FromInner<c_int> for File {
916916
fn from_inner(fd: c_int) -> File {
917917
File(FileDesc::new(fd))
@@ -998,11 +998,11 @@ impl fmt::Debug for File {
998998

999999
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
10001000
let root = p.to_path_buf();
1001-
let p = cstr(p)?;
1001+
let p = OsPathBuf::new(p)?;
10021002
unsafe {
10031003
let ptr = libc::opendir(p.as_ptr());
10041004
if ptr.is_null() {
1005-
Err(Error::last_os_error())
1005+
Err(Error::last_os_error().with_path(p))
10061006
} else {
10071007
let inner = InnerReadDir { dirp: Dir(ptr), root };
10081008
Ok(ReadDir {
@@ -1020,39 +1020,42 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
10201020
}
10211021

10221022
pub fn unlink(p: &Path) -> io::Result<()> {
1023-
let p = cstr(p)?;
1024-
cvt(unsafe { libc::unlink(p.as_ptr()) })?;
1023+
let p = OsPathBuf::new(p)?;
1024+
cvt(unsafe { libc::unlink(p.as_ptr()) }).map_err(|e| e.with_path(p))?;
10251025
Ok(())
10261026
}
10271027

10281028
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
1029-
let old = cstr(old)?;
1030-
let new = cstr(new)?;
1029+
let old = OsPathBuf::new(old)?;
1030+
let new = OsPathBuf::new(new)?;
10311031
cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
10321032
Ok(())
10331033
}
10341034

10351035
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
1036-
let p = cstr(p)?;
1037-
cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
1036+
let p = OsPathBuf::new(p)?;
1037+
cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map_err(|e| e.with_path(p))?;
10381038
Ok(())
10391039
}
10401040

10411041
pub fn rmdir(p: &Path) -> io::Result<()> {
1042-
let p = cstr(p)?;
1043-
cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
1042+
let p = OsPathBuf::new(p)?;
1043+
cvt(unsafe { libc::rmdir(p.as_ptr()) }).map_err(|e| e.with_path(p))?;
10441044
Ok(())
10451045
}
10461046

10471047
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
1048-
let c_path = cstr(p)?;
1049-
let p = c_path.as_ptr();
1048+
let p = OsPathBuf::new(p)?;
10501049

10511050
let mut buf = Vec::with_capacity(256);
10521051

10531052
loop {
1054-
let buf_read =
1055-
cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
1053+
let buf_read = match cvt(unsafe {
1054+
libc::readlink(p.as_ptr(), buf.as_mut_ptr() as *mut _, buf.capacity())
1055+
}) {
1056+
Ok(r) => r as usize,
1057+
Err(e) => return Err(e.with_path(p)),
1058+
};
10561059

10571060
unsafe {
10581061
buf.set_len(buf_read);
@@ -1072,15 +1075,15 @@ pub fn readlink(p: &Path) -> io::Result<PathBuf> {
10721075
}
10731076

10741077
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1075-
let original = cstr(original)?;
1076-
let link = cstr(link)?;
1078+
let original = OsPathBuf::new(original)?;
1079+
let link = OsPathBuf::new(link)?;
10771080
cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?;
10781081
Ok(())
10791082
}
10801083

10811084
pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1082-
let original = cstr(original)?;
1083-
let link = cstr(link)?;
1085+
let original = OsPathBuf::new(original)?;
1086+
let link = OsPathBuf::new(link)?;
10841087
cfg_if::cfg_if! {
10851088
if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] {
10861089
// VxWorks, Redox, and old versions of Android lack `linkat`, so use
@@ -1099,7 +1102,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
10991102
}
11001103

11011104
pub fn stat(p: &Path) -> io::Result<FileAttr> {
1102-
let p = cstr(p)?;
1105+
let p = OsPathBuf::new(p)?;
11031106

11041107
cfg_has_statx! {
11051108
if let Some(ret) = unsafe { try_statx(
@@ -1108,17 +1111,17 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
11081111
libc::AT_STATX_SYNC_AS_STAT,
11091112
libc::STATX_ALL,
11101113
) } {
1111-
return ret;
1114+
return ret.map_err(|e| e.with_path(p));
11121115
}
11131116
}
11141117

11151118
let mut stat: stat64 = unsafe { mem::zeroed() };
1116-
cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
1119+
cvt(unsafe { stat64(p.as_ptr(), &mut stat) }).map_err(|e| e.with_path(p))?;
11171120
Ok(FileAttr::from_stat64(stat))
11181121
}
11191122

11201123
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
1121-
let p = cstr(p)?;
1124+
let p = OsPathBuf::new(p)?;
11221125

11231126
cfg_has_statx! {
11241127
if let Some(ret) = unsafe { try_statx(
@@ -1127,12 +1130,12 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
11271130
libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
11281131
libc::STATX_ALL,
11291132
) } {
1130-
return ret;
1133+
return ret.map_err(|e| e.with_path(p));
11311134
}
11321135
}
11331136

11341137
let mut stat: stat64 = unsafe { mem::zeroed() };
1135-
cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
1138+
cvt(unsafe { lstat64(p.as_ptr(), &mut stat) }).map_err(|e| e.with_path(p))?;
11361139
Ok(FileAttr::from_stat64(stat))
11371140
}
11381141

@@ -1284,7 +1287,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
12841287
// Opportunistically attempt to create a copy-on-write clone of `from`
12851288
// using `fclonefileat`.
12861289
if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
1287-
let to = cstr(to)?;
1290+
let to = OsPathBuf::new(to)?;
12881291
let clonefile_result =
12891292
cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
12901293
match clonefile_result {
@@ -1331,7 +1334,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
13311334

13321335
#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
13331336
pub fn chroot(dir: &Path) -> io::Result<()> {
1334-
let dir = cstr(dir)?;
1335-
cvt(unsafe { libc::chroot(dir.as_ptr()) })?;
1337+
let dir = OsPathBuf::new(dir)?;
1338+
cvt(unsafe { libc::chroot(dir.as_ptr()) }).map_err(|e| e.with_path(dir))?;
13361339
Ok(())
13371340
}

0 commit comments

Comments
 (0)