use crate::error::*;
use errno;
use libc;
use log::{debug, warn};
use std::ffi::{CString, OsString};
use std::fs::{self, Permissions};
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
#[cfg(not(target_os = "windows"))]
pub fn path_to_bytes<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
use std::os::unix::ffi::OsStrExt;
Ok(Vec::from(path.as_ref().as_os_str().as_bytes()))
}
#[cfg(target_os = "windows")]
pub fn path_to_bytes<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
use byteorder::{BigEndian, WriteBytesExt};
use std::os::windows::ffi::OsStrExt;
let chars: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
let mut bytes: Vec<u8> = Vec::with_capacity(chars.len() * 2);
for c in chars {
bytes.write_u16::<BigEndian>(c)?;
}
Ok(bytes)
}
#[cfg(not(target_os = "windows"))]
pub fn path_from_bytes(bytes: Vec<u8>) -> Result<PathBuf> {
use std::os::unix::ffi::OsStringExt;
Ok(PathBuf::from(OsString::from_vec(bytes)))
}
#[cfg(target_os = "windows")]
pub fn path_from_bytes(mut bytes: Vec<u8>) -> Result<PathBuf> {
use byteorder::{BigEndian, ReadBytesExt};
use std::os::windows::ffi::OsStringExt;
let mut chars: Vec<u16> = vec![0; bytes.len() / 2];
bytes.read_u16_into::<BigEndian>(chars.as_mut_slice())?;
Ok(PathBuf::from(OsString::from_wide(chars.as_slice())))
}
pub fn create_file<P: AsRef<Path>>(path: P) -> Result<()> {
let _f = fs::File::create(path)?;
Ok(())
}
#[cfg(not(target_os = "windows"))]
pub fn create_symlink<T: AsRef<Path>, S: AsRef<Path>>(target: T, symlink: S) -> Result<()> {
Ok(::std::os::unix::fs::symlink(target, symlink)?)
}
#[cfg(target_os = "windows")]
pub fn create_symlink<T: AsRef<Path>, S: AsRef<Path>>(target: T, symlink: S) -> Result<()> {
Ok(if target.as_ref().is_dir() {
::std::os::windows::fs::symlink_dir(target, symlink)?
} else {
::std::os::windows::fs::symlink_file(target, symlink)?
})
}
#[cfg(not(target_os = "windows"))]
pub fn set_permissions_mode<P: AsRef<Path>>(path: P, mode: u32) -> Result<()> {
use std::os::unix::fs::PermissionsExt;
let permissions = Permissions::from_mode(mode);
Ok(::std::fs::set_permissions(path, permissions)?)
}
#[cfg(target_os = "windows")]
pub fn set_permissions_mode<P: AsRef<Path>>(_: P, _: u32) -> Result<()> {
Ok(())
}
#[cfg(not(target_os = "windows"))]
pub fn set_ownership<P: AsRef<Path>>(
path: P,
uid: u32,
gid: u32,
fail_on_access_denied: bool,
follow: bool,
) -> Result<()> {
use errno::errno;
use libc::c_int;
let path_cstr = CString::new(path_to_bytes(path.as_ref())?)?;
let ret: c_int = unsafe {
match follow {
false => libc::lchown(path_cstr.as_ptr(), uid, gid),
true => libc::chown(path_cstr.as_ptr(), uid, gid),
}
};
if ret == -1 {
let error = errno();
if (error.0 == libc::EACCES || error.0 == libc::EPERM) && !fail_on_access_denied {
warn!(
"Failed to change ownership of '{}': {}",
path.as_ref().display(),
error
);
} else {
return Err(std::io::Error::from_raw_os_error(error.into()).into());
}
}
Ok(())
}
#[cfg(target_os = "windows")]
pub fn set_ownership<P: AsRef<Path>>(_: P, _: u32, _: u32, _: bool, _: bool) -> Result<()> {
Ok(())
}
#[cfg(not(target_os = "windows"))]
#[derive(Debug)]
enum SysconfBufferKind {
User,
Group,
}
#[cfg(not(target_os = "windows"))]
fn get_sysconf_buffer_size(kind: SysconfBufferKind) -> usize {
let k = match kind {
SysconfBufferKind::User => libc::_SC_GETPW_R_SIZE_MAX,
SysconfBufferKind::Group => libc::_SC_GETGR_R_SIZE_MAX,
};
let mut s = unsafe { libc::sysconf(k) } as usize;
if s == usize::MAX {
debug!(
"libc has no maximum {:?} buffer size; defaulting to 1024 bytes",
kind
);
s = 1024;
} else if s > 1024 * 1024 * 10 {
debug!(
"libc returned unreasonable {:?} buffer size ({} bytes); defaulting to 1024 bytes",
kind, s
);
s = 1024;
}
s
}
#[cfg(not(target_os = "windows"))]
fn lookup_uid(name: &str) -> Result<u32> {
let mut passwd = unsafe {
mem::transmute::<[u8; mem::size_of::<libc::passwd>()], libc::passwd>(
[0_u8; mem::size_of::<libc::passwd>()],
)
};
let mut passwd_ptr: *mut libc::passwd = ptr::null_mut();
let cname = CString::new(name)?;
let buf_len = get_sysconf_buffer_size(SysconfBufferKind::User);
let mut buf = vec![0_i8; buf_len];
let ret = unsafe {
libc::getpwnam_r(
cname.as_ptr(),
&mut passwd,
buf.as_mut_ptr(),
buf_len,
&mut passwd_ptr,
)
};
if passwd_ptr.is_null() {
if ret == 0
|| ret == libc::ENOENT
|| ret == libc::ESRCH
|| ret == libc::EBADF
|| ret == libc::EPERM
{
return Err(Error::NotFound(format!(
"unrecognized user name '{}'",
name
)));
} else {
return Err(std::io::Error::from_raw_os_error(ret).into());
}
}
Ok(passwd.pw_uid)
}
#[cfg(not(target_os = "windows"))]
fn lookup_gid(name: &str) -> Result<u32> {
let mut group = libc::group {
gr_name: ptr::null_mut(),
gr_passwd: ptr::null_mut(),
gr_gid: 0,
gr_mem: ptr::null_mut(),
};
let mut group_ptr: *mut libc::group = ptr::null_mut();
let cname = CString::new(name)?;
let buf_len = get_sysconf_buffer_size(SysconfBufferKind::Group);
let mut buf = vec![0_i8; buf_len];
let ret = unsafe {
libc::getgrnam_r(
cname.as_ptr(),
&mut group,
buf.as_mut_ptr(),
buf_len,
&mut group_ptr,
)
};
if group_ptr.is_null() {
if ret == 0
|| ret == libc::ENOENT
|| ret == libc::ESRCH
|| ret == libc::EBADF
|| ret == libc::EPERM
{
return Err(Error::NotFound(format!(
"unrecognized group name '{}'",
name
)));
} else {
return Err(std::io::Error::from_raw_os_error(ret).into());
}
}
Ok(group.gr_gid)
}
#[cfg(not(target_os = "windows"))]
pub fn set_ownership_by_name<P: AsRef<Path>>(
path: P,
user: &str,
group: &str,
fail_on_access_denied: bool,
follow: bool,
) -> Result<()> {
set_ownership(
path,
lookup_uid(user)?,
lookup_gid(group)?,
fail_on_access_denied,
follow,
)
}
#[cfg(target_os = "windows")]
pub fn set_ownership_by_name<P: AsRef<Path>>(
_: P,
_: &str,
_: &str,
_: bool,
_: bool,
) -> Result<()> {
Ok(())
}