(index<- )        ./libnative/io/file_win32.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   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  }