Skip to content

Fix case insensitive upload (#15245)#26369

Draft
AlexWalsh2 wants to merge 20 commits intoemscripten-core:mainfrom
AlexWalsh2:fix_case_insensitive_upload
Draft

Fix case insensitive upload (#15245)#26369
AlexWalsh2 wants to merge 20 commits intoemscripten-core:mainfrom
AlexWalsh2:fix_case_insensitive_upload

Conversation

@AlexWalsh2
Copy link

When files with the same case-insensitive filename are uploaded, the newest file overwrites the older one. Includes test for #15245, could fix #26317

@AlexWalsh2 AlexWalsh2 marked this pull request as draft February 28, 2026 23:11
@also_with_noderawfs
def test_fs_writev(self):
self.do_runf('fs/test_writev.c', 'success', cflags=['-sFORCE_FILESYSTEM'])
self.do_runf('fs/test_case_insensitive.c', 'success', cflags=['-sFORCE_FILESYSTEM', '-sCASE_INSENSITIVE_FS'])
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be separate test case (function)

Copy link
Author

Choose a reason for hiding this comment

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

I removed this test, other tests are in separate test cases

char buf1[] = "b=2\n";
struct iovec iov[] = {{.iov_base = buf0, .iov_len = 4},
{.iov_base = buf1, .iov_len = 4}};
ssize_t nwritten = pwritev(fd, iov, 2, 0);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Using pwritev here seem needless complicated. Can you just use fwrite with a single string constant?

struct iovec iov2[] = {{.iov_base = buf3, .iov_len = 4},
{.iov_base = buf4, .iov_len = 4}};
ssize_t nwritten2 = pwritev(fd2, iov2, 2, 0);
assert(nwritten2 == 8);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe write just 4 bytes (or some number that is not the same as the 8 above)?

close(fd);

printf("success\n");
return 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

How does this test case fail today (without the libfs patch)?

Copy link
Author

Choose a reason for hiding this comment

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

This test does not test what I thought it did, I have replaced it with two better tests

AlexWalsh2 and others added 9 commits March 4, 2026 11:01
Added a test that fails when preloaded files cause the JS to hang
Addressed space after if from code review in PR
Removed test_case_insensitive (does not test what I expected it to)

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
Adds test_insensitive_overwrite test to ensure, in case insensitive
filesystems, inaccessible prior versions of files are not left around
after overwrite

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
Add catch for throws coming out of file packager's
async FS_createDataFile
Add overwrite behavior to createDataFile instead of mknod so it can
throw an EEXISTS error up if the files have the exact same data
Add check for skipping duplicate node creation to the
test_insensitive_overwrite test

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
Fix for test_file_packager
Fix for test_insensitive_hang in instance and
esm_integration

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
Fix typos as indicated by clang-format in C and JS files

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
self.set_setting('DEFAULT_LIBRARY_FUNCS_TO_INCLUDE', ['$FS'])
self.set_setting('CASE_INSENSITIVE_FS', ['1'])
self.add_pre_run(read_file(test_file('fs/test_insensitive_overwrite.js')))
self.do_run_in_out_file_test('fs/test_insensitive_overwrite.c')
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think its probably fine to put these tests in test_other.py.

You can even put them alongside the existing test_fs_icase there

Copy link
Author

Choose a reason for hiding this comment

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

Should I set test_fs_icase to have the no_windows/no_mac requested for the other test?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Only if the tests do not pass on mac / windows.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you mark them as @crossplatform then CI will run them on mac and windows and you can see if they fail there,.

Copy link
Author

Choose a reason for hiding this comment

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

First thing, thank you for the suggestion regarding the crossplatform CI test - both tests seem to work fine on the CI's Mac and Windows.
Secondly, if I move the tests inside of test_fs_icase, then they get run with the experimental wasmfs, which causes test_overwrite_icase to fail (pre_run doesn't work the way I'm using it). Do we need to test it against the experimental filesystem?

// 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?

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.

AlexWalsh2 and others added 4 commits March 6, 2026 09:49
test_insensitive_hang renamed to test_crash_icase, moved to other, and
given windows-only and macos-only flags as they have/can have
case-insensitive filesystems by default
test_insensitive_overwrite renamed to test_overwrite_icase, and
moved to other
createDataFile now converts its data to its storage form
(char -> char code) before creating the FS node,
simplifying data comparison and avoiding duplicated efforts

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
Refactored overwrite test in test_fs_icase to remove the stub .c file
and the .out file, reducing it to a single .js file

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
Split test_overwrite_icase back out into its own test temporarily,
to more easily tell if it is crossplatform
Mark both new tests as crossplatform

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

So just the re-wind to the original problem.

Please correct me if my summary is wrong:

  1. FS.createDataFile does not currently allow the overwriting of files.
  2. If CASE_INSENSITIVE_FS used then FS.createDataFile(FOO.txt) followed by FS.createDataFile(foo.txt) will currently crash.
  3. You have a file packager bundle that contains such a cominbation of files that it cannot currently be loaded when CASE_INSENSITIVE_FS is set.

Is that right?

I wonder if we simply changed FS.createDataFile so that it always clobbers existing files.. would that be a bad breaking change for folks in the wild?

AlexWalsh2 and others added 2 commits March 9, 2026 16:09
Keep test_overwrite_icase outside of test_fs_icase for now since it
fails with wasmfs
Move test_crash_icase inside of test_fs_icase
Keep crossplatform flags on icase tests - they pass as of now

Signed-off-by: walshal <alexwalsh6x7@gmail.com>
@AlexWalsh2
Copy link
Author

Your summary looks correct to me, my apologies for any confusion that may be arising.

There are two issues (#15245 and #26327) in this PR, currently, should we split them out?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants