summaryrefslogtreecommitdiffstats
path: root/openat/src
diff options
context:
space:
mode:
authorTomasz Kramkowski <tomasz@kramkow.ski>2023-01-27 14:22:33 +0000
committerTomasz Kramkowski <tomasz@kramkow.ski>2023-01-27 14:22:33 +0000
commitbdd8126b938de11272f79bb7f512316740469ed7 (patch)
tree5a46eaf4942f4a21552cf606b35effe5ac1c97c4 /openat/src
parent6cef9f0fc159de4c9fd708050ec76adb4e74d390 (diff)
downloadpam_usercg_rust-bdd8126b938de11272f79bb7f512316740469ed7.tar.gz
pam_usercg_rust-bdd8126b938de11272f79bb7f512316740469ed7.tar.xz
pam_usercg_rust-bdd8126b938de11272f79bb7f512316740469ed7.zip
use cap-std
Diffstat (limited to 'openat/src')
-rw-r--r--openat/src/dir.rs696
-rw-r--r--openat/src/filetype.rs33
-rw-r--r--openat/src/lib.rs95
-rw-r--r--openat/src/list.rs153
-rw-r--r--openat/src/metadata.rs75
-rw-r--r--openat/src/name.rs76
6 files changed, 0 insertions, 1128 deletions
diff --git a/openat/src/dir.rs b/openat/src/dir.rs
deleted file mode 100644
index eac2f38..0000000
--- a/openat/src/dir.rs
+++ /dev/null
@@ -1,696 +0,0 @@
-use std::io;
-use std::mem;
-use std::ffi::{OsString, CStr};
-use std::fs::{File, read_link};
-use std::os::unix::io::{AsRawFd, RawFd, FromRawFd, IntoRawFd};
-use std::os::unix::ffi::{OsStringExt};
-use std::path::{PathBuf};
-
-use libc;
-use crate::metadata::{self, Metadata};
-use crate::list::{DirIter, open_dir, open_dirfd};
-
-use crate::{Dir, AsPath};
-
-#[cfg(target_os="linux")]
-const BASE_OPEN_FLAGS: libc::c_int = libc::O_PATH|libc::O_CLOEXEC;
-#[cfg(target_os="freebsd")]
-const BASE_OPEN_FLAGS: libc::c_int = libc::O_DIRECTORY|libc::O_CLOEXEC;
-#[cfg(not(any(target_os="linux", target_os="freebsd")))]
-const BASE_OPEN_FLAGS: libc::c_int = libc::O_CLOEXEC;
-
-impl Dir {
- /// Creates a directory descriptor that resolves paths relative to current
- /// working directory (AT_FDCWD)
- #[deprecated(since="0.1.15", note="\
- Use `Dir::open(\".\")` instead. \
- Dir::cwd() doesn't open actual file descriptor and uses magic value \
- instead which resolves to current dir on any syscall invocation. \
- This is usually counter-intuitive and yields a broken \
- file descriptor when using `Dir::as_raw_fd`. \
- Will be removed in version v0.2 of the library.")]
- pub fn cwd() -> Dir {
- Dir(libc::AT_FDCWD)
- }
-
- /// Open a directory descriptor at specified path
- // TODO(tailhook) maybe accept only absolute paths?
- pub fn open<P: AsPath>(path: P) -> io::Result<Dir> {
- Dir::_open(to_cstr(path)?.as_ref())
- }
-
- fn _open(path: &CStr) -> io::Result<Dir> {
- let fd = unsafe {
- libc::open(path.as_ptr(), BASE_OPEN_FLAGS)
- };
- if fd < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(Dir(fd))
- }
- }
-
- /// List subdirectory of this dir
- ///
- /// You can list directory itself with `list_self`.
- pub fn list_dir<P: AsPath>(&self, path: P) -> io::Result<DirIter> {
- open_dir(self, to_cstr(path)?.as_ref())
- }
-
- /// List this dir
- pub fn list_self(&self) -> io::Result<DirIter> {
- unsafe {
- open_dirfd(libc::dup(self.0))
- }
- }
-
- /// Open subdirectory
- ///
- /// Note that this method does not resolve symlinks by default, so you may have to call
- /// [`read_link`] to resolve the real path first.
- ///
- /// [`read_link`]: #method.read_link
- pub fn sub_dir<P: AsPath>(&self, path: P) -> io::Result<Dir> {
- self._sub_dir(to_cstr(path)?.as_ref())
- }
-
- fn _sub_dir(&self, path: &CStr) -> io::Result<Dir> {
- let fd = unsafe {
- libc::openat(self.0,
- path.as_ptr(),
- BASE_OPEN_FLAGS|libc::O_NOFOLLOW)
- };
- if fd < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(Dir(fd))
- }
- }
-
- /// Read link in this directory
- pub fn read_link<P: AsPath>(&self, path: P) -> io::Result<PathBuf> {
- self._read_link(to_cstr(path)?.as_ref())
- }
-
- fn _read_link(&self, path: &CStr) -> io::Result<PathBuf> {
- let mut buf = vec![0u8; 4096];
- let res = unsafe {
- libc::readlinkat(self.0,
- path.as_ptr(),
- buf.as_mut_ptr() as *mut libc::c_char, buf.len())
- };
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- buf.truncate(res as usize);
- Ok(OsString::from_vec(buf).into())
- }
- }
-
- /// Open file for reading in this directory
- ///
- /// Note that this method does not resolve symlinks by default, so you may have to call
- /// [`read_link`] to resolve the real path first.
- ///
- /// [`read_link`]: #method.read_link
- pub fn open_file<P: AsPath>(&self, path: P) -> io::Result<File> {
- self._open_file(to_cstr(path)?.as_ref(),
- libc::O_RDONLY, 0)
- }
-
- /// Open file with specified flags relative to this directory
- pub fn open_file_ex<P: AsPath>(&self, path: P, flags: libc::c_int, mode: libc::mode_t) -> io::Result<File> {
- self._open_file(to_cstr(path)?.as_ref(), flags, mode)
- }
-
- /// Open file for writing, create if necessary, truncate on open
- ///
- /// If there exists a symlink at the destination path, this method will fail. In that case, you
- /// will need to remove the symlink before calling this method. If you are on Linux, you can
- /// alternatively create an unnamed file with [`new_unnamed_file`] and then rename it,
- /// clobbering the symlink at the destination.
- ///
- /// [`new_unnamed_file`]: #method.new_unnamed_file
- pub fn write_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
- -> io::Result<File>
- {
- self._open_file(to_cstr(path)?.as_ref(),
- libc::O_CREAT|libc::O_WRONLY|libc::O_TRUNC,
- mode)
- }
-
- /// Open file for append, create if necessary
- ///
- /// If there exists a symlink at the destination path, this method will fail. In that case, you
- /// will need to call [`read_link`] to resolve the real path first.
- ///
- /// [`read_link`]: #method.read_link
- pub fn append_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
- -> io::Result<File>
- {
- self._open_file(to_cstr(path)?.as_ref(),
- libc::O_CREAT|libc::O_WRONLY|libc::O_APPEND,
- mode)
- }
-
- /// Create file for writing (and truncate) in this directory
- ///
- /// Deprecated alias for `write_file`
- ///
- /// If there exists a symlink at the destination path, this method will fail. In that case, you
- /// will need to remove the symlink before calling this method. If you are on Linux, you can
- /// alternatively create an unnamed file with [`new_unnamed_file`] and then rename it,
- /// clobbering the symlink at the destination.
- ///
- /// [`new_unnamed_file`]: #method.new_unnamed_file
- #[deprecated(since="0.1.7", note="please use `write_file` instead")]
- pub fn create_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
- -> io::Result<File>
- {
- self._open_file(to_cstr(path)?.as_ref(),
- libc::O_CREAT|libc::O_WRONLY|libc::O_TRUNC,
- mode)
- }
-
- /// Create a tmpfile in this directory which isn't linked to any filename
- ///
- /// This works by passing `O_TMPFILE` into the openat call. The flag is
- /// supported only on linux. So this function always returns error on
- /// such systems.
- ///
- /// **WARNING!** On glibc < 2.22 file permissions of the newly created file
- /// may be arbitrary. Consider chowning after creating a file.
- ///
- /// Note: It may be unclear why creating unnamed file requires a dir. There
- /// are two reasons:
- ///
- /// 1. It's created (and occupies space) on a real filesystem, so the
- /// directory is a way to find out which filesystem to attach file to
- /// 2. This method is mostly needed to initialize the file then link it
- /// using ``link_file_at`` to the real directory entry. When linking
- /// it must be linked into the same filesystem. But because for most
- /// programs finding out filesystem layout is an overkill the rule of
- /// thumb is to create a file in the the target directory.
- ///
- /// Currently, we recommend to fallback on any error if this operation
- /// can't be accomplished rather than relying on specific error codes,
- /// because semantics of errors are very ugly.
- #[cfg(target_os="linux")]
- pub fn new_unnamed_file(&self, mode: libc::mode_t)
- -> io::Result<File>
- {
- self._open_file(unsafe { CStr::from_bytes_with_nul_unchecked(b".\0") },
- libc::O_TMPFILE|libc::O_WRONLY,
- mode)
- }
-
- /// Create a tmpfile in this directory which isn't linked to any filename
- ///
- /// This works by passing `O_TMPFILE` into the openat call. The flag is
- /// supported only on linux. So this function always returns error on
- /// such systems.
- ///
- /// Note: It may be unclear why creating unnamed file requires a dir. There
- /// are two reasons:
- ///
- /// 1. It's created (and occupies space) on a real filesystem, so the
- /// directory is a way to find out which filesystem to attach file to
- /// 2. This method is mostly needed to initialize the file then link it
- /// using ``link_file_at`` to the real directory entry. When linking
- /// it must be linked into the same filesystem. But because for most
- /// programs finding out filesystem layout is an overkill the rule of
- /// thumb is to create a file in the the target directory.
- ///
- /// Currently, we recommend to fallback on any error if this operation
- /// can't be accomplished rather than relying on specific error codes,
- /// because semantics of errors are very ugly.
- #[cfg(not(target_os="linux"))]
- pub fn new_unnamed_file<P: AsPath>(&self, _mode: libc::mode_t)
- -> io::Result<File>
- {
- Err(io::Error::new(io::ErrorKind::Other,
- "creating unnamed tmpfiles is only supported on linux"))
- }
-
- /// Link open file to a specified path
- ///
- /// This is used with ``new_unnamed_file()`` to create and initialize the
- /// file before linking it into a filesystem. This requires `/proc` to be
- /// mounted and works **only on linux**.
- ///
- /// On systems other than linux this always returns error. It's expected
- /// that in most cases this methos is not called if ``new_unnamed_file``
- /// fails. But in obscure scenarios where `/proc` is not mounted this
- /// method may fail even on linux. So your code should be able to fallback
- /// to a named file if this method fails too.
- #[cfg(target_os="linux")]
- pub fn link_file_at<F: AsRawFd, P: AsPath>(&self, file: &F, path: P)
- -> io::Result<()>
- {
- let fd_path = format!("/proc/self/fd/{}", file.as_raw_fd());
- _hardlink(&Dir(libc::AT_FDCWD), to_cstr(fd_path)?.as_ref(),
- &self, to_cstr(path)?.as_ref(),
- libc::AT_SYMLINK_FOLLOW)
- }
-
- /// Link open file to a specified path
- ///
- /// This is used with ``new_unnamed_file()`` to create and initialize the
- /// file before linking it into a filesystem. This requires `/proc` to be
- /// mounted and works **only on linux**.
- ///
- /// On systems other than linux this always returns error. It's expected
- /// that in most cases this methos is not called if ``new_unnamed_file``
- /// fails. But in obscure scenarios where `/proc` is not mounted this
- /// method may fail even on linux. So your code should be able to fallback
- /// to a named file if this method fails too.
- #[cfg(not(target_os="linux"))]
- pub fn link_file_at<F: AsRawFd, P: AsPath>(&self, _file: F, _path: P)
- -> io::Result<()>
- {
- Err(io::Error::new(io::ErrorKind::Other,
- "linking unnamed fd to directories is only supported on linux"))
- }
-
- /// Create file if not exists, fail if exists
- ///
- /// This function checks existence and creates file atomically with
- /// respect to other threads and processes.
- ///
- /// Technically it means passing `O_EXCL` flag to open.
- pub fn new_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
- -> io::Result<File>
- {
- self._open_file(to_cstr(path)?.as_ref(),
- libc::O_CREAT|libc::O_EXCL|libc::O_WRONLY,
- mode)
- }
-
- /// Open file for reading and writing without truncation, create if needed
- ///
- /// If there exists a symlink at the destination path, this method will fail. In that case, you
- /// will need to call [`read_link`] to resolve the real path first.
- ///
- /// [`read_link`]: #method.read_link
- pub fn update_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
- -> io::Result<File>
- {
- self._open_file(to_cstr(path)?.as_ref(),
- libc::O_CREAT|libc::O_RDWR,
- mode)
- }
-
- fn _open_file(&self, path: &CStr, flags: libc::c_int, mode: libc::mode_t)
- -> io::Result<File>
- {
- unsafe {
- // Note: In below call to `openat`, *mode* must be cast to
- // `unsigned` because the optional `mode` argument to `openat` is
- // variadic in the signature. Since integers are not implicitly
- // promoted as they are in C this would break on Freebsd where
- // *mode_t* is an alias for `uint16_t`.
- let res = libc::openat(self.0, path.as_ptr(),
- flags|libc::O_CLOEXEC|libc::O_NOFOLLOW,
- mode as libc::c_uint);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(File::from_raw_fd(res))
- }
- }
- }
-
- /// Make a symlink in this directory
- ///
- /// Note: the order of arguments differ from `symlinkat`
- pub fn symlink<P: AsPath, R: AsPath>(&self, path: P, value: R)
- -> io::Result<()>
- {
- self._symlink(to_cstr(path)?.as_ref(), to_cstr(value)?.as_ref())
- }
- fn _symlink(&self, path: &CStr, link: &CStr) -> io::Result<()> {
- unsafe {
- let res = libc::symlinkat(link.as_ptr(),
- self.0, path.as_ptr());
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
- }
- }
-
- /// Create a subdirectory in this directory
- pub fn create_dir<P: AsPath>(&self, path: P, mode: libc::mode_t)
- -> io::Result<()>
- {
- self._create_dir(to_cstr(path)?.as_ref(), mode)
- }
- fn _create_dir(&self, path: &CStr, mode: libc::mode_t) -> io::Result<()> {
- unsafe {
- let res = libc::mkdirat(self.0, path.as_ptr(), mode);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
- }
- }
-
- /// Rename a file in this directory to another name (keeping same dir)
- pub fn local_rename<P: AsPath, R: AsPath>(&self, old: P, new: R)
- -> io::Result<()>
- {
- rename(self, to_cstr(old)?.as_ref(), self, to_cstr(new)?.as_ref())
- }
-
- /// Similar to `local_rename` but atomically swaps both paths
- ///
- /// Only supported on Linux.
- #[cfg(target_os="linux")]
- pub fn local_exchange<P: AsPath, R: AsPath>(&self, old: P, new: R)
- -> io::Result<()>
- {
- // Workaround https://github.com/tailhook/openat/issues/35
- // AKA https://github.com/rust-lang/libc/pull/2116
- // Unfortunately since we made this libc::c_int in our
- // public API, we can't easily change it right now.
- let flags = libc::RENAME_EXCHANGE as libc::c_int;
- rename_flags(self, to_cstr(old)?.as_ref(),
- self, to_cstr(new)?.as_ref(),
- flags)
- }
-
- /// Remove a subdirectory in this directory
- ///
- /// Note only empty directory may be removed
- pub fn remove_dir<P: AsPath>(&self, path: P)
- -> io::Result<()>
- {
- self._unlink(to_cstr(path)?.as_ref(), libc::AT_REMOVEDIR)
- }
- /// Remove a file in this directory
- pub fn remove_file<P: AsPath>(&self, path: P)
- -> io::Result<()>
- {
- self._unlink(to_cstr(path)?.as_ref(), 0)
- }
- fn _unlink(&self, path: &CStr, flags: libc::c_int) -> io::Result<()> {
- unsafe {
- let res = libc::unlinkat(self.0, path.as_ptr(), flags);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
- }
- }
-
- /// Get the path of this directory (if possible)
- ///
- /// This uses symlinks in `/proc/self`, they sometimes may not be
- /// available so use with care.
- pub fn recover_path(&self) -> io::Result<PathBuf> {
- let fd = self.0;
- if fd != libc::AT_FDCWD {
- read_link(format!("/proc/self/fd/{}", fd))
- } else {
- read_link("/proc/self/cwd")
- }
- }
-
- /// Returns metadata of an entry in this directory
- ///
- /// If the destination path is a symlink, this will return the metadata of the symlink itself.
- /// If you would like to follow the symlink and return the metadata of the target, you will
- /// have to call [`read_link`] to resolve the real path first.
- ///
- /// [`read_link`]: #method.read_link
- pub fn metadata<P: AsPath>(&self, path: P) -> io::Result<Metadata> {
- self._stat(to_cstr(path)?.as_ref(), libc::AT_SYMLINK_NOFOLLOW)
- }
- fn _stat(&self, path: &CStr, flags: libc::c_int) -> io::Result<Metadata> {
- unsafe {
- let mut stat = mem::zeroed();
- let res = libc::fstatat(self.0, path.as_ptr(),
- &mut stat, flags);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(metadata::new(stat))
- }
- }
- }
-
- /// Returns the metadata of the directory itself.
- pub fn self_metadata(&self) -> io::Result<Metadata> {
- unsafe {
- let mut stat = mem::zeroed();
- let res = libc::fstat(self.0, &mut stat);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(metadata::new(stat))
- }
- }
- }
-
- /// Constructs a new `Dir` from a given raw file descriptor,
- /// ensuring it is a directory file descriptor first.
- ///
- /// This function **consumes ownership** of the specified file
- /// descriptor. The returned `Dir` will take responsibility for
- /// closing it when it goes out of scope.
- pub unsafe fn from_raw_fd_checked(fd: RawFd) -> io::Result<Self> {
- let mut stat = mem::zeroed();
- let res = libc::fstat(fd, &mut stat);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- match stat.st_mode & libc::S_IFMT {
- libc::S_IFDIR => Ok(Dir(fd)),
- _ => Err(io::Error::from_raw_os_error(libc::ENOTDIR))
- }
- }
- }
-
- /// Creates a new independently owned handle to the underlying directory.
- pub fn try_clone(&self) -> io::Result<Self> {
- let fd = unsafe { libc::dup(self.0) };
- if fd == -1 {
- Err(io::Error::last_os_error())
- } else {
- unsafe { Self::from_raw_fd_checked(fd) }
- }
- }
-}
-
-/// Rename (move) a file between directories
-///
-/// Files must be on a single filesystem anyway. This funtion does **not**
-/// fallback to copying if needed.
-pub fn rename<P, R>(old_dir: &Dir, old: P, new_dir: &Dir, new: R)
- -> io::Result<()>
- where P: AsPath, R: AsPath,
-{
- _rename(old_dir, to_cstr(old)?.as_ref(), new_dir, to_cstr(new)?.as_ref())
-}
-
-fn _rename(old_dir: &Dir, old: &CStr, new_dir: &Dir, new: &CStr)
- -> io::Result<()>
-{
- unsafe {
- let res = libc::renameat(old_dir.0, old.as_ptr(),
- new_dir.0, new.as_ptr());
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
- }
-}
-
-/// Create a hardlink to a file
-///
-/// Files must be on a single filesystem even if they are in different
-/// directories.
-///
-/// Note: by default ``linkat`` syscall doesn't resolve symbolic links, and
-/// it's also behavior of this function. It's recommended to resolve symlinks
-/// manually if needed.
-pub fn hardlink<P, R>(old_dir: &Dir, old: P, new_dir: &Dir, new: R)
- -> io::Result<()>
- where P: AsPath, R: AsPath,
-{
- _hardlink(old_dir, to_cstr(old)?.as_ref(),
- new_dir, to_cstr(new)?.as_ref(),
- 0)
-}
-
-fn _hardlink(old_dir: &Dir, old: &CStr, new_dir: &Dir, new: &CStr,
- flags: libc::c_int)
- -> io::Result<()>
-{
- unsafe {
- let res = libc::linkat(old_dir.0, old.as_ptr(),
- new_dir.0, new.as_ptr(), flags);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
- }
-}
-
-/// Rename (move) a file between directories with flags
-///
-/// Files must be on a single filesystem anyway. This funtion does **not**
-/// fallback to copying if needed.
-///
-/// Only supported on Linux.
-#[cfg(target_os="linux")]
-pub fn rename_flags<P, R>(old_dir: &Dir, old: P, new_dir: &Dir, new: R,
- flags: libc::c_int)
- -> io::Result<()>
- where P: AsPath, R: AsPath,
-{
- _rename_flags(old_dir, to_cstr(old)?.as_ref(),
- new_dir, to_cstr(new)?.as_ref(),
- flags)
-}
-
-#[cfg(target_os="linux")]
-fn _rename_flags(old_dir: &Dir, old: &CStr, new_dir: &Dir, new: &CStr,
- flags: libc::c_int)
- -> io::Result<()>
-{
- unsafe {
- let res = libc::syscall(
- libc::SYS_renameat2,
- old_dir.0, old.as_ptr(),
- new_dir.0, new.as_ptr(), flags);
- if res < 0 {
- Err(io::Error::last_os_error())
- } else {
- Ok(())
- }
- }
-}
-
-impl AsRawFd for Dir {
- #[inline]
- fn as_raw_fd(&self) -> RawFd {
- self.0
- }
-}
-
-impl FromRawFd for Dir {
- /// The user must guarantee that the passed in `RawFd` is in fact
- /// a directory file descriptor.
- #[inline]
- unsafe fn from_raw_fd(fd: RawFd) -> Dir {
- Dir(fd)
- }
-}
-
-impl IntoRawFd for Dir {
- #[inline]
- fn into_raw_fd(self) -> RawFd {
- let result = self.0;
- mem::forget(self);
- return result;
- }
-}
-
-impl Drop for Dir {
- fn drop(&mut self) {
- let fd = self.0;
- if fd != libc::AT_FDCWD {
- unsafe {
- libc::close(fd);
- }
- }
- }
-}
-
-fn to_cstr<P: AsPath>(path: P) -> io::Result<P::Buffer> {
- path.to_path()
- .ok_or_else(|| {
- io::Error::new(io::ErrorKind::InvalidInput,
- "nul byte in file name")
- })
-}
-
-#[cfg(test)]
-mod test {
- use std::io::{Read};
- use std::path::Path;
- use std::os::unix::io::{FromRawFd, IntoRawFd};
- use crate::{Dir};
-
- #[test]
- fn test_open_ok() {
- assert!(Dir::open("src").is_ok());
- }
-
- #[test]
- #[cfg_attr(target_os="freebsd", should_panic(expected="Not a directory"))]
- fn test_open_file() {
- Dir::open("src/lib.rs").unwrap();
- }
-
- #[test]
- fn test_read_file() {
- let dir = Dir::open("src").unwrap();
- let mut buf = String::new();
- dir.open_file("lib.rs").unwrap()
- .read_to_string(&mut buf).unwrap();
- assert!(buf.find("extern crate libc;").is_some());
- }
-
- #[test]
- fn test_from_into() {
- let dir = Dir::open("src").unwrap();
- let dir = unsafe { Dir::from_raw_fd(dir.into_raw_fd()) };
- let mut buf = String::new();
- dir.open_file("lib.rs").unwrap()
- .read_to_string(&mut buf).unwrap();
- assert!(buf.find("extern crate libc;").is_some());
- }
-
- #[test]
- #[should_panic(expected="No such file or directory")]
- fn test_open_no_dir() {
- Dir::open("src/some-non-existent-file").unwrap();
- }
-
- #[test]
- fn test_list() {
- let dir = Dir::open("src").unwrap();
- let me = dir.list_dir(".").unwrap();
- assert!(me.collect::<Result<Vec<_>, _>>().unwrap()
- .iter().find(|x| {
- x.file_name() == Path::new("lib.rs").as_os_str()
- })
- .is_some());
- }
-
- #[test]
- fn test_from_raw_fd_checked() {
- let fd = Dir::open(".").unwrap().into_raw_fd();
- let dir = unsafe { Dir::from_raw_fd_checked(fd) }.unwrap();
- let filefd = dir.open_file("src/lib.rs").unwrap().into_raw_fd();
- match unsafe { Dir::from_raw_fd_checked(filefd) } {
- Ok(_) => assert!(false, "from_raw_fd_checked succeeded on a non-directory fd!"),
- Err(e) => assert_eq!(e.raw_os_error().unwrap(), libc::ENOTDIR)
- }
- }
-
- #[test]
- fn test_try_clone() {
- let d = Dir::open(".").unwrap();
- let d2 = d.try_clone().unwrap();
- drop(d);
- let _file = d2.open_file("src/lib.rs").unwrap();
- }
-}
diff --git a/openat/src/filetype.rs b/openat/src/filetype.rs
deleted file mode 100644
index efaedbe..0000000
--- a/openat/src/filetype.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use std::fs::Metadata;
-
-/// This is a simplified file type enum that is easy to match
-///
-/// It doesn't represent all the options, because that enum needs to extensible
-/// but most application do not actually need that power, so we provide
-/// this simplified enum that works for many appalications.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum SimpleType {
- /// Entry is a symlink
- Symlink,
- /// Entry is a directory
- Dir,
- /// Entry is a regular file
- File,
- /// Entry is neither a symlink, directory nor a regular file
- Other,
-}
-
-impl SimpleType {
- /// Find out a simple type from a file Metadata (stat)
- pub fn extract(stat: &Metadata) -> SimpleType {
- if stat.file_type().is_symlink() {
- SimpleType::Symlink
- } else if stat.is_dir() {
- SimpleType::Dir
- } else if stat.is_file() {
- SimpleType::File
- } else {
- SimpleType::Other
- }
- }
-}
diff --git a/openat/src/lib.rs b/openat/src/lib.rs
deleted file mode 100644
index a1a2feb..0000000
--- a/openat/src/lib.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-//! # Handling Files Relative to File Descriptor
-//!
-//! Main concept here is a `Dir` which holds `O_PATH` file descriptor, you
-//! can create it with:
-//!
-//! * `Dir::open("/some/path")` -- open this directory as a file descriptor
-//! * `Dir::from_raw_fd(fd)` -- uses a file descriptor provided elsewhere
-//!
-//! *Note after opening file descriptors refer to same directory regardless of
-//! where it's moved or mounted (with `pivot_root` or `mount --move`). It may
-//! also be unmounted or be out of chroot and you will still be able to
-//! access files relative to it.*
-//!
-//! *Note2: The constructor `Dir::cwd()` is deprecated, and it's recommended
-//! to use `Dir::open(".")` instead.*
-//!
-//! *Note3: Some OS's (e.g., macOS) do not provide `O_PATH`, in which case the
-//! file descriptor is of regular type.*
-//!
-//! Most other operations are done on `Dir` object and are executed relative
-//! to it:
-//!
-//! * `Dir::list_dir()`
-//! * `Dir::sub_dir()`
-//! * `Dir::read_link()`
-//! * `Dir::open_file()`
-//! * `Dir::create_file()`
-//! * `Dir::update_file()`
-//! * `Dir::create_dir()`
-//! * `Dir::symlink()`
-//! * `Dir::local_rename()`
-//!
-//! Functions that expect path relative to the directory accept both the
-//! traditional path-like objects, such as Path, PathBuf and &str, and
-//! `Entry` type returned from `list_dir()`. The latter is faster as underlying
-//! system call wants `CString` and we keep that in entry.
-//!
-//! Note that if path supplied to any method of dir is absolute the Dir file
-//! descriptor is ignored.
-//!
-//! Also while all methods of dir accept any path if you want to prevent
-//! certain symlink attacks and race condition you should only use
-//! a single-component path. I.e. open one part of a chain at a time.
-//!
-#![warn(missing_docs)]
-
-extern crate libc;
-
-mod dir;
-mod list;
-mod name;
-mod filetype;
-mod metadata;
-
-pub use crate::list::DirIter;
-pub use crate::name::AsPath;
-pub use crate::dir::{rename, hardlink};
-pub use crate::filetype::SimpleType;
-pub use crate::metadata::Metadata;
-
-use std::ffi::CString;
-use std::os::unix::io::RawFd;
-
-/// A safe wrapper around directory file descriptor
-///
-/// Construct it either with ``Dir::cwd()`` or ``Dir::open(path)``
-///
-#[derive(Debug)]
-pub struct Dir(RawFd);
-
-/// Entry returned by iterating over `DirIter` iterator
-#[derive(Debug)]
-pub struct Entry {
- name: CString,
- file_type: Option<SimpleType>,
-}
-
-#[cfg(test)]
-mod test {
- use std::mem;
- use super::Dir;
-
- fn assert_sync<T: Sync>(x: T) -> T { x }
- fn assert_send<T: Send>(x: T) -> T { x }
-
- #[test]
- fn test() {
- let d = Dir(3);
- let d = assert_sync(d);
- let d = assert_send(d);
- // don't execute close for our fake RawFd
- mem::forget(d);
- }
-}
-
diff --git a/openat/src/list.rs b/openat/src/list.rs
deleted file mode 100644
index 5b4d3cd..0000000
--- a/openat/src/list.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-use std::io;
-use std::ptr;
-use std::ffi::{CStr, OsStr};
-use std::os::unix::ffi::OsStrExt;
-
-use libc;
-
-use crate::{Dir, Entry, SimpleType};
-
-
-// We have such weird constants because C types are ugly
-const DOT: [libc::c_char; 2] = [b'.' as libc::c_char, 0];
-const DOTDOT: [libc::c_char; 3] = [b'.' as libc::c_char, b'.' as libc::c_char, 0];
-
-
-/// Iterator over directory entries
-///
-/// Created using `Dir::list_dir()`
-#[derive(Debug)]
-pub struct DirIter {
- dir: *mut libc::DIR,
-}
-
-/// Position in a DirIter as obtained by 'DirIter::current_position()'
-///
-/// The position is only valid for the DirIter it was retrieved from.
-pub struct DirPosition {
- pos: libc::c_long,
-}
-
-impl Entry {
- /// Returns the file name of this entry
- pub fn file_name(&self) -> &OsStr {
- OsStr::from_bytes(self.name.to_bytes())
- }
- /// Returns the simplified type of this entry
- pub fn simple_type(&self) -> Option<SimpleType> {
- self.file_type
- }
-}
-
-#[cfg(any(target_os="linux", target_os="fuchsia"))]
-unsafe fn errno_location() -> *mut libc::c_int {
- libc::__errno_location()
-}
-
-#[cfg(any(target_os="openbsd", target_os="netbsd", target_os="android"))]
-unsafe fn errno_location() -> *mut libc::c_int {
- libc::__errno()
-}
-
-#[cfg(not(any(target_os="linux", target_os="openbsd", target_os="netbsd", target_os="android", target_os="fuchsia")))]
-unsafe fn errno_location() -> *mut libc::c_int {
- libc::__error()
-}
-
-impl DirIter {
-
- unsafe fn next_entry(&mut self) -> io::Result<Option<&libc::dirent>>
- {
- // Reset errno to detect if error occurred
- *errno_location() = 0;
-
- let entry = libc::readdir(self.dir);
- if entry == ptr::null_mut() {
- if *errno_location() == 0 {
- return Ok(None)
- } else {
- return Err(io::Error::last_os_error());
- }
- }
- return Ok(Some(&*entry));
- }
-
- /// Returns the current directory iterator position. The result should be handled as opaque value
- pub fn current_position(&self) -> io::Result<DirPosition> {
- let pos = unsafe { libc::telldir(self.dir) };
-
- if pos == -1 {
- Err(io::Error::last_os_error())
- } else {
- Ok(DirPosition { pos })
- }
- }
-
- // note the C-API does not report errors for seekdir/rewinddir, thus we don't do as well.
- /// Sets the current directory iterator position to some location queried by 'current_position()'
- pub fn seek(&self, position: DirPosition) {
- unsafe { libc::seekdir(self.dir, position.pos) };
- }
-
- /// Resets the current directory iterator position to the beginning
- pub fn rewind(&self) {
- unsafe { libc::rewinddir(self.dir) };
- }
-}
-
-pub fn open_dirfd(fd: libc::c_int) -> io::Result<DirIter> {
- let dir = unsafe { libc::fdopendir(fd) };
- if dir == std::ptr::null_mut() {
- Err(io::Error::last_os_error())
- } else {
- Ok(DirIter { dir: dir })
- }
-}
-
-pub fn open_dir(dir: &Dir, path: &CStr) -> io::Result<DirIter> {
- let dir_fd = unsafe {
- libc::openat(dir.0, path.as_ptr(), libc::O_DIRECTORY|libc::O_CLOEXEC)
- };
- if dir_fd < 0 {
- Err(io::Error::last_os_error())
- } else {
- open_dirfd(dir_fd)
- }
-}
-
-impl Iterator for DirIter {
- type Item = io::Result<Entry>;
- fn next(&mut self) -> Option<Self::Item> {
- unsafe {
- loop {
- match self.next_entry() {
- Err(e) => return Some(Err(e)),
- Ok(None) => return None,
- Ok(Some(e)) if e.d_name[..2] == DOT => continue,
- Ok(Some(e)) if e.d_name[..3] == DOTDOT => continue,
- Ok(Some(e)) => {
- return Some(Ok(Entry {
- name: CStr::from_ptr((e.d_name).as_ptr())
- .to_owned(),
- file_type: match e.d_type {
- 0 => None,
- libc::DT_REG => Some(SimpleType::File),
- libc::DT_DIR => Some(SimpleType::Dir),
- libc::DT_LNK => Some(SimpleType::Symlink),
- _ => Some(SimpleType::Other),
- },
- }));
- }
- }
- }
- }
- }
-}
-
-impl Drop for DirIter {
- fn drop(&mut self) {
- unsafe {
- libc::closedir(self.dir);
- }
- }
-}
diff --git a/openat/src/metadata.rs b/openat/src/metadata.rs
deleted file mode 100644
index de7cc22..0000000
--- a/openat/src/metadata.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use std::fs::Permissions;
-use std::os::unix::fs::PermissionsExt;
-
-use libc;
-
-use crate::SimpleType;
-
-
-/// A file metadata
-///
-/// Because we can't freely create a `std::fs::Metadata` object we have to
-/// implement our own structure.
-pub struct Metadata {
- stat: libc::stat,
-}
-
-impl Metadata {
- /// Returns simplified type of the directory entry
- pub fn simple_type(&self) -> SimpleType {
- let typ = self.stat.st_mode & libc::S_IFMT;
- match typ {
- libc::S_IFREG => SimpleType::File,
- libc::S_IFDIR => SimpleType::Dir,
- libc::S_IFLNK => SimpleType::Symlink,
- _ => SimpleType::Other,
- }
- }
- /// Returns underlying stat structure
- pub fn stat(&self) -> &libc::stat {
- &self.stat
- }
- /// Returns `true` if the entry is a regular file
- pub fn is_file(&self) -> bool {
- self.simple_type() == SimpleType::File
- }
- /// Returns `true` if the entry is a directory
- pub fn is_dir(&self) -> bool {
- self.simple_type() == SimpleType::Dir
- }
- /// Returns permissions of the entry
- pub fn permissions(&self) -> Permissions {
- Permissions::from_mode(self.stat.st_mode as u32)
- }
- /// Returns file size
- pub fn len(&self) -> u64 {
- self.stat.st_size as u64
- }
-}
-
-pub fn new(stat: libc::stat) -> Metadata {
- Metadata { stat: stat }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn dir() {
- let d = crate::Dir::open(".").unwrap();
- let m = d.metadata("src").unwrap();
- assert_eq!(m.simple_type(), SimpleType::Dir);
- assert!(m.is_dir());
- assert!(!m.is_file());
- }
-
- #[test]
- fn file() {
- let d = crate::Dir::open("src").unwrap();
- let m = d.metadata("lib.rs").unwrap();
- assert_eq!(m.simple_type(), SimpleType::File);
- assert!(!m.is_dir());
- assert!(m.is_file());
- }
-}
diff --git a/openat/src/name.rs b/openat/src/name.rs
deleted file mode 100644
index c181db1..0000000
--- a/openat/src/name.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use std::ffi::{OsStr, CStr, CString};
-use std::path::{Path, PathBuf};
-use std::os::unix::ffi::OsStrExt;
-
-use crate::{Entry};
-
-
-/// The purpose of this is similar to `AsRef<Path>` but it's optimized for
-/// things that can be directly used as `CStr` (which is type passed to
-/// the underlying system call).
-///
-/// This trait should be implemented for everything for which `AsRef<Path>`
-/// is implemented
-pub trait AsPath {
- /// The return value of the `to_path` that holds data copied from the
- /// original path (if copy is needed, otherwise it's just a reference)
- type Buffer: AsRef<CStr>;
- /// Returns `None` when path contains a zero byte
- fn to_path(self) -> Option<Self::Buffer>;
-}
-
-impl<'a> AsPath for &'a Path {
- type Buffer = CString;
- fn to_path(self) -> Option<CString> {
- CString::new(self.as_os_str().as_bytes()).ok()
- }
-}
-
-impl<'a> AsPath for &'a PathBuf {
- type Buffer = CString;
- fn to_path(self) -> Option<CString> {
- CString::new(self.as_os_str().as_bytes()).ok()
- }
-}
-
-impl<'a> AsPath for &'a OsStr {
- type Buffer = CString;
- fn to_path(self) -> Option<CString> {
- CString::new(self.as_bytes()).ok()
- }
-}
-
-impl<'a> AsPath for &'a str {
- type Buffer = CString;
- fn to_path(self) -> Option<CString> {
- CString::new(self.as_bytes()).ok()
- }
-}
-
-impl<'a> AsPath for &'a String {
- type Buffer = CString;
- fn to_path(self) -> Option<CString> {
- CString::new(self.as_bytes()).ok()
- }
-}
-
-impl<'a> AsPath for String {
- type Buffer = CString;
- fn to_path(self) -> Option<CString> {
- CString::new(self).ok()
- }
-}
-
-impl<'a> AsPath for &'a CStr {
- type Buffer = &'a CStr;
- fn to_path(self) -> Option<&'a CStr> {
- Some(self)
- }
-}
-
-impl<'a> AsPath for &'a Entry {
- type Buffer = &'a CStr;
- fn to_path(self) -> Option<&'a CStr> {
- Some(&self.name)
- }
-}