feat: Resolve parent paths

This commit is contained in:
Ibuki Omatsu
2026-02-21 14:47:59 +00:00
committed by Jeremy Soller
parent d47cd7d3f6
commit ceeaf513b6
+72 -12
View File
@@ -347,7 +347,8 @@ impl<'sock, D: Disk> FileScheme<'sock, D> {
let node_ptr = resource.node_ptr();
{
let fmap_info = self.fmap
let fmap_info = self
.fmap
.entry(node_ptr.id())
.or_insert_with(FileMmapInfo::new);
if !fmap_info.in_use() {
@@ -375,7 +376,9 @@ impl<'sock, D: Disk> FileScheme<'sock, D> {
gid: u32,
nodes: &mut Vec<(TreeData<Node>, String)>,
) -> Result<Option<(TreeData<Node>, String)>> {
let mut parts = path.split('/').filter(|part| !part.is_empty());
let mut parts = path
.split('/')
.filter(|part| !part.is_empty() && *part != ".");
let mut part_opt: Option<&str> = None;
let mut node_ptr = start_ptr;
let mut node_name = String::new();
@@ -428,6 +431,58 @@ fn dirname(path: &str) -> Option<String> {
canonicalize_using_cwd(Some(path), "..")
}
// TODO: Reimplement these using the scheme common path related crate
pub fn resolve_path<D: Disk>(dir: &Box<dyn Resource<D>>, path: String) -> Option<String> {
let max_upward_depth = 64; // Same value as MAX_LEVEL of sym loops in relibc
let (canon, depth) =
canonicalize_using_cwd_with_max_upward_depth(dir.path(), &path, max_upward_depth)?;
Some(canon)
}
pub fn canonicalize_using_cwd_with_max_upward_depth(
cwd: &str,
path: &str,
max_upward_depth: usize,
) -> Option<(String, usize)> {
let (canonical, upward_depth) =
canonicalize_with_max_upward_depth(&format!("{}/{}", cwd, path), max_upward_depth)?;
Some((canonical, upward_depth))
}
pub fn canonicalize_with_max_upward_depth(
path: &str,
max_upward_depth: usize,
) -> Option<(String, usize)> {
let mut nskip = 0;
let mut parts = Vec::new();
let mut upward_depth = 0;
for part in path.split('/').rev() {
if part == "." || part.is_empty() {
continue;
} else if part == ".." {
nskip += 1;
upward_depth += 1;
if upward_depth > max_upward_depth {
return None;
}
} else if nskip > 0 {
nskip -= 1;
} else {
parts.push(part);
}
}
let canonical = parts.iter().rev().fold(String::new(), |mut string, &part| {
if !string.is_empty() && !string.ends_with('/') {
string.push('/');
}
string.push_str(part);
string
});
Some((canonical, upward_depth))
}
impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
fn scheme_root(&mut self) -> Result<usize> {
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
@@ -443,26 +498,30 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
_fcntl_flags: u32,
ctx: &CallerCtx,
) -> Result<OpenResult> {
let dir_node_ptr = match self.handles.get(&dirfd).ok_or(Error::new(EBADF))? {
let path = path.to_string();
let path_to_open = match self.handles.get(&dirfd).ok_or(Error::new(EBADF))? {
// If pathname is absolute, then dirfd is ignored.
Handle::Resource(dir_resource) if !path.starts_with('/') => {
// only allow dirresource as base for openat
dir_resource.node_ptr()
resolve_path(&dir_resource, path).ok_or(Error::new(ENOENT))?
}
_ => TreePtr::root(),
_ => path.to_string(),
};
self.open_internal(dir_node_ptr, path, flags, ctx)
self.open_internal(TreePtr::root(), &path_to_open, flags, ctx)
}
fn unlinkat(&mut self, dirfd: usize, url: &str, flags: usize, ctx: &CallerCtx) -> Result<()> {
let path = url.trim_matches('/');
let uid = ctx.uid;
let gid = ctx.gid;
let url = url.to_string();
let start_ptr = match self.handles.get(&dirfd).ok_or(Error::new(EBADF))? {
Handle::Resource(dir_resource) => dir_resource.node_ptr(),
Handle::SchemeRoot => TreePtr::root(),
let path = match self.handles.get(&dirfd).ok_or(Error::new(EBADF))? {
Handle::Resource(dir_resource) => {
resolve_path(&dir_resource, url).ok_or(Error::new(ENOENT))?
}
Handle::SchemeRoot => url,
};
let path = path.trim_matches('/');
let start_ptr = TreePtr::root();
// println!("Unlinkat '{}' flags: {:X}", path, flags);
@@ -1062,7 +1121,8 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
let node_ptr = resource.node_ptr();
{
let fmap_info = self.fmap
let fmap_info = self
.fmap
.entry(node_ptr.id())
.or_insert_with(FileMmapInfo::new);
if !fmap_info.in_use() {