diff options
Diffstat (limited to 'vendor/tempfile/src/file/imp')
-rw-r--r-- | vendor/tempfile/src/file/imp/mod.rs | 12 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/other.rs | 30 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/unix.rs | 137 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/windows.rs | 108 |
4 files changed, 287 insertions, 0 deletions
diff --git a/vendor/tempfile/src/file/imp/mod.rs b/vendor/tempfile/src/file/imp/mod.rs new file mode 100644 index 000000000..31e872886 --- /dev/null +++ b/vendor/tempfile/src/file/imp/mod.rs @@ -0,0 +1,12 @@ +cfg_if! { + if #[cfg(any(unix, target_os = "redox"))] { + mod unix; + pub use self::unix::*; + } else if #[cfg(windows)] { + mod windows; + pub use self::windows::*; + } else { + mod other; + pub use self::other::*; + } +} diff --git a/vendor/tempfile/src/file/imp/other.rs b/vendor/tempfile/src/file/imp/other.rs new file mode 100644 index 000000000..d8a55a745 --- /dev/null +++ b/vendor/tempfile/src/file/imp/other.rs @@ -0,0 +1,30 @@ +use std::fs::{File, OpenOptions}; +use std::io; +use std::path::Path; + +fn not_supported<T>() -> io::Result<T> { + Err(io::Error::new( + io::ErrorKind::Other, + "operation not supported on this platform", + )) +} + +pub fn create_named(_path: &Path, open_options: &mut OpenOptions) -> io::Result<File> { + not_supported() +} + +pub fn create(_dir: &Path) -> io::Result<File> { + not_supported() +} + +pub fn reopen(_file: &File, _path: &Path) -> io::Result<File> { + not_supported() +} + +pub fn persist(_old_path: &Path, _new_path: &Path, _overwrite: bool) -> io::Result<()> { + not_supported() +} + +pub fn keep(path: &Path) -> io::Result<()> { + not_supported() +} diff --git a/vendor/tempfile/src/file/imp/unix.rs b/vendor/tempfile/src/file/imp/unix.rs new file mode 100644 index 000000000..35b1ddb1e --- /dev/null +++ b/vendor/tempfile/src/file/imp/unix.rs @@ -0,0 +1,137 @@ +use std::env; +use std::ffi::{CString, OsStr}; +use std::fs::{self, File, OpenOptions}; +use std::io; +use std::os::unix::ffi::OsStrExt; +use std::os::unix::fs::{MetadataExt, OpenOptionsExt}; +use std::path::Path; +use crate::util; + +#[cfg(not(target_os = "redox"))] +use libc::{c_char, c_int, link, rename, unlink}; + +#[cfg(not(target_os = "redox"))] +#[inline(always)] +pub fn cvt_err(result: c_int) -> io::Result<c_int> { + if result == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(result) + } +} + +#[cfg(target_os = "redox")] +#[inline(always)] +pub fn cvt_err(result: Result<usize, syscall::Error>) -> io::Result<usize> { + result.map_err(|err| io::Error::from_raw_os_error(err.errno)) +} + +// Stolen from std. +pub fn cstr(path: &Path) -> io::Result<CString> { + CString::new(path.as_os_str().as_bytes()) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contained a null")) +} + +pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> { + open_options + .read(true) + .write(true) + .create_new(true) + .mode(0o600) + .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 libc::{EISDIR, ENOENT, EOPNOTSUPP, O_EXCL, O_TMPFILE}; + OpenOptions::new() + .read(true) + .write(true) + .custom_flags(O_TMPFILE | O_EXCL) // do not mix with `create_new(true)` + .open(dir) + .or_else(|e| { + match e.raw_os_error() { + // These are the three "not supported" error codes for O_TMPFILE. + Some(EOPNOTSUPP) | Some(EISDIR) | Some(ENOENT) => 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), + ) +} + +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(not(target_os = "redox"))] +pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { + unsafe { + let old_path = cstr(old_path)?; + let new_path = cstr(new_path)?; + if overwrite { + cvt_err(rename( + old_path.as_ptr() as *const c_char, + new_path.as_ptr() as *const c_char, + ))?; + } else { + cvt_err(link( + old_path.as_ptr() as *const c_char, + new_path.as_ptr() as *const c_char, + ))?; + // Ignore unlink errors. Can we do better? + // On recent linux, we can use renameat2 to do this atomically. + let _ = unlink(old_path.as_ptr() as *const c_char); + } + 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(()) +} diff --git a/vendor/tempfile/src/file/imp/windows.rs b/vendor/tempfile/src/file/imp/windows.rs new file mode 100644 index 000000000..71b474880 --- /dev/null +++ b/vendor/tempfile/src/file/imp/windows.rs @@ -0,0 +1,108 @@ +use std::ffi::OsStr; +use std::fs::{File, OpenOptions}; +use std::os::windows::ffi::OsStrExt; +use std::os::windows::fs::OpenOptionsExt; +use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle}; +use std::path::Path; +use std::{io, iter}; + +use winapi::um::fileapi::SetFileAttributesW; +use winapi::um::handleapi::INVALID_HANDLE_VALUE; +use winapi::um::winbase::{MoveFileExW, ReOpenFile}; +use winapi::um::winbase::{FILE_FLAG_DELETE_ON_CLOSE, MOVEFILE_REPLACE_EXISTING}; +use winapi::um::winnt::{FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY}; +use winapi::um::winnt::{FILE_GENERIC_READ, FILE_GENERIC_WRITE, HANDLE}; +use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; + +use crate::util; + +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> { + open_options + .create_new(true) + .read(true) + .write(true) + .custom_flags(FILE_ATTRIBUTE_TEMPORARY) + .open(path) +} + +pub fn create(dir: &Path) -> io::Result<File> { + util::create_helper( + dir, + OsStr::new(".tmp"), + OsStr::new(""), + crate::NUM_RAND_CHARS, + |path| { + OpenOptions::new() + .create_new(true) + .read(true) + .write(true) + .share_mode(0) + .custom_flags(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE) + .open(path) + }, + ) +} + +pub fn reopen(file: &File, _path: &Path) -> io::Result<File> { + let handle = file.as_raw_handle(); + unsafe { + let handle = ReOpenFile( + handle as HANDLE, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + ); + if handle == INVALID_HANDLE_VALUE { + Err(io::Error::last_os_error()) + } else { + Ok(FromRawHandle::from_raw_handle(handle as RawHandle)) + } + } +} + +pub fn keep(path: &Path) -> io::Result<()> { + unsafe { + let path_w = to_utf16(path); + if SetFileAttributesW(path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } +} + +pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { + // TODO: We should probably do this in one-shot using SetFileInformationByHandle but the API is + // really painful. + + unsafe { + let old_path_w = to_utf16(old_path); + let new_path_w = to_utf16(new_path); + + // Don't succeed if this fails. We don't want to claim to have successfully persisted a file + // still marked as temporary because this file won't have the same consistency guarantees. + if SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 { + return Err(io::Error::last_os_error()); + } + + let mut flags = 0; + + if overwrite { + flags |= MOVEFILE_REPLACE_EXISTING; + } + + if MoveFileExW(old_path_w.as_ptr(), new_path_w.as_ptr(), flags) == 0 { + let e = io::Error::last_os_error(); + // If this fails, the temporary file is now un-hidden and no longer marked temporary + // (slightly less efficient) but it will still work. + let _ = SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_TEMPORARY); + Err(e) + } else { + Ok(()) + } + } +} |