use std::{
io,
path::{Path, PathBuf},
os::{
raw::c_int,
unix::{
ffi::OsStrExt,
fs::MetadataExt,
}
},
};
pub trait PathPermission {
fn access(&self, amode: c_int) -> io::Result<bool>;
fn is_readable(&self) -> io::Result<bool>;
fn is_writable(&self) -> io::Result<bool>;
fn is_excutable(&self) -> io::Result<bool>;
fn is_creatable(&self) -> io::Result<bool>;
fn is_removable(&self) -> io::Result<bool>;
fn check_access(&self, mode: u16) -> io::Result<bool>;
fn get_access(&self) -> io::Result<String>;
fn chmod(&self, mode: u16) -> io::Result<bool>;
}
impl PathPermission for Path {
fn access(&self, amode: c_int) -> io::Result<bool> {
access(self, amode)
}
fn is_readable(&self) -> io::Result<bool> {
self.access(libc::R_OK)
}
fn is_writable(&self) -> io::Result<bool> {
self.access(libc::W_OK)
}
fn is_excutable(&self) -> io::Result<bool> {
self.access(libc::X_OK)
}
fn is_creatable(&self) -> io::Result<bool> {
let parent = match self.parent() {
None => Path::new("./"),
Some(parent) => parent,
};
if ! parent.exists() {
parent.is_creatable()
} else {
parent.access(libc::X_OK + libc::W_OK)
}
}
fn is_removable(&self) -> io::Result<bool> {
if ! self.exists() {
return Ok(false)
}
let parent = match self.parent() {
None => Path::new("./"),
Some(parent) => parent,
};
if ! parent.check_access(0o1000).unwrap() {
parent.access(libc::X_OK + libc::W_OK)
} else {
unsafe {
if libc::getuid() == self.metadata().unwrap().uid() {
parent.access(libc::X_OK + libc::W_OK)
} else {
Ok(false)
}
}
}
}
fn check_access(&self, mode: u16) -> io::Result<bool> {
if let Ok(metadata) = self.metadata() {
if metadata.mode() as u16 & mode == mode {
Ok(true)
} else {
Ok(false)
}
} else {
Err(io::Error::last_os_error())
}
}
fn get_access(&self) -> io::Result<String> {
if let Ok(metadata) = self.metadata() {
Ok(format!("{:o}{:o}",
metadata.mode() as u16 & 0o7000,
metadata.mode() as u16 & 0o777))
} else {
Err(io::Error::last_os_error())
}
}
fn chmod(&self, mode: u16) -> io::Result<bool> {
chmod(self, mode)
}
}
impl PathPermission for PathBuf {
fn access(&self, amode: c_int) -> io::Result<bool> {
self.as_path().access(amode)
}
fn is_readable(&self) -> io::Result<bool> {
self.as_path().is_readable()
}
fn is_writable(&self) -> io::Result<bool> {
self.as_path().is_writable()
}
fn is_excutable(&self) -> io::Result<bool> {
self.as_path().is_excutable()
}
fn is_creatable(&self) -> io::Result<bool> {
self.as_path().is_creatable()
}
fn is_removable(&self) -> io::Result<bool> {
self.as_path().is_removable()
}
fn check_access(&self, mode: u16) -> io::Result<bool> {
self.as_path().check_access(mode)
}
fn get_access(&self) -> io::Result<String> {
self.as_path().get_access()
}
fn chmod(&self, mode: u16) -> io::Result<bool> {
self.as_path().chmod(mode)
}
}
fn access(path: &Path, mod_mask: c_int) ->io::Result<bool> {
let mut buf = Vec::new();
let buf_ptr;
buf.extend(path.as_os_str().as_bytes());
buf.push(0);
buf_ptr = buf.as_ptr() as *const libc::c_char;
let result = unsafe {
libc::access(buf_ptr, mod_mask)
};
match result {
0 => Ok(true),
_ => {
let err = io::Error::last_os_error();
if err.raw_os_error().unwrap() == libc::EACCES {
Ok(false) } else {
Err(err) }
}
}
}
fn chmod(path: &Path, mode: u16) -> io::Result<bool> {
let mut buf = Vec::new();
let buf_ptr;
buf.extend(path.as_os_str().as_bytes());
buf.push(0);
buf_ptr = buf.as_ptr() as *const libc::c_char;
let result = unsafe {
libc::chmod(buf_ptr, mode)
};
match result {
0 => Ok(true),
1 | 2 => Ok(false),
_ => Err(io::Error::last_os_error()),
}
}