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::vec;
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 // Only supported on named pipes currently. Note that this doesn't have an
215 // impact on the std::io primitives, this is never called via
216 // std::io::PipeStream. If the functionality is exposed in the future, then
217 // these methods will need to be implemented.
218 fn close_read(&mut self) -> IoResult<()> {
219 Err(io::standard_error(io::InvalidInput))
220 }
221 fn close_write(&mut self) -> IoResult<()> {
222 Err(io::standard_error(io::InvalidInput))
223 }
224 fn set_timeout(&mut self, _t: Option<u64>) {}
225 fn set_read_timeout(&mut self, _t: Option<u64>) {}
226 fn set_write_timeout(&mut self, _t: Option<u64>) {}
227 }
228
229 impl rtio::RtioTTY for FileDesc {
230 fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
231 self.inner_read(buf)
232 }
233 fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
234 self.inner_write(buf)
235 }
236 fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> {
237 Err(super::unimpl())
238 }
239 fn get_winsize(&mut self) -> Result<(int, int), IoError> {
240 Err(super::unimpl())
241 }
242 fn isatty(&self) -> bool { false }
243 }
244
245 impl Drop for Inner {
246 fn drop(&mut self) {
247 // closing stdio file handles makes no sense, so never do it. Also, note
248 // that errors are ignored when closing a file descriptor. The reason
249 // for this is that if an error occurs we don't actually know if the
250 // file descriptor was closed or not, and if we retried (for something
251 // like EINTR), we might close another valid file descriptor (opened
252 // after we closed ours.
253 if self.close_on_drop && self.fd > libc::STDERR_FILENO {
254 let n = unsafe { libc::close(self.fd) };
255 if n != 0 {
256 println!("error {} when closing file descriptor {}", n, self.fd);
257 }
258 }
259 }
260 }
261
262 pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess)
263 -> IoResult<FileDesc> {
264 // Flags passed to open_osfhandle
265 let flags = match fm {
266 io::Open => 0,
267 io::Append => libc::O_APPEND,
268 io::Truncate => libc::O_TRUNC,
269 };
270 let flags = match fa {
271 io::Read => flags | libc::O_RDONLY,
272 io::Write => flags | libc::O_WRONLY | libc::O_CREAT,
273 io::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
274 };
275
276 let mut dwDesiredAccess = match fa {
277 io::Read => libc::FILE_GENERIC_READ,
278 io::Write => libc::FILE_GENERIC_WRITE,
279 io::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
280 };
281
282 // libuv has a good comment about this, but the basic idea is what we try to
283 // emulate unix semantics by enabling all sharing by allowing things such as
284 // deleting a file while it's still open.
285 let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
286 libc::FILE_SHARE_DELETE;
287
288 let dwCreationDisposition = match (fm, fa) {
289 (io::Truncate, io::Read) => libc::TRUNCATE_EXISTING,
290 (io::Truncate, _) => libc::CREATE_ALWAYS,
291 (io::Open, io::Read) => libc::OPEN_EXISTING,
292 (io::Open, _) => libc::OPEN_ALWAYS,
293 (io::Append, io::Read) => {
294 dwDesiredAccess |= libc::FILE_APPEND_DATA;
295 libc::OPEN_EXISTING
296 }
297 (io::Append, _) => {
298 dwDesiredAccess &= !libc::FILE_WRITE_DATA;
299 dwDesiredAccess |= libc::FILE_APPEND_DATA;
300 libc::OPEN_ALWAYS
301 }
302 };
303
304 let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
305 // Compat with unix, this allows opening directories (see libuv)
306 dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
307
308 let handle = as_utf16_p(path.as_str().unwrap(), |buf| unsafe {
309 libc::CreateFileW(buf,
310 dwDesiredAccess,
311 dwShareMode,
312 ptr::mut_null(),
313 dwCreationDisposition,
314 dwFlagsAndAttributes,
315 ptr::mut_null())
316 });
317 if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
318 Err(super::last_error())
319 } else {
320 let fd = unsafe {
321 libc::open_osfhandle(handle as libc::intptr_t, flags)
322 };
323 if fd < 0 {
324 let _ = unsafe { libc::CloseHandle(handle) };
325 Err(super::last_error())
326 } else {
327 Ok(FileDesc::new(fd, true))
328 }
329 }
330 }
331
332 pub fn mkdir(p: &CString, _mode: io::FilePermission) -> IoResult<()> {
333 super::mkerr_winbool(unsafe {
334 // FIXME: turn mode into something useful? #2623
335 as_utf16_p(p.as_str().unwrap(), |buf| {
336 libc::CreateDirectoryW(buf, ptr::mut_null())
337 })
338 })
339 }
340
341 pub fn readdir(p: &CString) -> IoResult<Vec<Path>> {
342 use std::rt::global_heap::malloc_raw;
343
344 fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
345 let root = unsafe { CString::new(root.with_ref(|p| p), false) };
346 let root = Path::new(root);
347
348 dirs.move_iter().filter(|path| {
349 path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..")
350 }).map(|path| root.join(path)).collect()
351 }
352
353 extern {
354 fn rust_list_dir_wfd_size() -> libc::size_t;
355 fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16;
356 }
357 let star = Path::new(unsafe {
358 CString::new(p.with_ref(|p| p), false)
359 }).join("*");
360 as_utf16_p(star.as_str().unwrap(), |path_ptr| unsafe {
361 let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
362 let find_handle = libc::FindFirstFileW(path_ptr, wfd_ptr as libc::HANDLE);
363 if find_handle as libc::c_int != libc::INVALID_HANDLE_VALUE {
364 let mut paths = vec!();
365 let mut more_files = 1 as libc::c_int;
366 while more_files != 0 {
367 let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr as *c_void);
368 if fp_buf as uint == 0 {
369 fail!("os::list_dir() failure: got null ptr from wfd");
370 } else {
371 let fp_vec = vec::raw::from_buf(fp_buf, libc::wcslen(fp_buf) as uint);
372 let fp_trimmed = str::truncate_utf16_at_nul(fp_vec.as_slice());
373 let fp_str = str::from_utf16(fp_trimmed)
374 .expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16");
375 paths.push(Path::new(fp_str));
376 }
377 more_files = libc::FindNextFileW(find_handle,
378 wfd_ptr as libc::HANDLE);
379 }
380 assert!(libc::FindClose(find_handle) != 0);
381 libc::free(wfd_ptr as *mut c_void);
382 Ok(prune(p, paths))
383 } else {
384 Err(super::last_error())
385 }
386 })
387 }
388
389 pub fn unlink(p: &CString) -> IoResult<()> {
390 super::mkerr_winbool(unsafe {
391 as_utf16_p(p.as_str().unwrap(), |buf| {
392 libc::DeleteFileW(buf)
393 })
394 })
395 }
396
397 pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
398 super::mkerr_winbool(unsafe {
399 as_utf16_p(old.as_str().unwrap(), |old| {
400 as_utf16_p(new.as_str().unwrap(), |new| {
401 libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING)
402 })
403 })
404 })
405 }
406
407 pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> {
408 super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
409 libc::wchmod(p, mode.bits() as libc::c_int)
410 }))
411 }
412
413 pub fn rmdir(p: &CString) -> IoResult<()> {
414 super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
415 libc::wrmdir(p)
416 }))
417 }
418
419 pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
420 // libuv has this as a no-op, so seems like this should as well?
421 Ok(())
422 }
423
424 pub fn readlink(p: &CString) -> IoResult<Path> {
425 // FIXME: I have a feeling that this reads intermediate symlinks as well.
426 let handle = unsafe {
427 as_utf16_p(p.as_str().unwrap(), |p| {
428 libc::CreateFileW(p,
429 libc::GENERIC_READ,
430 libc::FILE_SHARE_READ,
431 ptr::mut_null(),
432 libc::OPEN_EXISTING,
433 libc::FILE_ATTRIBUTE_NORMAL,
434 ptr::mut_null())
435 })
436 };
437 if handle as int == libc::INVALID_HANDLE_VALUE as int {
438 return Err(super::last_error())
439 }
440 // Specify (sz - 1) because the documentation states that it's the size
441 // without the null pointer
442 let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
443 libc::GetFinalPathNameByHandleW(handle,
444 buf as *u16,
445 sz - 1,
446 libc::VOLUME_NAME_DOS)
447 });
448 let ret = match ret {
449 Some(ref s) if s.starts_with(r"\\?\") => Ok(Path::new(s.slice_from(4))),
450 Some(s) => Ok(Path::new(s)),
451 None => Err(super::last_error()),
452 };
453 assert!(unsafe { libc::CloseHandle(handle) } != 0);
454 return ret;
455 }
456
457 pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
458 super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
459 as_utf16_p(dst.as_str().unwrap(), |dst| {
460 unsafe { libc::CreateSymbolicLinkW(dst, src, 0) }
461 }) as libc::BOOL
462 }))
463 }
464
465 pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
466 super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| {
467 as_utf16_p(dst.as_str().unwrap(), |dst| {
468 unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) }
469 })
470 }))
471 }
472
473 fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat {
474 let path = unsafe { CString::new(path.with_ref(|p| p), false) };
475 let kind = match (stat.st_mode as c_int) & libc::S_IFMT {
476 libc::S_IFREG => io::TypeFile,
477 libc::S_IFDIR => io::TypeDirectory,
478 libc::S_IFIFO => io::TypeNamedPipe,
479 libc::S_IFBLK => io::TypeBlockSpecial,
480 libc::S_IFLNK => io::TypeSymlink,
481 _ => io::TypeUnknown,
482 };
483
484 io::FileStat {
485 path: Path::new(path),
486 size: stat.st_size as u64,
487 kind: kind,
488 perm: unsafe {
489 io::FilePermission::from_bits(stat.st_mode as u32) & io::AllPermissions
490 },
491 created: stat.st_ctime as u64,
492 modified: stat.st_mtime as u64,
493 accessed: stat.st_atime as u64,
494 unstable: io::UnstableFileStat {
495 device: stat.st_dev as u64,
496 inode: stat.st_ino as u64,
497 rdev: stat.st_rdev as u64,
498 nlink: stat.st_nlink as u64,
499 uid: stat.st_uid as u64,
500 gid: stat.st_gid as u64,
501 blksize: 0,
502 blocks: 0,
503 flags: 0,
504 gen: 0,
505 }
506 }
507 }
508
509 pub fn stat(p: &CString) -> IoResult<io::FileStat> {
510 let mut stat: libc::stat = unsafe { mem::uninit() };
511 as_utf16_p(p.as_str().unwrap(), |up| {
512 match unsafe { libc::wstat(up, &mut stat) } {
513 0 => Ok(mkstat(&stat, p)),
514 _ => Err(super::last_error()),
515 }
516 })
517 }
518
519 pub fn lstat(_p: &CString) -> IoResult<io::FileStat> {
520 // FIXME: implementation is missing
521 Err(super::unimpl())
522 }
523
524 pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
525 let buf = libc::utimbuf {
526 actime: (atime / 1000) as libc::time64_t,
527 modtime: (mtime / 1000) as libc::time64_t,
528 };
529 super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe {
530 libc::wutime(p, &buf)
531 }))
532 }