diff --git a/src/library_fs.js b/src/library_fs.js index f1cdee8b34e31..47c12599deeeb 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -177,6 +177,7 @@ FS.staticInit(); // paths // lookupPath(path, opts = {}) { + var trailingSlash = path.endsWith('/'); path = PATH_FS.resolve(path); if (!path) return { path: '', node: null }; @@ -233,6 +234,10 @@ FS.staticInit(); } } + if (trailingSlash && !FS.isDir(current.mode)) { + throw new FS.ErrnoError({{{ cDefs.ENOTDIR }}}); + } + return { path: current_path, node: current }; }, getPath(node) { diff --git a/src/library_path.js b/src/library_path.js index ab3112503a202..5c5a82c5fd810 100644 --- a/src/library_path.js +++ b/src/library_path.js @@ -37,8 +37,8 @@ addToLibrary({ return parts; }, normalize: (path) => { - var isAbsolute = PATH.isAbs(path), - trailingSlash = path.substr(-1) === '/'; + var isAbsolute = PATH.isAbs(path); + var trailingSlash = path.endsWith('/'); // Normalize the path path = PATH.normalizeArray(path.split('/').filter((p) => !!p), !isAbsolute).join('/'); if (!path && !isAbsolute) { diff --git a/system/lib/wasmfs/paths.cpp b/system/lib/wasmfs/paths.cpp index 3d1fbf4c081e8..d92ffa332851f 100644 --- a/system/lib/wasmfs/paths.cpp +++ b/system/lib/wasmfs/paths.cpp @@ -75,11 +75,6 @@ ParsedParent doParseParent(std::string_view path, path.remove_prefix(1); } - // Ignore trailing '/'. - while (!path.empty() && path.back() == '/') { - path.remove_suffix(1); - } - // 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, "."). diff --git a/test/other/metadce/test_metadce_files_wasmfs.funcs b/test/other/metadce/test_metadce_files_wasmfs.funcs index 891e6faaa85b8..d37f5864b4895 100644 --- a/test/other/metadce/test_metadce_files_wasmfs.funcs +++ b/test/other/metadce/test_metadce_files_wasmfs.funcs @@ -31,7 +31,9 @@ $__wasm_call_ctors $abort $char*\20std::__2::copy_n\5babi:nn180100\5d\28char\20const*\2c\20unsigned\20long\2c\20char*\29 $decltype\28auto\29\20std::__2::__variant_detail::__visitation::__base::__dispatcher<0ul>::__dispatch\5babi:nn180100\5d>\2c\20int>\2c\20\28std::__2::__variant_detail::_Trait\291>::__destroy\5babi:nn180100\5d\28\29::'lambda'\28auto&\29&&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20std::__2::vector>\2c\20int>&>\28auto\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20std::__2::vector>\2c\20int>&\29 +$decltype\28auto\29\20std::__2::__variant_detail::__visitation::__base::__dispatcher<0ul\2c\200ul>::__dispatch\5babi:nn180100\5d>>::__generic_construct\5babi:nn180100\5d>\2c\20\28std::__2::__variant_detail::_Trait\291>>\28std::__2::__variant_detail::__ctor>>&\2c\20std::__2::__variant_detail::__move_constructor>\2c\20\28std::__2::__variant_detail::_Trait\291>&&\29::'lambda'\28std::__2::__variant_detail::__move_constructor>\2c\20\28std::__2::__variant_detail::_Trait\291>&\2c\20auto&&\29&&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&&>\28std::__2::__variant_detail::__move_constructor>\2c\20\28std::__2::__variant_detail::_Trait\291>\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&&\29 $decltype\28auto\29\20std::__2::__variant_detail::__visitation::__base::__dispatcher<1ul>::__dispatch\5babi:nn180100\5d>\2c\20\28std::__2::__variant_detail::_Trait\291>::__destroy\5babi:nn180100\5d\28\29::'lambda'\28auto&\29&&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&>\28auto\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&\29 +$decltype\28auto\29\20std::__2::__variant_detail::__visitation::__base::__dispatcher<1ul\2c\201ul>::__dispatch\5babi:nn180100\5d>>::__generic_construct\5babi:nn180100\5d>\2c\20\28std::__2::__variant_detail::_Trait\291>>\28std::__2::__variant_detail::__ctor>>&\2c\20std::__2::__variant_detail::__move_constructor>\2c\20\28std::__2::__variant_detail::_Trait\291>&&\29::'lambda'\28std::__2::__variant_detail::__move_constructor>\2c\20\28std::__2::__variant_detail::_Trait\291>&\2c\20auto&&\29&&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&&>\28std::__2::__variant_detail::__move_constructor>\2c\20\28std::__2::__variant_detail::_Trait\291>\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&\2c\20std::__2::__variant_detail::__base<\28std::__2::__variant_detail::_Trait\291\2c\20long\2c\20std::__2::shared_ptr>&&\29 $dummy $emscripten_builtin_malloc $fflush diff --git a/test/other/metadce/test_metadce_files_wasmfs.size b/test/other/metadce/test_metadce_files_wasmfs.size index f88426bec9949..6e8a50fa66625 100644 --- a/test/other/metadce/test_metadce_files_wasmfs.size +++ b/test/other/metadce/test_metadce_files_wasmfs.size @@ -1 +1 @@ -50957 +51335 diff --git a/test/other/test_realpath_2.c b/test/other/test_realpath_2.c index 8bf02855c23dc..22a396325583d 100644 --- a/test/other/test_realpath_2.c +++ b/test/other/test_realpath_2.c @@ -20,6 +20,8 @@ int main(int argc, char **argv) { testrealpath("testfile.txt"); testrealpath("Folder/testfile.txt"); testrealpath("testnonexistentfile.txt"); + // Filenames with trailing slash should fail. + testrealpath("Folder/testfile.txt/"); // folders testrealpath("Folder"); testrealpath("/Folder"); diff --git a/test/other/test_realpath_2.out b/test/other/test_realpath_2.out index f3ca23f2c8c0d..bd4122198a237 100644 --- a/test/other/test_realpath_2.out +++ b/test/other/test_realpath_2.out @@ -1,6 +1,7 @@ Resolved: "testfile.txt" => "/testfile.txt" Resolved: "Folder/testfile.txt" => "/Folder/testfile.txt" Resolve failed: "testnonexistentfile.txt" +Resolve failed: "Folder/testfile.txt/" Resolved: "Folder" => "/Folder" Resolved: "/Folder" => "/Folder" Resolved: "./" => "/" diff --git a/test/wasmfs/wasmfs_open.c b/test/wasmfs/wasmfs_open.c index eb8454f1ccd0a..fcd80404c0a65 100644 --- a/test/wasmfs/wasmfs_open.c +++ b/test/wasmfs/wasmfs_open.c @@ -16,13 +16,15 @@ // FIXME: Merge with other existing close and open tests. int main() { - // Test writing to a file with a trailing slash. + // Opening a file with trailing backslash should fail. int fd = open("/dev/stdout/", O_WRONLY); - - dprintf(fd, "WORKING WITH TRAILING BACKSLASH\n"); - - // Close open file - close(fd); + assert(fd == -1); +#ifdef WASMFS + assert(errno == ENOTDIR); +#else + assert(errno == ENOENT); +#endif + printf("Errno: %s\n", strerror(errno)); // 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 835911b7987a0..f30bc3195dba1 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 716e90e2b2bde..e5a8118968245 100644 --- a/test/wasmfs/wasmfs_stat.c +++ b/test/wasmfs/wasmfs_stat.c @@ -25,9 +25,13 @@ int main() { assert(result == -1); assert(errno == EBADF); + // Test stat of file with trailing slash should fail. + assert(stat("/dev/stdout/", &invalid) == -1); + assert(errno == ENOTDIR); + // Test opening a file and calling fstat. struct stat file; - int fd = open("/dev/stdout/", O_WRONLY); + int fd = open("/dev/stdout", O_WRONLY); assert(fd >= 0); assert(fstat(fd, &file) != -1); @@ -48,7 +52,7 @@ int main() { 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 +99,7 @@ int main() { // Test calling stat without opening a file. struct stat statFile; - assert(stat("/dev/stdout/", &statFile) != -1); + assert(stat("/dev/stdout", &statFile) != -1); assert(statFile.st_size == 0); assert((statFile.st_mode & S_IFMT) == S_IFCHR);