Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
83790a7
Overwrite same-name CASE_INSENSITIVE files (#26327)
AlexWalsh2 Feb 24, 2026
11c9609
Merge branch 'emscripten-core:main' into fix_case_insensitive_upload
AlexWalsh2 Feb 26, 2026
8303655
Merge branch 'emscripten-core:main' into fix_case_insensitive_upload
AlexWalsh2 Feb 28, 2026
2841fba
Add Case Insensitive Overwrite Test (#15245)
AlexWalsh2 Feb 28, 2026
69b327c
Add test code
AlexWalsh2 Feb 28, 2026
9a4f36f
Add test for #26327
AlexWalsh2 Mar 4, 2026
1d1e6f0
Write improved test for #15245
AlexWalsh2 Mar 4, 2026
519a05d
Files with identical contents no longer overwrite
AlexWalsh2 Mar 5, 2026
ab1dd58
Merge branch 'emscripten-core:main' into fix_case_insensitive_upload
AlexWalsh2 Mar 5, 2026
b95f524
Fix failing CI
AlexWalsh2 Mar 5, 2026
4ca1484
Merge branch 'emscripten-core:main' into fix_case_insensitive_upload
AlexWalsh2 Mar 5, 2026
c7ae52d
Merge branch 'fix_case_insensitive_upload' of github.com:AlexWalsh2/e…
AlexWalsh2 Mar 5, 2026
dbafd47
Fix typos
AlexWalsh2 Mar 6, 2026
b7a4d21
Merge branch 'emscripten-core:main' into fix_case_insensitive_upload
AlexWalsh2 Mar 6, 2026
97524da
Refactor tests and FS data comparison
AlexWalsh2 Mar 6, 2026
e95950f
Merge branch 'emscripten-core:main' into fix_case_insensitive_upload
AlexWalsh2 Mar 6, 2026
399e6a9
Refactor overwrite test
AlexWalsh2 Mar 6, 2026
f22817a
Test crossplatform of tests
AlexWalsh2 Mar 9, 2026
7a8f50d
Refactor tests
AlexWalsh2 Mar 9, 2026
5185579
Merge branch 'main' into fix_case_insensitive_upload
AlexWalsh2 Mar 9, 2026
bb86f51
Merge branch 'emscripten-core:main' into fix_case_insensitive_upload
AlexWalsh2 Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -602,3 +602,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Christian Lloyd <clloyd@teladochealth.com> (copyright owned by Teladoc Health, Inc.)
* Sean Morris <sean@seanmorr.is>
* Mitchell Wills <mwills@google.com> (copyright owned by Google, Inc.)
* Alex Walsh (alexwalsh6x7@gmail.com>
29 changes: 28 additions & 1 deletion src/lib/libfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1616,9 +1616,36 @@ FS.staticInit();`;
path = name ? PATH.join2(parent, name) : parent;
}
var mode = FS_getMode(canRead, canWrite);
var node = FS.create(path, mode);
if (data) {
data = FS_fileDataToTypedArray(data);
}
#if CASE_INSENSITIVE_FS
try {
#endif
var node = FS.create(path, mode);
#if CASE_INSENSITIVE_FS
} catch (e) {
if (e.errno === {{{ cDefs.EEXIST }}}) {
var oldNodeLookup = FS.lookupPath(path);
var oldNode = oldNodeLookup.node;
if (data.length === oldNode.contents.length) {
var isDup = true;
for (var i = 0; i < data.length; i++) {
if (data[i] !== oldNode.contents[i]) {
isDup = false;
break;
}
}
if (isDup) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it still and error when the contents are the same?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can swallow the error here, although then the caller would not know of the duplicate call

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what you mean by "the duplicate call"?

With this change are we not saying that its OK to upload with FOO.txt and foo.txt without getting an error. Why would it matter if the contents of the files match?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "duplicate call", I mean the second call that is trying to do the same operation (same name same data - createDataFile("FOO.txt, "foo") when we already have "foo.txt" with "foo" inside). My apologies for any lack of clarity on my part.
I am uncertain if it matters - the idea with the current structure is that we basically pass the question up to the calling function via the throw of EEXIST so it can decide what to do in that case of a "duplicate call". Is this worth throwing up an exception or should the function do something else (say, skip to being done or not check and just overwrite)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why would you want to silently ignore the error when the contents are different but hide the error when the contents are the same?

case1:

createDataFile("FOO.txt, "foo")
createDataFile("foo.txt, "foo")

case2:

createDataFile("FOO.txt, "foo")
createDataFile("foo.txt, "bar")

It seems like you are saying only one of these should be an error?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made that change based on this review of the previous code. That code handled both case1 and case2 the same (silently ignore/overwrite), is that preferable behavior? This seems to me like an architectural decision - I can implement whatever you want, or pick something on my own.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really not sure what the desired behaviour is based on #15245.

throw e;
}
}
FS.destroyNode(oldNode);
node = FS.create(path, mode);
}
}
#endif
if (data) {
// make sure we can write to the file
FS.chmod(node, mode | {{{ cDefs.S_IWUGO }}});
var stream = FS.open(node, {{{ cDefs.O_TRUNC | cDefs.O_CREAT | cDefs.O_WRONLY }}});
Expand Down
39 changes: 39 additions & 0 deletions test/other/test_crash_icase.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2026 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/

#include <assert.h>
#include <stdio.h>

#include <emscripten.h>

void loadScript() {
printf("load2");
FILE* file = fopen("file1.txt", "r");

if (!file) {
assert(false);
}

while (!feof(file)) {
char c = fgetc(file);
if (c != EOF) {
putchar(c);
}
}
fclose(file);
exit(0);
}

void scriptLoadFail() {
printf("failed to load data_files.js\n");
assert(false);
}

int main() {
emscripten_async_load_script("data_files.js", loadScript, scriptLoadFail);
return 99;
}
13 changes: 13 additions & 0 deletions test/other/test_overwrite_icase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FS.createDataFile('/', "file.txt", "foo");
FS.createDataFile('/', "fILe.txt", "foo2");
var fileContents = FS.readFile("/file.txt");
out('file.txt: ' + fileContents);
var ret = FS.analyzePath('/file.txt');
out('file.txt collison: ' + ret.object.name_next);
var errCode = 0;
try {
FS.createDataFile('/', "FIlE.txt", "foo2");
} catch (e) {
errCode = e.errno;
}
out('errorCode: ' + errCode);
15 changes: 15 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -13813,9 +13813,24 @@ def test_recursive_cache_lock(self):
self.assertContained('AssertionError: attempt to lock the cache while a parent process is holding the lock', err)

@also_with_wasmfs
@crossplatform
def test_fs_icase(self):
# c++20 for ends_with().
self.do_other_test('test_fs_icase.cpp', cflags=['-sCASE_INSENSITIVE_FS', '-std=c++20'])
create_file('file1.txt', 'one')
create_file('fILe1.txt', 'two')
# `--from-emcc` needed here otherwise the output defines `var Module =` which will shadow the
# global `Module`.
self.run_process([FILE_PACKAGER, 'test.data', '--preload', 'file1.txt', 'fILe1.txt', '--from-emcc', '--js-output=data_files.js'])
self.do_runf('other/test_crash_icase.c', cflags=['-sFORCE_FILESYSTEM', '-sCASE_INSENSITIVE_FS'])

@crossplatform
def test_overwrite_icase(self):
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$FS'])
self.set_setting('INCLUDE_FULL_LIBRARY', 1)
self.set_setting('CASE_INSENSITIVE_FS', 1)
self.add_pre_run(read_file(test_file('other/test_overwrite_icase.js')))
self.do_runf('hello_world.c', 'file.txt: 102,111,111,50\nfile.txt collison: undefined\nerrorCode: 20')

@crossplatform
@with_all_fs
Expand Down
8 changes: 6 additions & 2 deletions tools/file_packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,8 +683,12 @@ def generate_preload_js(data_target, data_files, metadata):
} catch (e) {
err(`Preloading file ${name} failed`, e);
}\n'''
create_data = '''// canOwn this data in the filesystem, it is a slice into the heap that will never change
Module['FS_createDataFile'](name, null, data, true, true, true);
create_data = '''try {
// canOwn this data in the filesystem, it is a slice into the heap that will never change
Module['FS_createDataFile'](name, null, data, true, true, true);
} catch(e) {
err(`Preloading file ${name} failed`, e);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some reason why its important to swallow this error here? Is it needed for this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a case (see #26327) where this error is not caught, causing the old code to hang. Is there a better place to catch it?

}
Module['removeRunDependency'](`fp ${name}`);'''

finish_handler = create_preloaded if options.use_preload_plugins else create_data
Expand Down