]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/windows/fs2.rs
rollup merge of #24962: tamird/unignore-android-tests
[rust.git] / src / libstd / sys / windows / fs2.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 core::prelude::*;
12 use io::prelude::*;
13 use os::windows::prelude::*;
14
15 use ffi::OsString;
16 use fmt;
17 use io::{self, Error, SeekFrom};
18 use libc::{self, HANDLE};
19 use mem;
20 use path::{Path, PathBuf};
21 use ptr;
22 use slice;
23 use sync::Arc;
24 use sys::handle::Handle;
25 use sys::{c, cvt};
26 use sys_common::FromInner;
27 use vec::Vec;
28
29 pub struct File { handle: Handle }
30
31 pub struct FileAttr {
32     data: c::WIN32_FILE_ATTRIBUTE_DATA,
33     is_symlink: bool,
34 }
35
36 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
37 pub enum FileType {
38     Dir, File, Symlink, ReparsePoint
39 }
40
41 pub struct ReadDir {
42     handle: FindNextFileHandle,
43     root: Arc<PathBuf>,
44     first: Option<libc::WIN32_FIND_DATAW>,
45 }
46
47 struct FindNextFileHandle(libc::HANDLE);
48
49 unsafe impl Send for FindNextFileHandle {}
50 unsafe impl Sync for FindNextFileHandle {}
51
52 pub struct DirEntry {
53     root: Arc<PathBuf>,
54     data: libc::WIN32_FIND_DATAW,
55 }
56
57 #[derive(Clone, Default)]
58 pub struct OpenOptions {
59     create: bool,
60     append: bool,
61     read: bool,
62     write: bool,
63     truncate: bool,
64     desired_access: Option<libc::DWORD>,
65     share_mode: Option<libc::DWORD>,
66     creation_disposition: Option<libc::DWORD>,
67     flags_and_attributes: Option<libc::DWORD>,
68 }
69
70 #[derive(Clone, PartialEq, Eq, Debug)]
71 pub struct FilePermissions { attrs: libc::DWORD }
72
73 pub struct DirBuilder;
74
75 impl Iterator for ReadDir {
76     type Item = io::Result<DirEntry>;
77     fn next(&mut self) -> Option<io::Result<DirEntry>> {
78         if let Some(first) = self.first.take() {
79             if let Some(e) = DirEntry::new(&self.root, &first) {
80                 return Some(Ok(e));
81             }
82         }
83         unsafe {
84             let mut wfd = mem::zeroed();
85             loop {
86                 if libc::FindNextFileW(self.handle.0, &mut wfd) == 0 {
87                     if libc::GetLastError() ==
88                         c::ERROR_NO_MORE_FILES as libc::DWORD {
89                         return None
90                     } else {
91                         return Some(Err(Error::last_os_error()))
92                     }
93                 }
94                 if let Some(e) = DirEntry::new(&self.root, &wfd) {
95                     return Some(Ok(e))
96                 }
97             }
98         }
99     }
100 }
101
102 impl Drop for FindNextFileHandle {
103     fn drop(&mut self) {
104         let r = unsafe { libc::FindClose(self.0) };
105         debug_assert!(r != 0);
106     }
107 }
108
109 impl DirEntry {
110     fn new(root: &Arc<PathBuf>, wfd: &libc::WIN32_FIND_DATAW) -> Option<DirEntry> {
111         match &wfd.cFileName[0..3] {
112             // check for '.' and '..'
113             [46, 0, ..] |
114             [46, 46, 0, ..] => return None,
115             _ => {}
116         }
117
118         Some(DirEntry {
119             root: root.clone(),
120             data: *wfd,
121         })
122     }
123
124     pub fn path(&self) -> PathBuf {
125         self.root.join(&self.file_name())
126     }
127
128     pub fn file_name(&self) -> OsString {
129         let filename = super::truncate_utf16_at_nul(&self.data.cFileName);
130         OsString::from_wide(filename)
131     }
132
133     pub fn file_type(&self) -> io::Result<FileType> {
134         Ok(FileType::new(self.data.dwFileAttributes,
135                          self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK))
136     }
137
138     pub fn metadata(&self) -> io::Result<FileAttr> {
139         Ok(FileAttr {
140             data: c::WIN32_FILE_ATTRIBUTE_DATA {
141                 dwFileAttributes: self.data.dwFileAttributes,
142                 ftCreationTime: self.data.ftCreationTime,
143                 ftLastAccessTime: self.data.ftLastAccessTime,
144                 ftLastWriteTime: self.data.ftLastWriteTime,
145                 nFileSizeHigh: self.data.nFileSizeHigh,
146                 nFileSizeLow: self.data.nFileSizeLow,
147             },
148             is_symlink: self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK,
149         })
150     }
151 }
152
153 impl OpenOptions {
154     pub fn new() -> OpenOptions { Default::default() }
155     pub fn read(&mut self, read: bool) { self.read = read; }
156     pub fn write(&mut self, write: bool) { self.write = write; }
157     pub fn append(&mut self, append: bool) { self.append = append; }
158     pub fn create(&mut self, create: bool) { self.create = create; }
159     pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; }
160     pub fn creation_disposition(&mut self, val: i32) {
161         self.creation_disposition = Some(val as libc::DWORD);
162     }
163     pub fn flags_and_attributes(&mut self, val: i32) {
164         self.flags_and_attributes = Some(val as libc::DWORD);
165     }
166     pub fn desired_access(&mut self, val: i32) {
167         self.desired_access = Some(val as libc::DWORD);
168     }
169     pub fn share_mode(&mut self, val: i32) {
170         self.share_mode = Some(val as libc::DWORD);
171     }
172
173     fn get_desired_access(&self) -> libc::DWORD {
174         self.desired_access.unwrap_or({
175             let mut base = if self.read {libc::FILE_GENERIC_READ} else {0} |
176                            if self.write {libc::FILE_GENERIC_WRITE} else {0};
177             if self.append {
178                 base &= !libc::FILE_WRITE_DATA;
179                 base |= libc::FILE_APPEND_DATA;
180             }
181             base
182         })
183     }
184
185     fn get_share_mode(&self) -> libc::DWORD {
186         // libuv has a good comment about this, but the basic idea is that
187         // we try to emulate unix semantics by enabling all sharing by
188         // allowing things such as deleting a file while it's still open.
189         self.share_mode.unwrap_or(libc::FILE_SHARE_READ |
190                                   libc::FILE_SHARE_WRITE |
191                                   libc::FILE_SHARE_DELETE)
192     }
193
194     fn get_creation_disposition(&self) -> libc::DWORD {
195         self.creation_disposition.unwrap_or({
196             match (self.create, self.truncate) {
197                 (true, true) => libc::CREATE_ALWAYS,
198                 (true, false) => libc::OPEN_ALWAYS,
199                 (false, false) => libc::OPEN_EXISTING,
200                 (false, true) => {
201                     if self.write && !self.append {
202                         libc::CREATE_ALWAYS
203                     } else {
204                         libc::TRUNCATE_EXISTING
205                     }
206                 }
207             }
208         })
209     }
210
211     fn get_flags_and_attributes(&self) -> libc::DWORD {
212         self.flags_and_attributes.unwrap_or(libc::FILE_ATTRIBUTE_NORMAL)
213     }
214 }
215
216 impl File {
217     fn open_reparse_point(path: &Path) -> io::Result<File> {
218         let mut opts = OpenOptions::new();
219         opts.read(true);
220         opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32);
221         File::open(path, &opts)
222     }
223
224     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
225         let path = to_utf16(path);
226         let handle = unsafe {
227             libc::CreateFileW(path.as_ptr(),
228                               opts.get_desired_access(),
229                               opts.get_share_mode(),
230                               ptr::null_mut(),
231                               opts.get_creation_disposition(),
232                               opts.get_flags_and_attributes(),
233                               ptr::null_mut())
234         };
235         if handle == libc::INVALID_HANDLE_VALUE {
236             Err(Error::last_os_error())
237         } else {
238             Ok(File { handle: Handle::new(handle) })
239         }
240     }
241
242     pub fn fsync(&self) -> io::Result<()> {
243         try!(cvt(unsafe { libc::FlushFileBuffers(self.handle.raw()) }));
244         Ok(())
245     }
246
247     pub fn datasync(&self) -> io::Result<()> { self.fsync() }
248
249     pub fn truncate(&self, size: u64) -> io::Result<()> {
250         let mut info = c::FILE_END_OF_FILE_INFO {
251             EndOfFile: size as libc::LARGE_INTEGER,
252         };
253         let size = mem::size_of_val(&info);
254         try!(cvt(unsafe {
255             c::SetFileInformationByHandle(self.handle.raw(),
256                                           c::FileEndOfFileInfo,
257                                           &mut info as *mut _ as *mut _,
258                                           size as libc::DWORD)
259         }));
260         Ok(())
261     }
262
263     pub fn file_attr(&self) -> io::Result<FileAttr> {
264         unsafe {
265             let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
266             try!(cvt(c::GetFileInformationByHandle(self.handle.raw(),
267                                                    &mut info)));
268             let mut attr = FileAttr {
269                 data: c::WIN32_FILE_ATTRIBUTE_DATA {
270                     dwFileAttributes: info.dwFileAttributes,
271                     ftCreationTime: info.ftCreationTime,
272                     ftLastAccessTime: info.ftLastAccessTime,
273                     ftLastWriteTime: info.ftLastWriteTime,
274                     nFileSizeHigh: info.nFileSizeHigh,
275                     nFileSizeLow: info.nFileSizeLow,
276                 },
277                 is_symlink: false,
278             };
279             if attr.is_reparse_point() {
280                 attr.is_symlink = self.is_symlink();
281             }
282             Ok(attr)
283         }
284     }
285
286     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
287         self.handle.read(buf)
288     }
289
290     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
291         self.handle.write(buf)
292     }
293
294     pub fn flush(&self) -> io::Result<()> { Ok(()) }
295
296     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
297         let (whence, pos) = match pos {
298             SeekFrom::Start(n) => (libc::FILE_BEGIN, n as i64),
299             SeekFrom::End(n) => (libc::FILE_END, n),
300             SeekFrom::Current(n) => (libc::FILE_CURRENT, n),
301         };
302         let pos = pos as libc::LARGE_INTEGER;
303         let mut newpos = 0;
304         try!(cvt(unsafe {
305             libc::SetFilePointerEx(self.handle.raw(), pos,
306                                    &mut newpos, whence)
307         }));
308         Ok(newpos as u64)
309     }
310
311     pub fn handle(&self) -> &Handle { &self.handle }
312
313     fn is_symlink(&self) -> bool {
314         self.readlink().is_ok()
315     }
316
317     fn readlink(&self) -> io::Result<PathBuf> {
318         let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
319         let mut bytes = 0;
320
321         unsafe {
322             try!(cvt({
323                 c::DeviceIoControl(self.handle.raw(),
324                                    c::FSCTL_GET_REPARSE_POINT,
325                                    0 as *mut _,
326                                    0,
327                                    space.as_mut_ptr() as *mut _,
328                                    space.len() as libc::DWORD,
329                                    &mut bytes,
330                                    0 as *mut _)
331             }));
332             let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _;
333             if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK {
334                 return Err(io::Error::new(io::ErrorKind::Other, "not a symlink"))
335             }
336             let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
337                     &(*buf).rest as *const _ as *const _;
338             let path_buffer = &(*info).PathBuffer as *const _ as *const u16;
339             let subst_off = (*info).SubstituteNameOffset / 2;
340             let subst_ptr = path_buffer.offset(subst_off as isize);
341             let subst_len = (*info).SubstituteNameLength / 2;
342             let subst = slice::from_raw_parts(subst_ptr, subst_len as usize);
343
344             Ok(PathBuf::from(OsString::from_wide(subst)))
345         }
346     }
347 }
348
349 impl FromInner<libc::HANDLE> for File {
350     fn from_inner(handle: libc::HANDLE) -> File {
351         File { handle: Handle::new(handle) }
352     }
353 }
354
355 impl fmt::Debug for File {
356     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357         // FIXME(#24570): add more info here (e.g. path, mode)
358         f.debug_struct("File")
359             .field("handle", &self.handle.raw())
360             .finish()
361     }
362 }
363
364 pub fn to_utf16(s: &Path) -> Vec<u16> {
365     s.as_os_str().encode_wide().chain(Some(0).into_iter()).collect()
366 }
367
368 impl FileAttr {
369     pub fn size(&self) -> u64 {
370         ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64)
371     }
372
373     pub fn perm(&self) -> FilePermissions {
374         FilePermissions { attrs: self.data.dwFileAttributes }
375     }
376
377     pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 }
378
379     pub fn file_type(&self) -> FileType {
380         FileType::new(self.data.dwFileAttributes, self.is_symlink)
381     }
382
383     pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) }
384     pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) }
385     pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) }
386
387     fn to_u64(&self, ft: &libc::FILETIME) -> u64 {
388         (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
389     }
390
391     fn is_reparse_point(&self) -> bool {
392         self.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0
393     }
394 }
395
396 impl FilePermissions {
397     pub fn readonly(&self) -> bool {
398         self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
399     }
400
401     pub fn set_readonly(&mut self, readonly: bool) {
402         if readonly {
403             self.attrs |= c::FILE_ATTRIBUTE_READONLY;
404         } else {
405             self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
406         }
407     }
408 }
409
410 impl FileType {
411     fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType {
412         if attrs & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
413             if is_symlink {
414                 FileType::Symlink
415             } else {
416                 FileType::ReparsePoint
417             }
418         } else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 {
419             FileType::Dir
420         } else {
421             FileType::File
422         }
423     }
424
425     pub fn is_dir(&self) -> bool { *self == FileType::Dir }
426     pub fn is_file(&self) -> bool { *self == FileType::File }
427     pub fn is_symlink(&self) -> bool { *self == FileType::Symlink }
428 }
429
430 impl DirBuilder {
431     pub fn new() -> DirBuilder { DirBuilder }
432
433     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
434         let p = to_utf16(p);
435         try!(cvt(unsafe {
436             libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
437         }));
438         Ok(())
439     }
440 }
441
442 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
443     let root = p.to_path_buf();
444     let star = p.join("*");
445     let path = to_utf16(&star);
446
447     unsafe {
448         let mut wfd = mem::zeroed();
449         let find_handle = libc::FindFirstFileW(path.as_ptr(), &mut wfd);
450         if find_handle != libc::INVALID_HANDLE_VALUE {
451             Ok(ReadDir {
452                 handle: FindNextFileHandle(find_handle),
453                 root: Arc::new(root),
454                 first: Some(wfd),
455             })
456         } else {
457             Err(Error::last_os_error())
458         }
459     }
460 }
461
462 pub fn unlink(p: &Path) -> io::Result<()> {
463     let p_utf16 = to_utf16(p);
464     try!(cvt(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) }));
465     Ok(())
466 }
467
468 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
469     let old = to_utf16(old);
470     let new = to_utf16(new);
471     try!(cvt(unsafe {
472         libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
473                           libc::MOVEFILE_REPLACE_EXISTING)
474     }));
475     Ok(())
476 }
477
478 pub fn rmdir(p: &Path) -> io::Result<()> {
479     let p = to_utf16(p);
480     try!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }));
481     Ok(())
482 }
483
484 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
485     let file = try!(File::open_reparse_point(p));
486     file.readlink()
487 }
488
489 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
490     symlink_inner(src, dst, false)
491 }
492
493 pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
494     use sys::c::compat::kernel32::CreateSymbolicLinkW;
495     let src = to_utf16(src);
496     let dst = to_utf16(dst);
497     let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
498     try!(cvt(unsafe {
499         CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as libc::BOOL
500     }));
501     Ok(())
502 }
503
504 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
505     let src = to_utf16(src);
506     let dst = to_utf16(dst);
507     try!(cvt(unsafe {
508         libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
509     }));
510     Ok(())
511 }
512
513 pub fn stat(p: &Path) -> io::Result<FileAttr> {
514     let attr = try!(lstat(p));
515     if attr.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
516         let opts = OpenOptions::new();
517         let file = try!(File::open(p, &opts));
518         file.file_attr()
519     } else {
520         Ok(attr)
521     }
522 }
523
524 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
525     let utf16 = to_utf16(p);
526     unsafe {
527         let mut attr: FileAttr = mem::zeroed();
528         try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(),
529                                          c::GetFileExInfoStandard,
530                                          &mut attr.data as *mut _ as *mut _)));
531         if attr.is_reparse_point() {
532             attr.is_symlink = File::open_reparse_point(p).map(|f| {
533                 f.is_symlink()
534             }).unwrap_or(false);
535         }
536         Ok(attr)
537     }
538 }
539
540 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
541     let p = to_utf16(p);
542     unsafe {
543         try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs)));
544         Ok(())
545     }
546 }
547
548 pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
549     let atime = super::ms_to_filetime(atime);
550     let mtime = super::ms_to_filetime(mtime);
551
552     let mut o = OpenOptions::new();
553     o.write(true);
554     let f = try!(File::open(p, &o));
555     try!(cvt(unsafe {
556         c::SetFileTime(f.handle.raw(), 0 as *const _, &atime, &mtime)
557     }));
558     Ok(())
559 }
560
561 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
562     use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
563
564     let mut opts = OpenOptions::new();
565     opts.read(true);
566     let f = try!(File::open(p, &opts));
567     super::fill_utf16_buf(|buf, sz| unsafe {
568         GetFinalPathNameByHandleW(f.handle.raw(), buf, sz,
569                                   libc::VOLUME_NAME_DOS)
570     }, |buf| {
571         PathBuf::from(OsString::from_wide(buf))
572     })
573 }