src/pxar/fuse.rs: cleanup callback interface and store decoder within session context
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
parent
7750b7f2b7
commit
742e64ea0b
203
src/pxar/fuse.rs
203
src/pxar/fuse.rs
@ -4,23 +4,47 @@
|
||||
|
||||
use std::ffi::{OsStr, CString};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use libc;
|
||||
use libc::{c_int, c_void, c_char, size_t};
|
||||
use failure::*;
|
||||
|
||||
use super::decoder::Decoder;
|
||||
|
||||
/// Node ID of the root inode
|
||||
/// This is the only one whose ID is not equal to the offset in the file.
|
||||
/// This is ok since offset 1 is part of the entry header and will therefore
|
||||
/// not occur again, but remapping to the correct offset of 0 is required.
|
||||
const FUSE_ROOT_ID: u64 = 1;
|
||||
|
||||
fn decoder_callback(path: &Path) -> Result<(), Error> {
|
||||
println!("{:#?}", path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// FFI types for easier readability
|
||||
type Request = *mut c_void;
|
||||
type MutPtr = *mut c_void;
|
||||
type ConstPtr = *const c_void;
|
||||
type StrPtr = *const c_char;
|
||||
|
||||
#[link(name = "fuse3")]
|
||||
extern "C" {
|
||||
fn fuse_session_new(args: *const FuseArgs, oprs: *const Operations, size: size_t, op: *const c_void) -> *mut c_void;
|
||||
fn fuse_set_signal_handlers(session: *const c_void) -> c_int;
|
||||
fn fuse_remove_signal_handlers(session: *const c_void);
|
||||
fn fuse_session_new(args: *const FuseArgs, oprs: *const Operations, size: size_t, op: ConstPtr) -> MutPtr;
|
||||
fn fuse_set_signal_handlers(session: ConstPtr) -> c_int;
|
||||
fn fuse_remove_signal_handlers(session: ConstPtr);
|
||||
fn fuse_daemonize(foreground: c_int) -> c_int;
|
||||
fn fuse_session_mount(session: *const c_void, mountpoint: *const c_char) -> c_int;
|
||||
fn fuse_session_unmount(session: *const c_void);
|
||||
fn fuse_session_loop(session: *const c_void) -> c_int;
|
||||
fn fuse_session_destroy(session: *const c_void);
|
||||
fn fuse_session_mount(session: ConstPtr, mountpoint: StrPtr) -> c_int;
|
||||
fn fuse_session_unmount(session: ConstPtr);
|
||||
fn fuse_session_loop(session: ConstPtr) -> c_int;
|
||||
fn fuse_session_destroy(session: ConstPtr);
|
||||
fn fuse_reply_attr(req: Request, attr: *const libc::stat, timeout: f64) -> c_int;
|
||||
fn fuse_reply_err(req: Request, errno: c_int) -> c_int;
|
||||
fn fuse_req_userdata(req: Request) -> MutPtr;
|
||||
}
|
||||
|
||||
/// Command line arguments passed to fuse.
|
||||
@ -28,24 +52,67 @@ extern "C" {
|
||||
#[derive(Debug)]
|
||||
struct FuseArgs {
|
||||
argc: c_int,
|
||||
argv: *const *const c_char,
|
||||
argv: *const StrPtr,
|
||||
allocated: c_int,
|
||||
}
|
||||
|
||||
/// `Session` stores a pointer to the session context and is used to mount the
|
||||
/// archive to the given mountpoint.
|
||||
#[derive(Debug)]
|
||||
pub struct Session {
|
||||
ptr: *mut c_void,
|
||||
archive: File,
|
||||
ptr: MutPtr,
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
/// `Operations` defines the callback function table of supported operations.
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
struct Operations {
|
||||
init: extern fn(userdata: *mut c_void) -> *mut c_void,
|
||||
destroy: extern fn(userdata: *mut c_void) -> *mut c_void,
|
||||
// The order in which the functions are listed matters, as the offset in the
|
||||
// struct defines what function the fuse driver uses.
|
||||
// It should therefore not be altered!
|
||||
init: Option<extern fn(userdata: MutPtr)>,
|
||||
destroy: Option<extern fn(userdata: MutPtr)>,
|
||||
lookup: Option<extern fn(req: Request, parent: u64, name: StrPtr)>,
|
||||
forget: Option<extern fn(req: Request, inode: u64, nlookup: u64)>,
|
||||
getattr: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr)>,
|
||||
setattr: Option<extern fn(req: Request, inode: u64, attr: MutPtr, to_set: c_int, fileinfo: MutPtr)>,
|
||||
readlink: Option<extern fn(req: Request, inode: u64)>,
|
||||
mknod: Option<extern fn(req: Request, parent: u64, name: StrPtr, mode: c_int, rdev: c_int)>,
|
||||
mkdir: Option<extern fn(req: Request, parent: u64, name: StrPtr, mode: c_int)>,
|
||||
unlink: Option<extern fn(req: Request, parent: u64, name: StrPtr)>,
|
||||
rmdir: Option<extern fn(req: Request, parent: u64, name: StrPtr)>,
|
||||
symlink: Option<extern fn(req: Request, link: StrPtr, parent: u64, name: StrPtr)>,
|
||||
rename: Option<extern fn(req: Request, parent: u64, name: StrPtr, newparent: u64, newname: StrPtr, flags: c_int)>,
|
||||
link: Option<extern fn(req: Request, inode: u64, newparent: u64, newname: StrPtr)>,
|
||||
open: Option<extern fn(req: Request, indoe: u64, fileinfo: MutPtr)>,
|
||||
read: Option<extern fn(req: Request, inode: u64, size: size_t, offset: c_int, fileinfo: MutPtr)>,
|
||||
write: Option<extern fn(req: Request, inode: u64, buffer: StrPtr, size: size_t, offset: c_void, fileinfo: MutPtr)>,
|
||||
flush: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr)>,
|
||||
release: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr)>,
|
||||
fsync: Option<extern fn(req: Request, inode: u64, datasync: c_int, fileinfo: MutPtr)>,
|
||||
opendir: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr)>,
|
||||
readdir: Option<extern fn(req: Request, inode: u64, size: size_t, offset: c_int, fileinfo: MutPtr)>,
|
||||
releasedir: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr)>,
|
||||
fsyncdir: Option<extern fn(req: Request, inode: u64, datasync: c_int, fileinfo: MutPtr)>,
|
||||
statfs: Option<extern fn(req: Request, inode: u64)>,
|
||||
setxattr: Option<extern fn(req: Request, inode: u64, name: StrPtr, value: StrPtr, size: size_t, flags: c_int)>,
|
||||
getxattr: Option<extern fn(req: Request, inode: u64, name: StrPtr, size: size_t)>,
|
||||
listxattr: Option<extern fn(req: Request, inode: u64, size: size_t)>,
|
||||
removexattr: Option<extern fn(req: Request, inode: u64, name: StrPtr)>,
|
||||
access: Option<extern fn(req: Request, inode: u64, mask: i32)>,
|
||||
create: Option<extern fn(req: Request, parent: u64, name: StrPtr, mode: c_int, fileinfo: MutPtr)>,
|
||||
getlk: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr, lock: MutPtr)>,
|
||||
setlk: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr, lock: MutPtr, sleep: c_int)>,
|
||||
bmap: Option<extern fn(req: Request, inode: u64, blocksize: size_t, idx: u64)>,
|
||||
ioctl: Option<extern fn(req: Request, inode: u64, cmd: c_int, arg: MutPtr, fileinfo: MutPtr, flags: c_int, in_buf: ConstPtr, in_bufsz: size_t, out_bufsz: size_t)>,
|
||||
poll: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr, pollhandle: MutPtr)>,
|
||||
write_buf: Option<extern fn(req: Request, inode: u64, bufv: MutPtr, offset: c_int, fileinfo: MutPtr)>,
|
||||
retrieve_reply: Option<extern fn(req: Request, cookie: ConstPtr, inode: u64, offset: c_int, bufv: MutPtr)>,
|
||||
forget_multi: Option<extern fn(req: Request, count: size_t, forgets: MutPtr)>,
|
||||
flock: Option<extern fn(req: Request, inode: u64, fileinfo: MutPtr, op: c_int)>,
|
||||
fallocate: Option<extern fn(req: Request, inode: u64, mode: c_int, offset: c_int, length: c_int, fileinfo: MutPtr)>,
|
||||
readdirplus: Option<extern fn(req: Request, inode: u64, size: size_t, offset: c_int, fileinfo: MutPtr)>,
|
||||
copy_file_range: Option<extern fn(req: Request, ino_in: u64, off_in: c_int, fi_in: MutPtr, ino_out: u64, off_out: c_int, fi_out: MutPtr, len: size_t, flags: c_int)>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
@ -72,16 +139,30 @@ impl Session {
|
||||
};
|
||||
|
||||
// Register the callback funcitons for the session
|
||||
let oprs = Operations {
|
||||
init: init,
|
||||
destroy: destroy,
|
||||
};
|
||||
let mut oprs = Operations::default();
|
||||
oprs.init = Some(init);
|
||||
oprs.destroy = Some(destroy);
|
||||
oprs.lookup = Some(lookup);
|
||||
oprs.getattr = Some(getattr);
|
||||
oprs.open = Some(open);
|
||||
oprs.read = Some(read);
|
||||
oprs.opendir = Some(opendir);
|
||||
oprs.readdir = Some(readdir);
|
||||
|
||||
// By storing the decoder as userdata of the session, each request may
|
||||
// access it.
|
||||
let reader = BufReader::new(file);
|
||||
let decoder = Decoder::new(reader, decoder_callback as fn(&Path) -> Result<(), Error>)?;
|
||||
let session_decoder = Box::new(Mutex::new(decoder));
|
||||
let session_ptr = unsafe { fuse_session_new(
|
||||
&args as *const FuseArgs,
|
||||
&oprs as *const Operations,
|
||||
std::mem::size_of::<Operations>(),
|
||||
std::ptr::null()
|
||||
// Ownership of session_decoder is passed to the session here.
|
||||
// It has to be reclaimed before dropping the session to free
|
||||
// the decoder and close the underlying file. This is done inside
|
||||
// the destroy callback function.
|
||||
Box::into_raw(session_decoder) as ConstPtr
|
||||
)};
|
||||
|
||||
if session_ptr.is_null() {
|
||||
@ -94,7 +175,6 @@ impl Session {
|
||||
|
||||
Ok(Self {
|
||||
ptr: session_ptr,
|
||||
archive: file,
|
||||
verbose: verbose,
|
||||
})
|
||||
}
|
||||
@ -147,13 +227,84 @@ impl Drop for Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback functions for fuse kernel driver.
|
||||
extern "C" fn init(_userdata: *mut c_void) -> *mut c_void {
|
||||
println!("Init callback");
|
||||
return std::ptr::null_mut();
|
||||
/// Creates a context providing an exclusive mutable reference to the decoder.
|
||||
///
|
||||
/// Each callback function needing access to the decoder can easily get an
|
||||
/// exclusive handle by running the code inside this context.
|
||||
/// Responses with error code can easily be generated by returning with the
|
||||
/// error code.
|
||||
fn run_in_context<F: FnOnce(&mut Decoder<BufReader<File>, fn(&Path) -> Result<(), Error>>) -> Result<(), i32>>(req: Request, code: F) {
|
||||
let ptr = unsafe { fuse_req_userdata(req) as *mut Mutex<Decoder<BufReader<File>, fn(&Path) -> Result<(), Error>>> };
|
||||
let boxed_decoder = unsafe { Box::from_raw(ptr) };
|
||||
let result = boxed_decoder.lock()
|
||||
.map(|mut decoder| code(&mut decoder))
|
||||
.unwrap_or(Err(libc::ENOENT));
|
||||
|
||||
if let Err(err) = result {
|
||||
unsafe { let _res = fuse_reply_err(req, err); }
|
||||
}
|
||||
|
||||
// Release ownership of boxed decoder, do not drop it.
|
||||
let _ = Box::into_raw(boxed_decoder);
|
||||
}
|
||||
|
||||
extern "C" fn destroy(_userdata: *mut c_void) -> *mut c_void {
|
||||
println!("Destroy callback");
|
||||
return std::ptr::null_mut();
|
||||
|
||||
/// Callback functions for fuse kernel driver.
|
||||
extern "C" fn init(_decoder: MutPtr) {
|
||||
// Notting to do here for now
|
||||
}
|
||||
|
||||
/// Cleanup the userdata created while creating the session, which is the decoder
|
||||
extern "C" fn destroy(decoder: MutPtr) {
|
||||
// Get ownership of the decoder and drop it when Box goes out of scope.
|
||||
unsafe { Box::from_raw(decoder); }
|
||||
}
|
||||
|
||||
extern "C" fn lookup(req: Request, _parent: u64, _name: StrPtr) {
|
||||
run_in_context(req, |decoder| {
|
||||
// code goes here
|
||||
|
||||
Err(libc::ENOENT)
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn getattr(req: Request, _inode: u64, _fileinfo: MutPtr) {
|
||||
run_in_context(req, |decoder| {
|
||||
// code goes here
|
||||
|
||||
Err(libc::ENOENT)
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn open(req: Request, _inode: u64, _fileinfo: MutPtr) {
|
||||
run_in_context(req, |decoder| {
|
||||
// code goes here
|
||||
|
||||
Err(libc::ENOENT)
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn read(req: Request, _inode: u64, _size: size_t, _offset: c_int, _fileinfo: MutPtr) {
|
||||
run_in_context(req, |decoder| {
|
||||
// code goes here
|
||||
|
||||
Err(libc::ENOENT)
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn opendir(req: Request, _inode: u64, _fileinfo: MutPtr) {
|
||||
run_in_context(req, |decoder| {
|
||||
// code goes here
|
||||
|
||||
Err(libc::ENOENT)
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn readdir(req: Request, _inode: u64, _size: size_t, _offset: c_int, _fileinfo: MutPtr) {
|
||||
run_in_context(req, |decoder| {
|
||||
// code goes here
|
||||
|
||||
Err(libc::ENOENT)
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user