]>
Commit | Line | Data |
---|---|---|
532ac7d7 XL |
1 | use crate::os::windows::prelude::*; |
2 | ||
3 | use crate::ffi::OsString; | |
4 | use crate::fmt; | |
60c5eb7d | 5 | use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; |
532ac7d7 | 6 | use crate::mem; |
94222f64 | 7 | use crate::os::windows::io::{AsHandle, BorrowedHandle}; |
532ac7d7 XL |
8 | use crate::path::{Path, PathBuf}; |
9 | use crate::ptr; | |
10 | use crate::slice; | |
11 | use crate::sync::Arc; | |
12 | use crate::sys::handle::Handle; | |
13 | use crate::sys::time::SystemTime; | |
14 | use crate::sys::{c, cvt}; | |
94222f64 | 15 | use crate::sys_common::{AsInner, FromInner, IntoInner}; |
92a42be0 SL |
16 | |
17 | use super::to_u16s; | |
85aaf69f | 18 | |
60c5eb7d XL |
19 | pub struct File { |
20 | handle: Handle, | |
21 | } | |
d9579d0f | 22 | |
b039eaaf | 23 | #[derive(Clone)] |
d9579d0f | 24 | pub struct FileAttr { |
7453a54e SL |
25 | attributes: c::DWORD, |
26 | creation_time: c::FILETIME, | |
27 | last_access_time: c::FILETIME, | |
28 | last_write_time: c::FILETIME, | |
29 | file_size: u64, | |
92a42be0 | 30 | reparse_tag: c::DWORD, |
416331ca XL |
31 | volume_serial_number: Option<u32>, |
32 | number_of_links: Option<u32>, | |
33 | file_index: Option<u64>, | |
d9579d0f AL |
34 | } |
35 | ||
5bcae85e | 36 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
0531ce1d XL |
37 | pub struct FileType { |
38 | attributes: c::DWORD, | |
39 | reparse_tag: c::DWORD, | |
d9579d0f | 40 | } |
85aaf69f SL |
41 | |
42 | pub struct ReadDir { | |
c34b1796 AL |
43 | handle: FindNextFileHandle, |
44 | root: Arc<PathBuf>, | |
92a42be0 | 45 | first: Option<c::WIN32_FIND_DATAW>, |
85aaf69f SL |
46 | } |
47 | ||
92a42be0 | 48 | struct FindNextFileHandle(c::HANDLE); |
c34b1796 AL |
49 | |
50 | unsafe impl Send for FindNextFileHandle {} | |
51 | unsafe impl Sync for FindNextFileHandle {} | |
52 | ||
53 | pub struct DirEntry { | |
54 | root: Arc<PathBuf>, | |
92a42be0 | 55 | data: c::WIN32_FIND_DATAW, |
c34b1796 | 56 | } |
85aaf69f | 57 | |
32a655c1 | 58 | #[derive(Clone, Debug)] |
85aaf69f | 59 | pub struct OpenOptions { |
7453a54e | 60 | // generic |
85aaf69f SL |
61 | read: bool, |
62 | write: bool, | |
7453a54e | 63 | append: bool, |
85aaf69f | 64 | truncate: bool, |
7453a54e SL |
65 | create: bool, |
66 | create_new: bool, | |
67 | // system-specific | |
68 | custom_flags: u32, | |
69 | access_mode: Option<c::DWORD>, | |
70 | attributes: c::DWORD, | |
71 | share_mode: c::DWORD, | |
72 | security_qos_flags: c::DWORD, | |
73 | security_attributes: usize, // FIXME: should be a reference | |
85aaf69f SL |
74 | } |
75 | ||
76 | #[derive(Clone, PartialEq, Eq, Debug)] | |
60c5eb7d XL |
77 | pub struct FilePermissions { |
78 | attrs: c::DWORD, | |
79 | } | |
85aaf69f | 80 | |
32a655c1 | 81 | #[derive(Debug)] |
d9579d0f AL |
82 | pub struct DirBuilder; |
83 | ||
c30ab7b3 | 84 | impl fmt::Debug for ReadDir { |
532ac7d7 | 85 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
c30ab7b3 SL |
86 | // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. |
87 | // Thus the result will be e g 'ReadDir("C:\")' | |
88 | fmt::Debug::fmt(&*self.root, f) | |
89 | } | |
90 | } | |
91 | ||
85aaf69f SL |
92 | impl Iterator for ReadDir { |
93 | type Item = io::Result<DirEntry>; | |
94 | fn next(&mut self) -> Option<io::Result<DirEntry>> { | |
95 | if let Some(first) = self.first.take() { | |
96 | if let Some(e) = DirEntry::new(&self.root, &first) { | |
97 | return Some(Ok(e)); | |
98 | } | |
99 | } | |
100 | unsafe { | |
101 | let mut wfd = mem::zeroed(); | |
102 | loop { | |
92a42be0 SL |
103 | if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { |
104 | if c::GetLastError() == c::ERROR_NO_MORE_FILES { | |
60c5eb7d | 105 | return None; |
85aaf69f | 106 | } else { |
60c5eb7d | 107 | return Some(Err(Error::last_os_error())); |
85aaf69f SL |
108 | } |
109 | } | |
110 | if let Some(e) = DirEntry::new(&self.root, &wfd) { | |
60c5eb7d | 111 | return Some(Ok(e)); |
85aaf69f SL |
112 | } |
113 | } | |
114 | } | |
115 | } | |
116 | } | |
117 | ||
c34b1796 | 118 | impl Drop for FindNextFileHandle { |
85aaf69f | 119 | fn drop(&mut self) { |
92a42be0 | 120 | let r = unsafe { c::FindClose(self.0) }; |
85aaf69f SL |
121 | debug_assert!(r != 0); |
122 | } | |
123 | } | |
124 | ||
125 | impl DirEntry { | |
92a42be0 | 126 | fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> { |
5bcae85e | 127 | match &wfd.cFileName[0..3] { |
85aaf69f | 128 | // check for '.' and '..' |
60c5eb7d | 129 | &[46, 0, ..] | &[46, 46, 0, ..] => return None, |
85aaf69f SL |
130 | _ => {} |
131 | } | |
132 | ||
60c5eb7d | 133 | Some(DirEntry { root: root.clone(), data: *wfd }) |
85aaf69f SL |
134 | } |
135 | ||
136 | pub fn path(&self) -> PathBuf { | |
d9579d0f AL |
137 | self.root.join(&self.file_name()) |
138 | } | |
139 | ||
140 | pub fn file_name(&self) -> OsString { | |
c34b1796 | 141 | let filename = super::truncate_utf16_at_nul(&self.data.cFileName); |
d9579d0f AL |
142 | OsString::from_wide(filename) |
143 | } | |
144 | ||
145 | pub fn file_type(&self) -> io::Result<FileType> { | |
60c5eb7d XL |
146 | Ok(FileType::new( |
147 | self.data.dwFileAttributes, | |
148 | /* reparse_tag = */ self.data.dwReserved0, | |
149 | )) | |
d9579d0f AL |
150 | } |
151 | ||
152 | pub fn metadata(&self) -> io::Result<FileAttr> { | |
153 | Ok(FileAttr { | |
7453a54e SL |
154 | attributes: self.data.dwFileAttributes, |
155 | creation_time: self.data.ftCreationTime, | |
156 | last_access_time: self.data.ftLastAccessTime, | |
157 | last_write_time: self.data.ftLastWriteTime, | |
158 | file_size: ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64), | |
159 | reparse_tag: if self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { | |
60c5eb7d XL |
160 | // reserved unless this is a reparse point |
161 | self.data.dwReserved0 | |
162 | } else { | |
163 | 0 | |
164 | }, | |
416331ca XL |
165 | volume_serial_number: None, |
166 | number_of_links: None, | |
167 | file_index: None, | |
d9579d0f | 168 | }) |
85aaf69f SL |
169 | } |
170 | } | |
171 | ||
172 | impl OpenOptions { | |
7453a54e SL |
173 | pub fn new() -> OpenOptions { |
174 | OpenOptions { | |
175 | // generic | |
176 | read: false, | |
177 | write: false, | |
178 | append: false, | |
179 | truncate: false, | |
180 | create: false, | |
181 | create_new: false, | |
182 | // system-specific | |
183 | custom_flags: 0, | |
184 | access_mode: None, | |
185 | share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, | |
186 | attributes: 0, | |
187 | security_qos_flags: 0, | |
188 | security_attributes: 0, | |
189 | } | |
190 | } | |
191 | ||
60c5eb7d XL |
192 | pub fn read(&mut self, read: bool) { |
193 | self.read = read; | |
194 | } | |
195 | pub fn write(&mut self, write: bool) { | |
196 | self.write = write; | |
197 | } | |
198 | pub fn append(&mut self, append: bool) { | |
199 | self.append = append; | |
200 | } | |
201 | pub fn truncate(&mut self, truncate: bool) { | |
202 | self.truncate = truncate; | |
203 | } | |
204 | pub fn create(&mut self, create: bool) { | |
205 | self.create = create; | |
206 | } | |
207 | pub fn create_new(&mut self, create_new: bool) { | |
208 | self.create_new = create_new; | |
209 | } | |
7453a54e | 210 | |
60c5eb7d XL |
211 | pub fn custom_flags(&mut self, flags: u32) { |
212 | self.custom_flags = flags; | |
213 | } | |
214 | pub fn access_mode(&mut self, access_mode: u32) { | |
215 | self.access_mode = Some(access_mode); | |
216 | } | |
217 | pub fn share_mode(&mut self, share_mode: u32) { | |
218 | self.share_mode = share_mode; | |
219 | } | |
220 | pub fn attributes(&mut self, attrs: u32) { | |
221 | self.attributes = attrs; | |
222 | } | |
532ac7d7 XL |
223 | pub fn security_qos_flags(&mut self, flags: u32) { |
224 | // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can | |
225 | // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on. | |
226 | self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT; | |
227 | } | |
92a42be0 | 228 | pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) { |
bd371182 AL |
229 | self.security_attributes = attrs as usize; |
230 | } | |
85aaf69f | 231 | |
7453a54e SL |
232 | fn get_access_mode(&self) -> io::Result<c::DWORD> { |
233 | const ERROR_INVALID_PARAMETER: i32 = 87; | |
234 | ||
235 | match (self.read, self.write, self.append, self.access_mode) { | |
9e0c209e | 236 | (.., Some(mode)) => Ok(mode), |
60c5eb7d XL |
237 | (true, false, false, None) => Ok(c::GENERIC_READ), |
238 | (false, true, false, None) => Ok(c::GENERIC_WRITE), | |
239 | (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE), | |
240 | (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA), | |
241 | (true, _, true, None) => { | |
242 | Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) | |
243 | } | |
7453a54e SL |
244 | (false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)), |
245 | } | |
85aaf69f SL |
246 | } |
247 | ||
7453a54e SL |
248 | fn get_creation_mode(&self) -> io::Result<c::DWORD> { |
249 | const ERROR_INVALID_PARAMETER: i32 = 87; | |
250 | ||
251 | match (self.write, self.append) { | |
252 | (true, false) => {} | |
60c5eb7d | 253 | (false, false) => { |
7453a54e SL |
254 | if self.truncate || self.create || self.create_new { |
255 | return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); | |
60c5eb7d XL |
256 | } |
257 | } | |
258 | (_, true) => { | |
7453a54e SL |
259 | if self.truncate && !self.create_new { |
260 | return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); | |
60c5eb7d XL |
261 | } |
262 | } | |
7453a54e SL |
263 | } |
264 | ||
265 | Ok(match (self.create, self.truncate, self.create_new) { | |
60c5eb7d XL |
266 | (false, false, false) => c::OPEN_EXISTING, |
267 | (true, false, false) => c::OPEN_ALWAYS, | |
268 | (false, true, false) => c::TRUNCATE_EXISTING, | |
269 | (true, true, false) => c::CREATE_ALWAYS, | |
270 | (_, _, true) => c::CREATE_NEW, | |
271 | }) | |
85aaf69f SL |
272 | } |
273 | ||
92a42be0 | 274 | fn get_flags_and_attributes(&self) -> c::DWORD { |
60c5eb7d XL |
275 | self.custom_flags |
276 | | self.attributes | |
277 | | self.security_qos_flags | |
278 | | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 } | |
85aaf69f SL |
279 | } |
280 | } | |
281 | ||
282 | impl File { | |
283 | pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { | |
54a0048b | 284 | let path = to_u16s(path)?; |
85aaf69f | 285 | let handle = unsafe { |
60c5eb7d XL |
286 | c::CreateFileW( |
287 | path.as_ptr(), | |
288 | opts.get_access_mode()?, | |
289 | opts.share_mode, | |
290 | opts.security_attributes as *mut _, | |
291 | opts.get_creation_mode()?, | |
292 | opts.get_flags_and_attributes(), | |
293 | ptr::null_mut(), | |
294 | ) | |
85aaf69f | 295 | }; |
92a42be0 | 296 | if handle == c::INVALID_HANDLE_VALUE { |
85aaf69f SL |
297 | Err(Error::last_os_error()) |
298 | } else { | |
94222f64 | 299 | unsafe { Ok(File { handle: Handle::from_raw_handle(handle) }) } |
85aaf69f SL |
300 | } |
301 | } | |
302 | ||
303 | pub fn fsync(&self) -> io::Result<()> { | |
94222f64 | 304 | cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?; |
85aaf69f SL |
305 | Ok(()) |
306 | } | |
307 | ||
60c5eb7d XL |
308 | pub fn datasync(&self) -> io::Result<()> { |
309 | self.fsync() | |
310 | } | |
85aaf69f SL |
311 | |
312 | pub fn truncate(&self, size: u64) -> io::Result<()> { | |
60c5eb7d | 313 | let mut info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as c::LARGE_INTEGER }; |
85aaf69f | 314 | let size = mem::size_of_val(&info); |
54a0048b | 315 | cvt(unsafe { |
60c5eb7d | 316 | c::SetFileInformationByHandle( |
94222f64 | 317 | self.handle.as_raw_handle(), |
60c5eb7d XL |
318 | c::FileEndOfFileInfo, |
319 | &mut info as *mut _ as *mut _, | |
320 | size as c::DWORD, | |
321 | ) | |
54a0048b | 322 | })?; |
85aaf69f SL |
323 | Ok(()) |
324 | } | |
325 | ||
416331ca | 326 | #[cfg(not(target_vendor = "uwp"))] |
85aaf69f SL |
327 | pub fn file_attr(&self) -> io::Result<FileAttr> { |
328 | unsafe { | |
329 | let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); | |
94222f64 | 330 | cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; |
416331ca XL |
331 | let mut reparse_tag = 0; |
332 | if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { | |
333 | let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | |
334 | if let Ok((_, buf)) = self.reparse_point(&mut b) { | |
335 | reparse_tag = buf.ReparseTag; | |
336 | } | |
337 | } | |
338 | Ok(FileAttr { | |
7453a54e SL |
339 | attributes: info.dwFileAttributes, |
340 | creation_time: info.ftCreationTime, | |
341 | last_access_time: info.ftLastAccessTime, | |
342 | last_write_time: info.ftLastWriteTime, | |
416331ca XL |
343 | file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32), |
344 | reparse_tag, | |
345 | volume_serial_number: Some(info.dwVolumeSerialNumber), | |
346 | number_of_links: Some(info.nNumberOfLinks), | |
60c5eb7d XL |
347 | file_index: Some( |
348 | (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32), | |
349 | ), | |
416331ca XL |
350 | }) |
351 | } | |
352 | } | |
353 | ||
354 | #[cfg(target_vendor = "uwp")] | |
355 | pub fn file_attr(&self) -> io::Result<FileAttr> { | |
356 | unsafe { | |
357 | let mut info: c::FILE_BASIC_INFO = mem::zeroed(); | |
358 | let size = mem::size_of_val(&info); | |
60c5eb7d | 359 | cvt(c::GetFileInformationByHandleEx( |
94222f64 | 360 | self.handle.as_raw_handle(), |
60c5eb7d XL |
361 | c::FileBasicInfo, |
362 | &mut info as *mut _ as *mut libc::c_void, | |
363 | size as c::DWORD, | |
364 | ))?; | |
416331ca XL |
365 | let mut attr = FileAttr { |
366 | attributes: info.FileAttributes, | |
367 | creation_time: c::FILETIME { | |
368 | dwLowDateTime: info.CreationTime as c::DWORD, | |
369 | dwHighDateTime: (info.CreationTime >> 32) as c::DWORD, | |
370 | }, | |
371 | last_access_time: c::FILETIME { | |
372 | dwLowDateTime: info.LastAccessTime as c::DWORD, | |
373 | dwHighDateTime: (info.LastAccessTime >> 32) as c::DWORD, | |
374 | }, | |
375 | last_write_time: c::FILETIME { | |
376 | dwLowDateTime: info.LastWriteTime as c::DWORD, | |
377 | dwHighDateTime: (info.LastWriteTime >> 32) as c::DWORD, | |
378 | }, | |
379 | file_size: 0, | |
c1a9b12d | 380 | reparse_tag: 0, |
416331ca XL |
381 | volume_serial_number: None, |
382 | number_of_links: None, | |
383 | file_index: None, | |
d9579d0f | 384 | }; |
416331ca XL |
385 | let mut info: c::FILE_STANDARD_INFO = mem::zeroed(); |
386 | let size = mem::size_of_val(&info); | |
60c5eb7d | 387 | cvt(c::GetFileInformationByHandleEx( |
94222f64 | 388 | self.handle.as_raw_handle(), |
60c5eb7d XL |
389 | c::FileStandardInfo, |
390 | &mut info as *mut _ as *mut libc::c_void, | |
391 | size as c::DWORD, | |
392 | ))?; | |
416331ca XL |
393 | attr.file_size = info.AllocationSize as u64; |
394 | attr.number_of_links = Some(info.NumberOfLinks); | |
e1599b0c | 395 | if attr.file_type().is_reparse_point() { |
c1a9b12d SL |
396 | let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; |
397 | if let Ok((_, buf)) = self.reparse_point(&mut b) { | |
398 | attr.reparse_tag = buf.ReparseTag; | |
399 | } | |
d9579d0f AL |
400 | } |
401 | Ok(attr) | |
85aaf69f SL |
402 | } |
403 | } | |
404 | ||
405 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { | |
406 | self.handle.read(buf) | |
407 | } | |
408 | ||
48663c56 XL |
409 | pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
410 | self.handle.read_vectored(bufs) | |
411 | } | |
412 | ||
f9f354fc XL |
413 | #[inline] |
414 | pub fn is_read_vectored(&self) -> bool { | |
415 | self.handle.is_read_vectored() | |
416 | } | |
417 | ||
c30ab7b3 SL |
418 | pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { |
419 | self.handle.read_at(buf, offset) | |
420 | } | |
421 | ||
85aaf69f SL |
422 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { |
423 | self.handle.write(buf) | |
424 | } | |
425 | ||
48663c56 XL |
426 | pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
427 | self.handle.write_vectored(bufs) | |
428 | } | |
429 | ||
f9f354fc XL |
430 | #[inline] |
431 | pub fn is_write_vectored(&self) -> bool { | |
432 | self.handle.is_write_vectored() | |
433 | } | |
434 | ||
c30ab7b3 SL |
435 | pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { |
436 | self.handle.write_at(buf, offset) | |
437 | } | |
438 | ||
60c5eb7d XL |
439 | pub fn flush(&self) -> io::Result<()> { |
440 | Ok(()) | |
441 | } | |
85aaf69f SL |
442 | |
443 | pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { | |
444 | let (whence, pos) = match pos { | |
5bcae85e SL |
445 | // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this |
446 | // integer as `u64`. | |
92a42be0 SL |
447 | SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64), |
448 | SeekFrom::End(n) => (c::FILE_END, n), | |
449 | SeekFrom::Current(n) => (c::FILE_CURRENT, n), | |
85aaf69f | 450 | }; |
92a42be0 | 451 | let pos = pos as c::LARGE_INTEGER; |
85aaf69f | 452 | let mut newpos = 0; |
94222f64 | 453 | cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?; |
85aaf69f SL |
454 | Ok(newpos as u64) |
455 | } | |
456 | ||
7453a54e | 457 | pub fn duplicate(&self) -> io::Result<File> { |
60c5eb7d | 458 | Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? }) |
7453a54e SL |
459 | } |
460 | ||
60c5eb7d XL |
461 | fn reparse_point<'a>( |
462 | &self, | |
463 | space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE], | |
464 | ) -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> { | |
d9579d0f | 465 | unsafe { |
c1a9b12d | 466 | let mut bytes = 0; |
54a0048b | 467 | cvt({ |
60c5eb7d | 468 | c::DeviceIoControl( |
94222f64 | 469 | self.handle.as_raw_handle(), |
60c5eb7d XL |
470 | c::FSCTL_GET_REPARSE_POINT, |
471 | ptr::null_mut(), | |
472 | 0, | |
473 | space.as_mut_ptr() as *mut _, | |
474 | space.len() as c::DWORD, | |
475 | &mut bytes, | |
476 | ptr::null_mut(), | |
477 | ) | |
54a0048b | 478 | })?; |
c1a9b12d SL |
479 | Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER))) |
480 | } | |
481 | } | |
482 | ||
483 | fn readlink(&self) -> io::Result<PathBuf> { | |
484 | let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | |
54a0048b | 485 | let (_bytes, buf) = self.reparse_point(&mut space)?; |
c1a9b12d | 486 | unsafe { |
7453a54e SL |
487 | let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag { |
488 | c::IO_REPARSE_TAG_SYMLINK => { | |
489 | let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = | |
490 | &buf.rest as *const _ as *const _; | |
60c5eb7d XL |
491 | ( |
492 | &(*info).PathBuffer as *const _ as *const u16, | |
493 | (*info).SubstituteNameOffset / 2, | |
494 | (*info).SubstituteNameLength / 2, | |
495 | (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, | |
496 | ) | |
497 | } | |
7453a54e SL |
498 | c::IO_REPARSE_TAG_MOUNT_POINT => { |
499 | let info: *const c::MOUNT_POINT_REPARSE_BUFFER = | |
500 | &buf.rest as *const _ as *const _; | |
60c5eb7d XL |
501 | ( |
502 | &(*info).PathBuffer as *const _ as *const u16, | |
503 | (*info).SubstituteNameOffset / 2, | |
504 | (*info).SubstituteNameLength / 2, | |
505 | false, | |
506 | ) | |
507 | } | |
508 | _ => { | |
cdc7bbd5 | 509 | return Err(io::Error::new_const( |
136023e0 | 510 | io::ErrorKind::Uncategorized, |
cdc7bbd5 | 511 | &"Unsupported reparse point type", |
60c5eb7d XL |
512 | )); |
513 | } | |
7453a54e | 514 | }; |
d9579d0f | 515 | let subst_ptr = path_buffer.offset(subst_off as isize); |
7453a54e SL |
516 | let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize); |
517 | // Absolute paths start with an NT internal namespace prefix `\??\` | |
518 | // We should not let it leak through. | |
519 | if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) { | |
520 | subst = &subst[4..]; | |
521 | } | |
d9579d0f AL |
522 | Ok(PathBuf::from(OsString::from_wide(subst))) |
523 | } | |
524 | } | |
476ff2be SL |
525 | |
526 | pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { | |
527 | let mut info = c::FILE_BASIC_INFO { | |
528 | CreationTime: 0, | |
529 | LastAccessTime: 0, | |
530 | LastWriteTime: 0, | |
531 | ChangeTime: 0, | |
532 | FileAttributes: perm.attrs, | |
533 | }; | |
534 | let size = mem::size_of_val(&info); | |
535 | cvt(unsafe { | |
60c5eb7d | 536 | c::SetFileInformationByHandle( |
94222f64 | 537 | self.handle.as_raw_handle(), |
60c5eb7d XL |
538 | c::FileBasicInfo, |
539 | &mut info as *mut _ as *mut _, | |
540 | size as c::DWORD, | |
541 | ) | |
476ff2be SL |
542 | })?; |
543 | Ok(()) | |
544 | } | |
c34b1796 AL |
545 | } |
546 | ||
94222f64 XL |
547 | impl AsInner<Handle> for File { |
548 | fn as_inner(&self) -> &Handle { | |
549 | &self.handle | |
550 | } | |
551 | } | |
552 | ||
553 | impl IntoInner<Handle> for File { | |
554 | fn into_inner(self) -> Handle { | |
555 | self.handle | |
556 | } | |
557 | } | |
558 | ||
559 | impl FromInner<Handle> for File { | |
560 | fn from_inner(handle: Handle) -> File { | |
561 | File { handle: handle } | |
562 | } | |
563 | } | |
564 | ||
565 | impl AsHandle for File { | |
566 | fn as_handle(&self) -> BorrowedHandle<'_> { | |
567 | self.as_inner().as_handle() | |
568 | } | |
569 | } | |
570 | ||
571 | impl AsRawHandle for File { | |
572 | fn as_raw_handle(&self) -> RawHandle { | |
573 | self.as_inner().as_raw_handle() | |
574 | } | |
575 | } | |
576 | ||
577 | impl IntoRawHandle for File { | |
578 | fn into_raw_handle(self) -> RawHandle { | |
579 | self.into_inner().into_raw_handle() | |
580 | } | |
581 | } | |
582 | ||
583 | impl FromRawHandle for File { | |
584 | unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { | |
585 | Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } | |
c34b1796 | 586 | } |
85aaf69f SL |
587 | } |
588 | ||
d9579d0f | 589 | impl fmt::Debug for File { |
532ac7d7 | 590 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
0731742a | 591 | // FIXME(#24570): add more info here (e.g., mode) |
c1a9b12d | 592 | let mut b = f.debug_struct("File"); |
94222f64 | 593 | b.field("handle", &self.handle.as_raw_handle()); |
c1a9b12d SL |
594 | if let Ok(path) = get_path(&self) { |
595 | b.field("path", &path); | |
596 | } | |
597 | b.finish() | |
d9579d0f AL |
598 | } |
599 | } | |
600 | ||
85aaf69f | 601 | impl FileAttr { |
85aaf69f | 602 | pub fn size(&self) -> u64 { |
7453a54e | 603 | self.file_size |
85aaf69f | 604 | } |
d9579d0f | 605 | |
85aaf69f | 606 | pub fn perm(&self) -> FilePermissions { |
7453a54e | 607 | FilePermissions { attrs: self.attributes } |
85aaf69f SL |
608 | } |
609 | ||
416331ca XL |
610 | pub fn attrs(&self) -> u32 { |
611 | self.attributes | |
612 | } | |
d9579d0f AL |
613 | |
614 | pub fn file_type(&self) -> FileType { | |
7453a54e SL |
615 | FileType::new(self.attributes, self.reparse_tag) |
616 | } | |
617 | ||
618 | pub fn modified(&self) -> io::Result<SystemTime> { | |
619 | Ok(SystemTime::from(self.last_write_time)) | |
620 | } | |
621 | ||
622 | pub fn accessed(&self) -> io::Result<SystemTime> { | |
623 | Ok(SystemTime::from(self.last_access_time)) | |
624 | } | |
625 | ||
626 | pub fn created(&self) -> io::Result<SystemTime> { | |
627 | Ok(SystemTime::from(self.creation_time)) | |
d9579d0f AL |
628 | } |
629 | ||
7453a54e SL |
630 | pub fn modified_u64(&self) -> u64 { |
631 | to_u64(&self.last_write_time) | |
632 | } | |
633 | ||
634 | pub fn accessed_u64(&self) -> u64 { | |
635 | to_u64(&self.last_access_time) | |
636 | } | |
85aaf69f | 637 | |
7453a54e SL |
638 | pub fn created_u64(&self) -> u64 { |
639 | to_u64(&self.creation_time) | |
d9579d0f AL |
640 | } |
641 | ||
416331ca XL |
642 | pub fn volume_serial_number(&self) -> Option<u32> { |
643 | self.volume_serial_number | |
644 | } | |
645 | ||
646 | pub fn number_of_links(&self) -> Option<u32> { | |
647 | self.number_of_links | |
648 | } | |
649 | ||
650 | pub fn file_index(&self) -> Option<u64> { | |
651 | self.file_index | |
85aaf69f SL |
652 | } |
653 | } | |
654 | ||
7453a54e SL |
655 | fn to_u64(ft: &c::FILETIME) -> u64 { |
656 | (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) | |
657 | } | |
658 | ||
85aaf69f SL |
659 | impl FilePermissions { |
660 | pub fn readonly(&self) -> bool { | |
661 | self.attrs & c::FILE_ATTRIBUTE_READONLY != 0 | |
662 | } | |
663 | ||
664 | pub fn set_readonly(&mut self, readonly: bool) { | |
665 | if readonly { | |
666 | self.attrs |= c::FILE_ATTRIBUTE_READONLY; | |
667 | } else { | |
668 | self.attrs &= !c::FILE_ATTRIBUTE_READONLY; | |
669 | } | |
670 | } | |
671 | } | |
672 | ||
d9579d0f | 673 | impl FileType { |
92a42be0 | 674 | fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType { |
60c5eb7d | 675 | FileType { attributes: attrs, reparse_tag: reparse_tag } |
d9579d0f | 676 | } |
0531ce1d XL |
677 | pub fn is_dir(&self) -> bool { |
678 | !self.is_symlink() && self.is_directory() | |
679 | } | |
680 | pub fn is_file(&self) -> bool { | |
681 | !self.is_symlink() && !self.is_directory() | |
682 | } | |
c1a9b12d | 683 | pub fn is_symlink(&self) -> bool { |
0531ce1d | 684 | self.is_reparse_point() && self.is_reparse_tag_name_surrogate() |
7453a54e SL |
685 | } |
686 | pub fn is_symlink_dir(&self) -> bool { | |
0531ce1d XL |
687 | self.is_symlink() && self.is_directory() |
688 | } | |
689 | pub fn is_symlink_file(&self) -> bool { | |
690 | self.is_symlink() && !self.is_directory() | |
691 | } | |
692 | fn is_directory(&self) -> bool { | |
693 | self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 | |
694 | } | |
695 | fn is_reparse_point(&self) -> bool { | |
696 | self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 | |
697 | } | |
698 | fn is_reparse_tag_name_surrogate(&self) -> bool { | |
699 | self.reparse_tag & 0x20000000 != 0 | |
c1a9b12d | 700 | } |
d9579d0f AL |
701 | } |
702 | ||
703 | impl DirBuilder { | |
60c5eb7d XL |
704 | pub fn new() -> DirBuilder { |
705 | DirBuilder | |
706 | } | |
d9579d0f AL |
707 | |
708 | pub fn mkdir(&self, p: &Path) -> io::Result<()> { | |
54a0048b | 709 | let p = to_u16s(p)?; |
60c5eb7d | 710 | cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?; |
d9579d0f AL |
711 | Ok(()) |
712 | } | |
85aaf69f SL |
713 | } |
714 | ||
715 | pub fn readdir(p: &Path) -> io::Result<ReadDir> { | |
716 | let root = p.to_path_buf(); | |
717 | let star = p.join("*"); | |
54a0048b | 718 | let path = to_u16s(&star)?; |
85aaf69f SL |
719 | |
720 | unsafe { | |
721 | let mut wfd = mem::zeroed(); | |
92a42be0 SL |
722 | let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); |
723 | if find_handle != c::INVALID_HANDLE_VALUE { | |
c34b1796 AL |
724 | Ok(ReadDir { |
725 | handle: FindNextFileHandle(find_handle), | |
726 | root: Arc::new(root), | |
727 | first: Some(wfd), | |
728 | }) | |
85aaf69f SL |
729 | } else { |
730 | Err(Error::last_os_error()) | |
731 | } | |
732 | } | |
733 | } | |
734 | ||
735 | pub fn unlink(p: &Path) -> io::Result<()> { | |
54a0048b SL |
736 | let p_u16s = to_u16s(p)?; |
737 | cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?; | |
85aaf69f SL |
738 | Ok(()) |
739 | } | |
740 | ||
741 | pub fn rename(old: &Path, new: &Path) -> io::Result<()> { | |
54a0048b SL |
742 | let old = to_u16s(old)?; |
743 | let new = to_u16s(new)?; | |
60c5eb7d | 744 | cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) })?; |
85aaf69f SL |
745 | Ok(()) |
746 | } | |
747 | ||
748 | pub fn rmdir(p: &Path) -> io::Result<()> { | |
54a0048b SL |
749 | let p = to_u16s(p)?; |
750 | cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?; | |
85aaf69f SL |
751 | Ok(()) |
752 | } | |
753 | ||
7453a54e | 754 | pub fn remove_dir_all(path: &Path) -> io::Result<()> { |
54a0048b | 755 | let filetype = lstat(path)?.file_type(); |
7453a54e SL |
756 | if filetype.is_symlink() { |
757 | // On Windows symlinks to files and directories are removed differently. | |
758 | // rmdir only deletes dir symlinks and junctions, not file symlinks. | |
759 | rmdir(path) | |
760 | } else { | |
761 | remove_dir_all_recursive(path) | |
762 | } | |
763 | } | |
764 | ||
765 | fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { | |
54a0048b SL |
766 | for child in readdir(path)? { |
767 | let child = child?; | |
768 | let child_type = child.file_type()?; | |
7453a54e | 769 | if child_type.is_dir() { |
54a0048b | 770 | remove_dir_all_recursive(&child.path())?; |
7453a54e | 771 | } else if child_type.is_symlink_dir() { |
54a0048b | 772 | rmdir(&child.path())?; |
7453a54e | 773 | } else { |
54a0048b | 774 | unlink(&child.path())?; |
7453a54e SL |
775 | } |
776 | } | |
777 | rmdir(path) | |
778 | } | |
779 | ||
780 | pub fn readlink(path: &Path) -> io::Result<PathBuf> { | |
781 | // Open the link with no access mode, instead of generic read. | |
782 | // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so | |
783 | // this is needed for a common case. | |
784 | let mut opts = OpenOptions::new(); | |
785 | opts.access_mode(0); | |
60c5eb7d | 786 | opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); |
54a0048b | 787 | let file = File::open(&path, &opts)?; |
d9579d0f | 788 | file.readlink() |
85aaf69f SL |
789 | } |
790 | ||
fc512014 XL |
791 | pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { |
792 | symlink_inner(original, link, false) | |
d9579d0f AL |
793 | } |
794 | ||
fc512014 XL |
795 | pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> { |
796 | let original = to_u16s(original)?; | |
797 | let link = to_u16s(link)?; | |
d9579d0f | 798 | let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 }; |
8bb4bdeb XL |
799 | // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10 |
800 | // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the | |
801 | // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be | |
802 | // added to dwFlags to opt into this behaviour. | |
803 | let result = cvt(unsafe { | |
60c5eb7d | 804 | c::CreateSymbolicLinkW( |
fc512014 XL |
805 | link.as_ptr(), |
806 | original.as_ptr(), | |
60c5eb7d XL |
807 | flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, |
808 | ) as c::BOOL | |
8bb4bdeb XL |
809 | }); |
810 | if let Err(err) = result { | |
811 | if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) { | |
812 | // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, | |
813 | // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag. | |
fc512014 XL |
814 | cvt(unsafe { |
815 | c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL | |
816 | })?; | |
8bb4bdeb XL |
817 | } else { |
818 | return Err(err); | |
819 | } | |
820 | } | |
85aaf69f SL |
821 | Ok(()) |
822 | } | |
823 | ||
416331ca | 824 | #[cfg(not(target_vendor = "uwp"))] |
fc512014 XL |
825 | pub fn link(original: &Path, link: &Path) -> io::Result<()> { |
826 | let original = to_u16s(original)?; | |
827 | let link = to_u16s(link)?; | |
828 | cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?; | |
85aaf69f SL |
829 | Ok(()) |
830 | } | |
831 | ||
416331ca | 832 | #[cfg(target_vendor = "uwp")] |
fc512014 | 833 | pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { |
cdc7bbd5 XL |
834 | return Err(io::Error::new_const( |
835 | io::ErrorKind::Unsupported, | |
836 | &"hard link are not supported on UWP", | |
837 | )); | |
416331ca XL |
838 | } |
839 | ||
7453a54e SL |
840 | pub fn stat(path: &Path) -> io::Result<FileAttr> { |
841 | let mut opts = OpenOptions::new(); | |
842 | // No read or write permissions are necessary | |
843 | opts.access_mode(0); | |
844 | // This flag is so we can open directories too | |
845 | opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); | |
54a0048b | 846 | let file = File::open(path, &opts)?; |
7453a54e | 847 | file.file_attr() |
d9579d0f AL |
848 | } |
849 | ||
7453a54e SL |
850 | pub fn lstat(path: &Path) -> io::Result<FileAttr> { |
851 | let mut opts = OpenOptions::new(); | |
852 | // No read or write permissions are necessary | |
853 | opts.access_mode(0); | |
854 | opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); | |
54a0048b | 855 | let file = File::open(path, &opts)?; |
7453a54e | 856 | file.file_attr() |
85aaf69f SL |
857 | } |
858 | ||
859 | pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { | |
54a0048b | 860 | let p = to_u16s(p)?; |
85aaf69f | 861 | unsafe { |
54a0048b | 862 | cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?; |
85aaf69f SL |
863 | Ok(()) |
864 | } | |
865 | } | |
866 | ||
c1a9b12d | 867 | fn get_path(f: &File) -> io::Result<PathBuf> { |
60c5eb7d XL |
868 | super::fill_utf16_buf( |
869 | |buf, sz| unsafe { | |
94222f64 | 870 | c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) |
60c5eb7d XL |
871 | }, |
872 | |buf| PathBuf::from(OsString::from_wide(buf)), | |
873 | ) | |
d9579d0f | 874 | } |
c1a9b12d SL |
875 | |
876 | pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { | |
877 | let mut opts = OpenOptions::new(); | |
7453a54e SL |
878 | // No read or write permissions are necessary |
879 | opts.access_mode(0); | |
b039eaaf | 880 | // This flag is so we can open directories too |
7453a54e | 881 | opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); |
54a0048b | 882 | let f = File::open(p, &opts)?; |
c1a9b12d SL |
883 | get_path(&f) |
884 | } | |
885 | ||
886 | pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { | |
887 | unsafe extern "system" fn callback( | |
92a42be0 | 888 | _TotalFileSize: c::LARGE_INTEGER, |
ea8adc8c | 889 | _TotalBytesTransferred: c::LARGE_INTEGER, |
92a42be0 | 890 | _StreamSize: c::LARGE_INTEGER, |
ea8adc8c XL |
891 | StreamBytesTransferred: c::LARGE_INTEGER, |
892 | dwStreamNumber: c::DWORD, | |
92a42be0 SL |
893 | _dwCallbackReason: c::DWORD, |
894 | _hSourceFile: c::HANDLE, | |
895 | _hDestinationFile: c::HANDLE, | |
896 | lpData: c::LPVOID, | |
897 | ) -> c::DWORD { | |
60c5eb7d XL |
898 | if dwStreamNumber == 1 { |
899 | *(lpData as *mut i64) = StreamBytesTransferred; | |
900 | } | |
c1a9b12d SL |
901 | c::PROGRESS_CONTINUE |
902 | } | |
54a0048b SL |
903 | let pfrom = to_u16s(from)?; |
904 | let pto = to_u16s(to)?; | |
c1a9b12d | 905 | let mut size = 0i64; |
54a0048b | 906 | cvt(unsafe { |
60c5eb7d XL |
907 | c::CopyFileExW( |
908 | pfrom.as_ptr(), | |
909 | pto.as_ptr(), | |
910 | Some(callback), | |
911 | &mut size as *mut _ as *mut _, | |
912 | ptr::null_mut(), | |
913 | 0, | |
914 | ) | |
54a0048b | 915 | })?; |
c1a9b12d SL |
916 | Ok(size as u64) |
917 | } | |
918 | ||
7453a54e | 919 | #[allow(dead_code)] |
fc512014 XL |
920 | pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>( |
921 | original: P, | |
922 | junction: Q, | |
923 | ) -> io::Result<()> { | |
924 | symlink_junction_inner(original.as_ref(), junction.as_ref()) | |
7453a54e | 925 | } |
c1a9b12d | 926 | |
7453a54e SL |
927 | // Creating a directory junction on windows involves dealing with reparse |
928 | // points and the DeviceIoControl function, and this code is a skeleton of | |
929 | // what can be found here: | |
930 | // | |
931 | // http://www.flexhex.com/docs/articles/hard-links.phtml | |
932 | #[allow(dead_code)] | |
fc512014 | 933 | fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { |
c1a9b12d | 934 | let d = DirBuilder::new(); |
54a0048b | 935 | d.mkdir(&junction)?; |
c1a9b12d | 936 | |
7453a54e SL |
937 | let mut opts = OpenOptions::new(); |
938 | opts.write(true); | |
60c5eb7d | 939 | opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); |
54a0048b | 940 | let f = File::open(junction, &opts)?; |
94222f64 | 941 | let h = f.as_inner().as_raw_handle(); |
c1a9b12d | 942 | |
7453a54e SL |
943 | unsafe { |
944 | let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | |
60c5eb7d | 945 | let db = data.as_mut_ptr() as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER; |
ff7c6d11 | 946 | let buf = &mut (*db).ReparseTarget as *mut c::WCHAR; |
7453a54e SL |
947 | let mut i = 0; |
948 | // FIXME: this conversion is very hacky | |
949 | let v = br"\??\"; | |
950 | let v = v.iter().map(|x| *x as u16); | |
fc512014 | 951 | for c in v.chain(original.as_os_str().encode_wide()) { |
7453a54e SL |
952 | *buf.offset(i) = c; |
953 | i += 1; | |
c1a9b12d | 954 | } |
7453a54e SL |
955 | *buf.offset(i) = 0; |
956 | i += 1; | |
957 | (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT; | |
958 | (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD; | |
959 | (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD; | |
60c5eb7d | 960 | (*db).ReparseDataLength = (*db).ReparseTargetLength as c::DWORD + 12; |
7453a54e SL |
961 | |
962 | let mut ret = 0; | |
60c5eb7d XL |
963 | cvt(c::DeviceIoControl( |
964 | h as *mut _, | |
965 | c::FSCTL_SET_REPARSE_POINT, | |
966 | data.as_ptr() as *mut _, | |
967 | (*db).ReparseDataLength + 8, | |
968 | ptr::null_mut(), | |
969 | 0, | |
970 | &mut ret, | |
971 | ptr::null_mut(), | |
972 | )) | |
dfeec247 | 973 | .map(drop) |
c1a9b12d SL |
974 | } |
975 | } | |
17df50a5 XL |
976 | |
977 | // Try to see if a file exists but, unlike `exists`, report I/O errors. | |
978 | pub fn try_exists(path: &Path) -> io::Result<bool> { | |
979 | // Open the file to ensure any symlinks are followed to their target. | |
980 | let mut opts = OpenOptions::new(); | |
981 | // No read, write, etc access rights are needed. | |
982 | opts.access_mode(0); | |
983 | // Backup semantics enables opening directories as well as files. | |
984 | opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); | |
985 | match File::open(path, &opts) { | |
986 | Err(e) => match e.kind() { | |
987 | // The file definitely does not exist | |
988 | io::ErrorKind::NotFound => Ok(false), | |
989 | ||
990 | // `ERROR_SHARING_VIOLATION` means that the file has been locked by | |
991 | // another process. This is often temporary so we simply report it | |
992 | // as the file existing. | |
136023e0 XL |
993 | _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true), |
994 | ||
17df50a5 XL |
995 | // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the |
996 | // file exists. However, these types of errors are usually more | |
997 | // permanent so we report them here. | |
998 | _ => Err(e), | |
999 | }, | |
1000 | // The file was opened successfully therefore it must exist, | |
1001 | Ok(_) => Ok(true), | |
1002 | } | |
1003 | } |