diff options
author | Tomasz Kramkowski <tomasz@kramkow.ski> | 2023-01-27 14:22:33 +0000 |
---|---|---|
committer | Tomasz Kramkowski <tomasz@kramkow.ski> | 2023-01-27 14:22:33 +0000 |
commit | bdd8126b938de11272f79bb7f512316740469ed7 (patch) | |
tree | 5a46eaf4942f4a21552cf606b35effe5ac1c97c4 /openat/src | |
parent | 6cef9f0fc159de4c9fd708050ec76adb4e74d390 (diff) | |
download | pam_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.rs | 696 | ||||
-rw-r--r-- | openat/src/filetype.rs | 33 | ||||
-rw-r--r-- | openat/src/lib.rs | 95 | ||||
-rw-r--r-- | openat/src/list.rs | 153 | ||||
-rw-r--r-- | openat/src/metadata.rs | 75 | ||||
-rw-r--r-- | openat/src/name.rs | 76 |
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) - } -} |