]> git.lizzy.rs Git - rust.git/blob - src/libnative/io/file_win32.rs
auto merge of #13967 : richo/rust/features/ICE-fails, r=alexcrichton
[rust.git] / src / libnative / io / file_win32.rs
1 // Copyright 2013-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 //! Blocking win32-based file I/O
12
13 use std::c_str::CString;
14 use std::cast;
15 use std::io::IoError;
16 use std::io;
17 use libc::{c_int, c_void};
18 use libc;
19 use std::mem;
20 use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
21 use std::ptr;
22 use std::rt::rtio;
23 use std::str;
24 use std::sync::arc::UnsafeArc;
25 use std::slice;
26
27 use io::IoResult;
28
29 pub type fd_t = libc::c_int;
30
31 struct Inner {
32     fd: fd_t,
33     close_on_drop: bool,
34 }
35
36 pub struct FileDesc {
37     inner: UnsafeArc<Inner>
38 }
39
40 impl FileDesc {
41     /// Create a `FileDesc` from an open C file descriptor.
42     ///
43     /// The `FileDesc` will take ownership of the specified file descriptor and
44     /// close it upon destruction if the `close_on_drop` flag is true, otherwise
45     /// it will not close the file descriptor when this `FileDesc` is dropped.
46     ///
47     /// Note that all I/O operations done on this object will be *blocking*, but
48     /// they do not require the runtime to be active.
49     pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
50         FileDesc { inner: UnsafeArc::new(Inner {
51             fd: fd,
52             close_on_drop: close_on_drop
53         }) }
54     }
55
56     pub fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
57         let mut read = 0;
58         let ret = unsafe {
59             libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
60                            buf.len() as libc::DWORD, &mut read,
61                            ptr::mut_null())
62         };
63         if ret != 0 {
64             Ok(read as uint)
65         } else {
66             Err(super::last_error())
67         }
68     }
69     pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
70         let mut cur = buf.as_ptr();
71         let mut remaining = buf.len();
72         while remaining > 0 {
73             let mut amt = 0;
74             let ret = unsafe {
75                 libc::WriteFile(self.handle(), cur as libc::LPVOID,
76                                 remaining as libc::DWORD, &mut amt,
77                                 ptr::mut_null())
78             };
79             if ret != 0 {
80                 remaining -= amt as uint;
81                 cur = unsafe { cur.offset(amt as int) };
82             } else {
83                 return Err(super::last_error())
84             }
85         }
86         Ok(())
87     }
88
89     pub fn fd(&self) -> fd_t {
90         // This unsafety is fine because we're just reading off the file
91         // descriptor, no one is modifying this.
92         unsafe { (*self.inner.get()).fd }
93     }
94
95     pub fn handle(&self) -> libc::HANDLE {
96         unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
97     }
98 }
99
100 impl io::Reader for FileDesc {
101     fn read(&mut self, buf: &mut [u8]) -> io::IoResult<uint> {
102         self.inner_read(buf)
103     }
104 }
105
106 impl io::Writer for FileDesc {
107     fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
108         self.inner_write(buf)
109     }
110 }
111
112 impl rtio::RtioFileStream for FileDesc {
113     fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
114         self.inner_read(buf).map(|i| i as int)
115     }
116     fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
117         self.inner_write(buf)
118     }
119
120     fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
121         let mut read = 0;
122         let mut overlap: libc::OVERLAPPED = unsafe { mem::init() };
123         overlap.Offset = offset as libc::DWORD;
124         overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
125         let ret = unsafe {
126             libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
127                            buf.len() as libc::DWORD, &mut read,
128                            &mut overlap)
129         };
130         if ret != 0 {
131             Ok(read as int)
132         } else {
133             Err(super::last_error())
134         }
135     }
136     fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> Result<(), IoError> {
137         let mut cur = buf.as_ptr();
138         let mut remaining = buf.len();
139         let mut overlap: libc::OVERLAPPED = unsafe { mem::init() };
140         while remaining > 0 {
141             overlap.Offset = offset as libc::DWORD;
142             overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
143             let mut amt = 0;
144             let ret = unsafe {
145                 libc::WriteFile(self.handle(), cur as libc::LPVOID,
146                                 remaining as libc::DWORD, &mut amt,
147                                 &mut overlap)
148             };
149             if ret != 0 {
150                 remaining -= amt as uint;
151                 cur = unsafe { cur.offset(amt as int) };
152                 offset += amt as u64;
153             } else {
154                 return Err(super::last_error())
155             }
156         }
157         Ok(())
158     }
159     fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> {
160         let whence = match style {
161             io::SeekSet => libc::FILE_BEGIN,
162             io::SeekEnd => libc::FILE_END,
163             io::SeekCur => libc::FILE_CURRENT,
164         };
165         unsafe {
166             let mut newpos = 0;
167             match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
168                                          whence) {
169                 0 => Err(super::last_error()),
170                 _ => Ok(newpos as u64),
171             }
172         }
173     }
174     fn tell(&self) -> Result<u64, IoError> {
175         // This transmute is fine because our seek implementation doesn't
176         // actually use the mutable self at all.
177         // FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
178         unsafe { cast::transmute::<&_, &mut FileDesc>(self).seek(0, io::SeekCur) }
179     }
180
181     fn fsync(&mut self) -> Result<(), IoError> {
182         super::mkerr_winbool(unsafe {
183             libc::FlushFileBuffers(self.handle())
184         })
185     }
186
187     fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); }
188
189     fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
190         let orig_pos = try!(self.tell());
191         let _ = try!(self.seek(offset, io::SeekSet));
192         let ret = unsafe {
193             match libc::SetEndOfFile(self.handle()) {
194                 0 => Err(super::last_error()),
195                 _ => Ok(())
196             }
197         };
198         let _ = self.seek(orig_pos as i64, io::SeekSet);
199         return ret;
200     }
201 }
202
203 impl rtio::RtioPipe for FileDesc {
204     fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
205         self.inner_read(buf)
206     }
207     fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
208         self.inner_write(buf)
209     }
210     fn clone(&self) -> Box<rtio::RtioPipe:Send> {
211         box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe:Send>
212     }
213 }
214
215 impl rtio::RtioTTY for FileDesc {
216     fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
217         self.inner_read(buf)
218     }
219     fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
220         self.inner_write(buf)
221     }
222     fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> {
223         Err(super::unimpl())
224     }
225     fn get_winsize(&mut self) -> Result<(int, int), IoError> {
226         Err(super::unimpl())
227     }
228     fn isatty(&self) -> bool { false }
229 }
230
231 impl Drop for Inner {
232     fn drop(&mut self) {
233         // closing stdio file handles makes no sense, so never do it. Also, note
234         // that errors are ignored when closing a file descriptor. The reason
235         // for this is that if an error occurs we don't actually know if the
236         // file descriptor was closed or not, and if we retried (for something
237         // like EINTR), we might close another valid file descriptor (opened
238         // after we closed ours.
239         if self.close_on_drop && self.fd > libc::STDERR_FILENO {
240             let n = unsafe { libc::close(self.fd) };
241             if n != 0 {
242                 println!("error {} when closing file descriptor {}", n, self.fd);
243             }
244         }
245     }
246 }
247
248 pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
249         -> IoResult<FileDesc> {
250     // Flags passed to open_osfhandle
251     let flags = match fm {
252         io::Open => 0,
253         io::Append => libc::O_APPEND,
254         io::Truncate => libc::O_TRUNC,
255     };
256     let flags = match fa {
257         io::Read => flags | libc::O_RDONLY,
258         io::Write => flags | libc::O_WRONLY | libc::O_CREAT,
259         io::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
260     };
261
262     let mut dwDesiredAccess = match fa {
263         io::Read => libc::FILE_GENERIC_READ,
264         io::Write => libc::FILE_GENERIC_WRITE,
265         io::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
266     };
267
268     // libuv has a good comment about this, but the basic idea is what we try to
269     // emulate unix semantics by enabling all sharing by allowing things such as
270     // deleting a file while it's still open.
271     let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
272                       libc::FILE_SHARE_DELETE;
273
274     let dwCreationDisposition = match (fm, fa) {
275         (io::Truncate, io::Read) => libc::TRUNCATE_EXISTING,
276         (io::Truncate, _) => libc::CREATE_ALWAYS,
277         (io::Open, io::Read) => libc::OPEN_EXISTING,
278         (io::Open, _) => libc::OPEN_ALWAYS,
279         (io::Append, io::Read) => {
280             dwDesiredAccess |= libc::FILE_APPEND_DATA;
281             libc::OPEN_EXISTING
282         }
283         (io::Append, _) => {
284             dwDesiredAccess &= !libc::FILE_WRITE_DATA;
285             dwDesiredAccess |= libc::FILE_APPEND_DATA;
286             libc::OPEN_ALWAYS
287         }
288     };
289
290     let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
291     // Compat with unix, this allows opening directories (see libuv)
292     dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
293
294     let handle = as_utf16_p(path.as_str().unwrap(), |buf| unsafe {
295         libc::CreateFileW(buf,
296                           dwDesiredAccess,
297                           dwShareMode,
298                           ptr::mut_null(),
299                           dwCreationDisposition,
300                           dwFlagsAndAttributes,
301                           ptr::mut_null())
302     });
303     if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
304         Err(super::last_error())
305     } else {
306         let fd = unsafe {
307             libc::open_osfhandle(handle as libc::intptr_t, flags)
308         };
309         if fd < 0 {
310             let _ = unsafe { libc::CloseHandle(handle) };
311             Err(super::last_error())
312         } else {
313             Ok(FileDesc::new(fd, true))
314         }
315     }
316 }
317
318 pub fn mkdir(p: &CString, _mode: io::FilePermission) -> IoResult<()> {
319     super::mkerr_winbool(unsafe {
320         // FIXME: turn mode into something useful? #2623
321         as_utf16_p(p.as_str().unwrap(), |buf| {
322             libc::CreateDirectoryW(buf, ptr::mut_null())
323         })
324     })
325 }
326
327 pub fn readdir(p: &CString) -> IoResult<Vec<Path>> {
328     use std::rt::global_heap::malloc_raw;
329
330     fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
331         let root = unsafe { CString::new(root.with_ref(|p| p), false) };
332         let root = Path::new(root);
333
334         dirs.move_iter().filter(|path| {
335             path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
336         }).map(|path| root.join(path)).collect()
337     }
338
339     extern {
340         fn rust_list_dir_wfd_size() -> libc::size_t;
341         fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
342     }
343     let star = Path::new(unsafe {
344         CString::new(p.with_ref(|p| p), false)
345     }).join("*");
346     as_utf16_p(star.as_str().unwrap(), |path_ptr| unsafe {
347         let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
348         let find_handle = libc::FindFirstFileW(path_ptr, wfd_ptr as libc::HANDLE);
349         if find_handle as libc::c_int != libc::INVALID_HANDLE_VALUE {
350             let mut paths = vec!();
351             let mut more_files = 1 as libc::c_int;
352             while more_files != 0 {
353                 let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr as *c_void);
354                 if fp_buf as uint == 0 {
355                     fail!("os::list_dir() failure: got null ptr from wfd");
356                 } else {
357                     let fp_vec = slice::from_buf(fp_buf, libc::wcslen(fp_buf) as uint);
358                     let fp_trimmed = str::truncate_utf16_at_nul(fp_vec);
359                     let fp_str = str::from_utf16(fp_trimmed)
360                             .expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16");
361                     paths.push(Path::new(fp_str));
362                 }
363                 more_files = libc::FindNextFileW(find_handle,
364                                                  wfd_ptr as libc::HANDLE);
365             }
366             assert!(libc::FindClose(find_handle) != 0);
367             libc::free(wfd_ptr as *mut c_void);
368             Ok(prune(p, paths))
369         } else {
370             Err(super::last_error())
371         }
372     })
373 }
374
375 pub fn unlink(p: &CString) -> IoResult<()> {
376     super::mkerr_winbool(unsafe {
377         as_utf16_p(p.as_str().unwrap(), |buf| {
378             libc::DeleteFileW(buf)
379         })
380     })
381 }
382
383 pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
384     super::mkerr_winbool(unsafe {
385         as_utf16_p(old.as_str().unwrap(), |old| {
386             as_utf16_p(new.as_str().unwrap(), |new| {
387                 libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING)
388             })
389         })
390     })
391 }
392
393 pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> {
394     super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
395         libc::wchmod(p, mode.bits() as libc::c_int)
396     }))
397 }
398
399 pub fn rmdir(p: &CString) -> IoResult<()> {
400     super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
401         libc::wrmdir(p)
402     }))
403 }
404
405 pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
406     // libuv has this as a no-op, so seems like this should as well?
407     Ok(())
408 }
409
410 pub fn readlink(p: &CString) -> IoResult<Path> {
411     // FIXME: I have a feeling that this reads intermediate symlinks as well.
412     let handle = unsafe {
413         as_utf16_p(p.as_str().unwrap(), |p| {
414             libc::CreateFileW(p,
415                               libc::GENERIC_READ,
416                               libc::FILE_SHARE_READ,
417                               ptr::mut_null(),
418                               libc::OPEN_EXISTING,
419                               libc::FILE_ATTRIBUTE_NORMAL,
420                               ptr::mut_null())
421         })
422     };
423     if handle as int == libc::INVALID_HANDLE_VALUE as int {
424         return Err(super::last_error())
425     }
426     // Specify (sz - 1) because the documentation states that it's the size
427     // without the null pointer
428     let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
429         libc::GetFinalPathNameByHandleW(handle,
430                                         buf as *u16,
431                                         sz - 1,
432                                         libc::VOLUME_NAME_DOS)
433     });
434     let ret = match ret {
435         Some(ref s) if s.starts_with(r"\\?\") => Ok(Path::new(s.slice_from(4))),
436         Some(s) => Ok(Path::new(s)),
437         None => Err(super::last_error()),
438     };
439     assert!(unsafe { libc::CloseHandle(handle) } != 0);
440     return ret;
441 }
442
443 pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
444     super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
445         as_utf16_p(dst.as_str().unwrap(), |dst| {
446             unsafe { libc::CreateSymbolicLinkW(dst, src, 0) }
447         }) as libc::BOOL
448     }))
449 }
450
451 pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
452     super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
453         as_utf16_p(dst.as_str().unwrap(), |dst| {
454             unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) }
455         })
456     }))
457 }
458
459 fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
460     let path = unsafe { CString::new(path.with_ref(|p| p), false) };
461     let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
462         libc::S_IFREG => io::TypeFile,
463         libc::S_IFDIR => io::TypeDirectory,
464         libc::S_IFIFO => io::TypeNamedPipe,
465         libc::S_IFBLK => io::TypeBlockSpecial,
466         libc::S_IFLNK => io::TypeSymlink,
467         _ => io::TypeUnknown,
468     };
469
470     io::FileStat {
471         path: Path::new(path),
472         size: stat.st_size as u64,
473         kind: kind,
474         perm: unsafe {
475           io::FilePermission::from_bits(stat.st_mode as u32)  & io::AllPermissions
476         },
477         created: stat.st_ctime as u64,
478         modified: stat.st_mtime as u64,
479         accessed: stat.st_atime as u64,
480         unstable: io::UnstableFileStat {
481             device: stat.st_dev as u64,
482             inode: stat.st_ino as u64,
483             rdev: stat.st_rdev as u64,
484             nlink: stat.st_nlink as u64,
485             uid: stat.st_uid as u64,
486             gid: stat.st_gid as u64,
487             blksize: 0,
488             blocks: 0,
489             flags: 0,
490             gen: 0,
491         }
492     }
493 }
494
495 pub fn stat(p: &CString) -> IoResult<io::FileStat> {
496     let mut stat: libc::stat = unsafe { mem::uninit() };
497     as_utf16_p(p.as_str().unwrap(), |up| {
498         match unsafe { libc::wstat(up, &mut stat) } {
499             0 => Ok(mkstat(&stat, p)),
500             _ => Err(super::last_error()),
501         }
502     })
503 }
504
505 pub fn lstat(_p: &CString) -> IoResult<io::FileStat> {
506     // FIXME: implementation is missing
507     Err(super::unimpl())
508 }
509
510 pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
511     let buf = libc::utimbuf {
512         actime: (atime / 1000) as libc::time64_t,
513         modtime: (mtime / 1000) as libc::time64_t,
514     };
515     super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
516         libc::wutime(p, &buf)
517     }))
518 }