Skip to content

WASMFS does not handle symlinks??? #24123

Open
@rickg-hcl

Description

@rickg-hcl

Please include the following in your bug report:

Version of emscripten/emsdk:
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 4.0.6 (1ddaae4)
clang version 21.0.0git (https:/github.com/llvm/llvm-project 4775e6d9099467df9363e1a3cd5950cc3d2fde05)
Target: wasm32-unknown-emscripten
Thread model: posix

Failing command line in full:

Full link command and output with -v appended:

emcc -g -s WASMFS -pthread -s PROXY_TO_PTHREAD=1 -s EXPORTED_FUNCTIONS="_malloc,_free" -v wasmfs_links.c -o test.html
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/bin/clang -target wasm32-unknown-emscripten -fignore-exceptions -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot -D__EMSCRIPTEN_SHARED_MEMORY__=1 -DEMSCRIPTEN -Xclang -iwithsysroot/include/fakesdl -Xclang -iwithsysroot/include/compat -g3 -pthread -v -c wasmfs_links.c -o /var/folders/d8/08bv2b6s2b50q04__ln3cmbh0000gp/T/emscripten_temp_61hwxnjn/wasmfs_links_0.o
clang version 21.0.0git (https:/github.com/llvm/llvm-project 4775e6d9099467df9363e1a3cd5950cc3d2fde05)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /Users/richardgillaspy/GitHubOpen/emsdk/upstream/bin
 (in-process)
 "/Users/richardgillaspy/GitHubOpen/emsdk/upstream/bin/clang-21" -cc1 -triple wasm32-unknown-emscripten -emit-obj -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name wasmfs_links.c -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-feature +atomics -target-feature +bulk-memory -target-feature +mutable-globals -target-feature +sign-ext -target-cpu generic -fvisibility=hidden -debug-info-kind=constructor -dwarf-version=4 -debugger-tuning=gdb -fdebug-compilation-dir=/Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/test -v -fcoverage-compilation-dir=/Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/test -resource-dir /Users/richardgillaspy/GitHubOpen/emsdk/upstream/lib/clang/21 -D __EMSCRIPTEN_SHARED_MEMORY__=1 -D EMSCRIPTEN -isysroot /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot -internal-isystem /Users/richardgillaspy/GitHubOpen/emsdk/upstream/lib/clang/21/include -internal-isystem /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten -internal-isystem /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/include -ferror-limit 19 -pthread -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fignore-exceptions -fcolor-diagnostics -iwithsysroot/include/fakesdl -iwithsysroot/include/compat -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -o /var/folders/d8/08bv2b6s2b50q04__ln3cmbh0000gp/T/emscripten_temp_61hwxnjn/wasmfs_links_0.o -x c wasmfs_links.c
clang -cc1 version 21.0.0git based upon LLVM 21.0.0git default target x86_64-apple-darwin23.6.0
ignoring nonexistent directory "/Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten"
#include "..." search starts here:
#include <...> search starts here:
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/include/fakesdl
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/include/compat
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/lib/clang/21/include
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/include
End of search list.
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/bin/clang --version
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/bin/wasm-ld -o test.wasm /var/folders/d8/08bv2b6s2b50q04__ln3cmbh0000gp/T/emscripten_temp_61hwxnjn/wasmfs_links_0.o -L/Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten -L/Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/src/lib /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/crtbegin.o /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/crt1_proxy_main.o -lGL-mt-getprocaddr -lal -lhtml5 -lstubs-debug -lnoexit -lc-mt-debug -ldlmalloc-mt-debug -lcompiler_rt-mt -lc++-mt-noexcept -lc++abi-debug-mt-noexcept -lsockets-mt -lwasmfs_no_fs -lwasmfs-mt-debug -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /var/folders/d8/08bv2b6s2b50q04__ln3cmbh0000gp/T/tmpg0r0_o6vlibemscripten_js_symbols.so --import-memory --shared-memory --export=malloc --export=free --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=wasmfs_flush --export=_emscripten_stack_alloc --export=_emscripten_thread_free_data --export=_emscripten_thread_crashed --export=__wasm_call_ctors --export=_emscripten_tls_init --export=_emscripten_thread_init --export=_emscripten_stack_restore --export=emscripten_stack_set_limits --export=_emscripten_thread_exit --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --no-growable-memory --initial-memory=16777216 --entry=_emscripten_proxy_main --stack-first --table-base=1
 /Users/richardgillaspy/GitHubOpen/emsdk/upstream/bin/llvm-objcopy test.wasm test.wasm --remove-section=producers
emcc: warning: `main` is defined in the input files, but `_main` is not in `EXPORTED_FUNCTIONS`. Add it to this list if you want `main` to run. [-Wunused-main]
 /Users/richardgillaspy/GitHubOpen/emsdk/node/20.18.0_64bit/bin/node /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/tools/compiler.mjs -
 /Users/richardgillaspy/GitHubOpen/emsdk/node/20.18.0_64bit/bin/node /Users/richardgillaspy/GitHubOpen/emsdk/upstream/emscripten/tools/preprocessor.mjs - shell.html

I just modified the unistd/link.c test to use WASMFS and it fails.

/*
 * Copyright 2011 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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <emscripten/wasmfs.h>
#include <emscripten/console.h>

#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

void makedir(const char *dir) {
  int rtn = mkdir(dir, 0777);
  assert(rtn == 0);
}

void makefile(const char *file, const char *content) {
  int fd = open(file, O_RDWR | O_CREAT, 0777);
  assert(fd >= 0);
  int rtn = write(fd, content, strlen(content));
  assert(rtn == strlen(content));
  close(fd);
}

void makelink(const char *link, const char *path) {
  int rtn = symlink(link, path);
  assert(rtn == 0);
}

void changedir(const char *dir) {
  int rtn = chdir(dir);
  assert(rtn == 0);
}

void setup() {
//  makedir("/working");
#if defined(__EMSCRIPTEN__) && defined(NODEFS)
  EM_ASM(FS.mount(NODEFS, { root: '.' }, '/working'));
#endif
  changedir("/working");
  makelink("../test/../there!", "link");
  makefile("file", "test");
  makedir("directory");
  makedir("directory/subdirectory");
  makefile("directory/subdirectory/file", "Subdirectory");

  makedir("relative");
  makedir("relative/subrelative");
  makefile("relative/file", "Relative");
  makefile("relative/subrelative/file", "Subrelative");
  makelink("../relative/file", "directory/relative");
  makelink("../../relative/subrelative/file", "directory/subdirectory/subrelative");
  makelink("directory/subdirectory/file", "subdirectoryrelative");

  makedir("absolute");
  makedir("absolute/subabsolute");
  makefile("absolute/file", "Absolute");
  makefile("absolute/subabsolute/file", "Subabsolute");
  makelink("/working/absolute/file", "/working/directory/absolute");
  makelink("/working/absolute/subabsolute/file", "/working/directory/subdirectory/subabsolute");
  makelink("/working/directory/subdirectory/file", "/working/subdirectoryabsolute");
}

void test_reading_existing_symlinks() {
  char* files[] = {"link", "file", "directory"};

  for (int i = 0; i < sizeof files / sizeof files[0]; i++) {
    char buffer[256] = {0};
    int rtn = readlink(files[i], buffer, 256);
    printf("readlink: '%s'\n", files[i]);
    printf("errno: %s\n\n", strerror(errno));
    if (rtn < 0) {
      continue;
    }

    assert(strcmp(buffer, "../test/../there!") == 0);
    assert(strlen(buffer) == rtn);
    errno = 0;
  }
}

void test_overwriting_symlink() {
  int rtn = symlink("new-nonexistent-path", "link");
  assert(rtn == -1);
  assert(errno == EEXIST);
  errno = 0;
}

void test_creating_symlink() {
  int rtn = symlink("new-nonexistent-path", "directory/link");
  assert(rtn == 0);
  assert(errno == 0);
  errno = 0;

  char buffer[256] = {0};
  rtn = readlink("directory/link", buffer, 256);
  assert(errno == 0);
  assert(strcmp(buffer, "new-nonexistent-path") == 0);
  assert(strlen(buffer) == rtn);
  errno = 0;
}

void test_reading_shortened_symlink() {
  char buffer[256] = {0};
  readlink("directory/link", buffer, 256);
  buffer[0] = buffer[1] = buffer[2] = buffer[3] = buffer[4] = buffer[5] = '*';
  int rtn = readlink("link", buffer, 4);
  assert(errno == 0);
  assert(rtn == 4);
  assert(strcmp(buffer, "../t**nexistent-path") == 0);
  errno = 0;
}

void test_noticing_loop_in_symlink() {
  // FS.lookupPath should notice the symlink loop and return ELOOP, not go into
  // an infinite recurse.
  //
  // This test doesn't work in wasmfs -- probably because access sees the
  // symlink and returns true without bothering to chase the symlink
  symlink("./loop-link/inside", "./loop-link");
  int rtn = access("loop-link", F_OK);
  assert(rtn == -1);
  assert(errno == ELOOP);
  errno = 0;
}


void test_relative_path_symlinks() {
  char* parents[] = {
    "/working/directory/",
    "/working/directory/subdirectory/",
    "/working/"
  };

  char* links[] = {
    "relative",
    "subrelative",
    "subdirectoryrelative",
  };

  for (int i = 0; i < sizeof links / sizeof links[0]; i++) {
    int rtn = chdir(parents[i]);
    assert(rtn == 0);
    char symlink[256] = {0};
    strcat(strcpy(symlink, parents[i]), links[i]);
    printf("symlink: '%s'\n", symlink);
    char buf[256] = {0};
    rtn = readlink(links[i], buf, 256);
    FILE *fd = fopen(buf, "r");
    assert(fd);
    char buffer[13] = {0};
    rtn = fread(buffer, 1, 13, fd);
    assert(rtn <= 13);
    printf("buffer: '%s'\n\n", buffer);
    fclose(fd);
  }
}

void test_absolute_path_symlinks() {
  char* links[] = {
    "/working/directory/absolute",
    "/working/directory/subdirectory/subabsolute",
    "/working/subdirectoryabsolute"
  };

  for (int i = 0; i < sizeof links / sizeof links[0]; i++) {
    printf("symlink: '%s'\n", links[i]);
    char buf[256] = {0};
    readlink(links[i], buf, 256);
    FILE *fd = fopen(buf, "r");
    assert(fd);
    char buffer[13] = {0};
    int rtn = fread(buffer, 1, 13, fd);
    assert(rtn <= 13);
    printf("buffer: '%s'\n\n", buffer);
    fclose(fd);
  }
}

int main() {
  backend_t opfs = wasmfs_create_opfs_backend();
  emscripten_console_log("created OPFS backend");

  int err = wasmfs_create_directory("/working", 0777, opfs);
  assert(err == 0);
  emscripten_console_log("mounted OPFS root directory");

  setup();
  test_reading_existing_symlinks();
  test_overwriting_symlink();
  test_creating_symlink();
  test_reading_shortened_symlink();
  test_noticing_loop_in_symlink();
  test_relative_path_symlinks();
  test_absolute_path_symlinks();
  return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions