Skip to content

Commit 24a1a98

Browse files
committed
Merge branch 'main' into symlink-path-resolution
2 parents 0f2713d + 3dff04c commit 24a1a98

File tree

164 files changed

+696
-834
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

164 files changed

+696
-834
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ See docs/process.md for more on how version tagging works.
2020

2121
3.1.74 (in development)
2222
-----------------------
23+
- The file system was updated to independently track atime, mtime and ctime
24+
instead of using the same time for all three. (#22998)
25+
- The minimum supported chrome version was bumped from 32 to 33. (#23077)
2326

2427
3.1.73 - 11/28/24
2528
-----------------
2629
- libunwind was updated to LLVM 19.1.4. (#22394)
30+
- mimalloc was updated to 2.1.7. (#21548)
2731

2832
3.1.72 - 11/19/24
2933
-----------------

site/source/docs/tools_reference/settings_reference.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2939,7 +2939,7 @@ This setting also applies to modern Chromium-based Edge, which shares version
29392939
numbers with Chrome.
29402940
Chrome 85 was released on 2020-08-25.
29412941
MAX_INT (0x7FFFFFFF, or -1) specifies that target is not supported.
2942-
Minimum supported value is 32, which was released on 2014-01-04.
2942+
Minimum supported value is 33, which was released on 2014-02-18.
29432943

29442944
Default value: 85
29452945

src/closure-externs/node-externs.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,15 @@ fs.Stats.prototype.mtimeMs;
119119
* @type {number}
120120
*/
121121
fs.Stats.prototype.ctimeMs;
122+
123+
/**
124+
* @param {string} p
125+
* @return {boolean}
126+
* @nosideeffects
127+
*/
128+
path.isAbsolute;
129+
130+
/**
131+
* @type {Object.<string,*>}
132+
*/
133+
path.posix;

src/library_async.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,9 +520,7 @@ addToLibrary({
520520
var imports = {'primary': wasmExports};
521521
// Replace '.wasm' suffix with '.deferred.wasm'.
522522
var deferred = wasmBinaryFile.slice(0, -5) + '.deferred.wasm';
523-
await new Promise((resolve) => {
524-
instantiateAsync(null, deferred, imports, resolve);
525-
});
523+
await instantiateAsync(null, deferred, imports);
526524
},
527525

528526
$Fibers__deps: ['$Asyncify', 'emscripten_stack_set_limits', '$stackRestore'],

src/library_fs.js

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ FS.staticInit();
146146
this.name = name;
147147
this.mode = mode;
148148
this.rdev = rdev;
149+
this.atime = this.mtime = this.ctime = Date.now();
149150
}
150151
get read() {
151152
return (this.mode & this.readMode) === this.readMode;
@@ -200,8 +201,18 @@ FS.staticInit();
200201
continue;
201202
}
202203

203-
current = FS.lookupNode(current, parts[i]);
204204
current_path = PATH.join2(current_path, parts[i]);
205+
try {
206+
current = FS.lookupNode(current, parts[i]);
207+
} catch (e) {
208+
// if noent_okay is true, suppress a ENOENT in the last component
209+
// and return an object with an undefined node. This is needed for
210+
// resolving symlinks in the path when creating a file.
211+
if ((e?.errno === {{{ cDefs.ENOENT }}}) && islast && opts.noent_okay) {
212+
return { path: current_path };
213+
}
214+
throw e;
215+
}
205216

206217
// jump to the mount's root node if this is a mountpoint
207218
if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) {
@@ -954,7 +965,7 @@ FS.staticInit();
954965
}
955966
node.node_ops.setattr(node, {
956967
mode: (mode & {{{ cDefs.S_IALLUGO }}}) | (node.mode & ~{{{ cDefs.S_IALLUGO }}}),
957-
timestamp: Date.now()
968+
ctime: Date.now()
958969
});
959970
},
960971
lchmod(path, mode) {
@@ -1027,7 +1038,8 @@ FS.staticInit();
10271038
var lookup = FS.lookupPath(path, { follow: true });
10281039
var node = lookup.node;
10291040
node.node_ops.setattr(node, {
1030-
timestamp: Math.max(atime, mtime)
1041+
atime: atime,
1042+
mtime: mtime
10311043
});
10321044
},
10331045
open(path, flags, mode = 0o666) {
@@ -1044,14 +1056,15 @@ FS.staticInit();
10441056
if (typeof path == 'object') {
10451057
node = path;
10461058
} else {
1047-
try {
1048-
var lookup = FS.lookupPath(path, {
1049-
follow: !(flags & {{{ cDefs.O_NOFOLLOW }}})
1050-
});
1051-
node = lookup.node;
1052-
} catch (e) {
1053-
// ignore
1054-
}
1059+
// noent_okay makes it so that if the final component of the path
1060+
// doesn't exist, lookupPath returns `node: undefined`. `path` will be
1061+
// updated to point to the target of all symlinks.
1062+
var lookup = FS.lookupPath(path, {
1063+
follow: !(flags & {{{ cDefs.O_NOFOLLOW }}}),
1064+
noent_okay: true
1065+
});
1066+
node = lookup.node;
1067+
path = lookup.path;
10551068
}
10561069
// perhaps we need to create the node
10571070
var created = false;
@@ -1401,6 +1414,9 @@ FS.staticInit();
14011414
FS.mount({
14021415
mount() {
14031416
var node = FS.createNode(proc_self, 'fd', {{{ cDefs.S_IFDIR | 0o777 }}}, {{{ cDefs.S_IXUGO }}});
1417+
node.stream_ops = {
1418+
llseek: MEMFS.stream_ops.llseek,
1419+
};
14041420
node.node_ops = {
14051421
lookup(parent, name) {
14061422
var fd = +name;
@@ -1409,9 +1425,15 @@ FS.staticInit();
14091425
parent: null,
14101426
mount: { mountpoint: 'fake' },
14111427
node_ops: { readlink: () => stream.path },
1428+
id: fd + 1,
14121429
};
14131430
ret.parent = ret; // make it look like a simple root node
14141431
return ret;
1432+
},
1433+
readdir() {
1434+
return Array.from(FS.streams.entries())
1435+
.filter(([k, v]) => v)
1436+
.map(([k, v]) => k.toString());
14151437
}
14161438
};
14171439
return node;
@@ -1628,7 +1650,7 @@ FS.staticInit();
16281650
buffer[offset+i] = result;
16291651
}
16301652
if (bytesRead) {
1631-
stream.node.timestamp = Date.now();
1653+
stream.node.atime = Date.now();
16321654
}
16331655
return bytesRead;
16341656
},
@@ -1641,7 +1663,7 @@ FS.staticInit();
16411663
}
16421664
}
16431665
if (length) {
1644-
stream.node.timestamp = Date.now();
1666+
stream.node.mtime = stream.node.ctime = Date.now();
16451667
}
16461668
return i;
16471669
}

src/library_lz4.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ addToLibrary({
7070
node.mode = mode;
7171
node.node_ops = LZ4.node_ops;
7272
node.stream_ops = LZ4.stream_ops;
73-
node.timestamp = (mtime || new Date).getTime();
73+
this.atime = this.mtime = this.ctime = (mtime || new Date).getTime();
7474
assert(LZ4.FILE_MODE !== LZ4.DIR_MODE);
7575
if (mode === LZ4.FILE_MODE) {
7676
node.size = contents.end - contents.start;
@@ -95,19 +95,18 @@ addToLibrary({
9595
gid: 0,
9696
rdev: 0,
9797
size: node.size,
98-
atime: new Date(node.timestamp),
99-
mtime: new Date(node.timestamp),
100-
ctime: new Date(node.timestamp),
98+
atime: new Date(node.atime),
99+
mtime: new Date(node.mtime),
100+
ctime: new Date(node.ctime),
101101
blksize: 4096,
102102
blocks: Math.ceil(node.size / 4096),
103103
};
104104
},
105105
setattr(node, attr) {
106-
if (attr.mode !== undefined) {
107-
node.mode = attr.mode;
108-
}
109-
if (attr.timestamp !== undefined) {
110-
node.timestamp = attr.timestamp;
106+
for (const key of ['mode', 'atime', 'mtime', 'ctime']) {
107+
if (attr[key]) {
108+
node[key] = attr[key];
109+
}
111110
}
112111
},
113112
lookup(parent, name) {

src/library_memfs.js

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ addToLibrary({
9292
node.node_ops = MEMFS.ops_table.chrdev.node;
9393
node.stream_ops = MEMFS.ops_table.chrdev.stream;
9494
}
95-
node.timestamp = Date.now();
95+
node.atime = node.mtime = node.ctime = Date.now();
9696
// add the new node to the parent
9797
if (parent) {
9898
parent.contents[name] = node;
99-
parent.timestamp = node.timestamp;
99+
parent.atime = parent.mtime = parent.ctime = node.atime;
100100
}
101101
return node;
102102
},
@@ -161,21 +161,20 @@ addToLibrary({
161161
} else {
162162
attr.size = 0;
163163
}
164-
attr.atime = new Date(node.timestamp);
165-
attr.mtime = new Date(node.timestamp);
166-
attr.ctime = new Date(node.timestamp);
164+
attr.atime = new Date(node.atime);
165+
attr.mtime = new Date(node.mtime);
166+
attr.ctime = new Date(node.ctime);
167167
// NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize),
168168
// but this is not required by the standard.
169169
attr.blksize = 4096;
170170
attr.blocks = Math.ceil(attr.size / attr.blksize);
171171
return attr;
172172
},
173173
setattr(node, attr) {
174-
if (attr.mode !== undefined) {
175-
node.mode = attr.mode;
176-
}
177-
if (attr.timestamp !== undefined) {
178-
node.timestamp = attr.timestamp;
174+
for (const key of ["mode", "atime", "mtime", "ctime"]) {
175+
if (attr[key]) {
176+
node[key] = attr[key];
177+
}
179178
}
180179
if (attr.size !== undefined) {
181180
MEMFS.resizeFileStorage(node, attr.size);
@@ -192,44 +191,39 @@ addToLibrary({
192191
return MEMFS.createNode(parent, name, mode, dev);
193192
},
194193
rename(old_node, new_dir, new_name) {
195-
// if we're overwriting a directory at new_name, make sure it's empty.
196-
if (FS.isDir(old_node.mode)) {
197-
var new_node;
198-
try {
199-
new_node = FS.lookupNode(new_dir, new_name);
200-
} catch (e) {
201-
}
202-
if (new_node) {
194+
var new_node;
195+
try {
196+
new_node = FS.lookupNode(new_dir, new_name);
197+
} catch (e) {}
198+
if (new_node) {
199+
if (FS.isDir(old_node.mode)) {
200+
// if we're overwriting a directory at new_name, make sure it's empty.
203201
for (var i in new_node.contents) {
204202
throw new FS.ErrnoError({{{ cDefs.ENOTEMPTY }}});
205203
}
206204
}
205+
FS.hashRemoveNode(new_node);
207206
}
208207
// do the internal rewiring
209208
delete old_node.parent.contents[old_node.name];
210-
old_node.parent.timestamp = Date.now()
211-
old_node.name = new_name;
212209
new_dir.contents[new_name] = old_node;
213-
new_dir.timestamp = old_node.parent.timestamp;
210+
old_node.name = new_name;
211+
new_dir.ctime = new_dir.mtime = old_node.parent.ctime = old_node.parent.mtime = Date.now();
214212
},
215213
unlink(parent, name) {
216214
delete parent.contents[name];
217-
parent.timestamp = Date.now();
215+
parent.ctime = parent.mtime = Date.now();
218216
},
219217
rmdir(parent, name) {
220218
var node = FS.lookupNode(parent, name);
221219
for (var i in node.contents) {
222220
throw new FS.ErrnoError({{{ cDefs.ENOTEMPTY }}});
223221
}
224222
delete parent.contents[name];
225-
parent.timestamp = Date.now();
223+
parent.ctime = parent.mtime = Date.now();
226224
},
227225
readdir(node) {
228-
var entries = ['.', '..'];
229-
for (var key of Object.keys(node.contents)) {
230-
entries.push(key);
231-
}
232-
return entries;
226+
return ['.', '..', ...Object.keys(node.contents)];
233227
},
234228
symlink(parent, newname, oldpath) {
235229
var node = MEMFS.createNode(parent, newname, 0o777 | {{{ cDefs.S_IFLNK }}}, 0);
@@ -282,7 +276,7 @@ addToLibrary({
282276

283277
if (!length) return 0;
284278
var node = stream.node;
285-
node.timestamp = Date.now();
279+
node.mtime = node.ctime = Date.now();
286280

287281
if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array?
288282
if (canOwn) {

src/library_nodefs.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ addToLibrary({
166166
// update the common node structure mode as well
167167
node.mode = attr.mode;
168168
}
169-
if (attr.timestamp !== undefined) {
170-
var date = new Date(attr.timestamp);
171-
fs.utimesSync(path, date, date);
169+
if (attr.atime || attr.mtime) {
170+
var atime = attr.atime && new Date(attr.atime);
171+
var mtime = attr.mtime && new Date(attr.mtime);
172+
fs.utimesSync(path, atime, mtime);
172173
}
173174
if (attr.size !== undefined) {
174175
fs.truncateSync(path, attr.size);
@@ -196,6 +197,9 @@ addToLibrary({
196197
rename(oldNode, newDir, newName) {
197198
var oldPath = NODEFS.realPath(oldNode);
198199
var newPath = PATH.join2(NODEFS.realPath(newDir), newName);
200+
try {
201+
FS.unlink(newPath);
202+
} catch(e) {}
199203
NODEFS.tryFSOperation(() => fs.renameSync(oldPath, newPath));
200204
oldNode.name = newName;
201205
},

src/library_nodepath.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@
1212
// operations. Hence, using `nodePath` should be safe here.
1313

1414
addToLibrary({
15-
$PATH: {
16-
isAbs: (path) => nodePath['isAbsolute'](path),
17-
normalize: (path) => nodePath['normalize'](path),
18-
dirname: (path) => nodePath['dirname'](path),
19-
basename: (path) => nodePath['basename'](path),
20-
join: (...args) => nodePath['join'](...args),
21-
join2: (l, r) => nodePath['join'](l, r),
22-
},
15+
$PATH: `{
16+
isAbs: nodePath.isAbsolute,
17+
normalize: nodePath.normalize,
18+
dirname: nodePath.dirname,
19+
basename: nodePath.basename,
20+
join: nodePath.join,
21+
join2: nodePath.join,
22+
}`,
2323
// The FS-using parts are split out into a separate object, so simple path
2424
// usage does not require the FS.
2525
$PATH_FS__deps: ['$FS'],
2626
$PATH_FS__docs: '/** @type{{resolve: function(...*)}} */',
2727
$PATH_FS: {
2828
resolve: (...paths) => {
2929
paths.unshift(FS.cwd());
30-
return nodePath['posix']['resolve'](...paths);
30+
return nodePath.posix.resolve(...paths);
3131
},
32-
relative: (from, to) => nodePath['posix']['relative'](from || FS.cwd(), to || FS.cwd()),
32+
relative: (from, to) => nodePath.posix.relative(from || FS.cwd(), to || FS.cwd()),
3333
}
3434
});

src/library_noderawfs.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ addToLibrary({
4040
},
4141
lookupPath(path, opts = {}) {
4242
if (opts.parent) {
43-
path = nodePath.dirname(path);
43+
path = PATH.dirname(path);
4444
}
4545
var st = fs.lstatSync(path);
4646
var mode = NODEFS.getMode(path);
@@ -112,13 +112,13 @@ addToLibrary({
112112
fs.ftruncateSync(stream.nfd, len);
113113
},
114114
utime(path, atime, mtime) {
115-
// -1 here for atime or mtime means UTIME_OMIT was passed. Since node
115+
// null here for atime or mtime means UTIME_OMIT was passed. Since node
116116
// doesn't support this concept we need to first find the existing
117117
// timestamps in order to preserve them.
118-
if (atime == -1 || mtime == -1) {
118+
if ((atime === null) || (mtime === null)) {
119119
var st = fs.statSync(path);
120-
if (atime == -1) atime = st.atimeMs;
121-
if (mtime == -1) mtime = st.mtimeMs;
120+
atime ||= st.atimeMs;
121+
mtime ||= st.mtimeMs;
122122
}
123123
fs.utimesSync(path, atime/1000, mtime/1000);
124124
},

0 commit comments

Comments
 (0)