]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/fs.rs
Auto merge of #34757 - sourcefrog:debug-filetype, r=alexcrichton
[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 prelude::v1::*;
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, Debug)]
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 = to_u16s(path)?;
251         let handle = unsafe {
252             c::CreateFileW(path.as_ptr(),
253                            opts.get_access_mode()?,
254                            opts.share_mode,
255                            opts.security_attributes as *mut _,
256                            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         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         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             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 read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
316         self.handle.read_to_end(buf)
317     }
318
319     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
320         self.handle.write(buf)
321     }
322
323     pub fn flush(&self) -> io::Result<()> { Ok(()) }
324
325     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
326         let (whence, pos) = match pos {
327             SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64),
328             SeekFrom::End(n) => (c::FILE_END, n),
329             SeekFrom::Current(n) => (c::FILE_CURRENT, n),
330         };
331         let pos = pos as c::LARGE_INTEGER;
332         let mut newpos = 0;
333         cvt(unsafe {
334             c::SetFilePointerEx(self.handle.raw(), pos,
335                                 &mut newpos, whence)
336         })?;
337         Ok(newpos as u64)
338     }
339
340     pub fn duplicate(&self) -> io::Result<File> {
341         Ok(File {
342             handle: self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)?,
343         })
344     }
345
346     pub fn handle(&self) -> &Handle { &self.handle }
347
348     pub fn into_handle(self) -> Handle { self.handle }
349
350     fn reparse_point<'a>(&self,
351                          space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE])
352                          -> io::Result<(c::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
353         unsafe {
354             let mut bytes = 0;
355             cvt({
356                 c::DeviceIoControl(self.handle.raw(),
357                                    c::FSCTL_GET_REPARSE_POINT,
358                                    ptr::null_mut(),
359                                    0,
360                                    space.as_mut_ptr() as *mut _,
361                                    space.len() as c::DWORD,
362                                    &mut bytes,
363                                    ptr::null_mut())
364             })?;
365             Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
366         }
367     }
368
369     fn readlink(&self) -> io::Result<PathBuf> {
370         let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
371         let (_bytes, buf) = self.reparse_point(&mut space)?;
372         unsafe {
373             let (path_buffer, subst_off, subst_len, relative) = match buf.ReparseTag {
374                 c::IO_REPARSE_TAG_SYMLINK => {
375                     let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
376                         &buf.rest as *const _ as *const _;
377                     (&(*info).PathBuffer as *const _ as *const u16,
378                      (*info).SubstituteNameOffset / 2,
379                      (*info).SubstituteNameLength / 2,
380                      (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0)
381                 },
382                 c::IO_REPARSE_TAG_MOUNT_POINT => {
383                     let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
384                         &buf.rest as *const _ as *const _;
385                     (&(*info).PathBuffer as *const _ as *const u16,
386                      (*info).SubstituteNameOffset / 2,
387                      (*info).SubstituteNameLength / 2,
388                      false)
389                 },
390                 _ => return Err(io::Error::new(io::ErrorKind::Other,
391                                                "Unsupported reparse point type"))
392             };
393             let subst_ptr = path_buffer.offset(subst_off as isize);
394             let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize);
395             // Absolute paths start with an NT internal namespace prefix `\??\`
396             // We should not let it leak through.
397             if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
398                 subst = &subst[4..];
399             }
400             Ok(PathBuf::from(OsString::from_wide(subst)))
401         }
402     }
403 }
404
405 impl FromInner<c::HANDLE> for File {
406     fn from_inner(handle: c::HANDLE) -> File {
407         File { handle: Handle::new(handle) }
408     }
409 }
410
411 impl fmt::Debug for File {
412     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413         // FIXME(#24570): add more info here (e.g. mode)
414         let mut b = f.debug_struct("File");
415         b.field("handle", &self.handle.raw());
416         if let Ok(path) = get_path(&self) {
417             b.field("path", &path);
418         }
419         b.finish()
420     }
421 }
422
423 impl FileAttr {
424     pub fn size(&self) -> u64 {
425         self.file_size
426     }
427
428     pub fn perm(&self) -> FilePermissions {
429         FilePermissions { attrs: self.attributes }
430     }
431
432     pub fn attrs(&self) -> u32 { self.attributes as u32 }
433
434     pub fn file_type(&self) -> FileType {
435         FileType::new(self.attributes, self.reparse_tag)
436     }
437
438     pub fn modified(&self) -> io::Result<SystemTime> {
439         Ok(SystemTime::from(self.last_write_time))
440     }
441
442     pub fn accessed(&self) -> io::Result<SystemTime> {
443         Ok(SystemTime::from(self.last_access_time))
444     }
445
446     pub fn created(&self) -> io::Result<SystemTime> {
447         Ok(SystemTime::from(self.creation_time))
448     }
449
450     pub fn modified_u64(&self) -> u64 {
451         to_u64(&self.last_write_time)
452     }
453
454     pub fn accessed_u64(&self) -> u64 {
455         to_u64(&self.last_access_time)
456     }
457
458     pub fn created_u64(&self) -> u64 {
459         to_u64(&self.creation_time)
460     }
461
462     fn is_reparse_point(&self) -> bool {
463         self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
464     }
465 }
466
467 fn to_u64(ft: &c::FILETIME) -> u64 {
468     (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
469 }
470
471 impl FilePermissions {
472     pub fn readonly(&self) -> bool {
473         self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
474     }
475
476     pub fn set_readonly(&mut self, readonly: bool) {
477         if readonly {
478             self.attrs |= c::FILE_ATTRIBUTE_READONLY;
479         } else {
480             self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
481         }
482     }
483 }
484
485 impl FileType {
486     fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType {
487         match (attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0,
488                attrs & c::FILE_ATTRIBUTE_REPARSE_POINT != 0,
489                reparse_tag) {
490             (false, false, _) => FileType::File,
491             (true, false, _) => FileType::Dir,
492             (false, true, c::IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkFile,
493             (true, true, c::IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkDir,
494             (true, true, c::IO_REPARSE_TAG_MOUNT_POINT) => FileType::MountPoint,
495             (_, true, _) => FileType::ReparsePoint,
496             // Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is
497             // invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint
498             // to indicate it is something symlink-like, but not something you can follow.
499         }
500     }
501
502     pub fn is_dir(&self) -> bool { *self == FileType::Dir }
503     pub fn is_file(&self) -> bool { *self == FileType::File }
504     pub fn is_symlink(&self) -> bool {
505         *self == FileType::SymlinkFile ||
506         *self == FileType::SymlinkDir ||
507         *self == FileType::MountPoint
508     }
509     pub fn is_symlink_dir(&self) -> bool {
510         *self == FileType::SymlinkDir || *self == FileType::MountPoint
511     }
512 }
513
514 impl DirBuilder {
515     pub fn new() -> DirBuilder { DirBuilder }
516
517     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
518         let p = to_u16s(p)?;
519         cvt(unsafe {
520             c::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
521         })?;
522         Ok(())
523     }
524 }
525
526 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
527     let root = p.to_path_buf();
528     let star = p.join("*");
529     let path = to_u16s(&star)?;
530
531     unsafe {
532         let mut wfd = mem::zeroed();
533         let find_handle = c::FindFirstFileW(path.as_ptr(), &mut wfd);
534         if find_handle != c::INVALID_HANDLE_VALUE {
535             Ok(ReadDir {
536                 handle: FindNextFileHandle(find_handle),
537                 root: Arc::new(root),
538                 first: Some(wfd),
539             })
540         } else {
541             Err(Error::last_os_error())
542         }
543     }
544 }
545
546 pub fn unlink(p: &Path) -> io::Result<()> {
547     let p_u16s = to_u16s(p)?;
548     cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
549     Ok(())
550 }
551
552 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
553     let old = to_u16s(old)?;
554     let new = to_u16s(new)?;
555     cvt(unsafe {
556         c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING)
557     })?;
558     Ok(())
559 }
560
561 pub fn rmdir(p: &Path) -> io::Result<()> {
562     let p = to_u16s(p)?;
563     cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
564     Ok(())
565 }
566
567 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
568     let filetype = lstat(path)?.file_type();
569     if filetype.is_symlink() {
570         // On Windows symlinks to files and directories are removed differently.
571         // rmdir only deletes dir symlinks and junctions, not file symlinks.
572         rmdir(path)
573     } else {
574         remove_dir_all_recursive(path)
575     }
576 }
577
578 fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
579     for child in readdir(path)? {
580         let child = child?;
581         let child_type = child.file_type()?;
582         if child_type.is_dir() {
583             remove_dir_all_recursive(&child.path())?;
584         } else if child_type.is_symlink_dir() {
585             rmdir(&child.path())?;
586         } else {
587             unlink(&child.path())?;
588         }
589     }
590     rmdir(path)
591 }
592
593 pub fn readlink(path: &Path) -> io::Result<PathBuf> {
594     // Open the link with no access mode, instead of generic read.
595     // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
596     // this is needed for a common case.
597     let mut opts = OpenOptions::new();
598     opts.access_mode(0);
599     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT |
600                       c::FILE_FLAG_BACKUP_SEMANTICS);
601     let file = File::open(&path, &opts)?;
602     file.readlink()
603 }
604
605 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
606     symlink_inner(src, dst, false)
607 }
608
609 pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
610     let src = to_u16s(src)?;
611     let dst = to_u16s(dst)?;
612     let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
613     cvt(unsafe {
614         c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as c::BOOL
615     })?;
616     Ok(())
617 }
618
619 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
620     let src = to_u16s(src)?;
621     let dst = to_u16s(dst)?;
622     cvt(unsafe {
623         c::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
624     })?;
625     Ok(())
626 }
627
628 pub fn stat(path: &Path) -> io::Result<FileAttr> {
629     let mut opts = OpenOptions::new();
630     // No read or write permissions are necessary
631     opts.access_mode(0);
632     // This flag is so we can open directories too
633     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
634     let file = File::open(path, &opts)?;
635     file.file_attr()
636 }
637
638 pub fn lstat(path: &Path) -> io::Result<FileAttr> {
639     let mut opts = OpenOptions::new();
640     // No read or write permissions are necessary
641     opts.access_mode(0);
642     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
643     let file = File::open(path, &opts)?;
644     file.file_attr()
645 }
646
647 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
648     let p = to_u16s(p)?;
649     unsafe {
650         cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
651         Ok(())
652     }
653 }
654
655 fn get_path(f: &File) -> io::Result<PathBuf> {
656     super::fill_utf16_buf(|buf, sz| unsafe {
657         c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz,
658                                      c::VOLUME_NAME_DOS)
659     }, |buf| {
660         PathBuf::from(OsString::from_wide(buf))
661     })
662 }
663
664 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
665     let mut opts = OpenOptions::new();
666     // No read or write permissions are necessary
667     opts.access_mode(0);
668     // This flag is so we can open directories too
669     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
670     let f = File::open(p, &opts)?;
671     get_path(&f)
672 }
673
674 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
675     unsafe extern "system" fn callback(
676         _TotalFileSize: c::LARGE_INTEGER,
677         TotalBytesTransferred: c::LARGE_INTEGER,
678         _StreamSize: c::LARGE_INTEGER,
679         _StreamBytesTransferred: c::LARGE_INTEGER,
680         _dwStreamNumber: c::DWORD,
681         _dwCallbackReason: c::DWORD,
682         _hSourceFile: c::HANDLE,
683         _hDestinationFile: c::HANDLE,
684         lpData: c::LPVOID,
685     ) -> c::DWORD {
686         *(lpData as *mut i64) = TotalBytesTransferred;
687         c::PROGRESS_CONTINUE
688     }
689     let pfrom = to_u16s(from)?;
690     let pto = to_u16s(to)?;
691     let mut size = 0i64;
692     cvt(unsafe {
693         c::CopyFileExW(pfrom.as_ptr(), pto.as_ptr(), Some(callback),
694                        &mut size as *mut _ as *mut _, ptr::null_mut(), 0)
695     })?;
696     Ok(size as u64)
697 }
698
699 #[allow(dead_code)]
700 pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
701     symlink_junction_inner(src.as_ref(), dst.as_ref())
702 }
703
704 // Creating a directory junction on windows involves dealing with reparse
705 // points and the DeviceIoControl function, and this code is a skeleton of
706 // what can be found here:
707 //
708 // http://www.flexhex.com/docs/articles/hard-links.phtml
709 #[allow(dead_code)]
710 fn symlink_junction_inner(target: &Path, junction: &Path) -> io::Result<()> {
711     let d = DirBuilder::new();
712     d.mkdir(&junction)?;
713
714     let mut opts = OpenOptions::new();
715     opts.write(true);
716     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT |
717                       c::FILE_FLAG_BACKUP_SEMANTICS);
718     let f = File::open(junction, &opts)?;
719     let h = f.handle().raw();
720
721     unsafe {
722         let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
723         let mut db = data.as_mut_ptr()
724                         as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
725         let buf = &mut (*db).ReparseTarget as *mut _;
726         let mut i = 0;
727         // FIXME: this conversion is very hacky
728         let v = br"\??\";
729         let v = v.iter().map(|x| *x as u16);
730         for c in v.chain(target.as_os_str().encode_wide()) {
731             *buf.offset(i) = c;
732             i += 1;
733         }
734         *buf.offset(i) = 0;
735         i += 1;
736         (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
737         (*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
738         (*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD;
739         (*db).ReparseDataLength =
740                 (*db).ReparseTargetLength as c::DWORD + 12;
741
742         let mut ret = 0;
743         cvt(c::DeviceIoControl(h as *mut _,
744                                c::FSCTL_SET_REPARSE_POINT,
745                                data.as_ptr() as *mut _,
746                                (*db).ReparseDataLength + 8,
747                                ptr::null_mut(), 0,
748                                &mut ret,
749                                ptr::null_mut())).map(|_| ())
750     }
751 }