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