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