/* * \brief Noux syscall dispatcher * \author Norman Feske * \date 2011-02-14 */ /* * Copyright (C) 2011-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ /* Noux includes */ #include #include #include #include namespace Noux { /** * This function is used to generate inode values from the given * path using the FNV-1a algorithm. */ inline uint32_t hash_path(const char *path, size_t len) { const unsigned char * p = reinterpret_cast(path); uint32_t hash = 2166136261U; for (size_t i = 0; i < len; i++) { hash ^= p[i]; hash *= 16777619; } return hash; } }; bool Noux::Child::syscall(Noux::Session::Syscall sc) { if (_verbose.syscalls()) log("PID ", pid(), " -> SYSCALL ", Noux::Session::syscall_name(sc)); bool result = false; try { switch (sc) { case SYSCALL_WRITE: { Shared_pointer io = _lookup_channel(_sysio.write_in.fd); if (!io->nonblocking()) _block_for_io_channel(io, false, true, false); if (io->check_unblock(false, true, false)) { /* 'io->write' is expected to update '_sysio.write_out.count' */ result = io->write(_sysio); } else { if (io->nonblocking()) _sysio.error.write = Vfs::File_io_service::WRITE_ERR_WOULD_BLOCK; else _sysio.error.write = Vfs::File_io_service::WRITE_ERR_INTERRUPT; } break; } case SYSCALL_READ: { Shared_pointer io = _lookup_channel(_sysio.read_in.fd); if (!io->nonblocking()) _block_for_io_channel(io, true, false, false); if (io->check_unblock(true, false, false)) result = io->read(_sysio); else { if (io->nonblocking()) _sysio.error.read = Vfs::File_io_service::READ_ERR_WOULD_BLOCK; else _sysio.error.read = Vfs::File_io_service::READ_ERR_INTERRUPT; } break; } case SYSCALL_FTRUNCATE: { Shared_pointer io = _lookup_channel(_sysio.ftruncate_in.fd); _block_for_io_channel(io, false, true, false); if (io->check_unblock(false, true, false)) result = io->ftruncate(_sysio); else _sysio.error.ftruncate = Vfs::File_io_service::FTRUNCATE_ERR_INTERRUPT; break; } case SYSCALL_STAT: case SYSCALL_LSTAT: /* XXX implement difference between 'lstat' and 'stat' */ { /** * We calculate the inode by hashing the path because there is * no inode registry in noux. */ size_t path_len = strlen(_sysio.stat_in.path); uint32_t path_hash = hash_path(_sysio.stat_in.path, path_len); Vfs::Directory_service::Stat stat_out; _sysio.error.stat = _root_dir.stat(_sysio.stat_in.path, stat_out); result = (_sysio.error.stat == Vfs::Directory_service::STAT_OK); /* * Instead of using the uid/gid given by the actual file system * we use the ones specificed in the config. */ if (result) { stat_out.inode = path_hash; } _sysio.stat_out.st = stat_out; break; } case SYSCALL_FSTAT: { Shared_pointer io = _lookup_channel(_sysio.fstat_in.fd); result = io->fstat(_sysio); if (result) { Sysio::Path path; /** * Only actual fd's are valid fstat targets. */ if (io->path(path, sizeof (path))) { size_t path_len = strlen(path); uint32_t path_hash = hash_path(path, path_len); _sysio.stat_out.st.inode = path_hash; } } break; } case SYSCALL_FCNTL: if (_sysio.fcntl_in.cmd == Sysio::FCNTL_CMD_SET_FD_FLAGS) { /* we assume that there is only the close-on-execve flag */ close_fd_on_execve(_sysio.fcntl_in.fd, !!_sysio.fcntl_in.long_arg); result = true; break; } if (_sysio.fcntl_in.cmd == Sysio::FCNTL_CMD_GET_FD_FLAGS) { /* we assume that there is only the close-on-execve flag */ _sysio.fcntl_out.result = close_fd_on_execve(_sysio.fcntl_in.fd); result = true; break; } result = _lookup_channel(_sysio.fcntl_in.fd)->fcntl(_sysio); break; case SYSCALL_OPEN: { Vfs::Vfs_handle *vfs_handle = 0; if (_root_dir.directory(_sysio.open_in.path)) { typedef Vfs::Directory_service::Opendir_result Opendir_result; typedef Vfs::Directory_service::Open_result Open_result; Opendir_result opendir_result = _root_dir.opendir(_sysio.open_in.path, false, &vfs_handle, _heap); switch (opendir_result) { case Opendir_result::OPENDIR_OK: _sysio.error.open = Open_result::OPEN_OK; break; case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED: _sysio.error.open = Open_result::OPEN_ERR_UNACCESSIBLE; break; case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG: _sysio.error.open = Open_result::OPEN_ERR_NAME_TOO_LONG; break; case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS: _sysio.error.open = Open_result::OPEN_ERR_EXISTS; break; case Opendir_result::OPENDIR_ERR_NO_SPACE: _sysio.error.open = Open_result::OPEN_ERR_NO_SPACE; break; case Opendir_result::OPENDIR_ERR_OUT_OF_RAM: case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS: case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED: _sysio.error.open = Open_result::OPEN_ERR_NO_PERM; break; } } else { _sysio.error.open = _root_dir.open(_sysio.open_in.path, _sysio.open_in.mode, &vfs_handle, _heap); } if (!vfs_handle) break; char const *leaf_path = _root_dir.leaf_path(_sysio.open_in.path); /* * File descriptors of opened directories are handled by * '_root_dir'. In this case, we use the absolute path as leaf * path because path operations always refer to the global * root. */ if (&vfs_handle->ds() == &_root_dir) leaf_path = _sysio.open_in.path; Shared_pointer channel(new (_heap) Vfs_io_channel(_sysio.open_in.path, leaf_path, vfs_handle, _vfs_io_waiter_registry, _env.ep(), _time_info, _timer_connection), _heap); _sysio.open_out.fd = add_io_channel(channel); result = true; break; } case SYSCALL_CLOSE: { remove_io_channel(_sysio.close_in.fd); result = true; break; } case SYSCALL_IOCTL: result = _lookup_channel(_sysio.ioctl_in.fd)->ioctl(_sysio); break; case SYSCALL_LSEEK: result = _lookup_channel(_sysio.lseek_in.fd)->lseek(_sysio); break; case SYSCALL_DIRENT: result = _lookup_channel(_sysio.dirent_in.fd)->dirent(_sysio); break; case SYSCALL_EXECVE: typedef Child_env Execve_child_env; try { Execve_child_env child_env(_sysio.execve_in.filename, _sysio.execve_in.args, _sysio.execve_in.env, _root_dir, _vfs_io_waiter_registry, _env.ram(), _env.rm(), _heap); _parent_execve.execve_child(*this, child_env.binary_name(), child_env.args(), child_env.env()); /* * 'return' instead of 'break' to skip possible signal delivery, * which might cause the old child process to exit itself */ return true; } catch (Execve_child_env::Binary_does_not_exist) { _sysio.error.execve = Sysio::EXECVE_ERR_NO_ENTRY; } catch (Execve_child_env::Binary_is_not_accessible) { _sysio.error.execve = Sysio::EXECVE_ERR_ACCESS; } catch (Execve_child_env::Binary_is_not_executable) { _sysio.error.execve = Sysio::EXECVE_ERR_NO_EXEC; } catch (Child::Insufficient_memory) { _sysio.error.execve = Sysio::EXECVE_ERR_NO_MEMORY; } break; case SYSCALL_SELECT: { size_t in_fds_total = _sysio.select_in.fds.total_fds(); Sysio::Select_fds in_fds; for (Genode::size_t i = 0; i < in_fds_total; i++) in_fds.array[i] = _sysio.select_in.fds.array[i]; in_fds.num_rd = _sysio.select_in.fds.num_rd; in_fds.num_wr = _sysio.select_in.fds.num_wr; in_fds.num_ex = _sysio.select_in.fds.num_ex; int _rd_array[in_fds_total]; int _wr_array[in_fds_total]; Genode::uint64_t timeout_sec = _sysio.select_in.timeout.sec; Genode::uint64_t timeout_usec = _sysio.select_in.timeout.usec; bool timeout_reached = false; /* reset the blocker lock to the 'locked' state */ _blocker.unlock(); _blocker.lock(); /* * Register ourself at all watched I/O channels * * We instantiate as many notifiers as we have file * descriptors to observe. Each notifier is associated * with the child's blocking semaphore. When any of the * notifiers get woken up, the semaphore gets unblocked. * * XXX However, the blocker may get unblocked for other * conditions such as the destruction of the child. * ...to be done. */ Wake_up_notifier notifiers[in_fds_total]; for (Genode::size_t i = 0; i < in_fds_total; i++) { int fd = in_fds.array[i]; if (!fd_in_use(fd)) continue; Shared_pointer io = io_channel_by_fd(fd); notifiers[i].lock = &_blocker; io->register_wake_up_notifier(¬ifiers[i]); } /** * Register ourself at the Io_receptor_registry * * Each entry in the registry will be unblocked if an external * event has happend, e.g. network I/O. */ Io_receptor receptor(&_blocker); io_receptor_registry()->register_receptor(&receptor); /* * Block for one action of the watched file descriptors */ for (;;) { /* * Check I/O channels of specified file descriptors for * unblock condition. Return if one I/O channel satisfies * the condition. */ size_t unblock_rd = 0; size_t unblock_wr = 0; size_t unblock_ex = 0; /* process read fds */ for (Genode::size_t i = 0; i < in_fds_total; i++) { int fd = in_fds.array[i]; if (!fd_in_use(fd)) continue; Shared_pointer io = io_channel_by_fd(fd); if (in_fds.watch_for_rd(i)) if (io->check_unblock(true, false, false)) { _rd_array[unblock_rd++] = fd; } if (in_fds.watch_for_wr(i)) if (io->check_unblock(false, true, false)) { _wr_array[unblock_wr++] = fd; } if (in_fds.watch_for_ex(i)) if (io->check_unblock(false, false, true)) { unblock_ex++; } } if (unblock_rd || unblock_wr || unblock_ex) { /** * Merge the fd arrays in one output array */ for (size_t i = 0; i < unblock_rd; i++) { _sysio.select_out.fds.array[i] = _rd_array[i]; } _sysio.select_out.fds.num_rd = unblock_rd; /* XXX could use a pointer to select_out.fds.array instead */ for (size_t j = unblock_rd, i = 0; i < unblock_wr; i++, j++) { _sysio.select_out.fds.array[j] = _wr_array[i]; } _sysio.select_out.fds.num_wr = unblock_wr; /* exception fds are currently not considered */ _sysio.select_out.fds.num_ex = unblock_ex; result = true; break; } /* * Return if timeout is zero or timeout exceeded */ if (_sysio.select_in.timeout.zero() || timeout_reached) { /* if (timeout_reached) log("timeout_reached"); else log("timeout.zero()"); */ _sysio.select_out.fds.num_rd = 0; _sysio.select_out.fds.num_wr = 0; _sysio.select_out.fds.num_ex = 0; result = true; break; } /* * Return if signals are pending */ if (!_pending_signals.empty()) { _sysio.error.select = Sysio::SELECT_ERR_INTERRUPT; break; } /* * Block at barrier except when reaching the timeout */ if (!_sysio.select_in.timeout.infinite()) { Microseconds const us { timeout_sec*1000U*1000U + timeout_usec }; Armed_timeout::State ts { }; Armed_timeout timeout(ts, _blocker, _timer_connection, us); /* block until timeout is reached or we were unblocked */ _blocker.lock(); if (ts.timed_out) { timeout_reached = 1; } else { /* * We woke up before reaching the timeout, * so we discard the alarm */ timeout.discard(); } } else { /* let's block infinitely */ _blocker.lock(); } } /* * Unregister barrier at watched I/O channels */ for (Genode::size_t i = 0; i < in_fds_total; i++) { int fd = in_fds.array[i]; if (!fd_in_use(fd)) continue; Shared_pointer io = io_channel_by_fd(fd); io->unregister_wake_up_notifier(¬ifiers[i]); } /* * Unregister receptor */ io_receptor_registry()->unregister_receptor(&receptor); break; } case SYSCALL_FORK: { Genode::addr_t ip = _sysio.fork_in.ip; Genode::addr_t sp = _sysio.fork_in.sp; Genode::addr_t parent_cap_addr = _sysio.fork_in.parent_cap_addr; int const new_pid = _pid_allocator.alloc(); Child * child = nullptr; try { /* * XXX To ease debugging, it would be useful to generate a * unique name that includes the PID instead of just * reusing the name of the parent. */ child = new (_heap) Child(_child_policy.name(), _verbose, _user_info, _time_info, this, _kill_broadcaster, _timer_connection, *this, _pid_allocator, new_pid, _env, _root_dir, _vfs_io_waiter_registry, _args, _sysio_env.env(), _heap, _ref_pd, _ref_pd_cap, _parent_services, true, _destruct_queue); } catch (Child::Insufficient_memory) { _sysio.error.fork = Sysio::FORK_NOMEM; break; } Family_member::insert(child); _assign_io_channels_to(child, false); /* copy our address space into the new child */ try { _pd.replay(child->pd(), _env.rm(), _heap, child->ds_registry(), _ep); /* start executing the main thread of the new process */ child->start_forked_main_thread(ip, sp, parent_cap_addr); /* activate child entrypoint, thereby starting the new process */ child->start(); _sysio.fork_out.pid = new_pid; result = true; } catch (Region_map::Region_conflict) { error("region conflict while replaying the address space"); } break; } case SYSCALL_GETPID: { _sysio.getpid_out.pid = pid(); return true; } case SYSCALL_WAIT4: { Family_member *exited = _sysio.wait4_in.nohang ? poll4() : wait4(); if (exited) { _sysio.wait4_out.pid = exited->pid(); _sysio.wait4_out.status = exited->exit_status(); Family_member::remove(exited); static_cast(exited)->submit_exit_signal(); } else { if (_sysio.wait4_in.nohang) { _sysio.wait4_out.pid = 0; _sysio.wait4_out.status = 0; } else { _sysio.error.wait4 = Sysio::WAIT4_ERR_INTERRUPT; break; } } result = true; break; } case SYSCALL_PIPE: { Shared_pointer pipe (new (_heap) Pipe, _heap); Shared_pointer pipe_sink (new (_heap) Pipe_sink_io_channel (pipe, _env.ep()), _heap); Shared_pointer pipe_source(new (_heap) Pipe_source_io_channel(pipe, _env.ep()), _heap); _sysio.pipe_out.fd[0] = add_io_channel(pipe_source); _sysio.pipe_out.fd[1] = add_io_channel(pipe_sink); result = true; break; } case SYSCALL_DUP2: { int fd = add_io_channel(io_channel_by_fd(_sysio.dup2_in.fd), _sysio.dup2_in.to_fd); _sysio.dup2_out.fd = fd; result = true; break; } case SYSCALL_UNLINK: _sysio.error.unlink = _root_dir.unlink(_sysio.unlink_in.path); result = (_sysio.error.unlink == Vfs::Directory_service::UNLINK_OK); break; case SYSCALL_READLINK: { Vfs::Vfs_handle *symlink_handle { 0 }; Vfs::Directory_service::Openlink_result openlink_result = _root_dir.openlink(_sysio.readlink_in.path, false, &symlink_handle, _heap); switch (openlink_result) { case Vfs::Directory_service::OPENLINK_OK: result = true; break; case Vfs::Directory_service::OPENLINK_ERR_LOOKUP_FAILED: _sysio.error.readlink = Sysio::READLINK_ERR_NO_ENTRY; break; case Vfs::Directory_service::OPENLINK_ERR_NAME_TOO_LONG: case Vfs::Directory_service::OPENLINK_ERR_NODE_ALREADY_EXISTS: case Vfs::Directory_service::OPENLINK_ERR_NO_SPACE: case Vfs::Directory_service::OPENLINK_ERR_OUT_OF_RAM: case Vfs::Directory_service::OPENLINK_ERR_OUT_OF_CAPS: case Vfs::Directory_service::OPENLINK_ERR_PERMISSION_DENIED: _sysio.error.readlink = Sysio::READLINK_ERR_NO_PERM; } if (openlink_result != Vfs::Directory_service::OPENLINK_OK) break; Vfs::file_size count = min(_sysio.readlink_in.bufsiz, sizeof(_sysio.readlink_out.chunk)); Registered_no_delete vfs_io_waiter(_vfs_io_waiter_registry); while (!symlink_handle->fs().queue_read(symlink_handle, count)) vfs_io_waiter.wait_for_io(); Vfs_handle_context read_context; Vfs::Vfs_handle::Guard guard(symlink_handle); symlink_handle->handler(&read_context); Vfs::file_size out_count = 0; Vfs::File_io_service::Read_result read_result; for (;;) { read_result = symlink_handle->fs().complete_read(symlink_handle, _sysio.readlink_out.chunk, count, out_count); if (read_result != Vfs::File_io_service::READ_QUEUED) break; read_context.vfs_io_waiter.wait_for_io(); } /* wake up threads blocking for 'queue_*()' or 'write()' */ _vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) { r.wakeup(); }); _sysio.readlink_out.count = out_count; break; } case SYSCALL_RENAME: _sysio.error.rename = _root_dir.rename(_sysio.rename_in.from_path, _sysio.rename_in.to_path); result = (_sysio.error.rename == Vfs::Directory_service::RENAME_OK); break; case SYSCALL_MKDIR: { Vfs::Vfs_handle *dir_handle { 0 }; typedef Vfs::Directory_service::Opendir_result Opendir_result; Opendir_result opendir_result = _root_dir.opendir(_sysio.mkdir_in.path, true, &dir_handle, _heap); switch (opendir_result) { case Opendir_result::OPENDIR_OK: dir_handle->ds().close(dir_handle); result = true; break; case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED: _sysio.error.mkdir = Sysio::MKDIR_ERR_NO_ENTRY; break; case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG: _sysio.error.mkdir = Sysio::MKDIR_ERR_NAME_TOO_LONG; break; case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS: _sysio.error.mkdir = Sysio::MKDIR_ERR_EXISTS; break; case Opendir_result::OPENDIR_ERR_NO_SPACE: _sysio.error.mkdir = Sysio::MKDIR_ERR_NO_SPACE; break; case Opendir_result::OPENDIR_ERR_OUT_OF_RAM: case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS: case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED: _sysio.error.mkdir = Sysio::MKDIR_ERR_NO_PERM; break; } break; } case SYSCALL_SYMLINK: { Vfs::Vfs_handle *symlink_handle { 0 }; typedef Vfs::Directory_service::Openlink_result Openlink_result; Openlink_result openlink_result = _root_dir.openlink(_sysio.symlink_in.newpath, true, &symlink_handle, _heap); switch (openlink_result) { case Openlink_result::OPENLINK_OK: result = true; break; case Openlink_result::OPENLINK_ERR_LOOKUP_FAILED: _sysio.error.symlink = Sysio::SYMLINK_ERR_NO_ENTRY; break; case Openlink_result::OPENLINK_ERR_NAME_TOO_LONG: case Openlink_result::OPENLINK_ERR_NODE_ALREADY_EXISTS: case Openlink_result::OPENLINK_ERR_NO_SPACE: case Openlink_result::OPENLINK_ERR_OUT_OF_RAM: case Openlink_result::OPENLINK_ERR_OUT_OF_CAPS: case Openlink_result::OPENLINK_ERR_PERMISSION_DENIED: _sysio.error.symlink = Sysio::SYMLINK_ERR_NO_PERM; } if (openlink_result != Openlink_result::OPENLINK_OK) break; Vfs::file_size count = strlen(_sysio.symlink_in.oldpath) + 1; Vfs::file_size out_count; Registered_no_delete vfs_io_waiter(_vfs_io_waiter_registry); for (;;) { try { symlink_handle->fs().write(symlink_handle, _sysio.symlink_in.oldpath, strlen(_sysio.symlink_in.oldpath) + 1, out_count); break; } catch (Vfs::File_io_service::Insufficient_buffer) { vfs_io_waiter.wait_for_io(); } } /* wake up threads blocking for 'queue_*()' or 'write()' */ _vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) { r.wakeup(); }); if (out_count != count) { _sysio.error.symlink = Sysio::SYMLINK_ERR_NAME_TOO_LONG; result = false; } while (!symlink_handle->fs().queue_sync(symlink_handle)) vfs_io_waiter.wait_for_io(); Vfs_handle_context sync_context; Vfs::Vfs_handle::Guard guard(symlink_handle); symlink_handle->handler(&sync_context); while (symlink_handle->fs().complete_sync(symlink_handle) == Vfs::File_io_service::SYNC_QUEUED) sync_context.vfs_io_waiter.wait_for_io(); /* wake up threads blocking for 'queue_*()' or 'write()' */ _vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) { r.wakeup(); }); break; } case SYSCALL_USERINFO: { if (_sysio.userinfo_in.request == Sysio::USERINFO_GET_UID || _sysio.userinfo_in.request == Sysio::USERINFO_GET_GID) { _sysio.userinfo_out.uid = _user_info.uid(); _sysio.userinfo_out.gid = _user_info.gid(); result = true; break; } /* * Since NOUX supports exactly one user, return false if we * got a unknown uid. */ if (_sysio.userinfo_in.uid != _user_info.uid()) break; Genode::memcpy(_sysio.userinfo_out.name, _user_info.name().string(), sizeof(User_info::Name)); Genode::memcpy(_sysio.userinfo_out.shell, _user_info.shell().string(), sizeof(User_info::Shell)); Genode::memcpy(_sysio.userinfo_out.home, _user_info.home().string(), sizeof(User_info::Home)); _sysio.userinfo_out.uid = _user_info.uid(); _sysio.userinfo_out.gid = _user_info.gid(); result = true; break; } case SYSCALL_GETTIMEOFDAY: { Milliseconds const ms = _timer_connection.curr_time().trunc_to_plain_ms(); _sysio.gettimeofday_out.sec = _time_info.initial_time(); _sysio.gettimeofday_out.sec += (ms.value / 1000); _sysio.gettimeofday_out.usec = (ms.value % 1000) * 1000; result = true; break; } case SYSCALL_CLOCK_GETTIME: { /** * It's the same procedure as in SYSCALL_GETTIMEOFDAY. */ Milliseconds const ms = _timer_connection.curr_time().trunc_to_plain_ms(); switch (_sysio.clock_gettime_in.clock_id) { /* CLOCK_SECOND is used by time(3) in the libc. */ case Sysio::CLOCK_ID_SECOND: { _sysio.clock_gettime_out.sec = _time_info.initial_time(); _sysio.clock_gettime_out.sec += (ms.value / 1000); _sysio.clock_gettime_out.nsec = 0; result = true; break; } default: { _sysio.clock_gettime_out.sec = 0; _sysio.clock_gettime_out.nsec = 0; _sysio.error.clock = Sysio::CLOCK_ERR_INVALID; break; } } break; } case SYSCALL_UTIMES: { /** * Always return true, even if 'update_modification_timestamp' * failed to keep programs, e.g. make, happy. */ result = true; char const *path = (char const *)_sysio.utimes_in.path; unsigned long sec = _sysio.utimes_in.sec; unsigned long usec = _sysio.utimes_in.usec; (void)usec; Vfs::Vfs_handle *vfs_handle = 0; _root_dir.open(path, 0, &vfs_handle, _heap); if (!vfs_handle) { break; } if (!sec) { Milliseconds const ms = _timer_connection.curr_time().trunc_to_plain_ms(); sec = _time_info.initial_time(); sec += (ms.value / 1000); usec = (ms.value % 1000) * 1000; } Registered_no_delete vfs_io_waiter(_vfs_io_waiter_registry); Vfs::Timestamp ts { .value = (long long)sec }; for (;;) { if (vfs_handle->fs().update_modification_timestamp(vfs_handle, ts)) { break; } else { vfs_io_waiter.wait_for_io(); } } _root_dir.close(vfs_handle); break; } case SYSCALL_SYNC: { /* no errors supported at this time */ result = true; Vfs::Vfs_handle *sync_handle; Vfs::Directory_service::Opendir_result opendir_result = _root_dir.opendir("/", false, &sync_handle, _heap); if (opendir_result != Vfs::Directory_service::OPENDIR_OK) break; Registered_no_delete vfs_io_waiter(_vfs_io_waiter_registry); while (!sync_handle->fs().queue_sync(sync_handle)) vfs_io_waiter.wait_for_io(); Vfs_handle_context sync_context; Vfs::Vfs_handle::Guard guard(sync_handle); sync_handle->handler(&sync_context); while (sync_handle->fs().complete_sync(sync_handle) == Vfs::File_io_service::SYNC_QUEUED) sync_context.vfs_io_waiter.wait_for_io(); /* wake up threads blocking for 'queue_*()' or 'write()' */ _vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) { r.wakeup(); }); break; } case SYSCALL_KILL: { if (_kill_broadcaster.kill(_sysio.kill_in.pid, _sysio.kill_in.sig)) result = true; else _sysio.error.kill = Sysio::KILL_ERR_SRCH; break; } case SYSCALL_GETDTABLESIZE: { _sysio.getdtablesize_out.n = Noux::File_descriptor_registry::MAX_FILE_DESCRIPTORS; result = true; break; } case SYSCALL_SOCKET: case SYSCALL_GETSOCKOPT: case SYSCALL_SETSOCKOPT: case SYSCALL_ACCEPT: case SYSCALL_BIND: case SYSCALL_LISTEN: case SYSCALL_SEND: case SYSCALL_SENDTO: case SYSCALL_RECV: case SYSCALL_RECVFROM: case SYSCALL_GETPEERNAME: case SYSCALL_SHUTDOWN: case SYSCALL_CONNECT: /* network support was removed */ result = false; break; case SYSCALL_INVALID: break; } } catch (Invalid_fd) { _sysio.error.general = Vfs::Directory_service::ERR_FD_INVALID; error("invalid file descriptor"); } catch (...) { error("unexpected exception"); } /* handle signals which might have occured */ while (!_pending_signals.empty() && (_sysio.pending_signals.avail_capacity() > 0)) { _sysio.pending_signals.add(_pending_signals.get()); } return result; }