mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Auto merge of #77666 - fusion-engineering-forks:vxworks-cleanup, r=dtolnay
Vxworks / Unix deduplication `sys/vxworks` was almost entirely an (outdated) copy of `sys/unix`. I went through every file to check the differences and tried to figure out if they were simply outdated or intentional differences between `unix` and `vxworks`. Most of them did not have any `vxworks`-specific changes, so are deleted. I've added some minor `cfg(target_os = "vxworks")`-specific things to `sys/unix` to allow for deduplication. Before this change, the vxworks target didn't compile because its outdated process implementation did not match the expected interface anymore. This also fixes that: `std` compiles again for `x86_64-wrs-vxworks`. It's probably good to merge `sys/vxworks` entirely into `sys/unix`, but it might be better to to that in a follow-up PR. `@rustbot` modify labels: +T-libs +C-cleanup
This commit is contained in:
commit
95b4a4f0ee
@ -26,10 +26,16 @@ pub trait MetadataExt {
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_atime(&self) -> i64;
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_atime_nsec(&self) -> i64;
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_mtime(&self) -> i64;
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_mtime_nsec(&self) -> i64;
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_ctime(&self) -> i64;
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_ctime_nsec(&self) -> i64;
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_blksize(&self) -> u64;
|
||||
#[stable(feature = "metadata_ext2", since = "1.8.0")]
|
||||
fn st_blocks(&self) -> u64;
|
||||
@ -66,12 +72,21 @@ impl MetadataExt for Metadata {
|
||||
fn st_atime(&self) -> i64 {
|
||||
self.as_inner().as_inner().st_atime as i64
|
||||
}
|
||||
fn st_atime_nsec(&self) -> i64 {
|
||||
0
|
||||
}
|
||||
fn st_mtime(&self) -> i64 {
|
||||
self.as_inner().as_inner().st_mtime as i64
|
||||
}
|
||||
fn st_mtime_nsec(&self) -> i64 {
|
||||
0
|
||||
}
|
||||
fn st_ctime(&self) -> i64 {
|
||||
self.as_inner().as_inner().st_ctime as i64
|
||||
}
|
||||
fn st_ctime_nsec(&self) -> i64 {
|
||||
0
|
||||
}
|
||||
fn st_blksize(&self) -> u64 {
|
||||
self.as_inner().as_inner().st_blksize as u64
|
||||
}
|
||||
|
@ -5,3 +5,6 @@ use crate::os::raw::c_ulong;
|
||||
|
||||
#[stable(feature = "pthread_t", since = "1.8.0")]
|
||||
pub type pthread_t = c_ulong;
|
||||
|
||||
#[stable(feature = "raw_ext", since = "1.1.0")]
|
||||
pub use libc::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, time_t};
|
||||
|
@ -70,7 +70,8 @@ impl DoubleEndedIterator for Args {
|
||||
target_os = "haiku",
|
||||
target_os = "l4re",
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox"
|
||||
target_os = "redox",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
mod imp {
|
||||
use super::Args;
|
||||
|
@ -650,6 +650,9 @@ pub trait MetadataExt {
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn blocks(&self) -> u64;
|
||||
#[cfg(target_os = "vxworks")]
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn attrib(&self) -> u8;
|
||||
}
|
||||
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
@ -702,6 +705,10 @@ impl MetadataExt for fs::Metadata {
|
||||
fn blocks(&self) -> u64 {
|
||||
self.st_blocks()
|
||||
}
|
||||
#[cfg(target_os = "vxworks")]
|
||||
fn attrib(&self) -> u8 {
|
||||
self.st_attrib()
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extensions for [`fs::FileType`].
|
||||
|
@ -16,12 +16,20 @@ pub trait CommandExt {
|
||||
/// `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: u32) -> &mut process::Command;
|
||||
fn uid(
|
||||
&mut self,
|
||||
#[cfg(not(target_os = "vxworks"))] id: u32,
|
||||
#[cfg(target_os = "vxworks")] id: u16,
|
||||
) -> &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: u32) -> &mut process::Command;
|
||||
fn gid(
|
||||
&mut self,
|
||||
#[cfg(not(target_os = "vxworks"))] id: u32,
|
||||
#[cfg(target_os = "vxworks")] id: u16,
|
||||
) -> &mut process::Command;
|
||||
|
||||
/// Schedules a closure to be run just before the `exec` function is
|
||||
/// invoked.
|
||||
@ -115,12 +123,20 @@ pub trait CommandExt {
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl CommandExt for process::Command {
|
||||
fn uid(&mut self, id: u32) -> &mut process::Command {
|
||||
fn uid(
|
||||
&mut self,
|
||||
#[cfg(not(target_os = "vxworks"))] id: u32,
|
||||
#[cfg(target_os = "vxworks")] id: u16,
|
||||
) -> &mut process::Command {
|
||||
self.as_inner_mut().uid(id);
|
||||
self
|
||||
}
|
||||
|
||||
fn gid(&mut self, id: u32) -> &mut process::Command {
|
||||
fn gid(
|
||||
&mut self,
|
||||
#[cfg(not(target_os = "vxworks"))] id: u32,
|
||||
#[cfg(target_os = "vxworks")] id: u16,
|
||||
) -> &mut process::Command {
|
||||
self.as_inner_mut().gid(id);
|
||||
self
|
||||
}
|
||||
|
@ -200,7 +200,8 @@ impl FileDesc {
|
||||
target_os = "l4re",
|
||||
target_os = "linux",
|
||||
target_os = "haiku",
|
||||
target_os = "redox"
|
||||
target_os = "redox",
|
||||
target_os = "vxworks"
|
||||
)))]
|
||||
pub fn set_cloexec(&self) -> io::Result<()> {
|
||||
unsafe {
|
||||
@ -217,7 +218,8 @@ impl FileDesc {
|
||||
target_os = "l4re",
|
||||
target_os = "linux",
|
||||
target_os = "haiku",
|
||||
target_os = "redox"
|
||||
target_os = "redox",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
pub fn set_cloexec(&self) -> io::Result<()> {
|
||||
unsafe {
|
||||
|
@ -297,6 +297,7 @@ impl FileAttr {
|
||||
|
||||
#[cfg(not(target_os = "netbsd"))]
|
||||
impl FileAttr {
|
||||
#[cfg(not(target_os = "vxworks"))]
|
||||
pub fn modified(&self) -> io::Result<SystemTime> {
|
||||
Ok(SystemTime::from(libc::timespec {
|
||||
tv_sec: self.stat.st_mtime as libc::time_t,
|
||||
@ -304,6 +305,15 @@ impl FileAttr {
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn modified(&self) -> io::Result<SystemTime> {
|
||||
Ok(SystemTime::from(libc::timespec {
|
||||
tv_sec: self.stat.st_mtime as libc::time_t,
|
||||
tv_nsec: 0,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "vxworks"))]
|
||||
pub fn accessed(&self) -> io::Result<SystemTime> {
|
||||
Ok(SystemTime::from(libc::timespec {
|
||||
tv_sec: self.stat.st_atime as libc::time_t,
|
||||
@ -311,6 +321,14 @@ impl FileAttr {
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn accessed(&self) -> io::Result<SystemTime> {
|
||||
Ok(SystemTime::from(libc::timespec {
|
||||
tv_sec: self.stat.st_atime as libc::time_t,
|
||||
tv_nsec: 0,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
@ -535,12 +553,22 @@ impl DirEntry {
|
||||
lstat(&self.path())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))]
|
||||
#[cfg(any(
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "haiku",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
pub fn file_type(&self) -> io::Result<FileType> {
|
||||
lstat(&self.path()).map(|m| m.file_type())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "haiku")))]
|
||||
#[cfg(not(any(
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
target_os = "haiku",
|
||||
target_os = "vxworks"
|
||||
)))]
|
||||
pub fn file_type(&self) -> io::Result<FileType> {
|
||||
match self.entry.d_type {
|
||||
libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
|
||||
@ -565,7 +593,8 @@ impl DirEntry {
|
||||
target_os = "haiku",
|
||||
target_os = "l4re",
|
||||
target_os = "fuchsia",
|
||||
target_os = "redox"
|
||||
target_os = "redox",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
pub fn ino(&self) -> u64 {
|
||||
self.entry.d_ino as u64
|
||||
@ -603,7 +632,8 @@ impl DirEntry {
|
||||
target_os = "linux",
|
||||
target_os = "emscripten",
|
||||
target_os = "l4re",
|
||||
target_os = "haiku"
|
||||
target_os = "haiku",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
fn name_bytes(&self) -> &[u8] {
|
||||
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
|
||||
@ -901,13 +931,25 @@ impl fmt::Debug for File {
|
||||
Some(PathBuf::from(OsString::from_vec(buf)))
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
#[cfg(target_os = "vxworks")]
|
||||
fn get_path(fd: c_int) -> Option<PathBuf> {
|
||||
let mut buf = vec![0; libc::PATH_MAX as usize];
|
||||
let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
|
||||
if n == -1 {
|
||||
return None;
|
||||
}
|
||||
let l = buf.iter().position(|&c| c == 0).unwrap();
|
||||
buf.truncate(l as usize);
|
||||
Some(PathBuf::from(OsString::from_vec(buf)))
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
|
||||
fn get_path(_fd: c_int) -> Option<PathBuf> {
|
||||
// FIXME(#24570): implement this for other Unix platforms
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))]
|
||||
fn get_mode(fd: c_int) -> Option<(bool, bool)> {
|
||||
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
|
||||
if mode == -1 {
|
||||
@ -921,7 +963,7 @@ impl fmt::Debug for File {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
|
||||
fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
|
||||
// FIXME(#24570): implement this for other Unix platforms
|
||||
None
|
||||
|
@ -77,6 +77,7 @@ impl Socket {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "vxworks"))]
|
||||
pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
|
||||
unsafe {
|
||||
let mut fds = [0, 0];
|
||||
@ -98,6 +99,11 @@ impl Socket {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
|
||||
self.set_nonblocking(true)?;
|
||||
let r = unsafe {
|
||||
@ -366,7 +372,7 @@ impl IntoInner<c_int> for Socket {
|
||||
// res_init unconditionally, we call it only when we detect we're linking
|
||||
// against glibc version < 2.26. (That is, when we both know its needed and
|
||||
// believe it's thread-safe).
|
||||
#[cfg(target_env = "gnu")]
|
||||
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
|
||||
fn on_resolver_failure() {
|
||||
use crate::sys;
|
||||
|
||||
@ -378,5 +384,5 @@ fn on_resolver_failure() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_env = "gnu"))]
|
||||
#[cfg(any(not(target_env = "gnu"), target_os = "vxworks"))]
|
||||
fn on_resolver_failure() {}
|
||||
|
@ -37,7 +37,7 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[cfg(not(target_os = "dragonfly"))]
|
||||
#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
|
||||
#[cfg_attr(
|
||||
any(
|
||||
target_os = "linux",
|
||||
@ -67,18 +67,28 @@ extern "C" {
|
||||
}
|
||||
|
||||
/// Returns the platform-specific value of errno
|
||||
#[cfg(not(target_os = "dragonfly"))]
|
||||
#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))]
|
||||
pub fn errno() -> i32 {
|
||||
unsafe { (*errno_location()) as i32 }
|
||||
}
|
||||
|
||||
/// Sets the platform-specific value of errno
|
||||
#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall!
|
||||
#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall!
|
||||
#[allow(dead_code)] // but not all target cfgs actually end up using it
|
||||
pub fn set_errno(e: i32) {
|
||||
unsafe { *errno_location() = e as c_int }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn errno() -> i32 {
|
||||
unsafe { libc::errnoGet() }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn set_errno(e: i32) {
|
||||
unsafe { libc::errnoSet(e as c_int) };
|
||||
}
|
||||
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
pub fn errno() -> i32 {
|
||||
extern "C" {
|
||||
@ -439,6 +449,19 @@ pub fn current_exe() -> io::Result<PathBuf> {
|
||||
Err(io::Error::new(ErrorKind::Other, "Not yet implemented!"))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub fn current_exe() -> io::Result<PathBuf> {
|
||||
#[cfg(test)]
|
||||
use realstd::env;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use crate::env;
|
||||
|
||||
let exe_path = env::args().next().unwrap();
|
||||
let path = path::Path::new(&exe_path);
|
||||
path.canonicalize()
|
||||
}
|
||||
|
||||
pub struct Env {
|
||||
iter: vec::IntoIter<(OsString, OsString)>,
|
||||
_dont_send_or_sync_me: PhantomData<*mut ()>,
|
||||
@ -568,7 +591,8 @@ pub fn home_dir() -> Option<PathBuf> {
|
||||
target_os = "android",
|
||||
target_os = "ios",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox"
|
||||
target_os = "redox",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
unsafe fn fallback() -> Option<OsString> {
|
||||
None
|
||||
@ -577,7 +601,8 @@ pub fn home_dir() -> Option<PathBuf> {
|
||||
target_os = "android",
|
||||
target_os = "ios",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox"
|
||||
target_os = "redox",
|
||||
target_os = "vxworks"
|
||||
)))]
|
||||
unsafe fn fallback() -> Option<OsString> {
|
||||
let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
|
||||
|
@ -24,6 +24,8 @@ cfg_if::cfg_if! {
|
||||
// fuchsia doesn't have /dev/null
|
||||
} else if #[cfg(target_os = "redox")] {
|
||||
const DEV_NULL: &str = "null:\0";
|
||||
} else if #[cfg(target_os = "vxworks")] {
|
||||
const DEV_NULL: &str = "/null\0";
|
||||
} else {
|
||||
const DEV_NULL: &str = "/dev/null\0";
|
||||
}
|
||||
@ -48,7 +50,7 @@ cfg_if::cfg_if! {
|
||||
raw[bit / 8] |= 1 << (bit % 8);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
} else if #[cfg(not(target_os = "vxworks"))] {
|
||||
pub use libc::{sigemptyset, sigaddset};
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@ use crate::sys;
|
||||
use crate::sys::cvt;
|
||||
use crate::sys::process::process_common::*;
|
||||
|
||||
#[cfg(target_os = "vxworks")]
|
||||
use libc::RTP_ID as pid_t;
|
||||
|
||||
#[cfg(not(target_os = "vxworks"))]
|
||||
use libc::{c_int, gid_t, pid_t, uid_t};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -219,7 +219,7 @@ mod imp {
|
||||
target_os = "solaris",
|
||||
target_os = "illumos",
|
||||
all(target_os = "netbsd", not(target_vendor = "rumprun")),
|
||||
target_os = "openbsd"
|
||||
target_os = "openbsd",
|
||||
)))]
|
||||
mod imp {
|
||||
pub unsafe fn init() {}
|
||||
|
@ -6,10 +6,12 @@ use crate::ptr;
|
||||
use crate::sys::{os, stack_overflow};
|
||||
use crate::time::Duration;
|
||||
|
||||
#[cfg(not(target_os = "l4re"))]
|
||||
#[cfg(not(any(target_os = "l4re", target_os = "vxworks")))]
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
|
||||
#[cfg(target_os = "l4re")]
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
|
||||
#[cfg(target_os = "vxworks")]
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024;
|
||||
|
||||
pub struct Thread {
|
||||
id: libc::pthread_t,
|
||||
@ -152,10 +154,11 @@ impl Thread {
|
||||
target_os = "haiku",
|
||||
target_os = "l4re",
|
||||
target_os = "emscripten",
|
||||
target_os = "redox"
|
||||
target_os = "redox",
|
||||
target_os = "vxworks"
|
||||
))]
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// Newlib, Haiku, and Emscripten have no way to set a thread name.
|
||||
// Newlib, Haiku, Emscripten, and VxWorks have no way to set a thread name.
|
||||
}
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
pub fn set_name(_name: &CStr) {
|
||||
|
@ -1,49 +0,0 @@
|
||||
use crate::alloc::{GlobalAlloc, Layout, System};
|
||||
use crate::ptr;
|
||||
use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN};
|
||||
|
||||
#[stable(feature = "alloc_system_type", since = "1.28.0")]
|
||||
unsafe impl GlobalAlloc for System {
|
||||
#[inline]
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
|
||||
libc::malloc(layout.size()) as *mut u8
|
||||
} else {
|
||||
aligned_malloc(&layout)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
||||
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
|
||||
libc::calloc(layout.size(), 1) as *mut u8
|
||||
} else {
|
||||
let ptr = self.alloc(layout.clone());
|
||||
if !ptr.is_null() {
|
||||
ptr::write_bytes(ptr, 0, layout.size());
|
||||
}
|
||||
ptr
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||
libc::free(ptr as *mut libc::c_void)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
||||
if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
|
||||
libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
|
||||
} else {
|
||||
realloc_fallback(self, ptr, layout, new_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
|
||||
let mut out = ptr::null_mut();
|
||||
let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
|
||||
if ret != 0 { ptr::null_mut() } else { out as *mut u8 }
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
#![allow(dead_code)] // runtime init functions not used during testing
|
||||
use crate::ffi::OsString;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::vec;
|
||||
|
||||
/// One-time global initialization.
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
imp::init(argc, argv)
|
||||
}
|
||||
|
||||
/// One-time global cleanup.
|
||||
pub unsafe fn cleanup() {
|
||||
imp::cleanup()
|
||||
}
|
||||
|
||||
/// Returns the command line arguments
|
||||
pub fn args() -> Args {
|
||||
imp::args()
|
||||
}
|
||||
|
||||
pub struct Args {
|
||||
iter: vec::IntoIter<OsString>,
|
||||
_dont_send_or_sync_me: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn inner_debug(&self) -> &[OsString] {
|
||||
self.iter.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Args {
|
||||
type Item = OsString;
|
||||
fn next(&mut self) -> Option<OsString> {
|
||||
self.iter.next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for Args {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for Args {
|
||||
fn next_back(&mut self) -> Option<OsString> {
|
||||
self.iter.next_back()
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use super::Args;
|
||||
use crate::ffi::{CStr, OsString};
|
||||
use crate::marker::PhantomData;
|
||||
use crate::ptr;
|
||||
|
||||
use crate::sys_common::mutex::StaticMutex;
|
||||
|
||||
static mut ARGC: isize = 0;
|
||||
static mut ARGV: *const *const u8 = ptr::null();
|
||||
static LOCK: StaticMutex = StaticMutex::new();
|
||||
|
||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
||||
let _guard = LOCK.lock();
|
||||
ARGC = argc;
|
||||
ARGV = argv;
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {
|
||||
let _guard = LOCK.lock();
|
||||
ARGC = 0;
|
||||
ARGV = ptr::null();
|
||||
}
|
||||
|
||||
pub fn args() -> Args {
|
||||
Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData }
|
||||
}
|
||||
|
||||
fn clone() -> Vec<OsString> {
|
||||
unsafe {
|
||||
let _guard = LOCK.lock();
|
||||
let ret = (0..ARGC)
|
||||
.map(|i| {
|
||||
let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char);
|
||||
use crate::sys::vxworks::ext::ffi::OsStringExt;
|
||||
OsStringExt::from_vec(cstr.to_bytes().to_vec())
|
||||
})
|
||||
.collect();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#![cfg(not(test))]
|
||||
|
||||
use libc::{c_double, c_float};
|
||||
|
||||
extern "C" {
|
||||
pub fn acos(n: c_double) -> c_double;
|
||||
pub fn acosf(n: c_float) -> c_float;
|
||||
pub fn asin(n: c_double) -> c_double;
|
||||
pub fn asinf(n: c_float) -> c_float;
|
||||
pub fn atan(n: c_double) -> c_double;
|
||||
pub fn atan2(a: c_double, b: c_double) -> c_double;
|
||||
pub fn atan2f(a: c_float, b: c_float) -> c_float;
|
||||
pub fn atanf(n: c_float) -> c_float;
|
||||
pub fn cbrt(n: c_double) -> c_double;
|
||||
pub fn cbrtf(n: c_float) -> c_float;
|
||||
pub fn cosh(n: c_double) -> c_double;
|
||||
pub fn coshf(n: c_float) -> c_float;
|
||||
pub fn expm1(n: c_double) -> c_double;
|
||||
pub fn expm1f(n: c_float) -> c_float;
|
||||
pub fn fdim(a: c_double, b: c_double) -> c_double;
|
||||
pub fn fdimf(a: c_float, b: c_float) -> c_float;
|
||||
pub fn hypot(x: c_double, y: c_double) -> c_double;
|
||||
pub fn hypotf(x: c_float, y: c_float) -> c_float;
|
||||
pub fn log1p(n: c_double) -> c_double;
|
||||
pub fn log1pf(n: c_float) -> c_float;
|
||||
pub fn sinh(n: c_double) -> c_double;
|
||||
pub fn sinhf(n: c_float) -> c_float;
|
||||
pub fn tan(n: c_double) -> c_double;
|
||||
pub fn tanf(n: c_float) -> c_float;
|
||||
pub fn tanh(n: c_double) -> c_double;
|
||||
pub fn tanhf(n: c_float) -> c_float;
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::sys::mutex::{self, Mutex};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Condvar {
|
||||
inner: UnsafeCell<libc::pthread_cond_t>,
|
||||
}
|
||||
|
||||
pub type MovableCondvar = Box<Condvar>;
|
||||
|
||||
unsafe impl Send for Condvar {}
|
||||
unsafe impl Sync for Condvar {}
|
||||
|
||||
const TIMESPEC_MAX: libc::timespec =
|
||||
libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
|
||||
|
||||
fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
|
||||
if value > <libc::time_t>::MAX as u64 { <libc::time_t>::MAX } else { value as libc::time_t }
|
||||
}
|
||||
|
||||
impl Condvar {
|
||||
pub const fn new() -> Condvar {
|
||||
// Might be moved and address is changing it is better to avoid
|
||||
// initialization of potentially opaque OS data before it landed
|
||||
Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) }
|
||||
}
|
||||
|
||||
pub unsafe fn init(&mut self) {
|
||||
use crate::mem::MaybeUninit;
|
||||
let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
|
||||
let r = libc::pthread_condattr_init(attr.as_mut_ptr());
|
||||
assert_eq!(r, 0);
|
||||
let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
|
||||
assert_eq!(r, 0);
|
||||
let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr());
|
||||
assert_eq!(r, 0);
|
||||
let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
|
||||
assert_eq!(r, 0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn notify_one(&self) {
|
||||
let r = libc::pthread_cond_signal(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn notify_all(&self) {
|
||||
let r = libc::pthread_cond_broadcast(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn wait(&self, mutex: &Mutex) {
|
||||
let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex));
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
|
||||
// This implementation is used on systems that support pthread_condattr_setclock
|
||||
// where we configure condition variable to use monotonic clock (instead of
|
||||
// default system clock). This approach avoids all problems that result
|
||||
// from changes made to the system time.
|
||||
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
|
||||
use crate::mem;
|
||||
|
||||
let mut now: libc::timespec = mem::zeroed();
|
||||
let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
|
||||
assert_eq!(r, 0);
|
||||
|
||||
// Nanosecond calculations can't overflow because both values are below 1e9.
|
||||
let nsec = dur.subsec_nanos() + now.tv_nsec as u32;
|
||||
|
||||
let sec = saturating_cast_to_time_t(dur.as_secs())
|
||||
.checked_add((nsec / 1_000_000_000) as libc::time_t)
|
||||
.and_then(|s| s.checked_add(now.tv_sec));
|
||||
let nsec = nsec % 1_000_000_000;
|
||||
|
||||
let timeout =
|
||||
sec.map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _ }).unwrap_or(TIMESPEC_MAX);
|
||||
|
||||
let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout);
|
||||
assert!(r == libc::ETIMEDOUT || r == 0);
|
||||
r == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn destroy(&self) {
|
||||
let r = libc::pthread_cond_destroy(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
//! Unix-specific extension to the primitives in the `std::ffi` module
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use std::ffi::OsString;
|
||||
//! use std::os::unix::ffi::OsStringExt;
|
||||
//!
|
||||
//! let bytes = b"foo".to_vec();
|
||||
//!
|
||||
//! // OsStringExt::from_vec
|
||||
//! let os_string = OsString::from_vec(bytes);
|
||||
//! assert_eq!(os_string.to_str(), Some("foo"));
|
||||
//!
|
||||
//! // OsStringExt::into_vec
|
||||
//! let bytes = os_string.into_vec();
|
||||
//! assert_eq!(bytes, b"foo");
|
||||
//! ```
|
||||
//!
|
||||
//! ```
|
||||
//! use std::ffi::OsStr;
|
||||
//! use std::os::unix::ffi::OsStrExt;
|
||||
//!
|
||||
//! let bytes = b"foo";
|
||||
//!
|
||||
//! // OsStrExt::from_bytes
|
||||
//! let os_str = OsStr::from_bytes(bytes);
|
||||
//! assert_eq!(os_str.to_str(), Some("foo"));
|
||||
//!
|
||||
//! // OsStrExt::as_bytes
|
||||
//! let bytes = os_str.as_bytes();
|
||||
//! assert_eq!(bytes, b"foo");
|
||||
//! ```
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use crate::sys_common::os_str_bytes::*;
|
@ -1,817 +0,0 @@
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::fs::{self, Permissions};
|
||||
use crate::io;
|
||||
use crate::path::Path;
|
||||
use crate::sys;
|
||||
use crate::sys::platform::fs::MetadataExt as UnixMetadataExt;
|
||||
use crate::sys_common::{AsInner, AsInnerMut, FromInner};
|
||||
|
||||
/// Unix-specific extensions to [`fs::File`].
|
||||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
pub trait FileExt {
|
||||
/// Reads a number of bytes starting from a given offset.
|
||||
///
|
||||
/// Returns the number of bytes read.
|
||||
///
|
||||
/// The offset is relative to the start of the file and thus independent
|
||||
/// from the current cursor.
|
||||
///
|
||||
/// The current file cursor is not affected by this function.
|
||||
///
|
||||
/// Note that similar to [`File::read`], it is not an error to return with a
|
||||
/// short read.
|
||||
///
|
||||
/// [`File::read`]: fs::File::read
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::fs::File;
|
||||
/// use std::os::unix::prelude::FileExt;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut buf = [0u8; 8];
|
||||
/// let file = File::open("foo.txt")?;
|
||||
///
|
||||
/// // We now read 8 bytes from the offset 10.
|
||||
/// let num_bytes_read = file.read_at(&mut buf, 10)?;
|
||||
/// println!("read {} bytes: {:?}", num_bytes_read, buf);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
|
||||
|
||||
/// Reads the exact number of byte required to fill `buf` from the given offset.
|
||||
///
|
||||
/// The offset is relative to the start of the file and thus independent
|
||||
/// from the current cursor.
|
||||
///
|
||||
/// The current file cursor is not affected by this function.
|
||||
///
|
||||
/// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
|
||||
///
|
||||
/// [`Read::read_exact`]: io::Read::read_exact
|
||||
/// [`read_at`]: FileExt::read_at
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If this function encounters an error of the kind
|
||||
/// [`ErrorKind::Interrupted`] then the error is ignored and the operation
|
||||
/// will continue.
|
||||
///
|
||||
/// If this function encounters an "end of file" before completely filling
|
||||
/// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
|
||||
/// The contents of `buf` are unspecified in this case.
|
||||
///
|
||||
/// If any other read error is encountered then this function immediately
|
||||
/// returns. The contents of `buf` are unspecified in this case.
|
||||
///
|
||||
/// If this function returns an error, it is unspecified how many bytes it
|
||||
/// has read, but it will never read more than would be necessary to
|
||||
/// completely fill the buffer.
|
||||
///
|
||||
/// [`ErrorKind::Interrupted`]: io::ErrorKind::Interrupted
|
||||
/// [`ErrorKind::UnexpectedEof`]: io::ErrorKind::UnexpectedEof
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(rw_exact_all_at)]
|
||||
/// use std::io;
|
||||
/// use std::fs::File;
|
||||
/// use std::os::unix::prelude::FileExt;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut buf = [0u8; 8];
|
||||
/// let file = File::open("foo.txt")?;
|
||||
///
|
||||
/// // We now read exactly 8 bytes from the offset 10.
|
||||
/// file.read_exact_at(&mut buf, 10)?;
|
||||
/// println!("read {} bytes: {:?}", buf.len(), buf);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
|
||||
fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match self.read_at(buf, offset) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
let tmp = buf;
|
||||
buf = &mut tmp[n..];
|
||||
offset += n as u64;
|
||||
}
|
||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
if !buf.is_empty() {
|
||||
Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a number of bytes starting from a given offset.
|
||||
///
|
||||
/// Returns the number of bytes written.
|
||||
///
|
||||
/// The offset is relative to the start of the file and thus independent
|
||||
/// from the current cursor.
|
||||
///
|
||||
/// The current file cursor is not affected by this function.
|
||||
///
|
||||
/// When writing beyond the end of the file, the file is appropriately
|
||||
/// extended and the intermediate bytes are initialized with the value 0.
|
||||
///
|
||||
/// Note that similar to [`File::write`], it is not an error to return a
|
||||
/// short write.
|
||||
///
|
||||
/// [`File::write`]: fs::File::write
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io;
|
||||
/// use std::os::unix::prelude::FileExt;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let file = File::open("foo.txt")?;
|
||||
///
|
||||
/// // We now write at the offset 10.
|
||||
/// file.write_at(b"sushi", 10)?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
|
||||
|
||||
/// Attempts to write an entire buffer starting from a given offset.
|
||||
///
|
||||
/// The offset is relative to the start of the file and thus independent
|
||||
/// from the current cursor.
|
||||
///
|
||||
/// The current file cursor is not affected by this function.
|
||||
///
|
||||
/// This method will continuously call [`write_at`] until there is no more data
|
||||
/// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
|
||||
/// returned. This method will not return until the entire buffer has been
|
||||
/// successfully written or such an error occurs. The first error that is
|
||||
/// not of [`ErrorKind::Interrupted`] kind generated from this method will be
|
||||
/// returned.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return the first error of
|
||||
/// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
|
||||
///
|
||||
/// [`ErrorKind::Interrupted`]: io::ErrorKind::Interrupted
|
||||
/// [`write_at`]: FileExt::write_at
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(rw_exact_all_at)]
|
||||
/// use std::fs::File;
|
||||
/// use std::io;
|
||||
/// use std::os::unix::prelude::FileExt;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let file = File::open("foo.txt")?;
|
||||
///
|
||||
/// // We now write at the offset 10.
|
||||
/// file.write_all_at(b"sushi", 10)?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
|
||||
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match self.write_at(buf, offset) {
|
||||
Ok(0) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
"failed to write whole buffer",
|
||||
));
|
||||
}
|
||||
Ok(n) => {
|
||||
buf = &buf[n..];
|
||||
offset += n as u64
|
||||
}
|
||||
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
impl FileExt for fs::File {
|
||||
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().read_at(buf, offset)
|
||||
}
|
||||
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().write_at(buf, offset)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extensions to [`fs::Permissions`].
|
||||
#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
pub trait PermissionsExt {
|
||||
/// Returns the underlying raw `st_mode` bits that contain the standard
|
||||
/// Unix permissions for this file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::os::unix::fs::PermissionsExt;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::create("foo.txt")?;
|
||||
/// let metadata = f.metadata()?;
|
||||
/// let permissions = metadata.permissions();
|
||||
///
|
||||
/// println!("permissions: {}", permissions.mode());
|
||||
/// Ok(()) }
|
||||
/// ```
|
||||
#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
fn mode(&self) -> u32;
|
||||
|
||||
/// Sets the underlying raw bits for this set of permissions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::os::unix::fs::PermissionsExt;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// let f = File::create("foo.txt")?;
|
||||
/// let metadata = f.metadata()?;
|
||||
/// let mut permissions = metadata.permissions();
|
||||
///
|
||||
/// permissions.set_mode(0o644); // Read/write for owner and read for others.
|
||||
/// assert_eq!(permissions.mode(), 0o644);
|
||||
/// Ok(()) }
|
||||
/// ```
|
||||
#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
fn set_mode(&mut self, mode: u32);
|
||||
|
||||
/// Creates a new instance of `Permissions` from the given set of Unix
|
||||
/// permission bits.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::fs::Permissions;
|
||||
/// use std::os::unix::fs::PermissionsExt;
|
||||
///
|
||||
/// // Read/write for owner and read for others.
|
||||
/// let permissions = Permissions::from_mode(0o644);
|
||||
/// assert_eq!(permissions.mode(), 0o644);
|
||||
/// ```
|
||||
#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
fn from_mode(mode: u32) -> Self;
|
||||
}
|
||||
|
||||
#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
impl PermissionsExt for Permissions {
|
||||
fn mode(&self) -> u32 {
|
||||
self.as_inner().mode()
|
||||
}
|
||||
|
||||
fn set_mode(&mut self, mode: u32) {
|
||||
*self = Permissions::from_inner(FromInner::from_inner(mode));
|
||||
}
|
||||
|
||||
fn from_mode(mode: u32) -> Permissions {
|
||||
Permissions::from_inner(FromInner::from_inner(mode))
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extensions to [`fs::OpenOptions`].
|
||||
#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
pub trait OpenOptionsExt {
|
||||
/// Sets the mode bits that a new file will be created with.
|
||||
///
|
||||
/// If a new file is created as part of an `OpenOptions::open` call then this
|
||||
/// specified `mode` will be used as the permission bits for the new file.
|
||||
/// If no `mode` is set, the default of `0o666` will be used.
|
||||
/// The operating system masks out bits with the system's `umask`, to produce
|
||||
/// the final permissions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::OpenOptions;
|
||||
/// use std::os::unix::fs::OpenOptionsExt;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let mut options = OpenOptions::new();
|
||||
/// options.mode(0o644); // Give read/write for owner and read for others.
|
||||
/// let file = options.open("foo.txt");
|
||||
/// # }
|
||||
/// ```
|
||||
#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
fn mode(&mut self, mode: u32) -> &mut Self;
|
||||
|
||||
/// Pass custom flags to the `flags` argument of `open`.
|
||||
///
|
||||
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
|
||||
/// ensure they do not interfere with the access mode set by Rusts options.
|
||||
///
|
||||
/// Custom flags can only set flags, not remove flags set by Rusts options.
|
||||
/// This options overwrites any previously set custom flags.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #![feature(libc)]
|
||||
/// extern crate libc;
|
||||
/// use std::fs::OpenOptions;
|
||||
/// use std::os::unix::fs::OpenOptionsExt;
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let mut options = OpenOptions::new();
|
||||
/// options.write(true);
|
||||
/// if cfg!(unix) {
|
||||
/// options.custom_flags(libc::O_NOFOLLOW);
|
||||
/// }
|
||||
/// let file = options.open("foo.txt");
|
||||
/// # }
|
||||
/// ```
|
||||
#[stable(feature = "open_options_ext", since = "1.10.0")]
|
||||
fn custom_flags(&mut self, flags: i32) -> &mut Self;
|
||||
}
|
||||
|
||||
/*#[stable(feature = "fs_ext", since = "1.1.0")]
|
||||
impl OpenOptionsExt for OpenOptions {
|
||||
fn mode(&mut self, mode: u32) -> &mut OpenOptions {
|
||||
self.as_inner_mut().mode(mode); self
|
||||
}
|
||||
|
||||
fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
|
||||
self.as_inner_mut().custom_flags(flags); self
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// Unix-specific extensions to [`fs::Metadata`].
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
pub trait MetadataExt {
|
||||
/// Returns the ID of the device containing the file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let dev_id = meta.dev();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn dev(&self) -> u64;
|
||||
/// Returns the inode number.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let inode = meta.ino();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn ino(&self) -> u64;
|
||||
/// Returns the rights applied to this file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let mode = meta.mode();
|
||||
/// let user_has_write_access = mode & 0o200;
|
||||
/// let user_has_read_write_access = mode & 0o600;
|
||||
/// let group_has_read_access = mode & 0o040;
|
||||
/// let others_have_exec_access = mode & 0o001;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn mode(&self) -> u32;
|
||||
/// Returns the number of hard links pointing to this file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let nb_hard_links = meta.nlink();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn nlink(&self) -> u64;
|
||||
/// Returns the user ID of the owner of this file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let user_id = meta.uid();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn uid(&self) -> u32;
|
||||
/// Returns the group ID of the owner of this file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let group_id = meta.gid();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn gid(&self) -> u32;
|
||||
/// Returns the device ID of this file (if it is a special one).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let device_id = meta.rdev();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn rdev(&self) -> u64;
|
||||
/// Returns the total size of this file in bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let file_size = meta.size();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn size(&self) -> u64;
|
||||
/// Returns the time of the last access to the file.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let last_access_time = meta.atime();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn atime(&self) -> i64;
|
||||
/// Returns the time of the last access to the file in nanoseconds.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let nano_last_access_time = meta.atime_nsec();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn mtime(&self) -> i64;
|
||||
/// Returns the time of the last modification of the file in nanoseconds.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let nano_last_modification_time = meta.mtime_nsec();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn ctime(&self) -> i64;
|
||||
/// Returns the time of the last status change of the file in nanoseconds.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let nano_last_status_change_time = meta.ctime_nsec();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn blksize(&self) -> u64;
|
||||
/// Returns the number of blocks allocated to the file, in 512-byte units.
|
||||
///
|
||||
/// Please note that this may be smaller than `st_size / 512` when the file has holes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::MetadataExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("some_file")?;
|
||||
/// let blocks = meta.blocks();
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn blocks(&self) -> u64;
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
fn attrib(&self) -> u8;
|
||||
}
|
||||
|
||||
#[stable(feature = "metadata_ext", since = "1.1.0")]
|
||||
impl MetadataExt for fs::Metadata {
|
||||
fn dev(&self) -> u64 {
|
||||
self.st_dev()
|
||||
}
|
||||
fn ino(&self) -> u64 {
|
||||
self.st_ino()
|
||||
}
|
||||
fn mode(&self) -> u32 {
|
||||
self.st_mode()
|
||||
}
|
||||
fn nlink(&self) -> u64 {
|
||||
self.st_nlink()
|
||||
}
|
||||
fn uid(&self) -> u32 {
|
||||
self.st_uid()
|
||||
}
|
||||
fn gid(&self) -> u32 {
|
||||
self.st_gid()
|
||||
}
|
||||
fn rdev(&self) -> u64 {
|
||||
self.st_rdev()
|
||||
}
|
||||
fn size(&self) -> u64 {
|
||||
self.st_size()
|
||||
}
|
||||
fn atime(&self) -> i64 {
|
||||
self.st_atime()
|
||||
}
|
||||
fn mtime(&self) -> i64 {
|
||||
self.st_mtime()
|
||||
}
|
||||
fn ctime(&self) -> i64 {
|
||||
self.st_ctime()
|
||||
}
|
||||
fn blksize(&self) -> u64 {
|
||||
self.st_blksize()
|
||||
}
|
||||
fn blocks(&self) -> u64 {
|
||||
self.st_blocks()
|
||||
}
|
||||
fn attrib(&self) -> u8 {
|
||||
self.st_attrib()
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extensions for [`fs::FileType`].
|
||||
///
|
||||
/// Adds support for special Unix file types such as block/character devices,
|
||||
/// pipes, and sockets.
|
||||
#[stable(feature = "file_type_ext", since = "1.5.0")]
|
||||
pub trait FileTypeExt {
|
||||
/// Returns whether this file type is a block device.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::FileTypeExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("block_device_file")?;
|
||||
/// let file_type = meta.file_type();
|
||||
/// assert!(file_type.is_block_device());
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "file_type_ext", since = "1.5.0")]
|
||||
fn is_block_device(&self) -> bool;
|
||||
/// Returns whether this file type is a char device.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::FileTypeExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("char_device_file")?;
|
||||
/// let file_type = meta.file_type();
|
||||
/// assert!(file_type.is_char_device());
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "file_type_ext", since = "1.5.0")]
|
||||
fn is_char_device(&self) -> bool;
|
||||
/// Returns whether this file type is a fifo.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::FileTypeExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("fifo_file")?;
|
||||
/// let file_type = meta.file_type();
|
||||
/// assert!(file_type.is_fifo());
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "file_type_ext", since = "1.5.0")]
|
||||
fn is_fifo(&self) -> bool;
|
||||
/// Returns whether this file type is a socket.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::FileTypeExt;
|
||||
/// use std::io;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let meta = fs::metadata("unix.socket")?;
|
||||
/// let file_type = meta.file_type();
|
||||
/// assert!(file_type.is_socket());
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "file_type_ext", since = "1.5.0")]
|
||||
fn is_socket(&self) -> bool;
|
||||
}
|
||||
|
||||
#[stable(feature = "file_type_ext", since = "1.5.0")]
|
||||
impl FileTypeExt for fs::FileType {
|
||||
fn is_block_device(&self) -> bool {
|
||||
self.as_inner().is(libc::S_IFBLK)
|
||||
}
|
||||
fn is_char_device(&self) -> bool {
|
||||
self.as_inner().is(libc::S_IFCHR)
|
||||
}
|
||||
fn is_fifo(&self) -> bool {
|
||||
self.as_inner().is(libc::S_IFIFO)
|
||||
}
|
||||
fn is_socket(&self) -> bool {
|
||||
self.as_inner().is(libc::S_IFSOCK)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extension methods for [`fs::DirEntry`].
|
||||
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
|
||||
pub trait DirEntryExt {
|
||||
/// Returns the underlying `d_ino` field in the contained `dirent`
|
||||
/// structure.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::fs;
|
||||
/// use std::os::unix::fs::DirEntryExt;
|
||||
///
|
||||
/// if let Ok(entries) = fs::read_dir(".") {
|
||||
/// for entry in entries {
|
||||
/// if let Ok(entry) = entry {
|
||||
/// // Here, `entry` is a `DirEntry`.
|
||||
/// println!("{:?}: {}", entry.file_name(), entry.ino());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
|
||||
fn ino(&self) -> u64;
|
||||
}
|
||||
|
||||
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
|
||||
impl DirEntryExt for fs::DirEntry {
|
||||
fn ino(&self) -> u64 {
|
||||
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.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::os::unix::fs;
|
||||
///
|
||||
/// fn main() -> std::io::Result<()> {
|
||||
/// fs::symlink("a.txt", "b.txt")?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "symlink", since = "1.1.0")]
|
||||
pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
|
||||
sys::fs::symlink(src.as_ref(), dst.as_ref())
|
||||
}
|
||||
|
||||
/// Unix-specific extensions to [`fs::DirBuilder`].
|
||||
#[stable(feature = "dir_builder", since = "1.6.0")]
|
||||
pub trait DirBuilderExt {
|
||||
/// Sets the mode to create new directories with. This option defaults to
|
||||
/// 0o777.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::DirBuilder;
|
||||
/// use std::os::unix::fs::DirBuilderExt;
|
||||
///
|
||||
/// let mut builder = DirBuilder::new();
|
||||
/// builder.mode(0o755);
|
||||
/// ```
|
||||
#[stable(feature = "dir_builder", since = "1.6.0")]
|
||||
fn mode(&mut self, mode: u32) -> &mut Self;
|
||||
}
|
||||
|
||||
#[stable(feature = "dir_builder", since = "1.6.0")]
|
||||
impl DirBuilderExt for fs::DirBuilder {
|
||||
fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder {
|
||||
self.as_inner_mut().set_mode(mode);
|
||||
self
|
||||
}
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
//! Unix-specific extensions to general I/O primitives
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::fs;
|
||||
use crate::io;
|
||||
use crate::net;
|
||||
use crate::os::raw;
|
||||
use crate::sys;
|
||||
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
|
||||
|
||||
/// 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 guaranteed 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.
|
||||
#[stable(feature = "from_raw_os", since = "1.1.0")]
|
||||
pub trait FromRawFd {
|
||||
/// Constructs a new instance 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.
|
||||
#[stable(feature = "from_raw_os", since = "1.1.0")]
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self;
|
||||
}
|
||||
|
||||
/// A trait to express the ability to consume an object and acquire ownership of
|
||||
/// its raw file descriptor.
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
pub trait IntoRawFd {
|
||||
/// Consumes this object, returning the raw underlying file descriptor.
|
||||
///
|
||||
/// This function **transfers ownership** of the underlying file descriptor
|
||||
/// to the caller. Callers are then the unique owners of the file descriptor
|
||||
/// and must close the descriptor once it's no longer needed.
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
fn into_raw_fd(self) -> RawFd;
|
||||
}
|
||||
|
||||
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
|
||||
impl AsRawFd for RawFd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
*self
|
||||
}
|
||||
}
|
||||
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
|
||||
impl IntoRawFd for RawFd {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self
|
||||
}
|
||||
}
|
||||
#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
|
||||
impl FromRawFd for RawFd {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl AsRawFd for fs::File {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.as_inner().fd().raw()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "from_raw_os", since = "1.1.0")]
|
||||
impl FromRawFd for fs::File {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
|
||||
fs::File::from_inner(sys::fs::File::from_inner(fd))
|
||||
}
|
||||
}
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
impl IntoRawFd for fs::File {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.into_inner().into_fd().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "asraw_stdio", since = "1.21.0")]
|
||||
impl AsRawFd for io::Stdin {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
libc::STDIN_FILENO
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "asraw_stdio", since = "1.21.0")]
|
||||
impl AsRawFd for io::Stdout {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
libc::STDOUT_FILENO
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "asraw_stdio", since = "1.21.0")]
|
||||
impl AsRawFd for io::Stderr {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
libc::STDERR_FILENO
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
|
||||
impl<'a> AsRawFd for io::StdinLock<'a> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
libc::STDIN_FILENO
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
|
||||
impl<'a> AsRawFd for io::StdoutLock<'a> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
libc::STDOUT_FILENO
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
|
||||
impl<'a> AsRawFd for io::StderrLock<'a> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
libc::STDERR_FILENO
|
||||
}
|
||||
}
|
||||
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "from_raw_os", since = "1.1.0")]
|
||||
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(sys_common::net::TcpStream::from_inner(socket))
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "from_raw_os", since = "1.1.0")]
|
||||
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(sys_common::net::TcpListener::from_inner(socket))
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "from_raw_os", since = "1.1.0")]
|
||||
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(sys_common::net::UdpSocket::from_inner(socket))
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
impl IntoRawFd for net::TcpStream {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.into_inner().into_socket().into_inner()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
impl IntoRawFd for net::TcpListener {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.into_inner().into_socket().into_inner()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
impl IntoRawFd for net::UdpSocket {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.into_inner().into_socket().into_inner()
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
pub mod ffi;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod process;
|
||||
pub mod raw;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub mod prelude {
|
||||
#[doc(no_inline)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::ffi::{OsStrExt, OsStringExt};
|
||||
#[doc(no_inline)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::fs::{FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt};
|
||||
#[doc(no_inline)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
#[doc(no_inline)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use super::process::ExitStatusExt;
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
//! Unix-specific extensions to primitives in the `std::process` module.
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::ffi::OsStr;
|
||||
use crate::io;
|
||||
use crate::process;
|
||||
use crate::sys;
|
||||
use crate::sys::vxworks::ext::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
|
||||
|
||||
/// Unix-specific extensions to the [`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: u16) -> &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: u16) -> &mut process::Command;
|
||||
|
||||
/// Schedules a closure to be run just before the `exec` function is
|
||||
/// invoked.
|
||||
///
|
||||
/// The closure is allowed to return an I/O error whose OS error code will
|
||||
/// be communicated back to the parent and returned as an error from when
|
||||
/// the spawn was requested.
|
||||
///
|
||||
/// Multiple closures can be registered and they will be called in order of
|
||||
/// their registration. If a closure returns `Err` then no further closures
|
||||
/// will be called and the spawn operation will immediately return with a
|
||||
/// failure.
|
||||
///
|
||||
/// # Notes and Safety
|
||||
///
|
||||
/// This closure will be run in the context of the child process after a
|
||||
/// `fork`. This primarily means that any modifications made to memory on
|
||||
/// behalf of this closure will **not** be visible to the parent process.
|
||||
/// This is often a very constrained environment where normal operations
|
||||
/// like `malloc` or acquiring a mutex are not guaranteed to work (due to
|
||||
/// other threads perhaps still running when the `fork` was run).
|
||||
///
|
||||
/// This also means that all resources such as file descriptors and
|
||||
/// memory-mapped regions got duplicated. It is your responsibility to make
|
||||
/// sure that the closure does not violate library invariants by making
|
||||
/// invalid use of these duplicates.
|
||||
///
|
||||
/// When this closure is run, aspects such as the stdio file descriptors and
|
||||
/// working directory have successfully been changed, so output to these
|
||||
/// locations may not appear where intended.
|
||||
#[stable(feature = "process_pre_exec", since = "1.34.0")]
|
||||
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
|
||||
where
|
||||
F: FnMut() -> io::Result<()> + Send + Sync + 'static;
|
||||
|
||||
/// Schedules a closure to be run just before the `exec` function is
|
||||
/// invoked.
|
||||
///
|
||||
/// This method is stable and usable, but it should be unsafe. To fix
|
||||
/// that, it got deprecated in favor of the unsafe [`pre_exec`].
|
||||
///
|
||||
/// [`pre_exec`]: CommandExt::pre_exec
|
||||
#[stable(feature = "process_exec", since = "1.15.0")]
|
||||
#[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")]
|
||||
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
|
||||
where
|
||||
F: FnMut() -> io::Result<()> + Send + Sync + 'static,
|
||||
{
|
||||
unsafe { self.pre_exec(f) }
|
||||
}
|
||||
|
||||
/// Performs all the required setup by this `Command`, followed by calling
|
||||
/// the `execvp` syscall.
|
||||
///
|
||||
/// On success this function will not return, and otherwise it will return
|
||||
/// an error indicating why the exec (or another part of the setup of the
|
||||
/// `Command`) failed.
|
||||
///
|
||||
/// `exec` not returning has the same implications as calling
|
||||
/// [`process::exit`] – no destructors on the current stack or any other
|
||||
/// thread’s stack will be run. Therefore, it is recommended to only call
|
||||
/// `exec` at a point where it is fine to not run any destructors. Note,
|
||||
/// that the `execvp` syscall independently guarantees that all memory is
|
||||
/// freed and all file descriptors with the `CLOEXEC` option (set by default
|
||||
/// on all file descriptors opened by the standard library) are closed.
|
||||
///
|
||||
/// This function, unlike `spawn`, will **not** `fork` the process to create
|
||||
/// a new child. Like spawn, however, the default behavior for the stdio
|
||||
/// descriptors will be to inherited from the current process.
|
||||
///
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// The process may be in a "broken state" if this function returns in
|
||||
/// error. For example the working directory, environment variables, signal
|
||||
/// handling settings, various user/group information, or aspects of stdio
|
||||
/// file descriptors may have changed. If a "transactional spawn" is
|
||||
/// required to gracefully handle errors it is recommended to use the
|
||||
/// cross-platform `spawn` instead.
|
||||
#[stable(feature = "process_exec2", since = "1.9.0")]
|
||||
fn exec(&mut self) -> io::Error;
|
||||
|
||||
/// Set executable argument
|
||||
///
|
||||
/// Set the first process argument, `argv[0]`, to something other than the
|
||||
/// default executable path.
|
||||
#[stable(feature = "process_set_argv0", since = "1.45.0")]
|
||||
fn arg0<S>(&mut self, arg: S) -> &mut process::Command
|
||||
where
|
||||
S: AsRef<OsStr>;
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl CommandExt for process::Command {
|
||||
fn uid(&mut self, id: u16) -> &mut process::Command {
|
||||
self.as_inner_mut().uid(id);
|
||||
self
|
||||
}
|
||||
|
||||
fn gid(&mut self, id: u16) -> &mut process::Command {
|
||||
self.as_inner_mut().gid(id);
|
||||
self
|
||||
}
|
||||
|
||||
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
|
||||
where
|
||||
F: FnMut() -> io::Result<()> + Send + Sync + 'static,
|
||||
{
|
||||
self.as_inner_mut().pre_exec(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
fn exec(&mut self) -> io::Error {
|
||||
self.as_inner_mut().exec(sys::process::Stdio::Inherit)
|
||||
}
|
||||
|
||||
fn arg0<S>(&mut self, arg: S) -> &mut process::Command
|
||||
where
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
self.as_inner_mut().set_arg_0(arg.as_ref());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extensions to [`process::ExitStatus`].
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait ExitStatusExt {
|
||||
/// Creates a new `ExitStatus` from the raw underlying `i32` return value of
|
||||
/// a process.
|
||||
#[stable(feature = "exit_status_from", since = "1.12.0")]
|
||||
fn from_raw(raw: i32) -> Self;
|
||||
|
||||
/// 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 from_raw(raw: i32) -> Self {
|
||||
process::ExitStatus::from_inner(From::from(raw))
|
||||
}
|
||||
|
||||
fn signal(&self) -> Option<i32> {
|
||||
self.as_inner().signal()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "process_extensions", since = "1.2.0")]
|
||||
impl FromRawFd for process::Stdio {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
|
||||
let fd = sys::fd::FileDesc::new(fd);
|
||||
let io = sys::process::Stdio::Fd(fd);
|
||||
process::Stdio::from_inner(io)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "process_extensions", since = "1.2.0")]
|
||||
impl AsRawFd for process::ChildStdin {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.as_inner().fd().raw()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "process_extensions", since = "1.2.0")]
|
||||
impl AsRawFd for process::ChildStdout {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.as_inner().fd().raw()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "process_extensions", since = "1.2.0")]
|
||||
impl AsRawFd for process::ChildStderr {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.as_inner().fd().raw()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
impl IntoRawFd for process::ChildStdin {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.into_inner().into_fd().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
impl IntoRawFd for process::ChildStdout {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.into_inner().into_fd().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
impl IntoRawFd for process::ChildStderr {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.into_inner().into_fd().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the OS-assigned process identifier associated with this process's parent.
|
||||
#[stable(feature = "unix_ppid", since = "1.27.0")]
|
||||
pub fn parent_id() -> u32 {
|
||||
crate::sys::os::getppid()
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#![stable(feature = "raw_ext", since = "1.1.0")]
|
||||
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "pthread_t", since = "1.8.0")]
|
||||
pub use crate::sys::platform::raw::pthread_t;
|
@ -1,201 +0,0 @@
|
||||
#![unstable(reason = "not public", issue = "none", feature = "fd")]
|
||||
|
||||
use crate::cmp;
|
||||
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
|
||||
use crate::mem;
|
||||
use crate::sys::cvt;
|
||||
use crate::sys_common::AsInner;
|
||||
|
||||
use libc::{self, c_int, c_void, ssize_t};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileDesc {
|
||||
fd: c_int,
|
||||
}
|
||||
|
||||
// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
|
||||
// with the man page quoting that if the count of bytes to read is
|
||||
// greater than `SSIZE_MAX` the result is "unspecified".
|
||||
const READ_LIMIT: usize = ssize_t::MAX as usize;
|
||||
|
||||
impl FileDesc {
|
||||
pub fn new(fd: c_int) -> FileDesc {
|
||||
FileDesc { fd: fd }
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> c_int {
|
||||
self.fd
|
||||
}
|
||||
|
||||
/// Extracts the actual file descriptor without closing it.
|
||||
pub fn into_raw(self) -> c_int {
|
||||
let fd = self.fd;
|
||||
mem::forget(self);
|
||||
fd
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::readv(
|
||||
self.fd,
|
||||
bufs.as_ptr() as *const libc::iovec,
|
||||
cmp::min(bufs.len(), c_int::MAX as usize) as c_int,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let mut me = self;
|
||||
(&mut me).read_to_end(buf)
|
||||
}
|
||||
|
||||
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
unsafe fn cvt_pread(
|
||||
fd: c_int,
|
||||
buf: *mut c_void,
|
||||
count: usize,
|
||||
offset: i64,
|
||||
) -> io::Result<isize> {
|
||||
use libc::pread;
|
||||
cvt(pread(fd, buf, count, offset))
|
||||
}
|
||||
|
||||
unsafe {
|
||||
cvt_pread(
|
||||
self.fd,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
cmp::min(buf.len(), READ_LIMIT),
|
||||
offset as i64,
|
||||
)
|
||||
.map(|n| n as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::writev(
|
||||
self.fd,
|
||||
bufs.as_ptr() as *const libc::iovec,
|
||||
cmp::min(bufs.len(), c_int::MAX as usize) as c_int,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
unsafe fn cvt_pwrite(
|
||||
fd: c_int,
|
||||
buf: *const c_void,
|
||||
count: usize,
|
||||
offset: i64,
|
||||
) -> io::Result<isize> {
|
||||
use libc::pwrite;
|
||||
cvt(pwrite(fd, buf, count, offset))
|
||||
}
|
||||
|
||||
unsafe {
|
||||
cvt_pwrite(
|
||||
self.fd,
|
||||
buf.as_ptr() as *const c_void,
|
||||
cmp::min(buf.len(), READ_LIMIT),
|
||||
offset as i64,
|
||||
)
|
||||
.map(|n| n as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cloexec(&self) -> io::Result<bool> {
|
||||
unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
|
||||
}
|
||||
|
||||
pub fn set_cloexec(&self) -> io::Result<()> {
|
||||
unsafe {
|
||||
let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
|
||||
let new = previous | libc::FD_CLOEXEC;
|
||||
if new != previous {
|
||||
cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||
unsafe {
|
||||
let v = nonblocking as c_int;
|
||||
cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// refer to pxPipeDrv library documentation.
|
||||
// VxWorks uses fcntl to set O_NONBLOCK to the pipes
|
||||
pub fn set_nonblocking_pipe(&self, nonblocking: bool) -> io::Result<()> {
|
||||
unsafe {
|
||||
let mut flags = cvt(libc::fcntl(self.fd, libc::F_GETFL, 0))?;
|
||||
flags = if nonblocking { flags | libc::O_NONBLOCK } else { flags & !libc::O_NONBLOCK };
|
||||
cvt(libc::fcntl(self.fd, libc::F_SETFL, flags))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<FileDesc> {
|
||||
let fd = self.raw();
|
||||
match cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }) {
|
||||
Ok(newfd) => Ok(FileDesc::new(newfd)),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Read for &'a FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
(**self).read(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn initializer(&self) -> Initializer {
|
||||
Initializer::nop()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsInner<c_int> for FileDesc {
|
||||
fn as_inner(&self) -> &c_int {
|
||||
&self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileDesc {
|
||||
fn drop(&mut self) {
|
||||
// Note that errors are ignored when closing a file descriptor. The
|
||||
// reason for this is that if an error occurs we don't actually know if
|
||||
// the file descriptor was closed or not, and if we retried (for
|
||||
// something like EINTR), we might close another valid file descriptor
|
||||
// (opened after we closed ours.
|
||||
let _ = unsafe { libc::close(self.fd) };
|
||||
}
|
||||
}
|
@ -1,624 +0,0 @@
|
||||
// copies from linuxx
|
||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
||||
use crate::fmt;
|
||||
use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
|
||||
use crate::mem;
|
||||
use crate::path::{Path, PathBuf};
|
||||
use crate::ptr;
|
||||
use crate::sync::Arc;
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys::time::SystemTime;
|
||||
use crate::sys::vxworks::ext::ffi::OsStrExt;
|
||||
use crate::sys::vxworks::ext::ffi::OsStringExt;
|
||||
use crate::sys::{cvt, cvt_r};
|
||||
use crate::sys_common::{AsInner, FromInner};
|
||||
use libc::{self, c_int, mode_t, off_t, stat64};
|
||||
use libc::{dirent, ftruncate, lseek, open, readdir_r as readdir64_r};
|
||||
pub struct File(FileDesc);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FileAttr {
|
||||
stat: stat64,
|
||||
}
|
||||
|
||||
// all DirEntry's will have a reference to this struct
|
||||
struct InnerReadDir {
|
||||
dirp: Dir,
|
||||
root: PathBuf,
|
||||
}
|
||||
|
||||
pub struct ReadDir {
|
||||
inner: Arc<InnerReadDir>,
|
||||
end_of_stream: bool,
|
||||
}
|
||||
|
||||
struct Dir(*mut libc::DIR);
|
||||
|
||||
unsafe impl Send for Dir {}
|
||||
unsafe impl Sync for Dir {}
|
||||
|
||||
pub struct DirEntry {
|
||||
entry: dirent,
|
||||
dir: Arc<InnerReadDir>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpenOptions {
|
||||
// generic
|
||||
read: bool,
|
||||
write: bool,
|
||||
append: bool,
|
||||
truncate: bool,
|
||||
create: bool,
|
||||
create_new: bool,
|
||||
// system-specific
|
||||
custom_flags: i32,
|
||||
mode: mode_t,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct FilePermissions {
|
||||
mode: mode_t,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct FileType {
|
||||
mode: mode_t,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DirBuilder {
|
||||
mode: mode_t,
|
||||
}
|
||||
|
||||
impl FileAttr {
|
||||
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) }
|
||||
}
|
||||
|
||||
pub fn file_type(&self) -> FileType {
|
||||
FileType { mode: self.stat.st_mode as mode_t }
|
||||
}
|
||||
|
||||
pub fn modified(&self) -> io::Result<SystemTime> {
|
||||
Ok(SystemTime::from(libc::timespec {
|
||||
tv_sec: self.stat.st_mtime as libc::time_t,
|
||||
tv_nsec: 0, // hack 2.0;
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn accessed(&self) -> io::Result<SystemTime> {
|
||||
Ok(SystemTime::from(libc::timespec {
|
||||
tv_sec: self.stat.st_atime as libc::time_t,
|
||||
tv_nsec: 0, // hack - a proper fix would be better
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn created(&self) -> io::Result<SystemTime> {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"creation time is not available on this platform currently",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsInner<stat64> for FileAttr {
|
||||
fn as_inner(&self) -> &stat64 {
|
||||
&self.stat
|
||||
}
|
||||
}
|
||||
|
||||
impl FilePermissions {
|
||||
pub fn readonly(&self) -> bool {
|
||||
// check if any class (owner, group, others) has write permission
|
||||
self.mode & 0o222 == 0
|
||||
}
|
||||
|
||||
pub fn set_readonly(&mut self, readonly: bool) {
|
||||
if readonly {
|
||||
// remove write permission for all classes; equivalent to `chmod a-w <file>`
|
||||
self.mode &= !0o222;
|
||||
} else {
|
||||
// add write permission for all classes; equivalent to `chmod a+w <file>`
|
||||
self.mode |= 0o222;
|
||||
}
|
||||
}
|
||||
pub fn mode(&self) -> u32 {
|
||||
self.mode as u32
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn is(&self, mode: mode_t) -> bool {
|
||||
self.mode & libc::S_IFMT == mode
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<u32> for FilePermissions {
|
||||
fn from_inner(mode: u32) -> FilePermissions {
|
||||
FilePermissions { mode: mode as mode_t }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ReadDir {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
|
||||
// Thus the result will be e g 'ReadDir("/home")'
|
||||
fmt::Debug::fmt(&*self.inner.root, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ReadDir {
|
||||
type Item = io::Result<DirEntry>;
|
||||
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
||||
if self.end_of_stream {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
|
||||
let mut entry_ptr = ptr::null_mut();
|
||||
loop {
|
||||
if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
|
||||
if entry_ptr.is_null() {
|
||||
// We encountered an error (which will be returned in this iteration), but
|
||||
// we also reached the end of the directory stream. The `end_of_stream`
|
||||
// flag is enabled to make sure that we return `None` in the next iteration
|
||||
// (instead of looping forever)
|
||||
self.end_of_stream = true;
|
||||
}
|
||||
return Some(Err(Error::last_os_error()));
|
||||
}
|
||||
if entry_ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
|
||||
return Some(Ok(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Dir {
|
||||
fn drop(&mut self) {
|
||||
let r = unsafe { libc::closedir(self.0) };
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
pub fn path(&self) -> PathBuf {
|
||||
use crate::sys::vxworks::ext::ffi::OsStrExt;
|
||||
self.dir.root.join(OsStr::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> {
|
||||
lstat(&self.path()).map(|m| m.file_type())
|
||||
}
|
||||
|
||||
pub fn ino(&self) -> u64 {
|
||||
self.entry.d_ino as u64
|
||||
}
|
||||
|
||||
fn name_bytes(&self) -> &[u8] {
|
||||
unsafe {
|
||||
//&*self.name
|
||||
CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenOptions {
|
||||
pub fn new() -> OpenOptions {
|
||||
OpenOptions {
|
||||
// generic
|
||||
read: false,
|
||||
write: false,
|
||||
append: false,
|
||||
truncate: false,
|
||||
create: false,
|
||||
create_new: false,
|
||||
// system-specific
|
||||
custom_flags: 0,
|
||||
mode: 0o666,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, read: bool) {
|
||||
self.read = read;
|
||||
}
|
||||
pub fn write(&mut self, write: bool) {
|
||||
self.write = write;
|
||||
}
|
||||
pub fn append(&mut self, append: bool) {
|
||||
self.append = append;
|
||||
}
|
||||
pub fn truncate(&mut self, truncate: bool) {
|
||||
self.truncate = truncate;
|
||||
}
|
||||
pub fn create(&mut self, create: bool) {
|
||||
self.create = create;
|
||||
}
|
||||
pub fn create_new(&mut self, create_new: bool) {
|
||||
self.create_new = create_new;
|
||||
}
|
||||
pub fn mode(&mut self, mode: u32) {
|
||||
self.mode = mode as mode_t;
|
||||
}
|
||||
|
||||
fn get_access_mode(&self) -> io::Result<c_int> {
|
||||
match (self.read, self.write, self.append) {
|
||||
(true, false, false) => Ok(libc::O_RDONLY),
|
||||
(false, true, false) => Ok(libc::O_WRONLY),
|
||||
(true, true, false) => Ok(libc::O_RDWR),
|
||||
(false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
|
||||
(true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
|
||||
(false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_creation_mode(&self) -> io::Result<c_int> {
|
||||
match (self.write, self.append) {
|
||||
(true, false) => {}
|
||||
(false, false) => {
|
||||
if self.truncate || self.create || self.create_new {
|
||||
return Err(Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
}
|
||||
(_, true) => {
|
||||
if self.truncate && !self.create_new {
|
||||
return Err(Error::from_raw_os_error(libc::EINVAL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(match (self.create, self.truncate, self.create_new) {
|
||||
(false, false, false) => 0,
|
||||
(true, false, false) => libc::O_CREAT,
|
||||
(false, true, false) => libc::O_TRUNC,
|
||||
(true, true, false) => libc::O_CREAT | libc::O_TRUNC,
|
||||
(_, _, true) => libc::O_CREAT | libc::O_EXCL,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
||||
let path = cstr(path)?;
|
||||
File::open_c(&path, opts)
|
||||
}
|
||||
|
||||
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
|
||||
let flags = libc::O_CLOEXEC
|
||||
| opts.get_access_mode()?
|
||||
| opts.get_creation_mode()?
|
||||
| (opts.custom_flags as c_int & !libc::O_ACCMODE);
|
||||
let fd = cvt_r(|| unsafe { open(path.as_ptr(), flags, opts.mode as c_int) })?;
|
||||
Ok(File(FileDesc::new(fd)))
|
||||
}
|
||||
|
||||
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
||||
let mut stat: stat64 = unsafe { mem::zeroed() };
|
||||
cvt(unsafe { ::libc::fstat(self.0.raw(), &mut stat) })?;
|
||||
Ok(FileAttr { stat: stat })
|
||||
}
|
||||
|
||||
pub fn fsync(&self) -> io::Result<()> {
|
||||
cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn datasync(&self) -> io::Result<()> {
|
||||
cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
|
||||
return Ok(());
|
||||
unsafe fn os_datasync(fd: c_int) -> c_int {
|
||||
libc::fsync(fd)
|
||||
} //not supported
|
||||
}
|
||||
|
||||
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
||||
return cvt_r(|| unsafe { ftruncate(self.0.raw(), size as off_t) }).map(drop);
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
|
||||
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
self.0.read_at(buf, offset)
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
|
||||
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
self.0.write_at(buf, offset)
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
|
||||
let (whence, pos) = match pos {
|
||||
// Casting to `i64` is fine, too large values will end up as
|
||||
// negative which will cause an error in `"lseek64"`.
|
||||
SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
|
||||
SeekFrom::End(off) => (libc::SEEK_END, off),
|
||||
SeekFrom::Current(off) => (libc::SEEK_CUR, off),
|
||||
};
|
||||
let n = cvt(unsafe { lseek(self.0.raw(), pos, whence) })?;
|
||||
Ok(n as u64)
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<File> {
|
||||
self.0.duplicate().map(File)
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> &FileDesc {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn into_fd(self) -> FileDesc {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
|
||||
cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn diverge(&self) -> ! {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
impl DirBuilder {
|
||||
pub fn new() -> DirBuilder {
|
||||
DirBuilder { mode: 0o777 }
|
||||
}
|
||||
|
||||
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
|
||||
let p = cstr(p)?;
|
||||
cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_mode(&mut self, mode: u32) {
|
||||
self.mode = mode as mode_t;
|
||||
}
|
||||
}
|
||||
|
||||
fn cstr(path: &Path) -> io::Result<CString> {
|
||||
use crate::sys::vxworks::ext::ffi::OsStrExt;
|
||||
Ok(CString::new(path.as_os_str().as_bytes())?)
|
||||
}
|
||||
|
||||
impl FromInner<c_int> for File {
|
||||
fn from_inner(fd: c_int) -> File {
|
||||
File(FileDesc::new(fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for File {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn get_path(fd: c_int) -> Option<PathBuf> {
|
||||
let mut buf = vec![0; libc::PATH_MAX as usize];
|
||||
let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
|
||||
if n == -1 {
|
||||
return None;
|
||||
}
|
||||
let l = buf.iter().position(|&c| c == 0).unwrap();
|
||||
buf.truncate(l as usize);
|
||||
Some(PathBuf::from(OsString::from_vec(buf)))
|
||||
}
|
||||
fn get_mode(fd: c_int) -> Option<(bool, bool)> {
|
||||
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
|
||||
if mode == -1 {
|
||||
return None;
|
||||
}
|
||||
match mode & libc::O_ACCMODE {
|
||||
libc::O_RDONLY => Some((true, false)),
|
||||
libc::O_RDWR => Some((true, true)),
|
||||
libc::O_WRONLY => Some((false, true)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
let fd = self.0.raw();
|
||||
let mut b = f.debug_struct("File");
|
||||
b.field("fd", &fd);
|
||||
if let Some(path) = get_path(fd) {
|
||||
b.field("path", &path);
|
||||
}
|
||||
if let Some((read, write)) = get_mode(fd) {
|
||||
b.field("read", &read).field("write", &write);
|
||||
}
|
||||
b.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
|
||||
let root = p.to_path_buf();
|
||||
let p = cstr(p)?;
|
||||
unsafe {
|
||||
let ptr = libc::opendir(p.as_ptr());
|
||||
if ptr.is_null() {
|
||||
Err(Error::last_os_error())
|
||||
} else {
|
||||
let inner = InnerReadDir { dirp: Dir(ptr), root };
|
||||
Ok(ReadDir { inner: Arc::new(inner), end_of_stream: false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlink(p: &Path) -> io::Result<()> {
|
||||
let p = cstr(p)?;
|
||||
cvt(unsafe { libc::unlink(p.as_ptr()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
|
||||
let old = cstr(old)?;
|
||||
let new = cstr(new)?;
|
||||
cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
|
||||
let p = cstr(p)?;
|
||||
cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn rmdir(p: &Path) -> io::Result<()> {
|
||||
let p = cstr(p)?;
|
||||
cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
|
||||
let filetype = lstat(path)?.file_type();
|
||||
if filetype.is_symlink() { unlink(path) } else { remove_dir_all_recursive(path) }
|
||||
}
|
||||
|
||||
fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
|
||||
for child in readdir(path)? {
|
||||
let child = child?;
|
||||
if child.file_type()?.is_dir() {
|
||||
remove_dir_all_recursive(&child.path())?;
|
||||
} else {
|
||||
unlink(&child.path())?;
|
||||
}
|
||||
}
|
||||
rmdir(path)
|
||||
}
|
||||
|
||||
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
|
||||
let c_path = cstr(p)?;
|
||||
let p = c_path.as_ptr();
|
||||
|
||||
let mut buf = Vec::with_capacity(256);
|
||||
|
||||
loop {
|
||||
let buf_read =
|
||||
cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
|
||||
|
||||
unsafe {
|
||||
buf.set_len(buf_read);
|
||||
}
|
||||
|
||||
if buf_read != buf.capacity() {
|
||||
buf.shrink_to_fit();
|
||||
|
||||
return Ok(PathBuf::from(OsString::from_vec(buf)));
|
||||
}
|
||||
|
||||
// Trigger the internal buffer resizing logic of `Vec` by requiring
|
||||
// more space than the current capacity. The length is guaranteed to be
|
||||
// the same as the capacity due to the if statement above.
|
||||
buf.reserve(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
let src = cstr(src)?;
|
||||
let dst = cstr(dst)?;
|
||||
cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
let src = cstr(src)?;
|
||||
let dst = cstr(dst)?;
|
||||
cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn stat(p: &Path) -> io::Result<FileAttr> {
|
||||
let p = cstr(p)?;
|
||||
let mut stat: stat64 = unsafe { mem::zeroed() };
|
||||
cvt(unsafe { libc::stat(p.as_ptr(), &mut stat as *mut _ as *mut _) })?;
|
||||
Ok(FileAttr { stat })
|
||||
}
|
||||
|
||||
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
|
||||
let p = cstr(p)?;
|
||||
let mut stat: stat64 = unsafe { mem::zeroed() };
|
||||
cvt(unsafe { ::libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) })?;
|
||||
Ok(FileAttr { stat })
|
||||
}
|
||||
|
||||
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
|
||||
use crate::sys::vxworks::ext::ffi::OsStrExt;
|
||||
let path = CString::new(p.as_os_str().as_bytes())?;
|
||||
let buf;
|
||||
unsafe {
|
||||
let r = libc::realpath(path.as_ptr(), ptr::null_mut());
|
||||
if r.is_null() {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
buf = CStr::from_ptr(r).to_bytes().to_vec();
|
||||
libc::free(r as *mut _);
|
||||
}
|
||||
Ok(PathBuf::from(OsString::from_vec(buf)))
|
||||
}
|
||||
|
||||
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||
use crate::fs::File;
|
||||
if !from.is_file() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"the source path is not an existing regular file",
|
||||
));
|
||||
}
|
||||
|
||||
let mut reader = File::open(from)?;
|
||||
let mut writer = File::create(to)?;
|
||||
let perm = reader.metadata()?.permissions();
|
||||
|
||||
let ret = io::copy(&mut reader, &mut writer)?;
|
||||
writer.set_permissions(perm)?;
|
||||
Ok(ret)
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
use crate::marker::PhantomData;
|
||||
use crate::slice;
|
||||
|
||||
use libc::{c_void, iovec};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct IoSlice<'a> {
|
||||
vec: iovec,
|
||||
_p: PhantomData<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSlice<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
|
||||
IoSlice {
|
||||
vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if self.vec.iov_len < n {
|
||||
panic!("advancing IoSlice beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.iov_len -= n;
|
||||
self.vec.iov_base = self.vec.iov_base.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IoSliceMut<'a> {
|
||||
vec: iovec,
|
||||
_p: PhantomData<&'a mut [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> IoSliceMut<'a> {
|
||||
#[inline]
|
||||
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
|
||||
IoSliceMut {
|
||||
vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance(&mut self, n: usize) {
|
||||
if self.vec.iov_len < n {
|
||||
panic!("advancing IoSliceMut beyond its length");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.vec.iov_len -= n;
|
||||
self.vec.iov_base = self.vec.iov_base.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// Original implementation taken from rust-memchr.
|
||||
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
|
||||
|
||||
pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
|
||||
let p = unsafe {
|
||||
libc::memchr(
|
||||
haystack.as_ptr() as *const libc::c_void,
|
||||
needle as libc::c_int,
|
||||
haystack.len(),
|
||||
)
|
||||
};
|
||||
if p.is_null() { None } else { Some(p as usize - (haystack.as_ptr() as usize)) }
|
||||
}
|
||||
|
||||
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
|
||||
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
|
||||
core::slice::memchr::memrchr(needle, haystack)
|
||||
}
|
||||
|
||||
memrchr_specific(needle, haystack)
|
||||
}
|
@ -7,29 +7,53 @@ pub use self::rand::hashmap_random_keys;
|
||||
pub use crate::os::vxworks as platform;
|
||||
pub use libc::strlen;
|
||||
|
||||
#[macro_use]
|
||||
#[path = "../unix/weak.rs"]
|
||||
pub mod weak;
|
||||
|
||||
#[path = "../unix/alloc.rs"]
|
||||
pub mod alloc;
|
||||
#[path = "../unix/args.rs"]
|
||||
pub mod args;
|
||||
#[path = "../unix/cmath.rs"]
|
||||
pub mod cmath;
|
||||
#[path = "../unix/condvar.rs"]
|
||||
pub mod condvar;
|
||||
pub mod env;
|
||||
#[path = "../unix/ext/mod.rs"]
|
||||
pub mod ext;
|
||||
#[path = "../unix/fd.rs"]
|
||||
pub mod fd;
|
||||
#[path = "../unix/fs.rs"]
|
||||
pub mod fs;
|
||||
#[path = "../unix/io.rs"]
|
||||
pub mod io;
|
||||
#[path = "../unix/memchr.rs"]
|
||||
pub mod memchr;
|
||||
#[path = "../unix/mutex.rs"]
|
||||
pub mod mutex;
|
||||
#[path = "../unix/net.rs"]
|
||||
pub mod net;
|
||||
#[path = "../unix/os.rs"]
|
||||
pub mod os;
|
||||
#[path = "../unix/path.rs"]
|
||||
pub mod path;
|
||||
#[path = "../unix/pipe.rs"]
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
pub mod rand;
|
||||
#[path = "../unix/rwlock.rs"]
|
||||
pub mod rwlock;
|
||||
#[path = "../unix/stack_overflow.rs"]
|
||||
pub mod stack_overflow;
|
||||
#[path = "../unix/stdio.rs"]
|
||||
pub mod stdio;
|
||||
#[path = "../unix/thread.rs"]
|
||||
pub mod thread;
|
||||
pub mod thread_local_dtor;
|
||||
#[path = "../unix/thread_local_key.rs"]
|
||||
pub mod thread_local_key;
|
||||
#[path = "../unix/time.rs"]
|
||||
pub mod time;
|
||||
|
||||
pub use crate::sys_common::os_str_bytes as os_str;
|
||||
|
@ -1,133 +0,0 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
pub struct Mutex {
|
||||
inner: UnsafeCell<libc::pthread_mutex_t>,
|
||||
}
|
||||
|
||||
pub type MovableMutex = Box<Mutex>;
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
|
||||
m.inner.get()
|
||||
}
|
||||
|
||||
unsafe impl Send for Mutex {}
|
||||
unsafe impl Sync for Mutex {}
|
||||
|
||||
#[allow(dead_code)] // sys isn't exported yet
|
||||
impl Mutex {
|
||||
pub const fn new() -> Mutex {
|
||||
// Might be moved to a different address, so it is better to avoid
|
||||
// initialization of potentially opaque OS data before it landed.
|
||||
// Be very careful using this newly constructed `Mutex`, reentrant
|
||||
// locking is undefined behavior until `init` is called!
|
||||
Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
|
||||
}
|
||||
#[inline]
|
||||
pub unsafe fn init(&mut self) {
|
||||
// Issue #33770
|
||||
//
|
||||
// A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
|
||||
// a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
|
||||
// try to re-lock it from the same thread when you already hold a lock.
|
||||
//
|
||||
// In practice, glibc takes advantage of this undefined behavior to
|
||||
// implement hardware lock elision, which uses hardware transactional
|
||||
// memory to avoid acquiring the lock. While a transaction is in
|
||||
// progress, the lock appears to be unlocked. This isn't a problem for
|
||||
// other threads since the transactional memory will abort if a conflict
|
||||
// is detected, however no abort is generated if re-locking from the
|
||||
// same thread.
|
||||
//
|
||||
// Since locking the same mutex twice will result in two aliasing &mut
|
||||
// references, we instead create the mutex with type
|
||||
// PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to
|
||||
// re-lock it from the same thread, thus avoiding undefined behavior.
|
||||
let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
|
||||
let r = libc::pthread_mutexattr_init(attr.as_mut_ptr());
|
||||
debug_assert_eq!(r, 0);
|
||||
let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL);
|
||||
debug_assert_eq!(r, 0);
|
||||
let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
|
||||
debug_assert_eq!(r, 0);
|
||||
let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
#[inline]
|
||||
pub unsafe fn lock(&self) {
|
||||
let r = libc::pthread_mutex_lock(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
#[inline]
|
||||
pub unsafe fn unlock(&self) {
|
||||
let r = libc::pthread_mutex_unlock(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
#[inline]
|
||||
pub unsafe fn try_lock(&self) -> bool {
|
||||
libc::pthread_mutex_trylock(self.inner.get()) == 0
|
||||
}
|
||||
#[inline]
|
||||
#[cfg(not(target_os = "dragonfly"))]
|
||||
pub unsafe fn destroy(&self) {
|
||||
let r = libc::pthread_mutex_destroy(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
#[inline]
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
pub unsafe fn destroy(&self) {
|
||||
let r = libc::pthread_mutex_destroy(self.inner.get());
|
||||
// On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
|
||||
// mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
|
||||
// Once it is used (locked/unlocked) or pthread_mutex_init() is called,
|
||||
// this behaviour no longer occurs.
|
||||
debug_assert!(r == 0 || r == libc::EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReentrantMutex {
|
||||
inner: UnsafeCell<libc::pthread_mutex_t>,
|
||||
}
|
||||
|
||||
unsafe impl Send for ReentrantMutex {}
|
||||
unsafe impl Sync for ReentrantMutex {}
|
||||
|
||||
impl ReentrantMutex {
|
||||
pub const unsafe fn uninitialized() -> ReentrantMutex {
|
||||
ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
|
||||
}
|
||||
|
||||
pub unsafe fn init(&self) {
|
||||
let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
|
||||
let result = libc::pthread_mutexattr_init(attr.as_mut_ptr());
|
||||
debug_assert_eq!(result, 0);
|
||||
let result =
|
||||
libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE);
|
||||
debug_assert_eq!(result, 0);
|
||||
let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
|
||||
debug_assert_eq!(result, 0);
|
||||
let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
|
||||
debug_assert_eq!(result, 0);
|
||||
}
|
||||
|
||||
pub unsafe fn lock(&self) {
|
||||
let result = libc::pthread_mutex_lock(self.inner.get());
|
||||
debug_assert_eq!(result, 0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn try_lock(&self) -> bool {
|
||||
libc::pthread_mutex_trylock(self.inner.get()) == 0
|
||||
}
|
||||
|
||||
pub unsafe fn unlock(&self) {
|
||||
let result = libc::pthread_mutex_unlock(self.inner.get());
|
||||
debug_assert_eq!(result, 0);
|
||||
}
|
||||
|
||||
pub unsafe fn destroy(&self) {
|
||||
let result = libc::pthread_mutex_destroy(self.inner.get());
|
||||
debug_assert_eq!(result, 0);
|
||||
}
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
#[cfg(all(test, taget_env = "gnu"))]
|
||||
mod tests;
|
||||
|
||||
use crate::cmp;
|
||||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::io::{IoSlice, IoSliceMut};
|
||||
use crate::mem;
|
||||
use crate::net::{Shutdown, SocketAddr};
|
||||
use crate::str;
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
use crate::time::{Duration, Instant};
|
||||
use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK};
|
||||
|
||||
pub use crate::sys::{cvt, cvt_r};
|
||||
|
||||
#[allow(unused_extern_crates)]
|
||||
pub extern crate libc as netc;
|
||||
|
||||
pub type wrlen_t = size_t;
|
||||
|
||||
pub struct Socket(FileDesc);
|
||||
|
||||
pub fn init() {}
|
||||
|
||||
pub fn cvt_gai(err: c_int) -> io::Result<()> {
|
||||
if err == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// We may need to trigger a glibc workaround. See on_resolver_failure() for details.
|
||||
on_resolver_failure();
|
||||
|
||||
if err == EAI_SYSTEM {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let detail = unsafe {
|
||||
str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned()
|
||||
};
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
&format!("failed to lookup address information: {}", detail)[..],
|
||||
))
|
||||
}
|
||||
|
||||
impl Socket {
|
||||
pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
|
||||
let fam = match *addr {
|
||||
SocketAddr::V4(..) => libc::AF_INET,
|
||||
SocketAddr::V6(..) => libc::AF_INET6,
|
||||
};
|
||||
Socket::new_raw(fam, ty)
|
||||
}
|
||||
|
||||
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
|
||||
unsafe {
|
||||
let fd = cvt(libc::socket(fam, ty, 0))?;
|
||||
let fd = FileDesc::new(fd);
|
||||
fd.set_cloexec()?;
|
||||
let socket = Socket(fd);
|
||||
Ok(socket)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
|
||||
self.set_nonblocking(true)?;
|
||||
let r = unsafe {
|
||||
let (addrp, len) = addr.into_inner();
|
||||
cvt(libc::connect(self.0.raw(), addrp, len))
|
||||
};
|
||||
self.set_nonblocking(false)?;
|
||||
|
||||
match r {
|
||||
Ok(_) => return Ok(()),
|
||||
// there's no ErrorKind for EINPROGRESS :(
|
||||
Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
|
||||
let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 };
|
||||
|
||||
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"cannot set a 0 duration timeout",
|
||||
));
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
loop {
|
||||
let elapsed = start.elapsed();
|
||||
if elapsed >= timeout {
|
||||
return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out"));
|
||||
}
|
||||
|
||||
let timeout = timeout - elapsed;
|
||||
let mut timeout = timeout
|
||||
.as_secs()
|
||||
.saturating_mul(1_000)
|
||||
.saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
|
||||
if timeout == 0 {
|
||||
timeout = 1;
|
||||
}
|
||||
|
||||
let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int;
|
||||
|
||||
match unsafe { libc::poll(&mut pollfd, 1, timeout) } {
|
||||
-1 => {
|
||||
let err = io::Error::last_os_error();
|
||||
if err.kind() != io::ErrorKind::Interrupted {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
0 => {}
|
||||
_ => {
|
||||
// linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
|
||||
// for POLLHUP rather than read readiness
|
||||
if pollfd.revents & libc::POLLHUP != 0 {
|
||||
let e = self.take_error()?.unwrap_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP")
|
||||
});
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> {
|
||||
let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
|
||||
let fd = FileDesc::new(fd);
|
||||
fd.set_cloexec()?;
|
||||
Ok(Socket(fd))
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<Socket> {
|
||||
self.0.duplicate().map(Socket)
|
||||
}
|
||||
|
||||
fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.recv_with_flags(buf, 0)
|
||||
}
|
||||
|
||||
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.recv_with_flags(buf, MSG_PEEK)
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
|
||||
fn recv_from_with_flags(
|
||||
&self,
|
||||
buf: &mut [u8],
|
||||
flags: c_int,
|
||||
) -> io::Result<(usize, SocketAddr)> {
|
||||
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
|
||||
let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t;
|
||||
|
||||
let n = cvt(unsafe {
|
||||
libc::recvfrom(
|
||||
self.0.raw(),
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
buf.len(),
|
||||
flags,
|
||||
&mut storage as *mut _ as *mut _,
|
||||
&mut addrlen,
|
||||
)
|
||||
})?;
|
||||
Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
|
||||
}
|
||||
|
||||
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.recv_from_with_flags(buf, 0)
|
||||
}
|
||||
|
||||
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.recv_from_with_flags(buf, MSG_PEEK)
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
|
||||
pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
|
||||
let timeout = match dur {
|
||||
Some(dur) => {
|
||||
if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"cannot set a 0 duration timeout",
|
||||
));
|
||||
}
|
||||
|
||||
let secs = if dur.as_secs() > libc::time_t::MAX as u64 {
|
||||
libc::time_t::MAX
|
||||
} else {
|
||||
dur.as_secs() as libc::time_t
|
||||
};
|
||||
let mut timeout = libc::timeval {
|
||||
tv_sec: secs,
|
||||
tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t,
|
||||
};
|
||||
if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
|
||||
timeout.tv_usec = 1;
|
||||
}
|
||||
timeout
|
||||
}
|
||||
None => libc::timeval { tv_sec: 0, tv_usec: 0 },
|
||||
};
|
||||
setsockopt(self, libc::SOL_SOCKET, kind, timeout)
|
||||
}
|
||||
|
||||
pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> {
|
||||
let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?;
|
||||
if raw.tv_sec == 0 && raw.tv_usec == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
let sec = raw.tv_sec as u64;
|
||||
let nsec = (raw.tv_usec as u32) * 1000;
|
||||
Ok(Some(Duration::new(sec, nsec)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
|
||||
let how = match how {
|
||||
Shutdown::Write => libc::SHUT_WR,
|
||||
Shutdown::Read => libc::SHUT_RD,
|
||||
Shutdown::Both => libc::SHUT_RDWR,
|
||||
};
|
||||
cvt(unsafe { libc::shutdown(self.0.raw(), how) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
|
||||
setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int)
|
||||
}
|
||||
|
||||
pub fn nodelay(&self) -> io::Result<bool> {
|
||||
let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?;
|
||||
Ok(raw != 0)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
|
||||
let mut nonblocking = nonblocking as libc::c_int;
|
||||
cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop)
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
|
||||
let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
|
||||
if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsInner<c_int> for Socket {
|
||||
fn as_inner(&self) -> &c_int {
|
||||
self.0.as_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<c_int> for Socket {
|
||||
fn from_inner(fd: c_int) -> Socket {
|
||||
Socket(FileDesc::new(fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<c_int> for Socket {
|
||||
fn into_inner(self) -> c_int {
|
||||
self.0.into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
// In versions of glibc prior to 2.26, there's a bug where the DNS resolver
|
||||
// will cache the contents of /etc/resolv.conf, so changes to that file on disk
|
||||
// can be ignored by a long-running program. That can break DNS lookups on e.g.
|
||||
// laptops where the network comes and goes. See
|
||||
// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some
|
||||
// distros including Debian have patched glibc to fix this for a long time.
|
||||
//
|
||||
// A workaround for this bug is to call the res_init libc function, to clear
|
||||
// the cached configs. Unfortunately, while we believe glibc's implementation
|
||||
// of res_init is thread-safe, we know that other implementations are not
|
||||
// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
|
||||
// try to synchronize its res_init calls with a Mutex, but that wouldn't
|
||||
// protect programs that call into libc in other ways. So instead of calling
|
||||
// res_init unconditionally, we call it only when we detect we're linking
|
||||
// against glibc version < 2.26. (That is, when we both know its needed and
|
||||
// believe it's thread-safe).
|
||||
#[cfg(target_env = "gnu")]
|
||||
fn on_resolver_failure() {
|
||||
/*
|
||||
use crate::sys;
|
||||
|
||||
// If the version fails to parse, we treat it the same as "not glibc".
|
||||
if let Some(version) = sys::os::glibc_version() {
|
||||
if version < (2, 26) {
|
||||
unsafe { libc::res_init() };
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[cfg(not(target_env = "gnu"))]
|
||||
fn on_resolver_failure() {}
|
@ -1,23 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_res_init() {
|
||||
// This mostly just tests that the weak linkage doesn't panic wildly...
|
||||
res_init_if_glibc_before_2_26().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_glibc_version() {
|
||||
let cases = [
|
||||
("0.0", Some((0, 0))),
|
||||
("01.+2", Some((1, 2))),
|
||||
("3.4.5.six", Some((3, 4))),
|
||||
("1", None),
|
||||
("1.-2", None),
|
||||
("1.foo", None),
|
||||
("foo.1", None),
|
||||
];
|
||||
for &(version_str, parsed) in cases.iter() {
|
||||
assert_eq!(parsed, parse_glibc_version(version_str));
|
||||
}
|
||||
}
|
@ -1,315 +0,0 @@
|
||||
use crate::error::Error as StdError;
|
||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
||||
use crate::fmt;
|
||||
use crate::io;
|
||||
use crate::iter;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem;
|
||||
use crate::memchr;
|
||||
use crate::path::{self, Path, PathBuf};
|
||||
use crate::slice;
|
||||
use crate::str;
|
||||
use crate::sys::cvt;
|
||||
use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
|
||||
use libc::{self, c_char /*,c_void */, c_int};
|
||||
/*use sys::fd; this one is probably important */
|
||||
use crate::vec;
|
||||
|
||||
const TMPBUF_SZ: usize = 128;
|
||||
|
||||
// This is a terrible fix
|
||||
use crate::sys::os_str::Buf;
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
|
||||
pub trait OsStringExt {
|
||||
fn from_vec(vec: Vec<u8>) -> Self;
|
||||
fn into_vec(self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OsStrExt {
|
||||
fn from_bytes(slice: &[u8]) -> &Self;
|
||||
fn as_bytes(&self) -> &[u8];
|
||||
}
|
||||
|
||||
impl OsStrExt for OsStr {
|
||||
fn from_bytes(slice: &[u8]) -> &OsStr {
|
||||
unsafe { mem::transmute(slice) }
|
||||
}
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
&self.as_inner().inner
|
||||
}
|
||||
}
|
||||
|
||||
pub fn errno() -> i32 {
|
||||
unsafe { libc::errnoGet() }
|
||||
}
|
||||
|
||||
pub fn set_errno(e: i32) {
|
||||
unsafe {
|
||||
libc::errnoSet(e as c_int);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a detailed string description for the given error number.
|
||||
pub fn error_string(errno: i32) -> String {
|
||||
let mut buf = [0 as c_char; TMPBUF_SZ];
|
||||
extern "C" {
|
||||
fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int;
|
||||
}
|
||||
|
||||
let p = buf.as_mut_ptr();
|
||||
unsafe {
|
||||
if strerror_r(errno as c_int, p, buf.len()) < 0 {
|
||||
panic!("strerror_r failure");
|
||||
}
|
||||
let p = p as *const _;
|
||||
str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getcwd() -> io::Result<PathBuf> {
|
||||
let mut buf = Vec::with_capacity(512);
|
||||
loop {
|
||||
unsafe {
|
||||
let ptr = buf.as_mut_ptr() as *mut libc::c_char;
|
||||
if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() {
|
||||
let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
|
||||
buf.set_len(len);
|
||||
buf.shrink_to_fit();
|
||||
return Ok(PathBuf::from(OsString::from_vec(buf)));
|
||||
} else {
|
||||
let error = io::Error::last_os_error();
|
||||
if error.raw_os_error() != Some(libc::ERANGE) {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
// Trigger the internal buffer resizing logic of `Vec` by requiring
|
||||
// more space than the current capacity.
|
||||
let cap = buf.capacity();
|
||||
buf.set_len(cap);
|
||||
buf.reserve(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chdir(p: &path::Path) -> io::Result<()> {
|
||||
let p: &OsStr = p.as_ref();
|
||||
let p = CString::new(p.as_bytes())?;
|
||||
unsafe {
|
||||
match libc::chdir(p.as_ptr()) == (0 as c_int) {
|
||||
true => Ok(()),
|
||||
false => Err(io::Error::last_os_error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SplitPaths<'a> {
|
||||
iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, fn(&'a [u8]) -> PathBuf>,
|
||||
}
|
||||
|
||||
pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
|
||||
fn bytes_to_path(b: &[u8]) -> PathBuf {
|
||||
PathBuf::from(<OsStr as OsStrExt>::from_bytes(b))
|
||||
}
|
||||
fn is_colon(b: &u8) -> bool {
|
||||
*b == b':'
|
||||
}
|
||||
let unparsed = unparsed.as_bytes();
|
||||
SplitPaths {
|
||||
iter: unparsed
|
||||
.split(is_colon as fn(&u8) -> bool)
|
||||
.map(bytes_to_path as fn(&[u8]) -> PathBuf),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SplitPaths<'a> {
|
||||
type Item = PathBuf;
|
||||
fn next(&mut self) -> Option<PathBuf> {
|
||||
self.iter.next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JoinPathsError;
|
||||
|
||||
pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: AsRef<OsStr>,
|
||||
{
|
||||
let mut joined = Vec::new();
|
||||
let sep = b':';
|
||||
|
||||
for (i, path) in paths.enumerate() {
|
||||
let path = path.as_ref().as_bytes();
|
||||
if i > 0 {
|
||||
joined.push(sep)
|
||||
}
|
||||
if path.contains(&sep) {
|
||||
return Err(JoinPathsError);
|
||||
}
|
||||
joined.extend_from_slice(path);
|
||||
}
|
||||
Ok(OsStringExt::from_vec(joined))
|
||||
}
|
||||
|
||||
impl fmt::Display for JoinPathsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"path segment contains separator `:`".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for JoinPathsError {
|
||||
#[allow(deprecated)]
|
||||
fn description(&self) -> &str {
|
||||
"failed to join paths"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_exe() -> io::Result<PathBuf> {
|
||||
#[cfg(test)]
|
||||
use realstd::env;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use crate::env;
|
||||
|
||||
let exe_path = env::args().next().unwrap();
|
||||
let path = Path::new(&exe_path);
|
||||
path.canonicalize()
|
||||
}
|
||||
|
||||
pub struct Env {
|
||||
iter: vec::IntoIter<(OsString, OsString)>,
|
||||
_dont_send_or_sync_me: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl Iterator for Env {
|
||||
type Item = (OsString, OsString);
|
||||
fn next(&mut self) -> Option<(OsString, OsString)> {
|
||||
self.iter.next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn environ() -> *mut *const *const c_char {
|
||||
extern "C" {
|
||||
static mut environ: *const *const c_char;
|
||||
}
|
||||
&mut environ
|
||||
}
|
||||
|
||||
pub unsafe fn env_lock() -> StaticMutexGuard {
|
||||
// It is UB to attempt to acquire this mutex reentrantly!
|
||||
static ENV_LOCK: StaticMutex = StaticMutex::new();
|
||||
ENV_LOCK.lock()
|
||||
}
|
||||
|
||||
/// Returns a vector of (variable, value) byte-vector pairs for all the
|
||||
/// environment variables of the current process.
|
||||
pub fn env() -> Env {
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
let mut environ = *environ();
|
||||
if environ.is_null() {
|
||||
panic!("os::env() failure getting env string from OS: {}", io::Error::last_os_error());
|
||||
}
|
||||
let mut result = Vec::new();
|
||||
while !(*environ).is_null() {
|
||||
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
|
||||
result.push(key_value);
|
||||
}
|
||||
environ = environ.add(1);
|
||||
}
|
||||
return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
|
||||
}
|
||||
|
||||
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
|
||||
// Strategy (copied from glibc): Variable name and value are separated
|
||||
// by an ASCII equals sign '='. Since a variable name must not be
|
||||
// empty, allow variable names starting with an equals sign. Skip all
|
||||
// malformed lines.
|
||||
if input.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
|
||||
pos.map(|p| {
|
||||
(
|
||||
OsStringExt::from_vec(input[..p].to_vec()),
|
||||
OsStringExt::from_vec(input[p + 1..].to_vec()),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
|
||||
// environment variables with a nul byte can't be set, so their value is
|
||||
// always None as well
|
||||
let k = CString::new(k.as_bytes())?;
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
|
||||
let ret = if s.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
|
||||
let k = CString::new(k.as_bytes())?;
|
||||
let v = CString::new(v.as_bytes())?;
|
||||
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
|
||||
let nbuf = CString::new(n.as_bytes())?;
|
||||
|
||||
unsafe {
|
||||
let _guard = env_lock();
|
||||
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn page_size() -> usize {
|
||||
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
|
||||
}
|
||||
|
||||
pub fn temp_dir() -> PathBuf {
|
||||
crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| PathBuf::from("/tmp"))
|
||||
}
|
||||
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
crate::env::var_os("HOME").or_else(|| None).map(PathBuf::from)
|
||||
}
|
||||
|
||||
pub fn exit(code: i32) -> ! {
|
||||
unsafe { libc::exit(code as c_int) }
|
||||
}
|
||||
|
||||
pub fn getpid() -> u32 {
|
||||
unsafe { libc::getpid() as u32 }
|
||||
}
|
||||
|
||||
pub fn getppid() -> u32 {
|
||||
unsafe { libc::getppid() as u32 }
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
use crate::ffi::OsStr;
|
||||
use crate::path::Prefix;
|
||||
|
||||
#[inline]
|
||||
pub fn is_sep_byte(b: u8) -> bool {
|
||||
b == b'/'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_verbatim_sep(b: u8) -> bool {
|
||||
b == b'/'
|
||||
}
|
||||
|
||||
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub const MAIN_SEP_STR: &str = "/";
|
||||
pub const MAIN_SEP: char = '/';
|
@ -1,107 +0,0 @@
|
||||
use crate::io::{self, IoSlice, IoSliceMut};
|
||||
use crate::mem;
|
||||
use crate::sync::atomic::AtomicBool;
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys::{cvt, cvt_r};
|
||||
use libc::{self /*, c_int apparently not used? */};
|
||||
|
||||
pub struct AnonPipe(FileDesc);
|
||||
|
||||
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
||||
static INVALID: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let mut fds = [0; 2];
|
||||
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
|
||||
|
||||
let fd0 = FileDesc::new(fds[0]);
|
||||
let fd1 = FileDesc::new(fds[1]);
|
||||
fd0.set_cloexec()?;
|
||||
fd1.set_cloexec()?;
|
||||
Ok((AnonPipe(fd0), AnonPipe(fd1)))
|
||||
}
|
||||
|
||||
impl AnonPipe {
|
||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> &FileDesc {
|
||||
&self.0
|
||||
}
|
||||
pub fn into_fd(self) -> FileDesc {
|
||||
self.0
|
||||
}
|
||||
pub fn diverge(&self) -> ! {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> {
|
||||
// Set both pipes into nonblocking mode as we're gonna be reading from both
|
||||
// in the `select` loop below, and we wouldn't want one to block the other!
|
||||
let p1 = p1.into_fd();
|
||||
let p2 = p2.into_fd();
|
||||
p1.set_nonblocking_pipe(true)?;
|
||||
p2.set_nonblocking_pipe(true)?;
|
||||
|
||||
let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
|
||||
fds[0].fd = p1.raw();
|
||||
fds[0].events = libc::POLLIN;
|
||||
fds[1].fd = p2.raw();
|
||||
fds[1].events = libc::POLLIN;
|
||||
loop {
|
||||
// wait for either pipe to become readable using `poll`
|
||||
cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?;
|
||||
|
||||
if fds[0].revents != 0 && read(&p1, v1)? {
|
||||
p2.set_nonblocking_pipe(false)?;
|
||||
return p2.read_to_end(v2).map(drop);
|
||||
}
|
||||
if fds[1].revents != 0 && read(&p2, v2)? {
|
||||
p1.set_nonblocking_pipe(false)?;
|
||||
return p1.read_to_end(v1).map(drop);
|
||||
}
|
||||
}
|
||||
|
||||
// Read as much as we can from each pipe, ignoring EWOULDBLOCK or
|
||||
// EAGAIN. If we hit EOF, then this will happen because the underlying
|
||||
// reader will return Ok(0), in which case we'll see `Ok` ourselves. In
|
||||
// this case we flip the other fd back into blocking mode and read
|
||||
// whatever's leftover on that file descriptor.
|
||||
fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> {
|
||||
match fd.read_to_end(dst) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(e) => {
|
||||
if e.raw_os_error() == Some(libc::EWOULDBLOCK)
|
||||
|| e.raw_os_error() == Some(libc::EAGAIN)
|
||||
{
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
pub use self::process_common::{Command, ExitCode, ExitStatus, Stdio, StdioPipes};
|
||||
pub use self::process_inner::Process;
|
||||
pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
|
||||
pub use self::process_inner::{ExitStatus, Process};
|
||||
pub use crate::ffi::OsString as EnvKey;
|
||||
pub use crate::sys_common::process::CommandEnvs;
|
||||
|
||||
#[path = "../../unix/process/process_common.rs"]
|
||||
mod process_common;
|
||||
#[path = "process_vxworks.rs"]
|
||||
mod process_inner;
|
||||
|
@ -1,399 +0,0 @@
|
||||
use crate::os::unix::prelude::*;
|
||||
|
||||
use crate::collections::BTreeMap;
|
||||
use crate::ffi::{CStr, CString, OsStr, OsString};
|
||||
use crate::fmt;
|
||||
use crate::io;
|
||||
use crate::ptr;
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys::fs::{File, OpenOptions};
|
||||
use crate::sys::pipe::{self, AnonPipe};
|
||||
use crate::sys_common::process::CommandEnv;
|
||||
|
||||
use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Command
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Command {
|
||||
// Currently we try hard to ensure that the call to `.exec()` doesn't
|
||||
// actually allocate any memory. While many platforms try to ensure that
|
||||
// memory allocation works after a fork in a multithreaded process, it's
|
||||
// been observed to be buggy and somewhat unreliable, so we do our best to
|
||||
// just not do it at all!
|
||||
//
|
||||
// Along those lines, the `argv` and `envp` raw pointers here are exactly
|
||||
// what's gonna get passed to `execvp`. The `argv` array starts with the
|
||||
// `program` and ends with a NULL, and the `envp` pointer, if present, is
|
||||
// also null-terminated.
|
||||
//
|
||||
// Right now we don't support removing arguments, so there's no much fancy
|
||||
// support there, but we support adding and removing environment variables,
|
||||
// so a side table is used to track where in the `envp` array each key is
|
||||
// located. Whenever we add a key we update it in place if it's already
|
||||
// present, and whenever we remove a key we update the locations of all
|
||||
// other keys.
|
||||
program: CString,
|
||||
args: Vec<CString>,
|
||||
argv: Argv,
|
||||
env: CommandEnv,
|
||||
|
||||
cwd: Option<CString>,
|
||||
uid: Option<uid_t>,
|
||||
gid: Option<gid_t>,
|
||||
saw_nul: bool,
|
||||
closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,
|
||||
stdin: Option<Stdio>,
|
||||
stdout: Option<Stdio>,
|
||||
stderr: Option<Stdio>,
|
||||
}
|
||||
|
||||
// Create a new type for `Argv`, so that we can make it `Send` and `Sync`
|
||||
struct Argv(Vec<*const c_char>);
|
||||
|
||||
// It is safe to make `Argv` `Send` and `Sync`, because it contains
|
||||
// pointers to memory owned by `Command.args`
|
||||
unsafe impl Send for Argv {}
|
||||
unsafe impl Sync for Argv {}
|
||||
|
||||
// passed back to std::process with the pipes connected to the child, if any
|
||||
// were requested
|
||||
pub struct StdioPipes {
|
||||
pub stdin: Option<AnonPipe>,
|
||||
pub stdout: Option<AnonPipe>,
|
||||
pub stderr: Option<AnonPipe>,
|
||||
}
|
||||
|
||||
// passed to do_exec() with configuration of what the child stdio should look
|
||||
// like
|
||||
pub struct ChildPipes {
|
||||
pub stdin: ChildStdio,
|
||||
pub stdout: ChildStdio,
|
||||
pub stderr: ChildStdio,
|
||||
}
|
||||
|
||||
pub enum ChildStdio {
|
||||
Inherit,
|
||||
Explicit(c_int),
|
||||
Owned(FileDesc),
|
||||
}
|
||||
|
||||
pub enum Stdio {
|
||||
Inherit,
|
||||
Null,
|
||||
MakePipe,
|
||||
Fd(FileDesc),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn new(program: &OsStr) -> Command {
|
||||
let mut saw_nul = false;
|
||||
let program = os2c(program, &mut saw_nul);
|
||||
Command {
|
||||
argv: Argv(vec![program.as_ptr(), ptr::null()]),
|
||||
args: vec![program.clone()],
|
||||
program,
|
||||
env: Default::default(),
|
||||
cwd: None,
|
||||
uid: None,
|
||||
gid: None,
|
||||
saw_nul,
|
||||
closures: Vec::new(),
|
||||
stdin: None,
|
||||
stdout: None,
|
||||
stderr: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_arg_0(&mut self, arg: &OsStr) {
|
||||
// Set a new arg0
|
||||
let arg = os2c(arg, &mut self.saw_nul);
|
||||
debug_assert!(self.argv.0.len() > 1);
|
||||
self.argv.0[0] = arg.as_ptr();
|
||||
self.args[0] = arg;
|
||||
}
|
||||
|
||||
pub fn arg(&mut self, arg: &OsStr) {
|
||||
// Overwrite the trailing NULL pointer in `argv` and then add a new null
|
||||
// pointer.
|
||||
let arg = os2c(arg, &mut self.saw_nul);
|
||||
self.argv.0[self.args.len()] = arg.as_ptr();
|
||||
self.argv.0.push(ptr::null());
|
||||
|
||||
// Also make sure we keep track of the owned value to schedule a
|
||||
// destructor for this memory.
|
||||
self.args.push(arg);
|
||||
}
|
||||
|
||||
pub fn cwd(&mut self, dir: &OsStr) {
|
||||
self.cwd = Some(os2c(dir, &mut self.saw_nul));
|
||||
}
|
||||
pub fn uid(&mut self, id: uid_t) {
|
||||
self.uid = Some(id);
|
||||
}
|
||||
pub fn gid(&mut self, id: gid_t) {
|
||||
self.gid = Some(id);
|
||||
}
|
||||
|
||||
pub fn saw_nul(&self) -> bool {
|
||||
self.saw_nul
|
||||
}
|
||||
pub fn get_argv(&self) -> &Vec<*const c_char> {
|
||||
&self.argv.0
|
||||
}
|
||||
|
||||
pub fn get_program(&self) -> &CStr {
|
||||
&*self.program
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_cwd(&self) -> &Option<CString> {
|
||||
&self.cwd
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn get_uid(&self) -> Option<uid_t> {
|
||||
self.uid
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn get_gid(&self) -> Option<gid_t> {
|
||||
self.gid
|
||||
}
|
||||
|
||||
pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> {
|
||||
&mut self.closures
|
||||
}
|
||||
|
||||
pub unsafe fn pre_exec(&mut self, _f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>) {
|
||||
// Fork() is not supported in vxWorks so no way to run the closure in the new procecss.
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn stdin(&mut self, stdin: Stdio) {
|
||||
self.stdin = Some(stdin);
|
||||
}
|
||||
|
||||
pub fn stdout(&mut self, stdout: Stdio) {
|
||||
self.stdout = Some(stdout);
|
||||
}
|
||||
|
||||
pub fn stderr(&mut self, stderr: Stdio) {
|
||||
self.stderr = Some(stderr);
|
||||
}
|
||||
|
||||
pub fn env_mut(&mut self) -> &mut CommandEnv {
|
||||
&mut self.env
|
||||
}
|
||||
|
||||
pub fn capture_env(&mut self) -> Option<CStringArray> {
|
||||
let maybe_env = self.env.capture_if_changed();
|
||||
maybe_env.map(|env| construct_envp(env, &mut self.saw_nul))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn env_saw_path(&self) -> bool {
|
||||
self.env.have_changed_path()
|
||||
}
|
||||
|
||||
pub fn setup_io(
|
||||
&self,
|
||||
default: Stdio,
|
||||
needs_stdin: bool,
|
||||
) -> io::Result<(StdioPipes, ChildPipes)> {
|
||||
let null = Stdio::Null;
|
||||
let default_stdin = if needs_stdin { &default } else { &null };
|
||||
let stdin = self.stdin.as_ref().unwrap_or(default_stdin);
|
||||
let stdout = self.stdout.as_ref().unwrap_or(&default);
|
||||
let stderr = self.stderr.as_ref().unwrap_or(&default);
|
||||
let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?;
|
||||
let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?;
|
||||
let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?;
|
||||
let ours = StdioPipes { stdin: our_stdin, stdout: our_stdout, stderr: our_stderr };
|
||||
let theirs = ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr };
|
||||
Ok((ours, theirs))
|
||||
}
|
||||
}
|
||||
|
||||
fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
|
||||
CString::new(s.as_bytes()).unwrap_or_else(|_e| {
|
||||
*saw_nul = true;
|
||||
CString::new("<string-with-nul>").unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
// Helper type to manage ownership of the strings within a C-style array.
|
||||
pub struct CStringArray {
|
||||
items: Vec<CString>,
|
||||
ptrs: Vec<*const c_char>,
|
||||
}
|
||||
|
||||
impl CStringArray {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let mut result = CStringArray {
|
||||
items: Vec::with_capacity(capacity),
|
||||
ptrs: Vec::with_capacity(capacity + 1),
|
||||
};
|
||||
result.ptrs.push(ptr::null());
|
||||
result
|
||||
}
|
||||
pub fn push(&mut self, item: CString) {
|
||||
let l = self.ptrs.len();
|
||||
self.ptrs[l - 1] = item.as_ptr();
|
||||
self.ptrs.push(ptr::null());
|
||||
self.items.push(item);
|
||||
}
|
||||
pub fn as_ptr(&self) -> *const *const c_char {
|
||||
self.ptrs.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_envp(env: BTreeMap<OsString, OsString>, saw_nul: &mut bool) -> CStringArray {
|
||||
let mut result = CStringArray::with_capacity(env.len());
|
||||
for (k, v) in env {
|
||||
let mut k: OsString = k.into();
|
||||
|
||||
// Reserve additional space for '=' and null terminator
|
||||
k.reserve_exact(v.len() + 2);
|
||||
k.push("=");
|
||||
k.push(&v);
|
||||
|
||||
// Add the new entry into the array
|
||||
if let Ok(item) = CString::new(k.into_vec()) {
|
||||
result.push(item);
|
||||
} else {
|
||||
*saw_nul = true;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
impl Stdio {
|
||||
pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option<AnonPipe>)> {
|
||||
match *self {
|
||||
Stdio::Inherit => Ok((ChildStdio::Inherit, None)),
|
||||
|
||||
// Make sure that the source descriptors are not an stdio
|
||||
// descriptor, otherwise the order which we set the child's
|
||||
// descriptors may blow away a descriptor which we are hoping to
|
||||
// save. For example, suppose we want the child's stderr to be the
|
||||
// parent's stdout, and the child's stdout to be the parent's
|
||||
// stderr. No matter which we dup first, the second will get
|
||||
// overwritten prematurely.
|
||||
Stdio::Fd(ref fd) => {
|
||||
if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO {
|
||||
Ok((ChildStdio::Owned(fd.duplicate()?), None))
|
||||
} else {
|
||||
Ok((ChildStdio::Explicit(fd.raw()), None))
|
||||
}
|
||||
}
|
||||
|
||||
Stdio::MakePipe => {
|
||||
let (reader, writer) = pipe::anon_pipe()?;
|
||||
let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) };
|
||||
Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
|
||||
}
|
||||
|
||||
Stdio::Null => {
|
||||
let mut opts = OpenOptions::new();
|
||||
opts.read(readable);
|
||||
opts.write(!readable);
|
||||
let path = unsafe { CStr::from_ptr("/null\0".as_ptr() as *const _) };
|
||||
let fd = File::open_c(&path, &opts)?;
|
||||
Ok((ChildStdio::Owned(fd.into_fd()), None))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnonPipe> for Stdio {
|
||||
fn from(pipe: AnonPipe) -> Stdio {
|
||||
Stdio::Fd(pipe.into_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<File> for Stdio {
|
||||
fn from(file: File) -> Stdio {
|
||||
Stdio::Fd(file.into_fd())
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildStdio {
|
||||
pub fn fd(&self) -> Option<c_int> {
|
||||
match *self {
|
||||
ChildStdio::Inherit => None,
|
||||
ChildStdio::Explicit(fd) => Some(fd),
|
||||
ChildStdio::Owned(ref fd) => Some(fd.raw()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Command {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.program != self.args[0] {
|
||||
write!(f, "[{:?}] ", self.program)?;
|
||||
}
|
||||
write!(f, "{:?}", self.args[0])?;
|
||||
|
||||
for arg in &self.args[1..] {
|
||||
write!(f, " {:?}", arg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix exit statuses
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ExitStatus(c_int);
|
||||
|
||||
impl ExitStatus {
|
||||
pub fn new(status: c_int) -> ExitStatus {
|
||||
ExitStatus(status)
|
||||
}
|
||||
|
||||
fn exited(&self) -> bool {
|
||||
libc::WIFEXITED(self.0)
|
||||
}
|
||||
|
||||
pub fn success(&self) -> bool {
|
||||
self.code() == Some(0)
|
||||
}
|
||||
|
||||
pub fn code(&self) -> Option<i32> {
|
||||
if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
|
||||
}
|
||||
|
||||
pub fn signal(&self) -> Option<i32> {
|
||||
if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
|
||||
impl From<c_int> for ExitStatus {
|
||||
fn from(a: c_int) -> ExitStatus {
|
||||
ExitStatus(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExitStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(code) = self.code() {
|
||||
write!(f, "exit code: {}", code)
|
||||
} else {
|
||||
let signal = self.signal().unwrap();
|
||||
write!(f, "signal: {}", signal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ExitCode(u8);
|
||||
|
||||
impl ExitCode {
|
||||
pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _);
|
||||
pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _);
|
||||
|
||||
#[inline]
|
||||
pub fn as_i32(&self) -> i32 {
|
||||
self.0 as i32
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
use crate::fmt;
|
||||
use crate::io::{self, Error, ErrorKind};
|
||||
use crate::sys;
|
||||
use crate::sys::cvt;
|
||||
@ -67,7 +68,7 @@ impl Command {
|
||||
let _lock = sys::os::env_lock();
|
||||
|
||||
let ret = libc::rtpSpawn(
|
||||
self.get_program().as_ptr(),
|
||||
self.get_program_cstr().as_ptr(),
|
||||
self.get_argv().as_ptr() as *mut *const c_char, // argv
|
||||
c_envp as *mut *const c_char,
|
||||
100 as c_int, // initial priority
|
||||
@ -167,3 +168,47 @@ impl Process {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix exit statuses
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ExitStatus(c_int);
|
||||
|
||||
impl ExitStatus {
|
||||
pub fn new(status: c_int) -> ExitStatus {
|
||||
ExitStatus(status)
|
||||
}
|
||||
|
||||
fn exited(&self) -> bool {
|
||||
libc::WIFEXITED(self.0)
|
||||
}
|
||||
|
||||
pub fn success(&self) -> bool {
|
||||
self.code() == Some(0)
|
||||
}
|
||||
|
||||
pub fn code(&self) -> Option<i32> {
|
||||
if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
|
||||
}
|
||||
|
||||
pub fn signal(&self) -> Option<i32> {
|
||||
if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
|
||||
impl From<c_int> for ExitStatus {
|
||||
fn from(a: c_int) -> ExitStatus {
|
||||
ExitStatus(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExitStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(code) = self.code() {
|
||||
write!(f, "exit code: {}", code)
|
||||
} else {
|
||||
let signal = self.signal().unwrap();
|
||||
write!(f, "signal: {}", signal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,114 +0,0 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
pub struct RWLock {
|
||||
inner: UnsafeCell<libc::pthread_rwlock_t>,
|
||||
write_locked: UnsafeCell<bool>,
|
||||
num_readers: AtomicUsize,
|
||||
}
|
||||
|
||||
unsafe impl Send for RWLock {}
|
||||
unsafe impl Sync for RWLock {}
|
||||
|
||||
impl RWLock {
|
||||
pub const fn new() -> RWLock {
|
||||
RWLock {
|
||||
inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER),
|
||||
write_locked: UnsafeCell::new(false),
|
||||
num_readers: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn read(&self) {
|
||||
let r = libc::pthread_rwlock_rdlock(self.inner.get());
|
||||
if r == libc::EAGAIN {
|
||||
panic!("rwlock maximum reader count exceeded");
|
||||
} else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) {
|
||||
if r == 0 {
|
||||
self.raw_unlock();
|
||||
}
|
||||
panic!("rwlock read lock would result in deadlock");
|
||||
} else {
|
||||
debug_assert_eq!(r, 0);
|
||||
self.num_readers.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn try_read(&self) -> bool {
|
||||
let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
|
||||
if r == 0 {
|
||||
if *self.write_locked.get() {
|
||||
self.raw_unlock();
|
||||
false
|
||||
} else {
|
||||
self.num_readers.fetch_add(1, Ordering::Relaxed);
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn write(&self) {
|
||||
let r = libc::pthread_rwlock_wrlock(self.inner.get());
|
||||
// See comments above for why we check for EDEADLK and write_locked. We
|
||||
// also need to check that num_readers is 0.
|
||||
if r == libc::EDEADLK
|
||||
|| *self.write_locked.get()
|
||||
|| self.num_readers.load(Ordering::Relaxed) != 0
|
||||
{
|
||||
if r == 0 {
|
||||
self.raw_unlock();
|
||||
}
|
||||
panic!("rwlock write lock would result in deadlock");
|
||||
} else {
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
*self.write_locked.get() = true;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn try_write(&self) -> bool {
|
||||
let r = libc::pthread_rwlock_trywrlock(self.inner.get());
|
||||
if r == 0 {
|
||||
if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 {
|
||||
self.raw_unlock();
|
||||
false
|
||||
} else {
|
||||
*self.write_locked.get() = true;
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn raw_unlock(&self) {
|
||||
let r = libc::pthread_rwlock_unlock(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn read_unlock(&self) {
|
||||
debug_assert!(!*self.write_locked.get());
|
||||
self.num_readers.fetch_sub(1, Ordering::Relaxed);
|
||||
self.raw_unlock();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn write_unlock(&self) {
|
||||
debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0);
|
||||
debug_assert!(*self.write_locked.get());
|
||||
*self.write_locked.get() = false;
|
||||
self.raw_unlock();
|
||||
}
|
||||
#[inline]
|
||||
pub unsafe fn destroy(&self) {
|
||||
let r = libc::pthread_rwlock_destroy(self.inner.get());
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
#![cfg_attr(test, allow(dead_code))]
|
||||
|
||||
use self::imp::{drop_handler, make_handler};
|
||||
|
||||
pub use self::imp::cleanup;
|
||||
pub use self::imp::init;
|
||||
|
||||
pub struct Handler {
|
||||
_data: *mut libc::c_void,
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub unsafe fn new() -> Handler {
|
||||
make_handler()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Handler {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
drop_handler(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use crate::ptr;
|
||||
|
||||
pub unsafe fn init() {}
|
||||
|
||||
pub unsafe fn cleanup() {}
|
||||
|
||||
pub unsafe fn make_handler() -> super::Handler {
|
||||
super::Handler { _data: ptr::null_mut() }
|
||||
}
|
||||
|
||||
pub unsafe fn drop_handler(_handler: &mut super::Handler) {}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
use crate::io;
|
||||
use crate::sys::fd::FileDesc;
|
||||
|
||||
pub struct Stdin(());
|
||||
pub struct Stdout(());
|
||||
pub struct Stderr(());
|
||||
|
||||
impl Stdin {
|
||||
pub const fn new() -> Stdin {
|
||||
Stdin(())
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let fd = FileDesc::new(libc::STDIN_FILENO);
|
||||
let ret = fd.read(buf);
|
||||
fd.into_raw(); // do not close this FD
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Stdout {
|
||||
pub const fn new() -> Stdout {
|
||||
Stdout(())
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for Stdout {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let fd = FileDesc::new(libc::STDOUT_FILENO);
|
||||
let ret = fd.write(buf);
|
||||
fd.into_raw(); // do not close this FD
|
||||
ret
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stderr {
|
||||
pub const fn new() -> Stderr {
|
||||
Stderr(())
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for Stderr {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let fd = FileDesc::new(libc::STDERR_FILENO);
|
||||
let ret = fd.write(buf);
|
||||
fd.into_raw(); // do not close this FD
|
||||
ret
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ebadf(err: &io::Error) -> bool {
|
||||
err.raw_os_error() == Some(libc::EBADF as i32)
|
||||
}
|
||||
|
||||
pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
pub fn panic_output() -> Option<impl io::Write> {
|
||||
Some(Stderr::new())
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
use crate::cmp;
|
||||
use crate::ffi::CStr;
|
||||
use crate::io;
|
||||
use crate::mem;
|
||||
use crate::ptr;
|
||||
use crate::sys::{os, stack_overflow};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub const DEFAULT_MIN_STACK_SIZE: usize = 0x40000; // 256K
|
||||
|
||||
pub struct Thread {
|
||||
id: libc::pthread_t,
|
||||
}
|
||||
|
||||
// Some platforms may have pthread_t as a pointer in which case we still want
|
||||
// a thread to be Send/Sync
|
||||
unsafe impl Send for Thread {}
|
||||
unsafe impl Sync for Thread {}
|
||||
|
||||
// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc,
|
||||
// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS.
|
||||
unsafe fn pthread_attr_setstacksize(
|
||||
attr: *mut libc::pthread_attr_t,
|
||||
stack_size: libc::size_t,
|
||||
) -> libc::c_int {
|
||||
libc::pthread_attr_setstacksize(attr, stack_size)
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(box p);
|
||||
let mut native: libc::pthread_t = mem::zeroed();
|
||||
let mut attr: libc::pthread_attr_t = mem::zeroed();
|
||||
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
|
||||
|
||||
let stack_size = cmp::max(stack, min_stack_size(&attr));
|
||||
|
||||
match pthread_attr_setstacksize(&mut attr, stack_size) {
|
||||
0 => {}
|
||||
n => {
|
||||
assert_eq!(n, libc::EINVAL);
|
||||
// EINVAL means |stack_size| is either too small or not a
|
||||
// multiple of the system page size. Because it's definitely
|
||||
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
|
||||
// Round up to the nearest page and try again.
|
||||
let page_size = os::page_size();
|
||||
let stack_size =
|
||||
(stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);
|
||||
assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0);
|
||||
}
|
||||
};
|
||||
|
||||
let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _);
|
||||
// Note: if the thread creation fails and this assert fails, then p will
|
||||
// be leaked. However, an alternative design could cause double-free
|
||||
// which is clearly worse.
|
||||
assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
|
||||
|
||||
return if ret != 0 {
|
||||
// The thread failed to start and as a result p was not consumed. Therefore, it is
|
||||
// safe to reconstruct the box so that it gets deallocated.
|
||||
drop(Box::from_raw(p));
|
||||
Err(io::Error::from_raw_os_error(ret))
|
||||
} else {
|
||||
Ok(Thread { id: native })
|
||||
};
|
||||
|
||||
extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
|
||||
unsafe {
|
||||
// Next, set up our stack overflow handler which may get triggered if we run
|
||||
// out of stack.
|
||||
let _handler = stack_overflow::Handler::new();
|
||||
// Finally, let's run some code.
|
||||
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
let ret = unsafe { libc::sched_yield() };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
|
||||
pub fn set_name(_name: &CStr) {
|
||||
// VxWorks does not provide a way to set the task name except at creation time
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
let mut secs = dur.as_secs();
|
||||
let mut nsecs = dur.subsec_nanos() as _;
|
||||
|
||||
// If we're awoken with a signal then the return value will be -1 and
|
||||
// nanosleep will fill in `ts` with the remaining time.
|
||||
unsafe {
|
||||
while secs > 0 || nsecs > 0 {
|
||||
let mut ts = libc::timespec {
|
||||
tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t,
|
||||
tv_nsec: nsecs,
|
||||
};
|
||||
secs -= ts.tv_sec as u64;
|
||||
if libc::nanosleep(&ts, &mut ts) == -1 {
|
||||
assert_eq!(os::errno(), libc::EINTR);
|
||||
secs += ts.tv_sec as u64;
|
||||
nsecs = ts.tv_nsec;
|
||||
} else {
|
||||
nsecs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(self) {
|
||||
unsafe {
|
||||
let ret = libc::pthread_join(self.id, ptr::null_mut());
|
||||
mem::forget(self);
|
||||
assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> libc::pthread_t {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn into_id(self) -> libc::pthread_t {
|
||||
let id = self.id;
|
||||
mem::forget(self);
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Thread {
|
||||
fn drop(&mut self) {
|
||||
let ret = unsafe { libc::pthread_detach(self.id) };
|
||||
debug_assert_eq!(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
pub mod guard {
|
||||
use crate::ops::Range;
|
||||
pub type Guard = Range<usize>;
|
||||
pub unsafe fn current() -> Option<Guard> {
|
||||
None
|
||||
}
|
||||
pub unsafe fn init() -> Option<Guard> {
|
||||
None
|
||||
}
|
||||
pub unsafe fn deinit() {}
|
||||
}
|
||||
|
||||
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
|
||||
libc::PTHREAD_STACK_MIN
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#![allow(dead_code)] // not used on all platforms
|
||||
|
||||
use crate::mem;
|
||||
|
||||
pub type Key = libc::pthread_key_t;
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn create(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
|
||||
let mut key = 0;
|
||||
assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
|
||||
key
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn set(key: Key, value: *mut u8) {
|
||||
let r = libc::pthread_setspecific(key, value as *mut _);
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get(key: Key) -> *mut u8 {
|
||||
libc::pthread_getspecific(key) as *mut u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn destroy(key: Key) {
|
||||
let r = libc::pthread_key_delete(key);
|
||||
debug_assert_eq!(r, 0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn requires_synchronized_create() -> bool {
|
||||
false
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
use crate::cmp::Ordering;
|
||||
use crate::time::Duration;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
|
||||
use crate::convert::TryInto;
|
||||
|
||||
const NSEC_PER_SEC: u64 = 1_000_000_000;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Timespec {
|
||||
t: libc::timespec,
|
||||
}
|
||||
|
||||
impl Timespec {
|
||||
const fn zero() -> Timespec {
|
||||
Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } }
|
||||
}
|
||||
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
|
||||
if self >= other {
|
||||
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
|
||||
Duration::new(
|
||||
(self.t.tv_sec - other.t.tv_sec) as u64,
|
||||
(self.t.tv_nsec - other.t.tv_nsec) as u32,
|
||||
)
|
||||
} else {
|
||||
Duration::new(
|
||||
(self.t.tv_sec - 1 - other.t.tv_sec) as u64,
|
||||
self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
match other.sub_timespec(self) {
|
||||
Ok(d) => Err(d),
|
||||
Err(d) => Ok(d),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = other
|
||||
.as_secs()
|
||||
.try_into() // <- target type would be `libc::time_t`
|
||||
.ok()
|
||||
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;
|
||||
|
||||
// Nano calculations can't overflow because nanos are <1B which fit
|
||||
// in a u32.
|
||||
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
|
||||
if nsec >= NSEC_PER_SEC as u32 {
|
||||
nsec -= NSEC_PER_SEC as u32;
|
||||
secs = secs.checked_add(1)?;
|
||||
}
|
||||
Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } })
|
||||
}
|
||||
|
||||
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
|
||||
let mut secs = other
|
||||
.as_secs()
|
||||
.try_into() // <- target type would be `libc::time_t`
|
||||
.ok()
|
||||
.and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
|
||||
|
||||
// Similar to above, nanos can't overflow.
|
||||
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
|
||||
if nsec < 0 {
|
||||
nsec += NSEC_PER_SEC as i32;
|
||||
secs = secs.checked_sub(1)?;
|
||||
}
|
||||
Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } })
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Timespec {
|
||||
fn eq(&self, other: &Timespec) -> bool {
|
||||
self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Timespec {}
|
||||
|
||||
impl PartialOrd for Timespec {
|
||||
fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Timespec {
|
||||
fn cmp(&self, other: &Timespec) -> Ordering {
|
||||
let me = (self.t.tv_sec, self.t.tv_nsec);
|
||||
let other = (other.t.tv_sec, other.t.tv_nsec);
|
||||
me.cmp(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Timespec {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.t.tv_sec.hash(state);
|
||||
self.t.tv_nsec.hash(state);
|
||||
}
|
||||
}
|
||||
mod inner {
|
||||
use crate::fmt;
|
||||
use crate::sys::cvt;
|
||||
use crate::time::Duration;
|
||||
|
||||
use super::Timespec;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Instant {
|
||||
t: Timespec,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct SystemTime {
|
||||
t: Timespec,
|
||||
}
|
||||
|
||||
pub const UNIX_EPOCH: SystemTime =
|
||||
SystemTime { t: Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } };
|
||||
|
||||
impl Instant {
|
||||
pub fn now() -> Instant {
|
||||
Instant { t: now(libc::CLOCK_MONOTONIC) }
|
||||
}
|
||||
|
||||
pub const fn zero() -> Instant {
|
||||
Instant { t: Timespec::zero() }
|
||||
}
|
||||
|
||||
pub fn actually_monotonic() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
|
||||
self.t.sub_timespec(&other.t).ok()
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant { t: self.t.checked_add_duration(other)? })
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
|
||||
Some(Instant { t: self.t.checked_sub_duration(other)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Instant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Instant")
|
||||
.field("tv_sec", &self.t.t.tv_sec)
|
||||
.field("tv_nsec", &self.t.t.tv_nsec)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
pub fn now() -> SystemTime {
|
||||
SystemTime { t: now(libc::CLOCK_REALTIME) }
|
||||
}
|
||||
|
||||
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
|
||||
self.t.sub_timespec(&other.t)
|
||||
}
|
||||
|
||||
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_add_duration(other)? })
|
||||
}
|
||||
|
||||
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
|
||||
Some(SystemTime { t: self.t.checked_sub_duration(other)? })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libc::timespec> for SystemTime {
|
||||
fn from(t: libc::timespec) -> SystemTime {
|
||||
SystemTime { t: Timespec { t: t } }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SystemTime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SystemTime")
|
||||
.field("tv_sec", &self.t.t.tv_sec)
|
||||
.field("tv_nsec", &self.t.t.tv_nsec)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub type clock_t = libc::c_int;
|
||||
|
||||
fn now(clock: clock_t) -> Timespec {
|
||||
let mut t = Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } };
|
||||
cvt(unsafe { libc::clock_gettime(clock, &mut t.t) }).unwrap();
|
||||
t
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user