diff --git a/vuinputd/src/main.rs b/vuinputd/src/main.rs index 5053ef9..580eb05 100644 --- a/vuinputd/src/main.rs +++ b/vuinputd/src/main.rs @@ -96,7 +96,7 @@ enum VuError { static VUINPUT_COUNTER: OnceLock = OnceLock::new(); static VUINPUT_STATE: OnceLock>>>> = OnceLock::new(); static JOB_DISPATCHER: OnceLock>= OnceLock::new(); -static SELF_REQUESTING_PROCESSES: OnceLock= OnceLock::new(); +static VUINPUTD_NAMESPACES: OnceLock= OnceLock::new(); // For log limiting. Idea: Move to log_limit crate static DEDUP_LAST_ERROR: OnceLock>> = OnceLock::new(); @@ -193,8 +193,8 @@ unsafe extern "C" fn vuinput_open( let fh = get_fresh_filehandle(); let ctx = fuse_lowlevel::fuse_req_ctx(_req); debug!("fh {}: opened by process id {} (host view)", fh, (*ctx).pid); - let namespaces = get_namespaces(Pid::Pid((*ctx).pid)); - debug!("fh {}: namespaces {}", fh, namespaces); + let requesting_process = get_requesting_process(Pid::Pid((*ctx).pid)); + debug!("fh {}: namespaces {}", fh, requesting_process); // namespaces net:4026531840, uts:4026531838, ipc:4026531839, pid:4026531836, pid_for_children:4026531836, user:4026531837, mnt:4026531841, cgroup:4026531835, time:4026531834, time_for_children:4026531834 (*_fi).fh = fh; // Open the path in read-only mode, returns `io::Result` @@ -210,7 +210,7 @@ unsafe extern "C" fn vuinput_open( &VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap()), VuInputState { file: v, - requesting_process: namespaces, + requesting_process, input_device: None }, ) @@ -238,6 +238,7 @@ unsafe extern "C" fn vuinput_write( "vuinput_write: offset needs to be 0 but is {}", _off ); + let fh = &(*_fi).fh; let slice = std::slice::from_raw_parts(_buf as *const u8, _size); let vuinput_state_mutex = get_vuinput_state(&VuFileHandle::from_fuse_file_info(_fi.as_ref().unwrap())).unwrap(); @@ -281,12 +282,20 @@ unsafe extern "C" fn vuinput_write( return; } - let result = vuinput_state.file.write_all(slice); + let chunksize = std::mem::size_of::(); + let mut bytes = 0; + let mut result = Result::Ok(()); + + while bytes + chunksize <= _size && result.is_ok() { + result = vuinput_state.file.write_all(&slice[bytes..bytes + chunksize]); + bytes += chunksize; + } + match result { Ok(_) => { - trace!("wrote {} bytes", _size); - fuse_lowlevel::fuse_reply_write(_req, _size); + debug!("wrote {} bytes", bytes); + fuse_lowlevel::fuse_reply_write(_req, bytes); } Err(e) => { let mut last_error = DEDUP_LAST_ERROR.get().unwrap().lock().unwrap(); @@ -317,7 +326,7 @@ unsafe extern "C" fn vuinput_release( // Only do this in case it has not already been done by the ioctl UI_DEV_DESTROY // this here is relevant if the process was killed and didn't have the chance to send the // ioctl UI_DEV_DESTROY. - if input_device.is_some() && ! SELF_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) { + if input_device.is_some() && ! VUINPUTD_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process.namespaces) { let input_device = input_device.unwrap(); let remove_job=RemoveFromContainerJob::new(vuinput_state.requesting_process.clone(),input_device.devnode.clone(),input_device.syspath.clone(),input_device.major,input_device.minor); JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job)); @@ -485,7 +494,7 @@ unsafe extern "C" fn vuinput_ioctl( vuinput_state.input_device = Some(VuInputDevice {cuse_fh:*fh, major: major, minor: minor, syspath: sysname.clone(), devnode: devnode.clone(), runtime_data: None, netlink_data: None }); // Create device in container, if the request was really from another namespace - if ! SELF_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) { + if ! VUINPUTD_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process.namespaces) { let inject_job=InjectInContainerJob::new(vuinput_state.requesting_process.clone(),devnode.clone(),sysname.clone(),major,minor); JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(inject_job)); } @@ -500,7 +509,7 @@ unsafe extern "C" fn vuinput_ioctl( let input_device = vuinput_state.input_device.take(); // Remove device in container, if the request was really from another namespace - if input_device.is_some() && ! SELF_REQUESTING_PROCESSES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process) { + if input_device.is_some() && ! VUINPUTD_NAMESPACES.get().unwrap().equal_mnt_and_net(&vuinput_state.requesting_process.namespaces) { let input_device = input_device.unwrap(); let remove_job=RemoveFromContainerJob::new(vuinput_state.requesting_process.clone(),input_device.devnode.clone(),input_device.syspath.clone(),input_device.major,input_device.minor); JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(remove_job)); @@ -724,7 +733,7 @@ fn main() -> std::io::Result<()> { VUINPUT_STATE.set(RwLock::new(HashMap::new())).unwrap(); VUINPUT_COUNTER.set(AtomicU64::new(3)).unwrap(); JOB_DISPATCHER.set(Mutex::new(Dispatcher::new())).unwrap(); - SELF_REQUESTING_PROCESSES.set(get_namespaces(Pid::SelfPid)).unwrap(); + VUINPUTD_NAMESPACES.set(get_namespace(Pid::SelfPid)).unwrap(); DEDUP_LAST_ERROR.set(Mutex::new(None)).unwrap(); JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(MonitorBackgroundLoop::new())); diff --git a/vuinputd/src/requesting_process.rs b/vuinputd/src/requesting_process.rs index fd1a5d2..4397e82 100644 --- a/vuinputd/src/requesting_process.rs +++ b/vuinputd/src/requesting_process.rs @@ -31,7 +31,7 @@ impl Pid { } #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] -struct Namespaces { +pub struct Namespaces { pub net: Option, pub uts: Option, pub ipc: Option, @@ -46,27 +46,34 @@ struct Namespaces { } /// Returns true if the process with `pid` is a 32-bit (compat) process. None, if unsure. -pub fn is_compat_process(pid: i32) -> Option { - const EI_CLASS: usize = 4; - const ELFCLASS32: u8 = 1; - const ELFCLASS64: u8 = 2; +pub fn is_compat_process(pid: Pid) -> Option { - let exe_path = format!("/proc/{}/exe", pid); - let mut buf = [0u8; 5]; + match pid { + Pid::Pid(pid) => { + const EI_CLASS: usize = 4; + const ELFCLASS32: u8 = 1; + const ELFCLASS64: u8 = 2; - match File::open(&exe_path).and_then(|mut f| f.read_exact(&mut buf)) { - Ok(()) => { - // ELF magic check - if &buf[0..4] != b"\x7FELF" { - return None; - } - match buf[EI_CLASS] { - ELFCLASS32 => Some(true), - ELFCLASS64 => Some(false), - _ => None, + let exe_path = format!("/proc/{}/exe", pid); + let mut buf = [0u8; 5]; + + match File::open(&exe_path).and_then(|mut f| f.read_exact(&mut buf)) { + Ok(()) => { + // ELF magic check + if &buf[0..4] != b"\x7FELF" { + return None; + } + match buf[EI_CLASS] { + ELFCLASS32 => Some(true), + ELFCLASS64 => Some(false), + _ => None, + } + } + Err(_) => None, } } - Err(_) => None, + Pid::SelfPid => + unreachable!() } } @@ -75,7 +82,8 @@ pub fn is_compat_process(pid: i32) -> Option { pub struct RequestingProcess { pub nspath: String, pub nsroot: String, - nsinodes: Namespaces, + pub namespaces: Namespaces, + pub is_compat: bool, } impl Namespaces { @@ -86,28 +94,32 @@ impl Namespaces { impl RequestingProcess { pub fn equal_mnt_and_net(&self, other: &RequestingProcess) -> bool { - self.nsinodes.equal_mnt_and_net(&other.nsinodes) + self.namespaces.equal_mnt_and_net(&other.namespaces) + } + + pub fn equal_mnt_and_net_ns(&self, other: &Namespaces) -> bool { + self.namespaces.equal_mnt_and_net(&other) } } impl std::fmt::Display for RequestingProcess { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "Namespaces:")?; - writeln!(f, " net: {:?}", self.nsinodes.net)?; - writeln!(f, " uts: {:?}", self.nsinodes.uts)?; - writeln!(f, " ipc: {:?}", self.nsinodes.ipc)?; - writeln!(f, " pid: {:?}", self.nsinodes.pid)?; - writeln!(f, " pid_for_children: {:?}", self.nsinodes.pid_for_children)?; - writeln!(f, " user: {:?}", self.nsinodes.user)?; - writeln!(f, " mnt: {:?}", self.nsinodes.mnt)?; - writeln!(f, " cgroup: {:?}", self.nsinodes.cgroup)?; - writeln!(f, " time: {:?}", self.nsinodes.time)?; - writeln!(f, " time_for_children: {:?}", self.nsinodes.time_for_children)?; + writeln!(f, " net: {:?}", self.namespaces.net)?; + writeln!(f, " uts: {:?}", self.namespaces.uts)?; + writeln!(f, " ipc: {:?}", self.namespaces.ipc)?; + writeln!(f, " pid: {:?}", self.namespaces.pid)?; + writeln!(f, " pid_for_children: {:?}", self.namespaces.pid_for_children)?; + writeln!(f, " user: {:?}", self.namespaces.user)?; + writeln!(f, " mnt: {:?}", self.namespaces.mnt)?; + writeln!(f, " cgroup: {:?}", self.namespaces.cgroup)?; + writeln!(f, " time: {:?}", self.namespaces.time)?; + writeln!(f, " time_for_children: {:?}", self.namespaces.time_for_children)?; Ok(()) } } -fn get_namespace_inodes(pid: Pid) -> Namespaces { +pub fn get_namespace(pid: Pid) -> Namespaces { let pid: String = match pid { Pid::Pid(pid) => pid.to_string(), Pid::SelfPid => "self".to_string(), @@ -171,21 +183,37 @@ fn get_ppid(pid: Pid) -> Option { -pub fn get_namespaces(pid: Pid) -> RequestingProcess { +pub fn get_requesting_process(pid: Pid) -> RequestingProcess { match pid { Pid::Pid(_) => { + let is_compat = match is_compat_process(pid) { + Some(true) => { + debug!("identified process {} as 64 bit process",pid.path()); + false + }, + Some(false) => { + debug!("identified process {} as 32 bit process",pid.path()); + true + }, + None => { + debug!("could not identify bitness of process {}. Assume 64 bit process",pid.path()); + false + }, + }; + + // go up the parent hierarchy until we find a parent with different namespaces let mut ppid = pid; - let nsinodes = get_namespace_inodes(pid); + let nsinodes = get_namespace(pid); loop { let candidate_ppid = get_ppid(ppid); match candidate_ppid { None => break, Some(candidate_ppid) => { - let ppid_nsinodes = get_namespace_inodes(candidate_ppid); + let ppid_nsinodes = get_namespace(candidate_ppid); if nsinodes.equal_mnt_and_net(&ppid_nsinodes) { ppid=candidate_ppid; } else { @@ -202,18 +230,13 @@ pub fn get_namespaces(pid: Pid) -> RequestingProcess { RequestingProcess { nspath: nspath, nsroot: nsroot, - nsinodes: nsinodes, + namespaces: nsinodes, + is_compat: is_compat } }, Pid::SelfPid => { - let nsinodes = get_namespace_inodes(pid); - let nspath = format!("{}/ns", pid.path()); - RequestingProcess { - nspath: nspath.clone(), - nsroot: nspath, - nsinodes: nsinodes, - } + unreachable!(); }, } }