From 0d76e1441b816d63c91d1e1d8a757c6e9b8d4d5d Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott <ian@iandouglasscott.com> Date: Thu, 29 Jun 2017 14:08:53 -0700 Subject: [PATCH] Relative symlink --- mount/redox/scheme.rs | 85 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/mount/redox/scheme.rs b/mount/redox/scheme.rs index 9572036..03989ac 100644 --- a/mount/redox/scheme.rs +++ b/mount/redox/scheme.rs @@ -63,6 +63,77 @@ fn path_nodes(fs: &mut FileSystem, path: &str, uid: u32, gid: u32, nodes: &mut V } } +/// Make a relative path absolute +/// Given a cwd of "scheme:/path" +/// This function will turn "foo" into "scheme:/path/foo" +/// "/foo" will turn into "scheme:/foo" +/// "bar:/foo" will be used directly, as it is already absolute +pub fn canonicalize(current: &[u8], path: &[u8]) -> Vec<u8> { + // This function is modified from a version in the kernel + let mut canon = if path.iter().position(|&b| b == b':').is_none() { + let cwd = ¤t[0..current.iter().rposition(|x| *x == '/' as u8).unwrap_or(0)]; + + let mut canon = if !path.starts_with(b"/") { + let mut c = cwd.to_vec(); + if ! c.ends_with(b"/") { + c.push(b'/'); + } + c + } else { + cwd[..cwd.iter().position(|&b| b == b':').map_or(1, |i| i + 1)].to_vec() + }; + + canon.extend_from_slice(&path); + canon + } else { + path.to_vec() + }; + + // NOTE: assumes the scheme does not include anything like "../" or "./" + let mut result = { + let parts = canon.split(|&c| c == b'/') + .filter(|&part| part != b".") + .rev() + .scan(0, |nskip, part| { + if part == b"." { + Some(None) + } else if part == b".." { + *nskip += 1; + Some(None) + } else { + if *nskip > 0 { + *nskip -= 1; + Some(None) + } else { + Some(Some(part)) + } + } + }) + .filter_map(|x| x) + .collect::<Vec<_>>(); + parts + .iter() + .rev() + .fold(Vec::new(), |mut vec, &part| { + vec.extend_from_slice(part); + vec.push(b'/'); + vec + }) + }; + result.pop(); // remove extra '/' + + // replace with the root of the scheme if it's empty + if result.len() == 0 { + let pos = canon.iter() + .position(|&b| b == b':') + .map_or(canon.len(), |p| p + 1); + canon.truncate(pos); + canon + } else { + result + } +} + impl Scheme for FileScheme { fn open(&self, url: &[u8], flags: usize, uid: u32, gid: u32) -> Result<usize> { let path = str::from_utf8(url).unwrap_or("").trim_matches('/'); @@ -109,17 +180,23 @@ impl Scheme for FileScheme { return Err(Error::new(EACCES)); } } else if node.1.is_symlink() && flags & O_STAT != O_STAT && flags & O_SYMLINK != O_SYMLINK { - // TODO: Find way to support symlink to another scheme let mut node = node; for _ in 1..10 { // XXX What should the limit be? let mut buf = [0; 4096]; let count = fs.read_node(node.0, 0, &mut buf)?; // XXX Relative paths - let path = str::from_utf8(&buf[0..count]).unwrap_or("").trim_matches('/'); + let scheme = format!("{}:", self.name); + let canon = canonicalize(format!("{}{}", scheme, str::from_utf8(url).unwrap()).as_bytes(), &buf[0..count]); + let path = str::from_utf8(&canon[scheme.len()..]).unwrap_or("").trim_matches('/'); if let Some(next_node) = path_nodes(&mut fs, path, uid, gid, &mut nodes)? { if !next_node.1.is_symlink() { - drop(fs); - return self.open(&buf[0..count], flags, uid, gid); + if canon.starts_with(scheme.as_bytes()) { + drop(fs); + return self.open(&canon[scheme.len()..], flags, uid, gid); + } else { + // TODO: Find way to support symlink to another scheme + return Err(Error::new(ENOENT)); + } } node = next_node; } else { -- GitLab