tempfile_fast/
persistable.rs1use std::fmt;
2use std::fs;
3use std::io;
4use std::io::Read;
5use std::io::Seek;
6use std::io::SeekFrom;
7use std::io::Write;
8use std::ops::Deref;
9use std::ops::DerefMut;
10use std::path::Path;
11
12use rand::RngCore;
13
14use crate::linux;
15use tempfile;
16
17pub enum PersistableTempFile {
19 Linux(fs::File),
20 Fallback(tempfile::NamedTempFile),
21}
22
23use self::PersistableTempFile::*;
24
25impl PersistableTempFile {
26 pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<PersistableTempFile> {
32 if let Ok(file) = linux::create_nonexclusive_tempfile_in(&dir) {
33 return Ok(Linux(file));
34 }
35
36 Ok(Fallback(tempfile::Builder::new().tempfile_in(dir)?))
37 }
38}
39
40impl AsRef<fs::File> for PersistableTempFile {
41 #[inline]
42 fn as_ref(&self) -> &fs::File {
43 match *self {
44 Linux(ref file) => file,
45 Fallback(ref named) => named.as_file(),
46 }
47 }
48}
49
50impl AsMut<fs::File> for PersistableTempFile {
51 #[inline]
52 fn as_mut(&mut self) -> &mut fs::File {
53 match *self {
54 Linux(ref mut file) => file,
55 Fallback(ref mut named) => named.as_file_mut(),
56 }
57 }
58}
59
60impl Deref for PersistableTempFile {
61 type Target = fs::File;
62 #[inline]
63 fn deref(&self) -> &fs::File {
64 self.as_ref()
65 }
66}
67
68impl DerefMut for PersistableTempFile {
69 #[inline]
70 fn deref_mut(&mut self) -> &mut fs::File {
71 self.as_mut()
72 }
73}
74
75impl fmt::Debug for PersistableTempFile {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 write!(
78 f,
79 "PersistableTempFile::{}",
80 match *self {
81 Linux(_) => "Linux",
82 Fallback(_) => "Fallback",
83 }
84 )
85 }
86}
87
88impl Read for PersistableTempFile {
89 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
90 self.as_mut().read(buf)
91 }
92}
93
94impl Write for PersistableTempFile {
95 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
96 self.as_mut().write(buf)
97 }
98
99 fn flush(&mut self) -> io::Result<()> {
100 self.as_mut().flush()
101 }
102}
103
104impl Seek for PersistableTempFile {
105 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
106 self.as_mut().seek(pos)
107 }
108}
109
110impl<'a> Read for &'a PersistableTempFile {
111 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
112 self.as_ref().read(buf)
113 }
114}
115
116impl<'a> Write for &'a PersistableTempFile {
117 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
118 self.as_ref().write(buf)
119 }
120
121 fn flush(&mut self) -> io::Result<()> {
122 self.as_ref().flush()
123 }
124}
125
126impl<'a> Seek for &'a PersistableTempFile {
127 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
128 self.as_ref().seek(pos)
129 }
130}
131
132#[cfg(unix)]
133impl ::std::os::unix::io::AsRawFd for PersistableTempFile {
134 #[inline]
135 fn as_raw_fd(&self) -> ::std::os::unix::io::RawFd {
136 self.as_ref().as_raw_fd()
137 }
138}
139
140#[cfg(windows)]
141impl ::std::os::windows::io::AsRawHandle for PersistableTempFile {
142 #[inline]
143 fn as_raw_handle(&self) -> ::std::os::windows::io::RawHandle {
144 self.as_ref().as_raw_handle()
145 }
146}
147
148#[derive(Debug)]
150pub struct PersistError {
151 pub error: io::Error,
153 pub file: PersistableTempFile,
155}
156
157impl PersistError {
158 fn new(error: io::Error, file: fs::File) -> PersistError {
159 PersistError {
160 error,
161 file: PersistableTempFile::Linux(file),
162 }
163 }
164}
165
166impl From<tempfile::PersistError> for PersistError {
167 fn from(e: tempfile::PersistError) -> Self {
168 PersistError {
169 error: e.error,
170 file: PersistableTempFile::Fallback(e.file),
171 }
172 }
173}
174
175impl PersistableTempFile {
176 pub fn persist_noclobber<P: AsRef<Path>>(self, dest: P) -> Result<(), PersistError> {
184 match self {
185 Linux(mut file) => {
186 if let Err(error) = file.flush() {
187 return Err(PersistError::new(error, file));
188 }
189 linux::link_at(&file, dest).map_err(|error| PersistError::new(error, file))
190 }
191 Fallback(named) => named
192 .persist_noclobber(dest)
193 .map(|_| ())
194 .map_err(PersistError::from),
195 }
196 }
197
198 pub fn persist_by_rename<P: AsRef<Path>>(self, dest: P) -> Result<(), PersistError> {
209 let mut file = match self {
210 Linux(file) => file,
211 Fallback(named) => return named.persist(dest).map(|_| ()).map_err(PersistError::from),
212 };
213
214 if let Err(error) = file.flush() {
215 return Err(PersistError::new(error, file));
216 }
217
218 if linux::link_at(&file, &dest).is_ok() {
219 return Ok(());
220 };
221
222 let mut dest_tmp = dest.as_ref().to_path_buf();
223 let mut rng = ::rand::thread_rng();
224
225 dest_tmp.pop();
227
228 for _ in 0..32768 {
229 dest_tmp.push(format!(".{:x}.tmp", rng.next_u64()));
231
232 match linux::link_at(&file, &dest_tmp) {
233 Ok(()) => {
234 return fs::rename(&dest_tmp, dest).map_err(|error| {
237 let _ = fs::remove_file(&dest_tmp);
240
241 PersistError::new(error, file)
242 });
243 }
244 Err(error) => {
245 if io::ErrorKind::AlreadyExists != error.kind() {
246 return Err(PersistError::new(error, file));
247 }
248 }
249 };
250 dest_tmp.pop();
251 }
252
253 Err(PersistError::new(
254 io::Error::new(io::ErrorKind::Other, "couldn't create temporary file"),
255 file,
256 ))
257 }
258}