From e3740914858b564590fb3d1ca698469b7b76f079 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 4 Feb 2024 21:02:04 +0100 Subject: [PATCH] Add support for setting permissions on directories as well. --- src/dir/imp/any.rs | 19 ++++++++++++ src/dir/imp/mod.rs | 9 ++++++ src/dir/imp/unix.rs | 21 +++++++++++++ src/{dir.rs => dir/mod.rs} | 15 ++++----- src/file/imp/unix.rs | 3 +- src/file/imp/windows.rs | 3 +- src/lib.rs | 62 ++++++++++++++++++++++++++++---------- src/util.rs | 5 +-- 8 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 src/dir/imp/any.rs create mode 100644 src/dir/imp/mod.rs create mode 100644 src/dir/imp/unix.rs rename src/{dir.rs => dir/mod.rs} (98%) diff --git a/src/dir/imp/any.rs b/src/dir/imp/any.rs new file mode 100644 index 000000000..015f9f87d --- /dev/null +++ b/src/dir/imp/any.rs @@ -0,0 +1,19 @@ +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(), + }) +} diff --git a/src/dir/imp/mod.rs b/src/dir/imp/mod.rs new file mode 100644 index 000000000..26d0a2273 --- /dev/null +++ b/src/dir/imp/mod.rs @@ -0,0 +1,9 @@ +#[cfg(unix)] +mod unix; +#[cfg(unix)] +pub use unix::*; + +#[cfg(not(unix))] +mod any; +#[cfg(not(unix))] +pub use any::*; diff --git a/src/dir/imp/unix.rs b/src/dir/imp/unix.rs new file mode 100644 index 000000000..47dd125c1 --- /dev/null +++ b/src/dir/imp/unix.rs @@ -0,0 +1,21 @@ +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) + .map(|_| TempDir { + path: path.into_boxed_path(), + }) +} diff --git a/src/dir.rs b/src/dir/mod.rs similarity index 98% rename from src/dir.rs rename to src/dir/mod.rs index 1b79be445..db70dd52e 100644 --- a/src/dir.rs +++ b/src/dir/mod.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,11 @@ 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; 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..2072f7a12 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,21 +415,19 @@ 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 /// /// This setting is unsupported and trying to set a file or directory read-only /// will cause an error to be returned.. /// - /// # Limitations - /// - /// Permissions for directories aren't currently set even though it would - /// be possible on Unix systems. - /// /// # Examples /// + /// Create a named temporary file that is world-readable. + /// /// ``` /// # use std::io; /// # fn main() { @@ -454,6 +452,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 +558,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 +633,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 +785,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.