diff options
39 files changed, 1031 insertions, 780 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 00d0095c..eca3d347 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -14,7 +14,7 @@ task: setup_script: - pkg install -y curl - curl https://sh.rustup.rs -sSf --output rustup.sh - - sh rustup.sh -y --default-toolchain 1.24.1 + - sh rustup.sh -y --default-toolchain 1.25.0 - $HOME/.cargo/bin/rustup target add i686-unknown-freebsd amd64_test_script: - . $HOME/.cargo/env diff --git a/.travis.yml b/.travis.yml index 7b0270de..a667201a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,71 +18,71 @@ matrix: # week. Additionally they're moved to the front of the line to get them in # the Travis OS X build queue first. - env: TARGET="aarch64-apple-ios;armv7-apple-ios;armv7s-apple-ios;i386-apple-ios;x86_64-apple-ios" DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 os: osx # Mac builds # These are also moved to be first because they wait in a long queue with # Travis - env: TARGET=i686-apple-darwin - rust: 1.24.1 + rust: 1.25.0 os: osx - env: TARGET=x86_64-apple-darwin - rust: 1.24.1 + rust: 1.25.0 os: osx # Android - env: TARGET=aarch64-linux-android DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=arm-linux-androideabi DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=i686-linux-android DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=x86_64-linux-android DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 # Linux - env: TARGET=aarch64-unknown-linux-gnu - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=arm-unknown-linux-gnueabi - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=arm-unknown-linux-musleabi DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=armv7-unknown-linux-gnueabihf - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=i686-unknown-linux-gnu - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=i686-unknown-linux-musl - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=mips-unknown-linux-gnu - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=mips64-unknown-linux-gnuabi64 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=mips64el-unknown-linux-gnuabi64 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=mipsel-unknown-linux-gnu - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=powerpc-unknown-linux-gnu DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=powerpc64-unknown-linux-gnu - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=powerpc64le-unknown-linux-gnu - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=x86_64-unknown-linux-gnu - rust: 1.24.1 + rust: 1.25.0 - env: TARGET=x86_64-unknown-linux-musl - rust: 1.24.1 + rust: 1.25.0 # *BSD # FreeBSD i686 and x86_64 use Cirrus instead of Travis # - env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1 # - env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1 - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 - rust: 1.24.1 + rust: 1.25.0 # Make sure stable is always working too - env: TARGET=x86_64-unknown-linux-gnu diff --git a/CHANGELOG.md b/CHANGELOG.md index 3420681b..b3d7089e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,25 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `MSG_WAITALL` to `MsgFlags` in `sys::socket`. ([#1079](https://github.com/nix-rust/nix/pull/1079)) +- Implemented `Clone`, `Copy`, `Debug`, `Eq`, `Hash`, and `PartialEq` for most + types that support them. ([#1035](https://github.com/nix-rust/nix/pull/1035)) +- Added `copy_file_range` wrapper + ([#1069](https://github.com/nix-rust/nix/pull/1069)) +- Add `mkdirat`. + ([#1084](https://github.com/nix-rust/nix/pull/1084)) ### Changed - Support for `ifaddrs` now present when building for Android. ([#1077](https://github.com/nix-rust/nix/pull/1077)) +- Minimum supported Rust version is now 1.25.0 + ([#1035](https://github.com/nix-rust/nix/pull/1035)) +- Now functions `statfs()` and `fstatfs()` return result with `Statfs` wrapper + ([#928](https://github.com/nix-rust/nix/pull/928)) ### Fixed +- Enabled `sched_yield` for all nix hosts. + ([#1090](https://github.com/nix-rust/nix/pull/1090)) + ### Removed ## [0.14.1] - 2019-06-06 @@ -16,7 +16,7 @@ exclude = [ ] [dependencies] -libc = { git = "https://github.com/rust-lang/libc" } +libc = { git = "https://github.com/rust-lang/libc", features = [ "extra_traits" ] } bitflags = "1.0" cfg-if = "0.1.2" void = "1.0.2" @@ -28,7 +28,7 @@ cc = "1" bytes = "0.4.8" lazy_static = "1.2" rand = ">= 0.6, < 0.7" -tempfile = "3.0.5" +tempfile = ">= 3.0.5, < 3.0.9" [target.'cfg(any(target_os = "android", target_os = "linux"))'.dev-dependencies] caps = "0.3.1" @@ -44,8 +44,7 @@ limitations. Support for platforms is split into three tiers: *do not* block the inclusion of new code. Testing may be run, but failures in tests don't block the inclusion of new code. -The following targets are all supported by nix on Rust 1.24.1 or newer (unless -otherwise noted): +The following targets are supported by `nix`: Tier 1: * aarch64-unknown-linux-gnu @@ -84,6 +83,8 @@ Tier 2: ## Usage +`nix` requires Rust 1.25.0 or newer. + To use `nix`, first add this to your `Cargo.toml`: ```toml @@ -3,7 +3,7 @@ use errno::Errno; use fcntl::{self, OFlag}; use libc; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; -use std::{ffi, fmt, ptr}; +use std::{ffi, ptr}; use sys; #[cfg(target_os = "linux")] @@ -25,9 +25,9 @@ use libc::{dirent, readdir_r}; /// * returns entries for `.` (current directory) and `..` (parent directory). /// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc /// does). +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Dir( - // This could be ptr::NonNull once nix requires Rust 1.25. - *mut libc::DIR + ptr::NonNull<libc::DIR> ); impl Dir { @@ -59,7 +59,8 @@ impl Dir { unsafe { libc::close(fd) }; return Err(e); }; - Ok(Dir(d)) + // Always guaranteed to be non-null by the previous check + Ok(Dir(ptr::NonNull::new(d).unwrap())) } /// Returns an iterator of `Result<Entry>` which rewinds when finished. @@ -78,25 +79,17 @@ unsafe impl Send for Dir {} impl AsRawFd for Dir { fn as_raw_fd(&self) -> RawFd { - unsafe { libc::dirfd(self.0) } - } -} - -impl fmt::Debug for Dir { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Dir") - .field("fd", &self.as_raw_fd()) - .finish() + unsafe { libc::dirfd(self.0.as_ptr()) } } } impl Drop for Dir { fn drop(&mut self) { - unsafe { libc::closedir(self.0) }; + unsafe { libc::closedir(self.0.as_ptr()) }; } } -#[derive(Debug)] +#[derive(Debug, Eq, Hash, PartialEq)] pub struct Iter<'d>(&'d mut Dir); impl<'d> Iterator for Iter<'d> { @@ -111,7 +104,7 @@ impl<'d> Iterator for Iter<'d> { // Probably fine here too then. let mut ent: Entry = Entry(::std::mem::uninitialized()); let mut result = ptr::null_mut(); - if let Err(e) = Errno::result(readdir_r((self.0).0, &mut ent.0, &mut result)) { + if let Err(e) = Errno::result(readdir_r((self.0).0.as_ptr(), &mut ent.0, &mut result)) { return Some(Err(e)); } if result == ptr::null_mut() { @@ -125,17 +118,17 @@ impl<'d> Iterator for Iter<'d> { impl<'d> Drop for Iter<'d> { fn drop(&mut self) { - unsafe { libc::rewinddir((self.0).0) } + unsafe { libc::rewinddir((self.0).0.as_ptr()) } } } /// A directory entry, similar to `std::fs::DirEntry`. /// /// Note that unlike the std version, this may represent the `.` or `..` entries. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct Entry(dirent); -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum Type { Fifo, CharacterDevice, @@ -198,13 +191,3 @@ impl Entry { } } } - -impl fmt::Debug for Entry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Entry") - .field("ino", &self.ino()) - .field("file_name", &self.file_name()) - .field("file_type", &self.file_type()) - .finish() - } -} diff --git a/src/fcntl.rs b/src/fcntl.rs index 40f4cf94..3d932a53 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -8,6 +8,8 @@ use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; #[cfg(any(target_os = "android", target_os = "linux"))] +use std::ptr; // For splice and copy_file_range +#[cfg(any(target_os = "android", target_os = "linux"))] use sys::uio::IoVec; // For vmsplice libc_bitflags!{ @@ -214,7 +216,7 @@ libc_bitflags!( } ); -#[allow(missing_debug_implementations)] +#[derive(Debug, Eq, Hash, PartialEq)] pub enum FcntlArg<'a> { F_DUPFD(RawFd), F_DUPFD_CLOEXEC(RawFd), @@ -277,8 +279,7 @@ pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { Errno::result(res) } -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum FlockArg { LockShared, LockExclusive, @@ -326,15 +327,70 @@ libc_bitflags! { } } +/// Copy a range of data from one file to another +/// +/// The `copy_file_range` system call performs an in-kernel copy between +/// file descriptors `fd_in` and `fd_out` without the additional cost of +/// transferring data from the kernel to user space and then back into the +/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to +/// file descriptor `fd_out`, overwriting any data that exists within the +/// requested range of the target file. +/// +/// If the `off_in` and/or `off_out` arguments are used, the values +/// will be mutated to reflect the new position within the file after +/// copying. If they are not used, the relevant filedescriptors will be seeked +/// to the new position. +/// +/// On successful completion the number of bytes actually copied will be +/// returned. +#[cfg(any(target_os = "android", target_os = "linux"))] +pub fn copy_file_range( + fd_in: RawFd, + off_in: Option<&mut libc::loff_t>, + fd_out: RawFd, + off_out: Option<&mut libc::loff_t>, + len: usize, +) -> Result<usize> { + let off_in = off_in + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + let off_out = off_out + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + + let ret = unsafe { + libc::syscall( + libc::SYS_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + 0, + ) + }; + Errno::result(ret).map(|r| r as usize) +} + #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>, - fd_out: RawFd, off_out: Option<&mut libc::loff_t>, - len: usize, flags: SpliceFFlags) -> Result<usize> { - use std::ptr; - let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut()); - let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut()); - - let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) }; +pub fn splice( + fd_in: RawFd, + off_in: Option<&mut libc::loff_t>, + fd_out: RawFd, + off_out: Option<&mut libc::loff_t>, + len: usize, + flags: SpliceFFlags, +) -> Result<usize> { + let off_in = off_in + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + let off_out = off_out + .map(|offset| offset as *mut libc::loff_t) + .unwrap_or(ptr::null_mut()); + + let ret = unsafe { + libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) + }; Errno::result(ret).map(|r| r as usize) } diff --git a/src/ifaddrs.rs b/src/ifaddrs.rs index c9790773..12b59bcc 100644 --- a/src/ifaddrs.rs +++ b/src/ifaddrs.rs @@ -15,7 +15,7 @@ use sys::socket::SockAddr; use net::if_::*; /// Describes a single address for an interface as returned by `getifaddrs`. -#[derive(Clone, Eq, Hash, PartialEq, Debug)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct InterfaceAddress { /// Name of the network interface pub interface_name: String, @@ -62,8 +62,6 @@ pub mod net; pub mod poll; #[deny(missing_docs)] pub mod pty; -#[cfg(any(target_os = "android", - target_os = "linux"))] pub mod sched; pub mod sys; // This can be implemented for other platforms as soon as libc @@ -98,7 +96,7 @@ pub type Result<T> = result::Result<T, Error>; /// error has a corresponding errno (usually the one from the /// underlying OS) to which it can be mapped in addition to /// implementing other common traits. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Error { Sys(Errno), InvalidPath, diff --git a/src/mqueue.rs b/src/mqueue.rs index 87be6532..b958b71c 100644 --- a/src/mqueue.rs +++ b/src/mqueue.rs @@ -29,22 +29,11 @@ libc_bitflags!{ } #[repr(C)] -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct MqAttr { mq_attr: libc::mq_attr, } -impl PartialEq<MqAttr> for MqAttr { - fn eq(&self, other: &MqAttr) -> bool { - let self_attr = self.mq_attr; - let other_attr = other.mq_attr; - self_attr.mq_flags == other_attr.mq_flags && self_attr.mq_maxmsg == other_attr.mq_maxmsg && - self_attr.mq_msgsize == other_attr.mq_msgsize && - self_attr.mq_curmsgs == other_attr.mq_curmsgs - } -} - impl MqAttr { pub fn new(mq_flags: c_long, mq_maxmsg: c_long, diff --git a/src/poll.rs b/src/poll.rs index 160b5bc6..c603611e 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -4,7 +4,6 @@ use sys::time::TimeSpec; #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))] use sys::signal::SigSet; use std::os::unix::io::RawFd; -use std::fmt; use libc; use Result; @@ -19,7 +18,7 @@ use errno::Errno; /// After a call to `poll` or `ppoll`, the events that occured can be /// retrieved by calling [`revents()`](#method.revents) on the `PollFd`. #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct PollFd { pollfd: libc::pollfd, } @@ -43,23 +42,6 @@ impl PollFd { } } -impl fmt::Debug for PollFd { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let pfd = self.pollfd; - let mut ds = f.debug_struct("PollFd"); - ds.field("fd", &pfd.fd); - match PollFlags::from_bits(pfd.events) { - None => ds.field("events", &pfd.events), - Some(ef) => ds.field("events", &ef), - }; - match PollFlags::from_bits(pfd.revents) { - None => ds.field("revents", &pfd.revents), - Some(ef) => ds.field("revents", &ef), - }; - ds.finish() - } -} - libc_bitflags! { /// These flags define the different events that can be monitored by `poll` and `ppoll` pub struct PollFlags: libc::c_short { @@ -18,8 +18,7 @@ use errno::Errno; /// /// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user /// must manually close the file descriptors. -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct OpenptyResult { /// The master port in a virtual pty pair pub master: RawFd, @@ -45,7 +44,7 @@ pub struct ForkptyResult { /// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY /// functions are given the correct file descriptor. Additionally this type implements `Drop`, /// so that when it's consumed or goes out of scope, it's automatically cleaned-up. -#[derive(Debug)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct PtyMaster(RawFd); impl AsRawFd for PtyMaster { diff --git a/src/sched.rs b/src/sched.rs index d381cbb5..064fc29d 100644 --- a/src/sched.rs +++ b/src/sched.rs @@ -1,132 +1,147 @@ -use std::mem; -use std::os::unix::io::RawFd; -use std::option::Option; -use libc::{self, c_int, c_void}; -use {Error, Result}; -use errno::Errno; -use ::unistd::Pid; - -// For some functions taking with a parameter of type CloneFlags, -// only a subset of these flags have an effect. -libc_bitflags!{ - pub struct CloneFlags: c_int { - CLONE_VM; - CLONE_FS; - CLONE_FILES; - CLONE_SIGHAND; - CLONE_PTRACE; - CLONE_VFORK; - CLONE_PARENT; - CLONE_THREAD; - CLONE_NEWNS; - CLONE_SYSVSEM; - CLONE_SETTLS; - CLONE_PARENT_SETTID; - CLONE_CHILD_CLEARTID; - CLONE_DETACHED; - CLONE_UNTRACED; - CLONE_CHILD_SETTID; - CLONE_NEWCGROUP; - CLONE_NEWUTS; - CLONE_NEWIPC; - CLONE_NEWUSER; - CLONE_NEWPID; - CLONE_NEWNET; - CLONE_IO; +use libc; +use {Errno, Result}; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::sched_linux_like::*; + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod sched_linux_like { + use errno::Errno; + use libc::{self, c_int, c_void}; + use std::mem; + use std::option::Option; + use std::os::unix::io::RawFd; + use unistd::Pid; + use {Error, Result}; + + // For some functions taking with a parameter of type CloneFlags, + // only a subset of these flags have an effect. + libc_bitflags! { + pub struct CloneFlags: c_int { + CLONE_VM; + CLONE_FS; + CLONE_FILES; + CLONE_SIGHAND; + CLONE_PTRACE; + CLONE_VFORK; + CLONE_PARENT; + CLONE_THREAD; + CLONE_NEWNS; + CLONE_SYSVSEM; + CLONE_SETTLS; + CLONE_PARENT_SETTID; + CLONE_CHILD_CLEARTID; + CLONE_DETACHED; + CLONE_UNTRACED; + CLONE_CHILD_SETTID; + CLONE_NEWCGROUP; + CLONE_NEWUTS; + CLONE_NEWIPC; + CLONE_NEWUSER; + CLONE_NEWPID; + CLONE_NEWNET; + CLONE_IO; + } } -} -pub type CloneCb<'a> = Box<FnMut() -> isize + 'a>; - -#[repr(C)] -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] -pub struct CpuSet { - cpu_set: libc::cpu_set_t, -} + pub type CloneCb<'a> = Box<FnMut() -> isize + 'a>; -impl CpuSet { - pub fn new() -> CpuSet { - CpuSet { cpu_set: unsafe { mem::zeroed() } } + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct CpuSet { + cpu_set: libc::cpu_set_t, } - pub fn is_set(&self, field: usize) -> Result<bool> { - if field >= 8 * mem::size_of::<libc::cpu_set_t>() { - Err(Error::Sys(Errno::EINVAL)) - } else { - Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) }) + impl CpuSet { + pub fn new() -> CpuSet { + CpuSet { + cpu_set: unsafe { mem::zeroed() }, + } } - } - pub fn set(&mut self, field: usize) -> Result<()> { - if field >= 8 * mem::size_of::<libc::cpu_set_t>() { - Err(Error::Sys(Errno::EINVAL)) - } else { - Ok(unsafe { libc::CPU_SET(field, &mut self.cpu_set) }) + pub fn is_set(&self, field: usize) -> Result<bool> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) }) + } } - } - pub fn unset(&mut self, field: usize) -> Result<()> { - if field >= 8 * mem::size_of::<libc::cpu_set_t>() { - Err(Error::Sys(Errno::EINVAL)) - } else { - Ok(unsafe { libc::CPU_CLR(field, &mut self.cpu_set) }) + pub fn set(&mut self, field: usize) -> Result<()> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_SET(field, &mut self.cpu_set) }) + } } - } -} -pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> { - let res = unsafe { - libc::sched_setaffinity(pid.into(), - mem::size_of::<CpuSet>() as libc::size_t, - &cpuset.cpu_set) - }; + pub fn unset(&mut self, field: usize) -> Result<()> { + if field >= 8 * mem::size_of::<libc::cpu_set_t>() { + Err(Error::Sys(Errno::EINVAL)) + } else { + Ok(unsafe { libc::CPU_CLR(field, &mut self.cpu_set) }) + } + } + } - Errno::result(res).map(drop) -} + pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> { + let res = unsafe { + libc::sched_setaffinity( + pid.into(), + mem::size_of::<CpuSet>() as libc::size_t, + &cpuset.cpu_set, + ) + }; -/// Explicitly yield the processor to other threads. -/// -/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html) -pub fn sched_yield() -> Result<()> { - let res = unsafe { - libc::sched_yield() - }; + Errno::result(res).map(drop) + } - Errno::result(res).map(drop) -} + pub fn clone( + mut cb: CloneCb, + stack: &mut [u8], + flags: CloneFlags, + signal: Option<c_int>, + ) -> Result<Pid> { + extern "C" fn callback(data: *mut CloneCb) -> c_int { + let cb: &mut CloneCb = unsafe { &mut *data }; + (*cb)() as c_int + } -pub fn clone(mut cb: CloneCb, - stack: &mut [u8], - flags: CloneFlags, - signal: Option<c_int>) - -> Result<Pid> { - extern "C" fn callback(data: *mut CloneCb) -> c_int { - let cb: &mut CloneCb = unsafe { &mut *data }; - (*cb)() as c_int + let res = unsafe { + let combined = flags.bits() | signal.unwrap_or(0); + let ptr = stack.as_mut_ptr().offset(stack.len() as isize); + let ptr_aligned = ptr.offset((ptr as usize % 16) as isize * -1); + libc::clone( + mem::transmute( + callback as extern "C" fn(*mut Box<::std::ops::FnMut() -> isize>) -> i32, + ), + ptr_aligned as *mut c_void, + combined, + &mut cb as *mut _ as *mut c_void, + ) + }; + + Errno::result(res).map(Pid::from_raw) } - let res = unsafe { - let combined = flags.bits() | signal.unwrap_or(0); - let ptr = stack.as_mut_ptr().offset(stack.len() as isize); - let ptr_aligned = ptr.offset((ptr as usize % 16) as isize * -1); - libc::clone(mem::transmute(callback as extern "C" fn(*mut Box<::std::ops::FnMut() -> isize>) -> i32), - ptr_aligned as *mut c_void, - combined, - &mut cb as *mut _ as *mut c_void) - }; - - Errno::result(res).map(Pid::from_raw) -} + pub fn unshare(flags: CloneFlags) -> Result<()> { + let res = unsafe { libc::unshare(flags.bits()) }; + + Errno::result(res).map(drop) + } -pub fn unshare(flags: CloneFlags) -> Result<()> { - let res = unsafe { libc::unshare(flags.bits()) }; + pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { + let res = unsafe { libc::setns(fd, nstype.bits()) }; - Errno::result(res).map(drop) + Errno::result(res).map(drop) + } } -pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { - let res = unsafe { libc::setns(fd, nstype.bits()) }; +/// Explicitly yield the processor to other threads. +/// +/// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html) +pub fn sched_yield() -> Result<()> { + let res = unsafe { libc::sched_yield() }; Errno::result(res).map(drop) } diff --git a/src/sys/aio.rs b/src/sys/aio.rs index c54c2e31..40fa4e15 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -80,7 +80,7 @@ libc_enum! { /// Return values for [`AioCb::cancel`](struct.AioCb.html#method.cancel) and /// [`aio_cancel_all`](fn.aio_cancel_all.html) #[repr(i32)] -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum AioCancelStat { /// All outstanding requests were canceled AioCanceled = libc::AIO_CANCELED, @@ -1021,13 +1021,7 @@ pub fn aio_suspend(list: &[&AioCb], timeout: Option<TimeSpec>) -> Result<()> { impl<'a> Debug for AioCb<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("AioCb") - .field("aio_fildes", &self.aiocb.aio_fildes) - .field("aio_offset", &self.aiocb.aio_offset) - .field("aio_buf", &self.aiocb.aio_buf) - .field("aio_nbytes", &self.aiocb.aio_nbytes) - .field("aio_lio_opcode", &self.aiocb.aio_lio_opcode) - .field("aio_reqprio", &self.aiocb.aio_reqprio) - .field("aio_sigevent", &SigEvent::from(&self.aiocb.aio_sigevent)) + .field("aiocb", &self.aiocb) .field("mutable", &self.mutable) .field("in_progress", &self.in_progress) .finish() diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs index 3ed1dd72..fef6f4e3 100644 --- a/src/sys/epoll.rs +++ b/src/sys/epoll.rs @@ -42,8 +42,7 @@ libc_bitflags!{ } } -#[allow(missing_debug_implementations)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(C)] pub struct EpollEvent { event: libc::epoll_event, diff --git a/src/sys/event.rs b/src/sys/event.rs index f1eaa0d1..8cd7372f 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -12,9 +12,8 @@ use std::ptr; use std::mem; // Redefine kevent in terms of programmer-friendly enums and bitfields. -#[derive(Clone, Copy)] #[repr(C)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct KEvent { kevent: libc::kevent, } diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 88c7251d..d3c2f92b 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -71,7 +71,14 @@ pub mod socket; pub mod stat; -#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "openbsd" +))] pub mod statfs; pub mod statvfs; diff --git a/src/sys/quota.rs b/src/sys/quota.rs index 14c04629..8946fca2 100644 --- a/src/sys/quota.rs +++ b/src/sys/quota.rs @@ -96,8 +96,7 @@ libc_bitflags!( /// Wrapper type for `if_dqblk` // FIXME: Change to repr(transparent) #[repr(C)] -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Dqblk(libc::dqblk); impl Default for Dqblk { diff --git a/src/sys/select.rs b/src/sys/select.rs index 95b6b148..1b518e29 100644 --- a/src/sys/select.rs +++ b/src/sys/select.rs @@ -11,8 +11,7 @@ pub use libc::FD_SETSIZE; // FIXME: Change to repr(transparent) once it's stable #[repr(C)] -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct FdSet(libc::fd_set); impl FdSet { diff --git a/src/sys/sendfile.rs b/src/sys/sendfile.rs index 1190518d..a47d8962 100644 --- a/src/sys/sendfile.rs +++ b/src/sys/sendfile.rs @@ -38,7 +38,7 @@ cfg_if! { target_os = "macos"))] { use sys::uio::IoVec; - #[allow(missing_debug_implementations)] + #[derive(Clone, Debug, Eq, Hash, PartialEq)] struct SendfileHeaderTrailer<'a>( libc::sf_hdtr, Option<Vec<IoVec<&'a [u8]>>>, diff --git a/src/sys/signal.rs b/src/sys/signal.rs index a49b273f..1013a77f 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -265,8 +265,7 @@ const SIGNALS: [Signal; 31] = [ pub const NSIG: libc::c_int = 32; -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SignalIterator { next: usize, } @@ -328,8 +327,7 @@ libc_enum! { } } -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SigSet { sigset: libc::sigset_t } @@ -427,7 +425,7 @@ impl AsRef<libc::sigset_t> for SigSet { /// A signal handler. #[allow(unknown_lints)] -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SigHandler { /// Default signal handling. SigDfl, @@ -441,8 +439,7 @@ pub enum SigHandler { } /// Action to take on receipt of a signal. Corresponds to `sigaction`. -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SigAction { sigaction: libc::sigaction } @@ -683,7 +680,7 @@ pub type type_of_thread_id = libc::pid_t; // sigval is actually a union of a int and a void*. But it's never really used // as a pointer, because neither libc nor the kernel ever dereference it. nix // therefore presents it as an intptr_t, which is how kevent uses it. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SigevNotify { /// No notification will be delivered SigevNone, @@ -710,7 +707,6 @@ mod sigevent { use libc; use std::mem; use std::ptr; - use std::fmt::{self, Debug}; use super::SigevNotify; #[cfg(any(target_os = "freebsd", target_os = "linux"))] use super::type_of_thread_id; @@ -718,7 +714,7 @@ mod sigevent { /// Used to request asynchronous notification of the completion of certain /// events, such as POSIX AIO and timers. #[repr(C)] - #[derive(Clone, Copy)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SigEvent { sigevent: libc::sigevent } @@ -788,28 +784,6 @@ mod sigevent { } } - impl Debug for SigEvent { - #[cfg(any(target_os = "freebsd", target_os = "linux"))] - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("SigEvent") - .field("sigev_notify", &self.sigevent.sigev_notify) - .field("sigev_signo", &self.sigevent.sigev_signo) - .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) - .field("sigev_notify_thread_id", - &self.sigevent.sigev_notify_thread_id) - .finish() - } - - #[cfg(not(any(target_os = "freebsd", target_os = "linux")))] - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("SigEvent") - .field("sigev_notify", &self.sigevent.sigev_notify) - .field("sigev_signo", &self.sigevent.sigev_signo) - .field("sigev_value", &self.sigevent.sigev_value.sival_ptr) - .finish() - } - } - impl<'a> From<&'a libc::sigevent> for SigEvent { fn from(sigevent: &libc::sigevent) -> Self { SigEvent{ sigevent: *sigevent } diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index fc24bd79..e6c5f8ae 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -2,8 +2,9 @@ use super::sa_family_t; use {Error, Result, NixPath}; use errno::Errno; use libc; -use std::{fmt, hash, mem, net, ptr, slice}; +use std::{fmt, mem, net, ptr, slice}; use std::ffi::OsStr; +use std::hash::{Hash, Hasher}; use std::path::Path; use std::os::unix::ffi::OsStrExt; #[cfg(any(target_os = "android", target_os = "linux"))] @@ -245,7 +246,7 @@ impl AddressFamily { } } -#[derive(Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum InetAddr { V4(libc::sockaddr_in), V6(libc::sockaddr_in6), @@ -331,52 +332,6 @@ impl InetAddr { } } -impl PartialEq for InetAddr { - fn eq(&self, other: &InetAddr) -> bool { - match (*self, *other) { - (InetAddr::V4(ref a), InetAddr::V4(ref b)) => { - a.sin_port == b.sin_port && - a.sin_addr.s_addr == b.sin_addr.s_addr - } - (InetAddr::V6(ref a), InetAddr::V6(ref b)) => { - a.sin6_port == b.sin6_port && - a.sin6_addr.s6_addr == b.sin6_addr.s6_addr && - a.sin6_flowinfo == b.sin6_flowinfo && - a.sin6_scope_id == b.sin6_scope_id - } - _ => false, - } - } -} - -impl Eq for InetAddr { -} - -impl hash::Hash for InetAddr { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - match *self { - InetAddr::V4(ref a) => { - ( a.sin_family, - a.sin_port, - a.sin_addr.s_addr ).hash(s) - } - InetAddr::V6(ref a) => { - ( a.sin6_family, - a.sin6_port, - &a.sin6_addr.s6_addr, - a.sin6_flowinfo, - a.sin6_scope_id ).hash(s) - } - } - } -} - -impl Clone for InetAddr { - fn clone(&self) -> InetAddr { - *self - } -} - impl fmt::Display for InetAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -386,18 +341,12 @@ impl fmt::Display for InetAddr { } } -impl fmt::Debug for InetAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - /* * * ===== IpAddr ===== * */ -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr), @@ -442,19 +391,13 @@ impl fmt::Display for IpAddr { } } -impl fmt::Debug for IpAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - /* * * ===== Ipv4Addr ===== * */ -#[derive(Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Ipv4Addr(pub libc::in_addr); impl Ipv4Addr { @@ -487,28 +430,6 @@ impl Ipv4Addr { } } -impl PartialEq for Ipv4Addr { - fn eq(&self, other: &Ipv4Addr) -> bool { - self.0.s_addr == other.0.s_addr - } -} - -impl Eq for Ipv4Addr { -} - -impl hash::Hash for Ipv4Addr { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - let saddr = self.0.s_addr; - saddr.hash(s) - } -} - -impl Clone for Ipv4Addr { - fn clone(&self) -> Ipv4Addr { - *self - } -} - impl fmt::Display for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let octets = self.octets(); @@ -516,19 +437,13 @@ impl fmt::Display for Ipv4Addr { } } -impl fmt::Debug for Ipv4Addr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - /* * * ===== Ipv6Addr ===== * */ -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Ipv6Addr(pub libc::in6_addr); // Note that IPv6 addresses are stored in big endian order on all architectures. @@ -576,18 +491,6 @@ impl fmt::Display for Ipv6Addr { } } -impl fmt::Debug for Ipv6Addr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -/* - * - * ===== UnixAddr ===== - * - */ - /// A wrapper around `sockaddr_un`. /// /// This also tracks the length of `sun_path` address (excluding @@ -596,7 +499,7 @@ impl fmt::Debug for Ipv6Addr { /// does not require that `sun_len` include the terminating null even for normal /// sockets. Note that the actual sockaddr length is greater by /// `offset_of!(libc::sockaddr_un, sun_path)` -#[derive(Copy)] +#[derive(Clone, Copy, Debug)] pub struct UnixAddr(pub libc::sockaddr_un, pub usize); impl UnixAddr { @@ -648,7 +551,7 @@ impl UnixAddr { ret.sun_path.as_mut_ptr().offset(1) as *mut u8, path.len()); - Ok(UnixAddr(ret, path.len() + 1)) + Ok(UnixAddr(ret, ret.sun_path.len())) } } @@ -688,27 +591,6 @@ impl UnixAddr { } } -impl PartialEq for UnixAddr { - fn eq(&self, other: &UnixAddr) -> bool { - self.sun_path() == other.sun_path() - } -} - -impl Eq for UnixAddr { -} - -impl hash::Hash for UnixAddr { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - ( self.0.sun_family, self.sun_path() ).hash(s) - } -} - -impl Clone for UnixAddr { - fn clone(&self) -> UnixAddr { - *self - } -} - impl fmt::Display for UnixAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.1 == 0 { @@ -722,20 +604,22 @@ impl fmt::Display for UnixAddr { } } -impl fmt::Debug for UnixAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) +impl PartialEq for UnixAddr { + fn eq(&self, other: &UnixAddr) -> bool { + self.sun_path() == other.sun_path() } } -/* - * - * ===== Sock addr ===== - * - */ +impl Eq for UnixAddr {} + +impl Hash for UnixAddr { + fn hash<H: Hasher>(&self, s: &mut H) { + ( self.0.sun_family, self.sun_path() ).hash(s) + } +} /// Represents a socket address -#[derive(Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SockAddr { Inet(InetAddr), Unix(UnixAddr), @@ -883,68 +767,6 @@ impl SockAddr { } } -impl PartialEq for SockAddr { - fn eq(&self, other: &SockAddr) -> bool { - match (*self, *other) { - (SockAddr::Inet(ref a), SockAddr::Inet(ref b)) => { - a == b - } - (SockAddr::Unix(ref a), SockAddr::Unix(ref b)) => { - a == b - } - #[cfg(any(target_os = "android", target_os = "linux"))] - (SockAddr::Netlink(ref a), SockAddr::Netlink(ref b)) => { - a == b - } - #[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd"))] - (SockAddr::Link(ref a), SockAddr::Link(ref b)) => { - a == b - } - _ => false, - } - } -} - -impl Eq for SockAddr { -} - -impl hash::Hash for SockAddr { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - match *self { - SockAddr::Inet(ref a) => a.hash(s), - SockAddr::Unix(ref a) => a.hash(s), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Netlink(ref a) => a.hash(s), - #[cfg(any(target_os = "android", target_os = "linux"))] - SockAddr::Alg(ref a) => a.hash(s), - #[cfg(any(target_os = "ios", target_os = "macos"))] - SockAddr::SysControl(ref a) => a.hash(s), - #[cfg(any(target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd"))] - SockAddr::Link(ref ether_addr) => ether_addr.hash(s) - } - } -} - -impl Clone for SockAddr { - fn clone(&self) -> SockAddr { - *self - } -} - impl fmt::Display for SockAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -974,30 +796,10 @@ pub mod netlink { use ::sys::socket::addr::AddressFamily; use libc::{sa_family_t, sockaddr_nl}; use std::{fmt, mem}; - use std::hash::{Hash, Hasher}; - #[derive(Copy, Clone)] + #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct NetlinkAddr(pub sockaddr_nl); - // , PartialEq, Eq, Debug, Hash - impl PartialEq for NetlinkAddr { - fn eq(&self, other: &Self) -> bool { - let (inner, other) = (self.0, other.0); - (inner.nl_family, inner.nl_pid, inner.nl_groups) == - (other.nl_family, other.nl_pid, other.nl_groups) - } - } - - impl Eq for NetlinkAddr {} - - impl Hash for NetlinkAddr { - fn hash<H: Hasher>(&self, s: &mut H) { - let inner = self.0; - (inner.nl_family, inner.nl_pid, inner.nl_groups).hash(s); - } - } - - impl NetlinkAddr { pub fn new(pid: u32, groups: u32) -> NetlinkAddr { let mut addr: sockaddr_nl = unsafe { mem::zeroed() }; @@ -1022,12 +824,6 @@ pub mod netlink { write!(f, "pid: {} groups: {}", self.pid(), self.groups()) } } - - impl fmt::Debug for NetlinkAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } - } } #[cfg(any(target_os = "android", target_os = "linux"))] @@ -1098,11 +894,13 @@ pub mod sys_control { use ::sys::socket::addr::AddressFamily; use libc::{self, c_uchar}; use std::{fmt, mem}; - use std::hash::{Hash, Hasher}; use std::os::unix::io::RawFd; use {Errno, Error, Result}; + // FIXME: Move type into `libc` #[repr(C)] + #[derive(Clone, Copy)] + #[allow(missing_debug_implementations)] pub struct ctl_ioc_info { pub ctl_id: u32, pub ctl_name: [c_uchar; MAX_KCTL_NAME], @@ -1114,28 +912,10 @@ pub mod sys_control { ioctl_readwrite!(ctl_info, CTL_IOC_MAGIC, CTL_IOC_INFO, ctl_ioc_info); - #[derive(Copy, Clone)] #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SysControlAddr(pub libc::sockaddr_ctl); - impl PartialEq for SysControlAddr { - fn eq(&self, other: &Self) -> bool { - let (inner, other) = (self.0, other.0); - (inner.sc_id, inner.sc_unit) == - (other.sc_id, other.sc_unit) - } - } - - impl Eq for SysControlAddr {} - - impl Hash for SysControlAddr { - fn hash<H: Hasher>(&self, s: &mut H) { - let inner = self.0; - (inner.sc_id, inner.sc_unit).hash(s); - } - } - - impl SysControlAddr { pub fn new(id: u32, unit: u32) -> SysControlAddr { let addr = libc::sockaddr_ctl { @@ -1178,27 +958,15 @@ pub mod sys_control { fmt::Debug::fmt(self, f) } } - - impl fmt::Debug for SysControlAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("SysControlAddr") - .field("sc_len", &self.0.sc_len) - .field("sc_family", &self.0.sc_family) - .field("ss_sysaddr", &self.0.ss_sysaddr) - .field("sc_id", &self.0.sc_id) - .field("sc_unit", &self.0.sc_unit) - .finish() - } - } } #[cfg(any(target_os = "android", target_os = "linux"))] mod datalink { - use super::{libc, hash, fmt, AddressFamily}; + use super::{libc, fmt, AddressFamily}; /// Hardware Address - #[derive(Clone, Copy)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct LinkAddr(pub libc::sockaddr_ll); impl LinkAddr { @@ -1246,26 +1014,6 @@ mod datalink { } } - impl Eq for LinkAddr {} - - impl PartialEq for LinkAddr { - fn eq(&self, other: &Self) -> bool { - let (a, b) = (self.0, other.0); - (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, - a.sll_pkttype, a.sll_halen, a.sll_addr) == - (b.sll_family, b.sll_protocol, b.sll_ifindex, b.sll_hatype, - b.sll_pkttype, b.sll_halen, b.sll_addr) - } - } - - impl hash::Hash for LinkAddr { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - let a = self.0; - (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, - a.sll_pkttype, a.sll_halen, a.sll_addr).hash(s); - } - } - impl fmt::Display for LinkAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let addr = self.addr(); @@ -1278,12 +1026,6 @@ mod datalink { addr[5]) } } - - impl fmt::Debug for LinkAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } - } } #[cfg(any(target_os = "dragonfly", @@ -1293,10 +1035,10 @@ mod datalink { target_os = "netbsd", target_os = "openbsd"))] mod datalink { - use super::{libc, hash, fmt, AddressFamily}; + use super::{libc, fmt, AddressFamily}; /// Hardware Address - #[derive(Clone, Copy)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct LinkAddr(pub libc::sockaddr_dl); impl LinkAddr { @@ -1368,52 +1110,6 @@ mod datalink { } } - impl Eq for LinkAddr {} - - impl PartialEq for LinkAddr { - #[cfg(any(target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd"))] - fn eq(&self, other: &Self) -> bool { - let (a, b) = (self.0, other.0); - (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, - a.sdl_nlen, a.sdl_alen, a.sdl_slen, &a.sdl_data[..]) == - (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, - b.sdl_nlen, b.sdl_alen, b.sdl_slen, &b.sdl_data[..]) - } - - #[cfg(target_os = "dragonfly")] - fn eq(&self, other: &Self) -> bool { - let (a, b) = (self.0, other.0); - (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, - a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route) == - (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, b.sdl_nlen, - b.sdl_alen, b.sdl_slen, b.sdl_data, b.sdl_rcf, b.sdl_route) - } - } - - impl hash::Hash for LinkAddr { - #[cfg(any(target_os = "freebsd", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd"))] - fn hash<H: hash::Hasher>(&self, s: &mut H) { - let a = self.0; - (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, - a.sdl_nlen, a.sdl_alen, a.sdl_slen, &a.sdl_data[..]).hash(s); - } - - #[cfg(target_os = "dragonfly")] - fn hash<H: hash::Hasher>(&self, s: &mut H) { - let a = self.0; - (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, - a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route).hash(s); - } - } - impl fmt::Display for LinkAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let addr = self.addr(); @@ -1426,19 +1122,15 @@ mod datalink { addr[5]) } } - - impl fmt::Debug for LinkAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } - } } #[cfg(test)] mod tests { - #[cfg(any(target_os = "dragonfly", + #[cfg(any(target_os = "android", + target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] @@ -1484,4 +1176,18 @@ mod tests { _ => { unreachable!() } }; } + + #[cfg(any(target_os = "android", target_os = "linux"))] + #[test] + fn test_abstract_sun_path() { + let name = String::from("nix\0abstract\0test"); + let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + + let sun_path1 = addr.sun_path(); + let sun_path2 = [0u8, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + assert_eq!(sun_path1.len(), sun_path2.len()); + for i in 0..sun_path1.len() { + assert_eq!(sun_path1[i], sun_path2[i]); + } + } } diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index a8631f24..0e27216f 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -5,7 +5,7 @@ use {Error, Result}; use errno::Errno; use libc::{self, c_void, c_int, iovec, socklen_t, size_t, CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN}; -use std::{fmt, mem, ptr, slice}; +use std::{mem, ptr, slice}; use std::os::unix::io::RawFd; use sys::time::TimeVal; use sys::uio::IoVec; @@ -189,7 +189,7 @@ cfg_if! { /// /// This struct is used with the `SO_PEERCRED` ancillary message for UNIX sockets. #[repr(C)] - #[derive(Clone, Copy)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct UnixCredentials(libc::ucred); impl UnixCredentials { @@ -209,13 +209,6 @@ cfg_if! { } } - impl PartialEq for UnixCredentials { - fn eq(&self, other: &Self) -> bool { - self.0.pid == other.0.pid && self.0.uid == other.0.uid && self.0.gid == other.0.gid - } - } - impl Eq for UnixCredentials {} - impl From<libc::ucred> for UnixCredentials { fn from(cred: libc::ucred) -> Self { UnixCredentials(cred) @@ -227,16 +220,6 @@ cfg_if! { self.0 } } - - impl fmt::Debug for UnixCredentials { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("UnixCredentials") - .field("pid", &self.0.pid) - .field("uid", &self.0.uid) - .field("gid", &self.0.gid) - .finish() - } - } } } @@ -244,7 +227,7 @@ cfg_if! { /// /// This is a wrapper type around `ip_mreq`. #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct IpMembershipRequest(libc::ip_mreq); impl IpMembershipRequest { @@ -259,32 +242,11 @@ impl IpMembershipRequest { } } -impl PartialEq for IpMembershipRequest { - fn eq(&self, other: &Self) -> bool { - self.0.imr_multiaddr.s_addr == other.0.imr_multiaddr.s_addr - && self.0.imr_interface.s_addr == other.0.imr_interface.s_addr - } -} -impl Eq for IpMembershipRequest {} - -impl fmt::Debug for IpMembershipRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mref = &self.0.imr_multiaddr; - let maddr = mref.s_addr; - let iref = &self.0.imr_interface; - let ifaddr = iref.s_addr; - f.debug_struct("IpMembershipRequest") - .field("imr_multiaddr", &maddr) - .field("imr_interface", &ifaddr) - .finish() - } -} - /// Request for ipv6 multicast socket operations /// /// This is a wrapper type around `ipv6_mreq`. #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Ipv6MembershipRequest(libc::ipv6_mreq); impl Ipv6MembershipRequest { @@ -297,23 +259,6 @@ impl Ipv6MembershipRequest { } } -impl PartialEq for Ipv6MembershipRequest { - fn eq(&self, other: &Self) -> bool { - self.0.ipv6mr_multiaddr.s6_addr == other.0.ipv6mr_multiaddr.s6_addr && - self.0.ipv6mr_interface == other.0.ipv6mr_interface - } -} -impl Eq for Ipv6MembershipRequest {} - -impl fmt::Debug for Ipv6MembershipRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Ipv6MembershipRequest") - .field("ipv6mr_multiaddr", &self.0.ipv6mr_multiaddr.s6_addr) - .field("ipv6mr_interface", &self.0.ipv6mr_interface) - .finish() - } -} - cfg_if! { // Darwin and DragonFly BSD always align struct cmsghdr to 32-bit only. if #[cfg(any(target_os = "dragonfly", target_os = "ios", target_os = "macos"))] { @@ -386,7 +331,7 @@ macro_rules! cmsg_space { /// let cmsg: CmsgSpace<([RawFd; 3], CmsgSpace<[RawFd; 2]>)> = CmsgSpace::new(); /// ``` #[repr(C)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct CmsgSpace<T> { _hdr: cmsghdr, _pad: [align_of_cmsg_data; 0], @@ -419,7 +364,7 @@ impl CmsgBuffer for Vec<u8> { } } -#[allow(missing_debug_implementations)] // msghdr isn't Debug +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RecvMsg<'a> { pub bytes: usize, cmsghdr: Option<&'a cmsghdr>, @@ -439,7 +384,7 @@ impl<'a> RecvMsg<'a> { } } -#[allow(missing_debug_implementations)] // msghdr isn't Debug +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct CmsgIterator<'a> { /// Control message buffer to decode from. Must adhere to cmsg alignment. cmsghdr: Option<&'a cmsghdr>, @@ -478,7 +423,7 @@ impl<'a> Iterator for CmsgIterator<'a> { // alignment issues. // // See https://github.com/nix-rust/nix/issues/999 -#[allow(missing_debug_implementations)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ControlMessageOwned { /// Received version of /// [`ControlMessage::ScmRights`][#enum.ControlMessage.html#variant.ScmRights] @@ -679,7 +624,7 @@ impl ControlMessageOwned { /// exhaustively pattern-match it. /// /// [Further reading](http://man7.org/linux/man-pages/man3/cmsg.3.html) -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ControlMessage<'a> { /// A message of type `SCM_RIGHTS`, containing an array of file /// descriptors passed between processes. @@ -742,7 +687,7 @@ pub enum ControlMessage<'a> { // An opaque structure used to prevent cmsghdr from being a public type #[doc(hidden)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct UnknownCmsg(cmsghdr, Vec<u8>); impl<'a> ControlMessage<'a> { diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 5f19e5f3..a9960100 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -173,7 +173,7 @@ macro_rules! sockopt_impl { }; (GetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty) => { - #[derive(Copy, Clone, Debug)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct $name; getsockopt_impl!($name, $level, $flag, $ty, $getter); @@ -184,14 +184,14 @@ macro_rules! sockopt_impl { }; (SetOnly, $name:ident, $level:path, $flag:path, $ty:ty, $setter:ty) => { - #[derive(Copy, Clone, Debug)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct $name; setsockopt_impl!($name, $level, $flag, $ty, $setter); }; (Both, $name:ident, $level:path, $flag:path, $ty:ty, $getter:ty, $setter:ty) => { - #[derive(Copy, Clone, Debug)] + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct $name; setsockopt_impl!($name, $level, $flag, $ty, $setter); diff --git a/src/sys/stat.rs b/src/sys/stat.rs index 1e0936ed..66c8c9dd 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -284,3 +284,11 @@ pub fn utimensat<P: ?Sized + NixPath>( Errno::result(res).map(drop) } + +pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> { + let res = path.with_nix_path(|cstr| { + unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) } + })?; + + Errno::result(res).map(drop) +} diff --git a/src/sys/statfs.rs b/src/sys/statfs.rs index e7ffae5e..d4596bf3 100644 --- a/src/sys/statfs.rs +++ b/src/sys/statfs.rs @@ -1,20 +1,548 @@ -use {Result, NixPath}; -use errno::Errno; +use std::fmt::{self, Debug}; +use std::mem; use std::os::unix::io::AsRawFd; +#[cfg(not(any(target_os = "linux", target_os = "android")))] +use std::ffi::CStr; + use libc; -pub fn statfs<P: ?Sized + NixPath>(path: &P, stat: &mut libc::statfs) -> Result<()> { - unsafe { - Errno::clear(); - let res = path.with_nix_path(|path| libc::statfs(path.as_ptr(), stat))?; +use {NixPath, Result}; +use errno::Errno; + +#[cfg(target_os = "android")] +pub type fsid_t = libc::__fsid_t; +#[cfg(not(target_os = "android"))] +pub type fsid_t = libc::fsid_t; + +#[derive(Clone, Copy)] +pub struct Statfs(libc::statfs); + +#[cfg(target_os = "freebsd")] +#[derive(Eq, Copy, Clone, PartialEq, Debug)] +pub struct FsType(u32); +#[cfg(target_os = "android")] +#[derive(Eq, Copy, Clone, PartialEq, Debug)] +pub struct FsType(libc::c_ulong); +#[cfg(all(target_os = "linux", target_arch = "s390x"))] +#[derive(Eq, Copy, Clone, PartialEq, Debug)] +pub struct FsType(u32); +#[cfg(all(target_os = "linux", target_env = "musl"))] +#[derive(Eq, Copy, Clone, PartialEq, Debug)] +pub struct FsType(libc::c_ulong); +#[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] +#[derive(Eq, Copy, Clone, PartialEq, Debug)] +pub struct FsType(libc::c_long); + +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const ADFS_SUPER_MAGIC: FsType = FsType(libc::ADFS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const AFFS_SUPER_MAGIC: FsType = FsType(libc::AFFS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const CODA_SUPER_MAGIC: FsType = FsType(libc::CODA_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const EXT2_SUPER_MAGIC: FsType = FsType(libc::EXT2_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const EXT3_SUPER_MAGIC: FsType = FsType(libc::EXT3_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const EXT4_SUPER_MAGIC: FsType = FsType(libc::EXT4_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const HPFS_SUPER_MAGIC: FsType = FsType(libc::HPFS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const ISOFS_SUPER_MAGIC: FsType = FsType(libc::ISOFS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const JFFS2_SUPER_MAGIC: FsType = FsType(libc::JFFS2_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const MINIX_SUPER_MAGIC: FsType = FsType(libc::MINIX_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const MINIX_SUPER_MAGIC2: FsType = FsType(libc::MINIX_SUPER_MAGIC2); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const MINIX2_SUPER_MAGIC: FsType = FsType(libc::MINIX2_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const MINIX2_SUPER_MAGIC2: FsType = FsType(libc::MINIX2_SUPER_MAGIC2); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const MSDOS_SUPER_MAGIC: FsType = FsType(libc::MSDOS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const OPENPROM_SUPER_MAGIC: FsType = FsType(libc::OPENPROM_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const PROC_SUPER_MAGIC: FsType = FsType(libc::PROC_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const QNX4_SUPER_MAGIC: FsType = FsType(libc::QNX4_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const REISERFS_SUPER_MAGIC: FsType = FsType(libc::REISERFS_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC); +#[cfg(all(target_os = "linux", not(target_env = "musl"), not(target_arch = "s390x")))] +pub const USBDEVICE_SUPER_MAGIC: FsType = FsType(libc::USBDEVICE_SUPER_MAGIC); + +impl Statfs { + /// Magic code defining system type + #[cfg(not(any( + target_os = "openbsd", + target_os = "ios", + target_os = "macos" + )))] + pub fn filesystem_type(&self) -> FsType { + FsType(self.0.f_type) + } + + /// Magic code defining system type + #[cfg(not(any(target_os = "linux", target_os = "android")))] + pub fn filesystem_type_name(&self) -> &str { + let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) }; + c_str.to_str().unwrap() + } + + /// Optimal transfer block size + #[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))] + pub fn optimal_transfer_size(&self) -> i32 { + self.0.f_iosize + } + + /// Optimal transfer block size + #[cfg(all(target_os = "linux", target_arch = "s390x"))] + pub fn optimal_transfer_size(&self) -> u32 { + self.0.f_bsize + } + + /// Optimal transfer block size + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn optimal_transfer_size(&self) -> libc::c_ulong { + self.0.f_bsize + } + + /// Optimal transfer block size + #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] + pub fn optimal_transfer_size(&self) -> libc::c_long { + self.0.f_bsize + } + + /// Optimal transfer block size + #[cfg(target_os = "android")] + pub fn optimal_transfer_size(&self) -> libc::c_ulong { + self.0.f_bsize + } + + /// Optimal transfer block size + #[cfg(target_os = "dragonfly")] + pub fn optimal_transfer_size(&self) -> libc::c_long { + self.0.f_iosize + } + + /// Optimal transfer block size + #[cfg(target_os = "freebsd")] + pub fn optimal_transfer_size(&self) -> u64 { + self.0.f_iosize + } + + /// Size of a block + #[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))] + pub fn block_size(&self) -> u32 { + self.0.f_bsize + } + + /// Size of a block + // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471 + #[cfg(all(target_os = "linux", target_arch = "s390x"))] + pub fn block_size(&self) -> u32 { + self.0.f_bsize + } + + /// Size of a block + // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471 + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn block_size(&self) -> libc::c_ulong { + self.0.f_bsize + } + + /// Size of a block + // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471 + #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] + pub fn block_size(&self) -> libc::c_long { + self.0.f_bsize + } + + /// Size of a block + #[cfg(target_os = "freebsd")] + pub fn block_size(&self) -> u64 { + self.0.f_bsize + } + + /// Size of a block + #[cfg(target_os = "android")] + pub fn block_size(&self) -> libc::c_ulong { + self.0.f_bsize + } + + /// Size of a block + #[cfg(target_os = "dragonfly")] + pub fn block_size(&self) -> libc::c_long { + self.0.f_bsize + } + + /// Maximum length of filenames + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + pub fn maximum_name_length(&self) -> u32 { + self.0.f_namemax + } + + /// Maximum length of filenames + #[cfg(all(target_os = "linux", target_arch = "s390x"))] + pub fn maximum_name_length(&self) -> u32 { + self.0.f_namelen + } + + /// Maximum length of filenames + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn maximum_name_length(&self) -> libc::c_ulong { + self.0.f_namelen + } + + /// Maximum length of filenames + #[cfg(all(target_os = "linux", not(any(target_arch = "s390x", target_env = "musl"))))] + pub fn maximum_name_length(&self) -> libc::c_long { + self.0.f_namelen + } + + /// Maximum length of filenames + #[cfg(target_os = "android")] + pub fn maximum_name_length(&self) -> libc::c_ulong { + self.0.f_namelen + } + + /// Total data blocks in filesystem + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + ))] + pub fn blocks(&self) -> u64 { + self.0.f_blocks + } + + /// Total data blocks in filesystem + #[cfg(target_os = "dragonfly")] + pub fn blocks(&self) -> libc::c_long { + self.0.f_blocks + } + + /// Total data blocks in filesystem + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn blocks(&self) -> u64 { + self.0.f_blocks + } + + /// Total data blocks in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", target_env = "musl") + )))] + pub fn blocks(&self) -> libc::c_ulong { + self.0.f_blocks + } + + /// Free blocks in filesystem + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + ))] + pub fn blocks_free(&self) -> u64 { + self.0.f_bfree + } + + /// Free blocks in filesystem + #[cfg(target_os = "dragonfly")] + pub fn blocks_free(&self) -> libc::c_long { + self.0.f_bfree + } - Errno::result(res).map(drop) + /// Free blocks in filesystem + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn blocks_free(&self) -> u64 { + self.0.f_bfree + } + + /// Free blocks in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", target_env = "musl") + )))] + pub fn blocks_free(&self) -> libc::c_ulong { + self.0.f_bfree + } + + /// Free blocks available to unprivileged user + #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))] + pub fn blocks_available(&self) -> u64 { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(target_os = "dragonfly")] + pub fn blocks_available(&self) -> libc::c_long { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + pub fn blocks_available(&self) -> i64 { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn blocks_available(&self) -> u64 { + self.0.f_bavail + } + + /// Free blocks available to unprivileged user + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", target_env = "musl") + )))] + pub fn blocks_available(&self) -> libc::c_ulong { + self.0.f_bavail + } + + /// Total file nodes in filesystem + #[cfg(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + ))] + pub fn files(&self) -> u64 { + self.0.f_files + } + + /// Total file nodes in filesystem + #[cfg(target_os = "dragonfly")] + pub fn files(&self) -> libc::c_long { + self.0.f_files + } + + /// Total file nodes in filesystem + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn files(&self) -> u64 { + self.0.f_files + } + + /// Total file nodes in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", target_env = "musl") + )))] + pub fn files(&self) -> libc::c_ulong { + self.0.f_files + } + + /// Free file nodes in filesystem + #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))] + pub fn files_free(&self) -> u64 { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(target_os = "dragonfly")] + pub fn files_free(&self) -> libc::c_long { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + pub fn files_free(&self) -> i64 { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(all(target_os = "linux", target_env = "musl"))] + pub fn files_free(&self) -> u64 { + self.0.f_ffree + } + + /// Free file nodes in filesystem + #[cfg(not(any( + target_os = "ios", + target_os = "macos", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + all(target_os = "linux", target_env = "musl") + )))] + pub fn files_free(&self) -> libc::c_ulong { + self.0.f_ffree + } + + /// Filesystem ID + pub fn filesystem_id(&self) -> fsid_t { + self.0.f_fsid + } +} + +impl Debug for Statfs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Statfs") + .field("optimal_transfer_size", &self.optimal_transfer_size()) + .field("block_size", &self.block_size()) + .field("blocks", &self.blocks()) + .field("blocks_free", &self.blocks_free()) + .field("blocks_available", &self.blocks_available()) + .field("files", &self.files()) + .field("files_free", &self.files_free()) + .field("filesystem_id", &self.filesystem_id()) + .finish() + } +} + +pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> { + unsafe { + let mut stat: Statfs = mem::uninitialized(); + let res = path.with_nix_path(|path| libc::statfs(path.as_ptr(), &mut stat.0))?; + Errno::result(res).map(|_| stat) } } -pub fn fstatfs<T: AsRawFd>(fd: &T, stat: &mut libc::statfs) -> Result<()> { +pub fn fstatfs<T: AsRawFd>(fd: &T) -> Result<Statfs> { unsafe { - Errno::clear(); - Errno::result(libc::fstatfs(fd.as_raw_fd(), stat)).map(drop) + let mut stat: Statfs = mem::uninitialized(); + Errno::result(libc::fstatfs(fd.as_raw_fd(), &mut stat.0)).map(|_| stat) + } +} + +#[cfg(test)] +mod test { + use std::fs::File; + + use sys::statfs::*; + use sys::statvfs::*; + use std::path::Path; + + #[test] + fn statfs_call() { + check_statfs("/tmp"); + check_statfs("/dev"); + check_statfs("/run"); + check_statfs("/"); + } + + #[test] + fn fstatfs_call() { + check_fstatfs("/tmp"); + check_fstatfs("/dev"); + check_fstatfs("/run"); + check_fstatfs("/"); + } + + fn check_fstatfs(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()).unwrap(); + let file = File::open(path).unwrap(); + let fs = fstatfs(&file).unwrap(); + assert_fs_equals(fs, vfs); + } + + fn check_statfs(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()).unwrap(); + let fs = statfs(path.as_bytes()).unwrap(); + assert_fs_equals(fs, vfs); + } + + fn assert_fs_equals(fs: Statfs, vfs: Statvfs) { + assert_eq!(fs.files() as u64, vfs.files() as u64); + assert_eq!(fs.blocks() as u64, vfs.blocks() as u64); + assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64); + } + + // This test is ignored because files_free/blocks_free can change after statvfs call and before + // statfs call. + #[test] + #[ignore] + fn statfs_call_strict() { + check_statfs_strict("/tmp"); + check_statfs_strict("/dev"); + check_statfs_strict("/run"); + check_statfs_strict("/"); + } + + // This test is ignored because files_free/blocks_free can change after statvfs call and before + // fstatfs call. + #[test] + #[ignore] + fn fstatfs_call_strict() { + check_fstatfs_strict("/tmp"); + check_fstatfs_strict("/dev"); + check_fstatfs_strict("/run"); + check_fstatfs_strict("/"); + } + + fn check_fstatfs_strict(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()); + let file = File::open(path).unwrap(); + let fs = fstatfs(&file); + assert_fs_equals_strict(fs.unwrap(), vfs.unwrap()) + } + + fn check_statfs_strict(path: &str) { + if !Path::new(path).exists() { + return; + } + let vfs = statvfs(path.as_bytes()); + let fs = statfs(path.as_bytes()); + assert_fs_equals_strict(fs.unwrap(), vfs.unwrap()) + } + + fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) { + assert_eq!(fs.files_free() as u64, vfs.files_free() as u64); + assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64); + assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64); + assert_eq!(fs.files() as u64, vfs.files() as u64); + assert_eq!(fs.blocks() as u64, vfs.blocks() as u64); + assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64); } } diff --git a/src/sys/statvfs.rs b/src/sys/statvfs.rs index de69ae51..e5980369 100644 --- a/src/sys/statvfs.rs +++ b/src/sys/statvfs.rs @@ -57,8 +57,7 @@ libc_bitflags!( /// For more information see the [`statvfs(3)` man pages](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html). // FIXME: Replace with repr(transparent) #[repr(C)] -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Statvfs(libc::statvfs); impl Statvfs { diff --git a/src/sys/sysinfo.rs b/src/sys/sysinfo.rs index 98ef7bd5..4c8e3898 100644 --- a/src/sys/sysinfo.rs +++ b/src/sys/sysinfo.rs @@ -6,8 +6,7 @@ use Result; use errno::Errno; /// System info structure returned by `sysinfo`. -#[derive(Copy, Clone)] -#[allow(missing_debug_implementations)] // libc::sysinfo doesn't impl Debug +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct SysInfo(libc::sysinfo); impl SysInfo { diff --git a/src/sys/termios.rs b/src/sys/termios.rs index d8815baa..c7cdf10b 100644 --- a/src/sys/termios.rs +++ b/src/sys/termios.rs @@ -176,8 +176,7 @@ use ::unistd::Pid; /// This is a wrapper around the `libc::termios` struct that provides a safe interface for the /// standard fields. The only safe way to obtain an instance of this struct is to extract it from /// an open port using `tcgetattr()`. -#[derive(Clone)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Termios { inner: RefCell<libc::termios>, /// Input mode flags (see `termios.c_iflag` documentation) diff --git a/src/sys/time.rs b/src/sys/time.rs index 4bd3b780..3ad57543 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -45,7 +45,7 @@ pub trait TimeValLike: Sized { } #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct TimeSpec(timespec); const NANOS_PER_SEC: i64 = 1_000_000_000; @@ -67,25 +67,6 @@ impl AsRef<timespec> for TimeSpec { } } -impl fmt::Debug for TimeSpec { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("TimeSpec") - .field("tv_sec", &self.tv_sec()) - .field("tv_nsec", &self.tv_nsec()) - .finish() - } -} - -impl PartialEq for TimeSpec { - // The implementation of cmp is simplified by assuming that the struct is - // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) - fn eq(&self, other: &TimeSpec) -> bool { - self.tv_sec() == other.tv_sec() && self.tv_nsec() == other.tv_nsec() - } -} - -impl Eq for TimeSpec {} - impl Ord for TimeSpec { // The implementation of cmp is simplified by assuming that the struct is // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) @@ -259,7 +240,7 @@ impl fmt::Display for TimeSpec { #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct TimeVal(timeval); const MICROS_PER_SEC: i64 = 1_000_000; @@ -278,25 +259,6 @@ impl AsRef<timeval> for TimeVal { } } -impl fmt::Debug for TimeVal { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("TimeVal") - .field("tv_sec", &self.tv_sec()) - .field("tv_usec", &self.tv_usec()) - .finish() - } -} - -impl PartialEq for TimeVal { - // The implementation of cmp is simplified by assuming that the struct is - // normalized. That is, tv_usec must always be within [0, 1_000_000) - fn eq(&self, other: &TimeVal) -> bool { - self.tv_sec() == other.tv_sec() && self.tv_usec() == other.tv_usec() - } -} - -impl Eq for TimeVal {} - impl Ord for TimeVal { // The implementation of cmp is simplified by assuming that the struct is // normalized. That is, tv_usec must always be within [0, 1_000_000) diff --git a/src/sys/uio.rs b/src/sys/uio.rs index 45860be5..d089084e 100644 --- a/src/sys/uio.rs +++ b/src/sys/uio.rs @@ -88,8 +88,7 @@ pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>{ /// and [`process_vm_writev`](fn.process_vm_writev.html). #[cfg(target_os = "linux")] #[repr(C)] -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct RemoteIoVec { /// The starting address of this slice (`iov_base`). pub base: usize, @@ -160,7 +159,7 @@ pub fn process_vm_readv(pid: ::unistd::Pid, local_iov: &[IoVec<&mut [u8]>], remo } #[repr(C)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct IoVec<T>(libc::iovec, PhantomData<T>); impl<T> IoVec<T> { diff --git a/src/sys/utsname.rs b/src/sys/utsname.rs index e33d0739..ab09c7d2 100644 --- a/src/sys/utsname.rs +++ b/src/sys/utsname.rs @@ -4,8 +4,7 @@ use std::ffi::CStr; use std::str::from_utf8_unchecked; #[repr(C)] -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct UtsName(libc::utsname); impl UtsName { diff --git a/src/sys/wait.rs b/src/sys/wait.rs index 3f99757d..c54f7ec5 100644 --- a/src/sys/wait.rs +++ b/src/sys/wait.rs @@ -55,7 +55,7 @@ libc_bitflags!( /// Note that there are two Linux-specific enum variants, `PtraceEvent` /// and `PtraceSyscall`. Portable code should avoid exhaustively /// matching on `WaitStatus`. -#[derive(Eq, PartialEq, Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum WaitStatus { /// The process exited normally (as with `exit()` or returning from /// `main`) with the given exit code. This case matches the C macro diff --git a/src/ucontext.rs b/src/ucontext.rs index c94464d5..5e10e7d1 100644 --- a/src/ucontext.rs +++ b/src/ucontext.rs @@ -6,8 +6,7 @@ use errno::Errno; use std::mem; use sys::signal::SigSet; -#[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct UContext { context: libc::ucontext_t, } diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 9cbf0257..12afc84c 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -1,9 +1,11 @@ use nix::sys::socket::{InetAddr, UnixAddr, getsockname}; -use std::slice; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; use std::net::{self, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::os::unix::io::RawFd; use std::path::Path; +use std::slice; use std::str::FromStr; -use std::os::unix::io::RawFd; use libc::c_char; use tempfile; @@ -66,26 +68,70 @@ pub fn test_path_to_sock_addr() { assert_eq!(addr.path(), Some(actual)); } +fn calculate_hash<T: Hash>(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + +#[test] +pub fn test_addr_equality_path() { + let path = "/foo/bar"; + let actual = Path::new(path); + let addr1 = UnixAddr::new(actual).unwrap(); + let mut addr2 = addr1.clone(); + + addr2.0.sun_path[10] = 127; + + assert_eq!(addr1, addr2); + assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +pub fn test_abstract_sun_path_too_long() { + let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough"); + let addr = UnixAddr::new_abstract(name.as_bytes()); + assert!(addr.is_err()); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +pub fn test_addr_equality_abstract() { + let name = String::from("nix\0abstract\0test"); + let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + let mut addr2 = addr1.clone(); + + assert_eq!(addr1, addr2); + assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); + + addr2.0.sun_path[18] = 127; + assert_ne!(addr1, addr2); + assert_ne!(calculate_hash(&addr1), calculate_hash(&addr2)); +} + // Test getting/setting abstract addresses (without unix socket creation) #[cfg(target_os = "linux")] #[test] pub fn test_abstract_uds_addr() { let empty = String::new(); let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); - assert_eq!(addr.as_abstract(), Some(empty.as_bytes())); + let sun_path = [0u8; 107]; + assert_eq!(addr.as_abstract(), Some(&sun_path[..])); let name = String::from("nix\0abstract\0test"); let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); - assert_eq!(addr.as_abstract(), Some(name.as_bytes())); + let sun_path = [ + 110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + assert_eq!(addr.as_abstract(), Some(&sun_path[..])); assert_eq!(addr.path(), None); // Internally, name is null-prefixed (abstract namespace) - let internal: &[u8] = unsafe { - slice::from_raw_parts(addr.0.sun_path.as_ptr() as *const u8, addr.1) - }; - let mut abstract_name = name.clone(); - abstract_name.insert(0, '\0'); - assert_eq!(internal, abstract_name.as_bytes()); + assert_eq!(addr.0.sun_path[0], 0); } #[test] diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs index bcc523bf..8d02f147 100644 --- a/test/test_fcntl.rs +++ b/test/test_fcntl.rs @@ -48,16 +48,55 @@ fn test_readlink() { #[cfg(any(target_os = "linux", target_os = "android"))] mod linux_android { use std::io::prelude::*; + use std::io::SeekFrom; use std::os::unix::prelude::*; use libc::loff_t; - use nix::fcntl::{SpliceFFlags, FallocateFlags, fallocate, splice, tee, vmsplice}; + use nix::fcntl::*; use nix::sys::uio::IoVec; use nix::unistd::{close, pipe, read, write}; use tempfile::{tempfile, NamedTempFile}; + /// This test creates a temporary file containing the contents + /// 'foobarbaz' and uses the `copy_file_range` call to transfer + /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The + /// resulting file is read and should contain the contents `bar`. + /// The from_offset should be updated by the call to reflect + /// the 3 bytes read (6). + /// + /// FIXME: This test is disabled for linux based builds, because Travis + /// Linux version is too old for `copy_file_range`. + #[test] + #[ignore] + fn test_copy_file_range() { + const CONTENTS: &[u8] = b"foobarbaz"; + + let mut tmp1 = tempfile().unwrap(); + let mut tmp2 = tempfile().unwrap(); + + tmp1.write_all(CONTENTS).unwrap(); + tmp1.flush().unwrap(); + + let mut from_offset: i64 = 3; + copy_file_range( + tmp1.as_raw_fd(), + Some(&mut from_offset), + tmp2.as_raw_fd(), + None, + 3, + ) + .unwrap(); + + let mut res: String = String::new(); + tmp2.seek(SeekFrom::Start(0)).unwrap(); + tmp2.read_to_string(&mut res).unwrap(); + + assert_eq!(res, String::from("bar")); + assert_eq!(from_offset, 6); + } + #[test] fn test_splice() { const CONTENTS: &[u8] = b"abcdef123456"; diff --git a/test/test_mq.rs b/test/test_mq.rs index 41ab358b..caac4fc2 100644 --- a/test/test_mq.rs +++ b/test/test_mq.rs @@ -55,8 +55,8 @@ fn test_mq_getattr() { }; let mqd = r.unwrap(); - let read_attr = mq_getattr(mqd); - assert!(read_attr.unwrap() == initial_attr); + let read_attr = mq_getattr(mqd).unwrap(); + assert_eq!(read_attr, initial_attr); mq_close(mqd).unwrap(); } @@ -79,21 +79,21 @@ fn test_mq_setattr() { let mqd = r.unwrap(); let new_attr = MqAttr::new(0, 20, MSG_SIZE * 2, 100); - let old_attr = mq_setattr(mqd, &new_attr); - assert!(old_attr.unwrap() == initial_attr); + let old_attr = mq_setattr(mqd, &new_attr).unwrap(); + assert_eq!(old_attr, initial_attr); - let new_attr_get = mq_getattr(mqd); + let new_attr_get = mq_getattr(mqd).unwrap(); // The following tests make sense. No changes here because according to the Linux man page only // O_NONBLOCK can be set (see tests below) - assert!(new_attr_get.unwrap() != new_attr); + assert_ne!(new_attr_get, new_attr); let new_attr_non_blocking = MqAttr::new(MQ_OFlag::O_NONBLOCK.bits() as c_long, 10, MSG_SIZE, 0); mq_setattr(mqd, &new_attr_non_blocking).unwrap(); - let new_attr_get = mq_getattr(mqd); + let new_attr_get = mq_getattr(mqd).unwrap(); // now the O_NONBLOCK flag has been set - assert!(new_attr_get.unwrap() != initial_attr); - assert!(new_attr_get.unwrap() == new_attr_non_blocking); + assert_ne!(new_attr_get, initial_attr); + assert_eq!(new_attr_get, new_attr_non_blocking); mq_close(mqd).unwrap(); } diff --git a/test/test_poll.rs b/test/test_poll.rs index 6cac3b77..aef40e47 100644 --- a/test/test_poll.rs +++ b/test/test_poll.rs @@ -1,5 +1,5 @@ use nix::poll::{PollFlags, poll, PollFd}; -use nix::unistd::{write, pipe, close}; +use nix::unistd::{write, pipe}; #[test] fn test_poll() { @@ -19,24 +19,6 @@ fn test_poll() { assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN)); } -#[test] -fn test_poll_debug() { - assert_eq!(format!("{:?}", PollFd::new(0, PollFlags::empty())), - "PollFd { fd: 0, events: (empty), revents: (empty) }"); - assert_eq!(format!("{:?}", PollFd::new(1, PollFlags::POLLIN)), - "PollFd { fd: 1, events: POLLIN, revents: (empty) }"); - - // Testing revents requires doing some I/O - let (r, w) = pipe().unwrap(); - let mut fds = [PollFd::new(r, PollFlags::POLLIN)]; - write(w, b" ").unwrap(); - close(w).unwrap(); - poll(&mut fds, -1).unwrap(); - assert_eq!(format!("{:?}", fds[0]), - format!("PollFd {{ fd: {}, events: POLLIN, revents: POLLIN | POLLHUP }}", r)); - close(r).unwrap(); -} - // ppoll(2) is the same as poll except for how it handles timeouts and signals. // Repeating the test for poll(2) should be sufficient to check that our // bindings are correct. diff --git a/test/test_stat.rs b/test/test_stat.rs index b9da7fc3..1173455f 100644 --- a/test/test_stat.rs +++ b/test/test_stat.rs @@ -1,13 +1,15 @@ use std::fs::{self, File}; -use std::os::unix::fs::symlink; +use std::os::unix::fs::{symlink, PermissionsExt}; use std::os::unix::prelude::AsRawFd; use std::time::{Duration, UNIX_EPOCH}; +use std::path::Path; #[cfg(not(any(target_os = "netbsd")))] -use libc::{S_IFMT, S_IFLNK}; +use libc::{S_IFMT, S_IFLNK, mode_t}; -use nix::fcntl; -use nix::sys::stat::{self, fchmod, fchmodat, futimens, stat, utimes, utimensat}; +use nix::{fcntl, Error}; +use nix::errno::{Errno}; +use nix::sys::stat::{self, fchmod, fchmodat, futimens, stat, utimes, utimensat, mkdirat}; #[cfg(any(target_os = "linux", target_os = "haiku", target_os = "ios", @@ -260,3 +262,35 @@ fn test_utimensat() { UtimensatFlags::FollowSymlink).unwrap(); assert_times_eq(500, 800, &fs::metadata(&fullpath).unwrap()); } + +#[test] +fn test_mkdirat_success_path() { + let tempdir = tempfile::tempdir().unwrap(); + let filename = "example_subdir"; + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok()); + assert!(Path::exists(&tempdir.path().join(filename))); +} + +#[test] +fn test_mkdirat_success_mode() { + let expected_bits = stat::SFlag::S_IFDIR.bits() | stat::Mode::S_IRWXU.bits(); + let tempdir = tempfile::tempdir().unwrap(); + let filename = "example_subdir"; + let dirfd = fcntl::open(tempdir.path(), fcntl::OFlag::empty(), stat::Mode::empty()).unwrap(); + assert!((mkdirat(dirfd, filename, Mode::S_IRWXU)).is_ok()); + let permissions = fs::metadata(tempdir.path().join(filename)).unwrap().permissions(); + let mode = permissions.mode(); + assert_eq!(mode as mode_t, expected_bits) +} + +#[test] +fn test_mkdirat_fail() { + let tempdir = tempfile::tempdir().unwrap(); + let not_dir_filename= "example_not_dir"; + let filename = "example_subdir_dir"; + let dirfd = fcntl::open(&tempdir.path().join(not_dir_filename), fcntl::OFlag::O_CREAT, + stat::Mode::empty()).unwrap(); + let result = mkdirat(dirfd, filename, Mode::S_IRWXU).unwrap_err(); + assert_eq!(result, Error::Sys(Errno::ENOTDIR)); +} |