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