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;
20 use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode};
28 pub type fd_t = libc::c_int;
40 /// Create a `FileDesc` from an open C file descriptor.
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.
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 {
51 close_on_drop: close_on_drop
55 pub fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
58 libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
59 buf.len() as libc::DWORD, &mut read,
65 Err(super::last_error())
68 pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
69 let mut cur = buf.as_ptr();
70 let mut remaining = buf.len();
74 libc::WriteFile(self.handle(), cur as libc::LPVOID,
75 remaining as libc::DWORD, &mut amt,
79 remaining -= amt as uint;
80 cur = unsafe { cur.offset(amt as int) };
82 return Err(super::last_error())
88 pub fn fd(&self) -> fd_t { self.inner.fd }
90 pub fn handle(&self) -> libc::HANDLE {
91 unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
95 impl io::Reader for FileDesc {
96 fn read(&mut self, buf: &mut [u8]) -> io::IoResult<uint> {
101 impl io::Writer for FileDesc {
102 fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
103 self.inner_write(buf)
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)
111 fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
112 self.inner_write(buf)
115 fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> {
117 let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
118 overlap.Offset = offset as libc::DWORD;
119 overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
121 libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
122 buf.len() as libc::DWORD, &mut read,
128 Err(super::last_error())
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;
140 libc::WriteFile(self.handle(), cur as libc::LPVOID,
141 remaining as libc::DWORD, &mut amt,
145 remaining -= amt as uint;
146 cur = unsafe { cur.offset(amt as int) };
147 offset += amt as u64;
149 return Err(super::last_error())
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,
162 match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
164 0 => Err(super::last_error()),
165 _ => Ok(newpos as u64),
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) }
176 fn fsync(&mut self) -> Result<(), IoError> {
177 super::mkerr_winbool(unsafe {
178 libc::FlushFileBuffers(self.handle())
182 fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); }
184 fn truncate(&mut self, offset: i64) -> Result<(), IoError> {
185 let orig_pos = try!(self.tell());
186 let _ = try!(self.seek(offset, io::SeekSet));
188 match libc::SetEndOfFile(self.handle()) {
189 0 => Err(super::last_error()),
193 let _ = self.seek(orig_pos as i64, io::SeekSet);
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()),
206 impl rtio::RtioPipe for FileDesc {
207 fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
210 fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
211 self.inner_write(buf)
213 fn clone(&self) -> Box<rtio::RtioPipe:Send> {
214 box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe:Send>
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))
224 fn close_write(&mut self) -> IoResult<()> {
225 Err(io::standard_error(io::InvalidInput))
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>) {}
232 impl rtio::RtioTTY for FileDesc {
233 fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
236 fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
237 self.inner_write(buf)
239 fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> {
242 fn get_winsize(&mut self) -> Result<(int, int), IoError> {
245 fn isatty(&self) -> bool { false }
248 impl Drop for Inner {
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) };
259 println!("error {} when closing file descriptor {}", n, self.fd);
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 {
270 io::Append => libc::O_APPEND,
271 io::Truncate => libc::O_TRUNC,
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,
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
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;
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;
301 dwDesiredAccess &= !libc::FILE_WRITE_DATA;
302 dwDesiredAccess |= libc::FILE_APPEND_DATA;
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;
311 let handle = as_utf16_p(path.as_str().unwrap(), |buf| unsafe {
312 libc::CreateFileW(buf,
316 dwCreationDisposition,
317 dwFlagsAndAttributes,
320 if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
321 Err(super::last_error())
324 libc::open_osfhandle(handle as libc::intptr_t, flags)
327 let _ = unsafe { libc::CloseHandle(handle) };
328 Err(super::last_error())
330 Ok(FileDesc::new(fd, true))
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())
344 pub fn readdir(p: &CString) -> IoResult<Vec<Path>> {
345 use std::rt::libc_heap::malloc_raw;
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);
351 dirs.move_iter().filter(|path| {
352 path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
353 }).map(|path| root.join(path)).collect()
357 fn rust_list_dir_wfd_size() -> libc::size_t;
358 fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
360 let star = Path::new(unsafe {
361 CString::new(p.with_ref(|p| p), false)
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");
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));
380 more_files = libc::FindNextFileW(find_handle,
381 wfd_ptr as libc::HANDLE);
383 assert!(libc::FindClose(find_handle) != 0);
384 libc::free(wfd_ptr as *mut c_void);
387 Err(super::last_error())
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)
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)
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)
416 pub fn rmdir(p: &CString) -> IoResult<()> {
417 super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
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?
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| {
434 libc::FILE_SHARE_READ,
437 libc::FILE_ATTRIBUTE_NORMAL,
441 if handle as int == libc::INVALID_HANDLE_VALUE as int {
442 return Err(super::last_error())
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,
450 libc::VOLUME_NAME_DOS)
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)))
456 Some(s) => Ok(Path::new(s)),
457 None => Err(super::last_error()),
459 assert!(unsafe { libc::CloseHandle(handle) } != 0);
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) }
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()) }
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,
491 size: stat.st_size as u64,
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,
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()),
522 pub fn lstat(_p: &CString) -> IoResult<io::FileStat> {
523 // FIXME: implementation is missing
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,
532 super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
533 libc::wutime(p, &buf)