diff --git a/src/mount/redox/scheme.rs b/src/mount/redox/scheme.rs index 1cda78f925..3cfaf7a573 100644 --- a/src/mount/redox/scheme.rs +++ b/src/mount/redox/scheme.rs @@ -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, String)>, ) -> Result, 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 { canonicalize_using_cwd(Some(path), "..") } +// TODO: Reimplement these using the scheme common path related crate +pub fn resolve_path(dir: &Box>, path: String) -> Option { + 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 { 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 { - 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() {