diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 3a652678d..f2f7f675a 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -223,6 +223,15 @@ impl From for PathOrFd { } } +impl PathOrFd { + pub fn filename(&self, vm: &VirtualMachine) -> PyObjectRef { + match self { + PathOrFd::Path(path) => path.filename(vm).unwrap_or_else(|_| vm.ctx.none()), + PathOrFd::Fd(fd) => vm.ctx.new_int(*fd), + } + } +} + impl IntoPyException for io::Error { fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef { (&self).into_pyexception(vm) @@ -257,27 +266,42 @@ impl IntoPyException for nix::Error { io::Error::from(self).into_pyexception(vm) } } - -pub struct IOErrorWithFilename { +pub struct IOErrorBuilder { error: io::Error, - filename: PyObjectRef, + filename: Option, + filename2: Option, } -impl IOErrorWithFilename { - pub(crate) fn new(error: io::Error, path: impl Into, vm: &VirtualMachine) -> Self { - let filename = match path.into() { - PathOrFd::Path(path) => path.filename(vm).unwrap_or_else(|_| vm.ctx.none()), - PathOrFd::Fd(fd) => vm.ctx.new_int(fd), - }; - IOErrorWithFilename { error, filename } +impl IOErrorBuilder { + pub fn new(error: io::Error) -> Self { + Self { + error, + filename: None, + filename2: None, + } + } + pub(crate) fn filename(mut self, filename: impl Into) -> Self { + self.filename.replace(filename.into()); + self + } + pub(crate) fn filename2(mut self, filename: impl Into) -> Self { + self.filename2.replace(filename.into()); + self } } -impl IntoPyException for IOErrorWithFilename { +impl IntoPyException for IOErrorBuilder { fn into_pyexception(self, vm: &VirtualMachine) -> PyBaseExceptionRef { let excp = self.error.into_pyexception(vm); - vm.set_attr(excp.as_object(), "filename", self.filename) - .unwrap(); + + if let Some(filename) = self.filename { + vm.set_attr(excp.as_object(), "filename", filename.filename(vm)) + .unwrap(); + } + if let Some(filename2) = self.filename2 { + vm.set_attr(excp.as_object(), "filename2", filename2.filename(vm)) + .unwrap(); + } excp } } @@ -397,8 +421,8 @@ fn bytes_as_osstr<'a>(b: &'a [u8], vm: &VirtualMachine) -> PyResult<&'a ffi::OsS #[pymodule(name = "_os")] pub(super) mod _os { use super::{ - errno_err, DirFd, FollowSymlinks, FsPath, IOErrorWithFilename, OutputMode, PathOrFd, - PyPathLike, SupportFunc, + errno_err, DirFd, FollowSymlinks, FsPath, IOErrorBuilder, OutputMode, PathOrFd, PyPathLike, + SupportFunc, }; use crate::common::lock::{OnceCell, PyRwLock}; use crate::{ @@ -506,7 +530,7 @@ pub(super) mod _os { } }; fd.map(|fd| fd.0) - .map_err(|e| IOErrorWithFilename::new(e, name, vm).into_pyexception(vm)) + .map_err(|e| IOErrorBuilder::new(e).filename(name).into_pyexception(vm)) } #[pyfunction] @@ -548,7 +572,7 @@ pub(super) mod _os { } else { fs::remove_file(&path) }; - res.map_err(|err| IOErrorWithFilename::new(err, path, vm).into_pyexception(vm)) + res.map_err(|e| IOErrorBuilder::new(e).filename(path).into_pyexception(vm)) } #[cfg(not(windows))] @@ -586,7 +610,7 @@ pub(super) mod _os { fn rmdir(path: PyPathLike, dir_fd: DirFd<0>, vm: &VirtualMachine) -> PyResult<()> { let [] = dir_fd.0; fs::remove_dir(&path) - .map_err(|err| IOErrorWithFilename::new(err, path, vm).into_pyexception(vm)) + .map_err(|e| IOErrorBuilder::new(e).filename(path).into_pyexception(vm)) } const LISTDIR_FD: bool = cfg!(all(unix, not(target_os = "redox"))); @@ -600,9 +624,9 @@ pub(super) mod _os { dir_iter .map(|entry| match entry { Ok(entry_path) => path.mode.process_path(entry_path.file_name(), vm), - Err(e) => { - Err(IOErrorWithFilename::new(e, path.clone(), vm).into_pyexception(vm)) - } + Err(e) => Err(IOErrorBuilder::new(e) + .filename(path.clone()) + .into_pyexception(vm)), }) .collect::>()? } @@ -675,7 +699,8 @@ pub(super) mod _os { fn readlink(path: PyPathLike, dir_fd: DirFd<0>, vm: &VirtualMachine) -> PyResult { let mode = path.mode; let [] = dir_fd.0; - let path = fs::read_link(path).map_err(|err| err.into_pyexception(vm))?; + let path = fs::read_link(&path) + .map_err(|err| IOErrorBuilder::new(err).filename(path).into_pyexception(vm))?; mode.process_path(path, vm) } @@ -1148,7 +1173,7 @@ pub(super) mod _os { vm: &VirtualMachine, ) -> PyResult { let stat = stat_inner(file.clone(), dir_fd, follow_symlinks) - .map_err(|e| IOErrorWithFilename::new(e, file, vm).into_pyexception(vm))? + .map_err(|e| IOErrorBuilder::new(e).filename(file).into_pyexception(vm))? .ok_or_else(|| crate::exceptions::cstring_error(vm))?; Ok(StatResult::from_stat(&stat).into_pyobject(vm)) } @@ -1187,7 +1212,7 @@ pub(super) mod _os { #[pyfunction] fn chdir(path: PyPathLike, vm: &VirtualMachine) -> PyResult<()> { env::set_current_dir(&path.path) - .map_err(|err| IOErrorWithFilename::new(err, path, vm).into_pyexception(vm)) + .map_err(|err| IOErrorBuilder::new(err).filename(path).into_pyexception(vm)) } #[pyfunction] @@ -1198,7 +1223,12 @@ pub(super) mod _os { #[pyfunction] #[pyfunction(name = "replace")] fn rename(src: PyPathLike, dst: PyPathLike, vm: &VirtualMachine) -> PyResult<()> { - fs::rename(src.path, dst.path).map_err(|err| err.into_pyexception(vm)) + fs::rename(&src.path, &dst.path).map_err(|err| { + IOErrorBuilder::new(err) + .filename(src) + .filename2(dst) + .into_pyexception(vm) + }) } #[pyfunction] @@ -1273,7 +1303,12 @@ pub(super) mod _os { #[pyfunction] fn link(src: PyPathLike, dst: PyPathLike, vm: &VirtualMachine) -> PyResult<()> { - fs::hard_link(src.path, dst.path).map_err(|err| err.into_pyexception(vm)) + fs::hard_link(&src.path, &dst.path).map_err(|err| { + IOErrorBuilder::new(err) + .filename(src) + .filename2(dst) + .into_pyexception(vm) + }) } #[derive(FromArgs)] diff --git a/vm/src/stdlib/posix.rs b/vm/src/stdlib/posix.rs index 90e3cc7f2..a121425cb 100644 --- a/vm/src/stdlib/posix.rs +++ b/vm/src/stdlib/posix.rs @@ -36,7 +36,7 @@ pub mod module { slots::SlotConstructor, stdlib::os::{ errno_err, DirFd, FollowSymlinks, PathOrFd, PyPathLike, SupportFunc, TargetIsDirectory, - _os, fs_metadata, IOErrorWithFilename, + _os, fs_metadata, IOErrorBuilder, }, utils::{Either, ToCString}, IntoPyObject, ItemProtocol, PyObjectRef, PyResult, PyValue, TryFromObject, VirtualMachine, @@ -329,7 +329,14 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] fn chroot(path: PyPathLike, vm: &VirtualMachine) -> PyResult<()> { - nix::unistd::chroot(&*path.path).map_err(|err| err.into_pyexception(vm)) + use crate::stdlib::os::IOErrorBuilder; + + nix::unistd::chroot(&*path.path).map_err(|err| { + // Use `From for io::Error` when it is available + IOErrorBuilder::new(io::Error::from_raw_os_error(err as i32)) + .filename(path) + .into_pyexception(vm) + }) } // As of now, redox does not seems to support chown command (cf. https://gitlab.redox-os.org/redox-os/coreutils , last checked on 05/07/2020) @@ -374,7 +381,8 @@ pub mod module { } .map_err(|err| { // Use `From for io::Error` when it is available - IOErrorWithFilename::new(io::Error::from_raw_os_error(err as i32), path, vm) + IOErrorBuilder::new(io::Error::from_raw_os_error(err as i32)) + .filename(path) .into_pyexception(vm) }) } @@ -705,7 +713,11 @@ pub mod module { permissions.set_mode(mode); fs::set_permissions(&path, permissions) }; - body().map_err(|err| IOErrorWithFilename::new(err, err_path, vm).into_pyexception(vm)) + body().map_err(|err| { + IOErrorBuilder::new(err) + .filename(err_path) + .into_pyexception(vm) + }) } #[pyfunction]