diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index f98ec173e3..65b7a76631 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -847,11 +847,13 @@ impl Clone for FuseFileHandle { } #[derive(Debug)] -pub(crate) struct FuseDirectory; +pub(crate) struct FuseDirectory { + prefix: Option, +} impl FuseDirectory { - pub const fn new() -> Self { - FuseDirectory {} + pub const fn new(prefix: Option) -> Self { + FuseDirectory { prefix } } } @@ -863,13 +865,23 @@ impl VfsNode for FuseDirectory { fn traverse_readdir(&self, components: &mut Vec<&str>) -> Result, IoError> { let path: String = if components.is_empty() { - "/".to_string() + if let Some(prefix) = &self.prefix { + "/".to_string() + prefix + } else { + "/".to_string() + } } else { - components + let path: String = components .iter() .rev() .map(|v| "/".to_owned() + v) - .collect() + .collect(); + + if let Some(prefix) = &self.prefix { + "/".to_owned() + &prefix.to_owned() + &path + } else { + path + } }; debug!("FUSE opendir: {}", path); @@ -925,7 +937,7 @@ impl VfsNode for FuseDirectory { unsafe { &*(rsp.payload.as_ptr().byte_add(offset) as *const fuse_abi::Dirent) }; offset += core::mem::size_of::() + dirent.d_namelen as usize; - // Allign to dirent struct + // Align to dirent struct offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); let name: &'static [u8] = unsafe { @@ -950,13 +962,23 @@ impl VfsNode for FuseDirectory { fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { let path: String = if components.is_empty() { - "/".to_string() + if let Some(prefix) = &self.prefix { + "/".to_string() + prefix + } else { + "/".to_string() + } } else { - components + let path: String = components .iter() .rev() .map(|v| "/".to_owned() + v) - .collect() + .collect(); + + if let Some(prefix) = &self.prefix { + "/".to_owned() + &prefix.to_owned() + &path + } else { + path + } }; debug!("FUSE stat: {}", path); @@ -987,13 +1009,23 @@ impl VfsNode for FuseDirectory { fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { let path: String = if components.is_empty() { - "/".to_string() + if let Some(prefix) = &self.prefix { + "/".to_string() + prefix + } else { + "/".to_string() + } } else { - components + let path: String = components .iter() .rev() .map(|v| "/".to_owned() + v) - .collect() + .collect(); + + if let Some(prefix) = &self.prefix { + "/".to_owned() + &prefix.to_owned() + &path + } else { + path + } }; debug!("FUSE lstat: {}", path); @@ -1015,13 +1047,23 @@ impl VfsNode for FuseDirectory { mode: AccessPermission, ) -> Result, IoError> { let path: String = if components.is_empty() { - "/".to_string() + if let Some(prefix) = &self.prefix { + "/".to_string() + prefix + } else { + "/".to_string() + } } else { - components + let path: String = components .iter() .rev() .map(|v| "/".to_owned() + v) - .collect() + .collect(); + + if let Some(prefix) = &self.prefix { + "/".to_owned() + &prefix.to_owned() + &path + } else { + path + } }; debug!("FUSE open: {}, {:?} {:?}", path, opt, mode); @@ -1071,13 +1113,23 @@ impl VfsNode for FuseDirectory { fn traverse_unlink(&self, components: &mut Vec<&str>) -> core::result::Result<(), IoError> { let path: String = if components.is_empty() { - "/".to_string() + if let Some(prefix) = &self.prefix { + "/".to_string() + prefix + } else { + "/".to_string() + } } else { - components + let path: String = components .iter() .rev() .map(|v| "/".to_owned() + v) - .collect() + .collect(); + + if let Some(prefix) = &self.prefix { + "/".to_owned() + &prefix.to_owned() + &path + } else { + path + } }; let (cmd, mut rsp) = ops::Unlink::create(&path); @@ -1092,13 +1144,23 @@ impl VfsNode for FuseDirectory { fn traverse_rmdir(&self, components: &mut Vec<&str>) -> core::result::Result<(), IoError> { let path: String = if components.is_empty() { - "/".to_string() + if let Some(prefix) = &self.prefix { + "/".to_string() + prefix + } else { + "/".to_string() + } } else { - components + let path: String = components .iter() .rev() .map(|v| "/".to_owned() + v) - .collect() + .collect(); + + if let Some(prefix) = &self.prefix { + "/".to_owned() + &prefix.to_owned() + &path + } else { + path + } }; let (cmd, mut rsp) = ops::Rmdir::create(&path); @@ -1117,13 +1179,23 @@ impl VfsNode for FuseDirectory { mode: AccessPermission, ) -> Result<(), IoError> { let path: String = if components.is_empty() { - "/".to_string() + if let Some(prefix) = &self.prefix { + "/".to_string() + prefix + } else { + "/".to_string() + } } else { - components + let path: String = components .iter() .rev() .map(|v| "/".to_owned() + v) - .collect() + .collect(); + + if let Some(prefix) = &self.prefix { + "/".to_owned() + &prefix.to_owned() + &path + } else { + path + } }; let (cmd, mut rsp) = ops::Mkdir::create(&path, mode.bits()); @@ -1153,12 +1225,121 @@ pub(crate) fn init() { .unwrap(); trace!("fuse init answer: {:?}", rsp); - let mount_point = format!("/{}", driver.lock().get_mount_point()); - info!("Mounting virtio-fs at {}", mount_point); - fs::FILESYSTEM - .get() - .unwrap() - .mount(mount_point.as_str(), Box::new(FuseDirectory::new())) - .expect("Mount failed. Duplicate mount_point?"); + let mount_point = driver.lock().get_mount_point().to_string(); + if mount_point == "/" { + let fuse_nid = lookup("/").unwrap(); + // Opendir + // Flag 0x10000 for O_DIRECTORY might not be necessary + let (mut cmd, mut rsp) = ops::Open::create(fuse_nid, 0x10000); + cmd.in_header.opcode = fuse_abi::Opcode::Opendir as u32; + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()) + .unwrap(); + let fuse_fh = unsafe { rsp.op_header.assume_init_ref().fh }; + + // Linux seems to allocate a single page to store the dirfile + let len = MAX_READ_LEN as u32; + let mut offset: usize = 0; + + // read content of the directory + let (mut cmd, mut rsp) = ops::Read::create(fuse_nid, fuse_fh, len, 0); + cmd.in_header.opcode = fuse_abi::Opcode::Readdir as u32; + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()) + .unwrap(); + + let len: usize = if unsafe { rsp.out_header.assume_init_ref().len } as usize + - ::core::mem::size_of::() + - ::core::mem::size_of::() + >= len.try_into().unwrap() + { + len.try_into().unwrap() + } else { + (unsafe { rsp.out_header.assume_init_ref().len } as usize) + - ::core::mem::size_of::() + - ::core::mem::size_of::() + }; + + if len <= core::mem::size_of::() { + panic!("FUSE no new dirs"); + } + + let mut entries: Vec = Vec::new(); + while (unsafe { rsp.out_header.assume_init_ref().len } as usize) - offset + > core::mem::size_of::() + { + let dirent = + unsafe { &*(rsp.payload.as_ptr().byte_add(offset) as *const fuse_abi::Dirent) }; + + offset += core::mem::size_of::() + dirent.d_namelen as usize; + // Allign to dirent struct + offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); + + let name: &'static [u8] = unsafe { + core::slice::from_raw_parts( + dirent.d_name.as_ptr(), + dirent.d_namelen.try_into().unwrap(), + ) + }; + entries.push(unsafe { core::str::from_utf8_unchecked(name).to_string() }); + } + + let (cmd, mut rsp) = ops::Release::create(fuse_nid, fuse_fh); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()) + .unwrap(); + + // remove predefined directories + entries.retain(|x| x != "."); + entries.retain(|x| x != ".."); + entries.retain(|x| x != "tmp"); + entries.retain(|x| x != "proc"); + warn!("Fuse don't mount the host directories 'tmp' and 'proc' into the guest file system!"); + + for i in entries { + let (cmd, mut rsp) = ops::Lookup::create(&i); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()) + .unwrap(); + + let attr = unsafe { rsp.op_header.assume_init().attr }; + let attr = FileAttr::from(attr); + + if attr.st_mode.contains(AccessPermission::S_IFDIR) { + info!("Fuse mount {} to /{}", i, i); + fs::FILESYSTEM + .get() + .unwrap() + .mount( + &("/".to_owned() + i.as_str()), + Box::new(FuseDirectory::new(Some(i))), + ) + .expect("Mount failed. Invalid mount_point?"); + } else { + warn!("Fuse don't mount {}. It isn't a directory!", i); + } + } + } else { + let mount_point = if mount_point.starts_with('/') { + mount_point + } else { + "/".to_owned() + &mount_point + }; + + info!("Mounting virtio-fs at {}", mount_point); + fs::FILESYSTEM + .get() + .unwrap() + .mount(mount_point.as_str(), Box::new(FuseDirectory::new(None))) + .expect("Mount failed. Invalid mount_point?"); + } } } diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index a01bc883b5..e4dfe7bac3 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -430,7 +430,7 @@ pub unsafe extern "C" fn sys_getdents64( return -crate::errno::EINVAL as i64; } - let limit = dirp as usize + count; + const ALIGN_DIRENT: usize = core::mem::align_of::(); let mut dirp: *mut Dirent64 = dirp; let mut offset: i64 = 0; let obj = get_object(fd); @@ -442,7 +442,9 @@ pub unsafe extern "C" fn sys_getdents64( |v| { for i in v.iter() { let len = i.name.len(); - if dirp as usize + core::mem::size_of::() + len + 1 >= limit { + let aligned_len = ((core::mem::size_of::() + len + 1) + + (ALIGN_DIRENT - 1)) & (!(ALIGN_DIRENT - 1)); + if offset as usize + aligned_len >= count { return -crate::errno::EINVAL as i64; } @@ -450,10 +452,8 @@ pub unsafe extern "C" fn sys_getdents64( dir.d_ino = 0; dir.d_type = 0; - dir.d_reclen = (core::mem::size_of::() + len + 1) - .try_into() - .unwrap(); - offset += i64::from(dir.d_reclen); + dir.d_reclen = aligned_len.try_into().unwrap(); + offset += i64::try_from(aligned_len).unwrap(); dir.d_off = offset; // copy null-terminated filename @@ -463,9 +463,7 @@ pub unsafe extern "C" fn sys_getdents64( s.add(len).write_bytes(0, 1); } - dirp = unsafe { - (dirp as *mut u8).add(dir.d_reclen as usize) as *mut Dirent64 - }; + dirp = unsafe { (dirp as *mut u8).add(aligned_len) as *mut Dirent64 }; } offset