#include "io-device.hpp" #include "orbis/KernelAllocator.hpp" #include "orbis/SocketAddress.hpp" #include "orbis/file.hpp" #include "orbis/stat.hpp" #include "orbis/thread/Process.hpp" #include "orbis/thread/Thread.hpp" #include "orbis/uio.hpp" #include "orbis/utils/Logs.hpp" #include "vfs.hpp" #include "vm.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct HostFile : orbis::File { bool closeOnExit = true; bool alignTruncate = false; ~HostFile() { if (hostFd > 0 && closeOnExit) { ::close(hostFd); } } }; struct SocketFile : orbis::File { orbis::utils::kstring name; int dom = -1; int type = -1; int prot = -1; orbis::kmap> options; ~SocketFile() { if (hostFd > 0) { ::close(hostFd); } } }; static orbis::ErrorCode convertErrc(std::errc errc) { if (errc == std::errc{}) { return {}; } switch (errc) { case std::errc::address_family_not_supported: return orbis::ErrorCode::AFNOSUPPORT; case std::errc::address_in_use: return orbis::ErrorCode::ADDRINUSE; case std::errc::address_not_available: return orbis::ErrorCode::ADDRNOTAVAIL; case std::errc::already_connected: return orbis::ErrorCode::ISCONN; case std::errc::argument_out_of_domain: return orbis::ErrorCode::DOM; case std::errc::bad_address: return orbis::ErrorCode::FAULT; case std::errc::bad_file_descriptor: return orbis::ErrorCode::BADF; case std::errc::bad_message: return orbis::ErrorCode::BADMSG; case std::errc::broken_pipe: return orbis::ErrorCode::PIPE; case std::errc::connection_aborted: return orbis::ErrorCode::CONNABORTED; case std::errc::connection_already_in_progress: return orbis::ErrorCode::ALREADY; case std::errc::connection_refused: return orbis::ErrorCode::CONNREFUSED; case std::errc::connection_reset: return orbis::ErrorCode::CONNRESET; case std::errc::cross_device_link: return orbis::ErrorCode::XDEV; case std::errc::destination_address_required: return orbis::ErrorCode::DESTADDRREQ; case std::errc::device_or_resource_busy: return orbis::ErrorCode::BUSY; case std::errc::directory_not_empty: return orbis::ErrorCode::NOTEMPTY; case std::errc::executable_format_error: return orbis::ErrorCode::NOEXEC; case std::errc::file_exists: return orbis::ErrorCode::EXIST; case std::errc::file_too_large: return orbis::ErrorCode::FBIG; case std::errc::filename_too_long: return orbis::ErrorCode::NAMETOOLONG; case std::errc::function_not_supported: return orbis::ErrorCode::NOSYS; case std::errc::host_unreachable: return orbis::ErrorCode::HOSTUNREACH; case std::errc::identifier_removed: return orbis::ErrorCode::IDRM; case std::errc::illegal_byte_sequence: return orbis::ErrorCode::ILSEQ; case std::errc::inappropriate_io_control_operation: return orbis::ErrorCode::NOTTY; case std::errc::interrupted: return orbis::ErrorCode::INTR; case std::errc::invalid_argument: return orbis::ErrorCode::INVAL; case std::errc::invalid_seek: return orbis::ErrorCode::SPIPE; case std::errc::io_error: return orbis::ErrorCode::IO; case std::errc::is_a_directory: return orbis::ErrorCode::ISDIR; case std::errc::message_size: return orbis::ErrorCode::MSGSIZE; case std::errc::network_down: return orbis::ErrorCode::NETDOWN; case std::errc::network_reset: return orbis::ErrorCode::NETRESET; case std::errc::network_unreachable: return orbis::ErrorCode::NETUNREACH; case std::errc::no_buffer_space: return orbis::ErrorCode::NOBUFS; case std::errc::no_child_process: return orbis::ErrorCode::CHILD; case std::errc::no_link: return orbis::ErrorCode::NOLINK; case std::errc::no_lock_available: return orbis::ErrorCode::NOLCK; case std::errc::no_message: return orbis::ErrorCode::NOMSG; case std::errc::no_protocol_option: return orbis::ErrorCode::NOPROTOOPT; case std::errc::no_space_on_device: return orbis::ErrorCode::NOSPC; case std::errc::no_such_device_or_address: return orbis::ErrorCode::NXIO; case std::errc::no_such_device: return orbis::ErrorCode::NODEV; case std::errc::no_such_file_or_directory: return orbis::ErrorCode::NOENT; case std::errc::no_such_process: return orbis::ErrorCode::SRCH; case std::errc::not_a_directory: return orbis::ErrorCode::NOTDIR; case std::errc::not_a_socket: return orbis::ErrorCode::NOTSOCK; case std::errc::not_connected: return orbis::ErrorCode::NOTCONN; case std::errc::not_enough_memory: return orbis::ErrorCode::NOMEM; case std::errc::not_supported: return orbis::ErrorCode::NOTSUP; case std::errc::operation_canceled: return orbis::ErrorCode::CANCELED; case std::errc::operation_in_progress: return orbis::ErrorCode::INPROGRESS; case std::errc::operation_not_permitted: return orbis::ErrorCode::PERM; case std::errc::operation_would_block: return orbis::ErrorCode::WOULDBLOCK; case std::errc::permission_denied: return orbis::ErrorCode::ACCES; case std::errc::protocol_error: return orbis::ErrorCode::PROTO; case std::errc::protocol_not_supported: return orbis::ErrorCode::PROTONOSUPPORT; case std::errc::read_only_file_system: return orbis::ErrorCode::ROFS; case std::errc::resource_deadlock_would_occur: return orbis::ErrorCode::DEADLK; case std::errc::result_out_of_range: return orbis::ErrorCode::RANGE; case std::errc::text_file_busy: return orbis::ErrorCode::TXTBSY; case std::errc::timed_out: return orbis::ErrorCode::TIMEDOUT; case std::errc::too_many_files_open_in_system: return orbis::ErrorCode::NFILE; case std::errc::too_many_files_open: return orbis::ErrorCode::MFILE; case std::errc::too_many_links: return orbis::ErrorCode::MLINK; case std::errc::too_many_symbolic_link_levels: return orbis::ErrorCode::LOOP; case std::errc::value_too_large: return orbis::ErrorCode::OVERFLOW; case std::errc::wrong_protocol_type: return orbis::ErrorCode::PROTOTYPE; default: return orbis::ErrorCode::FAULT; } } static orbis::ErrorCode convertErrorCode(const std::error_code &code) { if (!code) { return {}; } return convertErrc(static_cast(code.value())); } orbis::ErrorCode convertErrno() { switch (auto error = errno) { case EPERM: return orbis::ErrorCode::PERM; case ENOENT: return orbis::ErrorCode::NOENT; case EBADF: return orbis::ErrorCode::BADF; case EIO: return orbis::ErrorCode::IO; case EACCES: return orbis::ErrorCode::ACCES; case EEXIST: return orbis::ErrorCode::EXIST; case EBUSY: return orbis::ErrorCode::BUSY; case ENOTDIR: return orbis::ErrorCode::NOTDIR; case EISDIR: return orbis::ErrorCode::ISDIR; case EFBIG: return orbis::ErrorCode::FBIG; case ENOSPC: return orbis::ErrorCode::NOSPC; case ESPIPE: return orbis::ErrorCode::SPIPE; case EPIPE: return orbis::ErrorCode::PIPE; case EINVAL: return orbis::ErrorCode::INVAL; default: ORBIS_LOG_ERROR("Unconverted errno", error); } return orbis::ErrorCode::IO; } static orbis::ErrorCode host_fd_read(int hostFd, orbis::Uio *uio) { std::vector vec; for (auto entry : std::span(uio->iov, uio->iovcnt)) { vec.push_back({.iov_base = entry.base, .iov_len = entry.len}); } auto cnt = ::preadv(hostFd, vec.data(), vec.size(), uio->offset); if (cnt < 0) { cnt = 0; for (auto io : vec) { auto result = ::read(hostFd, io.iov_base, io.iov_len); if (result < 0) { if (cnt == 0) { return convertErrno(); } break; } cnt += result; if (cnt != io.iov_len) { break; } } } uio->resid -= cnt; uio->offset += cnt; return {}; } static orbis::ErrorCode host_fd_write(int hostFd, orbis::Uio *uio) { std::vector vec; for (auto entry : std::span(uio->iov, uio->iovcnt)) { vec.push_back({.iov_base = entry.base, .iov_len = entry.len}); } ssize_t cnt = ::pwritev(hostFd, vec.data(), vec.size(), uio->offset); if (cnt < 0) { cnt = 0; for (auto io : vec) { auto result = ::write(hostFd, io.iov_base, io.iov_len); if (result < 0) { if (cnt == 0) { return convertErrno(); } break; } cnt += result; if (cnt != io.iov_len) { break; } } } uio->resid -= cnt; uio->offset += cnt; return {}; } static orbis::ErrorCode host_read(orbis::File *file, orbis::Uio *uio, orbis::Thread *) { auto hostFile = static_cast(file); if (!hostFile->dirEntries.empty()) return orbis::ErrorCode::ISDIR; return host_fd_read(hostFile->hostFd, uio); } static orbis::ErrorCode host_write(orbis::File *file, orbis::Uio *uio, orbis::Thread *) { auto hostFile = static_cast(file); if (!hostFile->dirEntries.empty()) return orbis::ErrorCode::ISDIR; return host_fd_write(hostFile->hostFd, uio); } static orbis::ErrorCode host_mmap(orbis::File *file, void **address, std::uint64_t size, std::int32_t prot, std::int32_t flags, std::int64_t offset, orbis::Thread *thread) { auto hostFile = static_cast(file); if (!hostFile->dirEntries.empty()) return orbis::ErrorCode::ISDIR; auto result = rx::vm::map(*address, size, prot, flags, rx::vm::kMapInternalReserveOnly, hostFile->device.cast().get(), offset); if (result == (void *)-1) { return orbis::ErrorCode::NOMEM; } size = rx::alignUp(size, rx::vm::kPageSize); result = ::mmap( result, size, prot & rx::vm::kMapProtCpuAll, ((flags & rx::vm::kMapFlagPrivate) != 0 ? MAP_PRIVATE : MAP_SHARED) | MAP_FIXED, hostFile->hostFd, offset); if (result == (void *)-1) { auto errc = convertErrno(); std::printf("Failed to map file at %p-%p\n", *address, (char *)*address + size); return errc; } std::printf("file mapped at %p-%p:%lx\n", result, (char *)result + size, offset); struct stat stat; fstat(hostFile->hostFd, &stat); if (stat.st_size < offset + size) { std::size_t rest = std::min(offset + size - stat.st_size, rx::vm::kPageSize); if (rest > rx::mem::pageSize) { auto fillSize = rx::alignUp(rest, rx::mem::pageSize) - rx::mem::pageSize; std::printf("adding dummy mapping %p-%p, file ends at %p\n", (char *)result + size - fillSize, (char *)result + size, (char *)result + (stat.st_size - offset)); auto ptr = ::mmap((char *)result + size - fillSize, fillSize, prot & rx::vm::kMapProtCpuAll, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if (ptr == (void *)-1) { std::printf("failed to add dummy mapping %p-%p\n", result, (char *)result + size); } } } *address = result; return {}; } static orbis::ErrorCode host_stat(orbis::File *file, orbis::Stat *sb, orbis::Thread *thread) { auto hostFile = static_cast(file); struct stat hostStat; ::fstat(hostFile->hostFd, &hostStat); sb->dev = hostStat.st_dev; // TODO sb->ino = hostStat.st_ino; sb->mode = hostStat.st_mode; sb->nlink = hostStat.st_nlink; sb->uid = hostStat.st_uid; sb->gid = hostStat.st_gid; sb->rdev = hostStat.st_rdev; sb->atim = { .sec = static_cast(hostStat.st_atim.tv_sec), .nsec = static_cast(hostStat.st_atim.tv_nsec), }; sb->mtim = { .sec = static_cast(hostStat.st_mtim.tv_sec), .nsec = static_cast(hostStat.st_mtim.tv_nsec), }; sb->ctim = { .sec = static_cast(hostStat.st_mtim.tv_sec), .nsec = static_cast(hostStat.st_mtim.tv_nsec), }; sb->size = hostStat.st_size; sb->blocks = hostStat.st_blocks; sb->blksize = hostStat.st_blksize; // TODO sb->flags = 0; sb->gen = 0; sb->lspare = 0; sb->birthtim = { .sec = static_cast(hostStat.st_mtim.tv_sec), .nsec = static_cast(hostStat.st_mtim.tv_nsec), }; return {}; } static orbis::ErrorCode host_truncate(orbis::File *file, std::uint64_t len, orbis::Thread *thread) { auto hostFile = static_cast(file); if (!hostFile->dirEntries.empty()) { return orbis::ErrorCode::ISDIR; } if (hostFile->alignTruncate) { len = rx::alignUp(len, rx::vm::kPageSize); } if (::ftruncate(hostFile->hostFd, len)) { return convertErrno(); } return {}; } static orbis::ErrorCode socket_ioctl(orbis::File *file, std::uint64_t request, void *argp, orbis::Thread *thread) { auto soc = static_cast(file); ORBIS_LOG_FATAL("Unhandled socket ioctl", request, soc->name); return {}; } static orbis::ErrorCode socket_read(orbis::File *file, orbis::Uio *uio, orbis::Thread *) { auto socket = static_cast(file); if (socket->hostFd < 0) { while (true) { std::this_thread::sleep_for(std::chrono::days(1)); } return orbis::ErrorCode::INVAL; } ORBIS_LOG_FATAL(__FUNCTION__, file, uio->iov->len); return host_fd_read(socket->hostFd, uio); } static orbis::ErrorCode socket_write(orbis::File *file, orbis::Uio *uio, orbis::Thread *) { auto socket = static_cast(file); if (socket->hostFd < 0) { for (auto io : std::span(uio->iov, uio->iovcnt)) { uio->offset += io.len; } return {}; } ORBIS_LOG_FATAL(__FUNCTION__, file, uio->iov->len); return host_fd_write(socket->hostFd, uio); } static orbis::ErrorCode socket_bind(orbis::File *file, orbis::SocketAddress *address, std::size_t addressLen, orbis::Thread *thread) { auto socket = static_cast(file); if (socket->hostFd < 0) { return {}; } if (address->family == 1) { auto vfsPath = std::string_view((const char *)address->data); auto [device, path] = rx::vfs::get(vfsPath); if (auto hostDev = device.cast()) { auto socketPath = std::filesystem::path(hostDev->hostPath); socketPath /= path; if (socketPath.native().size() >= sizeof(sockaddr_un::sun_path)) { return orbis::ErrorCode::NAMETOOLONG; } ORBIS_LOG_ERROR(__FUNCTION__, vfsPath, socketPath.native()); sockaddr_un un{.sun_family = AF_UNIX}; std::strncpy(un.sun_path, socketPath.c_str(), sizeof(un.sun_path)); if (::bind(socket->hostFd, reinterpret_cast<::sockaddr *>(&un), sizeof(un)) < 0) { return convertErrno(); } return {}; } } return orbis::ErrorCode::NOTSUP; } static orbis::ErrorCode socket_listen(orbis::File *file, int backlog, orbis::Thread *thread) { auto socket = static_cast(file); if (socket->hostFd < 0) { return {}; } if (::listen(socket->hostFd, backlog) < 0) { return convertErrno(); } return {}; } static orbis::ErrorCode socket_accept(orbis::File *file, orbis::SocketAddress *address, std::uint32_t *addressLen, orbis::Thread *thread) { auto socket = static_cast(file); if (socket->hostFd < 0) { ORBIS_LOG_ERROR(__FUNCTION__, socket->name, "wait forever"); while (true) { std::this_thread::sleep_for(std::chrono::days(1)); } } ORBIS_LOG_ERROR(__FUNCTION__, socket->name); if (socket->dom == 1 && socket->type == 1 && socket->prot == 0) { sockaddr_un un{.sun_family = AF_UNIX}; socklen_t len = sizeof(un); int result = ::accept(socket->hostFd, reinterpret_cast(&un), &len); if (result < 0) { return convertErrno(); } if (addressLen && address) { *addressLen = 2; } auto guestSocket = wrapSocket(result, "", 1, 1, 0); auto guestFd = thread->tproc->fileDescriptors.insert(guestSocket); thread->retval[0] = guestFd; ORBIS_LOG_ERROR(__FUNCTION__, socket->name, guestFd); return {}; } return orbis::ErrorCode::NOTSUP; } static orbis::ErrorCode socket_connect(orbis::File *file, orbis::SocketAddress *address, std::uint32_t addressLen, orbis::Thread *thread) { auto socket = static_cast(file); if (socket->hostFd < 0) { return orbis::ErrorCode::CONNREFUSED; } if (address->family == 1) { auto vfsPath = std::string_view((const char *)address->data); auto [device, path] = rx::vfs::get(vfsPath); if (auto hostDev = device.cast()) { auto socketPath = std::filesystem::path(hostDev->hostPath); socketPath /= path; ORBIS_LOG_ERROR(__FUNCTION__, vfsPath, socketPath.native()); if (socketPath.native().size() >= sizeof(sockaddr_un::sun_path)) { return orbis::ErrorCode::NAMETOOLONG; } sockaddr_un un{.sun_family = AF_UNIX}; std::strncpy(un.sun_path, socketPath.c_str(), sizeof(un.sun_path)); if (::connect(socket->hostFd, reinterpret_cast<::sockaddr *>(&un), sizeof(un)) < 0) { return convertErrno(); } return {}; } } return orbis::ErrorCode::NOTSUP; } orbis::ErrorCode socket_setsockopt(orbis::File *file, orbis::sint level, orbis::sint name, const void *val, orbis::sint valsize, orbis::Thread *thread) { ORBIS_LOG_ERROR(__FUNCTION__, level, name); auto socket = static_cast(file); orbis::kvector option(valsize); std::memcpy(option.data(), val, valsize); socket->options[name] = std::move(option); return {}; } orbis::ErrorCode socket_getsockopt(orbis::File *file, orbis::sint level, orbis::sint name, void *val, orbis::sint *avalsize, orbis::Thread *thread) { ORBIS_LOG_ERROR(__FUNCTION__, level, name); auto socket = static_cast(file); auto &option = socket->options[name]; if (option.size() > *avalsize) { return orbis::ErrorCode::INVAL; } *avalsize = option.size(); std::memcpy(val, option.data(), option.size()); return {}; } orbis::ErrorCode socket_recvfrom(orbis::File *file, void *buf, orbis::size_t len, orbis::sint flags, orbis::SocketAddress *from, orbis::uint32_t *fromlenaddr, orbis::Thread *thread) { auto socket = static_cast(file); if (socket->hostFd < 0) { return orbis::ErrorCode::CONNREFUSED; } if (socket->dom == 1 && socket->type == 1 && socket->prot == 0) { auto count = ::recvfrom(socket->hostFd, buf, len, flags, nullptr, nullptr); if (count < 0) { return convertErrno(); } thread->retval[0] = count; if (from && fromlenaddr) { from->len = 0; from->family = 1; *fromlenaddr = 2; } return {}; } return orbis::ErrorCode::NOTSUP; } static const orbis::FileOps hostOps = { .read = host_read, .write = host_write, .truncate = host_truncate, .stat = host_stat, .mmap = host_mmap, }; static const orbis::FileOps socketOps = { .ioctl = socket_ioctl, .read = socket_read, .write = socket_write, .bind = socket_bind, .listen = socket_listen, .accept = socket_accept, .connect = socket_connect, .recvfrom = socket_recvfrom, .setsockopt = socket_setsockopt, .getsockopt = socket_getsockopt, }; IoDevice *createHostIoDevice(orbis::kstring hostPath, orbis::kstring virtualPath) { while (hostPath.size() > 0 && hostPath.ends_with("/")) { hostPath.resize(hostPath.size() - 1); } return orbis::knew(std::move(hostPath), std::move(virtualPath)); } orbis::Ref wrapSocket(int hostFd, orbis::kstring name, int dom, int type, int prot) { auto s = orbis::knew(); s->name = std::move(name); s->dom = dom; s->type = type; s->prot = prot; s->hostFd = hostFd; s->ops = &socketOps; return s; } orbis::ErrorCode createSocket(orbis::Ref *file, orbis::kstring name, int dom, int type, int prot) { // ORBIS_LOG_ERROR(__FUNCTION__, name, dom, type, prot); // *file = orbis::createPipe(); // return {}; int fd = -1; if (dom == 1 && type == 1 && prot == 0) { fd = ::socket(dom, type, prot); if (fd < 0) { return convertErrno(); } } *file = wrapSocket(fd, std::move(name), dom, type, prot); return {}; } static std::optional findFileInDir(const std::filesystem::path &dir, const char *name) { for (auto entry : std::filesystem::directory_iterator(dir)) { auto entryName = entry.path().filename(); if (strcasecmp(entryName.c_str(), name) == 0) { return entryName; } } return {}; } static std::optional toRealPath(const std::filesystem::path &inp) { if (inp.empty()) { return {}; } std::filesystem::path result; for (auto elem : inp) { if (result.empty() || std::filesystem::exists(result / elem)) { result /= elem; continue; } auto icaseElem = findFileInDir(result, elem.c_str()); if (!icaseElem) { return {}; } result /= *icaseElem; } return result; } orbis::ErrorCode HostFsDevice::open(orbis::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, orbis::Thread *thread) { auto realPath = hostPath + "/" + path; int realFlags = flags & O_ACCMODE; flags &= ~O_ACCMODE; if ((flags & kOpenFlagAppend) != 0) { realFlags |= O_APPEND; flags &= ~kOpenFlagAppend; } if ((flags & kOpenFlagNonBlock) != 0) { realFlags |= O_NONBLOCK; flags &= ~kOpenFlagNonBlock; } if ((flags & kOpenFlagFsync) != 0) { realFlags |= O_FSYNC; flags &= ~kOpenFlagFsync; } if ((flags & kOpenFlagAsync) != 0) { realFlags |= O_ASYNC; flags &= ~kOpenFlagAsync; } if ((flags & kOpenFlagTrunc) != 0) { realFlags |= O_TRUNC; flags &= ~kOpenFlagTrunc; } if ((flags & kOpenFlagCreat) != 0) { realFlags |= O_CREAT; flags &= ~kOpenFlagCreat; } if ((flags & kOpenFlagExcl) != 0) { realFlags |= O_EXCL; flags &= ~kOpenFlagExcl; } if ((flags & kOpenFlagDirectory) != 0) { realFlags |= O_DIRECTORY; flags &= ~kOpenFlagDirectory; } if (flags != 0) { ORBIS_LOG_ERROR("host_open: ***ERROR*** Unhandled open flags", flags); } int hostFd = ::open(realPath.c_str(), realFlags, 0777); orbis::ErrorCode error{}; if (hostFd < 0) { error = convertErrno(); if (auto icaseRealPath = toRealPath(realPath)) { ORBIS_LOG_WARNING(__FUNCTION__, path, realPath.c_str(), icaseRealPath->c_str()); hostFd = ::open(icaseRealPath->c_str(), realFlags, 0777); if (hostFd < 0) { ORBIS_LOG_ERROR("host_open failed", path, realPath.c_str(), icaseRealPath->c_str(), error); return convertErrno(); } } } if (hostFd < 0) { ORBIS_LOG_ERROR("host_open failed", path, realPath.c_str(), error); return error; } // Assume the file is a directory and try to read direntries orbis::utils::kvector dirEntries; char hostEntryBuffer[sizeof(dirent64) * 4]; while (true) { auto r = getdents64(hostFd, hostEntryBuffer, sizeof(hostEntryBuffer)); if (r <= 0) break; std::size_t offset = 0; while (offset < r) { ::dirent64 *entryPtr = reinterpret_cast(hostEntryBuffer + offset); offset += entryPtr->d_reclen; if (entryPtr->d_name == std::string_view("..") || entryPtr->d_name == std::string_view(".") || entryPtr->d_name == std::string_view(".rpcsx")) { continue; } // auto entryPath = (std::filesystem::path(virtualPath) / path / // entryPtr->d_name).lexically_normal().string(); std::string entryPath = entryPtr->d_name; auto &entry = dirEntries.emplace_back(); entry.fileno = dirEntries.size(); // TODO entry.reclen = sizeof(entry); entry.namlen = entryPath.length(); std::strncpy(entry.name, entryPath.c_str(), sizeof(entry.name)); if (entryPtr->d_type == DT_REG) entry.type = orbis::kDtReg; else if (entryPtr->d_type == DT_DIR || entryPtr->d_type == DT_LNK) entry.type = orbis::kDtDir; // Assume symlinks to be dirs else { ORBIS_LOG_ERROR("host_open: unknown directory entry d_type", realPath.c_str(), entryPtr->d_name, entryPtr->d_type); dirEntries.pop_back(); } } } auto newFile = orbis::knew(); newFile->hostFd = hostFd; newFile->dirEntries = std::move(dirEntries); newFile->ops = &hostOps; newFile->device = this; *file = newFile; return {}; } orbis::ErrorCode HostFsDevice::unlink(const char *path, bool recursive, orbis::Thread *thread) { std::error_code ec; if (recursive) { std::filesystem::remove_all(hostPath + "/" + path, ec); } else { std::filesystem::remove(hostPath + "/" + path, ec); } return convertErrorCode(ec); } orbis::ErrorCode HostFsDevice::createSymlink(const char *linkPath, const char *target, orbis::Thread *thread) { std::error_code ec; std::filesystem::create_symlink( std::filesystem::absolute(hostPath + "/" + linkPath), hostPath + "/" + target, ec); return convertErrorCode(ec); } orbis::ErrorCode HostFsDevice::mkdir(const char *path, int mode, orbis::Thread *thread) { std::error_code ec; std::filesystem::create_directories(hostPath + "/" + path, ec); return convertErrorCode(ec); } orbis::ErrorCode HostFsDevice::rmdir(const char *path, orbis::Thread *thread) { std::error_code ec; std::filesystem::remove_all(hostPath + "/" + path, ec); return convertErrorCode(ec); } orbis::ErrorCode HostFsDevice::rename(const char *from, const char *to, orbis::Thread *thread) { std::error_code ec; std::filesystem::rename(hostPath + "/" + from, hostPath + "/" + to, ec); return convertErrorCode(ec); } orbis::File *createHostFile(int hostFd, orbis::Ref device, bool alignTruncate) { auto newFile = orbis::knew(); newFile->hostFd = hostFd; newFile->ops = &hostOps; newFile->device = device; newFile->alignTruncate = alignTruncate; return newFile; } struct FdWrapDevice : public IoDevice { int fd; orbis::ErrorCode open(orbis::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, orbis::Thread *thread) override { *file = createHostFile(fd, this); static_cast(file->get())->closeOnExit = false; return {}; } }; IoDevice *createFdWrapDevice(int fd) { auto result = orbis::knew(); result->fd = fd; return result; }