[FS] Fix open() ignoring trailing slash on regular files Fixes: #26710
diff --git a/src/lib/libfs.js b/src/lib/libfs.js index 08656c3..1b958df 100644 --- a/src/lib/libfs.js +++ b/src/lib/libfs.js
@@ -179,6 +179,8 @@ path = FS.cwd() + '/' + path; } + var hasTrailingSlash = path.endsWith('/'); + // limit max consecutive symlinks to SYMLOOP_MAX. linkloop: for (var nlinks = 0; nlinks < {{{ cDefs.SYMLOOP_MAX }}}; nlinks++) { // split the absolute path @@ -196,10 +198,16 @@ } if (parts[i] === '.') { + if (!FS.isDir(current.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); + } continue; } if (parts[i] === '..') { + if (!FS.isDir(current.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); + } current_path = PATH.dirname(current_path); if (FS.isRoot(current)) { path = current_path + '/' + parts.slice(i + 1).join('/'); @@ -232,8 +240,9 @@ } // by default, lookupPath will not follow a symlink if it is the final path component. - // setting opts.follow = true will override this behavior. - if (FS.isLink(current.mode) && (!islast || opts.follow)) { + // setting opts.follow = true or having a trailing slash will override this behavior + // (POSIX requires that a trailing slash forces following of symbolic links). + if (FS.isLink(current.mode) && (!islast || opts.follow || hasTrailingSlash)) { if (!current.node_ops.readlink) { throw new FS.ErrnoError({{{ cDefs.ENOSYS }}}); } @@ -241,10 +250,16 @@ if (!PATH.isAbs(link)) { link = PATH.dirname(current_path) + '/' + link; } - path = link + '/' + parts.slice(i + 1).join('/'); + var suffix = parts.slice(i + 1).join('/'); + path = link + (suffix ? '/' + suffix : ''); continue linkloop; } } + // POSIX requires that a pathname with a trailing slash must refer to a + // directory. + if (hasTrailingSlash && !FS.isDir(current.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); + } return { path: current_path, node: current }; } throw new FS.ErrnoError({{{ cDefs.ELOOP }}});
diff --git a/system/lib/wasmfs/js_api.cpp b/system/lib/wasmfs/js_api.cpp index 5330b66..11943af 100644 --- a/system/lib/wasmfs/js_api.cpp +++ b/system/lib/wasmfs/js_api.cpp
@@ -69,7 +69,7 @@ if (parsedParent.getError()) { return 0; } - auto& [parent, childNameView] = parsedParent.getParentChild(); + auto& [parent, childNameView, hasTrailingSlash] = parsedParent.getParentChild(); std::string childName(childNameView); std::shared_ptr<File> child; @@ -77,6 +77,11 @@ auto lockedParent = parent->locked(); child = lockedParent.getChild(childName); if (!child) { + // POSIX requires that a pathname with a trailing slash must refer to a + // directory. If it doesn't exist, we can't create it as a directory here. + if (hasTrailingSlash) { + return 0; + } // Lookup failed; try creating the file. child = lockedParent.insertDataFile(childName, 0777); if (!child) { @@ -86,6 +91,12 @@ } } + // POSIX requires that a pathname with a trailing slash must refer to a + // directory. + if (hasTrailingSlash && !child->is<Directory>()) { + return 0; + } + auto dataFile = child->dynCast<DataFile>(); if (!dataFile) { // There is something here but it isn't a data file.
diff --git a/system/lib/wasmfs/paths.cpp b/system/lib/wasmfs/paths.cpp index 3d1fbf4..b7b9a28 100644 --- a/system/lib/wasmfs/paths.cpp +++ b/system/lib/wasmfs/paths.cpp
@@ -75,16 +75,18 @@ path.remove_prefix(1); } + bool hasTrailingSlash = false; // Ignore trailing '/'. while (!path.empty() && path.back() == '/') { path.remove_suffix(1); + hasTrailingSlash = true; } // An empty path here means that the path was equivalent to "/" and does not // contain a child segment for us to return. The root is its own parent, so we // can handle this by returning (root, "."). if (path.empty()) { - return {std::make_pair(std::move(curr), std::string_view("."))}; + return {std::move(curr), ".", hasTrailingSlash}; } while (true) { @@ -96,7 +98,7 @@ // If this is the leaf segment, return. size_t segment_end = path.find_first_of('/'); if (segment_end == std::string_view::npos) { - return {std::make_pair(std::move(curr), path)}; + return {std::move(curr), path, hasTrailingSlash}; } // Try to descend into the child segment. @@ -122,8 +124,25 @@ if (auto err = parsed.getError()) { return {err}; } - auto& [parent, child] = parsed.getParentChild(); - return getChild(parent, child, links, recursions); + auto& [parent, child, hasTrailingSlash] = parsed.getParentChild(); + + // POSIX requires that a trailing slash forces following of symbolic links. + if (hasTrailingSlash) { + links = FollowLinks; + } + + auto file = getChild(parent, child, links, recursions); + if (auto err = file.getError()) { + return err; + } + + // POSIX requires that a pathname with a trailing slash must refer to a + // directory. + if (hasTrailingSlash && file.getFile()->kind != File::DirectoryKind) { + return -ENOTDIR; + } + + return file; } } // anonymous namespace
diff --git a/system/lib/wasmfs/paths.h b/system/lib/wasmfs/paths.h index 353c55a..aae3f31 100644 --- a/system/lib/wasmfs/paths.h +++ b/system/lib/wasmfs/paths.h
@@ -21,7 +21,11 @@ // The parent directory and the name of an entry within it. The returned string // view is either backed by the same memory as the view passed to `parseParent` // or is a view into a static string. -using ParentChild = std::pair<std::shared_ptr<Directory>, std::string_view>; +struct ParentChild { + std::shared_ptr<Directory> parent; + std::string_view child; + bool hasTrailingSlash; +}; // If the path refers to a link, whether we should follow that link. Links among // the parent directories in the path are always followed. @@ -33,9 +37,13 @@ public: ParsedParent(Error err) : val(err) {} - ParsedParent(ParentChild pair) : val(pair) {} + ParsedParent(ParentChild pc) : val(pc) {} + ParsedParent(std::shared_ptr<Directory> parent, + std::string_view child, + bool hasTrailingSlash) + : val(ParentChild{parent, child, hasTrailingSlash}) {} // Always ok to call, returns 0 if there is no error. - long getError() { + long getError() const { if (auto* err = std::get_if<Error>(&val)) { assert(*err != 0 && "Unexpected zero error value"); return *err; @@ -43,7 +51,7 @@ return 0; } // Call only after checking for an error. - ParentChild& getParentChild() { + const ParentChild& getParentChild() const { auto* ptr = std::get_if<ParentChild>(&val); assert(ptr && "Unhandled path parsing error!"); return *ptr;
diff --git a/system/lib/wasmfs/syscalls.cpp b/system/lib/wasmfs/syscalls.cpp index 2a86a58..8ea0253 100644 --- a/system/lib/wasmfs/syscalls.cpp +++ b/system/lib/wasmfs/syscalls.cpp
@@ -433,7 +433,7 @@ if (auto err = parsed.getError()) { return err; } - auto& [parent, childName] = parsed.getParentChild(); + auto& [parent, childName, hasTrailingSlash] = parsed.getParentChild(); if (childName.size() > WASMFS_NAME_MAX) { return -ENAMETOOLONG; } @@ -444,6 +444,15 @@ child = lockedParent.getChild(std::string(childName)); // The requested node was not found. if (!child) { + // POSIX requires that a pathname with a trailing slash must refer to a + // directory. If it doesn't exist, we can't create it as a directory + // here (open() can only create regular files). + if (hasTrailingSlash) { + if (flags & O_CREAT) { + return -EISDIR; + } + return -ENOENT; + } // If curr is the last element and the create flag is specified // If O_DIRECTORY is also specified, still create a regular file: // https://man7.org/linux/man-pages/man2/open.2.html#BUGS @@ -534,8 +543,9 @@ return -EACCES; } - // Fail if O_DIRECTORY is specified and pathname is not a directory - if (flags & O_DIRECTORY && !child->is<Directory>()) { + // Fail if O_DIRECTORY (or a trailing slash is specified) and pathname is not + // a directory. + if ((hasTrailingSlash || (flags & O_DIRECTORY)) && !child->is<Directory>()) { return -ENOTDIR; } @@ -602,7 +612,7 @@ if (auto err = parsed.getError()) { return err; } - auto& [parent, childNameView] = parsed.getParentChild(); + auto& [parent, childNameView, hasTrailingSlash] = parsed.getParentChild(); std::string childName(childNameView); auto lockedParent = parent->locked(); @@ -611,7 +621,11 @@ } // Check if the requested directory already exists. - if (lockedParent.getChild(childName)) { + auto child = lockedParent.getChild(childName); + if (child) { + if (hasTrailingSlash && !child->is<Directory>()) { + return -ENOTDIR; + } return -EEXIST; } @@ -831,13 +845,18 @@ if (auto err = parsed.getError()) { return err; } - auto& [parent, childNameView] = parsed.getParentChild(); + auto& [parent, childNameView, hasTrailingSlash] = parsed.getParentChild(); std::string childName(childNameView); auto lockedParent = parent->locked(); auto file = lockedParent.getChild(childName); if (!file) { return -ENOENT; } + + if (hasTrailingSlash && !file->is<Directory>()) { + return -ENOTDIR; + } + // Disallow removing the root directory, even if it is empty. if (file == wasmFS.getRootDirectory()) { return -EBUSY; @@ -879,13 +898,18 @@ if (auto err = parsed.getError()) { return err; } - auto& [parent, childNameView] = parsed.getParentChild(); + auto& [parent, childNameView, hasTrailingSlash] = parsed.getParentChild(); std::string childName(childNameView); auto lockedParent = parent->locked(); auto file = lockedParent.getChild(childName); if (!file) { return -ENOENT; } + + if (hasTrailingSlash && !file->is<Directory>()) { + return -ENOTDIR; + } + // Disallow removing the root directory, even if it is empty. if (file == wasmFS.getRootDirectory()) { return -EBUSY; @@ -993,7 +1017,8 @@ if (auto err = parsedOld.getError()) { return err; } - auto& [oldParent, oldFileNameView] = parsedOld.getParentChild(); + auto& [oldParent, oldFileNameView, oldHasTrailingSlash] = + parsedOld.getParentChild(); std::string oldFileName(oldFileNameView); // Get the new directory. @@ -1001,7 +1026,8 @@ if (auto err = parsedNew.getError()) { return err; } - auto& [newParent, newFileNameView] = parsedNew.getParentChild(); + auto& [newParent, newFileNameView, newHasTrailingSlash] = + parsedNew.getParentChild(); std::string newFileName(newFileNameView); if (newFileNameView.size() > WASMFS_NAME_MAX) { @@ -1020,6 +1046,14 @@ return -ENOENT; } + if (oldHasTrailingSlash && !oldFile->is<Directory>()) { + return -ENOTDIR; + } + + if (newHasTrailingSlash && newFile && !newFile->is<Directory>()) { + return -ENOTDIR; + } + // If the source and destination are the same, do nothing. if (oldFile == newFile) { return 0; @@ -1083,10 +1117,15 @@ if (auto err = parsed.getError()) { return err; } - auto& [parent, childNameView] = parsed.getParentChild(); + auto& [parent, childNameView, hasTrailingSlash] = parsed.getParentChild(); if (childNameView.size() > WASMFS_NAME_MAX) { return -ENAMETOOLONG; } + + if (hasTrailingSlash) { + return -ENOENT; + } + auto lockedParent = parent->locked(); std::string childName(childNameView); if (lockedParent.getChild(childName)) {
diff --git a/system/lib/wasmfs/wasmfs.cpp b/system/lib/wasmfs/wasmfs.cpp index 7a771a8..d108d85 100644 --- a/system/lib/wasmfs/wasmfs.cpp +++ b/system/lib/wasmfs/wasmfs.cpp
@@ -189,7 +189,7 @@ emscripten_err("Fatal error during file preloading"); abort(); } - auto& [parent, childName] = parsed.getParentChild(); + auto& [parent, childName, hasTrailingSlash] = parsed.getParentChild(); auto created = parent->locked().insertDataFile(std::string(childName), (mode_t)mode); assert(created && "TODO: handle preload insertion errors");
diff --git a/test/codesize/test_codesize_cxx_ctors1.json b/test/codesize/test_codesize_cxx_ctors1.json index 2c79cb1..038b26e 100644 --- a/test/codesize/test_codesize_cxx_ctors1.json +++ b/test/codesize/test_codesize_cxx_ctors1.json
@@ -1,10 +1,10 @@ { - "a.out.js": 19260, - "a.out.js.gz": 7991, + "a.out.js": 19296, + "a.out.js.gz": 8030, "a.out.nodebug.wasm": 132627, "a.out.nodebug.wasm.gz": 49921, - "total": 151887, - "total_gz": 57912, + "total": 151923, + "total_gz": 57951, "sent": [ "__cxa_throw", "_abort_js",
diff --git a/test/codesize/test_codesize_cxx_ctors2.json b/test/codesize/test_codesize_cxx_ctors2.json index 804f183..b80f002 100644 --- a/test/codesize/test_codesize_cxx_ctors2.json +++ b/test/codesize/test_codesize_cxx_ctors2.json
@@ -1,10 +1,10 @@ { - "a.out.js": 19237, - "a.out.js.gz": 7978, + "a.out.js": 19273, + "a.out.js.gz": 8017, "a.out.nodebug.wasm": 132055, "a.out.nodebug.wasm.gz": 49575, - "total": 151292, - "total_gz": 57553, + "total": 151328, + "total_gz": 57592, "sent": [ "__cxa_throw", "_abort_js",
diff --git a/test/codesize/test_codesize_cxx_except.json b/test/codesize/test_codesize_cxx_except.json index 4117b5c..70c8b48 100644 --- a/test/codesize/test_codesize_cxx_except.json +++ b/test/codesize/test_codesize_cxx_except.json
@@ -1,10 +1,10 @@ { - "a.out.js": 23240, - "a.out.js.gz": 8977, + "a.out.js": 23277, + "a.out.js.gz": 9015, "a.out.nodebug.wasm": 172526, "a.out.nodebug.wasm.gz": 57447, - "total": 195766, - "total_gz": 66424, + "total": 195803, + "total_gz": 66462, "sent": [ "__cxa_begin_catch", "__cxa_end_catch",
diff --git a/test/codesize/test_codesize_cxx_except_wasm.json b/test/codesize/test_codesize_cxx_except_wasm.json index 69dbaf4..b003a8b 100644 --- a/test/codesize/test_codesize_cxx_except_wasm.json +++ b/test/codesize/test_codesize_cxx_except_wasm.json
@@ -1,10 +1,10 @@ { - "a.out.js": 19092, - "a.out.js.gz": 7925, + "a.out.js": 19128, + "a.out.js.gz": 7962, "a.out.nodebug.wasm": 147926, "a.out.nodebug.wasm.gz": 55309, - "total": 167018, - "total_gz": 63234, + "total": 167054, + "total_gz": 63271, "sent": [ "_abort_js", "_tzset_js",
diff --git a/test/codesize/test_codesize_cxx_except_wasm_legacy.json b/test/codesize/test_codesize_cxx_except_wasm_legacy.json index 00312e3..461330e 100644 --- a/test/codesize/test_codesize_cxx_except_wasm_legacy.json +++ b/test/codesize/test_codesize_cxx_except_wasm_legacy.json
@@ -1,10 +1,10 @@ { - "a.out.js": 19166, - "a.out.js.gz": 7949, + "a.out.js": 19202, + "a.out.js.gz": 7987, "a.out.nodebug.wasm": 145732, "a.out.nodebug.wasm.gz": 54936, - "total": 164898, - "total_gz": 62885, + "total": 164934, + "total_gz": 62923, "sent": [ "_abort_js", "_tzset_js",
diff --git a/test/codesize/test_codesize_cxx_lto.json b/test/codesize/test_codesize_cxx_lto.json index b5022ee..87dddb2 100644 --- a/test/codesize/test_codesize_cxx_lto.json +++ b/test/codesize/test_codesize_cxx_lto.json
@@ -1,10 +1,10 @@ { - "a.out.js": 18629, - "a.out.js.gz": 7684, + "a.out.js": 18665, + "a.out.js.gz": 7720, "a.out.nodebug.wasm": 101958, "a.out.nodebug.wasm.gz": 39461, - "total": 120587, - "total_gz": 47145, + "total": 120623, + "total_gz": 47181, "sent": [ "a (emscripten_resize_heap)", "b (_setitimer_js)",
diff --git a/test/codesize/test_codesize_cxx_mangle.json b/test/codesize/test_codesize_cxx_mangle.json index ab6962b..fe22d69 100644 --- a/test/codesize/test_codesize_cxx_mangle.json +++ b/test/codesize/test_codesize_cxx_mangle.json
@@ -1,10 +1,10 @@ { - "a.out.js": 23290, - "a.out.js.gz": 8999, + "a.out.js": 23327, + "a.out.js.gz": 9037, "a.out.nodebug.wasm": 238957, "a.out.nodebug.wasm.gz": 79820, - "total": 262247, - "total_gz": 88819, + "total": 262284, + "total_gz": 88857, "sent": [ "__cxa_begin_catch", "__cxa_end_catch",
diff --git a/test/codesize/test_codesize_cxx_noexcept.json b/test/codesize/test_codesize_cxx_noexcept.json index 32250f8..d3605bb 100644 --- a/test/codesize/test_codesize_cxx_noexcept.json +++ b/test/codesize/test_codesize_cxx_noexcept.json
@@ -1,10 +1,10 @@ { - "a.out.js": 19260, - "a.out.js.gz": 7991, + "a.out.js": 19296, + "a.out.js.gz": 8030, "a.out.nodebug.wasm": 134657, "a.out.nodebug.wasm.gz": 50769, - "total": 153917, - "total_gz": 58760, + "total": 153953, + "total_gz": 58799, "sent": [ "__cxa_throw", "_abort_js",
diff --git a/test/codesize/test_codesize_file_preload.expected.js b/test/codesize/test_codesize_file_preload.expected.js index 7921688..25581aa 100644 --- a/test/codesize/test_codesize_file_preload.expected.js +++ b/test/codesize/test_codesize_file_preload.expected.js
@@ -1470,6 +1470,7 @@ if (!PATH.isAbs(path)) { path = FS.cwd() + "/" + path; } + var hasTrailingSlash = path.endsWith("/"); // limit max consecutive symlinks to SYMLOOP_MAX. linkloop: for (var nlinks = 0; nlinks < 40; nlinks++) { // split the absolute path @@ -1484,9 +1485,15 @@ break; } if (parts[i] === ".") { + if (!FS.isDir(current.mode)) { + throw new FS.ErrnoError(54); + } continue; } if (parts[i] === "..") { + if (!FS.isDir(current.mode)) { + throw new FS.ErrnoError(54); + } current_path = PATH.dirname(current_path); if (FS.isRoot(current)) { path = current_path + "/" + parts.slice(i + 1).join("/"); @@ -1518,8 +1525,9 @@ current = current.mounted.root; } // by default, lookupPath will not follow a symlink if it is the final path component. - // setting opts.follow = true will override this behavior. - if (FS.isLink(current.mode) && (!islast || opts.follow)) { + // setting opts.follow = true or having a trailing slash will override this behavior + // (POSIX requires that a trailing slash forces following of symbolic links). + if (FS.isLink(current.mode) && (!islast || opts.follow || hasTrailingSlash)) { if (!current.node_ops.readlink) { throw new FS.ErrnoError(52); } @@ -1527,10 +1535,16 @@ if (!PATH.isAbs(link)) { link = PATH.dirname(current_path) + "/" + link; } - path = link + "/" + parts.slice(i + 1).join("/"); + var suffix = parts.slice(i + 1).join("/"); + path = link + (suffix ? "/" + suffix : ""); continue linkloop; } } + // POSIX requires that a pathname with a trailing slash must refer to a + // directory. + if (hasTrailingSlash && !FS.isDir(current.mode)) { + throw new FS.ErrnoError(54); + } return { path: current_path, node: current
diff --git a/test/codesize/test_codesize_file_preload.json b/test/codesize/test_codesize_file_preload.json index 45bd9ba..cfd062b 100644 --- a/test/codesize/test_codesize_file_preload.json +++ b/test/codesize/test_codesize_file_preload.json
@@ -1,10 +1,10 @@ { - "a.out.js": 22207, - "a.out.js.gz": 9203, + "a.out.js": 22341, + "a.out.js.gz": 9239, "a.out.nodebug.wasm": 1648, "a.out.nodebug.wasm.gz": 938, - "total": 23855, - "total_gz": 10141, + "total": 23989, + "total_gz": 10177, "sent": [ "a (fd_write)" ],
diff --git a/test/codesize/test_codesize_files_js_fs.json b/test/codesize/test_codesize_files_js_fs.json index fdf2c69..bce450b 100644 --- a/test/codesize/test_codesize_files_js_fs.json +++ b/test/codesize/test_codesize_files_js_fs.json
@@ -1,10 +1,10 @@ { - "a.out.js": 17919, - "a.out.js.gz": 7341, + "a.out.js": 17955, + "a.out.js.gz": 7374, "a.out.nodebug.wasm": 381, "a.out.nodebug.wasm.gz": 260, - "total": 18300, - "total_gz": 7601, + "total": 18336, + "total_gz": 7634, "sent": [ "a (fd_write)", "b (fd_read)",
diff --git a/test/codesize/test_codesize_hello_dylink.json b/test/codesize/test_codesize_hello_dylink.json index 2706ec7..70b02a2 100644 --- a/test/codesize/test_codesize_hello_dylink.json +++ b/test/codesize/test_codesize_hello_dylink.json
@@ -1,10 +1,10 @@ { - "a.out.js": 26251, - "a.out.js.gz": 11194, + "a.out.js": 26288, + "a.out.js.gz": 11248, "a.out.nodebug.wasm": 17671, "a.out.nodebug.wasm.gz": 8925, - "total": 43922, - "total_gz": 20119, + "total": 43959, + "total_gz": 20173, "sent": [ "__syscall_stat64", "emscripten_resize_heap",
diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index a6ae726..9b12b64 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json
@@ -1,7 +1,7 @@ { - "a.out.js": 244573, + "a.out.js": 244710, "a.out.nodebug.wasm": 578054, - "total": 822627, + "total": 822764, "sent": [ "IMG_Init", "IMG_Load",
diff --git a/test/fs/test_fs_enotdir.c b/test/fs/test_fs_enotdir.c index 0e776a7..c531cdc 100644 --- a/test/fs/test_fs_enotdir.c +++ b/test/fs/test_fs_enotdir.c
@@ -13,6 +13,16 @@ assert(close(src_fd) == 0); } { + // POSIX: open("file/") must fail with ENOTDIR if file is a regular file. + assert(open("file/", O_RDONLY) == -1); + assert(errno == ENOTDIR); + + assert(open("file/.", O_RDONLY) == -1); + assert(errno == ENOTDIR); + + assert(open("file/..", O_RDONLY) == -1); + assert(errno == ENOTDIR); + assert(mkdir("file/blah", 0777) == -1); assert(errno == ENOTDIR); } @@ -20,5 +30,19 @@ assert(open("./does-not-exist/", O_CREAT, 0777) == -1); assert(errno == EISDIR); } + { + assert(mkdir("dir", 0777) == 0); + assert(symlink("dir", "link_to_dir") == 0); + assert(symlink("file", "link_to_file") == 0); + + // link_to_dir/ should resolve to the directory. + int fd = open("link_to_dir/", O_RDONLY); + assert(fd >= 0); + close(fd); + + // link_to_file/ should fail with ENOTDIR. + assert(open("link_to_file/", O_RDONLY) == -1); + assert(errno == ENOTDIR); + } printf("success\n"); }
diff --git a/test/test_core.py b/test/test_core.py index 5ad9450..fcfaf52 100644 --- a/test/test_core.py +++ b/test/test_core.py
@@ -5934,7 +5934,6 @@ @no_windows('https://github.com/emscripten-core/emscripten/issues/8882') @crossplatform @also_with_nodefs_both - @no_wasmfs('Assertion failed: open("./does-not-exist/", O_CREAT, 0777) == -1 in test_fs_enotdir.c line 20. https://github.com/emscripten-core/emscripten/issues/25035') def test_fs_enotdir(self): if MACOS and '-DNODERAWFS' in self.cflags: self.skipTest('BSD libc sets a different errno') @@ -6039,7 +6038,6 @@ @also_with_nodefs_both @no_windows("stat ino values don't match on windows") @crossplatform - @no_wasmfs('Assertion failed: "a_ino == sta.st" in test_fs_readdir_ino_matches_stat_ino.c, line 58. https://github.com/emscripten-core/emscripten/issues/25035') def test_fs_readdir_ino_matches_stat_ino(self): self.do_runf('fs/test_fs_readdir_ino_matches_stat_ino.c', 'success')
diff --git a/test/wasmfs/wasmfs_open.c b/test/wasmfs/wasmfs_open.c index ac95cbd..585ef32 100644 --- a/test/wasmfs/wasmfs_open.c +++ b/test/wasmfs/wasmfs_open.c
@@ -15,14 +15,18 @@ // FIXME: Merge with other existing close and open tests. +#ifndef S_IWUGO +#define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH) +#define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH) +#define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + int main() { - // Test writing to a file with a trailing slash. + // Test that opening a file with a trailing slash fails with ENOTDIR. + errno = 0; int fd = open("/dev/stdout/", O_WRONLY); - - dprintf(fd, "WORKING WITH TRAILING BACKSLASH\n"); - - // Close open file - close(fd); + assert(fd == -1); + assert(errno == ENOTDIR); // Test writing to a file with no trailing backslash. int fd2 = open("/dev/stdout", O_WRONLY);
diff --git a/test/wasmfs/wasmfs_open.out b/test/wasmfs/wasmfs_open.out index 835911b..f30bc31 100644 --- a/test/wasmfs/wasmfs_open.out +++ b/test/wasmfs/wasmfs_open.out
@@ -1,4 +1,3 @@ -WORKING WITH TRAILING BACKSLASH WORKING WITHOUT TRAILING BACKSLASH Errno: Bad file descriptor Errno: Is a directory
diff --git a/test/wasmfs/wasmfs_stat.c b/test/wasmfs/wasmfs_stat.c index 716e90e..94a64bd 100644 --- a/test/wasmfs/wasmfs_stat.c +++ b/test/wasmfs/wasmfs_stat.c
@@ -27,7 +27,12 @@ // Test opening a file and calling fstat. struct stat file; + errno = 0; int fd = open("/dev/stdout/", O_WRONLY); + assert(fd == -1); + assert(errno == ENOTDIR); + + fd = open("/dev/stdout", O_WRONLY); assert(fd >= 0); assert(fstat(fd, &file) != -1); @@ -48,7 +53,7 @@ close(fd); // Check to see if the previous inode number matches. - int newfd = open("/dev/stdout/", O_WRONLY); + int newfd = open("/dev/stdout", O_WRONLY); struct stat newFile; assert(newfd >= 0); assert(fstat(newfd, &newFile) != -1); @@ -95,7 +100,10 @@ // Test calling stat without opening a file. struct stat statFile; - assert(stat("/dev/stdout/", &statFile) != -1); + errno = 0; + assert(stat("/dev/stdout/", &statFile) == -1); + assert(errno == ENOTDIR); + assert(stat("/dev/stdout", &statFile) != -1); assert(statFile.st_size == 0); assert((statFile.st_mode & S_IFMT) == S_IFCHR);