diff options
Diffstat (limited to 'third_party/rust/tempfile/src/file/imp/unix.rs')
-rw-r--r-- | third_party/rust/tempfile/src/file/imp/unix.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/third_party/rust/tempfile/src/file/imp/unix.rs b/third_party/rust/tempfile/src/file/imp/unix.rs new file mode 100644 index 0000000000..79aba783e4 --- /dev/null +++ b/third_party/rust/tempfile/src/file/imp/unix.rs @@ -0,0 +1,149 @@ +use std::env; +use std::ffi::OsStr; +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}; + } else { + #[cfg(feature = "nightly")] + use std::os::wasi::fs::MetadataExt; + } +} +use crate::util; +use std::path::Path; + +#[cfg(not(target_os = "redox"))] +use { + rustix::fs::{rename, unlink}, + std::fs::hard_link, +}; + +pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> { + open_options.read(true).write(true).create_new(true); + + #[cfg(not(target_os = "wasi"))] + { + open_options.mode(0o600); + } + + open_options.open(path) +} + +fn create_unlinked(path: &Path) -> io::Result<File> { + let tmp; + // shadow this to decrease the lifetime. It can't live longer than `tmp`. + let mut path = path; + if !path.is_absolute() { + let cur_dir = env::current_dir()?; + tmp = cur_dir.join(path); + path = &tmp; + } + + let f = create_named(path, &mut OpenOptions::new())?; + // 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); + Ok(f) +} + +#[cfg(target_os = "linux")] +pub fn create(dir: &Path) -> io::Result<File> { + use rustix::{fs::OFlags, io::Errno}; + OpenOptions::new() + .read(true) + .write(true) + .custom_flags(OFlags::TMPFILE.bits() as i32) // do not mix with `create_new(true)` + .open(dir) + .or_else(|e| { + match Errno::from_io_error(&e) { + // These are the three "not supported" error codes for O_TMPFILE. + Some(Errno::OPNOTSUPP) | Some(Errno::ISDIR) | Some(Errno::NOENT) => { + create_unix(dir) + } + _ => Err(e), + } + }) +} + +#[cfg(not(target_os = "linux"))] +pub fn create(dir: &Path) -> io::Result<File> { + create_unix(dir) +} + +fn create_unix(dir: &Path) -> io::Result<File> { + util::create_helper( + dir, + OsStr::new(".tmp"), + OsStr::new(""), + crate::NUM_RAND_CHARS, + |path| create_unlinked(&path), + ) +} + +#[cfg(any(not(target_os = "wasi"), feature = "nightly"))] +pub fn reopen(file: &File, path: &Path) -> io::Result<File> { + let new_file = OpenOptions::new().read(true).write(true).open(path)?; + let old_meta = file.metadata()?; + let new_meta = new_file.metadata()?; + if old_meta.dev() != new_meta.dev() || old_meta.ino() != new_meta.ino() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "original tempfile has been replaced", + )); + } + Ok(new_file) +} + +#[cfg(all(target_os = "wasi", not(feature = "nightly")))] +pub fn reopen(_file: &File, _path: &Path) -> io::Result<File> { + return Err(io::Error::new( + io::ErrorKind::Other, + "this operation is supported on WASI only on nightly Rust (with `nightly` feature enabled)", + )); +} + +#[cfg(not(target_os = "redox"))] +pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { + if overwrite { + rename(old_path, new_path)?; + } else { + // On Linux, use `renameat_with` to avoid overwriting an existing name, + // if the kernel and the filesystem support it. + #[cfg(any(target_os = "android", target_os = "linux"))] + { + use rustix::fs::{renameat_with, RenameFlags, CWD}; + use rustix::io::Errno; + use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; + + static NOSYS: AtomicBool = AtomicBool::new(false); + if !NOSYS.load(Relaxed) { + match renameat_with(CWD, old_path, CWD, new_path, RenameFlags::NOREPLACE) { + Ok(()) => return Ok(()), + Err(Errno::NOSYS) => NOSYS.store(true, Relaxed), + Err(Errno::INVAL) => {} + Err(e) => return Err(e.into()), + } + } + } + + // Otherwise use `hard_link` to create the new filesystem name, which + // will fail if the name already exists, and then `unlink` to remove + // the old name. + hard_link(old_path, new_path)?; + + // Ignore unlink errors. Can we do better? + let _ = unlink(old_path); + } + Ok(()) +} + +#[cfg(target_os = "redox")] +pub fn persist(_old_path: &Path, _new_path: &Path, _overwrite: bool) -> io::Result<()> { + // XXX implement when possible + Err(io::Error::from_raw_os_error(syscall::ENOSYS)) +} + +pub fn keep(_: &Path) -> io::Result<()> { + Ok(()) +} |