diff --git a/src/dir.rs b/src/dir.rs index 1b79be445..245b4b797 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -12,7 +12,7 @@ use std::ffi::OsStr; use std::fs::remove_dir_all; use std::mem; use std::path::{self, Path, PathBuf}; -use std::{fmt, fs, io}; +use std::{fmt, io}; use crate::error::IoResultExt; use crate::Builder; @@ -468,10 +468,69 @@ impl Drop for TempDir { } } -pub(crate) fn create(path: PathBuf) -> io::Result { - fs::create_dir(&path) - .with_err_path(|| &path) - .map(|_| TempDir { - path: path.into_boxed_path(), - }) +pub(crate) fn create( + path: PathBuf, + permissions: Option<&std::fs::Permissions>, +) -> io::Result { + imp::create(path, permissions) +} + +mod imp { + #[cfg(unix)] + mod unix { + use crate::error::IoResultExt; + use crate::TempDir; + use std::io; + use std::path::PathBuf; + + pub fn create( + path: PathBuf, + permissions: Option<&std::fs::Permissions>, + ) -> io::Result { + let mut dir_options = std::fs::DirBuilder::new(); + #[cfg(not(target_os = "wasi"))] + { + use std::os::unix::fs::{DirBuilderExt, PermissionsExt}; + if let Some(p) = permissions { + dir_options.mode(p.mode()); + } + } + dir_options + .create(&path) + .with_err_path(|| path.clone()) + .map(|_| TempDir { + path: path.into_boxed_path(), + }) + } + } + #[cfg(unix)] + pub use unix::*; + + #[cfg(not(unix))] + mod any { + use crate::error::IoResultExt; + use crate::TempDir; + use std::path::PathBuf; + use std::{fs, io}; + + fn not_supported(msg: &str) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, msg)) + } + + pub fn create( + path: PathBuf, + permissions: Option<&std::fs::Permissions>, + ) -> io::Result { + if permissions.map_or(false, |p| p.readonly()) { + return not_supported("changing permissions is not supported on this platform"); + } + fs::create_dir(&path) + .with_err_path(|| &path) + .map(|_| TempDir { + path: path.into_boxed_path(), + }) + } + } + #[cfg(not(unix))] + pub use any::*; } diff --git a/src/error.rs b/src/error.rs index ed6b6cc8d..40b958509 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,9 +2,9 @@ use std::path::PathBuf; use std::{error, fmt, io}; #[derive(Debug)] -struct PathError { - path: PathBuf, - err: io::Error, +pub(crate) struct PathError { + pub(crate) path: PathBuf, + pub(crate) err: io::Error, } impl fmt::Display for PathError { diff --git a/src/file/imp/unix.rs b/src/file/imp/unix.rs index 666996a63..5f2cb5dcb 100644 --- a/src/file/imp/unix.rs +++ b/src/file/imp/unix.rs @@ -83,7 +83,8 @@ fn create_unix(dir: &Path) -> io::Result { OsStr::new(".tmp"), OsStr::new(""), crate::NUM_RAND_CHARS, - |path| create_unlinked(&path), + None, + |path, _| create_unlinked(&path), ) } diff --git a/src/file/imp/windows.rs b/src/file/imp/windows.rs index e7f603843..bed62090f 100644 --- a/src/file/imp/windows.rs +++ b/src/file/imp/windows.rs @@ -45,7 +45,8 @@ pub fn create(dir: &Path) -> io::Result { OsStr::new(".tmp"), OsStr::new(""), crate::NUM_RAND_CHARS, - |path| { + None, + |path, _permissions| { OpenOptions::new() .create_new(true) .read(true) diff --git a/src/lib.rs b/src/lib.rs index 4fdf2449a..6b388f86b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -401,7 +401,7 @@ impl<'a, 'b> Builder<'a, 'b> { self } - /// The permissions to create the tempfile with. + /// The permissions to create the tempfile or [tempdir](Self::tempdir) with. /// This allows to them differ from the default mode of `0o600` on Unix. /// /// # Security @@ -415,8 +415,9 @@ impl<'a, 'b> Builder<'a, 'b> { /// # Platform Notes /// ## Unix /// - /// The actual permission bits set on the tempfile will be affected by the - /// `umask` applied by the underlying `open` syscall. + /// The actual permission bits set on the tempfile or tempdir will be affected by the + /// `umask` applied by the underlying syscall. + /// /// /// ## Windows and others /// @@ -430,6 +431,8 @@ impl<'a, 'b> Builder<'a, 'b> { /// /// # Examples /// + /// Create a named temporary file that is world-readable. + /// /// ``` /// # use std::io; /// # fn main() { @@ -454,6 +457,33 @@ impl<'a, 'b> Builder<'a, 'b> { /// # Ok(()) /// # } /// ``` + /// + /// Create a named temporary directory that is restricted to the owner. + /// + /// ``` + /// # use std::io; + /// # fn main() { + /// # if let Err(_) = run() { + /// # ::std::process::exit(1); + /// # } + /// # } + /// # fn run() -> Result<(), io::Error> { + /// # use tempfile::Builder; + /// #[cfg(unix)] + /// { + /// use std::os::unix::fs::PermissionsExt; + /// let owner_rwx = std::fs::Permissions::from_mode(0o700); + /// let tempdir = Builder::new().permissions(owner_rwx).tempdir()?; + /// let actual_permissions = tempdir.path().metadata()?.permissions(); + /// assert_eq!( + /// actual_permissions.mode() & !0o170000, + /// 0o700, + /// "we get the narrow permissions we asked for" + /// ); + /// } + /// # Ok(()) + /// # } + /// ``` pub fn permissions(&mut self, permissions: std::fs::Permissions) -> &mut Self { self.permissions = Some(permissions); self @@ -533,12 +563,9 @@ impl<'a, 'b> Builder<'a, 'b> { self.prefix, self.suffix, self.random_len, - |path| { - file::create_named( - path, - OpenOptions::new().append(self.append), - self.permissions.as_ref(), - ) + self.permissions.as_ref(), + |path, permissions| { + file::create_named(path, OpenOptions::new().append(self.append), permissions) }, ) } @@ -611,7 +638,14 @@ impl<'a, 'b> Builder<'a, 'b> { dir = &storage; } - util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create) + util::create_helper( + dir, + self.prefix, + self.suffix, + self.random_len, + self.permissions.as_ref(), + dir::create, + ) } /// Attempts to create a temporary file (or file-like object) using the @@ -756,7 +790,8 @@ impl<'a, 'b> Builder<'a, 'b> { self.prefix, self.suffix, self.random_len, - move |path| { + None, + move |path, _permissions| { Ok(NamedTempFile::from_parts( f(&path)?, TempPath::from_path(path), diff --git a/src/util.rs b/src/util.rs index d426ba3d7..8c04953a3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -20,7 +20,8 @@ pub fn create_helper( prefix: &OsStr, suffix: &OsStr, random_len: usize, - mut f: impl FnMut(PathBuf) -> io::Result, + permissions: Option<&std::fs::Permissions>, + mut f: impl FnMut(PathBuf, Option<&std::fs::Permissions>) -> io::Result, ) -> io::Result { let num_retries = if random_len != 0 { crate::NUM_RETRIES @@ -30,7 +31,7 @@ pub fn create_helper( for _ in 0..num_retries { let path = base.join(tmpname(prefix, suffix, random_len)); - return match f(path) { + return match f(path, permissions) { Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists && num_retries > 1 => continue, // AddrInUse can happen if we're creating a UNIX domain socket and // the path already exists.