Skip to content

Commit

Permalink
Add support for setting permissions on directories as well.
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Feb 4, 2024
1 parent 7a80c77 commit 8a172f4
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 24 deletions.
81 changes: 74 additions & 7 deletions src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -468,10 +468,77 @@ impl Drop for TempDir {
}
}

pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> {
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<TempDir> {
imp::create(path, permissions)
}

mod imp {
#[cfg(unix)]
mod unix {
use crate::TempDir;
use rustix::fs::Mode;
use std::io;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;

pub fn create(
path: PathBuf,
permissions: Option<&std::fs::Permissions>,
) -> io::Result<TempDir> {
let mode: rustix::fs::Mode = permissions.map_or(
Mode::RWXU | Mode::RGRP | Mode::XGRP | Mode::ROTH | Mode::XOTH,
|p| Mode::from_bits_truncate(p.mode() as _),
);
rustix::fs::mkdir(&path, mode)
.map_err({
let path = path.clone();
|errno| {
io::Error::new(
errno.kind(),
crate::error::PathError {
path,
err: io::Error::from_raw_os_error(errno.raw_os_error()),
},
)
}
})
.map(|_| TempDir {
path: path.into_boxed_path(),
})
}
}
#[cfg(unix)]
pub use unix::*;

#[cfg(not(unix))]
mod any {
use crate::error::IoResultExt;
use crate::util::not_supported;
use crate::TempDir;
use std::path::PathBuf;
use std::{fs, io};

fn not_supported<T>(msg: &str) -> io::Result<T> {
Err(io::Error::new(io::ErrorKind::Other, msg))
}

pub fn create(
path: PathBuf,
permissions: Option<&std::fs::Permissions>,
) -> io::Result<TempDir> {
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::*;
}
6 changes: 3 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion src/file/imp/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ fn create_unix(dir: &Path) -> io::Result<File> {
OsStr::new(".tmp"),
OsStr::new(""),
crate::NUM_RAND_CHARS,
|path| create_unlinked(&path),
None,
|path, _| create_unlinked(&path),
)
}

Expand Down
1 change: 1 addition & 0 deletions src/file/imp/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub fn create(dir: &Path) -> io::Result<File> {
OsStr::new(".tmp"),
OsStr::new(""),
crate::NUM_RAND_CHARS,
None,
|path| {
OpenOptions::new()
.create_new(true)
Expand Down
57 changes: 46 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
///
Expand All @@ -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() {
Expand All @@ -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
Expand Down Expand Up @@ -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)
},
)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down
5 changes: 3 additions & 2 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ pub fn create_helper<R>(
prefix: &OsStr,
suffix: &OsStr,
random_len: usize,
mut f: impl FnMut(PathBuf) -> io::Result<R>,
permissions: Option<&std::fs::Permissions>,
mut f: impl FnMut(PathBuf, Option<&std::fs::Permissions>) -> io::Result<R>,
) -> io::Result<R> {
let num_retries = if random_len != 0 {
crate::NUM_RETRIES
Expand All @@ -30,7 +31,7 @@ pub fn create_helper<R>(

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.
Expand Down

0 comments on commit 8a172f4

Please sign in to comment.