use crate::{
capi::utils::{self, CHandle, CRoot, Leakable},
error, syscalls, InodeType, OpenFlags, RenameFlags, Root,
};
use std::{
fs::Permissions,
os::unix::{
fs::PermissionsExt,
io::{AsRawFd, IntoRawFd, RawFd},
},
};
use libc::{c_char, c_int, c_uint, dev_t};
use snafu::ResultExt;
#[no_mangle]
pub extern "C" fn pathrs_open(path: *const c_char) -> &'static mut CRoot {
match utils::parse_path(path).and_then(Root::open) {
Ok(root) => CRoot::from(root),
Err(err) => CRoot::from_err(err),
}
.leak()
}
#[no_mangle]
pub extern "C" fn pathrs_reopen(handle: &CHandle, flags: c_int) -> RawFd {
handle.wrap_err(-1, |handle| {
let flags = OpenFlags(flags);
let file = handle.reopen(flags)?;
if flags.0 & libc::O_CLOEXEC == 0 {
syscalls::fcntl_unset_cloexec(file.as_raw_fd()).context(error::RawOsError {
operation: "clear O_CLOEXEC on fd",
})?;
}
Ok(file.into_raw_fd())
})
}
#[no_mangle]
pub extern "C" fn pathrs_resolve(
root: &CRoot,
path: *const c_char,
) -> Option<&'static mut CHandle> {
root.wrap_err(None, |root| {
root.resolve(utils::parse_path(path)?)
.map(CHandle::from)
.map(Leakable::leak)
.map(Option::from)
})
}
#[no_mangle]
pub extern "C" fn pathrs_rename(
root: &CRoot,
src: *const c_char,
dst: *const c_char,
flags: c_int,
) -> c_int {
root.wrap_err(-1, |root| {
let flags = RenameFlags(flags);
root.rename(utils::parse_path(src)?, utils::parse_path(dst)?, flags)
.and(Ok(0))
})
}
#[no_mangle]
pub extern "C" fn pathrs_creat(
root: &CRoot,
path: *const c_char,
mode: c_uint,
) -> Option<&'static mut CHandle> {
root.wrap_err(None, |root| {
let mode = mode & !libc::S_IFMT;
let perm = Permissions::from_mode(mode);
root.create_file(utils::parse_path(path)?, &perm)
.map(CHandle::from)
.map(Leakable::leak)
.map(Option::from)
})
}
#[no_mangle]
pub extern "C" fn pathrs_mkdir(root: &CRoot, path: *const c_char, mode: c_uint) -> c_int {
let mode = mode & !libc::S_IFMT;
pathrs_mknod(root, path, libc::S_IFDIR | mode, 0)
}
#[no_mangle]
pub extern "C" fn pathrs_mknod(
root: &CRoot,
path: *const c_char,
mode: c_uint,
dev: dev_t,
) -> c_int {
root.wrap_err(-1, |root| {
let fmt = mode & libc::S_IFMT;
let perms = Permissions::from_mode(mode ^ fmt);
let path = utils::parse_path(path)?;
let inode_type = match fmt {
libc::S_IFREG => InodeType::File(&perms),
libc::S_IFDIR => InodeType::Directory(&perms),
libc::S_IFBLK => InodeType::BlockDevice(&perms, dev),
libc::S_IFCHR => InodeType::CharacterDevice(&perms, dev),
libc::S_IFIFO => InodeType::Fifo(&perms),
libc::S_IFSOCK => error::NotImplemented {
feature: "mknod(S_IFSOCK)",
}
.fail()?,
_ => error::InvalidArgument {
name: "mode",
description: "invalid S_IFMT mask",
}
.fail()?,
};
root.create(path, &inode_type).and(Ok(0))
})
}
#[no_mangle]
pub extern "C" fn pathrs_symlink(
root: &CRoot,
path: *const c_char,
target: *const c_char,
) -> c_int {
root.wrap_err(-1, |root| {
let path = utils::parse_path(path)?;
let target = utils::parse_path(target)?;
root.create(path, &InodeType::Symlink(target)).and(Ok(0))
})
}
#[no_mangle]
pub extern "C" fn pathrs_hardlink(
root: &CRoot,
path: *const c_char,
target: *const c_char,
) -> c_int {
root.wrap_err(-1, |root| {
let path = utils::parse_path(path)?;
let target = utils::parse_path(target)?;
root.create(path, &InodeType::Hardlink(target)).and(Ok(0))
})
}