Skip to content

Commit 663eb48

Browse files
authored
tempfile: Use O_TMPFILE | O_EXCL for new_anonymous() (#288)
The addition of `new_anonymous()` was a bit of a last minute thing when I was working on the code originally. I'd meant to have it expose the Linux semantics for `O_TMPFILE | O_EXCL`, which means the file can never be linked into place. In theory (but I don't think in practice) this could allow e.g. the underlying filesystem and storage stack to optimize things knowing that the data will not persist across a reboot for example.
1 parent 9cb3789 commit 663eb48

File tree

1 file changed

+18
-13
lines changed

1 file changed

+18
-13
lines changed

cap-tempfile/src/tempfile.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,14 @@ impl<'d> Debug for TempFile<'d> {
5454
}
5555

5656
#[cfg(any(target_os = "android", target_os = "linux"))]
57-
fn new_tempfile_linux(d: &Dir) -> io::Result<Option<File>> {
57+
fn new_tempfile_linux(d: &Dir, anonymous: bool) -> io::Result<Option<File>> {
5858
use cap_std::io_lifetimes::OwnedFd;
5959
use rustix::fs::{Mode, OFlags};
6060
// openat's API uses WRONLY. There may be use cases for reading too, so let's support it.
61-
let oflags = OFlags::CLOEXEC | OFlags::TMPFILE | OFlags::RDWR;
61+
let mut oflags = OFlags::CLOEXEC | OFlags::TMPFILE | OFlags::RDWR;
62+
if anonymous {
63+
oflags |= OFlags::EXCL;
64+
}
6265
// We default to 0o666, same as main rust when creating new files; this will be modified by
6366
// umask: https://github.com/rust-lang/rust/blob/44628f7273052d0bb8e8218518dacab210e1fe0d/library/std/src/sys/unix/fs.rs#L762
6467
let mode = Mode::from_raw_mode(0o666);
@@ -92,27 +95,33 @@ fn generate_name_in(subdir: &Dir, f: &File) -> io::Result<String> {
9295
}
9396

9497
/// Create a new temporary file in the target directory, which may or may not have a (randomly generated) name at this point.
95-
fn new_tempfile(d: &Dir) -> io::Result<(File, Option<String>)> {
98+
/// If anonymous is specified, the file will be deleted
99+
fn new_tempfile(d: &Dir, anonymous: bool) -> io::Result<(File, Option<String>)> {
96100
// On Linux, try O_TMPFILE
97101
#[cfg(any(target_os = "android", target_os = "linux"))]
98-
if let Some(f) = new_tempfile_linux(d)? {
102+
if let Some(f) = new_tempfile_linux(d, anonymous)? {
99103
return Ok((f, None));
100104
}
101105
// Otherwise, fall back to just creating a randomly named file.
102106
let mut opts = cap_std::fs::OpenOptions::new();
103107
opts.read(true);
104108
opts.write(true);
105109
opts.create_new(true);
106-
super::retry_with_name_ignoring(io::ErrorKind::AlreadyExists, |name| {
110+
let (f, name) = super::retry_with_name_ignoring(io::ErrorKind::AlreadyExists, |name| {
107111
d.open_with(name, &opts)
108-
})
109-
.map(|(f, name)| (f, Some(name)))
112+
})?;
113+
if anonymous {
114+
d.remove_file(name)?;
115+
Ok((f, None))
116+
} else {
117+
Ok((f, Some(name)))
118+
}
110119
}
111120

112121
impl<'d> TempFile<'d> {
113122
/// Crate a new temporary file in the provided directory.
114123
pub fn new(dir: &'d Dir) -> io::Result<Self> {
115-
let (fd, name) = new_tempfile(dir)?;
124+
let (fd, name) = new_tempfile(dir, false)?;
116125
Ok(Self { dir, fd, name })
117126
}
118127

@@ -121,11 +130,7 @@ impl<'d> TempFile<'d> {
121130
///
122131
/// [`tempfile::tempfile_in`]: https://docs.rs/tempfile/latest/tempfile/fn.tempfile_in.html
123132
pub fn new_anonymous(dir: &'d Dir) -> io::Result<File> {
124-
let (fd, name) = new_tempfile(dir)?;
125-
if let Some(name) = name {
126-
dir.remove_file(name)?;
127-
}
128-
Ok(fd)
133+
new_tempfile(dir, true).map(|v| v.0)
129134
}
130135

131136
/// Get a reference to the underlying file.

0 commit comments

Comments
 (0)