wasi-common 0.1.0

WASI implementation in Rust
Documentation
diff --git a/src/hostcalls/fs.rs b/src/hostcalls/fs.rs
index b99b747..9dc423c 100644
--- a/src/hostcalls/fs.rs
+++ b/src/hostcalls/fs.rs
@@ -663,13 +663,17 @@ pub fn path_open(
             | host::__WASI_RIGHT_FD_ALLOCATE
             | host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE)
         != 0;
+    let open = (fs_rights_base & (host::__WASI_RIGHT_FD_TELL | host::__WASI_RIGHT_FD_ADVISE)) != 0
+        || (oflags & wasm32::__WASI_O_CREAT) != 0;
 
     let mut nix_all_oflags = if read && write {
         OFlag::O_RDWR
     } else if write {
         OFlag::O_WRONLY
-    } else {
+    } else if open {
         OFlag::O_RDONLY
+    } else {
+        OFlag::O_PATH
     };
 
     // on non-Capsicum systems, we always want nofollow
diff --git a/src/hostcalls/fs_helpers.rs b/src/hostcalls/fs_helpers.rs
index 5f5400e..85cae7c 100644
--- a/src/hostcalls/fs_helpers.rs
+++ b/src/hostcalls/fs_helpers.rs
@@ -20,6 +20,7 @@ pub fn path_get<P: AsRef<OsStr>>(
     needed_inheriting: host::__wasi_rights_t,
     needs_final_component: bool,
 ) -> Result<(RawFd, OsString), host::__wasi_errno_t> {
+    eprintln!("--------- path_get: nfc={}", needs_final_component);
     use nix::errno::Errno;
     use nix::fcntl::{openat, readlinkat, OFlag};
     use nix::sys::stat::Mode;
@@ -64,9 +65,18 @@ pub fn path_get<P: AsRef<OsStr>>(
     // escaping the base directory.
     let mut dir_stack = vec![dirfe.fd_object.rawfd];
 
+    let mut ends_with_slashes = false;
+    let mut path_vec = path.as_ref().to_owned().into_vec();
+    while path_vec.ends_with(b"/") {
+        ends_with_slashes = true;
+        path_vec.pop();
+    }
+    eprintln!("ews={}", ends_with_slashes);
+
     // Stack of paths left to process. This is initially the `path` argument to this function, but
     // any symlinks we encounter are processed by pushing them on the stack.
-    let mut path_stack = vec![path.as_ref().to_owned().into_vec()];
+    eprintln!("path {:?}", path.as_ref());
+    let mut path_stack = vec![path_vec];
 
     // Track the number of symlinks we've expanded, so we can return `ELOOP` after too many.
     let mut symlink_expansions = 0;
@@ -78,10 +88,10 @@ pub fn path_get<P: AsRef<OsStr>>(
     // trailing slashes. This version does way too much allocation, and is way too fiddly.
     loop {
         let component = if let Some(cur_path) = path_stack.pop() {
-            // eprintln!(
-            //     "cur_path = {:?}",
-            //     std::str::from_utf8(cur_path.as_slice()).unwrap()
-            // );
+            eprintln!(
+                "cur_path = {:?}",
+                std::str::from_utf8(cur_path.as_slice()).unwrap()
+            );
             let mut split = cur_path.splitn(2, |&c| c == '/' as u8);
             let head = split.next();
             let tail = split.next();
@@ -144,11 +154,10 @@ pub fn path_get<P: AsRef<OsStr>>(
             // should the component be a directory? it should if there is more path left to process, or
             // if it has a trailing slash and `needs_final_component` is not set
             component
-                if !path_stack.is_empty()
-                    || (component.ends_with(b"/") && !needs_final_component) =>
+                if !path_stack.is_empty() || (ends_with_slashes && !needs_final_component) =>
             {
                 match openat(
-                    *dir_stack.first().expect("dir_stack is never empty"),
+                    *dir_stack.last().expect("dir_stack is never empty"),
                     component,
                     OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW,
                     Mode::empty(),
@@ -181,7 +190,7 @@ pub fn path_get<P: AsRef<OsStr>>(
                                 // append a trailing slash if the component leading to it has one, so
                                 // that we preserve any ENOTDIR that might come from trying to open a
                                 // non-directory
-                                if component.ends_with(b"/") {
+                                if ends_with_slashes {
                                     link_path.push('/' as u8);
                                 }
 
@@ -189,9 +198,16 @@ pub fn path_get<P: AsRef<OsStr>>(
                                 continue;
                             }
                             Err(e) => {
+                                // readlink returns EINVAL if the path isn't a symlink. In that case,
+                                // it's more informative to return ENOTDIR.
+                                let report_errno = if e == nix::Error::Sys(Errno::EINVAL) {
+                                    nix::Error::Sys(Errno::ENOTDIR)
+                                } else {
+                                    e
+                                };
                                 return ret_error(
                                     &mut dir_stack,
-                                    host::errno_from_nix(e.as_errno().unwrap()),
+                                    host::errno_from_nix(report_errno.as_errno().unwrap()),
                                 );
                             }
                         }
@@ -208,8 +224,7 @@ pub fn path_get<P: AsRef<OsStr>>(
             component => {
                 // if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt
                 // symlink expansion
-                if component.ends_with(b"/") || (dirflags & host::__WASI_LOOKUP_SYMLINK_FOLLOW) != 0
-                {
+                if ends_with_slashes || (dirflags & host::__WASI_LOOKUP_SYMLINK_FOLLOW) != 0 {
                     match readlinkat(
                         *dir_stack.last().expect("dir_stack is never empty"),
                         component,
@@ -226,7 +241,7 @@ pub fn path_get<P: AsRef<OsStr>>(
                             // append a trailing slash if the component leading to it has one, so
                             // that we preserve any ENOTDIR that might come from trying to open a
                             // non-directory
-                            if component.ends_with(b"/") {
+                            if ends_with_slashes {
                                 link_path.push('/' as u8);
                             }