Skip to content

Commit

Permalink
If fd_readdir returns a zero inode, call fstatat to get the inode…
Browse files Browse the repository at this point in the history
… 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
WebAssembly/wasi-filesystem#65 for details.
  • Loading branch information
sunfishcode committed Nov 24, 2022
1 parent a00bf32 commit 3b42aff
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 2 deletions.
16 changes: 15 additions & 1 deletion libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ static_assert(DT_UNKNOWN == __WASI_FILETYPE_UNKNOWN, "Value mismatch");
} while (0)

struct dirent *readdir(DIR *dirp) {
struct stat statbuf;

for (;;) {
// Extract the next dirent header.
size_t buffer_left = dirp->buffer_used - dirp->buffer_processed;
Expand Down Expand Up @@ -72,12 +74,24 @@ struct dirent *readdir(DIR *dirp) {
continue;
}

// `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;
if (d_ino == 0) {
if (fstatat(dirp->fd, entry.d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
return NULL;
}

d_ino = statbuf.st_ino;
}

// Return the next directory entry. Ensure that the dirent is large
// enough to fit the filename.
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_ino = d_ino;
dirent->d_type = entry.d_type;
memcpy(dirent->d_name, name, entry.d_namlen);
dirent->d_name[entry.d_namlen] = '\0';
Expand Down
14 changes: 13 additions & 1 deletion libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,24 @@ int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***name
if (memchr(name, '\0', entry.d_namlen) != NULL)
continue;

// `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;
if (d_ino == 0) {
if (fstatat(dirp->fd, entry.d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
return NULL;
}

d_ino = statbuf.st_ino;
}

// Create the new directory entry.
struct dirent *dirent =
malloc(offsetof(struct dirent, d_name) + entry.d_namlen + 1);
if (dirent == NULL)
goto bad;
dirent->d_ino = entry.d_ino;
dirent->d_ino = d_ino;
dirent->d_type = entry.d_type;
memcpy(dirent->d_name, name, entry.d_namlen);
dirent->d_name[entry.d_namlen] = '\0';
Expand Down

0 comments on commit 3b42aff

Please sign in to comment.