| |
| |
| |
| |
|
|
| |
|
|
| #define _LARGEFILE64_SOURCE |
|
|
| #include <dirent.h> |
| #include <emscripten/emscripten.h> |
| #include <emscripten/heap.h> |
| #include <emscripten/html5.h> |
| #include <emscripten/syscalls.h> |
| #include <errno.h> |
| #include <mutex> |
| #include <poll.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/statfs.h> |
| #include <unistd.h> |
| #include <utility> |
| #include <vector> |
| #include <wasi/api.h> |
|
|
| #include "backend.h" |
| #include "file.h" |
| #include "file_table.h" |
| #include "paths.h" |
| #include "pipe_backend.h" |
| #include "special_files.h" |
| #include "wasmfs.h" |
|
|
| |
| |
| #define WASMFS_PERM_READ 0444 |
|
|
| #define WASMFS_PERM_WRITE 0222 |
|
|
| #define WASMFS_PERM_EXECUTE 0111 |
|
|
| |
| #define WASMFS_NAME_MAX 255 |
|
|
| extern "C" { |
|
|
| using namespace wasmfs; |
|
|
| int __syscall_dup3(int oldfd, int newfd, int flags) { |
| if (flags & ~O_CLOEXEC) { |
| return -EINVAL; |
| } |
| if (oldfd == newfd) { |
| return -EINVAL; |
| } |
|
|
| auto fileTable = wasmFS.getFileTable().locked(); |
| auto oldOpenFile = fileTable.getEntry(oldfd); |
| if (!oldOpenFile) { |
| return -EBADF; |
| } |
| if (newfd < 0 || newfd >= WASMFS_FD_MAX) { |
| return -EBADF; |
| } |
|
|
| |
| |
| (void)fileTable.setEntry(newfd, oldOpenFile); |
| return newfd; |
| } |
|
|
| int __syscall_dup(int fd) { |
| auto fileTable = wasmFS.getFileTable().locked(); |
|
|
| |
| auto openFile = fileTable.getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
| return fileTable.addEntry(openFile); |
| } |
|
|
| |
| |
| enum class OffsetHandling { OpenFileState, Argument }; |
|
|
| |
| |
| |
| static __wasi_errno_t writeAtOffset(OffsetHandling setOffset, |
| __wasi_fd_t fd, |
| const __wasi_ciovec_t* iovs, |
| size_t iovs_len, |
| __wasi_size_t* nwritten, |
| __wasi_filesize_t offset = 0) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return __WASI_ERRNO_BADF; |
| } |
|
|
| if (iovs_len < 0 || offset < 0) { |
| return __WASI_ERRNO_INVAL; |
| } |
|
|
| auto lockedOpenFile = openFile->locked(); |
| auto file = lockedOpenFile.getFile()->dynCast<DataFile>(); |
| if (!file) { |
| return __WASI_ERRNO_ISDIR; |
| } |
|
|
| auto lockedFile = file->locked(); |
|
|
| if (setOffset == OffsetHandling::OpenFileState) { |
| if (lockedOpenFile.getFlags() & O_APPEND) { |
| off_t size = lockedFile.getSize(); |
| if (size < 0) { |
| |
| return -size; |
| } |
| offset = size; |
| lockedOpenFile.setPosition(offset); |
| } else { |
| offset = lockedOpenFile.getPosition(); |
| } |
| } |
|
|
| |
|
|
| size_t bytesWritten = 0; |
| for (size_t i = 0; i < iovs_len; i++) { |
| const uint8_t* buf = iovs[i].buf; |
| off_t len = iovs[i].buf_len; |
|
|
| |
| |
| if (!buf && len > 0) { |
| return __WASI_ERRNO_INVAL; |
| } |
|
|
| |
| if (addWillOverFlow(offset, (__wasi_filesize_t)bytesWritten)) { |
| return __WASI_ERRNO_FBIG; |
| } |
|
|
| auto result = lockedFile.write(buf, len, offset + bytesWritten); |
| if (result < 0) { |
| |
| |
| if (bytesWritten > 0) { |
| break; |
| } |
| return -result; |
| } |
| |
| bytesWritten += result; |
| if (result < len) { |
| |
| break; |
| } |
| } |
| *nwritten = bytesWritten; |
| if (setOffset == OffsetHandling::OpenFileState && |
| lockedOpenFile.getFile()->isSeekable()) { |
| lockedOpenFile.setPosition(offset + bytesWritten); |
| } |
| if (bytesWritten) { |
| lockedFile.updateMTime(); |
| } |
| return __WASI_ERRNO_SUCCESS; |
| } |
|
|
| |
| |
| |
| |
| static __wasi_errno_t readAtOffset(OffsetHandling setOffset, |
| __wasi_fd_t fd, |
| const __wasi_iovec_t* iovs, |
| size_t iovs_len, |
| __wasi_size_t* nread, |
| __wasi_filesize_t offset = 0) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return __WASI_ERRNO_BADF; |
| } |
|
|
| auto lockedOpenFile = openFile->locked(); |
|
|
| if (setOffset == OffsetHandling::OpenFileState) { |
| offset = lockedOpenFile.getPosition(); |
| } |
|
|
| if (iovs_len < 0 || offset < 0) { |
| return __WASI_ERRNO_INVAL; |
| } |
|
|
| |
|
|
| auto file = lockedOpenFile.getFile()->dynCast<DataFile>(); |
|
|
| |
| if (!file) { |
| return __WASI_ERRNO_ISDIR; |
| } |
|
|
| auto lockedFile = file->locked(); |
|
|
| size_t bytesRead = 0; |
| for (size_t i = 0; i < iovs_len; i++) { |
| uint8_t* buf = iovs[i].buf; |
| size_t len = iovs[i].buf_len; |
|
|
| if (!buf && len > 0) { |
| return __WASI_ERRNO_INVAL; |
| } |
|
|
| |
| auto result = lockedFile.read(buf, len, offset + bytesRead); |
| if (result < 0) { |
| |
| |
| if (bytesRead > 0) { |
| break; |
| } |
| return -result; |
| } |
|
|
| |
|
|
| |
| assert(result <= len); |
|
|
| bytesRead += result; |
| if (result < len) { |
| |
| break; |
| } |
| } |
| *nread = bytesRead; |
| if (setOffset == OffsetHandling::OpenFileState && |
| lockedOpenFile.getFile()->isSeekable()) { |
| lockedOpenFile.setPosition(offset + bytesRead); |
| } |
| return __WASI_ERRNO_SUCCESS; |
| } |
|
|
| __wasi_errno_t __wasi_fd_write(__wasi_fd_t fd, |
| const __wasi_ciovec_t* iovs, |
| size_t iovs_len, |
| __wasi_size_t* nwritten) { |
| return writeAtOffset( |
| OffsetHandling::OpenFileState, fd, iovs, iovs_len, nwritten); |
| } |
|
|
| __wasi_errno_t __wasi_fd_read(__wasi_fd_t fd, |
| const __wasi_iovec_t* iovs, |
| size_t iovs_len, |
| __wasi_size_t* nread) { |
| return readAtOffset(OffsetHandling::OpenFileState, fd, iovs, iovs_len, nread); |
| } |
|
|
| __wasi_errno_t __wasi_fd_pwrite(__wasi_fd_t fd, |
| const __wasi_ciovec_t* iovs, |
| size_t iovs_len, |
| __wasi_filesize_t offset, |
| __wasi_size_t* nwritten) { |
| return writeAtOffset( |
| OffsetHandling::Argument, fd, iovs, iovs_len, nwritten, offset); |
| } |
|
|
| __wasi_errno_t __wasi_fd_pread(__wasi_fd_t fd, |
| const __wasi_iovec_t* iovs, |
| size_t iovs_len, |
| __wasi_filesize_t offset, |
| __wasi_size_t* nread) { |
| return readAtOffset( |
| OffsetHandling::Argument, fd, iovs, iovs_len, nread, offset); |
| } |
|
|
| __wasi_errno_t __wasi_fd_close(__wasi_fd_t fd) { |
| std::shared_ptr<DataFile> closee; |
| { |
| |
| auto fileTable = wasmFS.getFileTable().locked(); |
| auto entry = fileTable.getEntry(fd); |
| if (!entry) { |
| return __WASI_ERRNO_BADF; |
| } |
| closee = fileTable.setEntry(fd, nullptr); |
| } |
| if (closee) { |
| |
| int ret = -closee->locked().close(); |
| assert(ret >= 0); |
| return ret; |
| } |
| return __WASI_ERRNO_SUCCESS; |
| } |
|
|
| __wasi_errno_t __wasi_fd_sync(__wasi_fd_t fd) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return __WASI_ERRNO_BADF; |
| } |
|
|
| |
| |
| auto dataFile = openFile->locked().getFile()->dynCast<DataFile>(); |
| if (dataFile) { |
| auto ret = dataFile->locked().flush(); |
| assert(ret <= 0); |
| |
| return -ret; |
| } |
|
|
| return __WASI_ERRNO_SUCCESS; |
| } |
|
|
| int __syscall_fdatasync(int fd) { |
| |
| return __wasi_fd_sync(fd); |
| } |
|
|
| backend_t wasmfs_get_backend_by_fd(int fd) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return NullBackend; |
| } |
| return openFile->locked().getFile()->getBackend(); |
| } |
|
|
| |
| |
| backend_t wasmfs_get_backend_by_path(const char* path) { |
| auto parsed = path::parseFile(path); |
| if (parsed.getError()) { |
| |
| return NullBackend; |
| } |
| return parsed.getFile()->getBackend(); |
| } |
|
|
| static timespec ms_to_timespec(double ms) { |
| long long seconds = ms / 1000; |
| timespec ts; |
| ts.tv_sec = seconds; |
| ts.tv_nsec = (ms - (seconds * 1000)) * 1000 * 1000; |
| return ts; |
| } |
|
|
| int __syscall_newfstatat(int dirfd, intptr_t path, intptr_t buf, int flags) { |
| |
| if (flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW)) { |
| |
| return -EINVAL; |
| } |
| auto parsed = path::getFileAt(dirfd, (char*)path, flags); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto file = parsed.getFile(); |
|
|
| |
| auto lockedFile = file->locked(); |
| auto buffer = (struct stat*)buf; |
|
|
| off_t size = lockedFile.getSize(); |
| if (size < 0) { |
| return size; |
| } |
| buffer->st_size = size; |
|
|
| |
| |
| |
| |
| |
| buffer->st_dev = 1; |
| buffer->st_mode = lockedFile.getMode(); |
| buffer->st_ino = file->getIno(); |
| |
| buffer->st_nlink = 1; |
| buffer->st_uid = 0; |
| buffer->st_gid = 0; |
| |
| buffer->st_rdev = 0; |
| |
| buffer->st_blocks = (buffer->st_size + 511) / 512; |
| |
| buffer->st_blksize = 4096; |
| buffer->st_atim = ms_to_timespec(lockedFile.getATime()); |
| buffer->st_mtim = ms_to_timespec(lockedFile.getMTime()); |
| buffer->st_ctim = ms_to_timespec(lockedFile.getCTime()); |
| return __WASI_ERRNO_SUCCESS; |
| } |
|
|
| int __syscall_stat64(intptr_t path, intptr_t buf) { |
| return __syscall_newfstatat(AT_FDCWD, path, buf, 0); |
| } |
|
|
| int __syscall_lstat64(intptr_t path, intptr_t buf) { |
| return __syscall_newfstatat(AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW); |
| } |
|
|
| int __syscall_fstat64(int fd, intptr_t buf) { |
| return __syscall_newfstatat(fd, (intptr_t) "", buf, AT_EMPTY_PATH); |
| } |
|
|
| |
| |
| |
| enum class OpenReturnMode { FD, Nothing }; |
|
|
| static __wasi_fd_t doOpen(path::ParsedParent parsed, |
| int flags, |
| mode_t mode, |
| backend_t backend = NullBackend, |
| OpenReturnMode returnMode = OpenReturnMode::FD) { |
| int accessMode = (flags & O_ACCMODE); |
| if (accessMode != O_WRONLY && accessMode != O_RDONLY && |
| accessMode != O_RDWR) { |
| return -EINVAL; |
| } |
|
|
| |
| assert((flags & ~(O_CREAT | O_EXCL | O_DIRECTORY | O_TRUNC | O_APPEND | |
| O_RDWR | O_WRONLY | O_RDONLY | O_LARGEFILE | O_NOFOLLOW | |
| O_CLOEXEC | O_NONBLOCK)) == 0); |
|
|
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto& [parent, childName] = parsed.getParentChild(); |
| if (childName.size() > WASMFS_NAME_MAX) { |
| return -ENAMETOOLONG; |
| } |
|
|
| std::shared_ptr<File> child; |
| { |
| auto lockedParent = parent->locked(); |
| child = lockedParent.getChild(std::string(childName)); |
| |
| if (!child) { |
| |
| |
| |
| if (!(flags & O_CREAT)) { |
| return -ENOENT; |
| } |
|
|
| |
| if (!lockedParent.getParent()) { |
| return -ENOENT; |
| } |
|
|
| |
| mode &= S_IALLUGO; |
| mode &= ~wasmFS.getUmask(); |
|
|
| |
| if (!backend) { |
| backend = parent->getBackend(); |
| } |
|
|
| |
| std::shared_ptr<File> created; |
| if (backend == parent->getBackend()) { |
| created = lockedParent.insertDataFile(std::string(childName), mode); |
| if (!created) { |
| |
| |
| return -EIO; |
| } |
| } else { |
| created = backend->createFile(mode); |
| if (!created) { |
| |
| |
| return -EIO; |
| } |
| [[maybe_unused]] bool mounted = |
| lockedParent.mountChild(std::string(childName), created); |
| assert(mounted); |
| } |
| |
| if (returnMode == OpenReturnMode::Nothing) { |
| return 0; |
| } |
|
|
| std::shared_ptr<OpenFileState> openFile; |
| if (auto err = OpenFileState::create(created, flags, openFile)) { |
| assert(err < 0); |
| return err; |
| } |
| return wasmFS.getFileTable().locked().addEntry(openFile); |
| } |
| } |
|
|
| if (auto link = child->dynCast<Symlink>()) { |
| if (flags & O_NOFOLLOW) { |
| return -ELOOP; |
| } |
| |
| |
| auto target = link->getTarget(); |
| auto parsedLink = path::getFileFrom(parent, target); |
| if (auto err = parsedLink.getError()) { |
| return err; |
| } |
| child = parsedLink.getFile(); |
| } |
| assert(!child->is<Symlink>()); |
|
|
| |
| if ((flags & O_EXCL) && (flags & O_CREAT)) { |
| return -EEXIST; |
| } |
|
|
| if (child->is<Directory>() && (accessMode != O_RDONLY || (flags & O_CREAT))) { |
| return -EISDIR; |
| } |
|
|
| |
| auto fileMode = child->locked().getMode(); |
| if ((accessMode == O_RDONLY || accessMode == O_RDWR) && |
| !(fileMode & WASMFS_PERM_READ)) { |
| return -EACCES; |
| } |
| if ((accessMode == O_WRONLY || accessMode == O_RDWR) && |
| !(fileMode & WASMFS_PERM_WRITE)) { |
| return -EACCES; |
| } |
|
|
| |
| if (flags & O_DIRECTORY && !child->is<Directory>()) { |
| return -ENOTDIR; |
| } |
|
|
| |
| |
| std::shared_ptr<OpenFileState> openFile; |
| if (auto err = OpenFileState::create(child, flags, openFile)) { |
| assert(err < 0); |
| return err; |
| } |
|
|
| |
| if (flags & O_TRUNC) { |
| if (!child->is<DataFile>()) { |
| return -EISDIR; |
| } |
| if ((fileMode & WASMFS_PERM_WRITE) == 0) { |
| return -EACCES; |
| } |
| |
| (void)child->cast<DataFile>()->locked().setSize(0); |
| } |
|
|
| return wasmFS.getFileTable().locked().addEntry(openFile); |
| } |
|
|
| |
| |
| int wasmfs_create_file(char* pathname, mode_t mode, backend_t backend) { |
| static_assert(std::is_same_v<decltype(doOpen(0, 0, 0, 0)), unsigned int>, |
| "unexpected conversion from result of doOpen to int"); |
| return doOpen( |
| path::parseParent((char*)pathname), O_CREAT | O_EXCL, mode, backend); |
| } |
|
|
| |
| int __syscall_openat(int dirfd, intptr_t path, int flags, ...) { |
| mode_t mode = 0; |
| va_list v1; |
| va_start(v1, flags); |
| mode = va_arg(v1, int); |
| va_end(v1); |
|
|
| return doOpen(path::parseParent((char*)path, dirfd), flags, mode); |
| } |
|
|
| int __syscall_mknodat(int dirfd, intptr_t path, int mode, int dev) { |
| assert(dev == 0); |
| if (mode & S_IFDIR) { |
| return -EINVAL; |
| } |
| if (mode & S_IFIFO) { |
| return -EPERM; |
| } |
| return doOpen(path::parseParent((char*)path, dirfd), |
| O_CREAT | O_EXCL, |
| mode, |
| NullBackend, |
| OpenReturnMode::Nothing); |
| } |
|
|
| static int |
| doMkdir(path::ParsedParent parsed, int mode, backend_t backend = NullBackend) { |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto& [parent, childNameView] = parsed.getParentChild(); |
| std::string childName(childNameView); |
| auto lockedParent = parent->locked(); |
|
|
| if (childName.size() > WASMFS_NAME_MAX) { |
| return -ENAMETOOLONG; |
| } |
|
|
| |
| if (lockedParent.getChild(childName)) { |
| return -EEXIST; |
| } |
|
|
| |
| |
| |
| mode &= S_IRWXUGO | S_ISVTX; |
| mode &= ~wasmFS.getUmask(); |
|
|
| if (!(lockedParent.getMode() & WASMFS_PERM_WRITE)) { |
| return -EACCES; |
| } |
|
|
| |
| |
| |
| if (!backend) { |
| backend = parent->getBackend(); |
| } |
|
|
| if (backend == parent->getBackend()) { |
| if (!lockedParent.insertDirectory(childName, mode)) { |
| |
| |
| return -EIO; |
| } |
| } else { |
| auto created = backend->createDirectory(mode); |
| if (!created) { |
| |
| |
| return -EIO; |
| } |
| [[maybe_unused]] bool mounted = lockedParent.mountChild(childName, created); |
| assert(mounted); |
| } |
|
|
| |
|
|
| return 0; |
| } |
|
|
| |
| |
| int wasmfs_create_directory(char* path, int mode, backend_t backend) { |
| static_assert(std::is_same_v<decltype(doMkdir(0, 0, 0)), int>, |
| "unexpected conversion from result of doMkdir to int"); |
| return doMkdir(path::parseParent(path), mode, backend); |
| } |
|
|
| |
| int __syscall_mkdirat(int dirfd, intptr_t path, int mode) { |
| return doMkdir(path::parseParent((char*)path, dirfd), mode); |
| } |
|
|
| int __syscall_umask(int mask) { |
| mode_t old = wasmFS.getUmask(); |
| wasmFS.setUmask(mask); |
| return old; |
| } |
|
|
| __wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd, |
| __wasi_filedelta_t offset, |
| __wasi_whence_t whence, |
| __wasi_filesize_t* newoffset) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return __WASI_ERRNO_BADF; |
| } |
| auto lockedOpenFile = openFile->locked(); |
|
|
| if (!lockedOpenFile.getFile()->isSeekable()) { |
| return __WASI_ERRNO_SPIPE; |
| } |
|
|
| off_t position; |
| if (whence == SEEK_SET) { |
| position = offset; |
| } else if (whence == SEEK_CUR) { |
| position = lockedOpenFile.getPosition() + offset; |
| } else if (whence == SEEK_END) { |
| |
| |
| off_t size = lockedOpenFile.getFile()->locked().getSize(); |
| if (size < 0) { |
| |
| return -size; |
| } |
| position = size + offset; |
| } else { |
| return __WASI_ERRNO_INVAL; |
| } |
|
|
| if (position < 0) { |
| return __WASI_ERRNO_INVAL; |
| } |
|
|
| lockedOpenFile.setPosition(position); |
|
|
| if (newoffset) { |
| *newoffset = position; |
| } |
|
|
| return __WASI_ERRNO_SUCCESS; |
| } |
|
|
| static int doChdir(std::shared_ptr<File>& file) { |
| auto dir = file->dynCast<Directory>(); |
| if (!dir) { |
| return -ENOTDIR; |
| } |
| wasmFS.setCWD(dir); |
| return 0; |
| } |
|
|
| int __syscall_chdir(intptr_t path) { |
| auto parsed = path::parseFile((char*)path); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| return doChdir(parsed.getFile()); |
| } |
|
|
| int __syscall_fchdir(int fd) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
| return doChdir(openFile->locked().getFile()); |
| } |
|
|
| int __syscall_getcwd(intptr_t buf, size_t size) { |
| |
| if (!buf && size > 0) { |
| return -EFAULT; |
| } |
|
|
| |
| if (buf && size == 0) { |
| return -EINVAL; |
| } |
|
|
| auto curr = wasmFS.getCWD(); |
|
|
| std::string result = ""; |
|
|
| while (curr != wasmFS.getRootDirectory()) { |
| auto parent = curr->locked().getParent(); |
| |
| |
| if (!parent) { |
| return -ENOENT; |
| } |
|
|
| auto name = parent->locked().getName(curr); |
| result = '/' + name + result; |
| curr = parent; |
| } |
|
|
| |
| if (result.empty()) { |
| result = "/"; |
| } |
|
|
| int len = result.length() + 1; |
|
|
| |
| |
| if (len > size) { |
| return -ERANGE; |
| } |
|
|
| |
| strcpy((char*)buf, result.c_str()); |
|
|
| return len; |
| } |
|
|
| __wasi_errno_t __wasi_fd_fdstat_get(__wasi_fd_t fd, __wasi_fdstat_t* stat) { |
| |
| |
| |
| |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return __WASI_ERRNO_BADF; |
| } |
|
|
| if (openFile->locked().getFile()->is<Directory>()) { |
| stat->fs_filetype = __WASI_FILETYPE_DIRECTORY; |
| } else { |
| stat->fs_filetype = __WASI_FILETYPE_REGULAR_FILE; |
| } |
| return __WASI_ERRNO_SUCCESS; |
| } |
|
|
| |
| int __syscall_unlinkat(int dirfd, intptr_t path, int flags) { |
| if (flags & ~AT_REMOVEDIR) { |
| |
| return -EINVAL; |
| } |
| |
| |
| |
| if (flags == AT_REMOVEDIR) { |
| std::string_view p((char*)path); |
| |
| while (!p.empty() && p.back() == '/') { |
| p.remove_suffix(1); |
| } |
| if (p.size() >= 2 && p.substr(p.size() - 2) == std::string_view("/.")) { |
| return -EINVAL; |
| } |
| } |
| auto parsed = path::parseParent((char*)path, dirfd); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto& [parent, childNameView] = parsed.getParentChild(); |
| std::string childName(childNameView); |
| auto lockedParent = parent->locked(); |
| auto file = lockedParent.getChild(childName); |
| if (!file) { |
| return -ENOENT; |
| } |
| |
| if (file == wasmFS.getRootDirectory()) { |
| return -EBUSY; |
| } |
|
|
| auto lockedFile = file->locked(); |
| if (auto dir = file->dynCast<Directory>()) { |
| if (flags != AT_REMOVEDIR) { |
| return -EISDIR; |
| } |
| |
| if (dir->locked().getNumEntries() > 0) { |
| return -ENOTEMPTY; |
| } |
| } else { |
| |
| if (flags == AT_REMOVEDIR) { |
| return -ENOTDIR; |
| } |
| } |
|
|
| |
| if (!(lockedParent.getMode() & WASMFS_PERM_WRITE)) { |
| return -EACCES; |
| } |
|
|
| |
| return lockedParent.removeChild(childName); |
| } |
|
|
| int __syscall_rmdir(intptr_t path) { |
| return __syscall_unlinkat(AT_FDCWD, path, AT_REMOVEDIR); |
| } |
|
|
| |
| |
| int wasmfs_unmount(const char* path) { |
| auto parsed = path::parseParent(path, AT_FDCWD); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto& [parent, childNameView] = parsed.getParentChild(); |
| std::string childName(childNameView); |
| auto lockedParent = parent->locked(); |
| auto file = lockedParent.getChild(childName); |
| if (!file) { |
| return -ENOENT; |
| } |
| |
| if (file == wasmFS.getRootDirectory()) { |
| return -EBUSY; |
| } |
|
|
| if (!file->dynCast<Directory>()) { |
| |
| return -ENOTDIR; |
| } |
|
|
| if (parent->getBackend() == file->getBackend()) { |
| |
| return -EINVAL; |
| } |
|
|
| |
| return lockedParent.removeChild(childName); |
| } |
|
|
| int __syscall_getdents64(int fd, intptr_t dirp, size_t count) { |
| dirent* result = (dirent*)dirp; |
|
|
| |
| if (count / sizeof(dirent) == 0) { |
| return -EINVAL; |
| } |
|
|
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
| auto lockedOpenFile = openFile->locked(); |
|
|
| auto dir = lockedOpenFile.getFile()->dynCast<Directory>(); |
| if (!dir) { |
| return -ENOTDIR; |
| } |
| auto lockedDir = dir->locked(); |
|
|
| |
| int index = lockedOpenFile.getPosition(); |
|
|
| |
| |
| auto parent = lockedDir.getParent(); |
| if (!parent) { |
| return 0; |
| } |
|
|
| off_t bytesRead = 0; |
| const auto& dirents = openFile->dirents; |
| for (; index < dirents.size() && bytesRead + sizeof(dirent) <= count; |
| index++) { |
| const auto& entry = dirents[index]; |
| result->d_ino = entry.ino; |
| result->d_off = index + 1; |
| result->d_reclen = sizeof(dirent); |
| switch (entry.kind) { |
| case File::UnknownKind: |
| result->d_type = DT_UNKNOWN; |
| break; |
| case File::DataFileKind: |
| result->d_type = DT_REG; |
| break; |
| case File::DirectoryKind: |
| result->d_type = DT_DIR; |
| break; |
| case File::SymlinkKind: |
| result->d_type = DT_LNK; |
| break; |
| default: |
| result->d_type = DT_UNKNOWN; |
| break; |
| } |
| assert(entry.name.size() + 1 <= sizeof(result->d_name)); |
| strcpy(result->d_name, entry.name.c_str()); |
| ++result; |
| bytesRead += sizeof(dirent); |
| } |
|
|
| |
| lockedOpenFile.setPosition(index); |
|
|
| return bytesRead; |
| } |
|
|
| |
| int __syscall_renameat(int olddirfd, |
| intptr_t oldpath, |
| int newdirfd, |
| intptr_t newpath) { |
| |
| |
| |
| |
| |
| |
| |
| |
| static std::mutex renameMutex; |
| std::lock_guard<std::mutex> renameLock(renameMutex); |
|
|
| |
| auto parsedOld = path::parseParent((char*)oldpath, olddirfd); |
| if (auto err = parsedOld.getError()) { |
| return err; |
| } |
| auto& [oldParent, oldFileNameView] = parsedOld.getParentChild(); |
| std::string oldFileName(oldFileNameView); |
|
|
| |
| auto parsedNew = path::parseParent((char*)newpath, newdirfd); |
| if (auto err = parsedNew.getError()) { |
| return err; |
| } |
| auto& [newParent, newFileNameView] = parsedNew.getParentChild(); |
| std::string newFileName(newFileNameView); |
|
|
| if (newFileNameView.size() > WASMFS_NAME_MAX) { |
| return -ENAMETOOLONG; |
| } |
|
|
| |
| auto lockedOldParent = oldParent->locked(); |
| auto lockedNewParent = newParent->locked(); |
|
|
| |
| auto oldFile = lockedOldParent.getChild(oldFileName); |
| auto newFile = lockedNewParent.getChild(newFileName); |
|
|
| if (!oldFile) { |
| return -ENOENT; |
| } |
|
|
| |
| if (oldFile == newFile) { |
| return 0; |
| } |
|
|
| |
| auto root = wasmFS.getRootDirectory(); |
| if (oldFile == root || newFile == root) { |
| return -EBUSY; |
| } |
|
|
| |
| if (!(lockedOldParent.getMode() & WASMFS_PERM_WRITE) || |
| !(lockedNewParent.getMode() & WASMFS_PERM_WRITE)) { |
| return -EACCES; |
| } |
|
|
| |
| if (oldParent->getBackend() != newParent->getBackend()) { |
| return -EXDEV; |
| } |
|
|
| |
| for (auto curr = newParent; curr != root; curr = curr->locked().getParent()) { |
| if (curr == oldFile) { |
| return -EINVAL; |
| } |
| } |
|
|
| |
| if (newFile) { |
| if (auto newDir = newFile->dynCast<Directory>()) { |
| |
| auto oldDir = oldFile->dynCast<Directory>(); |
| if (!oldDir) { |
| return -EISDIR; |
| } |
| |
| if (newDir->locked().getNumEntries() > 0) { |
| return -ENOTEMPTY; |
| } |
| } else { |
| |
| if (oldFile->is<Directory>()) { |
| return -ENOTDIR; |
| } |
| } |
| } |
|
|
| |
| if (auto err = lockedNewParent.insertMove(newFileName, oldFile)) { |
| assert(err < 0); |
| return err; |
| } |
| return 0; |
| } |
|
|
| |
| int __syscall_symlinkat(intptr_t target, int newdirfd, intptr_t linkpath) { |
| auto parsed = path::parseParent((char*)linkpath, newdirfd); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto& [parent, childNameView] = parsed.getParentChild(); |
| if (childNameView.size() > WASMFS_NAME_MAX) { |
| return -ENAMETOOLONG; |
| } |
| auto lockedParent = parent->locked(); |
| std::string childName(childNameView); |
| if (lockedParent.getChild(childName)) { |
| return -EEXIST; |
| } |
| if (!lockedParent.insertSymlink(childName, (char*)target)) { |
| return -EPERM; |
| } |
| return 0; |
| } |
|
|
| |
| int __syscall_readlinkat(int dirfd, |
| intptr_t path, |
| intptr_t buf, |
| size_t bufsize) { |
| |
| auto parsed = path::parseFile((char*)path, dirfd, path::NoFollowLinks); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto link = parsed.getFile()->dynCast<Symlink>(); |
| if (!link) { |
| return -EINVAL; |
| } |
| const auto& target = link->getTarget(); |
| auto bytes = std::min((size_t)bufsize, target.size()); |
| memcpy((char*)buf, target.c_str(), bytes); |
| return bytes; |
| } |
|
|
| static double timespec_to_ms(timespec ts) { |
| if (ts.tv_nsec == UTIME_OMIT) { |
| return INFINITY; |
| } |
| if (ts.tv_nsec == UTIME_NOW) { |
| return emscripten_date_now(); |
| } |
| return double(ts.tv_sec) * 1000 + double(ts.tv_nsec) / (1000 * 1000); |
| } |
|
|
| |
| int __syscall_utimensat(int dirFD, intptr_t path_, intptr_t times_, int flags) { |
| const char* path = (const char*)path_; |
| const struct timespec* times = (const struct timespec*)times_; |
| if (flags & ~AT_SYMLINK_NOFOLLOW) { |
| |
| return -EINVAL; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| auto parsed = path::getFileAt(dirFD, path, flags | AT_EMPTY_PATH); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
|
|
| |
| |
| double aTime, mTime; |
|
|
| if (times == nullptr) { |
| aTime = mTime = emscripten_date_now(); |
| } else { |
| aTime = timespec_to_ms(times[0]); |
| mTime = timespec_to_ms(times[1]); |
| } |
|
|
| auto locked = parsed.getFile()->locked(); |
| if (aTime != INFINITY) { |
| locked.setATime(aTime); |
| } |
| if (mTime != INFINITY) { |
| locked.setMTime(mTime); |
| } |
|
|
| return 0; |
| } |
|
|
| |
| int __syscall_fchmodat2(int dirfd, intptr_t path, int mode, int flags) { |
| if (flags & ~AT_SYMLINK_NOFOLLOW) { |
| |
| return -EINVAL; |
| } |
| auto parsed = path::getFileAt(dirfd, (char*)path, flags); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| auto lockedFile = parsed.getFile()->locked(); |
| lockedFile.setMode(mode); |
| |
| lockedFile.updateCTime(); |
| return 0; |
| } |
|
|
| int __syscall_chmod(intptr_t path, int mode) { |
| return __syscall_fchmodat2(AT_FDCWD, path, mode, 0); |
| } |
|
|
| int __syscall_fchmod(int fd, int mode) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
| auto lockedFile = openFile->locked().getFile()->locked(); |
| lockedFile.setMode(mode); |
| lockedFile.updateCTime(); |
| return 0; |
| } |
|
|
| int __syscall_fchownat( |
| int dirfd, intptr_t path, int owner, int group, int flags) { |
| |
| if (flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) { |
| |
| return -EINVAL; |
| } |
| auto parsed = path::getFileAt(dirfd, (char*)path, flags); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
|
|
| |
| |
| return 0; |
| } |
|
|
| int __syscall_fchown32(int fd, int owner, int group) { |
| return __syscall_fchownat(fd, (intptr_t) "", owner, group, AT_EMPTY_PATH); |
| } |
|
|
| |
| int __syscall_faccessat(int dirfd, intptr_t path, int amode, int flags) { |
| |
| |
| if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK))) { |
| return -EINVAL; |
| } |
| if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW)) { |
| |
| return -EINVAL; |
| } |
|
|
| |
| auto parsed = path::parseFile((char*)path, dirfd); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
|
|
| if (amode != F_OK) { |
| auto mode = parsed.getFile()->locked().getMode(); |
| if ((amode & R_OK) && !(mode & WASMFS_PERM_READ)) { |
| return -EACCES; |
| } |
| if ((amode & W_OK) && !(mode & WASMFS_PERM_WRITE)) { |
| return -EACCES; |
| } |
| if ((amode & X_OK) && !(mode & WASMFS_PERM_EXECUTE)) { |
| return -EACCES; |
| } |
| } |
|
|
| return 0; |
| } |
|
|
| static int doTruncate(std::shared_ptr<File>& file, off_t size) { |
| auto dataFile = file->dynCast<DataFile>(); |
|
|
| if (!dataFile) { |
| return -EISDIR; |
| } |
|
|
| auto locked = dataFile->locked(); |
| if (!(locked.getMode() & WASMFS_PERM_WRITE)) { |
| return -EACCES; |
| } |
|
|
| if (size < 0) { |
| return -EINVAL; |
| } |
|
|
| int ret = locked.setSize(size); |
| assert(ret <= 0); |
| return ret; |
| } |
|
|
| int __syscall_truncate64(intptr_t path, off_t size) { |
| auto parsed = path::parseFile((char*)path); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| return doTruncate(parsed.getFile(), size); |
| } |
|
|
| int __syscall_ftruncate64(int fd, off_t size) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
| auto ret = doTruncate(openFile->locked().getFile(), size); |
| |
| |
| |
| if (ret == -EACCES) { |
| ret = -EINVAL; |
| } |
| return ret; |
| } |
|
|
| static bool isTTY(std::shared_ptr<File>& file) { |
| |
| |
| return file == SpecialFiles::getStdin() || |
| file == SpecialFiles::getStdout() || file == SpecialFiles::getStderr(); |
| } |
|
|
| int __syscall_ioctl(int fd, int request, ...) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
|
|
| va_list args; |
| va_start(args, request); |
| void* argp = va_arg(args, void*); |
| va_end(args); |
|
|
| auto openHandle = openFile->locked(); |
| auto file = openHandle.getFile(); |
|
|
| if (request == FIONREAD) { |
| off_t size = file->locked().getSize(); |
| if (size < 0) { |
| return (int)size; |
| } |
| if (file->isSeekable()) { |
| size -= openHandle.getPosition(); |
| } |
| *static_cast<int*>(argp) = static_cast<int>(size); |
| return 0; |
| } |
|
|
| if (!isTTY(file)) { |
| return -ENOTTY; |
| } |
| |
| switch (request) { |
| case TCGETA: |
| case TCGETS: |
| case TCSETA: |
| case TCSETAW: |
| case TCSETAF: |
| case TCSETS: |
| case TCSETSW: |
| case TCSETSF: |
| case TIOCGWINSZ: |
| case TIOCSWINSZ: { |
| |
| return 0; |
| } |
| default: { |
| return -EINVAL; |
| } |
| } |
| } |
|
|
| int __syscall_pipe2(intptr_t fd, int flags) { |
| auto* fds = (__wasi_fd_t*)fd; |
| if (flags && flags != O_CLOEXEC) { |
| return -ENOTSUP; |
| } |
|
|
| |
| |
| |
| |
| auto data = std::make_shared<PipeData>(); |
| auto reader = std::make_shared<PipeFile>(S_IRUGO, data); |
| auto writer = std::make_shared<PipeFile>(S_IWUGO, data); |
|
|
| std::shared_ptr<OpenFileState> openReader, openWriter; |
| (void)OpenFileState::create(reader, O_RDONLY, openReader); |
| (void)OpenFileState::create(writer, O_WRONLY, openWriter); |
|
|
| auto fileTable = wasmFS.getFileTable().locked(); |
| fds[0] = fileTable.addEntry(openReader); |
| fds[1] = fileTable.addEntry(openWriter); |
|
|
| return 0; |
| } |
|
|
| |
| int __syscall_poll(intptr_t fds_, int nfds, int timeout) { |
| struct pollfd* fds = (struct pollfd*)fds_; |
| auto fileTable = wasmFS.getFileTable().locked(); |
|
|
| |
| |
| int nonzero = 0; |
| for (nfds_t i = 0; i < nfds; i++) { |
| auto* pollfd = &fds[i]; |
| auto fd = pollfd->fd; |
| if (fd < 0) { |
| |
| pollfd->revents = 0; |
| continue; |
| } |
| |
| auto mask = POLLNVAL; |
| auto openFile = fileTable.getEntry(fd); |
| if (openFile) { |
| mask = 0; |
| auto flags = openFile->locked().getFlags(); |
| auto accessMode = flags & O_ACCMODE; |
| auto readBit = pollfd->events & POLLOUT; |
| if (readBit && (accessMode == O_WRONLY || accessMode == O_RDWR)) { |
| mask |= readBit; |
| } |
| auto writeBit = pollfd->events & POLLIN; |
| if (writeBit && (accessMode == O_RDONLY || accessMode == O_RDWR)) { |
| |
| |
| |
| |
| if (openFile->locked().getFile()->locked().getSize() > 0) { |
| mask |= writeBit; |
| } |
| } |
| |
| } |
| |
| |
| if (mask) { |
| nonzero++; |
| } |
| pollfd->revents = mask; |
| } |
| |
| |
| |
| return nonzero; |
| } |
|
|
| int __syscall_fallocate(int fd, int mode, off_t offset, off_t len) { |
| assert(mode == 0); |
|
|
| auto fileTable = wasmFS.getFileTable().locked(); |
| auto openFile = fileTable.getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
|
|
| auto dataFile = openFile->locked().getFile()->dynCast<DataFile>(); |
| |
| if (!dataFile) { |
| return -ENODEV; |
| } |
|
|
| auto locked = dataFile->locked(); |
| if (!(locked.getMode() & WASMFS_PERM_WRITE)) { |
| return -EBADF; |
| } |
|
|
| if (offset < 0 || len <= 0) { |
| return -EINVAL; |
| } |
|
|
| |
| |
| |
| auto newNeededSize = offset + len; |
| off_t size = locked.getSize(); |
| if (size < 0) { |
| return size; |
| } |
| if (newNeededSize > size) { |
| if (auto err = locked.setSize(newNeededSize)) { |
| assert(err < 0); |
| return err; |
| } |
| } |
|
|
| return 0; |
| } |
|
|
| int __syscall_fcntl64(int fd, int cmd, ...) { |
| auto fileTable = wasmFS.getFileTable().locked(); |
| auto openFile = fileTable.getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
|
|
| switch (cmd) { |
| case F_DUPFD: { |
| int newfd; |
| va_list v1; |
| va_start(v1, cmd); |
| newfd = va_arg(v1, int); |
| va_end(v1); |
| if (newfd < 0) { |
| return -EINVAL; |
| } |
|
|
| |
| |
| while (1) { |
| if (!fileTable.getEntry(newfd)) { |
| (void)fileTable.setEntry(newfd, openFile); |
| return newfd; |
| } |
| newfd++; |
| } |
| } |
| case F_GETFD: |
| case F_SETFD: |
| |
| return 0; |
| case F_GETFL: |
| return openFile->locked().getFlags(); |
| case F_SETFL: { |
| int flags; |
| va_list v1; |
| va_start(v1, cmd); |
| flags = va_arg(v1, int); |
| va_end(v1); |
|
|
| auto lockedOpenFile = openFile->locked(); |
| auto oldFlags = lockedOpenFile.getFlags(); |
| |
| int mask = O_APPEND | O_NONBLOCK | O_ASYNC | O_DIRECT | O_NOATIME; |
| lockedOpenFile.setFlags((oldFlags & ~mask) | (flags & mask)); |
| return 0; |
| } |
| case F_GETLK: { |
| |
| static_assert(F_GETLK == F_GETLK64); |
| flock* data; |
| va_list v1; |
| va_start(v1, cmd); |
| data = va_arg(v1, flock*); |
| va_end(v1); |
| |
| data->l_type = F_UNLCK; |
| return 0; |
| } |
| case F_SETLK: |
| case F_SETLKW: { |
| static_assert(F_SETLK == F_SETLK64); |
| static_assert(F_SETLKW == F_SETLKW64); |
| |
| |
| |
| |
| return 0; |
| } |
| default: { |
| |
| return -EINVAL; |
| } |
| } |
| } |
|
|
| static int |
| doStatFS(std::shared_ptr<File>& file, size_t size, struct statfs* buf) { |
| if (size != sizeof(struct statfs)) { |
| |
| return -EINVAL; |
| } |
|
|
| |
| |
| |
| buf->f_type = 0; |
| buf->f_bsize = 4096; |
| buf->f_frsize = 4096; |
| buf->f_blocks = 1000000; |
| buf->f_bfree = 500000; |
| buf->f_bavail = 500000; |
| buf->f_files = file->getIno(); |
| buf->f_ffree = 1000000; |
| buf->f_fsid = {0, 0}; |
| buf->f_flags = ST_NOSUID; |
| buf->f_namelen = 255; |
| return 0; |
| } |
|
|
| int __syscall_statfs64(intptr_t path, size_t size, intptr_t buf) { |
| auto parsed = path::parseFile((char*)path); |
| if (auto err = parsed.getError()) { |
| return err; |
| } |
| return doStatFS(parsed.getFile(), size, (struct statfs*)buf); |
| } |
|
|
| int __syscall_fstatfs64(int fd, size_t size, intptr_t buf) { |
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
| return doStatFS(openFile->locked().getFile(), size, (struct statfs*)buf); |
| } |
|
|
| int _mmap_js(size_t length, |
| int prot, |
| int flags, |
| int fd, |
| off_t offset, |
| int* allocated, |
| void** addr) { |
| |
| |
| if ((prot & PROT_EXEC)) { |
| return -EPERM; |
| } |
|
|
| if (!length) { |
| return -EINVAL; |
| } |
|
|
| |
| int mapType = flags & MAP_TYPE; |
| if (mapType != MAP_PRIVATE && mapType != MAP_SHARED && |
| mapType != MAP_SHARED_VALIDATE) { |
| return -EINVAL; |
| } |
|
|
| if (mapType == MAP_SHARED_VALIDATE) { |
| WASMFS_UNREACHABLE("TODO: MAP_SHARED_VALIDATE"); |
| } |
|
|
| auto openFile = wasmFS.getFileTable().locked().getEntry(fd); |
| if (!openFile) { |
| return -EBADF; |
| } |
|
|
| std::shared_ptr<DataFile> file; |
|
|
| |
| { |
| auto lockedOpenFile = openFile->locked(); |
|
|
| |
| |
| if ((lockedOpenFile.getFlags() & O_ACCMODE) == O_WRONLY) { |
| return -EACCES; |
| } |
|
|
| |
| |
| |
| if ((prot & PROT_WRITE) != 0 && mapType != MAP_PRIVATE && |
| (lockedOpenFile.getFlags() & O_ACCMODE) != O_RDWR) { |
| return -EACCES; |
| } |
|
|
| file = lockedOpenFile.getFile()->dynCast<DataFile>(); |
| } |
|
|
| if (!file) { |
| return -ENODEV; |
| } |
|
|
| |
| |
| |
| |
|
|
| |
| |
| |
| uint8_t* ptr = (uint8_t*)emscripten_builtin_memalign(WASM_PAGE_SIZE, length); |
| if (!ptr) { |
| return -ENOMEM; |
| } |
|
|
| auto nread = file->locked().read(ptr, length, offset); |
| if (nread < 0) { |
| |
| emscripten_builtin_free(ptr); |
| return nread; |
| } |
|
|
| |
| |
| *allocated = true; |
| *addr = (void*)ptr; |
|
|
| |
| assert(nread <= length); |
|
|
| |
| memset(ptr + nread, 0, length - nread); |
|
|
| return 0; |
| } |
|
|
| int _msync_js( |
| intptr_t addr, size_t length, int prot, int flags, int fd, off_t offset) { |
| |
| |
| int mapType = flags & MAP_TYPE; |
| if (mapType == MAP_SHARED && (prot & PROT_WRITE)) { |
| __wasi_ciovec_t iovec; |
| iovec.buf = (uint8_t*)addr; |
| iovec.buf_len = length; |
| __wasi_size_t nwritten; |
| |
| return -__wasi_fd_pwrite(fd, &iovec, 1, offset, &nwritten); |
| } |
| return 0; |
| } |
|
|
| int _munmap_js( |
| intptr_t addr, size_t length, int prot, int flags, int fd, off_t offset) { |
| |
| |
| |
| return _msync_js(addr, length, prot, flags, fd, offset); |
| } |
|
|
| |
|
|
| int __syscall_accept4(int sockfd, |
| intptr_t addr, |
| intptr_t addrlen, |
| int flags, |
| int dummy1, |
| int dummy2) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_bind( |
| int sockfd, intptr_t addr, size_t alen, int dummy, int dummy2, int dummy3) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_connect( |
| int sockfd, intptr_t addr, size_t len, int dummy, int dummy2, int dummy3) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_socket( |
| int domain, int type, int protocol, int dummy1, int dummy2, int dummy3) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_listen( |
| int sockfd, int backlog, int dummy1, int dummy2, int dummy3, int dummy4) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_getsockopt(int sockfd, |
| int level, |
| int optname, |
| intptr_t optval, |
| intptr_t optlen, |
| int dummy) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_getsockname( |
| int sockfd, intptr_t addr, intptr_t len, int dummy, int dummy2, int dummy3) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_getpeername( |
| int sockfd, intptr_t addr, intptr_t len, int dummy, int dummy2, int dummy3) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_sendto( |
| int sockfd, intptr_t msg, size_t len, int flags, intptr_t addr, size_t alen) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_sendmsg( |
| int sockfd, intptr_t msg, int flags, intptr_t addr, size_t alen, int dummy) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_recvfrom(int sockfd, |
| intptr_t msg, |
| size_t len, |
| int flags, |
| intptr_t addr, |
| intptr_t alen) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_recvmsg( |
| int sockfd, intptr_t msg, int flags, int dummy, int dummy2, int dummy3) { |
| return -ENOSYS; |
| } |
|
|
| int __syscall_fadvise64(int fd, off_t offset, off_t length, int advice) { |
| |
| return 0; |
| } |
|
|
| } |
|
|