tempfile/file/imp/
unix.rsuse 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))
}
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;
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())?;
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) .open(dir)
.or_else(|e| {
match e.raw_os_error() {
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,
))?;
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<()> {
Err(io::Error::from_raw_os_error(syscall::ENOSYS))
}
pub fn keep(_: &Path) -> io::Result<()> {
Ok(())
}