use {Result, NixPath};
use errno::Errno;
use libc::{self, c_int, c_uint, c_char, size_t, ssize_t};
use sys::stat::Mode;
use std::os::raw;
use std::os::unix::io::RawFd;
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;
#[cfg(any(target_os = "android", target_os = "linux"))]
use std::ptr;
#[cfg(any(target_os = "android", target_os = "linux"))]
use sys::uio::IoVec;
#[cfg(any(target_os = "linux",
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
any(target_os = "wasi", target_env = "wasi"),
target_env = "uclibc",
target_env = "freebsd"))]
pub use self::posix_fadvise::*;
libc_bitflags!{
pub struct AtFlags: c_int {
AT_REMOVEDIR;
AT_SYMLINK_FOLLOW;
AT_SYMLINK_NOFOLLOW;
#[cfg(any(target_os = "android", target_os = "linux"))]
AT_NO_AUTOMOUNT;
#[cfg(any(target_os = "android", target_os = "linux"))]
AT_EMPTY_PATH;
}
}
libc_bitflags!(
pub struct OFlag: c_int {
O_ACCMODE;
#[cfg(target_os = "netbsd")]
O_ALT_IO;
O_APPEND;
O_ASYNC;
O_CLOEXEC;
O_CREAT;
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd"))]
O_DIRECT;
O_DIRECTORY;
#[cfg(any(target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
O_DSYNC;
O_EXCL;
#[cfg(target_os = "freebsd")]
O_EXEC;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
O_EXLOCK;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
all(target_os = "linux", not(target_env = "musl")),
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
O_FSYNC;
#[cfg(any(target_os = "android", target_os = "linux"))]
O_LARGEFILE;
#[cfg(any(target_os = "android", target_os = "linux"))]
O_NOATIME;
O_NOCTTY;
O_NDELAY;
O_NOFOLLOW;
O_NONBLOCK;
#[cfg(target_os = "netbsd")]
O_NOSIGPIPE;
#[cfg(any(target_os = "android", target_os = "linux"))]
O_PATH;
O_RDONLY;
O_RDWR;
#[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
O_RSYNC;
#[cfg(target_os = "netbsd")]
O_SEARCH;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"))]
O_SHLOCK;
O_SYNC;
#[cfg(any(target_os = "android", target_os = "linux"))]
O_TMPFILE;
O_TRUNC;
#[cfg(target_os = "freebsd")]
O_TTY_INIT;
O_WRONLY;
}
);
pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
let fd = path.with_nix_path(|cstr| {
let modebits = c_uint::from(mode.bits());
unsafe { libc::open(cstr.as_ptr(), oflag.bits(), modebits) }
})?;
Errno::result(fd)
}
pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
let fd = path.with_nix_path(|cstr| {
let modebits = c_uint::from(mode.bits());
unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), modebits) }
})?;
Errno::result(fd)
}
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(old_dirfd: Option<RawFd>, old_path: &P1,
new_dirfd: Option<RawFd>, new_path: &P2)
-> Result<()> {
let res = old_path.with_nix_path(|old_cstr| {
new_path.with_nix_path(|new_cstr| unsafe {
libc::renameat(at_rawfd(old_dirfd), old_cstr.as_ptr(),
at_rawfd(new_dirfd), new_cstr.as_ptr())
})
})??;
Errno::result(res).map(drop)
}
fn wrap_readlink_result(v: &mut Vec<u8>, res: ssize_t) -> Result<OsString> {
match Errno::result(res) {
Err(err) => Err(err),
Ok(len) => {
unsafe { v.set_len(len as usize) }
Ok(OsString::from_vec(v.to_vec()))
}
}
}
pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
let res = path.with_nix_path(|cstr| {
unsafe { libc::readlink(cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t) }
})?;
wrap_readlink_result(&mut v, res)
}
pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
let res = path.with_nix_path(|cstr| {
unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t) }
})?;
wrap_readlink_result(&mut v, res)
}
pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
match fd {
None => libc::AT_FDCWD,
Some(fd) => fd,
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
libc_bitflags!(
pub struct SealFlag: c_int {
F_SEAL_SEAL;
F_SEAL_SHRINK;
F_SEAL_GROW;
F_SEAL_WRITE;
}
);
libc_bitflags!(
pub struct FdFlag: c_int {
FD_CLOEXEC;
}
);
#[derive(Debug, Eq, Hash, PartialEq)]
pub enum FcntlArg<'a> {
F_DUPFD(RawFd),
F_DUPFD_CLOEXEC(RawFd),
F_GETFD,
F_SETFD(FdFlag),
F_GETFL,
F_SETFL(OFlag),
F_SETLK(&'a libc::flock),
F_SETLKW(&'a libc::flock),
F_GETLK(&'a mut libc::flock),
#[cfg(any(target_os = "linux", target_os = "android"))]
F_OFD_SETLK(&'a libc::flock),
#[cfg(any(target_os = "linux", target_os = "android"))]
F_OFD_SETLKW(&'a libc::flock),
#[cfg(any(target_os = "linux", target_os = "android"))]
F_OFD_GETLK(&'a mut libc::flock),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_ADD_SEALS(SealFlag),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_GET_SEALS,
#[cfg(any(target_os = "macos", target_os = "ios"))]
F_FULLFSYNC,
#[cfg(any(target_os = "linux", target_os = "android"))]
F_GETPIPE_SZ,
#[cfg(any(target_os = "linux", target_os = "android"))]
F_SETPIPE_SZ(c_int),
}
pub use self::FcntlArg::*;
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
let res = unsafe {
match arg {
F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
F_GETFD => libc::fcntl(fd, libc::F_GETFD),
F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
F_GETFL => libc::fcntl(fd, libc::F_GETFL),
F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
#[cfg(any(target_os = "android", target_os = "linux"))]
F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
#[cfg(any(target_os = "macos", target_os = "ios"))]
F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
#[cfg(any(target_os = "linux", target_os = "android"))]
F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
#[cfg(any(target_os = "linux", target_os = "android"))]
F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
#[cfg(any(target_os = "linux", target_os = "android"))]
_ => unimplemented!()
}
};
Errno::result(res)
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FlockArg {
LockShared,
LockExclusive,
Unlock,
LockSharedNonblock,
LockExclusiveNonblock,
UnlockNonblock,
}
pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
use self::FlockArg::*;
let res = unsafe {
match arg {
LockShared => libc::flock(fd, libc::LOCK_SH),
LockExclusive => libc::flock(fd, libc::LOCK_EX),
Unlock => libc::flock(fd, libc::LOCK_UN),
LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
}
};
Errno::result(res).map(drop)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
libc_bitflags! {
pub struct SpliceFFlags: c_uint {
SPLICE_F_MOVE;
SPLICE_F_NONBLOCK;
SPLICE_F_MORE;
SPLICE_F_GIFT;
}
}
#[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> {
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)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
let ret = unsafe {
libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
};
Errno::result(ret).map(|r| r as usize)
}
#[cfg(any(target_os = "linux"))]
libc_bitflags!(
pub struct FallocateFlags: c_int {
FALLOC_FL_KEEP_SIZE;
FALLOC_FL_PUNCH_HOLE;
FALLOC_FL_COLLAPSE_RANGE;
FALLOC_FL_ZERO_RANGE;
FALLOC_FL_INSERT_RANGE;
FALLOC_FL_UNSHARE_RANGE;
}
);
#[cfg(any(target_os = "linux"))]
pub fn fallocate(fd: RawFd, mode: FallocateFlags, offset: libc::off_t, len: libc::off_t) -> Result<c_int> {
let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
Errno::result(res)
}
#[cfg(any(target_os = "linux",
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
any(target_os = "wasi", target_env = "wasi"),
target_env = "uclibc",
target_env = "freebsd"))]
mod posix_fadvise {
use Result;
use libc;
use errno::Errno;
use std::os::unix::io::RawFd;
libc_enum! {
#[repr(i32)]
pub enum PosixFadviseAdvice {
POSIX_FADV_NORMAL,
POSIX_FADV_SEQUENTIAL,
POSIX_FADV_RANDOM,
POSIX_FADV_NOREUSE,
POSIX_FADV_WILLNEED,
POSIX_FADV_DONTNEED,
}
}
pub fn posix_fadvise(fd: RawFd,
offset: libc::off_t,
len: libc::off_t,
advice: PosixFadviseAdvice) -> Result<libc::c_int> {
let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
Errno::result(res)
}
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
any(target_os = "wasi", target_env = "wasi"),
target_os = "freebsd"
))]
pub fn posix_fallocate(
fd: RawFd,
offset: libc::off_t,
len: libc::off_t
) -> Result<()> {
let res = unsafe { libc::posix_fallocate(fd, offset, len) };
match Errno::result(res) {
Err(err) => Err(err),
Ok(0) => Ok(()),
Ok(errno) => Err(crate::Error::Sys(Errno::from_i32(errno))),
}
}