]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/fs.rs
Elide invalid method receiver error when it contains TyErr
[rust.git] / src / libstd / sys / windows / fs.rs
1 use crate::os::windows::prelude::*;
2
3 use crate::ffi::OsString;
4 use crate::fmt;
5 use crate::io::{self, Error, SeekFrom};
6 use crate::mem;
7 use crate::path::{Path, PathBuf};
8 use crate::ptr;
9 use crate::slice;
10 use crate::sync::Arc;
11 use crate::sys::handle::Handle;
12 use crate::sys::time::SystemTime;
13 use crate::sys::{c, cvt};
14 use crate::sys_common::FromInner;
15
16 use super::to_u16s;
17
18 pub struct File { handle: Handle }
19
20 #[derive(Clone)]
21 pub struct FileAttr {
22     attributes: c::DWORD,
23     creation_time: c::FILETIME,
24     last_access_time: c::FILETIME,
25     last_write_time: c::FILETIME,
26     file_size: u64,
27     reparse_tag: c::DWORD,
28 }
29
30 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
31 pub struct FileType {
32     attributes: c::DWORD,
33     reparse_tag: c::DWORD,
34 }
35
36 pub struct ReadDir {
37     handle: FindNextFileHandle,
38     root: Arc<PathBuf>,
39     first: Option<c::WIN32_FIND_DATAW>,
40 }
41
42 struct FindNextFileHandle(c::HANDLE);
43
44 unsafe impl Send for FindNextFileHandle {}
45 unsafe impl Sync for FindNextFileHandle {}
46
47 pub struct DirEntry {
48     root: Arc<PathBuf>,
49     data: c::WIN32_FIND_DATAW,
50 }
51
52 #[derive(Clone, Debug)]
53 pub struct OpenOptions {
54     // generic
55     read: bool,
56     write: bool,
57     append: bool,
58     truncate: bool,
59     create: bool,
60     create_new: bool,
61     // system-specific
62     custom_flags: u32,
63     access_mode: Option<c::DWORD>,
64     attributes: c::DWORD,
65     share_mode: c::DWORD,
66     security_qos_flags: c::DWORD,
67     security_attributes: usize, // FIXME: should be a reference
68 }
69
70 #[derive(Clone, PartialEq, Eq, Debug)]
71 pub struct FilePermissions { attrs: c::DWORD }
72
73 #[derive(Debug)]
74 pub struct DirBuilder;
75
76 impl fmt::Debug for ReadDir {
77     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78         // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
79         // Thus the result will be e g 'ReadDir("C:\")'
80         fmt::Debug::fmt(&*self.root, f)
81     }
82 }
83
84 impl Iterator for ReadDir {
85     type Item = io::Result<DirEntry>;
86     fn next(&mut self) -> Option<io::Result<DirEntry>> {
87         if let Some(first) = self.first.take() {
88             if let Some(e) = DirEntry::new(&self.root, &first) {
89                 return Some(Ok(e));
90             }
91         }
92         unsafe {
93             let mut wfd = mem::zeroed();
94             loop {
95                 if c::FindNextFileW(self.handle.0, &mut wfd) == 0 {
96                     if c::GetLastError() == c::ERROR_NO_MORE_FILES {
97                         return None
98                     } else {
99                         return Some(Err(Error::last_os_error()))
100                     }
101                 }
102                 if let Some(e) = DirEntry::new(&self.root, &wfd) {
103                     return Some(Ok(e))
104                 }
105             }
106         }
107     }
108 }
109
110 impl Drop for FindNextFileHandle {
111     fn drop(&mut self) {
112         let r = unsafe { c::FindClose(self.0) };
113         debug_assert!(r != 0);
114     }
115 }
116
117 impl DirEntry {
118     fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {
119         match &wfd.cFileName[0..3] {
120             // check for '.' and '..'
121             &[46, 0, ..] |
122             &[46, 46, 0, ..] => return None,
123             _ => {}
124         }
125
126         Some(DirEntry {
127             root: root.clone(),
128             data: *wfd,
129         })
130     }
131
132     pub fn path(&self) -> PathBuf {
133         self.root.join(&self.file_name())
134     }
135
136     pub fn file_name(&self) -> OsString {
137         let filename = super::truncate_utf16_at_nul(&self.data.cFileName);
138         OsString::from_wide(filename)
139     }
140
141     pub fn file_type(&self) -> io::Result<FileType> {
142         Ok(FileType::new(self.data.dwFileAttributes,
143                          /* reparse_tag = */ self.data.dwReserved0))
144     }
145
146     pub fn metadata(&self) -> io::Result<FileAttr> {
147         Ok(FileAttr {
148             attributes: self.data.dwFileAttributes,
149             creation_time: self.data.ftCreationTime,
150             last_access_time: self.data.ftLastAccessTime,
151             last_write_time: self.data.ftLastWriteTime,
152             file_size: ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64),
153             reparse_tag: if self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
154                     // reserved unless this is a reparse point
155                     self.data.dwReserved0
156                 } else {
157                     0
158                 },
159         })
160     }
161 }
162
163 impl OpenOptions {
164     pub fn new() -> OpenOptions {
165         OpenOptions {
166             // generic
167             read: false,
168             write: false,
169             append: false,
170             truncate: false,
171             create: false,
172             create_new: false,
173             // system-specific
174             custom_flags: 0,
175             access_mode: None,
176             share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE,
177             attributes: 0,
178             security_qos_flags: 0,
179             security_attributes: 0,
180         }
181     }
182
183     pub fn read(&mut self, read: bool) { self.read = read; }
184     pub fn write(&mut self, write: bool) { self.write = write; }
185     pub fn append(&mut self, append: bool) { self.append = append; }
186     pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; }
187     pub fn create(&mut self, create: bool) { self.create = create; }
188     pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; }
189
190     pub fn custom_flags(&mut self, flags: u32) { self.custom_flags = flags; }
191     pub fn access_mode(&mut self, access_mode: u32) { self.access_mode = Some(access_mode); }
192     pub fn share_mode(&mut self, share_mode: u32) { self.share_mode = share_mode; }
193     pub fn attributes(&mut self, attrs: u32) { self.attributes = attrs; }
194     pub fn security_qos_flags(&mut self, flags: u32) { self.security_qos_flags = flags; }
195     pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) {
196         self.security_attributes = attrs as usize;
197     }
198
199     fn get_access_mode(&self) -> io::Result<c::DWORD> {
200         const ERROR_INVALID_PARAMETER: i32 = 87;
201
202         match (self.read, self.write, self.append, self.access_mode) {
203             (.., Some(mode)) => Ok(mode),
204             (true,  false, false, None) => Ok(c::GENERIC_READ),
205             (false, true,  false, None) => Ok(c::GENERIC_WRITE),
206             (true,  true,  false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE),
207             (false, _,     true,  None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA),
208             (true,  _,     true,  None) => Ok(c::GENERIC_READ |
209                                               (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)),
210             (false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)),
211         }
212     }
213
214     fn get_creation_mode(&self) -> io::Result<c::DWORD> {
215         const ERROR_INVALID_PARAMETER: i32 = 87;
216
217         match (self.write, self.append) {
218             (true, false) => {}
219             (false, false) =>
220                 if self.truncate || self.create || self.create_new {
221                     return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
222                 },
223             (_, true) =>
224                 if self.truncate && !self.create_new {
225                     return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
226                 },
227         }
228
229         Ok(match (self.create, self.truncate, self.create_new) {
230                 (false, false, false) => c::OPEN_EXISTING,
231                 (true,  false, false) => c::OPEN_ALWAYS,
232                 (false, true,  false) => c::TRUNCATE_EXISTING,
233                 (true,  true,  false) => c::CREATE_ALWAYS,
234                 (_,      _,    true)  => c::CREATE_NEW,
235            })
236     }
237
238     fn get_flags_and_attributes(&self) -> c::DWORD {
239         self.custom_flags |
240         self.attributes |
241         self.security_qos_flags |
242         if self.security_qos_flags != 0 { c::SECURITY_SQOS_PRESENT } else { 0 } |
243         if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 }
244     }
245 }
246
247 impl File {
248     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
249         let path = to_u16s(path)?;
250         let handle = unsafe {
251             c::CreateFileW(path.as_ptr(),
252                            opts.get_access_mode()?,
253                            opts.share_mode,
254                            opts.security_attributes as *mut _,
255                            opts.get_creation_mode()?,
256                            opts.get_flags_and_attributes(),
257                            ptr::null_mut())
258         };
259         if handle == c::INVALID_HANDLE_VALUE {
260             Err(Error::last_os_error())
261         } else {
262             Ok(File { handle: Handle::new(handle) })
263         }
264     }
265
266     pub fn fsync(&self) -> io::Result<()> {
267         cvt(unsafe { c::FlushFileBuffers(self.handle.raw()) })?;
268         Ok(())
269     }
270
271     pub fn datasync(&self) -> io::Result<()> { self.fsync() }
272
273     pub fn truncate(&self, size: u64) -> io::Result<()> {
274         let mut info = c::FILE_END_OF_FILE_INFO {
275             EndOfFile: size as c::LARGE_INTEGER,
276         };
277         let size = mem::size_of_val(&info);
278         cvt(unsafe {
279             c::SetFileInformationByHandle(self.handle.raw(),
280                                           c::FileEndOfFileInfo,
281                                           &mut info as *mut _ as *mut _,
282                                           size as c::DWORD)
283         })?;
284         Ok(())
285     }
286
287     pub fn file_attr(&self) -> io::Result<FileAttr> {
288         unsafe {
289             let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
290             cvt(c::GetFileInformationByHandle(self.handle.raw(),
291                                               &mut info))?;
292             let mut attr = FileAttr {
293                 attributes: info.dwFileAttributes,
294                 creation_time: info.ftCreationTime,
295                 last_access_time: info.ftLastAccessTime,
296                 last_write_time: info.ftLastWriteTime,
297                 file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64),
298                 reparse_tag: 0,
299             };
300             if attr.is_reparse_point() {
301                 let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
302                 if let Ok((_, buf)) = self.reparse_point(&mut b) {
303                     attr.reparse_tag = buf.ReparseTag;
304                 }
305             }
306             Ok(attr)
307         }
308     }
309
310     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
311         self.handle.read(buf)
312     }
313
314     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
315         self.handle.read_at(buf, offset)
316     }
317
318     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
319         self.handle.write(buf)
320     }
321
322     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
323         self.handle.write_at(buf, offset)
324     }
325
326     pub fn flush(&self) -> io::Result<()> { Ok(()) }
327
328     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
329         let (whence, pos) = match pos {
330             // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this
331             // integer as `u64`.
332             SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64),
333             SeekFrom::End(n) => (c::FILE_END, n),
334             SeekFrom::Current(n) => (c::FILE_CURRENT, n),
335         };
336         let pos = pos as c::LARGE_INTEGER;
337         let mut newpos = 0;
338         cvt(unsafe {
339             c::SetFilePointerEx(self.handle.raw(), pos,
340                                 &mut newpos, whence)
341         })?;
342         Ok(newpos as u64)
343     }
344
345     pub fn duplicate(&self) -> io::Result<File> {
346         Ok(File {
347             handle: self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)?,
348         })
349     }
350
351     pub fn handle(&self) -> &Handle { &self.handle }
352
353     pub fn into_handle(self) -> Handle { self.handle }
354
355     fn reparse_point<'a>(&self,
356                          space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE])
357                          -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
358         unsafe {
359             let mut bytes = 0;
360             cvt({
361                 c::DeviceIoControl(self.handle.raw(),
362                                    c::FSCTL_GET_REPARSE_POINT,
363                                    ptr::null_mut(),
364                                    0,
365                                    space.as_mut_ptr() as *mut _,
366                                    space.len() as c::DWORD,
367                                    &mut bytes,
368                                    ptr::null_mut())
369             })?;
370             Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
371         }
372     }
373
374     fn readlink(&self) -> io::Result<PathBuf> {
375         let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
376         let (_bytes, buf) = self.reparse_point(&mut space)?;
377         unsafe {
378             let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag {
379                 c::IO_REPARSE_TAG_SYMLINK => {
380                     let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
381                         &buf.rest as *const _ as *const _;
382                     (&(*info).PathBuffer as *const _ as *const u16,
383                      (*info).SubstituteNameOffset / 2,
384                      (*info).SubstituteNameLength / 2,
385                      (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0)
386                 },
387                 c::IO_REPARSE_TAG_MOUNT_POINT => {
388                     let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
389                         &buf.rest as *const _ as *const _;
390                     (&(*info).PathBuffer as *const _ as *const u16,
391                      (*info).SubstituteNameOffset / 2,
392                      (*info).SubstituteNameLength / 2,
393                      false)
394                 },
395                 _ => return Err(io::Error::new(io::ErrorKind::Other,
396                                                "Unsupported reparse point type"))
397             };
398             let subst_ptr = path_buffer.offset(subst_off as isize);
399             let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize);
400             // Absolute paths start with an NT internal namespace prefix `\??\`
401             // We should not let it leak through.
402             if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
403                 subst = &subst[4..];
404             }
405             Ok(PathBuf::from(OsString::from_wide(subst)))
406         }
407     }
408
409     pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
410         let mut info = c::FILE_BASIC_INFO {
411             CreationTime: 0,
412             LastAccessTime: 0,
413             LastWriteTime: 0,
414             ChangeTime: 0,
415             FileAttributes: perm.attrs,
416         };
417         let size = mem::size_of_val(&info);
418         cvt(unsafe {
419             c::SetFileInformationByHandle(self.handle.raw(),
420                                           c::FileBasicInfo,
421                                           &mut info as *mut _ as *mut _,
422                                           size as c::DWORD)
423         })?;
424         Ok(())
425     }
426 }
427
428 impl FromInner<c::HANDLE> for File {
429     fn from_inner(handle: c::HANDLE) -> File {
430         File { handle: Handle::new(handle) }
431     }
432 }
433
434 impl fmt::Debug for File {
435     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
436         // FIXME(#24570): add more info here (e.g., mode)
437         let mut b = f.debug_struct("File");
438         b.field("handle", &self.handle.raw());
439         if let Ok(path) = get_path(&self) {
440             b.field("path", &path);
441         }
442         b.finish()
443     }
444 }
445
446 impl FileAttr {
447     pub fn size(&self) -> u64 {
448         self.file_size
449     }
450
451     pub fn perm(&self) -> FilePermissions {
452         FilePermissions { attrs: self.attributes }
453     }
454
455     pub fn attrs(&self) -> u32 { self.attributes as u32 }
456
457     pub fn file_type(&self) -> FileType {
458         FileType::new(self.attributes, self.reparse_tag)
459     }
460
461     pub fn modified(&self) -> io::Result<SystemTime> {
462         Ok(SystemTime::from(self.last_write_time))
463     }
464
465     pub fn accessed(&self) -> io::Result<SystemTime> {
466         Ok(SystemTime::from(self.last_access_time))
467     }
468
469     pub fn created(&self) -> io::Result<SystemTime> {
470         Ok(SystemTime::from(self.creation_time))
471     }
472
473     pub fn modified_u64(&self) -> u64 {
474         to_u64(&self.last_write_time)
475     }
476
477     pub fn accessed_u64(&self) -> u64 {
478         to_u64(&self.last_access_time)
479     }
480
481     pub fn created_u64(&self) -> u64 {
482         to_u64(&self.creation_time)
483     }
484
485     fn is_reparse_point(&self) -> bool {
486         self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
487     }
488 }
489
490 fn to_u64(ft: &c::FILETIME) -> u64 {
491     (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
492 }
493
494 impl FilePermissions {
495     pub fn readonly(&self) -> bool {
496         self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
497     }
498
499     pub fn set_readonly(&mut self, readonly: bool) {
500         if readonly {
501             self.attrs |= c::FILE_ATTRIBUTE_READONLY;
502         } else {
503             self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
504         }
505     }
506 }
507
508 impl FileType {
509     fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType {
510         FileType {
511             attributes: attrs,
512             reparse_tag: reparse_tag,
513         }
514     }
515     pub fn is_dir(&self) -> bool {
516         !self.is_symlink() && self.is_directory()
517     }
518     pub fn is_file(&self) -> bool {
519         !self.is_symlink() && !self.is_directory()
520     }
521     pub fn is_symlink(&self) -> bool {
522         self.is_reparse_point() && self.is_reparse_tag_name_surrogate()
523     }
524     pub fn is_symlink_dir(&self) -> bool {
525         self.is_symlink() && self.is_directory()
526     }
527     pub fn is_symlink_file(&self) -> bool {
528         self.is_symlink() && !self.is_directory()
529     }
530     fn is_directory(&self) -> bool {
531         self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
532     }
533     fn is_reparse_point(&self) -> bool {
534         self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
535     }
536     fn is_reparse_tag_name_surrogate(&self) -> bool {
537         self.reparse_tag & 0x20000000 != 0
538     }
539 }
540
541 impl DirBuilder {
542     pub fn new() -> DirBuilder { DirBuilder }
543
544     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
545         let p = to_u16s(p)?;
546         cvt(unsafe {
547             c::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
548         })?;
549         Ok(())
550     }
551 }
552
553 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
554     let root = p.to_path_buf();
555     let star = p.join("*");
556     let path = to_u16s(&star)?;
557
558     unsafe {
559         let mut wfd = mem::zeroed();
560         let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
561         if find_handle != c::INVALID_HANDLE_VALUE {
562             Ok(ReadDir {
563                 handle: FindNextFileHandle(find_handle),
564                 root: Arc::new(root),
565                 first: Some(wfd),
566             })
567         } else {
568             Err(Error::last_os_error())
569         }
570     }
571 }
572
573 pub fn unlink(p: &Path) -> io::Result<()> {
574     let p_u16s = to_u16s(p)?;
575     cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
576     Ok(())
577 }
578
579 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
580     let old = to_u16s(old)?;
581     let new = to_u16s(new)?;
582     cvt(unsafe {
583         c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING)
584     })?;
585     Ok(())
586 }
587
588 pub fn rmdir(p: &Path) -> io::Result<()> {
589     let p = to_u16s(p)?;
590     cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
591     Ok(())
592 }
593
594 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
595     let filetype = lstat(path)?.file_type();
596     if filetype.is_symlink() {
597         // On Windows symlinks to files and directories are removed differently.
598         // rmdir only deletes dir symlinks and junctions, not file symlinks.
599         rmdir(path)
600     } else {
601         remove_dir_all_recursive(path)
602     }
603 }
604
605 fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
606     for child in readdir(path)? {
607         let child = child?;
608         let child_type = child.file_type()?;
609         if child_type.is_dir() {
610             remove_dir_all_recursive(&child.path())?;
611         } else if child_type.is_symlink_dir() {
612             rmdir(&child.path())?;
613         } else {
614             unlink(&child.path())?;
615         }
616     }
617     rmdir(path)
618 }
619
620 pub fn readlink(path: &Path) -> io::Result<PathBuf> {
621     // Open the link with no access mode, instead of generic read.
622     // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
623     // this is needed for a common case.
624     let mut opts = OpenOptions::new();
625     opts.access_mode(0);
626     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT |
627                       c::FILE_FLAG_BACKUP_SEMANTICS);
628     let file = File::open(&path, &opts)?;
629     file.readlink()
630 }
631
632 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
633     symlink_inner(src, dst, false)
634 }
635
636 pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
637     let src = to_u16s(src)?;
638     let dst = to_u16s(dst)?;
639     let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
640     // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
641     // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
642     // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
643     // added to dwFlags to opt into this behaviour.
644     let result = cvt(unsafe {
645         c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(),
646                                flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) as c::BOOL
647     });
648     if let Err(err) = result {
649         if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) {
650             // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
651             // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
652             cvt(unsafe {
653                 c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as c::BOOL
654             })?;
655         } else {
656             return Err(err);
657         }
658     }
659     Ok(())
660 }
661
662 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
663     let src = to_u16s(src)?;
664     let dst = to_u16s(dst)?;
665     cvt(unsafe {
666         c::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
667     })?;
668     Ok(())
669 }
670
671 pub fn stat(path: &Path) -> io::Result<FileAttr> {
672     let mut opts = OpenOptions::new();
673     // No read or write permissions are necessary
674     opts.access_mode(0);
675     // This flag is so we can open directories too
676     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
677     let file = File::open(path, &opts)?;
678     file.file_attr()
679 }
680
681 pub fn lstat(path: &Path) -> io::Result<FileAttr> {
682     let mut opts = OpenOptions::new();
683     // No read or write permissions are necessary
684     opts.access_mode(0);
685     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
686     let file = File::open(path, &opts)?;
687     file.file_attr()
688 }
689
690 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
691     let p = to_u16s(p)?;
692     unsafe {
693         cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
694         Ok(())
695     }
696 }
697
698 fn get_path(f: &File) -> io::Result<PathBuf> {
699     super::fill_utf16_buf(|buf, sz| unsafe {
700         c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz,
701                                      c::VOLUME_NAME_DOS)
702     }, |buf| {
703         PathBuf::from(OsString::from_wide(buf))
704     })
705 }
706
707 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
708     let mut opts = OpenOptions::new();
709     // No read or write permissions are necessary
710     opts.access_mode(0);
711     // This flag is so we can open directories too
712     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
713     let f = File::open(p, &opts)?;
714     get_path(&f)
715 }
716
717 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
718     unsafe extern "system" fn callback(
719         _TotalFileSize: c::LARGE_INTEGER,
720         _TotalBytesTransferred: c::LARGE_INTEGER,
721         _StreamSize: c::LARGE_INTEGER,
722         StreamBytesTransferred: c::LARGE_INTEGER,
723         dwStreamNumber: c::DWORD,
724         _dwCallbackReason: c::DWORD,
725         _hSourceFile: c::HANDLE,
726         _hDestinationFile: c::HANDLE,
727         lpData: c::LPVOID,
728     ) -> c::DWORD {
729         if dwStreamNumber == 1 {*(lpData as *mut i64) = StreamBytesTransferred;}
730         c::PROGRESS_CONTINUE
731     }
732     let pfrom = to_u16s(from)?;
733     let pto = to_u16s(to)?;
734     let mut size = 0i64;
735     cvt(unsafe {
736         c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),
737                        &mut size as *mut _ as *mut _, ptr::null_mut(), 0)
738     })?;
739     Ok(size as u64)
740 }
741
742 #[allow(dead_code)]
743 pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
744     symlink_junction_inner(src.as_ref(), dst.as_ref())
745 }
746
747 // Creating a directory junction on windows involves dealing with reparse
748 // points and the DeviceIoControl function, and this code is a skeleton of
749 // what can be found here:
750 //
751 // http://www.flexhex.com/docs/articles/hard-links.phtml
752 #[allow(dead_code)]
753 fn symlink_junction_inner(target: &Path, junction: &Path) -> io::Result<()> {
754     let d = DirBuilder::new();
755     d.mkdir(&junction)?;
756
757     let mut opts = OpenOptions::new();
758     opts.write(true);
759     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT |
760                       c::FILE_FLAG_BACKUP_SEMANTICS);
761     let f = File::open(junction, &opts)?;
762     let h = f.handle().raw();
763
764     unsafe {
765         let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
766         let db = data.as_mut_ptr()
767                     as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
768         let buf = &mut (*db).ReparseTarget as *mut c::WCHAR;
769         let mut i = 0;
770         // FIXME: this conversion is very hacky
771         let v = br"\??\";
772         let v = v.iter().map(|x| *x as u16);
773         for c in v.chain(target.as_os_str().encode_wide()) {
774             *buf.offset(i) = c;
775             i += 1;
776         }
777         *buf.offset(i) = 0;
778         i += 1;
779         (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
780         (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
781         (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD;
782         (*db).ReparseDataLength =
783                 (*db).ReparseTargetLength as c::DWORD + 12;
784
785         let mut ret = 0;
786         cvt(c::DeviceIoControl(h as *mut _,
787                                c::FSCTL_SET_REPARSE_POINT,
788                                data.as_ptr() as *mut _,
789                                (*db).ReparseDataLength + 8,
790                                ptr::null_mut(), 0,
791                                &mut ret,
792                                ptr::null_mut())).map(|_| ())
793     }
794 }