Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Builder::permissions() method. #273

Merged
merged 5 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/file/imp/other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ fn not_supported<T>() -> io::Result<T> {
))
}

pub fn create_named(_path: &Path, _open_options: &mut OpenOptions) -> io::Result<File> {
pub fn create_named(
_path: &Path,
_open_options: &mut OpenOptions,
_permissions: Option<&std::fs::Permissions>,
) -> io::Result<File> {
not_supported()
}

Expand Down
14 changes: 10 additions & 4 deletions src/file/imp/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fs::{self, File, OpenOptions};
use std::io;
cfg_if::cfg_if! {
if #[cfg(not(target_os = "wasi"))] {
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
use std::os::unix::fs::MetadataExt;
} else {
#[cfg(feature = "nightly")]
use std::os::wasi::fs::MetadataExt;
Expand All @@ -19,12 +19,17 @@ use {
std::fs::hard_link,
};

pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
pub fn create_named(
path: &Path,
open_options: &mut OpenOptions,
#[cfg_attr(target_os = "wasi", allow(unused))] permissions: Option<&std::fs::Permissions>,
) -> io::Result<File> {
open_options.read(true).write(true).create_new(true);

#[cfg(not(target_os = "wasi"))]
{
open_options.mode(0o600);
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
open_options.mode(permissions.map(|p| p.mode()).unwrap_or(0o600));
}

open_options.open(path)
Expand All @@ -40,7 +45,7 @@ fn create_unlinked(path: &Path) -> io::Result<File> {
path = &tmp;
}

let f = create_named(path, &mut OpenOptions::new())?;
let f = create_named(path, &mut OpenOptions::new(), None)?;
// don't care whether the path has already been unlinked,
// but perhaps there are some IO error conditions we should send up?
let _ = fs::remove_file(path);
Expand All @@ -50,6 +55,7 @@ fn create_unlinked(path: &Path) -> io::Result<File> {
#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
use rustix::{fs::OFlags, io::Errno};
use std::os::unix::fs::OpenOptionsExt;
OpenOptions::new()
.read(true)
.write(true)
Expand Down
6 changes: 5 additions & 1 deletion src/file/imp/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ fn to_utf16(s: &Path) -> Vec<u16> {
s.as_os_str().encode_wide().chain(iter::once(0)).collect()
}

pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
pub fn create_named(
path: &Path,
open_options: &mut OpenOptions,
_permissions: Option<&std::fs::Permissions>,
) -> io::Result<File> {
open_options
.create_new(true)
.read(true)
Expand Down
3 changes: 2 additions & 1 deletion src/file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1107,13 +1107,14 @@ impl<F: AsRawHandle> AsRawHandle for NamedTempFile<F> {
pub(crate) fn create_named(
mut path: PathBuf,
open_options: &mut OpenOptions,
permissions: Option<&std::fs::Permissions>,
) -> io::Result<NamedTempFile> {
// Make the path absolute. Otherwise, changing directories could cause us to
// delete the wrong file.
if !path.is_absolute() {
path = env::current_dir()?.join(path)
}
imp::create_named(&path, open_options)
imp::create_named(&path, open_options, permissions)
.with_err_path(|| path.clone())
.map(|file| NamedTempFile {
path: TempPath {
Expand Down
67 changes: 66 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ pub struct Builder<'a, 'b> {
prefix: &'a OsStr,
suffix: &'b OsStr,
append: bool,
permissions: Option<std::fs::Permissions>,
}

impl<'a, 'b> Default for Builder<'a, 'b> {
Expand All @@ -206,6 +207,7 @@ impl<'a, 'b> Default for Builder<'a, 'b> {
prefix: OsStr::new(".tmp"),
suffix: OsStr::new(""),
append: false,
permissions: None,
}
}
}
Expand Down Expand Up @@ -399,6 +401,63 @@ impl<'a, 'b> Builder<'a, 'b> {
self
}

/// The permissions to create the tempfile with.
/// This allows to them differ from the default mode of `0o600` on Unix.
///
/// # Security
///
/// By default, the permissions of tempfiles on unix are set for it to be
/// readable and writable by the owner only, yielding the greatest amount
/// of security.
/// As this method allows to widen the permissions, security would be
/// reduced in such cases.
///
/// # Platform Notes
/// ## Unix
///
/// The actual permission bits set on the tempfile will be affected by the
/// `umask` applied by the underlying `open` syscall.
///
/// ## Windows and others
///
/// This setting is ignored.
Stebalien marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Limitations
///
/// Permissions for directories aren't currently set even though it would
/// be possible on Unix systems.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to implement this before we merge this patch, unfortunately. This builder is used to create temporary directories as well, and I don't really trust users to read the docs.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the discussion in #61.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a look at this and saw that std::fs::create_dir() is used to create the directory, I don't think the standard library has a way to handle this.
Did you have an idea on how to do this? Maybe a rustix chmod call?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to use rustix::fs::mkdir.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is now out of date.

///
/// # Examples
///
/// ```
/// # 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 all_read_write = std::fs::Permissions::from_mode(0o666);
/// let tempfile = Builder::new().permissions(all_read_write).tempfile()?;
/// let actual_permissions = tempfile.path().metadata()?.permissions();
/// assert_ne!(
/// actual_permissions.mode() & !0o170000,
/// 0o600,
/// "we get broader permissions than the default despite umask"
/// );
/// }
/// # Ok(())
/// # }
/// ```
pub fn permissions(&mut self, permissions: std::fs::Permissions) -> &mut Self {
self.permissions = Some(permissions);
self
}

/// Create the named temporary file.
///
/// # Security
Expand Down Expand Up @@ -473,7 +532,13 @@ impl<'a, 'b> Builder<'a, 'b> {
self.prefix,
self.suffix,
self.random_len,
|path| file::create_named(path, OpenOptions::new().append(self.append)),
|path| {
file::create_named(
path,
OpenOptions::new().append(self.append),
self.permissions.as_ref(),
)
},
)
}

Expand Down