Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow mounting a virtiofsd share to / #774

Merged
merged 2 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 213 additions & 32 deletions src/fs/fuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -847,11 +847,13 @@ impl Clone for FuseFileHandle {
}

#[derive(Debug)]
pub(crate) struct FuseDirectory;
pub(crate) struct FuseDirectory {
prefix: Option<String>,
}

impl FuseDirectory {
pub const fn new() -> Self {
FuseDirectory {}
pub const fn new(prefix: Option<String>) -> Self {
FuseDirectory { prefix }
}
}

Expand All @@ -863,13 +865,23 @@ impl VfsNode for FuseDirectory {

fn traverse_readdir(&self, components: &mut Vec<&str>) -> Result<Vec<DirectoryEntry>, 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);
Expand Down Expand Up @@ -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::<fuse_abi::Dirent>() + 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 {
Expand All @@ -950,13 +962,23 @@ impl VfsNode for FuseDirectory {

fn traverse_stat(&self, components: &mut Vec<&str>) -> Result<FileAttr, 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 stat: {}", path);
Expand Down Expand Up @@ -987,13 +1009,23 @@ impl VfsNode for FuseDirectory {

fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result<FileAttr, 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 lstat: {}", path);
Expand All @@ -1015,13 +1047,23 @@ impl VfsNode for FuseDirectory {
mode: AccessPermission,
) -> Result<Arc<dyn ObjectInterface>, 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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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());

Expand Down Expand Up @@ -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::<fuse_abi::OutHeader>()
- ::core::mem::size_of::<fuse_abi::ReadOut>()
>= len.try_into().unwrap()
{
len.try_into().unwrap()
} else {
(unsafe { rsp.out_header.assume_init_ref().len } as usize)
- ::core::mem::size_of::<fuse_abi::OutHeader>()
- ::core::mem::size_of::<fuse_abi::ReadOut>()
};

if len <= core::mem::size_of::<fuse_abi::Dirent>() {
panic!("FUSE no new dirs");
}

let mut entries: Vec<String> = Vec::new();
while (unsafe { rsp.out_header.assume_init_ref().len } as usize) - offset
> core::mem::size_of::<fuse_abi::Dirent>()
{
let dirent =
unsafe { &*(rsp.payload.as_ptr().byte_add(offset) as *const fuse_abi::Dirent) };

offset += core::mem::size_of::<fuse_abi::Dirent>() + 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?");
}
}
}
16 changes: 7 additions & 9 deletions src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Dirent64>();
let mut dirp: *mut Dirent64 = dirp;
let mut offset: i64 = 0;
let obj = get_object(fd);
Expand All @@ -442,18 +442,18 @@ 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::<Dirent64>() + len + 1 >= limit {
let aligned_len = ((core::mem::size_of::<Dirent64>() + len + 1)
+ (ALIGN_DIRENT - 1)) & (!(ALIGN_DIRENT - 1));
if offset as usize + aligned_len >= count {
return -crate::errno::EINVAL as i64;
}

let dir = unsafe { &mut *dirp };

dir.d_ino = 0;
dir.d_type = 0;
dir.d_reclen = (core::mem::size_of::<Dirent64>() + 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
Expand All @@ -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
Expand Down
Loading