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