]> git.proxmox.com Git - wasi-libc.git/commitdiff
If `fd_readdir` returns a zero inode, call `fstatat` to get the inode value. (#345)
authorDan Gohman <dev@sunfishcode.online>
Fri, 2 Dec 2022 01:44:22 +0000 (17:44 -0800)
committerGitHub <noreply@github.com>
Fri, 2 Dec 2022 01:44:22 +0000 (17:44 -0800)
* If `fd_readdir` returns a zero inode, call `fstatat` to get the inode value.

On some systems, `fd_readdir` may not implement the `d_ino` field and
may set it to zero. When this happens, have wasi-libc call `fstatat` to
get the inode number.

See the discussion in
https://github.com/WebAssembly/wasi-filesystem/issues/65 for details.

* Update the `d_type` field too, in case it changes.

libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c

index b5650d6cdfa6c9d7e74ed14527a66fe70e69b1e6..951587417947e610601d215e77ba49d52b84d066 100644 (file)
@@ -3,6 +3,9 @@
 // SPDX-License-Identifier: BSD-2-Clause
 
 #include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include <assert.h>
 #include <wasi/api.h>
@@ -77,10 +80,35 @@ struct dirent *readdir(DIR *dirp) {
     GROW(dirp->dirent, dirp->dirent_size,
          offsetof(struct dirent, d_name) + entry.d_namlen + 1);
     struct dirent *dirent = dirp->dirent;
-    dirent->d_ino = entry.d_ino;
     dirent->d_type = entry.d_type;
     memcpy(dirent->d_name, name, entry.d_namlen);
     dirent->d_name[entry.d_namlen] = '\0';
+
+    // `fd_readdir` implementations may set the inode field to zero if the
+    // the inode number is unknown. In that case, do an `fstatat` to get the
+    // inode number.
+    off_t d_ino = entry.d_ino;
+    unsigned char d_type = entry.d_type;
+    if (d_ino == 0) {
+      struct stat statbuf;
+      if (fstatat(dirp->fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
+       if (errno == ENOENT) {
+         // The file disappeared before we could read it, so skip it.
+         continue;
+       }
+        return NULL;
+      }
+
+      // Fill in the inode.
+      d_ino = statbuf.st_ino;
+
+      // In case someone raced with us and replaced the object with this name
+      // with another of a different type, update the type too.
+      d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
+    }
+    dirent->d_ino = d_ino;
+    dirent->d_type = d_type;
+
     dirp->cookie = entry.d_next;
     dirp->buffer_processed += entry_size;
     return dirent;
index 06feae9e13c9875b372501f9d412df16affc6a36..079b3b3b29efd8fe1b597dff16d63c488b9a1403 100644 (file)
@@ -11,6 +11,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #include "dirent_impl.h"
 
@@ -21,6 +22,8 @@ static int sel_true(const struct dirent *de) {
 int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***namelist,
                                int (*sel)(const struct dirent *),
                                int (*compar)(const struct dirent **, const struct dirent **)) {
+  struct stat statbuf;
+
   // Match all files if no select function is provided.
   if (sel == NULL)
     sel = sel_true;
@@ -89,10 +92,30 @@ int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***name
         malloc(offsetof(struct dirent, d_name) + entry.d_namlen + 1);
     if (dirent == NULL)
       goto bad;
-    dirent->d_ino = entry.d_ino;
     dirent->d_type = entry.d_type;
     memcpy(dirent->d_name, name, entry.d_namlen);
     dirent->d_name[entry.d_namlen] = '\0';
+
+    // `fd_readdir` implementations may set the inode field to zero if the
+    // the inode number is unknown. In that case, do an `fstatat` to get the
+    // inode number.
+    off_t d_ino = entry.d_ino;
+    unsigned char d_type = entry.d_type;
+    if (d_ino == 0) {
+      if (fstatat(fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
+        return -1;
+      }
+
+      // Fill in the inode.
+      d_ino = statbuf.st_ino;
+
+      // In case someone raced with us and replaced the object with this name
+      // with another of a different type, update the type too.
+      d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
+    }
+    dirent->d_ino = d_ino;
+    dirent->d_type = d_type;
+
     cookie = entry.d_next;
 
     if (sel(dirent)) {