diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs
index 2b7e253b0c55d730bd67703c87b02cd5bf615991..1746d77aca06fe945929aa2a9bbd8b248c4ba743 100644
--- a/src/syscall/fs.rs
+++ b/src/syscall/fs.rs
@@ -8,7 +8,7 @@ use crate::scheme::{self, FileHandle};
 use crate::syscall;
 use crate::syscall::data::{Packet, Stat};
 use crate::syscall::error::*;
-use crate::syscall::flag::{F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_DUPFD, O_ACCMODE, O_DIRECTORY, O_RDONLY, O_WRONLY, MODE_DIR, MODE_FILE, O_CLOEXEC};
+use crate::syscall::flag::{F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_DUPFD, O_ACCMODE, O_DIRECTORY, O_RDONLY, O_SYMLINK, O_WRONLY, MODE_DIR, MODE_FILE, O_CLOEXEC};
 use crate::context::file::{FileDescriptor, FileDescription};
 
 pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> {
@@ -85,7 +85,7 @@ pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
 
 /// Open syscall
 pub fn open(path: &[u8], flags: usize) -> Result<FileHandle> {
-    let (path_canon, uid, gid, scheme_ns, umask) = {
+    let (mut path_canon, uid, gid, scheme_ns, umask) = {
         let contexts = context::contexts();
         let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
         let context = context_lock.read();
@@ -96,32 +96,60 @@ pub fn open(path: &[u8], flags: usize) -> Result<FileHandle> {
 
     //println!("open {}", unsafe { ::core::str::from_utf8_unchecked(&path_canon) });
 
-    let mut parts = path_canon.splitn(2, |&b| b == b':');
-    let scheme_name_opt = parts.next();
-    let reference_opt = parts.next();
+    for _level in 0..32 { // XXX What should the limit be?
+        //println!("  level {} = {:?}", _level, ::core::str::from_utf8(&path_canon));
 
-    let (scheme_id, file_id) = {
-        let scheme_name = scheme_name_opt.ok_or(Error::new(ENODEV))?;
-        let (scheme_id, scheme) = {
-            let schemes = scheme::schemes();
-            let (scheme_id, scheme) = schemes.get_name(scheme_ns, scheme_name).ok_or(Error::new(ENODEV))?;
-            (scheme_id, Arc::clone(&scheme))
+        let mut parts = path_canon.splitn(2, |&b| b == b':');
+        let scheme_name_opt = parts.next();
+        let reference_opt = parts.next();
+
+        let (scheme_id, file_id) = {
+            let scheme_name = scheme_name_opt.ok_or(Error::new(ENODEV))?;
+            let (scheme_id, scheme) = {
+                let schemes = scheme::schemes();
+                let (scheme_id, scheme) = schemes.get_name(scheme_ns, scheme_name).ok_or(Error::new(ENODEV))?;
+                (scheme_id, Arc::clone(&scheme))
+            };
+            let reference = reference_opt.unwrap_or(b"");
+            let file_id = match scheme.open(reference, flags, uid, gid) {
+                Ok(ok) => ok,
+                Err(err) => if err.errno == EXDEV {
+                    let resolve_flags = O_CLOEXEC | O_SYMLINK | O_RDONLY;
+                    let resolve_id = scheme.open(reference, resolve_flags, uid, gid)?;
+
+                    let mut buf = [0; 4096];
+                    let res = scheme.read(resolve_id, &mut buf);
+
+                    let _ = scheme.close(resolve_id);
+
+                    let count = res?;
+
+                    let contexts = context::contexts();
+                    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+                    let context = context_lock.read();
+                    path_canon = context.canonicalize(&buf[..count]);
+
+                    continue;
+                } else {
+                    return Err(err);
+                }
+            };
+            (scheme_id, file_id)
         };
-        let file_id = scheme.open(reference_opt.unwrap_or(b""), flags, uid, gid)?;
-        (scheme_id, file_id)
-    };
 
-    let contexts = context::contexts();
-    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
-    let context = context_lock.read();
-    context.add_file(FileDescriptor {
-        description: Arc::new(RwLock::new(FileDescription {
-            scheme: scheme_id,
-            number: file_id,
-            flags: flags & !O_CLOEXEC,
-        })),
-        cloexec: flags & O_CLOEXEC == O_CLOEXEC,
-    }).ok_or(Error::new(EMFILE))
+        let contexts = context::contexts();
+        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
+        let context = context_lock.read();
+        return context.add_file(FileDescriptor {
+            description: Arc::new(RwLock::new(FileDescription {
+                scheme: scheme_id,
+                number: file_id,
+                flags: flags & !O_CLOEXEC,
+            })),
+            cloexec: flags & O_CLOEXEC == O_CLOEXEC,
+        }).ok_or(Error::new(EMFILE));
+    }
+    Err(Error::new(ELOOP))
 }
 
 pub fn pipe2(fds: &mut [usize], flags: usize) -> Result<usize> {