Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WasmFS] Support utime() #16262

Merged
merged 13 commits into from
Feb 14, 2022
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ jobs:
- run-tests:
# also add a little select testing for wasm2js in -O3
# also add a little select wasmfs testing
test_targets: "core3 wasm2js3.test_memorygrowth_2 wasmfs.test_hello_world wasmfs.test_hello_world_standalone wasmfs.test_unistd_links* wasmfs.test_atexit_standalone wasmfs.test_emscripten_get_now wasmfs.test_dyncall_specific_minimal_runtime core2ss.test_pthread_dylink"
test_targets: "core3 wasm2js3.test_memorygrowth_2 wasmfs.test_hello_world wasmfs.test_hello_world_standalone wasmfs.test_unistd_links* wasmfs.test_atexit_standalone wasmfs.test_emscripten_get_now wasmfs.test_dyncall_specific_minimal_runtime core2ss.test_pthread_dylink wasmfs.test_utime"
test-wasm2js1:
executor: bionic
steps:
Expand Down
1 change: 1 addition & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1922,6 +1922,7 @@ def default_setting(name, new_default):

if settings.WASMFS:
state.forced_stdlibs.append('libwasmfs')
state.forced_stdlibs.append('libwasmfs_libc')
settings.FILESYSTEM = 0
settings.SYSCALLS_REQUIRE_FILESYSTEM = 0
settings.JS_LIBRARIES.append((0, 'library_wasmfs.js'))
Expand Down
19 changes: 16 additions & 3 deletions system/lib/wasmfs/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ size_t Symlink::getSize() {

ParsedPath getParsedPath(std::vector<std::string> pathParts,
long& err,
std::shared_ptr<File> forbiddenAncestor) {
std::shared_ptr<File> forbiddenAncestor,
std::optional<__wasi_fd_t> baseFD) {
std::shared_ptr<Directory> curr;
auto begin = pathParts.begin();

Expand All @@ -132,7 +133,20 @@ ParsedPath getParsedPath(std::vector<std::string> pathParts,
return ParsedPath{curr->locked(), curr};
}
} else {
curr = wasmFS.getCWD();
// This is a relative path. It is either relative to the current working
// directory if no base FD is given, or if the base FD is the special value
// indicating the CWD.
if (baseFD && *baseFD != AT_FDCWD) {
auto lockedOpenDir = wasmFS.getLockedFileTable()[*baseFD].locked();
auto openDir = lockedOpenDir.getFile();
if (!openDir->is<Directory>()) {
err = -EBADF;
return ParsedPath{{}, nullptr};
}
curr = openDir->dynCast<Directory>();
} else {
curr = wasmFS.getCWD();
}
}

for (auto pathPart = begin; pathPart != pathParts.end() - 1; ++pathPart) {
Expand Down Expand Up @@ -255,5 +269,4 @@ std::vector<std::string> splitPath(char* pathname) {

return pathParts;
}

} // namespace wasmfs
9 changes: 6 additions & 3 deletions system/lib/wasmfs/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ using backend_t = Backend*;
const backend_t NullBackend = nullptr;

class File : public std::enable_shared_from_this<File> {

public:
enum FileKind { DataFileKind = 0, DirectoryKind, SymlinkKind };

Expand Down Expand Up @@ -134,7 +133,6 @@ class File : public std::enable_shared_from_this<File> {
};

class DataFile : public File {

virtual __wasi_errno_t read(uint8_t* buf, size_t len, off_t offset) = 0;
virtual __wasi_errno_t
write(const uint8_t* buf, size_t len, off_t offset) = 0;
Expand Down Expand Up @@ -271,12 +269,17 @@ struct ParsedPath {
// TODO: When locking the directory structure is refactored, parent should be
// returned as a pointer, similar to child.
// Will return an empty handle if the parent is not a directory.
//
// Will error if the forbiddenAncestor is encountered while processing.
// If the forbiddenAncestor is encountered, err will be set to EINVAL and
// an empty parent handle will be returned.
//
// If baseFD is provided, and the path is relative, then it will be interpreted
// relative to the base. That is the behavior that the *at() syscalls require.
ParsedPath getParsedPath(std::vector<std::string> pathParts,
long& err,
std::shared_ptr<File> forbiddenAncestor = nullptr);
std::shared_ptr<File> forbiddenAncestor = nullptr,
std::optional<__wasi_fd_t> baseFD = {});

// Call getDir if one needs a parent directory of a file path.
// TODO: Remove this when directory structure locking is refactored and use
Expand Down
65 changes: 49 additions & 16 deletions system/lib/wasmfs/syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,24 +458,31 @@ static long doMkdir(char* path, long mode, backend_t backend = NullBackend) {
// Check if the requested directory already exists.
if (parsedPath.child) {
return -EEXIST;
} else {
// Mask rwx permissions for user, group and others, and the sticky bit.
// This prevents users from entering S_IFREG for example.
// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
mode &= S_IRWXUGO | S_ISVTX;

// By default, the backend that the directory is created in is the same as
// the parent directory. However, if a backend is passed as a parameter,
// then that backend is used.
if (!backend) {
backend = parsedPath.parent->unlocked()->getBackend();
}
// Create an empty in-memory directory.
auto created = backend->createDirectory(mode);
}

parsedPath.parent->setEntry(pathParts.back(), created);
return 0;
// Mask rwx permissions for user, group and others, and the sticky bit.
// This prevents users from entering S_IFREG for example.
// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
mode &= S_IRWXUGO | S_ISVTX;

// By default, the backend that the directory is created in is the same as
// the parent directory. However, if a backend is passed as a parameter,
// then that backend is used.
if (!backend) {
backend = parsedPath.parent->unlocked()->getBackend();
}
// Create an empty in-memory directory.
auto created = backend->createDirectory(mode);
parsedPath.parent->setEntry(pathParts.back(), created);

// Update the times.
auto lockedFile = created->locked();
time_t now = time(NULL);
lockedFile.atime() = now;
lockedFile.mtime() = now;
lockedFile.ctime() = now;

return 0;
}

// This function is exposed to users and allows users to specify a particular
Expand Down Expand Up @@ -936,4 +943,30 @@ long __syscall_readlink(char* path, char* buf, size_t bufSize) {

return bytes;
}

long __syscall_utimensat(int dirFD,
char* path,
const struct timespec times[2],
int flags) {
// TODO: support flags here
assert(flags == 0);

auto pathParts = splitPath(path);

long err;
auto parsedPath = getParsedPath(pathParts, err, nullptr, dirFD);
if (!parsedPath.parent) {
return err;
}

// TODO: tv_nsec (nanoseconds) as well? but time_t is seconds as an integer
auto aSeconds = times[0].tv_sec;
auto mSeconds = times[1].tv_sec;

auto locked = parsedPath.child->locked();
locked.atime() = aSeconds;
locked.mtime() = mSeconds;

return 0;
}
}
15 changes: 15 additions & 0 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1431,6 +1431,21 @@ def can_use(self):
return settings.WASMFS


# Libc components of WasmFS, which uses more libc code than the JS filesystem.
# This must be a separate library than libwasmfs because the build commands
# are different for libc code.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I hope this isn't necessary.

There are a couple of ways we can get the correct build flags:

  1. Have libwasmfs inherit from MuslInternalLibrary.
  2. Use the new customize_build_cmd to use different cflags for utime.c.

Copy link
Member Author

Choose a reason for hiding this comment

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

1 can't work as c++17 and c can't be used in the same library (the cflags conflict and clang errors out). But 2 sounds possible, thanks, I'll look into that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, switched to a single library and picking the build flags in customize_build_cmd.

class libwasmfs_libc(MTLibrary, DebugLibrary, AsanInstrumentedLibrary, MuslInternalLibrary):
name = 'libwasmfs_libc'

def get_files(self):
return files_in_path(
path='system/lib/libc/musl/src/time',
filenames=['utime.c'])

def can_use(self):
return settings.WASMFS


class libhtml5(Library):
name = 'libhtml5'

Expand Down