diff --git a/Cargo.lock b/Cargo.lock
index cc5241b412538493aed2dcc10f29fb1118b861f4..cae109214aa390d520cee6ac81fe81325433078d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -260,6 +260,12 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "384c2842d4e069d5ccacf5fe1dca4ef8d07a5444329715f0fc3c61813502d4d1"
 
+[[package]]
+name = "redox-path"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7"
+
 [[package]]
 name = "redox_simple_endian"
 version = "0.3.0"
@@ -303,6 +309,7 @@ dependencies = [
  "libc",
  "log",
  "range-tree",
+ "redox-path",
  "redox_simple_endian",
  "redox_syscall 0.4.1",
  "seahash",
diff --git a/Cargo.toml b/Cargo.toml
index d875c2556d21d534c31d01799cf74bf769e81772..5541f5aae9162ed2fe0c95044f531f0be9a231d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,6 +43,7 @@ range-tree = { version = "0.1", optional = true }
 seahash = { version = "4.1.0", default-features = false }
 termion = { version = "1.5.6", optional = true }
 uuid = { version = "1.4", default-features = false }
+redox-path = "0.2.0"
 
 # https://github.com/rexlunae/simple-endian-rs/pull/5
 [dependencies.redox_simple_endian]
diff --git a/src/bin/mount.rs b/src/bin/mount.rs
index 986bb5f63a70076e78049768bc95340d0eb104ab..e7347e2a97faac7d71d89d830c1dfec294fd487f 100644
--- a/src/bin/mount.rs
+++ b/src/bin/mount.rs
@@ -205,15 +205,17 @@ fn filesystem_by_uuid(
 ) -> Option<(String, FileSystem<DiskCache<DiskFile>>)> {
     use std::fs;
 
-    match fs::read_dir(":") {
+    use redox_path::canonicalize_using_scheme;
+
+    match fs::read_dir("/scheme") {
         Ok(entries) => {
             for entry_res in entries {
                 if let Ok(entry) = entry_res {
-                    if let Ok(path) = entry.path().into_os_string().into_string() {
-                        let scheme = path.trim_start_matches(':').trim_matches('/');
+                    if let Ok(scheme) = entry.file_name().into_string() {
                         if scheme.starts_with("disk") {
                             println!("redoxfs: found scheme {}", scheme);
-                            match fs::read_dir(&format!("{}:", scheme)) {
+                            let scheme_path = canonicalize_using_scheme(&scheme, "")?;
+                            match fs::read_dir(scheme_path) {
                                 Ok(entries) => {
                                     for entry_res in entries {
                                         if let Ok(entry) = entry_res {
diff --git a/src/mount/redox/scheme.rs b/src/mount/redox/scheme.rs
index c34fa7fd601e94b0e5fa843c0b534a463cf4d6fc..80bd73b2fe74e789e7064d3d139c335b40110486 100644
--- a/src/mount/redox/scheme.rs
+++ b/src/mount/redox/scheme.rs
@@ -15,6 +15,8 @@ use syscall::flag::{
 use syscall::scheme::SchemeMut;
 use syscall::{MunmapFlags, EBADFD};
 
+use redox_path::{canonicalize_using_cwd, canonicalize_using_scheme, RedoxPath};
+
 use crate::{Disk, FileSystem, Node, Transaction, TreeData, TreePtr, BLOCK_SIZE};
 
 use super::resource::{DirResource, FileResource, Resource};
@@ -43,15 +45,15 @@ impl<D: Disk> FileScheme<D> {
         tx: &mut Transaction<D>,
         uid: u32,
         gid: u32,
-        url: &str,
+        full_path: &str,
         node: TreeData<Node>,
         nodes: &mut Vec<(TreeData<Node>, String)>,
     ) -> Result<String> {
         let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
-        let scheme = format!("{}:", scheme_name);
+        let scheme_path = canonicalize_using_scheme(scheme_name, "").ok_or(Error::new(EINVAL))?;
 
         // symbolic link is relative to this part of the url
-        let mut working_dir = dirname(url).unwrap_or(&scheme).to_string();
+        let mut working_dir = dirname(full_path).unwrap_or(scheme_path.to_string());
         // node of the link
         let mut node = node;
 
@@ -66,22 +68,49 @@ impl<D: Disk> FileScheme<D> {
                 atime.as_secs(),
                 atime.subsec_nanos(),
             )?;
-            let target = canonicalize(
-                &working_dir,
-                str::from_utf8(&buf[..count]).map_err(|_| Error::new(EINVAL))?,
+
+            let target = canonicalize_using_cwd(
+                Some(&working_dir),
+                str::from_utf8(&buf[..count]).or(Err(Error::new(EINVAL)))?,
             )
             .ok_or(Error::new(EINVAL))?;
-            if !target.starts_with(&scheme) {
-                return Err(Error::new(EXDEV));
-            }
-            let target_path = &target[scheme.len()..];
+            let target_as_path = RedoxPath::from_absolute(&target).ok_or(Error::new(EINVAL))?;
+
+            let target_reference = match target_as_path {
+                RedoxPath::Standard(_) => {
+                    let (scheme, reference) =
+                        target_as_path.as_parts().ok_or(Error::new(EINVAL))?;
+                    if scheme.as_ref() != scheme_name {
+                        return Err(Error::new(EXDEV));
+                    }
+                    reference.as_ref().to_string()
+                }
+                RedoxPath::Legacy(scheme, reference) => {
+                    // Legacy references are not canonicalized because they can contain coded information
+                    // We know this is a `file:` reference, so canonicalize into a Standard path
+                    if scheme.as_ref() != scheme_name {
+                        return Err(Error::new(EXDEV));
+                    }
+                    let target = canonicalize_using_scheme(scheme_name, reference.as_ref())
+                        .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));
+                    }
+                    reference.as_ref().to_string()
+                }
+            };
+
             nodes.clear();
             if let Some((next_node, next_node_name)) =
-                Self::path_nodes(scheme_name, tx, target_path, uid, gid, nodes)?
+                Self::path_nodes(scheme_name, tx, &target_reference, uid, gid, nodes)?
             {
                 if !next_node.data().is_symlink() {
                     nodes.push((next_node, next_node_name));
-                    return Ok(target_path.to_string());
+                    return Ok(target_reference.to_string());
                 }
                 node = next_node;
                 working_dir = dirname(&target).ok_or(Error::new(EINVAL))?.to_string();
@@ -148,91 +177,9 @@ impl<D: Disk> FileScheme<D> {
     }
 }
 
-/// given a path with a prefix, return the containing directory (or scheme)
-fn dirname(path: &str) -> Option<&str> {
-    if let Some(separator_index) = path.trim_end_matches('/').rfind('/') {
-        return Some(&path[..=separator_index]);
-    }
-    // there was no path separator, just use the scheme
-    if let Some(scheme) = scheme_with_separator(path) {
-        return Some(scheme);
-    }
-    None
-}
-
-/// given a path with a scheme prefix, return the prefix including the separator
-fn scheme_with_separator(path: &str) -> Option<&str> {
-    if let Some(sep_idx) = path.find(':') {
-        let scheme = &path[..=sep_idx];
-        if scheme.find('/').is_none() {
-            return Some(&scheme);
-        }
-    }
-    None
-}
-
-/// 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: &str, path: &str) -> Option<String> {
-    // This function is modified from a version in the kernel
-    let mut canon = if scheme_with_separator(path).is_none() {
-        let mut canon = if !path.starts_with('/') {
-            let mut cwd = current.trim_end_matches('/').to_string();
-            cwd.push('/');
-            cwd
-        } else {
-            scheme_with_separator(current)?.to_string()
-        };
-
-        canon.push_str(path);
-        canon
-    } else {
-        path.to_string()
-    };
-
-    // NOTE: assumes the scheme does not include anything like "../" or "./"
-    let mut result = {
-        let parts = canon
-            .split('/')
-            .rev()
-            .scan(0, |nskip, part| {
-                if part == "" {
-                    Some(None)
-                } else if part == "." {
-                    Some(None)
-                } else if part == ".." {
-                    *nskip += 1;
-                    Some(None)
-                } else {
-                    if *nskip > 0 {
-                        *nskip -= 1;
-                        Some(None)
-                    } else {
-                        Some(Some(part))
-                    }
-                }
-            })
-            .filter_map(|x| x)
-            .filter(|x| !x.is_empty())
-            .collect::<Vec<_>>();
-        parts.iter().rev().fold(String::new(), |mut string, &part| {
-            string.push_str(part);
-            string.push('/');
-            string
-        })
-    };
-    result.pop(); // remove extra '/'
-
-    // replace with the root of the scheme if it's empty
-    if result.len() == 0 {
-        canon.truncate(scheme_with_separator(&canon)?.len());
-        Some(canon)
-    } else {
-        Some(result)
-    }
+/// given a path with a scheme, return the containing directory (or scheme)
+fn dirname(path: &str) -> Option<String> {
+    canonicalize_using_cwd(Some(path), "..")
 }
 
 impl<D: Disk> SchemeMut for FileScheme<D> {
@@ -296,13 +243,15 @@ impl<D: Disk> SchemeMut for FileScheme<D> {
                     && 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 resolved = self.fs.tx(|tx| {
                         Self::resolve_symlink(
                             scheme_name,
                             tx,
                             uid,
                             gid,
-                            &format!("{}:/{}", scheme_name, url),
+                            &full_path,
                             node,
                             &mut resolve_nodes,
                         )