From 7a4a41a34ed389bdb62e7b890b93ee6230b7f594 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 26 Jun 2017 09:14:12 -0700 Subject: [PATCH 1/2] Work in progress symlink implementation --- mount/redox/scheme.rs | 34 ++++++++++++++++++++++++++++++---- src/node.rs | 5 +++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/mount/redox/scheme.rs b/mount/redox/scheme.rs index 3587a86062..9572036c91 100644 --- a/mount/redox/scheme.rs +++ b/mount/redox/scheme.rs @@ -8,8 +8,8 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use syscall::data::{Stat, StatVfs}; -use syscall::error::{Error, Result, EACCES, EEXIST, EISDIR, ENOTDIR, EPERM, ENOENT, EBADF}; -use syscall::flag::{O_APPEND, O_CREAT, O_DIRECTORY, O_STAT, O_EXCL, O_TRUNC, O_ACCMODE, O_RDONLY, O_WRONLY, O_RDWR, MODE_PERM}; +use syscall::error::{Error, Result, EACCES, EEXIST, EISDIR, ENOTDIR, EPERM, ENOENT, EBADF, ELOOP}; +use syscall::flag::{O_APPEND, O_CREAT, O_DIRECTORY, O_STAT, O_EXCL, O_TRUNC, O_ACCMODE, O_RDONLY, O_WRONLY, O_RDWR, MODE_PERM, O_SYMLINK}; use syscall::scheme::Scheme; pub struct FileScheme { @@ -67,7 +67,7 @@ impl Scheme for FileScheme { fn open(&self, url: &[u8], flags: usize, uid: u32, gid: u32) -> Result { let path = str::from_utf8(url).unwrap_or("").trim_matches('/'); - //println!("Open '{}' {:X}", path, flags); + println!("Open '{}' {:X}", path, flags); let mut fs = self.fs.borrow_mut(); @@ -108,6 +108,25 @@ impl Scheme for FileScheme { // println!("dir not opened with O_RDONLY"); 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('/'); + 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); + } + node = next_node; + } else { + return Err(Error::new(ENOENT)); + } + } + return Err(Error::new(ELOOP)); } else { if flags & O_DIRECTORY == O_DIRECTORY { // println!("{:X} & {:X}: ENOTDIR {}", flags, O_DIRECTORY, path); @@ -156,8 +175,15 @@ impl Scheme for FileScheme { } 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 mut node = fs.create_node(if dir { Node::MODE_DIR } else { Node::MODE_FILE } | (flags as u16 & Node::MODE_PERM), &last_part, parent.0)?; + let mut node = fs.create_node(mode_type | (flags as u16 & Node::MODE_PERM), &last_part, parent.0)?; node.1.uid = uid; node.1.gid = gid; fs.write_at(node.0, &node.1)?; diff --git a/src/node.rs b/src/node.rs index 2c87fcc838..a6ecaf6caf 100644 --- a/src/node.rs +++ b/src/node.rs @@ -18,6 +18,7 @@ impl Node { pub const MODE_TYPE: u16 = 0xF000; pub const MODE_FILE: u16 = 0x8000; pub const MODE_DIR: u16 = 0x4000; + pub const MODE_SYMLINK: u16 = 0xA000; pub const MODE_PERM: u16 = 0x0FFF; pub const MODE_EXEC: u16 = 0o1; @@ -74,6 +75,10 @@ impl Node { self.mode & Node::MODE_TYPE == Node::MODE_FILE } + pub fn is_symlink(&self) -> bool { + self.mode & Node::MODE_TYPE == Node::MODE_SYMLINK + } + pub fn permission(&self, uid: u32, gid: u32, op: u16) -> bool { let mut perm = self.mode & 0o7; if self.uid == uid { From 0d76e1441b816d63c91d1e1d8a757c6e9b8d4d5d Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 29 Jun 2017 14:08:53 -0700 Subject: [PATCH 2/2] 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 9572036c91..03989acef6 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 { + // 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::>(); + 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 { 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 {