#[macro_use]
extern crate log;
use std::borrow::{Borrow, BorrowMut};
use std::cmp::min;
use std::env;
use std::ffi::{CStr, CString};
use std::mem::size_of;
use std::ops::{BitAnd, BitOr};
use std::ptr::{null, null_mut};
use libc::{c_char, c_int, c_void, dev_t, flock, mode_t, off_t, size_t, stat, EINTR};
mod filesystem;
mod fuse;
mod utils;
pub use crate::filesystem::FileSystem;
use crate::fuse::{
fuse_add_direntry, fuse_remove_signal_handlers, fuse_reply_attr, fuse_reply_bmap,
fuse_reply_buf, fuse_reply_create, fuse_reply_entry, fuse_reply_err, fuse_reply_lock,
fuse_reply_lseek, fuse_reply_none, fuse_reply_open, fuse_reply_poll, fuse_reply_readlink,
fuse_reply_statfs, fuse_reply_write, fuse_reply_xattr, fuse_req_ctx, fuse_req_userdata,
fuse_session_destroy, fuse_session_exited, fuse_session_mount, fuse_session_new,
fuse_session_process_buf, fuse_session_receive_buf, fuse_session_reset, fuse_session_unmount,
fuse_set_signal_handlers, FuseArgs, FuseBuf, FuseConnInfo, FuseLowLevelOps, FuseReq,
FuseSession,
};
pub use crate::fuse::{
FileType, FuseAttr, FuseBufvec, FuseCtx, FuseDirectory, FuseEntryParam, FuseFileInfo,
FuseForgetData, FuseLock, FusePollhandle, FuseStatvfs,
};
pub enum FuseOpFlag {
Init = 1 << 0,
Destroy = 1 << 1,
Lookup = 1 << 2,
Forget = 1 << 3,
Getattr = 1 << 4,
Setattr = 1 << 5,
Readlink = 1 << 6,
Mknod = 1 << 7,
Mkdir = 1 << 8,
Unlink = 1 << 9,
Rmdir = 1 << 10,
Symlink = 1 << 11,
Rename = 1 << 12,
Link = 1 << 13,
Open = 1 << 14,
Read = 1 << 15,
Write = 1 << 16,
Flush = 1 << 17,
Release = 1 << 18,
Fsync = 1 << 19,
Opendir = 1 << 20,
Readdir = 1 << 21,
Releasedir = 1 << 22,
Fsyncdir = 1 << 23,
Statfs = 1 << 24,
Setxattr = 1 << 25,
Getxattr = 1 << 26,
Listxattr = 1 << 27,
Removexattr = 1 << 28,
Access = 1 << 29,
Create = 1 << 30,
Getlk = 1 << 31,
Setlk = 1 << 32,
Bmap = 1 << 33,
Poll = 1 << 34,
WriteBuf = 1 << 35,
ForgetMulti = 1 << 36,
Flock = 1 << 37,
Fallocate = 1 << 38,
Readdirplus = 1 << 39,
CopyFileRange = 1 << 40,
Lseek = 1 << 41,
}
impl BitOr<FuseOpFlag> for FuseOpFlag {
type Output = u64;
fn bitor(self, rhs: Self) -> Self::Output {
self as u64 | rhs as u64
}
}
impl BitOr<FuseOpFlag> for u64 {
type Output = u64;
fn bitor(self, rhs: FuseOpFlag) -> Self::Output {
self | rhs as u64
}
}
impl BitAnd<FuseOpFlag> for u64 {
type Output = u64;
fn bitand(self, rhs: FuseOpFlag) -> Self::Output {
self & rhs as u64
}
}
struct FuseOps;
macro_rules! filesystem {
($req:expr) => {
unsafe {
let userdata = fuse_req_userdata($req);
(userdata as *mut T).as_mut().unwrap()
}
};
}
macro_rules! ctx {
($req:expr) => {
unsafe { fuse_req_ctx($req).as_ref().unwrap() }
};
}
macro_rules! op {
($ops:expr, $name:ident, $flag:ident) => {
if $ops & FuseOpFlag::$flag == 0 {
null()
} else {
(FuseOps::$name::<T>) as *const _
}
};
}
impl FuseOps {
fn fuse_low_level_ops<T: FileSystem>(ops: u64) -> FuseLowLevelOps {
FuseLowLevelOps {
init: op!(ops, init, Init),
destroy: op!(ops, destroy, Destroy),
lookup: op!(ops, lookup, Lookup),
forget: op!(ops, forget, Forget),
getattr: op!(ops, getattr, Getattr),
setattr: op!(ops, setattr, Setattr),
readlink: op!(ops, readlink, Readlink),
mknod: op!(ops, mknod, Mknod),
mkdir: op!(ops, mkdir, Mkdir),
unlink: op!(ops, unlink, Unlink),
rmdir: op!(ops, rmdir, Rmdir),
symlink: op!(ops, symlink, Symlink),
rename: op!(ops, rename, Rename),
link: op!(ops, link, Link),
open: op!(ops, open, Open),
read: op!(ops, read, Read),
write: op!(ops, write, Write),
flush: op!(ops, flush, Flush),
release: op!(ops, release, Release),
fsync: op!(ops, fsync, Fsync),
opendir: op!(ops, opendir, Opendir),
readdir: op!(ops, readdir, Readdir),
releasedir: op!(ops, releasedir, Releasedir),
fsyncdir: op!(ops, fsyncdir, Fsyncdir),
statfs: op!(ops, statfs, Statfs),
setxattr: op!(ops, setxattr, Setxattr),
getxattr: op!(ops, getxattr, Getxattr),
listxattr: op!(ops, listxattr, Listxattr),
removexattr: op!(ops, removexattr, Removexattr),
access: op!(ops, access, Access),
create: op!(ops, create, Create),
getlk: op!(ops, getlk, Getlk),
setlk: op!(ops, setlk, Setlk),
bmap: op!(ops, bmap, Bmap),
poll: op!(ops, poll, Poll),
write_buf: op!(ops, write_buf, WriteBuf),
forget_multi: op!(ops, forget_multi, ForgetMulti),
flock: op!(ops, flock, Flock),
fallocate: op!(ops, fallocate, Fallocate),
readdirplus: op!(ops, readdirplus, Readdirplus),
copy_file_range: op!(ops, copy_file_range, CopyFileRange),
lseek: op!(ops, lseek, Lseek),
}
}
fn init<T: FileSystem>(userdata: *mut c_void, _conn: *mut FuseConnInfo) {
let file_system = unsafe { (userdata as *mut T).as_mut().unwrap() };
let _ = file_system.init();
}
fn destroy<T: FileSystem>(userdata: *mut c_void) {
let file_system = unsafe { (userdata as *mut T).as_mut().unwrap() };
let _ = file_system.destroy();
}
fn lookup<T: FileSystem>(req: *mut FuseReq, parent: u64, name: *const c_char) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.lookup(ctx, parent, unsafe { CStr::from_ptr(name).to_bytes() }) {
Ok(entry_param) => unsafe {
let _ret = fuse_reply_entry(req, entry_param.borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn forget<T: FileSystem>(req: *mut FuseReq, ino: u64, nlookup: u64) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
file_system.forget(ctx, FuseForgetData { ino, nlookup });
unsafe {
fuse_reply_none(req);
}
}
fn getattr<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.getattr(ctx, ino, unsafe { fi.as_mut() }) {
Ok((attr, timeout)) => unsafe {
let _ret = fuse_reply_attr(req, attr.convert().borrow(), timeout);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn setattr<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
attr: *mut stat,
to_set: i16,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.setattr(
ctx,
ino,
&FuseAttr::new(unsafe { attr.as_ref().unwrap() }),
to_set,
unsafe { fi.as_mut() },
) {
Ok((attr, timeout)) => unsafe {
let _ret = fuse_reply_attr(req, attr.convert().borrow(), timeout);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn readlink<T: FileSystem>(req: *mut FuseReq, ino: u64) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.readlink(ctx, ino) {
Ok(link) => unsafe {
let link = CString::new(&link[..]).unwrap();
let _ret = fuse_reply_readlink(req, link.as_ptr());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn mknod<T: FileSystem>(
req: *mut FuseReq,
parent: u64,
name: *const c_char,
mode: mode_t,
rdev: dev_t,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.mknod(
ctx,
parent,
unsafe { CStr::from_ptr(name).to_bytes() },
mode,
rdev,
) {
Ok(entry_param) => unsafe {
let _ret = fuse_reply_entry(req, entry_param.borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn mkdir<T: FileSystem>(req: *mut FuseReq, parent: u64, name: *const c_char, mode: mode_t) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.mkdir(
ctx,
parent,
unsafe { CStr::from_ptr(name).to_bytes() },
mode,
) {
Ok(entry_param) => unsafe {
let _ret = fuse_reply_entry(req, entry_param.borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn unlink<T: FileSystem>(req: *mut FuseReq, parent: u64, name: *const c_char) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.unlink(ctx, parent, unsafe { CStr::from_ptr(name).to_bytes() }) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn rmdir<T: FileSystem>(req: *mut FuseReq, parent: u64, name: *const c_char) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.rmdir(ctx, parent, unsafe { CStr::from_ptr(name).to_bytes() }) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn symlink<T: FileSystem>(
req: *mut FuseReq,
link: *const c_char,
parent: u64,
name: *const c_char,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.symlink(
ctx,
unsafe { CStr::from_ptr(link).to_bytes() },
parent,
unsafe { CStr::from_ptr(name).to_bytes() },
) {
Ok(entry_param) => unsafe {
let _ret = fuse_reply_entry(req, entry_param.borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn rename<T: FileSystem>(
req: *mut FuseReq,
parent: u64,
name: *const c_char,
newparent: u64,
newname: *const c_char,
flags: u16,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.rename(
ctx,
parent,
unsafe { CStr::from_ptr(name).to_bytes() },
newparent,
unsafe { CStr::from_ptr(newname).to_bytes() },
flags,
) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn link<T: FileSystem>(req: *mut FuseReq, ino: u64, newparent: u64, newname: *const c_char) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.link(ctx, ino, newparent, unsafe {
CStr::from_ptr(newname).to_bytes()
}) {
Ok(entry_param) => unsafe {
let _ret = fuse_reply_entry(req, entry_param.borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn open<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.open(ctx, ino, unsafe { fi.read() }) {
Ok(fi) => unsafe {
let _ret = fuse_reply_open(req, fi.borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn read<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
size: size_t,
off: off_t,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.read(ctx, ino, size, off, unsafe { fi.as_mut().unwrap() }) {
Ok(message) => unsafe {
let buf = CString::new(&message[..]).unwrap();
let _ret = fuse_reply_buf(req, buf.as_ptr(), message.len());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn write<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
buf: *const c_char,
size: size_t,
off: off_t,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.write(
ctx,
ino,
unsafe { CStr::from_ptr(buf).to_bytes() },
size,
off,
unsafe { fi.as_mut().unwrap() },
) {
Ok(count) => unsafe {
let _ret = fuse_reply_write(req, count);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn flush<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.flush(ctx, ino, unsafe { fi.as_mut().unwrap() }) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn release<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.release(ctx, ino, unsafe { fi.as_mut().unwrap() }) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn fsync<T: FileSystem>(req: *mut FuseReq, ino: u64, datasync: c_int, fi: *mut FuseFileInfo) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.fsync(ctx, ino, datasync, unsafe { fi.as_mut().unwrap() }) {
Ok(..) => {}
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn opendir<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.opendir(ctx, ino, unsafe { fi.as_mut().unwrap() }) {
Ok(fi) => unsafe {
let _ret = fuse_reply_open(req, fi.borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn readdir<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
size: size_t,
off: off_t,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.readdir(ctx, ino, size, off, unsafe { fi.as_mut().unwrap() }) {
Ok(dirs) => unsafe {
let mut buf = Vec::<u8>::new();
let mut buf_size: usize = 0;
for dir in dirs.iter() {
let name_buf = CString::new(&dir.name[..]).unwrap();
let tmp_buf_size =
fuse_add_direntry(req, null_mut(), 0, name_buf.as_ptr(), null(), 0);
buf_size += tmp_buf_size;
let mut tmp_buf = Vec::<u8>::with_capacity(tmp_buf_size);
let ret = fuse_add_direntry(
req,
tmp_buf.as_mut_ptr() as *mut c_char,
tmp_buf_size,
name_buf.as_ptr(),
dir.attr().convert().borrow(),
buf_size as i64,
);
assert_eq!(tmp_buf_size, ret);
tmp_buf.set_len(ret);
buf.extend(tmp_buf);
}
if off == 0 {
let _ret =
fuse_reply_buf(req, buf.as_ptr() as *const c_char, min(buf.len(), size));
} else if (off as usize) < buf_size {
let out: Vec<_> = buf.drain((off as usize)..).collect();
let _ret =
fuse_reply_buf(req, out.as_ptr() as *const c_char, min(out.len(), size));
} else {
let _ret = fuse_reply_buf(req, null(), 0);
}
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn releasedir<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.releasedir(ctx, ino, unsafe { fi.as_mut().unwrap() }) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn fsyncdir<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
datasync: c_int,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.fsyncdir(ctx, ino, datasync, unsafe { fi.as_mut().unwrap() }) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn statfs<T: FileSystem>(req: *mut FuseReq, ino: u64) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.statfs(ctx, ino) {
Ok(stbuf) => unsafe {
let _ret = fuse_reply_statfs(req, stbuf.convert().borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn setxattr<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
name: *const c_char,
value: *const c_char,
size: size_t,
flags: c_int,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.setxattr(
ctx,
ino,
unsafe { CStr::from_ptr(name).to_bytes() },
unsafe { CStr::from_ptr(value).to_bytes() },
size,
flags,
) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn getxattr<T: FileSystem>(req: *mut FuseReq, ino: u64, name: *const c_char, size: size_t) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.getxattr(ctx, ino, unsafe { CStr::from_ptr(name).to_bytes() }, size) {
Ok(message) => unsafe {
if size == 0 {
let _ret = fuse_reply_xattr(req, message.len());
debug!("get_xattr: ret={}", _ret);
} else {
let buf = CString::new(&message[..]).unwrap();
let _ret = fuse_reply_buf(req, buf.as_ptr(), message.len());
}
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn listxattr<T: FileSystem>(req: *mut FuseReq, ino: u64, size: size_t) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.listxattr(ctx, ino, size) {
Ok(message) => unsafe {
if size == 0 {
let _ret = fuse_reply_xattr(req, message.len());
} else {
let buf = CString::new(&message[..]).unwrap();
let _ret = fuse_reply_buf(req, buf.as_ptr(), message.len());
}
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn removexattr<T: FileSystem>(req: *mut FuseReq, ino: u64, name: *const c_char) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.removexattr(ctx, ino, unsafe { CStr::from_ptr(name).to_bytes() }) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn access<T: FileSystem>(req: *mut FuseReq, ino: u64, mask: c_int) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.access(ctx, ino, mask) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn create<T: FileSystem>(
req: *mut FuseReq,
parent: u64,
name: *const c_char,
mode: mode_t,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.create(
ctx,
parent,
unsafe { CStr::from_ptr(name).to_bytes() },
mode,
unsafe { fi.as_mut().unwrap() },
) {
Ok(entry_param) => unsafe {
let _ret = fuse_reply_create(req, entry_param.borrow(), fi.as_ref().unwrap());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn getlk<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo, lock: *mut flock) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.getlk(
ctx,
ino,
unsafe { fi.as_mut().unwrap() },
&mut FuseLock::new(unsafe { lock.as_ref().unwrap() }),
) {
Ok(lock) => unsafe {
let _ret = fuse_reply_lock(req, lock.convert().borrow());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn setlk<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
fi: *mut FuseFileInfo,
lock: *mut flock,
sleep: c_int,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.setlk(
ctx,
ino,
unsafe { fi.as_mut().unwrap() },
&mut FuseLock::new(unsafe { lock.as_ref().unwrap() }),
sleep,
) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn bmap<T: FileSystem>(req: *mut FuseReq, ino: u64, blocksize: size_t, idx: u64) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.bmap(ctx, ino, blocksize, idx) {
Ok(idx) => unsafe {
let _ret = fuse_reply_bmap(req, idx);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn poll<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
fi: *mut FuseFileInfo,
ph: *mut FusePollhandle,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.poll(ctx, ino, unsafe { fi.as_mut().unwrap() }, unsafe {
ph.as_mut().unwrap()
}) {
Ok(revents) => unsafe {
let _ret = fuse_reply_poll(req, revents);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn write_buf<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
bufv: *mut FuseBufvec,
off: off_t,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.write_buf(ctx, ino, unsafe { bufv.as_mut().unwrap() }, off, unsafe {
fi.as_mut().unwrap()
}) {
Ok(count) => unsafe {
let _ret = fuse_reply_write(req, count);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn forget_multi<T: FileSystem>(req: *mut FuseReq, count: size_t, forgets: *mut FuseForgetData) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
let fs = (0..count)
.map(|i| unsafe { forgets.offset(i as isize).read() })
.collect();
file_system.forget_multi(ctx, fs);
unsafe {
fuse_reply_none(req);
}
}
fn flock<T: FileSystem>(req: *mut FuseReq, ino: u64, fi: *mut FuseFileInfo, op: c_int) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.flock(ctx, ino, unsafe { fi.as_mut().unwrap() }, op) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn fallocate<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
mode: c_int,
offset: off_t,
length: off_t,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.fallocate(ctx, ino, mode, offset, length, unsafe {
fi.as_mut().unwrap()
}) {
Ok(..) => unsafe {
fuse_reply_err(req, 0);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn readdirplus<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
size: size_t,
off: off_t,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.readdirplus(ctx, ino, size, off, unsafe { fi.as_mut().unwrap() }) {
Ok(message) => unsafe {
let buf = CString::new(&message[..]).unwrap();
let _ret = fuse_reply_buf(req, buf.as_ptr(), message.len());
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn copy_file_range<T: FileSystem>(
req: *mut FuseReq,
ino_in: u64,
off_in: off_t,
fi_in: *mut FuseFileInfo,
ino_out: u64,
off_out: off_t,
fi_out: *mut FuseFileInfo,
len: size_t,
flags: c_int,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.copy_file_range(
ctx,
ino_in,
off_in,
unsafe { fi_in.as_mut().unwrap() },
ino_out,
off_out,
unsafe { fi_out.as_mut().unwrap() },
len,
flags,
) {
Ok(count) => unsafe {
let _ret = fuse_reply_write(req, count);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
fn lseek<T: FileSystem>(
req: *mut FuseReq,
ino: u64,
off: off_t,
whence: c_int,
fi: *mut FuseFileInfo,
) {
let file_system = filesystem!(req);
let ctx = ctx!(req);
match file_system.lseek(ctx, ino, off, whence, unsafe { fi.as_mut().unwrap() }) {
Ok(off) => unsafe {
let _ret = fuse_reply_lseek(req, off);
},
Err(e) => unsafe {
fuse_reply_err(req, e);
},
}
}
}
pub struct Fuse {
session: &'static mut FuseSession,
}
impl Fuse {
pub fn new<T: FileSystem>(mountpoint: &str, file_system: &mut T, ops: u64) -> Self {
let arg0 = CString::new(env::args().nth(0).unwrap()).unwrap();
let mountpoint = CString::new(mountpoint).unwrap();
let c_argv: Vec<*const c_char> = vec![arg0.as_ptr()];
let mut fuse_args = FuseArgs {
argc: 1 as c_int,
argv: c_argv.as_ptr(),
allocated: 0 as c_int,
};
let op = FuseOps::fuse_low_level_ops::<T>(ops);
let session = unsafe {
let session = fuse_session_new(
fuse_args.borrow_mut(),
op.borrow(),
size_of::<FuseLowLevelOps>(),
file_system.borrow_mut() as *mut T as *mut c_void,
);
let _ = fuse_set_signal_handlers(session);
let _ = fuse_session_mount(session, mountpoint.as_ptr());
session.as_mut().unwrap()
};
Fuse { session }
}
pub fn run(&mut self) {
let sess = self.session.borrow_mut();
let mut buf = FuseBuf::new();
unsafe {
while fuse_session_exited(sess) == 0 {
let res = fuse_session_receive_buf(sess, buf.borrow_mut());
if res == -EINTR {
continue;
} else if res < 0 {
break;
}
let _ = fuse_session_process_buf(sess, buf.borrow());
}
fuse_session_reset(sess);
}
}
}
impl Drop for Fuse {
fn drop(&mut self) {
let sess = self.session.borrow_mut();
unsafe {
fuse_session_unmount(sess);
fuse_remove_signal_handlers(sess);
fuse_session_destroy(sess);
}
}
}