From 57ad275d8f7ca40703c5cac52f371c107ed96d33 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sun, 5 Jul 2026 18:33:55 +0700 Subject: [PATCH] Update redox-path --- Cargo.lock | 5 +- Cargo.toml | 4 +- src/mount/redox/scheme.rs | 346 ++++++++++++++++---------------------- 3 files changed, 155 insertions(+), 200 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 658eb9180a..f212f670e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -555,9 +555,9 @@ checksum = "384c2842d4e069d5ccacf5fe1dca4ef8d07a5444329715f0fc3c61813502d4d1" [[package]] name = "redox-path" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" +checksum = "54ec1b67c01c63545205ea6d218b336751399f0d8974c1a635c28604bedb81bc" [[package]] name = "redox-scheme" @@ -602,6 +602,7 @@ dependencies = [ "redox-scheme", "redox_syscall", "seahash", + "smallvec", "termion", "uuid", "xts-mode", diff --git a/Cargo.toml b/Cargo.toml index b4b7ade58c..cd76b889ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ parse-size = { version = "1", optional = true } range-tree = { version = "0.1", optional = true } redox_syscall = "0.9.0" seahash = { version = "4.1.0", default-features = false } +smallvec = { version = "1.15.2", optional = true } termion = { version = "4", optional = true } uuid = { version = "1.4", default-features = false } xts-mode = { version = "0.5", default-features = false } @@ -79,6 +80,7 @@ std = [ "uuid/v4", "redox_syscall/std", "redox-scheme", + "smallvec", ] [target.'cfg(not(target_os = "redox"))'.dependencies] @@ -86,7 +88,7 @@ fuser = { version = "0.16", optional = true } [target.'cfg(target_os = "redox")'.dependencies] libredox = { version = "0.1.18", optional = true } -redox-path = "0.3.0" +redox-path = "0.4.0" redox-scheme = { version = "0.11.2", optional = true } [lints.rust] diff --git a/src/mount/redox/scheme.rs b/src/mount/redox/scheme.rs index 6fe54a1ef2..8eccfd24b2 100644 --- a/src/mount/redox/scheme.rs +++ b/src/mount/redox/scheme.rs @@ -6,7 +6,11 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; use libredox::Fd; +use redox_path::RedoxReference; +use redox_path::RedoxScheme; +use redox_path::RedoxStr; use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, SendFdRequest, Socket}; +use smallvec::SmallVec; use syscall::data::{Stat, StatVfs, StdFsCallMeta, TimeSpec}; use syscall::dirent::DirentBuf; use syscall::error::{ @@ -22,10 +26,7 @@ use syscall::FobtainFdFlags; use syscall::FsCall; use syscall::MunmapFlags; -use redox_path::{ - canonicalize_to_standard, canonicalize_using_cwd, canonicalize_using_scheme, scheme_path, - RedoxPath, -}; +use redox_path::RedoxPath; use crate::{Disk, FileSystem, Node, Transaction, TreeData, TreePtr, BLOCK_SIZE}; @@ -64,7 +65,7 @@ impl Handle { } pub struct FileScheme<'sock, D: Disk> { - scheme_name: String, + scheme_name: RedoxScheme<'sock>, mounted_path: String, pub(crate) fs: FileSystem, socket: &'sock Socket, @@ -86,7 +87,8 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { socket: &'sock Socket, ) -> Result> { Ok(FileScheme { - scheme_name, + scheme_name: RedoxScheme::new(scheme_name) + .expect("scheme name for FileScheme is not valid"), mounted_path, fs, socket, @@ -104,24 +106,23 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { }) } - fn resolve_symlink( - scheme_name: &str, + /// Resolve a symbolic link of given `node`. `full_path` must be non-canonicalized path from root node. + fn resolve_symlink<'a>( + scheme_name: &RedoxScheme<'sock>, tx: &mut Transaction, uid: u32, gid: u32, - full_path: &str, + full_path: RedoxReference<'a>, node: TreeData, - nodes: &mut Vec<(TreeData, String)>, - ) -> Result { + nodes: &mut SmallVec<[(TreeData, String); 16]>, + ) -> Result> { let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - // symbolic link is relative to this part of the url - let mut working_dir = - dirname(full_path).unwrap_or(scheme_path(scheme_name).ok_or(Error::new(EINVAL))?); + let mut working_dir = full_path.dirname(); // node of the link let mut node = node; - for _ in 0..32 { + for _ in 0..64 { // XXX What should the limit be? assert!(node.data().is_symlink()); let mut buf = [0; 4096]; @@ -133,18 +134,20 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { atime.subsec_nanos(), )?; - let target = canonicalize_to_standard( - Some(&working_dir), - str::from_utf8(&buf[..count]).or(Err(Error::new(EINVAL)))?, - ) - .ok_or(Error::new(EINVAL))?; - let target_as_path = RedoxPath::from_absolute(&target).ok_or(Error::new(EINVAL))?; - - let (scheme, reference) = target_as_path.as_parts().ok_or(Error::new(EINVAL))?; - if scheme.as_ref() != scheme_name { - return Err(Error::new(EXDEV)); - } - let target_reference = reference.to_string(); + let path = str::from_utf8(&buf[..count]).or(Err(Error::new(EINVAL)))?; + let path = RedoxStr::new(path).ok_or(Error::new(EINVAL))?; + let target_reference = match path { + RedoxStr::Absolute(redox_path) => { + if redox_path + .get_scheme() + .is_some_and(|s| s.as_ref() != scheme_name.as_ref()) + { + return Err(Error::new(EXDEV)); + } + redox_path.to_reference() + } + RedoxStr::Relative(redox_reference) => working_dir.join_checked(redox_reference), + }; nodes.clear(); if let Some((next_node, next_node_name)) = Self::path_nodes( @@ -158,10 +161,10 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { )? { if !next_node.data().is_symlink() { nodes.push((next_node, next_node_name)); - return Ok(target_reference); + return Ok(target_reference.into_owned()); } node = next_node; - working_dir = dirname(&target).ok_or(Error::new(EINVAL))?.to_string(); + working_dir = target_reference.dirname() } else { return Err(Error::new(ENOENT)); } @@ -180,29 +183,32 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { return Ok(len); } - fn open(&mut self, url: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn open( + &mut self, + url: RedoxReference<'_>, + flags: usize, + ctx: &CallerCtx, + ) -> Result { self.open_internal(TreePtr::root(), url, flags, ctx) } fn open_internal( &mut self, start_ptr: TreePtr, - url: &str, + path: RedoxReference<'_>, flags: usize, ctx: &CallerCtx, ) -> Result { let CallerCtx { uid, gid, .. } = *ctx; - let path = url.trim_matches('/'); - // println!("Open '{}' {:X}", &path, flags); //TODO: try to move things into one transaction let scheme_name = &self.scheme_name; - let mut nodes = Vec::new(); + let mut nodes = SmallVec::new(); let node_opt = self .fs - .tx(|tx| Self::path_nodes(scheme_name, tx, start_ptr, path, uid, gid, &mut nodes))?; + .tx(|tx| Self::path_nodes(scheme_name, tx, start_ptr, &path, uid, gid, &mut nodes))?; let parent_ptr_opt = nodes.last().map(|x| x.0.ptr()); let handle: Handle = match node_opt { Some((node, _node_name)) => { @@ -257,21 +263,19 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { && !(flags & O_STAT == O_STAT && flags & O_NOFOLLOW == O_NOFOLLOW) && flags & O_SYMLINK != O_SYMLINK { - let mut resolve_nodes = Vec::new(); - let full_path = - canonicalize_using_scheme(scheme_name, url).ok_or(Error::new(EINVAL))?; + let mut resolve_nodes = SmallVec::new(); let resolved = self.fs.tx(|tx| { Self::resolve_symlink( scheme_name, tx, uid, gid, - &full_path, + path, node, &mut resolve_nodes, ) })?; - return self.open(&resolved, flags, ctx); + return self.open(resolved, flags, ctx); } else if !node.data().is_symlink() && flags & O_SYMLINK == O_SYMLINK { return Err(Error::new(EINVAL)); } else { @@ -315,79 +319,64 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { } } None => { - if flags & O_CREAT == O_CREAT { - let mut last_part = String::new(); - for part in path.split('/') { - if !part.is_empty() { - last_part = part.to_string(); - } - } - if !last_part.is_empty() { - if let Some((parent, _parent_name)) = nodes.last() { - if !parent.data().permission(uid, gid, Node::MODE_WRITE) { - // println!("dir not writable {:o}", parent.1.mode); - return Err(Error::new(EACCES)); - } - - let dir = flags & O_DIRECTORY == O_DIRECTORY; - let mode_type = if dir { - Node::MODE_DIR - } else if flags & O_SYMLINK == O_SYMLINK { - Node::MODE_SYMLINK - } else { - Node::MODE_FILE - }; - - let node_ptr = self.fs.tx(|tx| { - let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let mut node = tx.create_node( - parent.ptr(), - &last_part, - mode_type as u16 | (flags as u16 & Node::MODE_PERM), - ctime.as_secs(), - ctime.subsec_nanos(), - )?; - let node_ptr = node.ptr(); - if node.data().uid() != uid || node.data().gid() != gid { - node.data_mut().set_uid(uid); - node.data_mut().set_gid(gid); - tx.sync_tree(node)?; - } - Ok(node_ptr) - })?; - - if dir { - Handle::ResourceDir(( - DirResource::new( - path.to_string(), - parent_ptr_opt, - node_ptr, - None, - uid, - ), - PhantomData, - )) - } else { - Handle::ResourceFile(( - FileResource::new( - path.to_string(), - parent_ptr_opt, - node_ptr, - flags, - uid, - ), - PhantomData, - )) - } - } else { - return Err(Error::new(EPERM)); - } - } else { - return Err(Error::new(EPERM)); - } - } else { + if flags & O_CREAT != O_CREAT { return Err(Error::new(ENOENT)); } + let mut last_part = String::new(); + for part in path.as_ref().split('/') { + if !part.is_empty() { + last_part = part.to_string(); + } + } + if last_part.is_empty() { + return Err(Error::new(EPERM)); + } + let Some((parent, _parent_name)) = nodes.last() else { + return Err(Error::new(EPERM)); + }; + if !parent.data().permission(uid, gid, Node::MODE_WRITE) { + // println!("dir not writable {:o}", parent.1.mode); + return Err(Error::new(EACCES)); + } + + let dir = flags & O_DIRECTORY == O_DIRECTORY; + let mode_type = if dir { + Node::MODE_DIR + } else if flags & O_SYMLINK == O_SYMLINK { + Node::MODE_SYMLINK + } else { + Node::MODE_FILE + }; + + let node_ptr = self.fs.tx(|tx| { + let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let mut node = tx.create_node( + parent.ptr(), + &last_part, + mode_type as u16 | (flags as u16 & Node::MODE_PERM), + ctime.as_secs(), + ctime.subsec_nanos(), + )?; + let node_ptr = node.ptr(); + if node.data().uid() != uid || node.data().gid() != gid { + node.data_mut().set_uid(uid); + node.data_mut().set_gid(gid); + tx.sync_tree(node)?; + } + Ok(node_ptr) + })?; + + if dir { + Handle::ResourceDir(( + DirResource::new(path.to_string(), parent_ptr_opt, node_ptr, None, uid), + PhantomData, + )) + } else { + Handle::ResourceFile(( + FileResource::new(path.to_string(), parent_ptr_opt, node_ptr, flags, uid), + PhantomData, + )) + } } }; @@ -416,7 +405,7 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { fn unlink_internal( &mut self, start_ptr: TreePtr, - path: &str, + path: &RedoxReference<'_>, flags: usize, uid: u32, gid: u32, @@ -424,7 +413,7 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { let scheme_name = &self.scheme_name; let unlink_result = self.fs.tx(|tx| { - let mut nodes = Vec::new(); + let mut nodes = SmallVec::new(); let Some((child, child_name)) = Self::path_nodes(scheme_name, tx, start_ptr, path, uid, gid, &mut nodes)? @@ -485,15 +474,16 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { } fn path_nodes( - scheme_name: &str, + scheme_name: &RedoxScheme<'sock>, tx: &mut Transaction, start_ptr: TreePtr, - path: &str, + path: &RedoxReference<'_>, uid: u32, gid: u32, - nodes: &mut Vec<(TreeData, String)>, + nodes: &mut SmallVec<[(TreeData, String); 16]>, ) -> Result, String)>> { let mut parts = path + .as_ref() .split('/') .filter(|part| !part.is_empty() && *part != "."); let mut part_opt: Option<&str> = None; @@ -516,13 +506,16 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { } if node.data().is_symlink() { let mut url = String::new(); - url.push_str(scheme_name); - url.push(':'); + // url.push_str(scheme_name.as_ref()); + // url.push(':'); for (_parent, parent_name) in nodes.iter() { - url.push('/'); + if !url.is_empty() { + url.push('/'); + } url.push_str(&parent_name); } - Self::resolve_symlink(scheme_name, tx, uid, gid, &url, node, nodes)?; + let url = RedoxReference::new(url).ok_or(Error::new(EINVAL))?; + Self::resolve_symlink(scheme_name, tx, uid, gid, url, node, nodes)?; node_ptr = nodes.last().unwrap().0.ptr(); } else if !node.data().is_dir() { return Err(Error::new(ENOTDIR)); @@ -543,61 +536,13 @@ impl<'sock, D: Disk> FileScheme<'sock, D> { } } -/// given a path with a scheme, return the containing directory (or scheme) -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: &dyn Resource, 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)) +pub fn resolve_path<'a, 'b, D: Disk>( + dir: &'a dyn Resource, + path: RedoxReference<'b>, +) -> Result> { + let dirpath = RedoxReference::new(dir.path()); + let dirpath = dirpath.ok_or(Error::new(ENOENT))?; + Ok(dirpath.join_checked(path)) } impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { @@ -615,31 +560,34 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { _fcntl_flags: u32, ctx: &CallerCtx, ) -> Result { - let path = path.to_string(); + let path = RedoxReference::new(path).ok_or(Error::new(EINVAL))?; let path_to_open = match Handle::get_resource_or(self.handles.get(&dirfd))? { // If pathname is absolute, then dirfd is ignored. - Some(res) if !path.starts_with('/') => { - resolve_path(res, path).ok_or(Error::new(ENOENT))? - } - _ => path.to_string(), + Some(res) if path.is_relative() => resolve_path(res, path)?, + _ => path, }; - self.open_internal(TreePtr::root(), &path_to_open, flags, ctx) + self.open_internal( + TreePtr::root(), + path_to_open.to_relative().canonical(), + flags, + ctx, + ) } - fn unlinkat(&mut self, dirfd: usize, url: &str, flags: usize, ctx: &CallerCtx) -> Result<()> { + fn unlinkat(&mut self, dirfd: usize, path: &str, flags: usize, ctx: &CallerCtx) -> Result<()> { let uid = ctx.uid; let gid = ctx.gid; - let url = url.to_string(); + let path = RedoxReference::new(path).ok_or(Error::new(EINVAL))?; let path = match Handle::get_resource_or(self.handles.get(&dirfd))? { - Some(res) => resolve_path(res, url).ok_or(Error::new(ENOENT))?, - None => url, + // If pathname is absolute, then dirfd is ignored. + Some(res) if path.is_relative() => resolve_path(res, path)?, + _ => path, }; - let path = path.trim_matches('/'); let start_ptr = TreePtr::root(); // println!("Unlinkat '{}' flags: {:X}", path, flags); - self.unlink_internal(start_ptr, path, flags, uid, gid) + self.unlink_internal(start_ptr, &path.canonical(), flags, uid, gid) } /* Resource operations */ @@ -727,7 +675,9 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { //TODO: this function has too much code, try to simplify it fn flink(&mut self, id: usize, url: &str, ctx: &CallerCtx) -> Result { - let new_path = url.trim_matches('/'); + let new_path = RedoxReference::new(url) + .ok_or(Error::new(EINVAL))? + .canonical(); let uid = ctx.uid; let gid = ctx.gid; @@ -749,7 +699,7 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { } let mut new_name = String::new(); - for part in new_path.split('/') { + for part in new_path.as_ref().split('/') { if !part.is_empty() { new_name = part.to_string(); } @@ -775,12 +725,12 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { return Err(Error::new(EACCES)); } - let mut new_nodes = Vec::new(); + let mut new_nodes = SmallVec::new(); let new_node_opt = Self::path_nodes( scheme_name, tx, TreePtr::root(), - new_path, + &new_path, uid, gid, &mut new_nodes, @@ -821,7 +771,7 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { tx.link_node(new_parent.ptr(), &new_name, orig_node.ptr())?; - file.set_path(new_path); + file.set_path(new_path.as_ref()); Ok(0) } else { Err(Error::new(EPERM)) @@ -831,7 +781,9 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { //TODO: this function has too much code, try to simplify it fn frename(&mut self, id: usize, url: &str, ctx: &CallerCtx) -> Result { - let new_path = url.trim_matches('/'); + let new_path = RedoxReference::new(url) + .ok_or(Error::new(EINVAL))? + .canonical(); let uid = ctx.uid; let gid = ctx.gid; @@ -853,7 +805,7 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { } let mut new_name = String::new(); - for part in new_path.split('/') { + for part in new_path.as_ref().split('/') { if !part.is_empty() { new_name = part.to_string(); } @@ -879,12 +831,12 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { return Err(Error::new(EACCES)); } - let mut new_nodes = Vec::new(); + let mut new_nodes = SmallVec::new(); let new_node_opt = Self::path_nodes( scheme_name, tx, TreePtr::root(), - new_path, + &new_path, uid, gid, &mut new_nodes, @@ -925,7 +877,7 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> { tx.rename_node(orig_parent_ptr, &old_name, new_parent.ptr(), &new_name)?; - file.set_path(new_path); + file.set_path(new_path.as_ref()); Ok(0) } else { Err(Error::new(EPERM))