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:
		
				
					committed by
					
						 Dietmar Maurer
						Dietmar Maurer
					
				
			
			
				
	
			
			
			
						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::ffi::{OsStr, CString}; | ||||||
| use std::fs::File; | use std::fs::File; | ||||||
|  | use std::io::BufReader; | ||||||
| use std::os::unix::ffi::OsStrExt; | use std::os::unix::ffi::OsStrExt; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
|  | use std::sync::Mutex; | ||||||
|  |  | ||||||
| use libc; | use libc; | ||||||
| use libc::{c_int, c_void, c_char, size_t}; | use libc::{c_int, c_void, c_char, size_t}; | ||||||
| use failure::*; | 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")] | #[link(name = "fuse3")] | ||||||
| extern "C" { | extern "C" { | ||||||
|     fn fuse_session_new(args: *const FuseArgs, oprs: *const Operations, size: size_t, op: *const c_void) -> *mut c_void; |     fn fuse_session_new(args: *const FuseArgs, oprs: *const Operations, size: size_t, op: ConstPtr) -> MutPtr; | ||||||
|     fn fuse_set_signal_handlers(session: *const c_void) -> c_int; |     fn fuse_set_signal_handlers(session: ConstPtr) -> c_int; | ||||||
|     fn fuse_remove_signal_handlers(session: *const c_void); |     fn fuse_remove_signal_handlers(session: ConstPtr); | ||||||
|     fn fuse_daemonize(foreground: c_int) -> c_int; |     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_mount(session: ConstPtr, mountpoint: StrPtr) -> c_int; | ||||||
|     fn fuse_session_unmount(session: *const c_void); |     fn fuse_session_unmount(session: ConstPtr); | ||||||
|     fn fuse_session_loop(session: *const c_void) -> c_int; |     fn fuse_session_loop(session: ConstPtr) -> c_int; | ||||||
|     fn fuse_session_destroy(session: *const c_void); |     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. | /// Command line arguments passed to fuse. | ||||||
| @ -28,24 +52,67 @@ extern "C" { | |||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| struct FuseArgs { | struct FuseArgs { | ||||||
|     argc: c_int, |     argc: c_int, | ||||||
|     argv: *const *const c_char, |     argv: *const StrPtr, | ||||||
|     allocated: c_int, |     allocated: c_int, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// `Session` stores a pointer to the session context and is used to mount the | /// `Session` stores a pointer to the session context and is used to mount the | ||||||
| /// archive to the given mountpoint. | /// archive to the given mountpoint. | ||||||
| #[derive(Debug)] |  | ||||||
| pub struct Session { | pub struct Session { | ||||||
|     ptr: *mut c_void, |     ptr: MutPtr, | ||||||
|     archive: File, |  | ||||||
|     verbose: bool, |     verbose: bool, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// `Operations` defines the callback function table of supported operations. | /// `Operations` defines the callback function table of supported operations. | ||||||
| #[repr(C)] | #[repr(C)] | ||||||
|  | #[derive(Default)] | ||||||
| struct Operations { | struct Operations { | ||||||
|     init: extern fn(userdata: *mut c_void) -> *mut c_void, |     // The order in which the functions are listed matters, as the offset in the | ||||||
|     destroy: extern fn(userdata: *mut c_void) -> *mut c_void, |     // 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 { | impl Session { | ||||||
| @ -72,16 +139,30 @@ impl Session { | |||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         // Register the callback funcitons for the session |         // Register the callback funcitons for the session | ||||||
|         let oprs = Operations { |         let mut oprs    = Operations::default(); | ||||||
|             init: init, |         oprs.init       = Some(init); | ||||||
|             destroy: destroy, |         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( |         let session_ptr = unsafe { fuse_session_new( | ||||||
|             &args as *const FuseArgs, |             &args as *const FuseArgs, | ||||||
|             &oprs as *const Operations, |             &oprs as *const Operations, | ||||||
|             std::mem::size_of::<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() { |         if session_ptr.is_null() { | ||||||
| @ -94,7 +175,6 @@ impl Session { | |||||||
|  |  | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|             ptr: session_ptr, |             ptr: session_ptr, | ||||||
|             archive: file, |  | ||||||
|             verbose: verbose, |             verbose: verbose, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| @ -147,13 +227,84 @@ impl Drop for Session { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Callback functions for fuse kernel driver. | /// Creates a context providing an exclusive mutable reference to the decoder. | ||||||
| extern "C" fn init(_userdata: *mut c_void) -> *mut c_void { | /// | ||||||
|     println!("Init callback"); | /// Each callback function needing access to the decoder can easily get an | ||||||
|     return std::ptr::null_mut(); | /// 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); } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| extern "C" fn destroy(_userdata: *mut c_void) -> *mut c_void { |     // Release ownership of boxed decoder, do not drop it. | ||||||
|     println!("Destroy callback"); |     let _ = Box::into_raw(boxed_decoder); | ||||||
|     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) | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user