pxar: add basic code for FUSE implementation.
This adds the basic code in order to create a fuse session and mount an archive. It adds libfuse3-3 as runtime dependency and libfuse3-dev as build dependency. Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
		
				
					committed by
					
						 Dietmar Maurer
						Dietmar Maurer
					
				
			
			
				
	
			
			
			
						parent
						
							781ac11c6a
						
					
				
				
					commit
					c50f87442c
				
			
							
								
								
									
										159
									
								
								src/pxar/fuse.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								src/pxar/fuse.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,159 @@ | ||||
| //! Low level FUSE implementation for pxar. | ||||
| //! | ||||
| //! Allows to mount the archive as read-only filesystem to inspect its contents. | ||||
|  | ||||
| use std::ffi::{OsStr, CString}; | ||||
| use std::fs::File; | ||||
| use std::os::unix::ffi::OsStrExt; | ||||
| use std::path::Path; | ||||
|  | ||||
| use libc; | ||||
| use libc::{c_int, c_void, c_char, size_t}; | ||||
| use failure::*; | ||||
|  | ||||
| #[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_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); | ||||
| } | ||||
|  | ||||
| /// Command line arguments passed to fuse. | ||||
| #[repr(C)] | ||||
| #[derive(Debug)] | ||||
| struct FuseArgs { | ||||
|     argc: c_int, | ||||
|     argv: *const *const c_char, | ||||
|     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, | ||||
|     verbose: bool, | ||||
| } | ||||
|  | ||||
| /// `Operations` defines the callback function table of supported operations. | ||||
| #[repr(C)] | ||||
| struct Operations { | ||||
|     init: extern fn(userdata: *mut c_void) -> *mut c_void, | ||||
|     destroy: extern fn(userdata: *mut c_void) -> *mut c_void, | ||||
| } | ||||
|  | ||||
| impl Session { | ||||
|     /// Create a new low level fuse session. | ||||
|     /// | ||||
|     /// `Session` is created using the provided mount options and sets the | ||||
|     /// default signal handlers. | ||||
|     /// Options have to be provided as comma separated OsStr, e.g. | ||||
|     /// ("ro,default_permissions"). | ||||
|     pub fn new(archive_path: &Path, options: &OsStr, verbose: bool)-> Result<Self, Error> { | ||||
|         let file = File::open(archive_path)?; | ||||
|         // First argument should be the executable name | ||||
|         let arguments = vec![ | ||||
|             CString::new("pxar-mount").unwrap(), | ||||
|             CString::new("-o").unwrap(), | ||||
|             CString::new(options.as_bytes())?, | ||||
|         ]; | ||||
|  | ||||
|         let arg_ptrs: Vec<_> = arguments.iter().map(|opt| opt.as_ptr()).collect(); | ||||
|         let args = FuseArgs { | ||||
|             argc: arg_ptrs.len() as i32, | ||||
|             argv: arg_ptrs.as_ptr(), | ||||
|             allocated: 0, | ||||
|         }; | ||||
|  | ||||
|         // Register the callback funcitons for the session | ||||
|         let oprs = Operations { | ||||
|             init: init, | ||||
|             destroy: destroy, | ||||
|         }; | ||||
|  | ||||
|         let session_ptr = unsafe { fuse_session_new( | ||||
|             &args as *const FuseArgs, | ||||
|             &oprs as *const Operations, | ||||
|             std::mem::size_of::<Operations>(), | ||||
|             std::ptr::null() | ||||
|         )}; | ||||
|  | ||||
|         if session_ptr.is_null() { | ||||
|             bail!("error while creating new fuse session"); | ||||
|         } | ||||
|  | ||||
|         if unsafe { fuse_set_signal_handlers(session_ptr) } != 0 { | ||||
|             bail!("error while setting signal handlers"); | ||||
|         } | ||||
|  | ||||
|         Ok(Self { | ||||
|             ptr: session_ptr, | ||||
|             archive: file, | ||||
|             verbose: verbose, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /// Actually mount the filesystem for this session on the provided mountpoint | ||||
|     /// and daemonize process. | ||||
|     pub fn mount(&mut self, mountpoint: &Path) -> Result<(), Error> { | ||||
|         if self.verbose { | ||||
|             println!("Mounting archive to {:#?}", mountpoint); | ||||
|         } | ||||
|         let mountpoint = mountpoint.canonicalize()?; | ||||
|         let path_cstr = CString::new(mountpoint.as_os_str().as_bytes()) | ||||
|             .map_err(|err| format_err!("invalid mountpoint - {}", err))?; | ||||
|         if unsafe { fuse_session_mount(self.ptr, path_cstr.as_ptr()) } != 0 { | ||||
|             bail!("mounting on {:#?} failed", mountpoint); | ||||
|         } | ||||
|  | ||||
|         // Do not send process to background if verbose flag is set | ||||
|         if !self.verbose && unsafe { fuse_daemonize(0) } != 0 { | ||||
|             bail!("could not send process to background"); | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Execute session loop which handles requests from kernel. | ||||
|     pub fn run_loop(&mut self) -> Result<(), Error> { | ||||
|         if self.verbose { | ||||
|             println!("Executing fuse session loop"); | ||||
|         } | ||||
|         let result = unsafe { fuse_session_loop(self.ptr) }; | ||||
|         if result < 0 { | ||||
|             bail!("fuse session loop exited with - {}", result); | ||||
|         } | ||||
|         if result > 0 { | ||||
|             eprintln!("fuse session loop recieved signal - {}", result); | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for Session { | ||||
|     fn drop(&mut self) { | ||||
|         unsafe { | ||||
|             fuse_session_unmount(self.ptr); | ||||
|             fuse_remove_signal_handlers(self.ptr); | ||||
|             fuse_session_destroy(self.ptr); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// 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(); | ||||
| } | ||||
|  | ||||
| extern "C" fn destroy(_userdata: *mut c_void) -> *mut c_void { | ||||
|     println!("Destroy callback"); | ||||
|     return std::ptr::null_mut(); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user