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.
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.
13 use os::windows::prelude::*;
16 use ffi::{OsString, AsOsStr};
18 use io::{self, Error, SeekFrom};
19 use libc::{self, HANDLE};
21 use path::{Path, PathBuf};
25 use sys::handle::Handle;
27 use sys_common::FromInner;
30 pub struct File { handle: Handle }
31 pub struct FileAttr { data: c::WIN32_FILE_ATTRIBUTE_DATA }
34 handle: FindNextFileHandle,
36 first: Option<libc::WIN32_FIND_DATAW>,
39 struct FindNextFileHandle(libc::HANDLE);
41 unsafe impl Send for FindNextFileHandle {}
42 unsafe impl Sync for FindNextFileHandle {}
46 data: libc::WIN32_FIND_DATAW,
49 #[derive(Clone, Default)]
50 pub struct OpenOptions {
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>,
62 #[derive(Clone, PartialEq, Eq, Debug)]
63 pub struct FilePermissions { attrs: libc::DWORD }
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) {
74 let mut wfd = mem::zeroed();
76 if libc::FindNextFileW(self.handle.0, &mut wfd) == 0 {
77 if libc::GetLastError() ==
78 c::ERROR_NO_MORE_FILES as libc::DWORD {
81 return Some(Err(Error::last_os_error()))
84 if let Some(e) = DirEntry::new(&self.root, &wfd) {
92 impl Drop for FindNextFileHandle {
94 let r = unsafe { libc::FindClose(self.0) };
95 debug_assert!(r != 0);
100 fn new(root: &Arc<PathBuf>, wfd: &libc::WIN32_FIND_DATAW) -> Option<DirEntry> {
101 match &wfd.cFileName[0..3] {
102 // check for '.' and '..'
104 [46, 46, 0, ..] => return None,
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))
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);
130 pub fn flags_and_attributes(&mut self, val: i32) {
131 self.flags_and_attributes = Some(val as libc::DWORD);
133 pub fn desired_access(&mut self, val: i32) {
134 self.desired_access = Some(val as libc::DWORD);
136 pub fn share_mode(&mut self, val: i32) {
137 self.share_mode = Some(val as libc::DWORD);
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};
145 base &= !libc::FILE_WRITE_DATA;
146 base |= libc::FILE_APPEND_DATA;
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)
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,
168 if self.write && !self.append {
171 libc::TRUNCATE_EXISTING
178 fn get_flags_and_attributes(&self) -> libc::DWORD {
179 self.flags_and_attributes.unwrap_or(libc::FILE_ATTRIBUTE_NORMAL)
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(),
191 opts.get_creation_disposition(),
192 opts.get_flags_and_attributes(),
195 if handle == libc::INVALID_HANDLE_VALUE {
196 Err(Error::last_os_error())
198 Ok(File { handle: Handle::new(handle) })
202 pub fn fsync(&self) -> io::Result<()> {
203 try!(cvt(unsafe { libc::FlushFileBuffers(self.handle.raw()) }));
207 pub fn datasync(&self) -> io::Result<()> { self.fsync() }
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,
213 let size = mem::size_of_val(&info);
215 c::SetFileInformationByHandle(self.handle.raw(),
216 c::FileEndOfFileInfo,
217 &mut info as *mut _ as *mut _,
223 pub fn file_attr(&self) -> io::Result<FileAttr> {
225 let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
226 try!(cvt(c::GetFileInformationByHandle(self.handle.raw(),
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,
241 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
242 self.handle.read(buf)
245 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
246 self.handle.write(buf)
249 pub fn flush(&self) -> io::Result<()> { Ok(()) }
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),
257 let pos = pos as libc::LARGE_INTEGER;
260 libc::SetFilePointerEx(self.handle.raw(), pos,
266 pub fn handle(&self) -> &Handle { &self.handle }
269 impl FromInner<libc::HANDLE> for File {
270 fn from_inner(handle: libc::HANDLE) -> File {
271 File { handle: Handle::new(handle) }
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())
284 pub fn to_utf16(s: &Path) -> Vec<u16> {
285 s.as_os_str().encode_wide().chain(Some(0).into_iter()).collect()
289 pub fn is_dir(&self) -> bool {
290 self.data.dwFileAttributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
292 pub fn is_file(&self) -> bool {
295 pub fn size(&self) -> u64 {
296 ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64)
298 pub fn perm(&self) -> FilePermissions {
299 FilePermissions { attrs: self.data.dwFileAttributes }
302 pub fn accessed(&self) -> u64 { self.to_ms(&self.data.ftLastAccessTime) }
303 pub fn modified(&self) -> u64 { self.to_ms(&self.data.ftLastWriteTime) }
305 fn to_ms(&self, ft: &libc::FILETIME) -> u64 {
306 // FILETIME is in 100ns intervals and there are 10000 intervals in a
308 let bits = (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32);
313 impl FilePermissions {
314 pub fn readonly(&self) -> bool {
315 self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
318 pub fn set_readonly(&mut self, readonly: bool) {
320 self.attrs |= c::FILE_ATTRIBUTE_READONLY;
322 self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
327 pub fn mkdir(p: &Path) -> io::Result<()> {
330 libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut())
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);
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 {
345 handle: FindNextFileHandle(find_handle),
346 root: Arc::new(root),
350 Err(Error::last_os_error())
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()) }));
361 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
362 let old = to_utf16(old);
363 let new = to_utf16(new);
365 libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
366 libc::MOVEFILE_REPLACE_EXISTING)
371 pub fn rmdir(p: &Path) -> io::Result<()> {
373 try!(cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) }));
377 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
378 let mut opts = OpenOptions::new();
380 opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32);
381 let file = try!(File::open(p, &opts));
383 let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
388 c::DeviceIoControl(file.handle.raw(),
389 c::FSCTL_GET_REPARSE_POINT,
392 space.as_mut_ptr() as *mut _,
393 space.len() as libc::DWORD,
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"))
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);
409 Ok(PathBuf::from(OsString::from_wide(subst)))
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);
419 CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
424 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
425 let src = to_utf16(src);
426 let dst = to_utf16(dst);
428 libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::null_mut())
433 pub fn stat(p: &Path) -> io::Result<FileAttr> {
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 _)));
444 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
447 try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs)));
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);
456 let mut o = OpenOptions::new();
458 let f = try!(File::open(p, &o));
460 c::SetFileTime(f.handle.raw(), 0 as *const _, &atime, &mtime)