From 75be9961ff3448dcd6962b22e9132a48ea66ce55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 10 Feb 2026 16:47:45 +0100 Subject: [PATCH 1/6] refactor(fs): iterate over `&str` instead of `&&str` --- src/fs/uhyve.rs | 5 ++++- src/fs/virtio_fs.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 1bd47c4ce0..2de4ec7711 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -138,7 +138,10 @@ impl UhyveDirectory { fn traversal_path(&self, components: &[&str]) -> CString { let prefix_deref = self.prefix.as_deref(); - let components_with_prefix = prefix_deref.iter().chain(components.iter().rev()); + let components_with_prefix = prefix_deref + .iter() + .copied() + .chain(components.iter().copied().rev()); // Unlike src/fs/virtio_fs.rs, we skip the first element here so as to not prepend / before /root let path: String = components_with_prefix .flat_map(|component| ["/", component]) diff --git a/src/fs/virtio_fs.rs b/src/fs/virtio_fs.rs index d57860d6b5..c6b6e569bc 100644 --- a/src/fs/virtio_fs.rs +++ b/src/fs/virtio_fs.rs @@ -1120,7 +1120,10 @@ impl VirtioFsDirectory { fn traversal_path(&self, components: &[&str]) -> CString { let prefix_deref = self.prefix.as_deref(); - let components_with_prefix = prefix_deref.iter().chain(components.iter().rev()); + let components_with_prefix = prefix_deref + .iter() + .copied() + .chain(components.iter().copied().rev()); let path: String = components_with_prefix .flat_map(|component| ["/", component]) .collect(); From fa3831aaa3f1e8c1b9e9421ab7a7ec4df8db6dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 10 Feb 2026 16:52:05 +0100 Subject: [PATCH 2/6] refactor(fs): represent `prefix` as `String` instead of `Option` This should make the code simpler in the future, since we can just join empty prefixes and don't need to handle `None` explicitly. --- src/fs/uhyve.rs | 18 +++++++++--------- src/fs/virtio_fs.rs | 32 +++++++++++++++++--------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 2de4ec7711..5b46f63e31 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -4,6 +4,7 @@ use alloc::ffi::CString; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; +use core::iter; use async_lock::Mutex; use embedded_io::{ErrorType, Read, Write}; @@ -128,19 +129,18 @@ impl Clone for UhyveFileHandle { #[derive(Debug)] pub(crate) struct UhyveDirectory { - prefix: Option, + prefix: String, } impl UhyveDirectory { - pub const fn new(prefix: Option) -> Self { + pub const fn new(prefix: String) -> Self { UhyveDirectory { prefix } } fn traversal_path(&self, components: &[&str]) -> CString { - let prefix_deref = self.prefix.as_deref(); - let components_with_prefix = prefix_deref - .iter() - .copied() + let prefix = self.prefix.as_str(); + let components_with_prefix = iter::once(prefix) + .filter(|prefix| !prefix.is_empty()) .chain(components.iter().copied().rev()); // Unlike src/fs/virtio_fs.rs, we skip the first element here so as to not prepend / before /root let path: String = components_with_prefix @@ -249,7 +249,7 @@ pub(crate) fn init() { .unwrap() .mount( &mount_point, - Box::new(UhyveDirectory::new(Some(mount_point.clone()))), + Box::new(UhyveDirectory::new(mount_point.clone())), ) .expect("Mount failed. Duplicate mount_point?"); return; @@ -259,7 +259,7 @@ pub(crate) fn init() { for mount_point in mount_str.split('\0') { info!("Mounting uhyve filesystem at {mount_point}"); - let obj = Box::new(UhyveDirectory::new(Some(mount_point.to_owned()))); + let obj = Box::new(UhyveDirectory::new(mount_point.to_owned())); let Err(errno) = fs::FILESYSTEM.get().unwrap().mount(mount_point, obj) else { return; }; @@ -269,7 +269,7 @@ pub(crate) fn init() { let (parent_path, _file_name) = mount_point.rsplit_once('/').unwrap(); create_dir_recursive(parent_path, AccessPermission::S_IRWXU).unwrap(); - let obj = Box::new(UhyveDirectory::new(Some(mount_point.to_owned()))); + let obj = Box::new(UhyveDirectory::new(mount_point.to_owned())); fs::FILESYSTEM .get() .unwrap() diff --git a/src/fs/virtio_fs.rs b/src/fs/virtio_fs.rs index c6b6e569bc..de787da703 100644 --- a/src/fs/virtio_fs.rs +++ b/src/fs/virtio_fs.rs @@ -8,7 +8,7 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicU64, Ordering}; use core::task::Poll; -use core::{future, mem, ptr, slice}; +use core::{future, iter, mem, ptr, slice}; use align_address::Align; use async_lock::Mutex; @@ -964,12 +964,12 @@ impl Clone for VirtioFsFileHandle { } pub struct VirtioFsDirectoryHandle { - name: Option, + name: String, read_position: Mutex, } impl VirtioFsDirectoryHandle { - pub fn new(name: Option) -> Self { + pub fn new(name: String) -> Self { Self { name, read_position: Mutex::new(0), @@ -979,10 +979,10 @@ impl VirtioFsDirectoryHandle { impl ObjectInterface for VirtioFsDirectoryHandle { async fn getdents(&self, buf: &mut [MaybeUninit]) -> io::Result { - let path: CString = if let Some(name) = &self.name { - CString::new("/".to_owned() + name).unwrap() - } else { + let path = if self.name.is_empty() { CString::new("/").unwrap() + } else { + CString::new("/".to_owned() + &self.name).unwrap() }; debug!("virtio-fs opendir: {path:#?}"); @@ -1097,12 +1097,12 @@ impl ObjectInterface for VirtioFsDirectoryHandle { #[derive(Debug)] pub(crate) struct VirtioFsDirectory { - prefix: Option, + prefix: String, attr: FileAttr, } impl VirtioFsDirectory { - pub fn new(prefix: Option) -> Self { + pub fn new(prefix: String) -> Self { let microseconds = arch::kernel::systemtime::now_micros(); let t = timespec::from_usec(microseconds as i64); @@ -1119,10 +1119,9 @@ impl VirtioFsDirectory { } fn traversal_path(&self, components: &[&str]) -> CString { - let prefix_deref = self.prefix.as_deref(); - let components_with_prefix = prefix_deref - .iter() - .copied() + let prefix = self.prefix.as_str(); + let components_with_prefix = iter::once(prefix) + .filter(|prefix| !prefix.is_empty()) .chain(components.iter().copied().rev()); let path: String = components_with_prefix .flat_map(|component| ["/", component]) @@ -1303,7 +1302,7 @@ impl VfsNode for VirtioFsDirectory { let mut path = path.into_string().unwrap(); path.remove(0); return Ok(Arc::new(async_lock::RwLock::new( - VirtioFsDirectoryHandle::new(Some(path)).into(), + VirtioFsDirectoryHandle::new(path).into(), ))); } @@ -1415,7 +1414,10 @@ pub(crate) fn init() { fs::FILESYSTEM .get() .unwrap() - .mount(mount_point.as_str(), Box::new(VirtioFsDirectory::new(None))) + .mount( + mount_point.as_str(), + Box::new(VirtioFsDirectory::new(String::new())), + ) .expect("Mount failed. Invalid mount_point?"); return; } @@ -1513,7 +1515,7 @@ pub(crate) fn init() { .unwrap() .mount( &("/".to_owned() + entry.as_str()), - Box::new(VirtioFsDirectory::new(Some(entry))), + Box::new(VirtioFsDirectory::new(entry)), ) .expect("Mount failed. Invalid mount_point?"); } else { From da921a8302fb537aaf6f3a84c20983b5ee120e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 10 Feb 2026 17:17:57 +0100 Subject: [PATCH 3/6] refactor(virtio-fs): use `[&str]::join` instead of creating and appending --- src/fs/virtio_fs.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/fs/virtio_fs.rs b/src/fs/virtio_fs.rs index de787da703..9266e24d33 100644 --- a/src/fs/virtio_fs.rs +++ b/src/fs/virtio_fs.rs @@ -982,7 +982,8 @@ impl ObjectInterface for VirtioFsDirectoryHandle { let path = if self.name.is_empty() { CString::new("/").unwrap() } else { - CString::new("/".to_owned() + &self.name).unwrap() + let path = ["/", &self.name].join(""); + CString::new(path).unwrap() }; debug!("virtio-fs opendir: {path:#?}"); @@ -1407,7 +1408,7 @@ pub(crate) fn init() { let mount_point = if mount_point.starts_with('/') { mount_point } else { - "/".to_owned() + &mount_point + ["/", &mount_point].join("") }; info!("Mounting virtio-fs at {mount_point}"); @@ -1509,14 +1510,12 @@ pub(crate) fn init() { let attr = FileAttr::from(rsp.headers.op_header.attr); if attr.st_mode.contains(AccessPermission::S_IFDIR) { - info!("virtio-fs mount {entry} to /{entry}"); + let path = ["/", &entry].join(""); + info!("virtio-fs mount {entry} to {path}"); fs::FILESYSTEM .get() .unwrap() - .mount( - &("/".to_owned() + entry.as_str()), - Box::new(VirtioFsDirectory::new(entry)), - ) + .mount(&path, Box::new(VirtioFsDirectory::new(entry))) .expect("Mount failed. Invalid mount_point?"); } else { warn!("virtio-fs don't mount {entry}. It isn't a directory!"); From db0054484a13bebd562b4936a7b1649bdce325e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 10 Feb 2026 17:54:15 +0100 Subject: [PATCH 4/6] refactor(fs): align virtio-fs prefix to Uhyve --- src/fs/uhyve.rs | 1 - src/fs/virtio_fs.rs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 5b46f63e31..ed42917de3 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -142,7 +142,6 @@ impl UhyveDirectory { let components_with_prefix = iter::once(prefix) .filter(|prefix| !prefix.is_empty()) .chain(components.iter().copied().rev()); - // Unlike src/fs/virtio_fs.rs, we skip the first element here so as to not prepend / before /root let path: String = components_with_prefix .flat_map(|component| ["/", component]) .skip(1) diff --git a/src/fs/virtio_fs.rs b/src/fs/virtio_fs.rs index 9266e24d33..477b12a372 100644 --- a/src/fs/virtio_fs.rs +++ b/src/fs/virtio_fs.rs @@ -1126,6 +1126,7 @@ impl VirtioFsDirectory { .chain(components.iter().copied().rev()); let path: String = components_with_prefix .flat_map(|component| ["/", component]) + .skip(1) .collect(); if path.is_empty() { CString::new("/").unwrap() @@ -1417,7 +1418,7 @@ pub(crate) fn init() { .unwrap() .mount( mount_point.as_str(), - Box::new(VirtioFsDirectory::new(String::new())), + Box::new(VirtioFsDirectory::new("/".to_owned())), ) .expect("Mount failed. Invalid mount_point?"); return; @@ -1515,7 +1516,7 @@ pub(crate) fn init() { fs::FILESYSTEM .get() .unwrap() - .mount(&path, Box::new(VirtioFsDirectory::new(entry))) + .mount(&path, Box::new(VirtioFsDirectory::new(path.clone()))) .expect("Mount failed. Invalid mount_point?"); } else { warn!("virtio-fs don't mount {entry}. It isn't a directory!"); From 3cb6d6b0986bec7c54cdf2157e7bfdd79f42420d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 10 Feb 2026 17:06:21 +0100 Subject: [PATCH 5/6] docs(fs): document `prefix` --- src/fs/uhyve.rs | 4 ++++ src/fs/virtio_fs.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index ed42917de3..966aa86648 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -129,6 +129,10 @@ impl Clone for UhyveFileHandle { #[derive(Debug)] pub(crate) struct UhyveDirectory { + /// The external path of this directory. + /// + /// Before talking to virtio-fs, the relative path inside this directory is + /// adjoined with this prefix. prefix: String, } diff --git a/src/fs/virtio_fs.rs b/src/fs/virtio_fs.rs index 477b12a372..cdbbeaf823 100644 --- a/src/fs/virtio_fs.rs +++ b/src/fs/virtio_fs.rs @@ -1098,6 +1098,10 @@ impl ObjectInterface for VirtioFsDirectoryHandle { #[derive(Debug)] pub(crate) struct VirtioFsDirectory { + /// The external path of this directory. + /// + /// Before talking to virtio-fs, the relative path inside this directory is + /// adjoined with this prefix. prefix: String, attr: FileAttr, } From 354b277ea310732148e62e6d99ae7ffc89513dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 11 Feb 2026 15:59:34 +0100 Subject: [PATCH 6/6] refactor(fs): replace `components: &mut Vec<&str>` with `path: &str` --- src/fs/mem.rs | 90 ++++++++++++++++++++++----------------------- src/fs/mod.rs | 88 ++++++++++++++------------------------------ src/fs/uhyve.rs | 39 ++++++-------------- src/fs/virtio_fs.rs | 50 ++++++++++--------------- 4 files changed, 105 insertions(+), 162 deletions(-) diff --git a/src/fs/mem.rs b/src/fs/mem.rs index ec50894851..61002da066 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -279,16 +279,16 @@ impl VfsNode for RomFile { block_on(async { Ok(*self.data.attr.read().await) }, None) } - fn traverse_lstat(&self, components: &mut Vec<&str>) -> io::Result { - if !components.is_empty() { + fn traverse_lstat(&self, path: &str) -> io::Result { + if !path.is_empty() { return Err(Errno::Badf); } self.get_file_attributes() } - fn traverse_stat(&self, components: &mut Vec<&str>) -> io::Result { - if !components.is_empty() { + fn traverse_stat(&self, path: &str) -> io::Result { + if !path.is_empty() { return Err(Errno::Badf); } @@ -335,16 +335,16 @@ impl VfsNode for RamFile { block_on(async { Ok(self.data.read().await.attr) }, None) } - fn traverse_lstat(&self, components: &mut Vec<&str>) -> io::Result { - if !components.is_empty() { + fn traverse_lstat(&self, path: &str) -> io::Result { + if !path.is_empty() { return Err(Errno::Badf); } self.get_file_attributes() } - fn traverse_stat(&self, components: &mut Vec<&str>) -> io::Result { - if !components.is_empty() { + fn traverse_stat(&self, path: &str) -> io::Result { + if !path.is_empty() { return Err(Errno::Badf); } @@ -466,16 +466,16 @@ impl MemDirectory { async fn async_traverse_open( &self, - components: &mut Vec<&str>, + path: &str, opt: OpenOption, mode: AccessPermission, ) -> io::Result>> { - let component = components.pop().ok_or(Errno::Noent)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); - if !components.is_empty() { + if !rest.is_empty() { let inner = self.inner.read().await; let directory = inner.get(component).ok_or(Errno::Noent)?; - return directory.traverse_open(components, opt, mode); + return directory.traverse_open(rest, opt, mode); } let mut inner = self.inner.write().await; @@ -517,16 +517,16 @@ impl VfsNode for MemDirectory { Ok(self.attr) } - fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: AccessPermission) -> io::Result<()> { + fn traverse_mkdir(&self, path: &str, mode: AccessPermission) -> io::Result<()> { block_on( async { - let component = components.pop().ok_or(Errno::Badf)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); if let Some(directory) = self.inner.read().await.get(component) { - return directory.traverse_mkdir(components, mode); + return directory.traverse_mkdir(rest, mode); } - if !components.is_empty() { + if !rest.is_empty() { return Err(Errno::Badf); } @@ -540,15 +540,15 @@ impl VfsNode for MemDirectory { ) } - fn traverse_rmdir(&self, components: &mut Vec<&str>) -> io::Result<()> { + fn traverse_rmdir(&self, path: &str) -> io::Result<()> { block_on( async { - let component = components.pop().ok_or(Errno::Badf)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); - if !components.is_empty() { + if !rest.is_empty() { let inner = &*self.inner.read().await; let directory = inner.get(component).ok_or(Errno::Badf)?; - return directory.traverse_rmdir(components); + return directory.traverse_rmdir(rest); } let mut guard = self.inner.write().await; @@ -565,15 +565,15 @@ impl VfsNode for MemDirectory { ) } - fn traverse_unlink(&self, components: &mut Vec<&str>) -> io::Result<()> { + fn traverse_unlink(&self, path: &str) -> io::Result<()> { block_on( async { - let component = components.pop().ok_or(Errno::Badf)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); - if !components.is_empty() { + if !rest.is_empty() { let inner = self.inner.read().await; let directory = inner.get(component).ok_or(Errno::Badf)?; - return directory.traverse_unlink(components); + return directory.traverse_unlink(rest); } let mut guard = self.inner.write().await; @@ -590,13 +590,13 @@ impl VfsNode for MemDirectory { ) } - fn traverse_readdir(&self, components: &mut Vec<&str>) -> io::Result> { + fn traverse_readdir(&self, path: &str) -> io::Result> { block_on( async { - if let Some(component) = components.pop() { + if let Some((component, rest)) = path.split_once("/") { let inner = self.inner.read().await; let directory = inner.get(component).ok_or(Errno::Badf)?; - return directory.traverse_readdir(components); + return directory.traverse_readdir(rest); }; let mut entries = Vec::new(); @@ -610,15 +610,15 @@ impl VfsNode for MemDirectory { ) } - fn traverse_lstat(&self, components: &mut Vec<&str>) -> io::Result { + fn traverse_lstat(&self, path: &str) -> io::Result { block_on( async { - let component = components.pop().ok_or(Errno::Nosys)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); - if !components.is_empty() { + if !rest.is_empty() { let inner = self.inner.read().await; let directory = inner.get(component).ok_or(Errno::Badf)?; - return directory.traverse_lstat(components); + return directory.traverse_lstat(rest); } let inner = self.inner.read().await; @@ -629,15 +629,15 @@ impl VfsNode for MemDirectory { ) } - fn traverse_stat(&self, components: &mut Vec<&str>) -> io::Result { + fn traverse_stat(&self, path: &str) -> io::Result { block_on( async { - let component = components.pop().ok_or(Errno::Nosys)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); - if !components.is_empty() { + if !rest.is_empty() { let inner = self.inner.read().await; let directory = inner.get(component).ok_or(Errno::Badf)?; - return directory.traverse_stat(components); + return directory.traverse_stat(rest); } let inner = self.inner.read().await; @@ -648,16 +648,16 @@ impl VfsNode for MemDirectory { ) } - fn traverse_mount(&self, components: &mut Vec<&str>, obj: Box) -> io::Result<()> { + fn traverse_mount(&self, path: &str, obj: Box) -> io::Result<()> { block_on( async { - let component = components.pop().ok_or(Errno::Badf)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); if let Some(directory) = self.inner.read().await.get(component) { - return directory.traverse_mount(components, obj); + return directory.traverse_mount(rest, obj); } - if !components.is_empty() { + if !rest.is_empty() { return Err(Errno::Badf); } @@ -670,27 +670,27 @@ impl VfsNode for MemDirectory { fn traverse_open( &self, - components: &mut Vec<&str>, + path: &str, opt: OpenOption, mode: AccessPermission, ) -> io::Result>> { - block_on(self.async_traverse_open(components, opt, mode), None) + block_on(self.async_traverse_open(path, opt, mode), None) } fn traverse_create_file( &self, - components: &mut Vec<&str>, + path: &str, data: &'static [u8], mode: AccessPermission, ) -> io::Result<()> { block_on( async { - let component = components.pop().ok_or(Errno::Noent)?; + let (component, rest) = path.split_once("/").unwrap_or((path, "")); - if !components.is_empty() { + if !rest.is_empty() { let inner = self.inner.read().await; let directory = inner.get(component).ok_or(Errno::Noent)?; - return directory.traverse_create_file(components, data, mode); + return directory.traverse_create_file(rest, data, mode); } let file = RomFile::new(data, mode); diff --git a/src/fs/mod.rs b/src/fs/mod.rs index b2a9294d92..eb65cd1e98 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -66,52 +66,44 @@ pub(crate) trait VfsNode: Send + Sync + fmt::Debug { } /// Creates a new directory node - fn traverse_mkdir( - &self, - _components: &mut Vec<&str>, - _mode: AccessPermission, - ) -> io::Result<()> { + fn traverse_mkdir(&self, _path: &str, _mode: AccessPermission) -> io::Result<()> { Err(Errno::Nosys) } /// Deletes a directory node - fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> io::Result<()> { + fn traverse_rmdir(&self, _path: &str) -> io::Result<()> { Err(Errno::Nosys) } /// Removes the specified file - fn traverse_unlink(&self, _components: &mut Vec<&str>) -> io::Result<()> { + fn traverse_unlink(&self, _path: &str) -> io::Result<()> { Err(Errno::Nosys) } /// Opens a directory - fn traverse_readdir(&self, _components: &mut Vec<&str>) -> io::Result> { + fn traverse_readdir(&self, _path: &str) -> io::Result> { Err(Errno::Nosys) } /// Gets file status - fn traverse_lstat(&self, _components: &mut Vec<&str>) -> io::Result { + fn traverse_lstat(&self, _path: &str) -> io::Result { Err(Errno::Nosys) } /// Gets file status - fn traverse_stat(&self, _components: &mut Vec<&str>) -> io::Result { + fn traverse_stat(&self, _path: &str) -> io::Result { Err(Errno::Nosys) } /// Mounts a file system - fn traverse_mount( - &self, - _components: &mut Vec<&str>, - _obj: Box, - ) -> io::Result<()> { + fn traverse_mount(&self, _path: &str, _obj: Box) -> io::Result<()> { Err(Errno::Nosys) } /// Opens a file fn traverse_open( &self, - _components: &mut Vec<&str>, + _path: &str, _option: OpenOption, _mode: AccessPermission, ) -> io::Result>> { @@ -121,7 +113,7 @@ pub(crate) trait VfsNode: Send + Sync + fmt::Debug { /// Creates a read-only file fn traverse_create_file( &self, - _components: &mut Vec<&str>, + _path: &str, _data: &'static [u8], _mode: AccessPermission, ) -> io::Result<()> { @@ -165,45 +157,37 @@ impl Filesystem { mode: AccessPermission, ) -> io::Result>> { debug!("Open file {path} with {opt:?}"); - let mut components: Vec<&str> = path.split('/').collect(); - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_open(&mut components, opt, mode) + self.root.traverse_open(path, opt, mode) } /// Unlinks a file given by path pub fn unlink(&self, path: &str) -> io::Result<()> { debug!("Unlinking file {path}"); - let mut components: Vec<&str> = path.split('/').collect(); - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_unlink(&mut components) + self.root.traverse_unlink(path) } /// Remove directory given by path pub fn rmdir(&self, path: &str) -> io::Result<()> { debug!("Removing directory {path}"); - let mut components: Vec<&str> = path.split('/').collect(); - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_rmdir(&mut components) + self.root.traverse_rmdir(path) } /// Create directory given by path pub fn mkdir(&self, path: &str, mode: AccessPermission) -> io::Result<()> { debug!("Create directory {path}"); - let mut components: Vec<&str> = path.split('/').collect(); - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_mkdir(&mut components, mode) + self.root.traverse_mkdir(path, mode) } pub fn opendir(&self, path: &str) -> io::Result>> { @@ -215,51 +199,38 @@ impl Filesystem { /// List given directory pub fn readdir(&self, path: &str) -> io::Result> { - if path.trim() == "/" { - let mut components: Vec<&str> = Vec::new(); - self.root.traverse_readdir(&mut components) - } else { - let mut components: Vec<&str> = path.split('/').collect(); + debug!("Readdir {path}"); - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_readdir(&mut components) - } + self.root.traverse_readdir(path) } /// stat pub fn stat(&self, path: &str) -> io::Result { debug!("Getting stats {path}"); - let mut components: Vec<&str> = path.split('/').collect(); - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_stat(&mut components) + self.root.traverse_stat(path) } /// lstat pub fn lstat(&self, path: &str) -> io::Result { debug!("Getting lstats {path}"); - let mut components: Vec<&str> = path.split('/').collect(); - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_lstat(&mut components) + self.root.traverse_lstat(path) } /// Create new backing-fs at mountpoint mntpath pub fn mount(&self, path: &str, obj: Box) -> io::Result<()> { debug!("Mounting {path}"); - let mut components: Vec<&str> = path.split('/').collect(); + let path = path.strip_prefix('/').unwrap_or(path); - components.reverse(); - components.pop(); - - self.root.traverse_mount(&mut components, obj) + self.root.traverse_mount(path, obj) } /// Create read-only file @@ -271,12 +242,9 @@ impl Filesystem { ) -> io::Result<()> { debug!("Create read-only file {path}"); - let mut components: Vec<&str> = path.split('/').collect(); - - components.reverse(); - components.pop(); + let path = path.strip_prefix('/').unwrap_or(path); - self.root.traverse_create_file(&mut components, data, mode) + self.root.traverse_create_file(path, data, mode) } } diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 966aa86648..682a5aee62 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -3,8 +3,6 @@ use alloc::boxed::Box; use alloc::ffi::CString; use alloc::string::String; use alloc::sync::Arc; -use alloc::vec::Vec; -use core::iter; use async_lock::Mutex; use embedded_io::{ErrorType, Read, Write}; @@ -141,20 +139,11 @@ impl UhyveDirectory { UhyveDirectory { prefix } } - fn traversal_path(&self, components: &[&str]) -> CString { + fn traversal_path(&self, path: &str) -> CString { let prefix = self.prefix.as_str(); - let components_with_prefix = iter::once(prefix) - .filter(|prefix| !prefix.is_empty()) - .chain(components.iter().copied().rev()); - let path: String = components_with_prefix - .flat_map(|component| ["/", component]) - .skip(1) - .collect(); - if path.is_empty() { - CString::new("/").unwrap() - } else { - CString::new(path).unwrap() - } + let prefix = prefix.strip_suffix("/").unwrap_or(prefix); + let path = [prefix, path].join("/"); + CString::new(path).unwrap() } } @@ -164,21 +153,21 @@ impl VfsNode for UhyveDirectory { NodeKind::Directory } - fn traverse_stat(&self, _components: &mut Vec<&str>) -> io::Result { + fn traverse_stat(&self, _path: &str) -> io::Result { Err(Errno::Nosys) } - fn traverse_lstat(&self, _components: &mut Vec<&str>) -> io::Result { + fn traverse_lstat(&self, _path: &str) -> io::Result { Err(Errno::Nosys) } fn traverse_open( &self, - components: &mut Vec<&str>, + path: &str, opt: OpenOption, mode: AccessPermission, ) -> io::Result>> { - let path = self.traversal_path(components); + let path = self.traversal_path(path); let mut open_params = OpenParams { name: GuestPhysAddr::new( @@ -201,8 +190,8 @@ impl VfsNode for UhyveDirectory { ))) } - fn traverse_unlink(&self, components: &mut Vec<&str>) -> io::Result<()> { - let path = self.traversal_path(components); + fn traverse_unlink(&self, path: &str) -> io::Result<()> { + let path = self.traversal_path(path); let mut unlink_params = UnlinkParams { name: GuestPhysAddr::new( @@ -221,15 +210,11 @@ impl VfsNode for UhyveDirectory { Ok(()) } - fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> io::Result<()> { + fn traverse_rmdir(&self, _path: &str) -> io::Result<()> { Err(Errno::Nosys) } - fn traverse_mkdir( - &self, - _components: &mut Vec<&str>, - _mode: AccessPermission, - ) -> io::Result<()> { + fn traverse_mkdir(&self, _path: &str, _mode: AccessPermission) -> io::Result<()> { Err(Errno::Nosys) } } diff --git a/src/fs/virtio_fs.rs b/src/fs/virtio_fs.rs index cdbbeaf823..202955008f 100644 --- a/src/fs/virtio_fs.rs +++ b/src/fs/virtio_fs.rs @@ -8,7 +8,7 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicU64, Ordering}; use core::task::Poll; -use core::{future, iter, mem, ptr, slice}; +use core::{future, mem, ptr, slice}; use align_address::Align; use async_lock::Mutex; @@ -1123,20 +1123,11 @@ impl VirtioFsDirectory { } } - fn traversal_path(&self, components: &[&str]) -> CString { + fn traversal_path(&self, path: &str) -> CString { let prefix = self.prefix.as_str(); - let components_with_prefix = iter::once(prefix) - .filter(|prefix| !prefix.is_empty()) - .chain(components.iter().copied().rev()); - let path: String = components_with_prefix - .flat_map(|component| ["/", component]) - .skip(1) - .collect(); - if path.is_empty() { - CString::new("/").unwrap() - } else { - CString::new(path).unwrap() - } + let prefix = prefix.strip_suffix("/").unwrap_or(prefix); + let path = [prefix, path].join("/"); + CString::new(path).unwrap() } } @@ -1156,8 +1147,8 @@ impl VfsNode for VirtioFsDirectory { ))) } - fn traverse_readdir(&self, components: &mut Vec<&str>) -> io::Result> { - let path = self.traversal_path(components); + fn traverse_readdir(&self, path: &str) -> io::Result> { + let path = self.traversal_path(path); debug!("virtio-fs opendir: {path:#?}"); @@ -1236,8 +1227,8 @@ impl VfsNode for VirtioFsDirectory { Ok(entries) } - fn traverse_stat(&self, components: &mut Vec<&str>) -> io::Result { - let path = self.traversal_path(components); + fn traverse_stat(&self, path: &str) -> io::Result { + let path = self.traversal_path(path); debug!("virtio-fs stat: {path:#?}"); @@ -1260,12 +1251,11 @@ impl VfsNode for VirtioFsDirectory { } let path = readlink(entry_out.nodeid)?; - let mut components: Vec<&str> = path.split('/').collect(); - self.traverse_stat(&mut components) + self.traverse_stat(&path) } - fn traverse_lstat(&self, components: &mut Vec<&str>) -> io::Result { - let path = self.traversal_path(components); + fn traverse_lstat(&self, path: &str) -> io::Result { + let path = self.traversal_path(path); debug!("virtio-fs lstat: {path:#?}"); @@ -1279,11 +1269,11 @@ impl VfsNode for VirtioFsDirectory { fn traverse_open( &self, - components: &mut Vec<&str>, + path: &str, opt: OpenOption, mode: AccessPermission, ) -> io::Result>> { - let path = self.traversal_path(components); + let path = self.traversal_path(path); debug!("virtio-fs open: {path:#?}, {opt:?} {mode:?}"); @@ -1355,8 +1345,8 @@ impl VfsNode for VirtioFsDirectory { Ok(Arc::new(async_lock::RwLock::new(file.into()))) } - fn traverse_unlink(&self, components: &mut Vec<&str>) -> io::Result<()> { - let path = self.traversal_path(components); + fn traverse_unlink(&self, path: &str) -> io::Result<()> { + let path = self.traversal_path(path); let (cmd, rsp_payload_len) = ops::Unlink::create(path); let rsp = get_filesystem_driver() @@ -1368,8 +1358,8 @@ impl VfsNode for VirtioFsDirectory { Ok(()) } - fn traverse_rmdir(&self, components: &mut Vec<&str>) -> io::Result<()> { - let path = self.traversal_path(components); + fn traverse_rmdir(&self, path: &str) -> io::Result<()> { + let path = self.traversal_path(path); let (cmd, rsp_payload_len) = ops::Rmdir::create(path); let rsp = get_filesystem_driver() @@ -1381,8 +1371,8 @@ impl VfsNode for VirtioFsDirectory { Ok(()) } - fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: AccessPermission) -> io::Result<()> { - let path = self.traversal_path(components); + fn traverse_mkdir(&self, path: &str, mode: AccessPermission) -> io::Result<()> { + let path = self.traversal_path(path); let (cmd, rsp_payload_len) = ops::Mkdir::create(path, mode.bits()); let rsp = get_filesystem_driver()