2 use std
::ffi
::{CString, OsStr}
;
3 use std
::fs
::{self, File, OpenOptions}
;
5 use std
::os
::unix
::ffi
::OsStrExt
;
6 use std
::os
::unix
::fs
::{MetadataExt, OpenOptionsExt}
;
10 #[cfg(not(target_os = "redox"))]
11 use libc
::{c_char, c_int, link, rename, unlink}
;
13 #[cfg(not(target_os = "redox"))]
15 pub fn cvt_err(result
: c_int
) -> io
::Result
<c_int
> {
17 Err(io
::Error
::last_os_error())
23 #[cfg(target_os = "redox")]
25 pub fn cvt_err(result
: Result
<usize, syscall
::Error
>) -> io
::Result
<usize> {
26 result
.map_err(|err
| io
::Error
::from_raw_os_error(err
.errno
))
30 pub fn cstr(path
: &Path
) -> io
::Result
<CString
> {
31 CString
::new(path
.as_os_str().as_bytes())
32 .map_err(|_
| io
::Error
::new(io
::ErrorKind
::InvalidInput
, "path contained a null"))
35 pub fn create_named(path
: &Path
) -> io
::Result
<File
> {
44 fn create_unlinked(path
: &Path
) -> io
::Result
<File
> {
46 // shadow this to decrease the lifetime. It can't live longer than `tmp`.
48 if !path
.is_absolute() {
49 let cur_dir
= env
::current_dir()?
;
50 tmp
= cur_dir
.join(path
);
54 let f
= create_named(path
)?
;
55 // don't care whether the path has already been unlinked,
56 // but perhaps there are some IO error conditions we should send up?
57 let _
= fs
::remove_file(path
);
61 #[cfg(target_os = "linux")]
62 pub fn create(dir
: &Path
) -> io
::Result
<File
> {
63 use libc
::{EISDIR, ENOENT, EOPNOTSUPP, O_EXCL, O_TMPFILE}
;
67 .custom_flags(O_TMPFILE
| O_EXCL
) // do not mix with `create_new(true)`
70 match e
.raw_os_error() {
71 // These are the three "not supported" error codes for O_TMPFILE.
72 Some(EOPNOTSUPP
) | Some(EISDIR
) | Some(ENOENT
) => create_unix(dir
),
78 #[cfg(not(target_os = "linux"))]
79 pub fn create(dir
: &Path
) -> io
::Result
<File
> {
83 fn create_unix(dir
: &Path
) -> io
::Result
<File
> {
89 |path
| create_unlinked(&path
),
93 pub fn reopen(file
: &File
, path
: &Path
) -> io
::Result
<File
> {
94 let new_file
= OpenOptions
::new().read(true).write(true).open(path
)?
;
95 let old_meta
= file
.metadata()?
;
96 let new_meta
= new_file
.metadata()?
;
97 if old_meta
.dev() != new_meta
.dev() || old_meta
.ino() != new_meta
.ino() {
98 return Err(io
::Error
::new(
99 io
::ErrorKind
::NotFound
,
100 "original tempfile has been replaced",
106 #[cfg(not(target_os = "redox"))]
107 pub fn persist(old_path
: &Path
, new_path
: &Path
, overwrite
: bool
) -> io
::Result
<()> {
109 let old_path
= cstr(old_path
)?
;
110 let new_path
= cstr(new_path
)?
;
113 old_path
.as_ptr() as *const c_char
,
114 new_path
.as_ptr() as *const c_char
,
118 old_path
.as_ptr() as *const c_char
,
119 new_path
.as_ptr() as *const c_char
,
121 // Ignore unlink errors. Can we do better?
122 // On recent linux, we can use renameat2 to do this atomically.
123 let _
= unlink(old_path
.as_ptr() as *const c_char
);
129 #[cfg(target_os = "redox")]
130 pub fn persist(old_path
: &Path
, new_path
: &Path
, overwrite
: bool
) -> io
::Result
<()> {
131 // XXX implement when possible
132 Err(io
::Error
::from_raw_os_error(syscall
::ENOSYS
))
135 pub fn keep(_
: &Path
) -> io
::Result
<()> {