Skip to content

Commit 5ec38e7

Browse files
Dan Gohmanjohn-sharratt
authored andcommitted
If fd_readdir returns a zero inode, call fstatat to get the inode value. (WebAssembly#345)
* 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.
1 parent 812e928 commit 5ec38e7

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// SPDX-License-Identifier: BSD-2-Clause
44

55
#include <sys/stat.h>
6+
#include <sys/types.h>
7+
#include <unistd.h>
8+
#include <fcntl.h>
69

710
#include <assert.h>
811
#include <wasi/api.h>
@@ -77,10 +80,35 @@ struct dirent *readdir(DIR *dirp) {
7780
GROW(dirp->dirent, dirp->dirent_size,
7881
offsetof(struct dirent, d_name) + entry.d_namlen + 1);
7982
struct dirent *dirent = dirp->dirent;
80-
dirent->d_ino = entry.d_ino;
8183
dirent->d_type = entry.d_type;
8284
memcpy(dirent->d_name, name, entry.d_namlen);
8385
dirent->d_name[entry.d_namlen] = '\0';
86+
87+
// `fd_readdir` implementations may set the inode field to zero if the
88+
// the inode number is unknown. In that case, do an `fstatat` to get the
89+
// inode number.
90+
off_t d_ino = entry.d_ino;
91+
unsigned char d_type = entry.d_type;
92+
if (d_ino == 0) {
93+
struct stat statbuf;
94+
if (fstatat(dirp->fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
95+
if (errno == ENOENT) {
96+
// The file disappeared before we could read it, so skip it.
97+
continue;
98+
}
99+
return NULL;
100+
}
101+
102+
// Fill in the inode.
103+
d_ino = statbuf.st_ino;
104+
105+
// In case someone raced with us and replaced the object with this name
106+
// with another of a different type, update the type too.
107+
d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
108+
}
109+
dirent->d_ino = d_ino;
110+
dirent->d_type = d_type;
111+
84112
dirp->cookie = entry.d_next;
85113
dirp->buffer_processed += entry_size;
86114
return dirent;

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <stdlib.h>
1212
#include <string.h>
1313
#include <unistd.h>
14+
#include <sys/stat.h>
1415

1516
#include "dirent_impl.h"
1617

@@ -21,6 +22,8 @@ static int sel_true(const struct dirent *de) {
2122
int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***namelist,
2223
int (*sel)(const struct dirent *),
2324
int (*compar)(const struct dirent **, const struct dirent **)) {
25+
struct stat statbuf;
26+
2427
// Match all files if no select function is provided.
2528
if (sel == NULL)
2629
sel = sel_true;
@@ -89,10 +92,30 @@ int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***name
8992
malloc(offsetof(struct dirent, d_name) + entry.d_namlen + 1);
9093
if (dirent == NULL)
9194
goto bad;
92-
dirent->d_ino = entry.d_ino;
9395
dirent->d_type = entry.d_type;
9496
memcpy(dirent->d_name, name, entry.d_namlen);
9597
dirent->d_name[entry.d_namlen] = '\0';
98+
99+
// `fd_readdir` implementations may set the inode field to zero if the
100+
// the inode number is unknown. In that case, do an `fstatat` to get the
101+
// inode number.
102+
off_t d_ino = entry.d_ino;
103+
unsigned char d_type = entry.d_type;
104+
if (d_ino == 0) {
105+
if (fstatat(fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
106+
return -1;
107+
}
108+
109+
// Fill in the inode.
110+
d_ino = statbuf.st_ino;
111+
112+
// In case someone raced with us and replaced the object with this name
113+
// with another of a different type, update the type too.
114+
d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
115+
}
116+
dirent->d_ino = d_ino;
117+
dirent->d_type = d_type;
118+
96119
cookie = entry.d_next;
97120

98121
if (sel(dirent)) {

0 commit comments

Comments
 (0)