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