Skip to content

Commit 66ed9cd

Browse files
authored
[WasmFS] /dev/null and directory mtime updates (#17241)
Implement all the functionality needed to pass wasmfs.test_stat except for the ability to follow symlinks. In particular, implement /dev/null and update directory mtimes when their contents are changed. Update WasmFS to unconditionally return 0 as the st_rdev to bring it in line with the legacy file system on normal files, although the legacy file system reports other values for special files.
1 parent fe7cfaa commit 66ed9cd

File tree

10 files changed

+78
-25
lines changed

10 files changed

+78
-25
lines changed

.circleci/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,8 @@ jobs:
419419
wasmfs.test_exit_status
420420
wasmfs.test_minimal_runtime_memorygrowth
421421
wasmfs.test_mmap_anon*
422-
wasmfs.test_getcwd_with_non_ascii_name"
422+
wasmfs.test_getcwd_with_non_ascii_name
423+
wasmfs.test_stat"
423424
test-wasm2js1:
424425
executor: bionic
425426
steps:

system/lib/wasmfs/file.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ Directory::Handle::insertDataFile(const std::string& name, mode_t mode) {
9999
return nullptr;
100100
}
101101
cacheChild(name, child, DCacheKind::Normal);
102+
setMTime(time(NULL));
102103
return child;
103104
}
104105

@@ -113,6 +114,7 @@ Directory::Handle::insertDirectory(const std::string& name, mode_t mode) {
113114
return nullptr;
114115
}
115116
cacheChild(name, child, DCacheKind::Normal);
117+
setMTime(time(NULL));
116118
return child;
117119
}
118120

@@ -128,6 +130,7 @@ Directory::Handle::insertSymlink(const std::string& name,
128130
return nullptr;
129131
}
130132
cacheChild(name, child, DCacheKind::Normal);
133+
setMTime(time(NULL));
131134
return child;
132135
}
133136

@@ -141,7 +144,8 @@ bool Directory::Handle::insertMove(const std::string& name,
141144
return false;
142145
}
143146
// Look up the file in its old parent's cache.
144-
auto& oldCache = file->locked().getParent()->dcache;
147+
auto oldParent = file->locked().getParent();
148+
auto& oldCache = oldParent->dcache;
145149
auto oldIt = std::find_if(oldCache.begin(), oldCache.end(), [&](auto& kv) {
146150
return kv.second.file == file;
147151
});
@@ -162,6 +166,11 @@ bool Directory::Handle::insertMove(const std::string& name,
162166
it->second = entry;
163167
}
164168
file->locked().setParent(getDir());
169+
170+
auto now = time(NULL);
171+
oldParent->locked().setMTime(now);
172+
setMTime(now);
173+
165174
return true;
166175
}
167176

@@ -180,6 +189,7 @@ bool Directory::Handle::removeChild(const std::string& name) {
180189
entry->second.file->locked().setParent(nullptr);
181190
dcache.erase(entry);
182191
}
192+
setMTime(time(NULL));
183193
return true;
184194
}
185195

system/lib/wasmfs/file.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,20 @@ class File : public std::enable_shared_from_this<File> {
9393

9494
protected:
9595
File(FileKind kind, mode_t mode, backend_t backend)
96-
: kind(kind), mode(mode), backend(backend) {}
96+
: kind(kind), mode(mode), backend(backend) {
97+
atime = mtime = ctime = time(NULL);
98+
}
99+
97100
// A mutex is needed for multiple accesses to the same file.
98101
std::recursive_mutex mutex;
99102

100103
virtual size_t getSize() = 0;
101104

102105
mode_t mode = 0; // User and group mode bits for access permission.
103106

104-
time_t ctime = 0; // Time when the file node was last modified.
105-
time_t mtime = 0; // Time when the file content was last modified.
106107
time_t atime = 0; // Time when the content was last accessed.
108+
time_t mtime = 0; // Time when the file content was last modified.
109+
time_t ctime = 0; // Time when the file node was last modified.
107110

108111
// Reference to parent of current file node. This can be used to
109112
// traverse up the directory tree. A weak_ptr ensures that the ref
@@ -233,7 +236,7 @@ class Symlink : public File {
233236
// Note that symlinks provide a mode of 0 to File. The mode of a symlink does
234237
// not matter, so that value will never be read (what matters is the mode of
235238
// the target).
236-
Symlink(backend_t backend) : File(File::SymlinkKind, 0, backend) {}
239+
Symlink(backend_t backend) : File(File::SymlinkKind, S_IFLNK, backend) {}
237240
virtual ~Symlink() = default;
238241

239242
// Constant, and therefore thread-safe, and can be done without locking.

system/lib/wasmfs/special_files.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,25 @@ namespace wasmfs::SpecialFiles {
1717

1818
namespace {
1919

20+
// No-op reads and writes: /dev/null
21+
class NullFile : public DataFile {
22+
void open(oflags_t) override {}
23+
void close() override {}
24+
25+
ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {
26+
return len;
27+
}
28+
29+
ssize_t read(uint8_t* buf, size_t len, off_t offset) override { return 0; }
30+
31+
void flush() override {}
32+
size_t getSize() override { return 0; }
33+
void setSize(size_t size) override {}
34+
35+
public:
36+
NullFile() : DataFile(S_IRUGO | S_IWUGO, NullBackend, S_IFCHR) {}
37+
};
38+
2039
class StdinFile : public DataFile {
2140
void open(oflags_t) override {}
2241
void close() override {}
@@ -141,6 +160,11 @@ class RandomFile : public DataFile {
141160

142161
} // anonymous namespace
143162

163+
std::shared_ptr<DataFile> getNull() {
164+
static auto null = std::make_shared<NullFile>();
165+
return null;
166+
}
167+
144168
std::shared_ptr<DataFile> getStdin() {
145169
static auto stdin = std::make_shared<StdinFile>();
146170
return stdin;

system/lib/wasmfs/special_files.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
namespace wasmfs::SpecialFiles {
1111

12-
// /dev/stdin/
12+
// /dev/null
13+
std::shared_ptr<DataFile> getNull();
14+
15+
// /dev/stdin
1316
std::shared_ptr<DataFile> getStdin();
1417

1518
// /dev/stdout

system/lib/wasmfs/syscalls.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ int __syscall_fstatat64(int dirfd, intptr_t path, intptr_t buf, int flags) {
347347
buffer->st_uid = 0;
348348
buffer->st_gid = 0;
349349
// Device ID (if special file) No meaning right now for Emscripten.
350-
buffer->st_rdev = 1;
350+
buffer->st_rdev = 0;
351351
// The syscall docs state this is hardcoded to # of 512 byte blocks.
352352
buffer->st_blocks = (buffer->st_size + 511) / 512;
353353
// Specifies the preferred blocksize for efficient disk I/O.
@@ -438,6 +438,7 @@ static __wasi_fd_t doOpen(path::ParsedParent parsed,
438438
if (returnMode == OpenReturnMode::Nothing) {
439439
return 0;
440440
}
441+
441442
auto openFile = std::make_shared<OpenFileState>(0, flags, created);
442443
return wasmFS.getFileTable().locked().addEntry(openFile);
443444
}
@@ -562,13 +563,6 @@ doMkdir(path::ParsedParent parsed, int mode, backend_t backend = NullBackend) {
562563

563564
// TODO: Check that the insertion is successful.
564565

565-
// Update the times.
566-
auto lockedFile = created->locked();
567-
time_t now = time(NULL);
568-
lockedFile.setATime(now);
569-
lockedFile.setMTime(now);
570-
lockedFile.setCTime(now);
571-
572566
return 0;
573567
}
574568

system/lib/wasmfs/wasmfs.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ std::shared_ptr<Directory> WasmFS::initRootDirectory() {
7979
assert(devDir);
8080
{
8181
auto lockedDev = devDir->locked();
82+
lockedDev.mountChild("null", SpecialFiles::getNull());
8283
lockedDev.mountChild("stdin", SpecialFiles::getStdin());
8384
lockedDev.mountChild("stdout", SpecialFiles::getStdout());
8485
lockedDev.mountChild("stderr", SpecialFiles::getStderr());

tests/stat/test_stat.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,20 @@ void test() {
6666
assert(s.st_ino);
6767
assert(S_ISDIR(s.st_mode));
6868
assert(s.st_nlink);
69+
#ifndef WASMFS
6970
assert(s.st_rdev == 0);
71+
#endif
7072
assert(s.st_size);
7173
assert(s.st_atime == 1200000000);
7274
assert(s.st_mtime == 1200000000);
7375
assert(s.st_ctime);
7476
#ifdef __EMSCRIPTEN__
7577
assert(s.st_blksize == 4096);
78+
#ifdef WASMFS
79+
assert(s.st_blocks == 8);
80+
#else
7681
assert(s.st_blocks == 1);
82+
#endif
7783
#endif
7884

7985
// stat a file
@@ -121,8 +127,11 @@ void test() {
121127
assert(S_ISCHR(s.st_mode));
122128
assert(s.st_nlink);
123129
#ifndef __APPLE__
130+
#ifndef WASMFS
124131
// mac uses makedev(3, 2) for /dev/null
132+
// WasmFS doesn't report a meaningful st_rdev.
125133
assert(s.st_rdev == makedev(1, 3));
134+
#endif
126135
#endif
127136
assert(!s.st_size);
128137
assert(s.st_atime);
@@ -133,6 +142,8 @@ void test() {
133142
assert(s.st_blocks == 0);
134143
#endif
135144

145+
// WasmFS does not follow symlinks yet.
146+
#ifndef WASMFS
136147
// stat a link (should match the file stat from above)
137148
memset(&s, 0, sizeof(s));
138149
err = stat("folder/file-link", &s);
@@ -149,6 +160,7 @@ void test() {
149160
#ifdef __EMSCRIPTEN__
150161
assert(s.st_blksize == 4096);
151162
assert(s.st_blocks == 1);
163+
#endif
152164
#endif
153165

154166
// lstat a link (should NOT match the file stat from above)

tests/wasmfs/wasmfs_getdents.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ d.d_name = ..
2424
d.d_reclen = 280
2525
d.d_type = directory
2626

27+
d.d_name = null
28+
d.d_reclen = 280
29+
d.d_type = regular
30+
2731
d.d_name = random
2832
d.d_reclen = 280
2933
d.d_type = regular

tests/wasmfs/wasmfs_stat.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ int main() {
3838
assert(file.st_nlink);
3939
assert(file.st_uid == 0);
4040
assert(file.st_gid == 0);
41-
assert(file.st_rdev);
41+
#ifdef WASMFS
42+
assert(file.st_rdev == 0);
43+
#endif
4244
assert(file.st_blocks == 0);
4345
assert(file.st_blksize == 4096);
4446

@@ -67,12 +69,12 @@ int main() {
6769
assert(directory.st_nlink);
6870
assert(directory.st_uid == 0);
6971
assert(directory.st_gid == 0);
72+
assert(directory.st_rdev == 0);
7073
#ifdef WASMFS
71-
assert(directory.st_rdev);
7274
// blkcnt_t st_blocks; /* Number of 512B blocks allocated */
7375
assert(directory.st_blocks == 8);
7476
#else
75-
assert(!directory.st_rdev);
77+
assert(directory.st_rdev == 0);
7678
// The JS file system calculates st_blocks using Math.ceil(attr.size /
7779
// attr.blksize);
7880
assert(directory.st_blocks == 1);
@@ -100,7 +102,9 @@ int main() {
100102
assert(statFile.st_nlink);
101103
assert(statFile.st_uid == 0);
102104
assert(statFile.st_gid == 0);
103-
assert(statFile.st_rdev);
105+
#ifdef WASMFS
106+
assert(statFile.st_rdev == 0);
107+
#endif
104108
assert(statFile.st_blocks == 0);
105109
assert(statFile.st_blksize == 4096);
106110

@@ -114,12 +118,11 @@ int main() {
114118
assert(statDirectory.st_nlink);
115119
assert(statDirectory.st_uid == 0);
116120
assert(statDirectory.st_gid == 0);
121+
assert(statDirectory.st_rdev == 0);
117122
#ifdef WASMFS
118-
assert(statDirectory.st_rdev);
119123
// blkcnt_t st_blocks; /* Number of 512B blocks allocated */
120124
assert(statDirectory.st_blocks == 8);
121125
#else
122-
assert(!statDirectory.st_rdev);
123126
assert(statDirectory.st_blocks == 1);
124127
#endif
125128
assert(statDirectory.st_blksize == 4096);
@@ -150,16 +153,15 @@ int main() {
150153
assert(lstatFile.st_uid == 0);
151154
assert(lstatFile.st_gid == 0);
152155
assert(lstatFile.st_blksize == 4096);
156+
assert(lstatFile.st_rdev == 0);
153157
#ifdef WASMFS
154158
assert(lstatFile.st_size == 0);
155159
assert(lstatFile.st_blocks == 0);
156-
assert(lstatFile.st_rdev);
157160
#else
158161
// dev/stdout is a symlink to dev/tty.
159162
// TODO: When symlinks are added, one should return stat info on the symlink.
160163
assert(lstatFile.st_size == 8);
161164
assert(lstatFile.st_blocks == 1);
162-
assert(!lstatFile.st_rdev);
163165
#endif
164166

165167
// Test calling lstat without opening a directory.
@@ -172,12 +174,11 @@ int main() {
172174
assert(lstatDirectory.st_nlink);
173175
assert(lstatDirectory.st_uid == 0);
174176
assert(lstatDirectory.st_gid == 0);
177+
assert(lstatDirectory.st_rdev == 0);
175178
#ifdef WASMFS
176-
assert(lstatDirectory.st_rdev);
177179
// blkcnt_t st_blocks; /* Number of 512B blocks allocated */
178180
assert(lstatDirectory.st_blocks == 8);
179181
#else
180-
assert(!lstatDirectory.st_rdev);
181182
assert(lstatDirectory.st_blocks == 1);
182183
#endif
183184
assert(lstatDirectory.st_blksize == 4096);

0 commit comments

Comments
 (0)