diff options
Diffstat (limited to 'src/libstd/sys')
| -rw-r--r-- | src/libstd/sys/unix/c.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext.rs | 337 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext/ffi.rs | 62 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext/fs.rs | 191 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext/io.rs | 108 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext/mod.rs | 53 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext/process.rs | 65 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext/raw.rs | 22 | ||||
| -rw-r--r-- | src/libstd/sys/unix/fs2.rs | 112 | ||||
| -rw-r--r-- | src/libstd/sys/unix/mod.rs | 10 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext.rs | 301 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext/ffi.rs | 58 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext/fs.rs | 150 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext/io.rs | 131 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext/mod.rs | 35 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext/raw.rs | 21 | ||||
| -rw-r--r-- | src/libstd/sys/windows/fs2.rs | 208 |
17 files changed, 1156 insertions, 710 deletions
diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index aa4bf821207..1e68eac5a67 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -161,6 +161,8 @@ extern { pub fn gai_strerror(errcode: libc::c_int) -> *const libc::c_char; pub fn setgroups(ngroups: libc::c_int, ptr: *const libc::c_void) -> libc::c_int; + pub fn realpath(pathname: *const libc::c_char, resolved: *mut libc::c_char) + -> *mut libc::c_char; } #[cfg(any(target_os = "macos", target_os = "ios"))] diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs deleted file mode 100644 index 66aaf26b09b..00000000000 --- a/src/libstd/sys/unix/ext.rs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Experimental extensions to `std` for Unix platforms. -//! -//! For now, this module is limited to extracting file descriptors, -//! but its functionality will grow over time. -//! -//! # Example -//! -//! ```no_run -//! use std::fs::File; -//! use std::os::unix::prelude::*; -//! -//! fn main() { -//! let f = File::create("foo.txt").unwrap(); -//! let fd = f.as_raw_fd(); -//! -//! // use fd with native unix bindings -//! } -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -/// Unix-specific extensions to general I/O primitives -#[stable(feature = "rust1", since = "1.0.0")] -pub mod io { - use fs; - use libc; - use net; - use sys_common::{net2, AsInner, FromInner}; - use sys; - - /// Raw file descriptors. - #[stable(feature = "rust1", since = "1.0.0")] - pub type RawFd = libc::c_int; - - /// A trait to extract the raw unix file descriptor from an underlying - /// object. - /// - /// This is only available on unix platforms and must be imported in order - /// to call the method. Windows platforms have a corresponding `AsRawHandle` - /// and `AsRawSocket` set of traits. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guarantee to be valid while - /// the original object has not yet been destroyed. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_fd(&self) -> RawFd; - } - - /// A trait to express the ability to construct an object from a raw file - /// descriptor. - #[unstable(feature = "from_raw_os", - reason = "recent addition to std::os::unix::io")] - pub trait FromRawFd { - /// Constructs a new instances of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_fd(fd: RawFd) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for fs::File { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for fs::File { - unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs2::File::from_inner(fd)) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for net::TcpStream { - fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for net::TcpListener { - fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for net::UdpSocket { - fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } - } - - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for net::TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - let socket = sys::net::Socket::from_inner(fd); - net::TcpStream::from_inner(net2::TcpStream::from_inner(socket)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for net::TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - let socket = sys::net::Socket::from_inner(fd); - net::TcpListener::from_inner(net2::TcpListener::from_inner(socket)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for net::UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - let socket = sys::net::Socket::from_inner(fd); - net::UdpSocket::from_inner(net2::UdpSocket::from_inner(socket)) - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// OsString and OsStr -//////////////////////////////////////////////////////////////////////////////// - -/// Unix-specific extension to the primitives in the `std::ffi` module -#[stable(feature = "rust1", since = "1.0.0")] -pub mod ffi { - use ffi::{OsStr, OsString}; - use mem; - use prelude::v1::*; - use sys::os_str::Buf; - use sys_common::{FromInner, IntoInner, AsInner}; - - /// Unix-specific extensions to `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStringExt { - /// Creates an `OsString` from a byte vector. - #[stable(feature = "rust1", since = "1.0.0")] - fn from_vec(vec: Vec<u8>) -> Self; - - /// Yields the underlying byte vector of this `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - fn into_vec(self) -> Vec<u8>; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStringExt for OsString { - fn from_vec(vec: Vec<u8>) -> OsString { - FromInner::from_inner(Buf { inner: vec }) - } - fn into_vec(self) -> Vec<u8> { - self.into_inner().inner - } - } - - /// Unix-specific extensions to `OsStr`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStrExt { - #[stable(feature = "rust1", since = "1.0.0")] - fn from_bytes(slice: &[u8]) -> &Self; - - /// Gets the underlying byte view of the `OsStr` slice. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_bytes(&self) -> &[u8]; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStrExt for OsStr { - fn from_bytes(slice: &[u8]) -> &OsStr { - unsafe { mem::transmute(slice) } - } - fn as_bytes(&self) -> &[u8] { - &self.as_inner().inner - } - } -} - -/// Unix-specific extensions to primitives in the `std::fs` module. -#[unstable(feature = "fs_ext", - reason = "may want a more useful mode abstraction")] -pub mod fs { - use sys; - use sys_common::{FromInner, AsInner, AsInnerMut}; - use fs::{Permissions, OpenOptions}; - use path::Path; - use convert::AsRef; - use io; - - /// Unix-specific extensions to `Permissions` - pub trait PermissionsExt { - fn mode(&self) -> i32; - fn set_mode(&mut self, mode: i32); - } - - impl PermissionsExt for Permissions { - fn mode(&self) -> i32 { self.as_inner().mode() } - - fn set_mode(&mut self, mode: i32) { - *self = FromInner::from_inner(FromInner::from_inner(mode)); - } - } - - /// Unix-specific extensions to `OpenOptions` - pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of a `File::open_opts` call then this - /// specified `mode` will be used as the permission bits for the new file. - fn mode(&mut self, mode: i32) -> &mut Self; - } - - impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: i32) -> &mut OpenOptions { - self.as_inner_mut().mode(mode); self - } - } - - /// Creates a new symbolic link on the filesystem. - /// - /// The `dst` path will be a symbolic link pointing to the `src` path. - /// - /// # Note - /// - /// On Windows, you must specify whether a symbolic link points to a file - /// or directory. Use `os::windows::fs::symlink_file` to create a - /// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a - /// symbolic link to a directory. Additionally, the process must have - /// `SeCreateSymbolicLinkPrivilege` in order to be able to create a - /// symbolic link. - /// - /// # Examples - /// - /// ``` - /// #![feature(fs_ext)] - /// use std::os::unix::fs; - /// - /// # fn foo() -> std::io::Result<()> { - /// try!(fs::symlink("a.txt", "b.txt")); - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> - { - sys::fs2::symlink(src.as_ref(), dst.as_ref()) - } - -} - -//////////////////////////////////////////////////////////////////////////////// -// Process and Command -//////////////////////////////////////////////////////////////////////////////// - -/// Unix-specific extensions to primitives in the `std::process` module. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod process { - use prelude::v1::*; - use libc::{uid_t, gid_t}; - use process; - use sys; - use sys_common::{AsInnerMut, AsInner}; - - /// Unix-specific extensions to the `std::process::Command` builder - #[stable(feature = "rust1", since = "1.0.0")] - pub trait CommandExt { - /// Sets the child process's user id. This translates to a - /// `setuid` call in the child process. Failure in the `setuid` - /// call will cause the spawn to fail. - #[stable(feature = "rust1", since = "1.0.0")] - fn uid(&mut self, id: uid_t) -> &mut process::Command; - - /// Similar to `uid`, but sets the group id of the child process. This has - /// the same semantics as the `uid` field. - #[stable(feature = "rust1", since = "1.0.0")] - fn gid(&mut self, id: gid_t) -> &mut process::Command; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl CommandExt for process::Command { - fn uid(&mut self, id: uid_t) -> &mut process::Command { - self.as_inner_mut().uid = Some(id); - self - } - - fn gid(&mut self, id: gid_t) -> &mut process::Command { - self.as_inner_mut().gid = Some(id); - self - } - } - - /// Unix-specific extensions to `std::process::ExitStatus` - #[stable(feature = "rust1", since = "1.0.0")] - pub trait ExitStatusExt { - /// If the process was terminated by a signal, returns that signal. - #[stable(feature = "rust1", since = "1.0.0")] - fn signal(&self) -> Option<i32>; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl ExitStatusExt for process::ExitStatus { - fn signal(&self) -> Option<i32> { - match *self.as_inner() { - sys::process2::ExitStatus::Signal(s) => Some(s), - _ => None - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Prelude -//////////////////////////////////////////////////////////////////////////////// - -/// A prelude for conveniently writing platform-specific code. -/// -/// Includes all extension traits, and some important type definitions. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod prelude { - #[doc(no_inline)] - pub use super::io::{RawFd, AsRawFd}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::ffi::{OsStrExt, OsStringExt}; - #[doc(no_inline)] - pub use super::fs::{PermissionsExt, OpenOptionsExt}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::process::{CommandExt, ExitStatusExt}; -} diff --git a/src/libstd/sys/unix/ext/ffi.rs b/src/libstd/sys/unix/ext/ffi.rs new file mode 100644 index 00000000000..825e74cabde --- /dev/null +++ b/src/libstd/sys/unix/ext/ffi.rs @@ -0,0 +1,62 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extension to the primitives in the `std::ffi` module + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsStr, OsString}; +use mem; +use prelude::v1::*; +use sys::os_str::Buf; +use sys_common::{FromInner, IntoInner, AsInner}; + +/// Unix-specific extensions to `OsString`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a byte vector. + #[stable(feature = "rust1", since = "1.0.0")] + fn from_vec(vec: Vec<u8>) -> Self; + + /// Yields the underlying byte vector of this `OsString`. + #[stable(feature = "rust1", since = "1.0.0")] + fn into_vec(self) -> Vec<u8>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_vec(vec: Vec<u8>) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + fn into_vec(self) -> Vec<u8> { + self.into_inner().inner + } +} + +/// Unix-specific extensions to `OsStr`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + #[stable(feature = "rust1", since = "1.0.0")] + fn from_bytes(slice: &[u8]) -> &Self; + + /// Gets the underlying byte view of the `OsStr` slice. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_bytes(&self) -> &[u8]; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs new file mode 100644 index 00000000000..ad6a856d5bc --- /dev/null +++ b/src/libstd/sys/unix/ext/fs.rs @@ -0,0 +1,191 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use prelude::v1::*; + +use fs::{self, Permissions, OpenOptions}; +use io; +use mem; +use os::raw::c_long; +use os::unix::raw; +use path::Path; +use sys::platform; +use sys; +use sys_common::{FromInner, AsInner, AsInnerMut}; + +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_READ: raw::mode_t = 0o400; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_WRITE: raw::mode_t = 0o200; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_EXECUTE: raw::mode_t = 0o100; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_RWX: raw::mode_t = 0o700; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_READ: raw::mode_t = 0o040; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_WRITE: raw::mode_t = 0o020; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_EXECUTE: raw::mode_t = 0o010; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_RWX: raw::mode_t = 0o070; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_READ: raw::mode_t = 0o004; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_WRITE: raw::mode_t = 0o002; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_EXECUTE: raw::mode_t = 0o001; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_RWX: raw::mode_t = 0o007; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_READ: raw::mode_t = 0o444; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_WRITE: raw::mode_t = 0o222; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_EXECUTE: raw::mode_t = 0o111; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_RWX: raw::mode_t = 0o777; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const SETUID: raw::mode_t = 0o4000; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const SETGID: raw::mode_t = 0o2000; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const STICKY_BIT: raw::mode_t = 0o1000; + +/// Unix-specific extensions to `Permissions` +#[unstable(feature = "fs_ext", + reason = "may want a more useful mode abstraction")] +pub trait PermissionsExt { + fn mode(&self) -> raw::mode_t; + fn set_mode(&mut self, mode: raw::mode_t); + fn from_mode(mode: raw::mode_t) -> Self; +} + +impl PermissionsExt for Permissions { + fn mode(&self) -> raw::mode_t { self.as_inner().mode() } + + fn set_mode(&mut self, mode: raw::mode_t) { + *self = FromInner::from_inner(FromInner::from_inner(mode)); + } + + fn from_mode(mode: raw::mode_t) -> Permissions { + FromInner::from_inner(FromInner::from_inner(mode)) + } +} + +/// Unix-specific extensions to `OpenOptions` +#[unstable(feature = "fs_ext", + reason = "may want a more useful mode abstraction")] +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of a `File::open_opts` call then this + /// specified `mode` will be used as the permission bits for the new file. + fn mode(&mut self, mode: raw::mode_t) -> &mut Self; +} + +impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: raw::mode_t) -> &mut OpenOptions { + self.as_inner_mut().mode(mode); self + } +} + +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub struct Metadata(sys::fs2::FileAttr); + +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub trait MetadataExt { + fn as_raw(&self) -> &Metadata; +} + +impl MetadataExt for fs::Metadata { + fn as_raw(&self) -> &Metadata { + let inner: &sys::fs2::FileAttr = self.as_inner(); + unsafe { mem::transmute(inner) } + } +} + +impl AsInner<platform::raw::stat> for Metadata { + fn as_inner(&self) -> &platform::raw::stat { self.0.as_inner() } +} + +// Hm, why are there casts here to the returned type, shouldn't the types always +// be the same? Right you are! Turns out, however, on android at least the types +// in the raw `stat` structure are not the same as the types being returned. Who +// knew! +// +// As a result to make sure this compiles for all platforms we do the manual +// casts and rely on manual lowering to `stat` if the raw type is desired. +#[unstable(feature = "metadata_ext", reason = "recently added API")] +impl Metadata { + pub fn dev(&self) -> raw::dev_t { self.0.raw().st_dev as raw::dev_t } + pub fn ino(&self) -> raw::ino_t { self.0.raw().st_ino as raw::ino_t } + pub fn mode(&self) -> raw::mode_t { self.0.raw().st_mode as raw::mode_t } + pub fn nlink(&self) -> raw::nlink_t { self.0.raw().st_nlink as raw::nlink_t } + pub fn uid(&self) -> raw::uid_t { self.0.raw().st_uid as raw::uid_t } + pub fn gid(&self) -> raw::gid_t { self.0.raw().st_gid as raw::gid_t } + pub fn rdev(&self) -> raw::dev_t { self.0.raw().st_rdev as raw::dev_t } + pub fn size(&self) -> raw::off_t { self.0.raw().st_size as raw::off_t } + pub fn atime(&self) -> raw::time_t { self.0.raw().st_atime } + pub fn atime_nsec(&self) -> c_long { self.0.raw().st_atime } + pub fn mtime(&self) -> raw::time_t { self.0.raw().st_mtime } + pub fn mtime_nsec(&self) -> c_long { self.0.raw().st_mtime } + pub fn ctime(&self) -> raw::time_t { self.0.raw().st_ctime } + pub fn ctime_nsec(&self) -> c_long { self.0.raw().st_ctime } + + pub fn blksize(&self) -> raw::blksize_t { + self.0.raw().st_blksize as raw::blksize_t + } + pub fn blocks(&self) -> raw::blkcnt_t { + self.0.raw().st_blocks as raw::blkcnt_t + } +} + +#[unstable(feature = "dir_entry_ext", reason = "recently added API")] +pub trait DirEntryExt { + fn ino(&self) -> raw::ino_t; +} + +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> raw::ino_t { self.as_inner().ino() } +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// +/// # Note +/// +/// On Windows, you must specify whether a symbolic link points to a file +/// or directory. Use `os::windows::fs::symlink_file` to create a +/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a +/// symbolic link to a directory. Additionally, the process must have +/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a +/// symbolic link. +/// +/// # Examples +/// +/// ``` +/// use std::os::unix::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(fs::symlink("a.txt", "b.txt")); +/// # Ok(()) +/// # } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> +{ + sys::fs2::symlink(src.as_ref(), dst.as_ref()) +} diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs new file mode 100644 index 00000000000..8cb4b4907f6 --- /dev/null +++ b/src/libstd/sys/unix/ext/io.rs @@ -0,0 +1,108 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to general I/O primitives + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use net; +use os::raw; +use sys; +use sys_common::{net2, AsInner, FromInner}; + +/// Raw file descriptors. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = raw::c_int; + +/// A trait to extract the raw unix file descriptor from an underlying +/// object. +/// +/// This is only available on unix platforms and must be imported in order +/// to call the method. Windows platforms have a corresponding `AsRawHandle` +/// and `AsRawSocket` set of traits. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guarantee to be valid while + /// the original object has not yet been destroyed. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +#[unstable(feature = "from_raw_os", + reason = "recent addition to std::os::unix::io")] +pub trait FromRawFd { + /// Constructs a new instances of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs2::File::from_inner(fd)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} + +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + let socket = sys::net::Socket::from_inner(fd); + net::TcpStream::from_inner(net2::TcpStream::from_inner(socket)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + let socket = sys::net::Socket::from_inner(fd); + net::TcpListener::from_inner(net2::TcpListener::from_inner(socket)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + let socket = sys::net::Socket::from_inner(fd); + net::UdpSocket::from_inner(net2::UdpSocket::from_inner(socket)) + } +} diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs new file mode 100644 index 00000000000..6fde45a7301 --- /dev/null +++ b/src/libstd/sys/unix/ext/mod.rs @@ -0,0 +1,53 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental extensions to `std` for Unix platforms. +//! +//! For now, this module is limited to extracting file descriptors, +//! but its functionality will grow over time. +//! +//! # Example +//! +//! ```no_run +//! use std::fs::File; +//! use std::os::unix::prelude::*; +//! +//! fn main() { +//! let f = File::create("foo.txt").unwrap(); +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native unix bindings +//! } +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod io; +pub mod ffi; +pub mod fs; +pub mod process; +pub mod raw; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + pub use super::io::{RawFd, AsRawFd}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] + pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt}; + #[doc(no_inline)] + pub use super::fs::{DirEntryExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::process::{CommandExt, ExitStatusExt}; +} diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs new file mode 100644 index 00000000000..8c9d0a86583 --- /dev/null +++ b/src/libstd/sys/unix/ext/process.rs @@ -0,0 +1,65 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to primitives in the `std::process` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use os::unix::raw::{uid_t, gid_t}; +use prelude::v1::*; +use process; +use sys; +use sys_common::{AsInnerMut, AsInner}; + +/// Unix-specific extensions to the `std::process::Command` builder +#[stable(feature = "rust1", since = "1.0.0")] +pub trait CommandExt { + /// Sets the child process's user id. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + #[stable(feature = "rust1", since = "1.0.0")] + fn uid(&mut self, id: uid_t) -> &mut process::Command; + + /// Similar to `uid`, but sets the group id of the child process. This has + /// the same semantics as the `uid` field. + #[stable(feature = "rust1", since = "1.0.0")] + fn gid(&mut self, id: gid_t) -> &mut process::Command; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl CommandExt for process::Command { + fn uid(&mut self, id: uid_t) -> &mut process::Command { + self.as_inner_mut().uid = Some(id); + self + } + + fn gid(&mut self, id: gid_t) -> &mut process::Command { + self.as_inner_mut().gid = Some(id); + self + } +} + +/// Unix-specific extensions to `std::process::ExitStatus` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ExitStatusExt { + /// If the process was terminated by a signal, returns that signal. + #[stable(feature = "rust1", since = "1.0.0")] + fn signal(&self) -> Option<i32>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExitStatusExt for process::ExitStatus { + fn signal(&self) -> Option<i32> { + match *self.as_inner() { + sys::process2::ExitStatus::Signal(s) => Some(s), + _ => None + } + } +} diff --git a/src/libstd/sys/unix/ext/raw.rs b/src/libstd/sys/unix/ext/raw.rs new file mode 100644 index 00000000000..8fe4b90456a --- /dev/null +++ b/src/libstd/sys/unix/ext/raw.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific primitives available on all unix platforms + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub type uid_t = u32; +pub type gid_t = u32; +pub type pid_t = i32; + +#[doc(inline)] +pub use sys::platform::raw::{dev_t, ino_t, mode_t, nlink_t, off_t, blksize_t}; +#[doc(inline)] +pub use sys::platform::raw::{blkcnt_t, time_t}; diff --git a/src/libstd/sys/unix/fs2.rs b/src/libstd/sys/unix/fs2.rs index 8eb84b26f22..678d8065ce7 100644 --- a/src/libstd/sys/unix/fs2.rs +++ b/src/libstd/sys/unix/fs2.rs @@ -21,14 +21,15 @@ use path::{Path, PathBuf}; use ptr; use sync::Arc; use sys::fd::FileDesc; +use sys::platform::raw; use sys::{c, cvt, cvt_r}; -use sys_common::FromInner; +use sys_common::{AsInner, FromInner}; use vec::Vec; pub struct File(FileDesc); pub struct FileAttr { - stat: libc::stat, + stat: raw::stat, } pub struct ReadDir { @@ -57,13 +58,10 @@ pub struct OpenOptions { #[derive(Clone, PartialEq, Eq, Debug)] pub struct FilePermissions { mode: mode_t } +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct FileType { mode: mode_t } + impl FileAttr { - pub fn is_dir(&self) -> bool { - (self.stat.st_mode as mode_t) & libc::S_IFMT == libc::S_IFDIR - } - pub fn is_file(&self) -> bool { - (self.stat.st_mode as mode_t) & libc::S_IFMT == libc::S_IFREG - } pub fn size(&self) -> u64 { self.stat.st_size as u64 } pub fn perm(&self) -> FilePermissions { FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 } @@ -76,12 +74,35 @@ impl FileAttr { self.mktime(self.stat.st_mtime as u64, self.stat.st_mtime_nsec as u64) } + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as mode_t } + } + + pub fn raw(&self) -> &raw::stat { &self.stat } + // times are in milliseconds (currently) fn mktime(&self, secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } } +impl AsInner<raw::stat> for FileAttr { + fn as_inner(&self) -> &raw::stat { &self.stat } +} + +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub trait MetadataExt { + fn as_raw_stat(&self) -> &raw::stat; +} + +impl MetadataExt for ::fs::Metadata { + fn as_raw_stat(&self) -> &raw::stat { &self.as_inner().stat } +} + +impl MetadataExt for ::os::unix::fs::Metadata { + fn as_raw_stat(&self) -> &raw::stat { self.as_inner() } +} + impl FilePermissions { pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } pub fn set_readonly(&mut self, readonly: bool) { @@ -91,11 +112,19 @@ impl FilePermissions { self.mode |= 0o222; } } - pub fn mode(&self) -> i32 { self.mode as i32 } + pub fn mode(&self) -> raw::mode_t { self.mode } +} + +impl FileType { + pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } + pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } + pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } + + fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } } -impl FromInner<i32> for FilePermissions { - fn from_inner(mode: i32) -> FilePermissions { +impl FromInner<raw::mode_t> for FilePermissions { + fn from_inner(mode: raw::mode_t) -> FilePermissions { FilePermissions { mode: mode as mode_t } } } @@ -147,6 +176,33 @@ impl DirEntry { self.root.join(<OsStr as OsStrExt>::from_bytes(self.name_bytes())) } + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result<FileType> { + extern { + fn rust_dir_get_mode(ptr: *mut libc::dirent_t) -> c_int; + } + unsafe { + match rust_dir_get_mode(self.dirent()) { + -1 => lstat(&self.path()).map(|m| m.file_type()), + n => Ok(FileType { mode: n as mode_t }), + } + } + } + + pub fn ino(&self) -> raw::ino_t { + extern { + fn rust_dir_get_ino(ptr: *mut libc::dirent_t) -> raw::ino_t; + } + unsafe { rust_dir_get_ino(self.dirent()) } + } + fn name_bytes(&self) -> &[u8] { extern { fn rust_list_dir_val(ptr: *mut libc::dirent_t) -> *const c_char; @@ -191,7 +247,7 @@ impl OpenOptions { self.flag(libc::O_CREAT, create); } - pub fn mode(&mut self, mode: i32) { + pub fn mode(&mut self, mode: raw::mode_t) { self.mode = mode as mode_t; } @@ -228,8 +284,10 @@ impl File { pub fn into_fd(self) -> FileDesc { self.0 } pub fn file_attr(&self) -> io::Result<FileAttr> { - let mut stat: libc::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { libc::fstat(self.0.raw(), &mut stat) })); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::fstat(self.0.raw(), &mut stat as *mut _ as *mut _) + })); Ok(FileAttr { stat: stat }) } @@ -420,15 +478,19 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> { pub fn stat(p: &Path) -> io::Result<FileAttr> { let p = try!(cstr(p)); - let mut stat: libc::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { libc::stat(p.as_ptr(), &mut stat) })); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::stat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })); Ok(FileAttr { stat: stat }) } pub fn lstat(p: &Path) -> io::Result<FileAttr> { let p = try!(cstr(p)); - let mut stat: libc::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { libc::lstat(p.as_ptr(), &mut stat) })); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })); Ok(FileAttr { stat: stat }) } @@ -438,3 +500,17 @@ pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { try!(cvt(unsafe { c::utimes(p.as_ptr(), buf.as_ptr()) })); Ok(()) } + +pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { + let path = try!(CString::new(p.as_os_str().as_bytes())); + let mut buf = vec![0u8; 16 * 1024]; + unsafe { + let r = c::realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _); + if r.is_null() { + return Err(io::Error::last_os_error()) + } + } + let p = buf.iter().position(|i| *i == 0).unwrap(); + buf.truncate(p); + Ok(PathBuf::from(OsString::from_vec(buf))) +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index d99753a6a4c..78b798d3bff 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -18,6 +18,16 @@ use libc; use num::One; use ops::Neg; +#[cfg(target_os = "android")] pub use os::android as platform; +#[cfg(target_os = "bitrig")] pub use os::bitrig as platform; +#[cfg(target_os = "dragonfly")] pub use os::dragonfly as platform; +#[cfg(target_os = "freebsd")] pub use os::freebsd as platform; +#[cfg(target_os = "ios")] pub use os::ios as platform; +#[cfg(target_os = "linux")] pub use os::linux as platform; +#[cfg(target_os = "macos")] pub use os::macos as platform; +#[cfg(target_os = "nacl")] pub use os::nacl as platform; +#[cfg(target_os = "openbsd")] pub use os::openbsd as platform; + pub mod backtrace; pub mod c; pub mod condvar; diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs deleted file mode 100644 index dd747d202a0..00000000000 --- a/src/libstd/sys/windows/ext.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Experimental extensions to `std` for Windows. -//! -//! For now, this module is limited to extracting handles, file -//! descriptors, and sockets, but its functionality will grow over -//! time. - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub mod io { - use fs; - use libc; - use net; - use sys_common::{net2, AsInner, FromInner}; - use sys; - - /// Raw HANDLEs. - #[stable(feature = "rust1", since = "1.0.0")] - pub type RawHandle = libc::HANDLE; - - /// Raw SOCKETs. - #[stable(feature = "rust1", since = "1.0.0")] - pub type RawSocket = libc::SOCKET; - - /// Extract raw handles. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait AsRawHandle { - /// Extracts the raw handle, without taking any ownership. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_handle(&self) -> RawHandle; - } - - /// Construct I/O objects from raw handles. - #[unstable(feature = "from_raw_os", - reason = "recent addition to the std::os::windows::io module")] - pub trait FromRawHandle { - /// Constructs a new I/O object from the specified raw handle. - /// - /// This function will **consume ownership** of the handle given, - /// passing responsibility for closing the handle to the returned - /// object. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_handle(handle: RawHandle) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawHandle for fs::File { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() - } - } - - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawHandle for fs::File { - unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { - fs::File::from_inner(sys::fs2::File::from_inner(handle)) - } - } - - /// Extract raw sockets. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait AsRawSocket { - /// Extracts the underlying raw socket from this object. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_socket(&self) -> RawSocket; - } - - /// Create I/O objects from raw sockets. - #[unstable(feature = "from_raw_os", reason = "recent addition to module")] - pub trait FromRawSocket { - /// Creates a new I/O object from the given raw socket. - /// - /// This function will **consume ownership** of the socket provided and - /// it will be closed when the returned object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_socket(sock: RawSocket) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawSocket for net::TcpStream { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawSocket for net::TcpListener { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawSocket for net::UdpSocket { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } - } - - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawSocket for net::TcpStream { - unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { - let sock = sys::net::Socket::from_inner(sock); - net::TcpStream::from_inner(net2::TcpStream::from_inner(sock)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawSocket for net::TcpListener { - unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { - let sock = sys::net::Socket::from_inner(sock); - net::TcpListener::from_inner(net2::TcpListener::from_inner(sock)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawSocket for net::UdpSocket { - unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { - let sock = sys::net::Socket::from_inner(sock); - net::UdpSocket::from_inner(net2::UdpSocket::from_inner(sock)) - } - } -} - -/// Windows-specific extensions to the primitives in the `std::ffi` module. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod ffi { - use ffi::{OsString, OsStr}; - use sys::os_str::Buf; - use sys_common::wtf8::Wtf8Buf; - use sys_common::{FromInner, AsInner}; - - pub use sys_common::wtf8::EncodeWide; - - /// Windows-specific extensions to `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStringExt { - /// Creates an `OsString` from a potentially ill-formed UTF-16 slice of - /// 16-bit code units. - /// - /// This is lossless: calling `.encode_wide()` on the resulting string - /// will always return the original code units. - #[stable(feature = "rust1", since = "1.0.0")] - fn from_wide(wide: &[u16]) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStringExt for OsString { - fn from_wide(wide: &[u16]) -> OsString { - FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) - } - } - - /// Windows-specific extensions to `OsStr`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStrExt { - /// Re-encodes an `OsStr` as a wide character sequence, - /// i.e. potentially ill-formed UTF-16. - /// - /// This is lossless. Note that the encoding does not include a final - /// null. - #[stable(feature = "rust1", since = "1.0.0")] - fn encode_wide(&self) -> EncodeWide; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStrExt for OsStr { - fn encode_wide(&self) -> EncodeWide { - self.as_inner().inner.encode_wide() - } - } -} - -/// Windows-specific extensions for the primitives in `std::fs` -#[unstable(feature = "fs_ext", reason = "may require more thought/methods")] -pub mod fs { - use fs::OpenOptions; - use sys; - use sys_common::AsInnerMut; - use path::Path; - use convert::AsRef; - use io; - - /// Windows-specific extensions to `OpenOptions` - pub trait OpenOptionsExt { - /// Overrides the `dwDesiredAccess` argument to the call to `CreateFile` - /// with the specified value. - fn desired_access(&mut self, access: i32) -> &mut Self; - - /// Overrides the `dwCreationDisposition` argument to the call to - /// `CreateFile` with the specified value. - /// - /// This will override any values of the standard `create` flags, for - /// example. - fn creation_disposition(&mut self, val: i32) -> &mut Self; - - /// Overrides the `dwFlagsAndAttributes` argument to the call to - /// `CreateFile` with the specified value. - /// - /// This will override any values of the standard flags on the - /// `OpenOptions` structure. - fn flags_and_attributes(&mut self, val: i32) -> &mut Self; - - /// Overrides the `dwShareMode` argument to the call to `CreateFile` with - /// the specified value. - /// - /// This will override any values of the standard flags on the - /// `OpenOptions` structure. - fn share_mode(&mut self, val: i32) -> &mut Self; - } - - impl OpenOptionsExt for OpenOptions { - fn desired_access(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().desired_access(access); self - } - fn creation_disposition(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().creation_disposition(access); self - } - fn flags_and_attributes(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().flags_and_attributes(access); self - } - fn share_mode(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().share_mode(access); self - } - } - - /// Creates a new file symbolic link on the filesystem. - /// - /// The `dst` path will be a file symbolic link pointing to the `src` - /// path. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(fs_ext)] - /// use std::os::windows::fs; - /// - /// # fn foo() -> std::io::Result<()> { - /// try!(fs::symlink_file("a.txt", "b.txt")); - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) - -> io::Result<()> - { - sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), false) - } - - /// Creates a new directory symlink on the filesystem. - /// - /// The `dst` path will be a directory symbolic link pointing to the `src` - /// path. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(fs_ext)] - /// use std::os::windows::fs; - /// - /// # fn foo() -> std::io::Result<()> { - /// try!(fs::symlink_file("a", "b")); - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>> (src: P, dst: Q) - -> io::Result<()> - { - sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), true) - } -} - -/// A prelude for conveniently writing platform-specific code. -/// -/// Includes all extension traits, and some important type definitions. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod prelude { - #[doc(no_inline)] - pub use super::io::{RawSocket, RawHandle, AsRawSocket, AsRawHandle}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::ffi::{OsStrExt, OsStringExt}; - #[doc(no_inline)] - pub use super::fs::OpenOptionsExt; -} diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs new file mode 100644 index 00000000000..3fa96f4dd13 --- /dev/null +++ b/src/libstd/sys/windows/ext/ffi.rs @@ -0,0 +1,58 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows-specific extensions to the primitives in the `std::ffi` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsString, OsStr}; +use sys::os_str::Buf; +use sys_common::wtf8::Wtf8Buf; +use sys_common::{FromInner, AsInner}; + +pub use sys_common::wtf8::EncodeWide; + +/// Windows-specific extensions to `OsString`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a potentially ill-formed UTF-16 slice of + /// 16-bit code units. + /// + /// This is lossless: calling `.encode_wide()` on the resulting string + /// will always return the original code units. + #[stable(feature = "rust1", since = "1.0.0")] + fn from_wide(wide: &[u16]) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_wide(wide: &[u16]) -> OsString { + FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) + } +} + +/// Windows-specific extensions to `OsStr`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + /// Re-encodes an `OsStr` as a wide character sequence, + /// i.e. potentially ill-formed UTF-16. + /// + /// This is lossless. Note that the encoding does not include a final + /// null. + #[stable(feature = "rust1", since = "1.0.0")] + fn encode_wide(&self) -> EncodeWide; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn encode_wide(&self) -> EncodeWide { + self.as_inner().inner.encode_wide() + } +} diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs new file mode 100644 index 00000000000..23c1fcf4b3c --- /dev/null +++ b/src/libstd/sys/windows/ext/fs.rs @@ -0,0 +1,150 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows-specific extensions for the primitives in `std::fs` + +#![stable(feature = "rust1", since = "1.0.0")] + +use prelude::v1::*; + +use fs::{OpenOptions, Metadata}; +use io; +use path::Path; +use sys; +use sys_common::{AsInnerMut, AsInner}; + +/// Windows-specific extensions to `OpenOptions` +#[unstable(feature = "fs_ext", reason = "may require more thought/methods")] +pub trait OpenOptionsExt { + /// Overrides the `dwDesiredAccess` argument to the call to `CreateFile` + /// with the specified value. + fn desired_access(&mut self, access: i32) -> &mut Self; + + /// Overrides the `dwCreationDisposition` argument to the call to + /// `CreateFile` with the specified value. + /// + /// This will override any values of the standard `create` flags, for + /// example. + fn creation_disposition(&mut self, val: i32) -> &mut Self; + + /// Overrides the `dwFlagsAndAttributes` argument to the call to + /// `CreateFile` with the specified value. + /// + /// This will override any values of the standard flags on the + /// `OpenOptions` structure. + fn flags_and_attributes(&mut self, val: i32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to `CreateFile` with + /// the specified value. + /// + /// This will override any values of the standard flags on the + /// `OpenOptions` structure. + fn share_mode(&mut self, val: i32) -> &mut Self; +} + +impl OpenOptionsExt for OpenOptions { + fn desired_access(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().desired_access(access); self + } + fn creation_disposition(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().creation_disposition(access); self + } + fn flags_and_attributes(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().flags_and_attributes(access); self + } + fn share_mode(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().share_mode(access); self + } +} + +/// Extension methods for `fs::Metadata` to access the raw fields contained +/// within. +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub trait MetadataExt { + /// Returns the value of the `dwFileAttributes` field of this metadata. + /// + /// This field contains the file system attribute information for a file + /// or directory. + fn file_attributes(&self) -> u32; + + /// Returns the value of the `ftCreationTime` field of this metadata. + /// + /// The returned 64-bit value represents the number of 100-nanosecond + /// intervals since January 1, 1601 (UTC). + fn creation_time(&self) -> u64; + + /// Returns the value of the `ftLastAccessTime` field of this metadata. + /// + /// The returned 64-bit value represents the number of 100-nanosecond + /// intervals since January 1, 1601 (UTC). + fn last_access_time(&self) -> u64; + + /// Returns the value of the `ftLastWriteTime` field of this metadata. + /// + /// The returned 64-bit value represents the number of 100-nanosecond + /// intervals since January 1, 1601 (UTC). + fn last_write_time(&self) -> u64; + + /// Returns the value of the `nFileSize{High,Low}` fields of this + /// metadata. + /// + /// The returned value does not have meaning for directories. + fn file_size(&self) -> u64; +} + +impl MetadataExt for Metadata { + fn file_attributes(&self) -> u32 { self.as_inner().attrs() } + fn creation_time(&self) -> u64 { self.as_inner().created() } + fn last_access_time(&self) -> u64 { self.as_inner().accessed() } + fn last_write_time(&self) -> u64 { self.as_inner().modified() } + fn file_size(&self) -> u64 { self.as_inner().size() } +} + +/// Creates a new file symbolic link on the filesystem. +/// +/// The `dst` path will be a file symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```ignore +/// use std::os::windows::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(fs::symlink_file("a.txt", "b.txt")); +/// # Ok(()) +/// # } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) + -> io::Result<()> { + sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), false) +} + +/// Creates a new directory symlink on the filesystem. +/// +/// The `dst` path will be a directory symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```ignore +/// use std::os::windows::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(fs::symlink_file("a", "b")); +/// # Ok(()) +/// # } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) + -> io::Result<()> { + sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), true) +} diff --git a/src/libstd/sys/windows/ext/io.rs b/src/libstd/sys/windows/ext/io.rs new file mode 100644 index 00000000000..b88a6316eee --- /dev/null +++ b/src/libstd/sys/windows/ext/io.rs @@ -0,0 +1,131 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use os::windows::raw; +use net; +use sys_common::{net2, AsInner, FromInner}; +use sys; + +/// Raw HANDLEs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawHandle = raw::HANDLE; + +/// Raw SOCKETs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawSocket = raw::SOCKET; + +/// Extract raw handles. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawHandle { + /// Extracts the raw handle, without taking any ownership. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_handle(&self) -> RawHandle; +} + +/// Construct I/O objects from raw handles. +#[unstable(feature = "from_raw_os", + reason = "recent addition to the std::os::windows::io module")] +pub trait FromRawHandle { + /// Constructs a new I/O object from the specified raw handle. + /// + /// This function will **consume ownership** of the handle given, + /// passing responsibility for closing the handle to the returned + /// object. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_handle(handle: RawHandle) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawHandle for fs::File { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as RawHandle + } +} + +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawHandle for fs::File { + unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { + let handle = handle as ::libc::HANDLE; + fs::File::from_inner(sys::fs2::File::from_inner(handle)) + } +} + +/// Extract raw sockets. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawSocket { + /// Extracts the underlying raw socket from this object. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_socket(&self) -> RawSocket; +} + +/// Create I/O objects from raw sockets. +#[unstable(feature = "from_raw_os", reason = "recent addition to module")] +pub trait FromRawSocket { + /// Creates a new I/O object from the given raw socket. + /// + /// This function will **consume ownership** of the socket provided and + /// it will be closed when the returned object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_socket(sock: RawSocket) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpStream { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpListener { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} + +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawSocket for net::TcpStream { + unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { + let sock = sys::net::Socket::from_inner(sock); + net::TcpStream::from_inner(net2::TcpStream::from_inner(sock)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawSocket for net::TcpListener { + unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { + let sock = sys::net::Socket::from_inner(sock); + net::TcpListener::from_inner(net2::TcpListener::from_inner(sock)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawSocket for net::UdpSocket { + unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { + let sock = sys::net::Socket::from_inner(sock); + net::UdpSocket::from_inner(net2::UdpSocket::from_inner(sock)) + } +} diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs new file mode 100644 index 00000000000..08dfa4cc877 --- /dev/null +++ b/src/libstd/sys/windows/ext/mod.rs @@ -0,0 +1,35 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental extensions to `std` for Windows. +//! +//! For now, this module is limited to extracting handles, file +//! descriptors, and sockets, but its functionality will grow over +//! time. + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod ffi; +pub mod fs; +pub mod io; +pub mod raw; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + pub use super::io::{RawSocket, RawHandle, AsRawSocket, AsRawHandle}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] + pub use super::fs::{OpenOptionsExt, MetadataExt}; +} diff --git a/src/libstd/sys/windows/ext/raw.rs b/src/libstd/sys/windows/ext/raw.rs new file mode 100644 index 00000000000..656e480ad09 --- /dev/null +++ b/src/libstd/sys/windows/ext/raw.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows-specific primitives + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +use os::raw; + +pub type HANDLE = *mut raw::c_void; +#[cfg(target_pointer_width = "32")] +pub type SOCKET = u32; +#[cfg(target_pointer_width = "64")] +pub type SOCKET = u64; diff --git a/src/libstd/sys/windows/fs2.rs b/src/libstd/sys/windows/fs2.rs index 5ac9a0ace58..2c81c34d3a4 100644 --- a/src/libstd/sys/windows/fs2.rs +++ b/src/libstd/sys/windows/fs2.rs @@ -27,7 +27,16 @@ use sys_common::FromInner; use vec::Vec; pub struct File { handle: Handle } -pub struct FileAttr { data: c::WIN32_FILE_ATTRIBUTE_DATA } + +pub struct FileAttr { + data: c::WIN32_FILE_ATTRIBUTE_DATA, + is_symlink: bool, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum FileType { + Dir, File, Symlink, ReparsePoint +} pub struct ReadDir { handle: FindNextFileHandle, @@ -111,8 +120,31 @@ impl DirEntry { } pub fn path(&self) -> PathBuf { + self.root.join(&self.file_name()) + } + + pub fn file_name(&self) -> OsString { let filename = super::truncate_utf16_at_nul(&self.data.cFileName); - self.root.join(&<OsString as OsStringExt>::from_wide(filename)) + OsString::from_wide(filename) + } + + pub fn file_type(&self) -> io::Result<FileType> { + Ok(FileType::new(self.data.dwFileAttributes, + self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK)) + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + Ok(FileAttr { + data: c::WIN32_FILE_ATTRIBUTE_DATA { + dwFileAttributes: self.data.dwFileAttributes, + ftCreationTime: self.data.ftCreationTime, + ftLastAccessTime: self.data.ftLastAccessTime, + ftLastWriteTime: self.data.ftLastWriteTime, + nFileSizeHigh: self.data.nFileSizeHigh, + nFileSizeLow: self.data.nFileSizeLow, + }, + is_symlink: self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK, + }) } } @@ -180,6 +212,13 @@ impl OpenOptions { } impl File { + fn open_reparse_point(path: &Path) -> io::Result<File> { + let mut opts = OpenOptions::new(); + opts.read(true); + opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32); + File::open(path, &opts) + } + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { let path = to_utf16(path); let handle = unsafe { @@ -224,7 +263,7 @@ impl File { let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); try!(cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))); - Ok(FileAttr { + let mut attr = FileAttr { data: c::WIN32_FILE_ATTRIBUTE_DATA { dwFileAttributes: info.dwFileAttributes, ftCreationTime: info.ftCreationTime, @@ -232,8 +271,13 @@ impl File { ftLastWriteTime: info.ftLastWriteTime, nFileSizeHigh: info.nFileSizeHigh, nFileSizeLow: info.nFileSizeLow, - } - }) + }, + is_symlink: false, + }; + if attr.is_reparse_point() { + attr.is_symlink = self.is_symlink(); + } + Ok(attr) } } @@ -263,6 +307,41 @@ impl File { } pub fn handle(&self) -> &Handle { &self.handle } + + fn is_symlink(&self) -> bool { + self.readlink().is_ok() + } + + fn readlink(&self) -> io::Result<PathBuf> { + let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let mut bytes = 0; + + unsafe { + try!(cvt({ + c::DeviceIoControl(self.handle.raw(), + c::FSCTL_GET_REPARSE_POINT, + 0 as *mut _, + 0, + space.as_mut_ptr() as *mut _, + space.len() as libc::DWORD, + &mut bytes, + 0 as *mut _) + })); + let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _; + if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK { + return Err(io::Error::new(io::ErrorKind::Other, "not a symlink")) + } + let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = + &(*buf).rest as *const _ as *const _; + let path_buffer = &(*info).PathBuffer as *const _ as *const u16; + let subst_off = (*info).SubstituteNameOffset / 2; + let subst_ptr = path_buffer.offset(subst_off as isize); + let subst_len = (*info).SubstituteNameLength / 2; + let subst = slice::from_raw_parts(subst_ptr, subst_len as usize); + + Ok(PathBuf::from(OsString::from_wide(subst))) + } + } } impl FromInner<libc::HANDLE> for File { @@ -285,27 +364,30 @@ pub fn to_utf16(s: &Path) -> Vec<u16> { } impl FileAttr { - pub fn is_dir(&self) -> bool { - self.data.dwFileAttributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 - } - pub fn is_file(&self) -> bool { - !self.is_dir() - } pub fn size(&self) -> u64 { ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64) } + pub fn perm(&self) -> FilePermissions { FilePermissions { attrs: self.data.dwFileAttributes } } - pub fn accessed(&self) -> u64 { self.to_ms(&self.data.ftLastAccessTime) } - pub fn modified(&self) -> u64 { self.to_ms(&self.data.ftLastWriteTime) } + pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 } + + pub fn file_type(&self) -> FileType { + FileType::new(self.data.dwFileAttributes, self.is_symlink) + } + + pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) } + pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) } + pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) } - fn to_ms(&self, ft: &libc::FILETIME) -> u64 { - // FILETIME is in 100ns intervals and there are 10000 intervals in a - // millisecond. - let bits = (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32); - bits / 10000 + fn to_u64(&self, ft: &libc::FILETIME) -> u64 { + (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) + } + + fn is_reparse_point(&self) -> bool { + self.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 } } @@ -323,6 +405,26 @@ impl FilePermissions { } } +impl FileType { + fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType { + if attrs & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + if is_symlink { + FileType::Symlink + } else { + FileType::ReparsePoint + } + } else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 { + FileType::Dir + } else { + FileType::File + } + } + + pub fn is_dir(&self) -> bool { *self == FileType::Dir } + pub fn is_file(&self) -> bool { *self == FileType::File } + pub fn is_symlink(&self) -> bool { *self == FileType::Symlink } +} + pub fn mkdir(p: &Path) -> io::Result<()> { let p = to_utf16(p); try!(cvt(unsafe { @@ -374,40 +476,8 @@ pub fn rmdir(p: &Path) -> io::Result<()> { } pub fn readlink(p: &Path) -> io::Result<PathBuf> { - let mut opts = OpenOptions::new(); - opts.read(true); - opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32); - let file = try!(File::open(p, &opts)); - - let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - let mut bytes = 0; - - unsafe { - try!(cvt({ - c::DeviceIoControl(file.handle.raw(), - c::FSCTL_GET_REPARSE_POINT, - 0 as *mut _, - 0, - space.as_mut_ptr() as *mut _, - space.len() as libc::DWORD, - &mut bytes, - 0 as *mut _) - })); - let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _; - if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK { - return Err(io::Error::new(io::ErrorKind::Other, "not a symlink")) - } - let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = - &(*buf).rest as *const _ as *const _; - let path_buffer = &(*info).PathBuffer as *const _ as *const u16; - let subst_off = (*info).SubstituteNameOffset / 2; - let subst_ptr = path_buffer.offset(subst_off as isize); - let subst_len = (*info).SubstituteNameLength / 2; - let subst = slice::from_raw_parts(subst_ptr, subst_len as usize); - - Ok(PathBuf::from(OsString::from_wide(subst))) - } - + let file = try!(File::open_reparse_point(p)); + file.readlink() } pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { @@ -435,12 +505,28 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> { } pub fn stat(p: &Path) -> io::Result<FileAttr> { - let p = to_utf16(p); + let attr = try!(lstat(p)); + if attr.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + let opts = OpenOptions::new(); + let file = try!(File::open(p, &opts)); + file.file_attr() + } else { + Ok(attr) + } +} + +pub fn lstat(p: &Path) -> io::Result<FileAttr> { + let utf16 = to_utf16(p); unsafe { let mut attr: FileAttr = mem::zeroed(); - try!(cvt(c::GetFileAttributesExW(p.as_ptr(), + try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(), c::GetFileExInfoStandard, &mut attr.data as *mut _ as *mut _))); + if attr.is_reparse_point() { + attr.is_symlink = File::open_reparse_point(p).map(|f| { + f.is_symlink() + }).unwrap_or(false); + } Ok(attr) } } @@ -465,3 +551,17 @@ pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { })); Ok(()) } + +pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { + use sys::c::compat::kernel32::GetFinalPathNameByHandleW; + + let mut opts = OpenOptions::new(); + opts.read(true); + let f = try!(File::open(p, &opts)); + super::fill_utf16_buf(|buf, sz| unsafe { + GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, + libc::VOLUME_NAME_DOS) + }, |buf| { + PathBuf::from(OsString::from_wide(buf)) + }) +} |
