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.
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.
11 //! Blocking win32-based file I/O
14 use libc::{c_int, c_void};
16 use std::c_str::CString;
18 use std::os::win32::fill_utf16_buf_and_decode;
21 use std::rt::rtio::{IoResult, IoError};
25 pub type fd_t = libc::c_int;
37 /// Create a `FileDesc` from an open C file descriptor.
39 /// The `FileDesc` will take ownership of the specified file descriptor and
40 /// close it upon destruction if the `close_on_drop` flag is true, otherwise
41 /// it will not close the file descriptor when this `FileDesc` is dropped.
43 /// Note that all I/O operations done on this object will be *blocking*, but
44 /// they do not require the runtime to be active.
45 pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
46 FileDesc { inner: Arc::new(Inner {
48 close_on_drop: close_on_drop
52 pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
55 libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
56 buf.len() as libc::DWORD, &mut read,
62 Err(super::last_error())
65 pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
66 let mut cur = buf.as_ptr();
67 let mut remaining = buf.len();
71 libc::WriteFile(self.handle(), cur as libc::LPVOID,
72 remaining as libc::DWORD, &mut amt,
76 remaining -= amt as uint;
77 cur = unsafe { cur.offset(amt as int) };
79 return Err(super::last_error())
85 pub fn fd(&self) -> fd_t { self.inner.fd }
87 pub fn handle(&self) -> libc::HANDLE {
88 unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
91 // A version of seek that takes &self so that tell can call it
92 // - the private seek should of course take &mut self.
93 fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
94 let whence = match style {
95 rtio::SeekSet => libc::FILE_BEGIN,
96 rtio::SeekEnd => libc::FILE_END,
97 rtio::SeekCur => libc::FILE_CURRENT,
101 match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
103 0 => Err(super::last_error()),
104 _ => Ok(newpos as u64),
111 impl rtio::RtioFileStream for FileDesc {
112 fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
113 self.inner_read(buf).map(|i| i as int)
115 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
116 self.inner_write(buf)
119 fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
121 let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
122 overlap.Offset = offset as libc::DWORD;
123 overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
125 libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
126 buf.len() as libc::DWORD, &mut read,
132 Err(super::last_error())
135 fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> {
136 let mut cur = buf.as_ptr();
137 let mut remaining = buf.len();
138 let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
139 while remaining > 0 {
140 overlap.Offset = offset as libc::DWORD;
141 overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
144 libc::WriteFile(self.handle(), cur as libc::LPVOID,
145 remaining as libc::DWORD, &mut amt,
149 remaining -= amt as uint;
150 cur = unsafe { cur.offset(amt as int) };
151 offset += amt as u64;
153 return Err(super::last_error())
159 fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
160 self.seek_common(pos, style)
163 fn tell(&self) -> IoResult<u64> {
164 self.seek_common(0, rtio::SeekCur)
167 fn fsync(&mut self) -> IoResult<()> {
168 super::mkerr_winbool(unsafe {
169 libc::FlushFileBuffers(self.handle())
173 fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
175 fn truncate(&mut self, offset: i64) -> IoResult<()> {
176 let orig_pos = try!(self.tell());
177 let _ = try!(self.seek(offset, rtio::SeekSet));
179 match libc::SetEndOfFile(self.handle()) {
180 0 => Err(super::last_error()),
184 let _ = self.seek(orig_pos as i64, rtio::SeekSet);
188 fn fstat(&mut self) -> IoResult<rtio::FileStat> {
189 let mut stat: libc::stat = unsafe { mem::zeroed() };
190 match unsafe { libc::fstat(self.fd(), &mut stat) } {
191 0 => Ok(mkstat(&stat)),
192 _ => Err(super::last_error()),
197 impl rtio::RtioPipe for FileDesc {
198 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
201 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
202 self.inner_write(buf)
204 fn clone(&self) -> Box<rtio::RtioPipe + Send> {
205 box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
208 // Only supported on named pipes currently. Note that this doesn't have an
209 // impact on the std::io primitives, this is never called via
210 // std::io::PipeStream. If the functionality is exposed in the future, then
211 // these methods will need to be implemented.
212 fn close_read(&mut self) -> IoResult<()> {
215 fn close_write(&mut self) -> IoResult<()> {
218 fn set_timeout(&mut self, _t: Option<u64>) {}
219 fn set_read_timeout(&mut self, _t: Option<u64>) {}
220 fn set_write_timeout(&mut self, _t: Option<u64>) {}
223 impl rtio::RtioTTY for FileDesc {
224 fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
227 fn write(&mut self, buf: &[u8]) -> IoResult<()> {
228 self.inner_write(buf)
230 fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
233 fn get_winsize(&mut self) -> IoResult<(int, int)> {
236 fn isatty(&self) -> bool { false }
239 impl Drop for Inner {
241 // closing stdio file handles makes no sense, so never do it. Also, note
242 // that errors are ignored when closing a file descriptor. The reason
243 // for this is that if an error occurs we don't actually know if the
244 // file descriptor was closed or not, and if we retried (for something
245 // like EINTR), we might close another valid file descriptor (opened
246 // after we closed ours.
247 if self.close_on_drop && self.fd > libc::STDERR_FILENO {
248 let n = unsafe { libc::close(self.fd) };
250 println!("error {} when closing file descriptor {}", n, self.fd);
256 pub fn to_utf16(s: &CString) -> IoResult<Vec<u16>> {
258 Some(s) => Ok(s.to_utf16().append_one(0)),
259 None => Err(IoError {
260 code: libc::ERROR_INVALID_NAME as uint,
262 detail: Some("valid unicode input required".to_str()),
267 pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
268 -> IoResult<FileDesc> {
269 // Flags passed to open_osfhandle
270 let flags = match fm {
272 rtio::Append => libc::O_APPEND,
273 rtio::Truncate => libc::O_TRUNC,
275 let flags = match fa {
276 rtio::Read => flags | libc::O_RDONLY,
277 rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT,
278 rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
281 let mut dwDesiredAccess = match fa {
282 rtio::Read => libc::FILE_GENERIC_READ,
283 rtio::Write => libc::FILE_GENERIC_WRITE,
284 rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
287 // libuv has a good comment about this, but the basic idea is what we try to
288 // emulate unix semantics by enabling all sharing by allowing things such as
289 // deleting a file while it's still open.
290 let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
291 libc::FILE_SHARE_DELETE;
293 let dwCreationDisposition = match (fm, fa) {
294 (rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING,
295 (rtio::Truncate, _) => libc::CREATE_ALWAYS,
296 (rtio::Open, rtio::Read) => libc::OPEN_EXISTING,
297 (rtio::Open, _) => libc::OPEN_ALWAYS,
298 (rtio::Append, rtio::Read) => {
299 dwDesiredAccess |= libc::FILE_APPEND_DATA;
302 (rtio::Append, _) => {
303 dwDesiredAccess &= !libc::FILE_WRITE_DATA;
304 dwDesiredAccess |= libc::FILE_APPEND_DATA;
309 let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
310 // Compat with unix, this allows opening directories (see libuv)
311 dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
313 let path = try!(to_utf16(path));
314 let handle = unsafe {
315 libc::CreateFileW(path.as_ptr(),
319 dwCreationDisposition,
320 dwFlagsAndAttributes,
323 if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
324 Err(super::last_error())
327 libc::open_osfhandle(handle as libc::intptr_t, flags)
330 let _ = unsafe { libc::CloseHandle(handle) };
331 Err(super::last_error())
333 Ok(FileDesc::new(fd, true))
338 pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
339 let p = try!(to_utf16(p));
340 super::mkerr_winbool(unsafe {
341 // FIXME: turn mode into something useful? #2623
342 libc::CreateDirectoryW(p.as_ptr(), ptr::mut_null())
346 pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
347 use std::rt::libc_heap::malloc_raw;
349 fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
350 let root = unsafe { CString::new(root.with_ref(|p| p), false) };
351 let root = Path::new(root);
353 dirs.move_iter().filter(|path| {
354 path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
355 }).map(|path| root.join(path).to_c_str()).collect()
359 fn rust_list_dir_wfd_size() -> libc::size_t;
360 fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
362 let star = Path::new(unsafe {
363 CString::new(p.with_ref(|p| p), false)
365 let path = try!(to_utf16(&star.to_c_str()));
368 let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
369 let find_handle = libc::FindFirstFileW(path.as_ptr(), wfd_ptr as libc::HANDLE);
370 if find_handle as libc::c_int != libc::INVALID_HANDLE_VALUE {
371 let mut paths = vec!();
372 let mut more_files = 1 as libc::c_int;
373 while more_files != 0 {
374 let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr as *c_void);
375 if fp_buf as uint == 0 {
376 fail!("os::list_dir() failure: got null ptr from wfd");
378 let fp_vec = vec::raw::from_buf(fp_buf, libc::wcslen(fp_buf) as uint);
379 let fp_trimmed = str::truncate_utf16_at_nul(fp_vec.as_slice());
380 let fp_str = str::from_utf16(fp_trimmed)
381 .expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16");
382 paths.push(Path::new(fp_str));
384 more_files = libc::FindNextFileW(find_handle,
385 wfd_ptr as libc::HANDLE);
387 assert!(libc::FindClose(find_handle) != 0);
388 libc::free(wfd_ptr as *mut c_void);
391 Err(super::last_error())
396 pub fn unlink(p: &CString) -> IoResult<()> {
397 let p = try!(to_utf16(p));
398 super::mkerr_winbool(unsafe {
399 libc::DeleteFileW(p.as_ptr())
403 pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
404 let old = try!(to_utf16(old));
405 let new = try!(to_utf16(new));
406 super::mkerr_winbool(unsafe {
407 libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
408 libc::MOVEFILE_REPLACE_EXISTING)
412 pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
413 let p = try!(to_utf16(p));
414 super::mkerr_libc(unsafe {
415 libc::wchmod(p.as_ptr(), mode as libc::c_int)
419 pub fn rmdir(p: &CString) -> IoResult<()> {
420 let p = try!(to_utf16(p));
421 super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
424 pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
425 // libuv has this as a no-op, so seems like this should as well?
429 pub fn readlink(p: &CString) -> IoResult<CString> {
430 // FIXME: I have a feeling that this reads intermediate symlinks as well.
431 use io::c::compat::kernel32::GetFinalPathNameByHandleW;
432 let p = try!(to_utf16(p));
433 let handle = unsafe {
434 libc::CreateFileW(p.as_ptr(),
436 libc::FILE_SHARE_READ,
439 libc::FILE_ATTRIBUTE_NORMAL,
442 if handle as int == libc::INVALID_HANDLE_VALUE as int {
443 return Err(super::last_error())
445 // Specify (sz - 1) because the documentation states that it's the size
446 // without the null pointer
447 let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
448 GetFinalPathNameByHandleW(handle,
451 libc::VOLUME_NAME_DOS)
453 let ret = match ret {
454 Some(ref s) if s.as_slice().starts_with(r"\\?\") => {
455 Ok(Path::new(s.as_slice().slice_from(4)).to_c_str())
457 Some(s) => Ok(Path::new(s).to_c_str()),
458 None => Err(super::last_error()),
460 assert!(unsafe { libc::CloseHandle(handle) } != 0);
464 pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
465 use io::c::compat::kernel32::CreateSymbolicLinkW;
466 let src = try!(to_utf16(src));
467 let dst = try!(to_utf16(dst));
468 super::mkerr_winbool(unsafe {
469 CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
473 pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
474 let src = try!(to_utf16(src));
475 let dst = try!(to_utf16(dst));
476 super::mkerr_winbool(unsafe {
477 libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::mut_null())
481 fn mkstat(stat: &libc::stat) -> rtio::FileStat {
483 size: stat.st_size as u64,
484 kind: stat.st_mode as u64,
485 perm: stat.st_mode as u64,
486 created: stat.st_ctime as u64,
487 modified: stat.st_mtime as u64,
488 accessed: stat.st_atime as u64,
489 device: stat.st_dev as u64,
490 inode: stat.st_ino as u64,
491 rdev: stat.st_rdev as u64,
492 nlink: stat.st_nlink as u64,
493 uid: stat.st_uid as u64,
494 gid: stat.st_gid as u64,
502 pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
503 let mut stat: libc::stat = unsafe { mem::zeroed() };
504 let p = try!(to_utf16(p));
505 match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
506 0 => Ok(mkstat(&stat)),
507 _ => Err(super::last_error()),
511 pub fn lstat(_p: &CString) -> IoResult<rtio::FileStat> {
512 // FIXME: implementation is missing
516 pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
517 let buf = libc::utimbuf {
518 actime: (atime / 1000) as libc::time64_t,
519 modtime: (mtime / 1000) as libc::time64_t,
521 let p = try!(to_utf16(p));
522 super::mkerr_libc(unsafe {
523 libc::wutime(p.as_ptr(), &buf)