1 #[cfg(not(target_os = "redox"))]
2 use libc
::{c_char, c_int, link, rename, unlink, O_CLOEXEC, O_CREAT, O_EXCL, O_RDWR}
;
4 use std
::fs
::{self, File, OpenOptions}
;
6 use std
::os
::unix
::ffi
::OsStrExt
;
7 use std
::os
::unix
::io
::{AsRawFd, FromRawFd, RawFd}
;
11 #[cfg(all(lfs_support, target_os = "linux"))]
12 use libc
::{fstat64 as fstat, open64 as open, stat64 as stat_t}
;
14 #[cfg(not(any(all(lfs_support, target_os = "linux"), target_os = "redox")))]
15 use libc
::{fstat, open, stat as stat_t}
;
17 #[cfg(target_os = "redox")]
18 use syscall
::{self, fstat, open, Stat as stat_t, O_CLOEXEC, O_CREAT, O_EXCL, O_RDWR}
;
20 #[cfg(not(target_os = "redox"))]
22 pub fn cvt_err(result
: c_int
) -> io
::Result
<c_int
> {
24 Err(io
::Error
::last_os_error())
30 #[cfg(target_os = "redox")]
32 pub fn cvt_err(result
: Result
<usize, syscall
::Error
>) -> io
::Result
<usize> {
33 result
.map_err(|err
| io
::Error
::from_raw_os_error(err
.errno
))
37 pub fn cstr(path
: &Path
) -> io
::Result
<CString
> {
38 CString
::new(path
.as_os_str().as_bytes())
39 .map_err(|_
| io
::Error
::new(io
::ErrorKind
::InvalidInput
, "path contained a null"))
42 #[cfg(not(target_os = "redox"))]
43 pub fn create_named(path
: &Path
) -> io
::Result
<File
> {
45 let path
= cstr(path
)?
;
46 let fd
= cvt_err(open(
47 path
.as_ptr() as *const c_char
,
48 O_CLOEXEC
| O_EXCL
| O_RDWR
| O_CREAT
,
51 Ok(FromRawFd
::from_raw_fd(fd
))
55 #[cfg(target_os = "redox")]
56 pub fn create_named(path
: PathBuf
) -> io
::Result
<File
> {
58 let fd
= cvt_err(open(
59 path
.as_os_str().as_bytes(),
60 O_CLOEXEC
| O_EXCL
| O_RDWR
| O_CREAT
| 0o600,
62 Ok(FromRawFd
::from_raw_fd(fd
))
66 fn create_unlinked(path
: &Path
) -> io
::Result
<File
> {
67 let f
= create_named(path
)?
;
68 // don't care whether the path has already been unlinked,
69 // but perhaps there are some IO error conditions we should send up?
70 let _
= fs
::remove_file(path
);
74 #[cfg(target_os = "linux")]
75 pub fn create(dir
: &Path
) -> io
::Result
<File
> {
78 let path
= cstr(dir
)?
;
80 path
.as_ptr() as *const c_char
,
81 O_CLOEXEC
| O_EXCL
| O_TMPFILE
| O_RDWR
,
85 -1 => create_unix(dir
),
86 fd
=> Ok(unsafe { FromRawFd::from_raw_fd(fd) }
),
90 #[cfg(not(target_os = "linux"))]
91 pub fn create(dir
: &Path
) -> io
::Result
<File
> {
95 fn create_unix(dir
: &Path
) -> io
::Result
<File
> {
96 util
::create_helper(dir
, ".tmp", "", ::NUM_RAND_CHARS
, |path
| {
97 create_unlinked(&path
)
101 unsafe fn stat(fd
: RawFd
) -> io
::Result
<stat_t
> {
102 let mut meta
: stat_t
= ::std
::mem
::zeroed();
103 cvt_err(fstat(fd
, &mut meta
))?
;
107 pub fn reopen(file
: &File
, path
: &Path
) -> io
::Result
<File
> {
108 let new_file
= OpenOptions
::new().read(true).write(true).open(path
)?
;
110 let old_meta
= stat(file
.as_raw_fd())?
;
111 let new_meta
= stat(new_file
.as_raw_fd())?
;
112 if old_meta
.st_dev
!= new_meta
.st_dev
|| old_meta
.st_ino
!= new_meta
.st_ino
{
113 return Err(io
::Error
::new(
114 io
::ErrorKind
::NotFound
,
115 "original tempfile has been replaced",
122 #[cfg(not(target_os = "redox"))]
123 pub fn persist(old_path
: &Path
, new_path
: &Path
, overwrite
: bool
) -> io
::Result
<()> {
125 let old_path
= cstr(old_path
)?
;
126 let new_path
= cstr(new_path
)?
;
129 old_path
.as_ptr() as *const c_char
,
130 new_path
.as_ptr() as *const c_char
,
134 old_path
.as_ptr() as *const c_char
,
135 new_path
.as_ptr() as *const c_char
,
137 // Ignore unlink errors. Can we do better?
138 // On recent linux, we can use renameat2 to do this atomically.
139 let _
= unlink(old_path
.as_ptr() as *const c_char
);
145 #[cfg(target_os = "redox")]
146 pub fn persist(old_path
: &Path
, new_path
: &Path
, overwrite
: bool
) -> io
::Result
<()> {
147 // XXX implement when possible
148 Err(io
::Error
::from_raw_os_error(syscall
::ENOSYS
))