diff --git a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc index 871e9f13ff..94b00b733d 100644 --- a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc +++ b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc @@ -163,9 +163,11 @@ namespace Vfs { class Lxip_address_file; class Lxip_vfs_handle; + class Lxip_vfs_file_handle; + class Lxip_vfs_dir_handle; class Lxip_file_system; - typedef Genode::List > Lxip_vfs_handles; + typedef Genode::List > Lxip_vfs_file_handles; } @@ -173,20 +175,6 @@ namespace Vfs { ** Vfs nodes ** ***************/ -struct Vfs::Lxip_vfs_handle final : Vfs::Vfs_handle -{ - Vfs::File &file; - - List_element file_le { this }; - List_element polling_le { this }; - - Lxip_vfs_handle(Vfs::File_system &fs, Allocator &alloc, int status_flags, - Vfs::File &file); - - ~Lxip_vfs_handle(); -}; - - struct Vfs::Node { char const *_name; @@ -196,21 +184,8 @@ struct Vfs::Node virtual ~Node() { } virtual char const *name() { return _name; } -}; - -struct Vfs::File : Vfs::Node -{ - Lxip_vfs_handles handles; - - File(char const *name) : Node(name) { } - - virtual ~File() { } - - /** - * Read or write operation would block exception - */ - struct Would_block { }; + virtual void close() { } /** * Pass len data to handle read callback @@ -229,6 +204,21 @@ struct Vfs::File : Vfs::Node Genode::error("lxip: write to read-only handle"); return -1; } +}; + + +struct Vfs::File : Vfs::Node +{ + Lxip_vfs_file_handles handles; + + File(char const *name) : Node(name) { } + + virtual ~File() { } + + /** + * Read or write operation would block exception + */ + struct Would_block { }; /** * Check for data to read or write @@ -237,43 +227,6 @@ struct Vfs::File : Vfs::Node }; -Vfs::Lxip_vfs_handle::Lxip_vfs_handle(Vfs::File_system &fs, - Allocator &alloc, - int status_flags, - Vfs::File &file) -: - Vfs_handle(fs, fs, alloc, status_flags), file(file) -{ - file.handles.insert(&file_le); -} - - -Vfs::Lxip_vfs_handle::~Lxip_vfs_handle() -{ - file.handles.remove(&file_le); -} - - -/** - * List of open handles to potentially poll - * - * Could be a dynamic queue, but this works for now. - */ -static Vfs::Lxip_vfs_handles _polling_handles; - -static void poll_all() -{ - using namespace Linux; - - for (Genode::List_element *le = _polling_handles.first(); - le; le = le->next()) - { - Vfs::Lxip_vfs_handle *handle = le->object(); - handle->file.poll(true, handle->context); - } -} - - struct Vfs::Directory : Vfs::Node { Directory(char const *name) : Node(name) { } @@ -281,11 +234,72 @@ struct Vfs::Directory : Vfs::Node virtual ~Directory() { }; virtual Vfs::Node *child(char const *) = 0; - virtual void dirent(file_offset, Directory_service::Dirent &) = 0; virtual file_size num_dirent() = 0; }; +struct Vfs::Lxip_vfs_handle : Vfs::Vfs_handle +{ + Node &node; + + Lxip_vfs_handle(Vfs::File_system &fs, Allocator &alloc, int status_flags, + Vfs::Node &node) + : Vfs::Vfs_handle(fs, fs, alloc, status_flags), node(node) { } +}; + + +struct Vfs::Lxip_vfs_file_handle final : Vfs::Lxip_vfs_handle +{ + Vfs::File &file; + + List_element file_le { this }; + List_element polling_le { this }; + + Lxip_vfs_file_handle(Vfs::File_system &fs, Allocator &alloc, int status_flags, + Vfs::File &file) + : Lxip_vfs_handle(fs, alloc, status_flags, file), file(file) + { + file.handles.insert(&file_le); + } + + ~Lxip_vfs_file_handle() + { + file.handles.remove(&file_le); + } +}; + + +struct Vfs::Lxip_vfs_dir_handle final : Vfs::Lxip_vfs_handle +{ + Vfs::Directory &dir; + + Lxip_vfs_dir_handle(Vfs::File_system &fs, Allocator &alloc, int status_flags, + Vfs::Directory &dir) + : Vfs::Lxip_vfs_handle(fs, alloc, status_flags, dir), + dir(dir) { } +}; + + +/** + * List of open handles to potentially poll + * + * Could be a dynamic queue, but this works for now. + */ +static Vfs::Lxip_vfs_file_handles _polling_handles; + +static void poll_all() +{ + using namespace Linux; + + for (Genode::List_element *le = _polling_handles.first(); + le; le = le->next()) + { + Vfs::Lxip_vfs_file_handle *handle = le->object(); + handle->file.poll(true, handle->context); + } +} + + /***************************** ** Lxip vfs specific nodes ** *****************************/ @@ -308,7 +322,7 @@ class Vfs::Lxip_file : public Vfs::File void dissolve_handles() { - for (Genode::List_element *le = handles.first(); + for (Genode::List_element *le = handles.first(); le; le = le->next()) { _polling_handles.remove(&le->object()->polling_le); @@ -914,11 +928,23 @@ class Vfs::Lxip_socket_dir final : public Vfs::Directory, return nullptr; } - void dirent(file_offset index, Directory_service::Dirent &out) override + file_size num_dirent() override { return _num_nodes(); } + + Lxip::ssize_t read(char *dst, Genode::size_t len, + file_size seek_offset) override { - out.fileno = index+1; - out.type = Directory_service::DIRENT_TYPE_END; - out.name[0] = '\0'; + typedef Vfs::Directory_service::Dirent Dirent; + + if (len < sizeof(Dirent)) + return -1; + + Vfs::file_size index = seek_offset / sizeof(Dirent); + + Dirent *out = (Dirent*)dst; + + out->fileno = index+1; + out->type = Directory_service::DIRENT_TYPE_END; + out->name[0] = '\0'; Vfs::Node *node = nullptr; for (Vfs::Node *n : _nodes) { @@ -930,14 +956,14 @@ class Vfs::Lxip_socket_dir final : public Vfs::Directory, --index; } } - if (!node) return; + if (!node) return -1; - out.type = Directory_service::DIRENT_TYPE_FILE; + out->type = Directory_service::DIRENT_TYPE_FILE; - strncpy(out.name, node->name(), sizeof(out.name)); + strncpy(out->name, node->name(), sizeof(out->name)); + + return sizeof(Dirent); } - - file_size num_dirent() override { return _num_nodes(); } }; @@ -1138,11 +1164,23 @@ class Lxip::Protocol_dir_impl : public Protocol_dir, ** Directory interface ** *************************/ - void dirent(Vfs::file_offset index, Vfs::Directory_service::Dirent &out) override + Vfs::file_size num_dirent() override { return _num_nodes(); } + + Lxip::ssize_t read(char *dst, Genode::size_t len, + Vfs::file_size seek_offset) override { - out.fileno = index+1; - out.type = Vfs::Directory_service::DIRENT_TYPE_END; - out.name[0] = '\0'; + typedef Vfs::Directory_service::Dirent Dirent; + + if (len < sizeof(Dirent)) + return -1; + + Vfs::file_size index = seek_offset / sizeof(Dirent); + + Dirent *out = (Dirent*)dst; + + out->fileno = index+1; + out->type = Vfs::Directory_service::DIRENT_TYPE_END; + out->name[0] = '\0'; Vfs::Node *node = nullptr; for (Vfs::Node *n : _nodes) { @@ -1154,19 +1192,19 @@ class Lxip::Protocol_dir_impl : public Protocol_dir, --index; } } - if (!node) return; + if (!node) return -1; if (dynamic_cast(node)) - out.type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY; + out->type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY; if (dynamic_cast(node)) - out.type = Vfs::Directory_service::DIRENT_TYPE_FILE; + out->type = Vfs::Directory_service::DIRENT_TYPE_FILE; - Genode::strncpy(out.name, node->name(), sizeof(out.name)); + Genode::strncpy(out->name, node->name(), sizeof(out->name)); + + return sizeof(Dirent); } - Vfs::file_size num_dirent() override { return _num_nodes(); } - Vfs::Node *child(char const *name) override { return nullptr; } }; @@ -1273,14 +1311,14 @@ class Vfs::Lxip_file_system : public Vfs::File_system, Vfs::file_size count, Vfs::file_size &out_count) { - Vfs::File &file = - static_cast(vfs_handle)->file; + Vfs::Node &node = + static_cast(vfs_handle)->node; if (!count) - Genode::error("zero read of ", file.name()); + Genode::error("zero read of ", node.name()); if (count) { - Lxip::ssize_t res = file.read(dst, count, vfs_handle->seek()); + Lxip::ssize_t res = node.read(dst, count, vfs_handle->seek()); if (res < 0) return READ_ERR_IO; out_count = res; @@ -1357,41 +1395,51 @@ class Vfs::Lxip_file_system : public Vfs::File_system, ** Directory interface ** *************************/ - void dirent(file_offset index, Directory_service::Dirent &out) override - { - if (index == 0) { - out.fileno = (Genode::addr_t)&_tcp_dir; - out.type = Directory_service::DIRENT_TYPE_DIRECTORY; - Genode::strncpy(out.name, "tcp", sizeof(out.name)); - } else if (index == 1) { - out.fileno = (Genode::addr_t)&_udp_dir; - out.type = Directory_service::DIRENT_TYPE_DIRECTORY; - Genode::strncpy(out.name, "udp", sizeof(out.name)); - } else if (index == 2) { - out.fileno = (Genode::addr_t)&_address; - out.type = Directory_service::DIRENT_TYPE_FILE; - Genode::strncpy(out.name, "address", sizeof(out.name)); - } else if (index == 3) { - out.fileno = (Genode::addr_t)&_netmask; - out.type = Directory_service::DIRENT_TYPE_FILE; - Genode::strncpy(out.name, "netmask", sizeof(out.name)); - } else if (index == 4) { - out.fileno = (Genode::addr_t)&_gateway; - out.type = Directory_service::DIRENT_TYPE_FILE; - Genode::strncpy(out.name, "gateway", sizeof(out.name)); - } else if (index == 5) { - out.fileno = (Genode::addr_t)&_nameserver; - out.type = Directory_service::DIRENT_TYPE_FILE; - Genode::strncpy(out.name, "nameserver", sizeof(out.name)); - } else { - out.fileno = 0; - out.type = Directory_service::DIRENT_TYPE_END; - out.name[0] = '\0'; - } - } - file_size num_dirent() override { return 6; } + Lxip::ssize_t read(char *dst, Genode::size_t len, + file_size seek_offset) override + { + if (len < sizeof(Dirent)) + return -1; + + file_size index = seek_offset / sizeof(Dirent); + + Dirent *out = (Dirent*)dst; + + if (index == 0) { + out->fileno = (Genode::addr_t)&_tcp_dir; + out->type = Directory_service::DIRENT_TYPE_DIRECTORY; + Genode::strncpy(out->name, "tcp", sizeof(out->name)); + } else if (index == 1) { + out->fileno = (Genode::addr_t)&_udp_dir; + out->type = Directory_service::DIRENT_TYPE_DIRECTORY; + Genode::strncpy(out->name, "udp", sizeof(out->name)); + } else if (index == 2) { + out->fileno = (Genode::addr_t)&_address; + out->type = Directory_service::DIRENT_TYPE_FILE; + Genode::strncpy(out->name, "address", sizeof(out->name)); + } else if (index == 3) { + out->fileno = (Genode::addr_t)&_netmask; + out->type = Directory_service::DIRENT_TYPE_FILE; + Genode::strncpy(out->name, "netmask", sizeof(out->name)); + } else if (index == 4) { + out->fileno = (Genode::addr_t)&_gateway; + out->type = Directory_service::DIRENT_TYPE_FILE; + Genode::strncpy(out->name, "gateway", sizeof(out->name)); + } else if (index == 5) { + out->fileno = (Genode::addr_t)&_nameserver; + out->type = Directory_service::DIRENT_TYPE_FILE; + Genode::strncpy(out->name, "nameserver", sizeof(out->name)); + } else { + out->fileno = 0; + out->type = Directory_service::DIRENT_TYPE_END; + out->name[0] = '\0'; + } + + return sizeof(Dirent); + } + Vfs::Node *child(char const *name) override { return nullptr; } /********************************* @@ -1429,20 +1477,6 @@ class Vfs::Lxip_file_system : public Vfs::File_system, return STAT_ERR_NO_ENTRY; } - Dirent_result dirent(char const *path, file_offset index, Dirent &out) override - { - Vfs::Node *node = _lookup(path); - if (!node) return DIRENT_ERR_INVALID_PATH; - - Vfs::Directory *dir = dynamic_cast(node); - if (dir) { - dir->dirent(index, out); - return DIRENT_OK; - } - - return DIRENT_ERR_INVALID_PATH; - } - file_size num_dirent(char const *path) override { if (_is_root(path)) return num_dirent(); @@ -1478,8 +1512,8 @@ class Vfs::Lxip_file_system : public Vfs::File_system, Vfs::File *file = dynamic_cast(node); if (file) { - Lxip_vfs_handle *handle = - new (alloc) Vfs::Lxip_vfs_handle(*this, alloc, 0, *file); + Lxip_vfs_file_handle *handle = + new (alloc) Vfs::Lxip_vfs_file_handle(*this, alloc, 0, *file); *out_handle = handle; return OPEN_OK; @@ -1488,14 +1522,40 @@ class Vfs::Lxip_file_system : public Vfs::File_system, return OPEN_ERR_UNACCESSIBLE; } + Opendir_result opendir(char const *path, bool create, + Vfs_handle **out_handle, Allocator &alloc) override + { + Vfs::Node *node = _lookup(path); + + if (!node) return OPENDIR_ERR_LOOKUP_FAILED; + + Vfs::Directory *dir = dynamic_cast(node); + if (dir) { + Lxip_vfs_dir_handle *handle = + new (alloc) Vfs::Lxip_vfs_dir_handle(*this, alloc, 0, *dir); + *out_handle = handle; + + return OPENDIR_OK; + } + + return OPENDIR_ERR_LOOKUP_FAILED; + } + void close(Vfs_handle *vfs_handle) override { Lxip_vfs_handle *handle = static_cast(vfs_handle); - if (handle) { - _polling_handles.remove(&handle->polling_le); - Genode::destroy(handle->alloc(), handle); - } + + if (!handle) + return; + + Lxip_vfs_file_handle *file_handle = + dynamic_cast(handle); + + if (file_handle) + _polling_handles.remove(&file_handle->polling_le); + + Genode::destroy(handle->alloc(), handle); } Unlink_result unlink(char const *path) override @@ -1509,19 +1569,9 @@ class Vfs::Lxip_file_system : public Vfs::File_system, return UNLINK_ERR_NO_ENTRY; } - Readlink_result readlink(char const *, char *, file_size, - file_size &) override { - return READLINK_ERR_NO_ENTRY; } - Rename_result rename(char const *, char const *) override { return RENAME_ERR_NO_PERM; } - Mkdir_result mkdir(char const *, unsigned) override { - return MKDIR_ERR_NO_PERM; } - - Symlink_result symlink(char const *, char const *) override { - return SYMLINK_ERR_NO_ENTRY; } - /************************************* ** Lxip_file I/O service interface ** *************************************/ @@ -1531,7 +1581,7 @@ class Vfs::Lxip_file_system : public Vfs::File_system, file_size &out_count) override { Vfs::File &file = - static_cast(vfs_handle)->file; + static_cast(vfs_handle)->file; if (!count) Genode::error("zero write of ",file.name()); @@ -1546,25 +1596,9 @@ class Vfs::Lxip_file_system : public Vfs::File_system, return WRITE_OK; } - Read_result read(Vfs::Vfs_handle *vfs_handle, char *dst, - Vfs::file_size count, - Vfs::file_size &out_count) override - { - try { return _read(vfs_handle, dst, count, out_count); } - catch (File::Would_block) { return READ_ERR_WOULD_BLOCK; } - } - - bool queue_read(Vfs_handle *vfs_handle, char *dst, file_size count, - Read_result &out_result, file_size &out_count) - { - try { out_result = _read(vfs_handle, dst, count, out_count); } - catch (File::Would_block) { out_result = READ_QUEUED; } - return true; - } - Read_result complete_read(Vfs_handle *vfs_handle, - char *dst, file_size count, - file_size &out_count) + char *dst, file_size count, + file_size &out_count) override { try { return _read(vfs_handle, dst, count, out_count); } catch (File::Would_block) { return READ_QUEUED; } @@ -1578,8 +1612,8 @@ class Vfs::Lxip_file_system : public Vfs::File_system, bool notify_read_ready(Vfs_handle *vfs_handle) override { - Lxip_vfs_handle *handle = - static_cast(vfs_handle); + Lxip_vfs_file_handle *handle = + static_cast(vfs_handle); if (dynamic_cast(&handle->file)) { _polling_handles.remove(&handle->polling_le); @@ -1591,8 +1625,8 @@ class Vfs::Lxip_file_system : public Vfs::File_system, bool read_ready(Vfs_handle *vfs_handle) override { - Lxip_vfs_handle *handle = - static_cast(vfs_handle); + Lxip_vfs_file_handle *handle = + static_cast(vfs_handle); return handle->file.poll(false, nullptr); } diff --git a/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc b/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc index ca27930893..76e2cc12a4 100644 --- a/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc +++ b/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc @@ -59,7 +59,29 @@ class Vfs::Rump_file_system : public File_system typedef Genode::Path Path; - class Rump_vfs_handle : public Vfs_handle + Genode::Env &_env; + + struct Rump_vfs_handle : public Vfs_handle + { + using Vfs_handle::Vfs_handle; + + virtual Read_result read(char *buf, file_size buf_size, + file_size seek_offset, file_size &out_count) + { + Genode::error("Rump_vfs_handle::read() called"); + return READ_ERR_INVALID; + } + + virtual Write_result write(char const *buf, file_size buf_size, + file_size seek_offset, + file_size &out_count) + { + Genode::error("Rump_vfs_handle::write() called"); + return WRITE_ERR_INVALID; + } + }; + + class Rump_vfs_file_handle : public Rump_vfs_handle { private: @@ -67,13 +89,197 @@ class Vfs::Rump_file_system : public File_system public: - Rump_vfs_handle(File_system &fs, Allocator &alloc, - int status_flags, int fd) - : Vfs_handle(fs, fs, alloc, status_flags), _fd(fd) { } + Rump_vfs_file_handle(File_system &fs, Allocator &alloc, + int status_flags, int fd) + : Rump_vfs_handle(fs, fs, alloc, status_flags), _fd(fd) { } - ~Rump_vfs_handle() { rump_sys_close(_fd); } + ~Rump_vfs_file_handle() { rump_sys_close(_fd); } - int fd() const { return _fd; } + Ftruncate_result ftruncate(file_size len) + { + if (rump_sys_ftruncate(_fd, len) != 0) switch (errno) { + case EACCES: return FTRUNCATE_ERR_NO_PERM; + case EINTR: return FTRUNCATE_ERR_INTERRUPT; + case ENOSPC: return FTRUNCATE_ERR_NO_SPACE; + default: + return FTRUNCATE_ERR_NO_PERM; + } + return FTRUNCATE_OK; + } + + Read_result read(char *buf, file_size buf_size, + file_size seek_offset, file_size &out_count) override + { + ssize_t n = rump_sys_pread(_fd, buf, buf_size, seek_offset); + if (n == -1) switch (errno) { + case EWOULDBLOCK: return READ_ERR_WOULD_BLOCK; + case EINVAL: return READ_ERR_INVALID; + case EIO: return READ_ERR_IO; + case EINTR: return READ_ERR_INTERRUPT; + default: + return READ_ERR_IO; + } + out_count = n; + return READ_OK; + } + + Write_result write(char const *buf, file_size buf_size, + file_size seek_offset, + file_size &out_count) override + { + out_count = 0; + + ssize_t n = rump_sys_pwrite(_fd, buf, buf_size, seek_offset); + if (n == -1) switch (errno) { + case EWOULDBLOCK: return WRITE_ERR_WOULD_BLOCK; + case EINVAL: return WRITE_ERR_INVALID; + case EIO: return WRITE_ERR_IO; + case EINTR: return WRITE_ERR_INTERRUPT; + default: + return WRITE_ERR_IO; + } + out_count = n; + return WRITE_OK; + } + }; + + class Rump_vfs_dir_handle : public Rump_vfs_handle + { + private: + + int _fd; + Path _path; + + Read_result _finish_read(char const *path, + struct ::dirent *dent, Dirent &vfs_dir) + { + /* + * We cannot use 'd_type' member of 'dirent' here since the EXT2 + * implementation sets the type to unkown. Hence we use stat. + */ + struct stat s; + rump_sys_lstat(path, &s); + + if (S_ISREG(s.st_mode)) + vfs_dir.type = Dirent_type::DIRENT_TYPE_FILE; + else if (S_ISDIR(s.st_mode)) + vfs_dir.type = Dirent_type::DIRENT_TYPE_DIRECTORY; + else if (S_ISLNK(s.st_mode)) + vfs_dir.type = Dirent_type::DIRENT_TYPE_SYMLINK; + else if (S_ISBLK(s.st_mode)) + vfs_dir.type = Dirent_type::DIRENT_TYPE_BLOCKDEV; + else if (S_ISCHR(s.st_mode)) + vfs_dir.type = Dirent_type::DIRENT_TYPE_CHARDEV; + else if (S_ISFIFO(s.st_mode)) + vfs_dir.type = Dirent_type::DIRENT_TYPE_FIFO; + else + vfs_dir.type = Dirent_type::DIRENT_TYPE_FILE; + + strncpy(vfs_dir.name, dent->d_name, sizeof(Dirent::name)); + + return READ_OK; + } + + public: + + Rump_vfs_dir_handle(File_system &fs, Allocator &alloc, + int status_flags, int fd, char const *path) + : Rump_vfs_handle(fs, fs, alloc, status_flags), + _fd(fd), + _path(path) { } + + ~Rump_vfs_dir_handle() { rump_sys_close(_fd); } + + Read_result read(char *dst, file_size count, + file_size seek_offset, + file_size &out_count) override + { + out_count = 0; + + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + file_size index = seek_offset / sizeof(Dirent); + + Dirent *vfs_dir = (Dirent*)dst; + + out_count = sizeof(Dirent); + + rump_sys_lseek(_fd, 0, SEEK_SET); + + int bytes; + vfs_dir->fileno = 0; + char *buf = _buffer(); + struct ::dirent *dent = nullptr; + do { + bytes = rump_sys_getdents(_fd, buf, BUFFER_SIZE); + void *current, *end; + for (current = buf, end = &buf[bytes]; + current < end; + current = _DIRENT_NEXT((::dirent *)current)) + { + dent = (::dirent *)current; + if (strcmp(".", dent->d_name) && strcmp("..", dent->d_name)) { + if (vfs_dir->fileno++ == index) { + Path newpath(dent->d_name, _path.base()); + return _finish_read(newpath.base(), dent, *vfs_dir); + } + } + } + } while (bytes > 0); + + vfs_dir->type = DIRENT_TYPE_END; + vfs_dir->name[0] = '\0'; + return READ_OK; + } + }; + + class Rump_vfs_symlink_handle : public Rump_vfs_handle + { + private: + + Path _path; + + public: + + Rump_vfs_symlink_handle(File_system &fs, Allocator &alloc, + int status_flags, char const *path) + : Rump_vfs_handle(fs, fs, alloc, status_flags), _path(path) { } + + Read_result read(char *buf, file_size buf_size, + file_size seek_offset, + file_size &out_count) override + { + out_count = 0; + + if (seek_offset != 0) { + /* partial read is not supported */ + return READ_ERR_INVALID; + } + + ssize_t n = rump_sys_readlink(_path.base(), buf, buf_size); + if (n == -1) + return READ_ERR_IO; + + out_count = n; + + return READ_OK; + } + + Write_result write(char const *buf, file_size buf_size, + file_size seek_offset, + file_size &out_count) override + { + rump_sys_unlink(_path.base()); + + if (rump_sys_symlink(buf, _path.base()) != 0) { + out_count = 0; + return WRITE_OK; + } + + out_count = buf_size; + return WRITE_OK; + } }; /** @@ -110,39 +316,10 @@ class Vfs::Rump_file_system : public File_system return buf; } - Dirent_result _dirent(char const *path, - struct ::dirent *dent, Dirent &vfs_dir) - { - /* - * We cannot use 'd_type' member of 'dirent' here since the EXT2 - * implementation sets the type to unkown. Hence we use stat. - */ - struct stat s; - rump_sys_lstat(path, &s); - - if (S_ISREG(s.st_mode)) - vfs_dir.type = Dirent_type::DIRENT_TYPE_FILE; - else if (S_ISDIR(s.st_mode)) - vfs_dir.type = Dirent_type::DIRENT_TYPE_DIRECTORY; - else if (S_ISLNK(s.st_mode)) - vfs_dir.type = Dirent_type::DIRENT_TYPE_SYMLINK; - else if (S_ISBLK(s.st_mode)) - vfs_dir.type = Dirent_type::DIRENT_TYPE_BLOCKDEV; - else if (S_ISCHR(s.st_mode)) - vfs_dir.type = Dirent_type::DIRENT_TYPE_CHARDEV; - else if (S_ISFIFO(s.st_mode)) - vfs_dir.type = Dirent_type::DIRENT_TYPE_FIFO; - else - vfs_dir.type = Dirent_type::DIRENT_TYPE_FILE; - - strncpy(vfs_dir.name, dent->d_name, sizeof(Dirent::name)); - - return DIRENT_OK; - } - public: - Rump_file_system(Xml_node const &config) + Rump_file_system(Genode::Env &env, Xml_node const &config) + : _env(env) { typedef Genode::String<16> Fs_type; @@ -180,9 +357,6 @@ class Vfs::Rump_file_system : public File_system ** Directory service interface ** *********************************/ - void sync(char const *path) override { - _rump_sync(); } - Genode::Dataspace_capability dataspace(char const *path) override { int fd = rump_sys_open(path, O_RDONLY); @@ -195,9 +369,9 @@ class Vfs::Rump_file_system : public File_system char *local_addr = nullptr; Ram_dataspace_capability ds_cap; try { - ds_cap = env()->ram_session()->alloc(ds_size); + ds_cap = _env.ram().alloc(ds_size); - local_addr = env()->rm_session()->attach(ds_cap); + local_addr = _env.rm().attach(ds_cap); enum { CHUNK_SIZE = 16U << 10 }; @@ -208,11 +382,11 @@ class Vfs::Rump_file_system : public File_system i += n; } - env()->rm_session()->detach(local_addr); + _env.rm().detach(local_addr); } catch(...) { if (local_addr) - env()->rm_session()->detach(local_addr); - env()->ram_session()->free(ds_cap); + _env.rm().detach(local_addr); + _env.ram().free(ds_cap); } rump_sys_close(fd); return ds_cap; @@ -222,7 +396,7 @@ class Vfs::Rump_file_system : public File_system Genode::Dataspace_capability ds_cap) override { if (ds_cap.valid()) - env()->ram_session()->free( + _env.ram().free( static_cap_cast(ds_cap)); } @@ -267,20 +441,6 @@ class Vfs::Rump_file_system : public File_system return (rump_sys_lstat(path, &s) == 0) ? path : 0; } - Mkdir_result mkdir(char const *path, unsigned mode) override - { - if (rump_sys_mkdir(path, mode|0777) != 0) switch (::errno) { - case ENAMETOOLONG: return MKDIR_ERR_NAME_TOO_LONG; - case EACCES: return MKDIR_ERR_NO_PERM; - case ENOENT: return MKDIR_ERR_NO_ENTRY; - case EEXIST: return MKDIR_ERR_EXISTS; - case ENOSPC: return MKDIR_ERR_NO_SPACE; - default: - return MKDIR_ERR_NO_PERM; - } - return MKDIR_OK; - } - Open_result open(char const *path, unsigned mode, Vfs_handle **handle, Allocator &alloc) override @@ -300,14 +460,74 @@ class Vfs::Rump_file_system : public File_system return OPEN_ERR_NO_PERM; } - *handle = new (alloc) Rump_vfs_handle(*this, alloc, mode, fd); + *handle = new (alloc) Rump_vfs_file_handle(*this, alloc, mode, fd); return OPEN_OK; } + Opendir_result opendir(char const *path, bool create, + Vfs_handle **handle, Allocator &alloc) override + { + if (strlen(path) == 0) + path = "/"; + + if (create) { + if (rump_sys_mkdir(path, 0777) != 0) switch (::errno) { + case ENAMETOOLONG: return OPENDIR_ERR_NAME_TOO_LONG; + case EACCES: return OPENDIR_ERR_PERMISSION_DENIED; + case ENOENT: return OPENDIR_ERR_LOOKUP_FAILED; + case EEXIST: return OPENDIR_ERR_NODE_ALREADY_EXISTS; + case ENOSPC: return OPENDIR_ERR_NO_SPACE; + default: + return OPENDIR_ERR_PERMISSION_DENIED; + } + } + + int fd = rump_sys_open(path, O_RDONLY | O_DIRECTORY); + if (fd == -1) switch (errno) { + case ENAMETOOLONG: return OPENDIR_ERR_NAME_TOO_LONG; + case EACCES: return OPENDIR_ERR_PERMISSION_DENIED; + case ENOENT: return OPENDIR_ERR_LOOKUP_FAILED; + case EEXIST: return OPENDIR_ERR_NODE_ALREADY_EXISTS; + case ENOSPC: return OPENDIR_ERR_NO_SPACE; + default: + return OPENDIR_ERR_PERMISSION_DENIED; + } + + *handle = new (alloc) Rump_vfs_dir_handle(*this, alloc, 0777, fd, path); + return OPENDIR_OK; + + } + + Openlink_result openlink(char const *path, bool create, + Vfs_handle **handle, Allocator &alloc) override + { + if (create) { + if (rump_sys_symlink("", path) != 0) switch (errno) { + case EEXIST: return OPENLINK_ERR_NODE_ALREADY_EXISTS; + case ENOENT: return OPENLINK_ERR_LOOKUP_FAILED; + case ENOSPC: return OPENLINK_ERR_NO_SPACE; + case EACCES: return OPENLINK_ERR_PERMISSION_DENIED; + case ENAMETOOLONG: return OPENLINK_ERR_NAME_TOO_LONG; + default: + return OPENLINK_ERR_PERMISSION_DENIED; + } + } + + char dummy; + if (rump_sys_readlink(path, &dummy, sizeof(dummy)) == -1) switch(errno) { + case ENOENT: return OPENLINK_ERR_LOOKUP_FAILED; + default: + return OPENLINK_ERR_PERMISSION_DENIED; + } + + *handle = new (alloc) Rump_vfs_symlink_handle(*this, alloc, 0777, path); + return OPENLINK_OK; + } + void close(Vfs_handle *vfs_handle) override { - Rump_vfs_handle *rump_handle = - static_cast(vfs_handle); + Rump_vfs_file_handle *rump_handle = + static_cast(vfs_handle); if (rump_handle) destroy(vfs_handle->alloc(), rump_handle); @@ -328,44 +548,6 @@ class Vfs::Rump_file_system : public File_system return STAT_OK; } - Dirent_result dirent(char const *path, file_offset index_, - Dirent &vfs_dir) override - { - int fd = rump_sys_open(*path ? path : "/", O_RDONLY | O_DIRECTORY); - if (fd == -1) - return DIRENT_ERR_INVALID_PATH; - - rump_sys_lseek(fd, 0, SEEK_SET); - - int bytes; - unsigned const index = index_; - vfs_dir.fileno = 0; - char *buf = _buffer(); - struct ::dirent *dent = nullptr; - do { - bytes = rump_sys_getdents(fd, buf, BUFFER_SIZE); - void *current, *end; - for (current = buf, end = &buf[bytes]; - current < end; - current = _DIRENT_NEXT((::dirent *)current)) - { - dent = (::dirent *)current; - if (strcmp(".", dent->d_name) && strcmp("..", dent->d_name)) { - if (vfs_dir.fileno++ == index) { - Path newpath(dent->d_name, path); - rump_sys_close(fd); - return _dirent(newpath.base(), dent, vfs_dir); - } - } - } - } while (bytes > 0); - rump_sys_close(fd); - - vfs_dir.type = DIRENT_TYPE_END; - vfs_dir.name[0] = '\0'; - return DIRENT_OK; - } - Unlink_result unlink(char const *path) override { struct stat s; @@ -384,39 +566,6 @@ class Vfs::Rump_file_system : public File_system return UNLINK_ERR_NO_PERM; } - Readlink_result readlink(char const *path, char *buf, - file_size buf_size, file_size &out_len) override - { - ssize_t n = rump_sys_readlink(path, buf, buf_size); - if (n == -1) { - out_len = 0; - return READLINK_ERR_NO_ENTRY; - } - - out_len = n; - return READLINK_OK; - } - - Symlink_result symlink(char const *from, char const *to) override - { - if (rump_sys_symlink(from, to) != 0) switch (errno) { - case EEXIST: { - if (rump_sys_readlink(to, NULL, 0) == -1) - return SYMLINK_ERR_EXISTS; - rump_sys_unlink(to); - return rump_sys_symlink(from, to) == 0 ? - SYMLINK_OK : SYMLINK_ERR_EXISTS; - } - case ENOENT: return SYMLINK_ERR_NO_ENTRY; - case ENOSPC: return SYMLINK_ERR_NO_SPACE; - case EACCES: return SYMLINK_ERR_NO_PERM; - case ENAMETOOLONG: return SYMLINK_ERR_NAME_TOO_LONG; - default: - return SYMLINK_ERR_NO_PERM; - } - return SYMLINK_OK; - } - Rename_result rename(char const *from, char const *to) override { if (rump_sys_rename(from, to) != 0) switch (errno) { @@ -439,53 +588,42 @@ class Vfs::Rump_file_system : public File_system Rump_vfs_handle *handle = static_cast(vfs_handle); - ssize_t n = rump_sys_pwrite(handle->fd(), buf, buf_size, handle->seek()); - if (n == -1) switch (errno) { - case EWOULDBLOCK: return WRITE_ERR_WOULD_BLOCK; - case EINVAL: return WRITE_ERR_INVALID; - case EIO: return WRITE_ERR_IO; - case EINTR: return WRITE_ERR_INTERRUPT; - default: - return WRITE_ERR_IO; - } - out_count = n; - return WRITE_OK; + if (handle) + return handle->write(buf, buf_size, handle->seek(), out_count); + + return WRITE_ERR_INVALID; } - Read_result read(Vfs_handle *vfs_handle, char *buf, file_size buf_size, - file_size &out_count) override + Read_result complete_read(Vfs_handle *vfs_handle, char *buf, + file_size buf_size, + file_size &out_count) override { Rump_vfs_handle *handle = static_cast(vfs_handle); - ssize_t n = rump_sys_pread(handle->fd(), buf, buf_size, handle->seek()); - if (n == -1) switch (errno) { - case EWOULDBLOCK: return READ_ERR_WOULD_BLOCK; - case EINVAL: return READ_ERR_INVALID; - case EIO: return READ_ERR_IO; - case EINTR: return READ_ERR_INTERRUPT; - default: - return READ_ERR_IO; - } - out_count = n; - return READ_OK; + if (handle) + return handle->read(buf, buf_size, handle->seek(), out_count); + + return READ_ERR_INVALID; } bool read_ready(Vfs_handle *) override { return true; } Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size len) override { - Rump_vfs_handle *handle = - static_cast(vfs_handle); + Rump_vfs_file_handle *handle = + dynamic_cast(vfs_handle); - if (rump_sys_ftruncate(handle->fd(), len) != 0) switch (errno) { - case EACCES: return FTRUNCATE_ERR_NO_PERM; - case EINTR: return FTRUNCATE_ERR_INTERRUPT; - case ENOSPC: return FTRUNCATE_ERR_NO_SPACE; - default: - return FTRUNCATE_ERR_NO_PERM; - } - return FTRUNCATE_OK; + if (handle) + return handle->ftruncate(len); + + return FTRUNCATE_ERR_NO_PERM; + } + + Sync_result complete_sync(Vfs_handle *) override + { + _rump_sync(); + return SYNC_OK; } }; @@ -531,7 +669,7 @@ class Rump_factory : public Vfs::File_system_factory Genode::Xml_node config, Vfs::Io_response_handler &) override { - return new (alloc) Vfs::Rump_file_system(config); + return new (alloc) Vfs::Rump_file_system(env, config); } }; diff --git a/repos/dde_rump/src/server/rump_fs/main.cc b/repos/dde_rump/src/server/rump_fs/main.cc index a4da67926f..afdbd46347 100644 --- a/repos/dde_rump/src/server/rump_fs/main.cc +++ b/repos/dde_rump/src/server/rump_fs/main.cc @@ -93,6 +93,10 @@ class Rump_fs::Session_component : public Session_rpc_object case Packet_descriptor::READ_READY: /* not supported */ break; + + case Packet_descriptor::SYNC: + rump_sys_sync(); + break; } packet.length(res_length); @@ -411,8 +415,6 @@ class Rump_fs::Session_component : public Session_rpc_object throw Invalid_handle(); } } - - void sync(Node_handle) override { rump_sys_sync(); } }; class Rump_fs::Root : public Root_component diff --git a/repos/gems/include/gems/vfs.h b/repos/gems/include/gems/vfs.h index 3277be04b2..12fe3e501c 100644 --- a/repos/gems/include/gems/vfs.h +++ b/repos/gems/include/gems/vfs.h @@ -76,8 +76,12 @@ struct Genode::Directory : Noncopyable Vfs::File_system &_fs; + Entrypoint &_ep; + Allocator &_alloc; + Vfs::Vfs_handle *_handle = nullptr; + friend class Readonly_file; friend class Root_directory; @@ -86,8 +90,8 @@ struct Genode::Directory : Noncopyable * * \throw Open_failed */ - Directory(Vfs::File_system &fs, Allocator &alloc, Path const &path) - : _path(""), _fs(fs), _alloc(alloc) + Directory(Vfs::File_system &fs, Entrypoint &ep, Allocator &alloc) + : _path(""), _fs(fs), _ep(ep), _alloc(alloc) { } /* @@ -124,12 +128,16 @@ struct Genode::Directory : Noncopyable * \throw Nonexistent_directory */ Directory(Directory &other, Path const &rel_path) - : _path(other._path, "/", rel_path), _fs(other._fs), _alloc(other._alloc) + : _path(other._path, "/", rel_path), _fs(other._fs), _ep(other._ep), + _alloc(other._alloc) { - if (!(other._stat(rel_path).mode & Vfs::Directory_service::STAT_MODE_DIRECTORY)) + if (_fs.opendir(_path.string(), false, &_handle, _alloc) != + Vfs::Directory_service::OPENDIR_OK) throw Nonexistent_directory(); } + ~Directory() { _handle->ds().close(_handle); } + template void for_each_entry(FN const &fn) { @@ -137,10 +145,29 @@ struct Genode::Directory : Noncopyable Entry entry; - Vfs::Directory_service::Dirent_result dirent_result = - _fs.dirent(_path.string(), i, entry._dirent); + _handle->seek(i * sizeof(entry._dirent)); - if (dirent_result != Vfs::Directory_service::DIRENT_OK) { + while (!_handle->fs().queue_read(_handle, sizeof(entry._dirent))) + _ep.wait_and_dispatch_one_io_signal(); + + Vfs::File_io_service::Read_result read_result; + Vfs::file_size out_count = 0; + + for (;;) { + + read_result = _handle->fs().complete_read(_handle, + (char*)&entry._dirent, + sizeof(entry._dirent), + out_count); + + if (read_result != Vfs::File_io_service::READ_QUEUED) + break; + + _ep.wait_and_dispatch_one_io_signal(); + } + + if ((read_result != Vfs::File_io_service::READ_OK) || + (out_count < sizeof(entry._dirent))) { error("could not access directory '", _path, "'"); throw Read_dir_failed(); } @@ -186,7 +213,7 @@ struct Genode::Root_directory : public Vfs::Io_response_handler, : Vfs::Global_file_system_factory(alloc), Vfs::Dir_file_system(env, alloc, config, *this, *this), - Directory(*this, alloc, "/") + Directory(*this, env.ep(), alloc) { } void apply_config(Xml_node config) { Vfs::Dir_file_system::apply_config(config); } @@ -207,7 +234,8 @@ class Genode::Readonly_file : public File { private: - Vfs::Vfs_handle *_handle = nullptr; + Vfs::Vfs_handle *_handle = nullptr; + Genode::Entrypoint &_ep; void _open(Vfs::File_system &fs, Allocator &alloc, Path const path) { @@ -229,6 +257,7 @@ class Genode::Readonly_file : public File * \throw File::Open_failed */ Readonly_file(Directory &dir, Path const &rel_path) + : _ep(dir._ep) { _open(dir._fs, dir._alloc, Path(dir._path, "/", rel_path)); } @@ -243,8 +272,21 @@ class Genode::Readonly_file : public File size_t read(char *dst, size_t bytes) { Vfs::file_size out_count = 0; - Vfs::File_io_service::Read_result const result = - _handle->fs().read(_handle, dst, bytes, out_count); + + while (!_handle->fs().queue_read(_handle, bytes)) + _ep.wait_and_dispatch_one_io_signal(); + + Vfs::File_io_service::Read_result result; + + for (;;) { + result = _handle->fs().complete_read(_handle, dst, bytes, + out_count); + + if (result != Vfs::File_io_service::READ_QUEUED) + break; + + _ep.wait_and_dispatch_one_io_signal(); + }; /* * XXX handle READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK, READ_QUEUED diff --git a/repos/gems/src/lib/file/file.cc b/repos/gems/src/lib/file/file.cc index 13c54ae3e8..f22c71659e 100644 --- a/repos/gems/src/lib/file/file.cc +++ b/repos/gems/src/lib/file/file.cc @@ -18,6 +18,7 @@ #include /* libc includes */ +#include #include #include #include @@ -29,7 +30,7 @@ static Genode::size_t file_size(char const *name) { struct stat s; s.st_size = 0; - stat(name, &s); + Libc::with_libc([&] () { stat(name, &s); }); return s.st_size; } @@ -40,12 +41,14 @@ File::File(char const *name, Genode::Allocator &alloc) _file_size(file_size(name)), _data(alloc.alloc(_file_size)) { - int const fd = open(name, O_RDONLY); - if (read(fd, _data, _file_size) < 0) { - Genode::error("reading from file \"", name, "\" failed (error ", errno, ")"); - throw Reading_failed(); - } - close(fd); + Libc::with_libc([&] () { + int const fd = open(name, O_RDONLY); + if (read(fd, _data, _file_size) < 0) { + Genode::error("reading from file \"", name, "\" failed (error ", errno, ")"); + throw Reading_failed(); + } + close(fd); + }); } diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index 8c06b5a740..4fb6cedebb 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -169,6 +169,45 @@ int Libc::Vfs_plugin::access(const char *path, int amode) Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, int libc_fd) { + if (_root_dir.directory(path)) { + + if (((flags & O_ACCMODE) != O_RDONLY)) { + errno = EINVAL; + return nullptr; + } + + Vfs::Vfs_handle *handle = 0; + + typedef Vfs::Directory_service::Opendir_result Opendir_result; + + switch (_root_dir.opendir(path, false, &handle, _alloc)) { + case Opendir_result::OPENDIR_OK: break; + case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED: errno = ENOENT; return nullptr; + case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return nullptr; + case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS: errno = EEXIST; return nullptr; + case Opendir_result::OPENDIR_ERR_NO_SPACE: errno = ENOSPC; return nullptr; + case Opendir_result::OPENDIR_ERR_OUT_OF_RAM: + case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS: + case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED: errno = EPERM; return nullptr; + } + + /* the directory was successfully opened */ + + Libc::File_descriptor *fd = + Libc::file_descriptor_allocator()->alloc(this, vfs_context(handle), libc_fd); + + /* FIXME error cleanup code leaks resources! */ + + if (!fd) { + errno = EMFILE; + return nullptr; + } + + fd->flags = flags & O_ACCMODE; + + return fd; + } + typedef Vfs::Directory_service::Open_result Result; Vfs::Vfs_handle *handle = 0; @@ -207,6 +246,8 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, case Result::OPEN_ERR_UNACCESSIBLE: errno = ENOENT; return 0; case Result::OPEN_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return 0; case Result::OPEN_ERR_NO_SPACE: errno = ENOSPC; return 0; + case Result::OPEN_ERR_OUT_OF_RAM: errno = ENOSPC; return 0; + case Result::OPEN_ERR_OUT_OF_CAPS: errno = ENOSPC; return 0; } } break; @@ -215,6 +256,8 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, case Result::OPEN_ERR_EXISTS: errno = EEXIST; return 0; case Result::OPEN_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return 0; case Result::OPEN_ERR_NO_SPACE: errno = ENOSPC; return 0; + case Result::OPEN_ERR_OUT_OF_RAM: errno = ENOSPC; return 0; + case Result::OPEN_ERR_OUT_OF_CAPS: errno = ENOSPC; return 0; } } @@ -244,6 +287,7 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, int Libc::Vfs_plugin::close(Libc::File_descriptor *fd) { Vfs::Vfs_handle *handle = vfs_handle(fd); + _vfs_sync(handle); handle->ds().close(handle); Libc::file_descriptor_allocator()->free(fd); return 0; @@ -278,16 +322,28 @@ int Libc::Vfs_plugin::fstatfs(Libc::File_descriptor *fd, struct statfs *buf) int Libc::Vfs_plugin::mkdir(const char *path, mode_t mode) { - typedef Vfs::Directory_service::Mkdir_result Result; + Vfs::Vfs_handle *dir_handle { 0 }; - switch (_root_dir.mkdir(path, mode)) { - case Result::MKDIR_ERR_EXISTS: errno = EEXIST; return -1; - case Result::MKDIR_ERR_NO_ENTRY: errno = ENOENT; return -1; - case Result::MKDIR_ERR_NO_SPACE: errno = ENOSPC; return -1; - case Result::MKDIR_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return -1; - case Result::MKDIR_ERR_NO_PERM: errno = EPERM; return -1; - case Result::MKDIR_OK: break; + typedef Vfs::Directory_service::Opendir_result Opendir_result; + + switch (_root_dir.opendir(path, true, &dir_handle, _alloc)) { + case Opendir_result::OPENDIR_OK: + dir_handle->ds().close(dir_handle); + break; + case Opendir_result::OPENDIR_ERR_LOOKUP_FAILED: + return Errno(ENOENT); + case Opendir_result::OPENDIR_ERR_NAME_TOO_LONG: + return Errno(ENAMETOOLONG); + case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS: + return Errno(EEXIST); + case Opendir_result::OPENDIR_ERR_NO_SPACE: + return Errno(ENOSPC); + case Opendir_result::OPENDIR_ERR_OUT_OF_RAM: + case Opendir_result::OPENDIR_ERR_OUT_OF_CAPS: + case Opendir_result::OPENDIR_ERR_PERMISSION_DENIED: + return Errno(EPERM); } + return 0; } @@ -321,15 +377,60 @@ ssize_t Libc::Vfs_plugin::write(Libc::File_descriptor *fd, const void *buf, Vfs::Vfs_handle *handle = vfs_handle(fd); - Vfs::file_size out_count = 0; + Vfs::file_size out_count = 0; + Result out_result = Result::WRITE_OK; - switch (handle->fs().write(handle, (char const *)buf, count, out_count)) { - case Result::WRITE_ERR_AGAIN: errno = EAGAIN; return -1; - case Result::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; return -1; - case Result::WRITE_ERR_INVALID: errno = EINVAL; return -1; - case Result::WRITE_ERR_IO: errno = EIO; return -1; - case Result::WRITE_ERR_INTERRUPT: errno = EINTR; return -1; - case Result::WRITE_OK: break; + if (fd->flags & O_NONBLOCK) { + + try { + out_result = handle->fs().write(handle, (char const *)buf, count, out_count); + } catch (Vfs::File_io_service::Insufficient_buffer) { } + + } else { + + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *handle; + void const *buf; + ::size_t count; + Vfs::file_size &out_count; + Result &out_result; + + Check(Vfs::Vfs_handle *handle, void const *buf, + ::size_t count, Vfs::file_size &out_count, + Result &out_result) + : handle(handle), buf(buf), count(count), out_count(out_count), + out_result(out_result) + { } + + bool suspend() override + { + try { + out_result = handle->fs().write(handle, (char const *)buf, + count, out_count); + retry = false; + } catch (Vfs::File_io_service::Insufficient_buffer) { + retry = true; + } + + return retry; + } + } check(handle, buf, count, out_count, out_result); + + do { + Libc::suspend(check); + } while (check.retry); + } + + switch (out_result) { + case Result::WRITE_ERR_AGAIN: return Errno(EAGAIN); + case Result::WRITE_ERR_WOULD_BLOCK: return Errno(EWOULDBLOCK); + case Result::WRITE_ERR_INVALID: return Errno(EINVAL); + case Result::WRITE_ERR_IO: return Errno(EIO); + case Result::WRITE_ERR_INTERRUPT: return Errno(EINTR); + case Result::WRITE_OK: break; } handle->advance_seek(out_count); @@ -338,64 +439,74 @@ ssize_t Libc::Vfs_plugin::write(Libc::File_descriptor *fd, const void *buf, } -typedef Vfs::File_io_service::Read_result Result; - -struct Read_check : Libc::Suspend_functor { - Vfs::Vfs_handle * handle; - void * buf; - ::size_t * count; - Vfs::file_size * out_count; - Result * out_result; - - Read_check(Vfs::Vfs_handle * handle, void * buf, ::size_t * count, - Vfs::file_size * out_count, Result * out_result) - : handle(handle), buf(buf), count(count), out_count(out_count), - out_result(out_result) - { } -}; - ssize_t Libc::Vfs_plugin::read(Libc::File_descriptor *fd, void *buf, ::size_t count) { - Vfs::Vfs_handle *handle = vfs_handle(fd); + typedef Vfs::File_io_service::Read_result Result; - Vfs::file_size out_count = 0; - Result out_result = Result::READ_OK; + Vfs::Vfs_handle *handle = vfs_handle(fd); if (fd->flags & O_NONBLOCK && !Libc::read_ready(fd)) return Errno(EAGAIN); - while (!handle->fs().queue_read(handle, (char *)buf, count, - out_result, out_count)) { - struct Check : Read_check { - Check(Vfs::Vfs_handle * handle, void * buf, ::size_t * count, - Vfs::file_size * out_count, Result * out_result) - : Read_check (handle, buf, count, out_count, out_result) { } + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; - bool suspend() override { - return !handle->fs().queue_read(handle, (char *)buf, *count, - *out_result, *out_count); } - } check ( handle, buf, &count, &out_count, &out_result); + Vfs::Vfs_handle *handle; + ::size_t count; - Libc::suspend(check); + Check(Vfs::Vfs_handle *handle, ::size_t count) + : handle(handle), count(count) { } + + bool suspend() override + { + retry = !handle->fs().queue_read(handle, count); + return retry; + } + } check ( handle, count); + + do { + Libc::suspend(check); + } while (check.retry); } - while (out_result == Result::READ_QUEUED) { + Vfs::file_size out_count = 0; + Result out_result; - struct Check : Read_check { - Check(Vfs::Vfs_handle * handle, void * buf, ::size_t * count, - Vfs::file_size * out_count, Result * out_result) - : Read_check (handle, buf, count, out_count, out_result) { } + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; - bool suspend() override { - *out_result = handle->fs().complete_read(handle, (char *)buf, - *count, *out_count); + Vfs::Vfs_handle *handle; + void *buf; + ::size_t count; + Vfs::file_size &out_count; + Result &out_result; + + Check(Vfs::Vfs_handle *handle, void *buf, ::size_t count, + Vfs::file_size &out_count, Result &out_result) + : handle(handle), buf(buf), count(count), out_count(out_count), + out_result(out_result) + { } + + bool suspend() override + { + out_result = handle->fs().complete_read(handle, (char *)buf, + count, out_count); /* suspend me if read is still queued */ - return *out_result == Result::READ_QUEUED; - } - } check ( handle, buf, &count, &out_count, &out_result); - Libc::suspend(check); + retry = (out_result == Result::READ_QUEUED); + + return retry; + } + } check ( handle, buf, count, out_count, out_result); + + do { + Libc::suspend(check); + } while (check.retry); } switch (out_result) { @@ -423,19 +534,77 @@ ssize_t Libc::Vfs_plugin::getdirentries(Libc::File_descriptor *fd, char *buf, return -1; } - typedef Vfs::Directory_service::Dirent_result Result; + typedef Vfs::File_io_service::Read_result Result; Vfs::Vfs_handle *handle = vfs_handle(fd); - Vfs::Directory_service::Dirent dirent_out; - Genode::memset(&dirent_out, 0, sizeof(dirent_out)); + typedef Vfs::Directory_service::Dirent Dirent; - unsigned const index = handle->seek() / sizeof(Vfs::Directory_service::Dirent); + Dirent dirent_out; - switch (handle->ds().dirent(fd->fd_path, index, dirent_out)) { - case Result::DIRENT_ERR_INVALID_PATH: errno = ENOENT; return -1; - case Result::DIRENT_ERR_NO_PERM: errno = EACCES; return -1; - case Result::DIRENT_OK: break; + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *handle; + + Check(Vfs::Vfs_handle *handle) + : handle(handle) { } + + bool suspend() override + { + retry = !handle->fs().queue_read(handle, sizeof(Dirent)); + return retry; + } + } check(handle); + + do { + Libc::suspend(check); + } while (check.retry); + } + + Result out_result; + Vfs::file_size out_count; + + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *handle; + Dirent &dirent_out; + Vfs::file_size &out_count; + Result &out_result; + + Check(Vfs::Vfs_handle *handle, Dirent &dirent_out, + Vfs::file_size &out_count, Result &out_result) + : handle(handle), dirent_out(dirent_out), out_count(out_count), + out_result(out_result) { } + + bool suspend() override + { + out_result = handle->fs().complete_read(handle, + (char*)&dirent_out, + sizeof(Dirent), + out_count); + + /* suspend me if read is still queued */ + + retry = (out_result == Result::READ_QUEUED); + + return retry; + } + } check(handle, dirent_out, out_count, out_result); + + do { + Libc::suspend(check); + } while (check.retry); + } + + if ((out_result != Result::READ_OK) || + (out_count < sizeof(Dirent))) { + return 0; } /* @@ -693,39 +862,187 @@ int Libc::Vfs_plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg) int Libc::Vfs_plugin::fsync(Libc::File_descriptor *fd) { - _root_dir.sync(fd->fd_path); + Vfs::Vfs_handle *handle = vfs_handle(fd); + _vfs_sync(handle); return 0; } int Libc::Vfs_plugin::symlink(const char *oldpath, const char *newpath) { - typedef Vfs::Directory_service::Symlink_result Result; + typedef Vfs::Directory_service::Openlink_result Openlink_result; - switch (_root_dir.symlink(oldpath, newpath)) { - case Result::SYMLINK_ERR_EXISTS: errno = EEXIST; return -1; - case Result::SYMLINK_ERR_NO_ENTRY: errno = ENOENT; return -1; - case Result::SYMLINK_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return -1; - case Result::SYMLINK_ERR_NO_PERM: errno = EPERM; return -1; - case Result::SYMLINK_ERR_NO_SPACE: errno = ENOSPC; return -1; - case Result::SYMLINK_OK: break; + Vfs::Vfs_handle *handle { 0 }; + + Openlink_result openlink_result = + _root_dir.openlink(newpath, true, &handle, _alloc); + + switch (openlink_result) { + case Openlink_result::OPENLINK_OK: + break; + case Openlink_result::OPENLINK_ERR_LOOKUP_FAILED: + return Errno(ENOENT); + case Openlink_result::OPENLINK_ERR_NAME_TOO_LONG: + return Errno(ENAMETOOLONG); + case Openlink_result::OPENLINK_ERR_NODE_ALREADY_EXISTS: + return Errno(EEXIST); + case Openlink_result::OPENLINK_ERR_NO_SPACE: + return Errno(ENOSPC); + case Openlink_result::OPENLINK_ERR_OUT_OF_RAM: + return Errno(ENOSPC); + case Openlink_result::OPENLINK_ERR_OUT_OF_CAPS: + return Errno(ENOSPC); + case Vfs::Directory_service::OPENLINK_ERR_PERMISSION_DENIED: + return Errno(EPERM); } + + Vfs::file_size count = ::strlen(oldpath) + 1; + Vfs::file_size out_count = 0; + + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *handle; + void const *buf; + ::size_t count; + Vfs::file_size &out_count; + + Check(Vfs::Vfs_handle *handle, void const *buf, + ::size_t count, Vfs::file_size &out_count) + : handle(handle), buf(buf), count(count), out_count(out_count) + { } + + bool suspend() override + { + try { + handle->fs().write(handle, (char const *)buf, + count, out_count); + retry = false; + } catch (Vfs::File_io_service::Insufficient_buffer) { + retry = true; + } + + return retry; + } + } check ( handle, oldpath, count, out_count); + + do { + Libc::suspend(check); + } while (check.retry); + + _vfs_sync(handle); + handle->ds().close(handle); + + if (out_count != count) + return Errno(ENAMETOOLONG); + return 0; } ssize_t Libc::Vfs_plugin::readlink(const char *path, char *buf, ::size_t buf_size) { - typedef Vfs::Directory_service::Readlink_result Result; + Vfs::Vfs_handle *symlink_handle { 0 }; + Vfs::Directory_service::Openlink_result openlink_result = + _root_dir.openlink(path, false, &symlink_handle, _alloc); + + switch (openlink_result) { + case Vfs::Directory_service::OPENLINK_OK: + break; + case Vfs::Directory_service::OPENLINK_ERR_LOOKUP_FAILED: + return Errno(ENOENT); + case Vfs::Directory_service::OPENLINK_ERR_NAME_TOO_LONG: + /* should not happen */ + return Errno(ENAMETOOLONG); + 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: + return Errno(EACCES); + } + + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *symlink_handle; + ::size_t const buf_size; + + Check(Vfs::Vfs_handle *symlink_handle, + ::size_t const buf_size) + : symlink_handle(symlink_handle), buf_size(buf_size) { } + + bool suspend() override + { + retry = + !symlink_handle->fs().queue_read(symlink_handle, buf_size); + return retry; + } + } check(symlink_handle, buf_size); + + do { + Libc::suspend(check); + } while (check.retry); + } + + typedef Vfs::File_io_service::Read_result Result; + + Result out_result; Vfs::file_size out_len = 0; - switch (_root_dir.readlink(path, buf, buf_size, out_len)) { - case Result::READLINK_ERR_NO_ENTRY: errno = ENOENT; return -1; - case Result::READLINK_ERR_NO_PERM: errno = EACCES; return -1; - case Result::READLINK_OK: break; + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *symlink_handle; + char *buf; + ::size_t const buf_size; + Vfs::file_size &out_len; + Result &out_result; + + Check(Vfs::Vfs_handle *symlink_handle, + char *buf, + ::size_t const buf_size, + Vfs::file_size &out_len, + Result &out_result) + : symlink_handle(symlink_handle), buf(buf), buf_size(buf_size), + out_len(out_len), out_result(out_result) { } + + bool suspend() override + { + out_result = symlink_handle->fs().complete_read(symlink_handle, buf, buf_size, out_len); + + /* suspend me if read is still queued */ + + retry = (out_result == Result::READ_QUEUED); + + return retry; + } + } check(symlink_handle, buf, buf_size, out_len, out_result); + + do { + Libc::suspend(check); + } while (check.retry); + } + + switch (out_result) { + case Result::READ_ERR_AGAIN: return Errno(EAGAIN); + case Result::READ_ERR_WOULD_BLOCK: return Errno(EWOULDBLOCK); + case Result::READ_ERR_INVALID: return Errno(EINVAL); + case Result::READ_ERR_IO: return Errno(EIO); + case Result::READ_ERR_INTERRUPT: return Errno(EINTR); + case Result::READ_OK: break; + + case Result::READ_QUEUED: /* handled above, so never reached */ break; }; + symlink_handle->ds().close(symlink_handle); + return out_len; } diff --git a/repos/libports/src/lib/libc/vfs_plugin.h b/repos/libports/src/lib/libc/vfs_plugin.h index bb4acf9ca6..22ba7984d3 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.h +++ b/repos/libports/src/lib/libc/vfs_plugin.h @@ -18,6 +18,7 @@ /* Genode includes */ #include +#include "task.h" /* libc includes */ #include @@ -72,6 +73,67 @@ class Libc::Vfs_plugin : public Libc::Plugin } catch (Xml_node::Nonexistent_attribute) { } } + void _vfs_sync(Vfs::Vfs_handle *vfs_handle) + { + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *vfs_handle; + + Check(Vfs::Vfs_handle *vfs_handle) + : vfs_handle(vfs_handle) { } + + bool suspend() override + { + retry = !vfs_handle->fs().queue_sync(vfs_handle); + return retry; + } + } check(vfs_handle); + + /* + * Cannot call Libc::suspend() immediately, because the Libc kernel + * might not be running yet. + */ + if (!vfs_handle->fs().queue_sync(vfs_handle)) { + do { + Libc::suspend(check); + } while (check.retry); + } + } + + { + struct Check : Libc::Suspend_functor + { + bool retry { false }; + + Vfs::Vfs_handle *vfs_handle; + + Check(Vfs::Vfs_handle *vfs_handle) + : vfs_handle(vfs_handle) { } + + bool suspend() override + { + retry = (vfs_handle->fs().complete_sync(vfs_handle) == + Vfs::File_io_service::SYNC_QUEUED); + return retry; + } + } check(vfs_handle); + + /* + * Cannot call Libc::suspend() immediately, because the Libc kernel + * might not be running yet. + */ + if (vfs_handle->fs().complete_sync(vfs_handle) == + Vfs::File_io_service::SYNC_QUEUED) { + do { + Libc::suspend(check); + } while (check.retry); + } + } + } + public: Vfs_plugin(Libc::Env &env, Genode::Allocator &alloc) diff --git a/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc b/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc index 9ecb454c7f..677d1ec44e 100644 --- a/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc +++ b/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc @@ -43,8 +43,8 @@ class Fatfs::File_system : public Vfs::File_system typedef Genode::Path Path; - struct Fatfs_handle; - typedef Genode::List Fatfs_handles; + struct Fatfs_file_handle; + typedef Genode::List Fatfs_file_handles; /** * The FatFS library does not support opening a file @@ -54,9 +54,9 @@ class Fatfs::File_system : public Vfs::File_system struct File : Genode::Avl_node { - Path path; - Fatfs::FIL fil; - Fatfs_handles handles; + Path path; + Fatfs::FIL fil; + Fatfs_file_handles handles; /************************ ** Avl node interface ** @@ -91,12 +91,99 @@ class Fatfs::File_system : public Vfs::File_system } }; - struct Fatfs_handle : Vfs_handle, Fatfs_handles::Element + struct Fatfs_handle : Vfs_handle + { + using Vfs_handle::Vfs_handle; + + virtual Read_result complete_read(char *buf, + file_size buf_size, + file_size &out_count) = 0; + }; + + struct Fatfs_file_handle : Fatfs_handle, Fatfs_file_handles::Element { File *file = nullptr; - Fatfs_handle(File_system &fs, Allocator &alloc, int status_flags) - : Vfs_handle(fs, fs, alloc, status_flags) { } + Fatfs_file_handle(File_system &fs, Allocator &alloc, int status_flags) + : Fatfs_handle(fs, fs, alloc, status_flags) { } + + Read_result complete_read(char *buf, + file_size buf_size, + file_size &out_count) override + { + if (!file) { + Genode::error("READ_ERR_INVALID"); + return READ_ERR_INVALID; + } + if ((status_flags()&OPEN_MODE_ACCMODE) == OPEN_MODE_WRONLY) + return READ_ERR_INVALID; + + FRESULT fres; + FIL *fil = &file->fil; + + fres = f_lseek(fil, seek()); + if (fres == FR_OK) { + UINT bw = 0; + fres = f_read(fil, buf, buf_size, &bw); + out_count = bw; + } + + switch (fres) { + case FR_OK: return READ_OK; + case FR_INVALID_OBJECT: return READ_ERR_INVALID; + case FR_TIMEOUT: return READ_ERR_WOULD_BLOCK; + case FR_DISK_ERR: return READ_ERR_IO; + case FR_INT_ERR: return READ_ERR_IO; + case FR_DENIED: return READ_ERR_IO; + default: return READ_ERR_IO; + } + } + }; + + struct Fatfs_dir_handle : Fatfs_handle + { + DIR dir; + + Fatfs_dir_handle(File_system &fs, Allocator &alloc) + : Fatfs_handle(fs, fs, alloc, 0) { } + + Read_result complete_read(char *buf, + file_size buf_size, + file_size &out_count) override + { + /* not very efficient, just N calls to f_readdir */ + + out_count = 0; + + if (buf_size < sizeof(Dirent)) + return READ_ERR_INVALID; + + file_size dir_index = seek() / sizeof(Dirent); + + Dirent *vfs_dir = (Dirent*)buf; + + FILINFO info; + FRESULT res; + vfs_dir->fileno = 1; /* inode 0 is a pending unlink */ + + do { + res = f_readdir (&dir, &info); + if ((res != FR_OK) || (!info.fname[0])) { + vfs_dir->type = DIRENT_TYPE_END; + vfs_dir->name[0] = '\0'; + out_count = sizeof(Dirent); + return READ_OK; + } + } while (dir_index-- > 0); + + vfs_dir->type = (info.fattrib & AM_DIR) ? + DIRENT_TYPE_DIRECTORY : DIRENT_TYPE_FILE; + Genode::strncpy(vfs_dir->name, (const char*)info.fname, + sizeof(vfs_dir->name)); + + out_count = sizeof(Dirent); + return READ_OK; + } }; Genode::Env &_env; @@ -161,7 +248,7 @@ class Fatfs::File_system : public Vfs::File_system void _close_all(File &file) { /* invalidate handles */ - for (Fatfs_handle *handle = file.handles.first(); + for (Fatfs_file_handle *handle = file.handles.first(); handle; handle = file.handles.first()) { handle->file = nullptr; @@ -245,7 +332,7 @@ class Fatfs::File_system : public Vfs::File_system Vfs_handle **vfs_handle, Allocator &alloc) override { - Fatfs_handle *handle; + Fatfs_file_handle *handle; File *file = _opened_file(path); bool create = vfs_mode & OPEN_MODE_CREATE; @@ -257,7 +344,7 @@ class Fatfs::File_system : public Vfs::File_system /* attempt allocation before modifying blocks */ if (!_next_file) _next_file = new (_alloc) File(); - handle = new (alloc) Fatfs_handle(*this, alloc, vfs_mode); + handle = new (alloc) Fatfs_file_handle(*this, alloc, vfs_mode); if (!file) { file = _next_file; @@ -286,30 +373,84 @@ class Fatfs::File_system : public Vfs::File_system return OPEN_OK; } - void close(Vfs_handle *vfs_handle) override + Opendir_result opendir(char const *path, bool create, + Vfs_handle **vfs_handle, + Allocator &alloc) override { - Fatfs_handle *handle = static_cast(vfs_handle); + Fatfs_dir_handle *handle; - File *file = handle->file; - if (file) { - file->handles.remove(handle); - if (!file->handles.first()) - _close(*file); - else - f_sync(&file->fil); + /* attempt allocation before modifying blocks */ + handle = new (alloc) Fatfs_dir_handle(*this, alloc); + + if (create) { + FRESULT res = f_mkdir((const TCHAR*)path); + if (res != FR_OK) { + destroy(alloc, handle); + switch (res) { + case FR_EXIST: return OPENDIR_ERR_NODE_ALREADY_EXISTS; + case FR_NO_PATH: return OPENDIR_ERR_LOOKUP_FAILED; + case FR_INVALID_NAME: return OPENDIR_ERR_NAME_TOO_LONG; + default: return OPENDIR_ERR_PERMISSION_DENIED; + } + } } - destroy(handle->alloc(), handle); + FRESULT res = f_opendir(&handle->dir, (const TCHAR*)path); + if (res != FR_OK) { + destroy(alloc, handle); + switch (res) { + case FR_NO_PATH: return OPENDIR_ERR_LOOKUP_FAILED; + default: return OPENDIR_ERR_PERMISSION_DENIED; + } + } + + *vfs_handle = handle; + + return OPENDIR_OK; } - void sync(char const *path) override + void close(Vfs_handle *vfs_handle) override { - /** - * Files are flushed when they are closed so - * only open files need to be synced. - */ - if (File *file = _opened_file(path)) + { + Fatfs_file_handle *handle; + + handle = dynamic_cast(vfs_handle); + + if (handle) { + File *file = handle->file; + if (file) { + file->handles.remove(handle); + if (!file->handles.first()) + _close(*file); + else + f_sync(&file->fil); + } + destroy(handle->alloc(), handle); + return; + } + } + + { + Fatfs_dir_handle *handle; + + handle = dynamic_cast(vfs_handle); + + if (handle) { + f_closedir(&handle->dir); + destroy(handle->alloc(), handle); + } + } + } + + Sync_result complete_sync(Vfs_handle *vfs_handle) override + { + Fatfs_file_handle *handle = static_cast(vfs_handle); + + File *file = handle->file; + if (file) f_sync(&file->fil); + + return SYNC_OK; } Genode::Dataspace_capability dataspace(char const *path) override @@ -355,18 +496,6 @@ class Fatfs::File_system : public Vfs::File_system } } - Mkdir_result mkdir(char const *path, unsigned mode) override - { - FRESULT res = f_mkdir((const TCHAR*)path); - switch (res) { - case FR_OK: return MKDIR_OK; - case FR_EXIST: return MKDIR_ERR_EXISTS; - case FR_NO_PATH: return MKDIR_ERR_NO_ENTRY; - case FR_INVALID_NAME: return MKDIR_ERR_NAME_TOO_LONG; - default: return MKDIR_ERR_NO_PERM; - } - } - Stat_result stat(char const *path, Stat &stat) { stat = Stat(); @@ -408,40 +537,6 @@ class Fatfs::File_system : public Vfs::File_system return STAT_ERR_NO_PERM; } - Dirent_result dirent(char const *path, file_offset dir_index, - Dirent &vfs_dir) override - { - /* not very efficient, just N calls to f_readdir */ - - DIR dir; - FILINFO info; - FRESULT res; - vfs_dir.fileno = 1; /* inode 0 is a pending unlink */ - - switch (f_opendir(&dir, (const TCHAR*)path)) { - case FR_OK: break; - case FR_NO_PATH: return DIRENT_ERR_INVALID_PATH; - default: return DIRENT_ERR_NO_PERM; - } - - do { - res = f_readdir (&dir, &info); - if ((res != FR_OK) || (!info.fname[0])) { - vfs_dir.type = DIRENT_TYPE_END; - vfs_dir.name[0] = '\0'; - f_closedir(&dir); - return DIRENT_OK; - } - } while (--dir_index >= 0); - - vfs_dir.type = (info.fattrib & AM_DIR) ? - DIRENT_TYPE_DIRECTORY : DIRENT_TYPE_FILE; - Genode::strncpy(vfs_dir.name, (const char*)info.fname, - sizeof(vfs_dir.name)); - f_closedir(&dir); - return DIRENT_OK; - } - Unlink_result unlink(char const *path) override { /* close the file if it is open */ @@ -456,12 +551,6 @@ class Fatfs::File_system : public Vfs::File_system } } - Readlink_result readlink(char const*, char*, file_size, file_size&) override { - return READLINK_ERR_NO_PERM; } - - Symlink_result symlink(char const*, char const*) override { - return SYMLINK_ERR_NO_PERM; } - Rename_result rename(char const *from, char const *to) override { if (File *to_file = _opened_file(to)) { @@ -498,7 +587,7 @@ class Fatfs::File_system : public Vfs::File_system char const *buf, file_size buf_size, file_size &out_count) override { - Fatfs_handle *handle = static_cast(vfs_handle); + Fatfs_file_handle *handle = static_cast(vfs_handle); if (!handle->file) return WRITE_ERR_INVALID; if ((handle->status_flags()&OPEN_MODE_ACCMODE) == OPEN_MODE_RDONLY) @@ -525,42 +614,19 @@ class Fatfs::File_system : public Vfs::File_system } } - Read_result read(Vfs_handle *vfs_handle, char *buf, file_size buf_size, - file_size &out_count) override + Read_result complete_read(Vfs_handle *vfs_handle, char *buf, + file_size buf_size, + file_size &out_count) override { - Fatfs_handle *handle = static_cast(vfs_handle); - if (!handle->file) { - Genode::error("READ_ERR_INVALID"); - return READ_ERR_INVALID; - } - if ((handle->status_flags()&OPEN_MODE_ACCMODE) == OPEN_MODE_WRONLY) - return READ_ERR_INVALID; + Fatfs_file_handle *handle = static_cast(vfs_handle); - FRESULT fres; - FIL *fil = &handle->file->fil; - - fres = f_lseek(fil, handle->seek()); - if (fres == FR_OK) { - UINT bw = 0; - fres = f_read(fil, buf, buf_size, &bw); - out_count = bw; - } - - switch (fres) { - case FR_OK: return READ_OK; - case FR_INVALID_OBJECT: return READ_ERR_INVALID; - case FR_TIMEOUT: return READ_ERR_WOULD_BLOCK; - case FR_DISK_ERR: return READ_ERR_IO; - case FR_INT_ERR: return READ_ERR_IO; - case FR_DENIED: return READ_ERR_IO; - default: return READ_ERR_IO; - } + return handle->complete_read(buf, buf_size, out_count); } Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size len) override { FRESULT res; - Fatfs_handle *handle = static_cast(vfs_handle); + Fatfs_file_handle *handle = static_cast(vfs_handle); if (!handle->file) return FTRUNCATE_ERR_NO_PERM; if ((handle->status_flags()&OPEN_MODE_ACCMODE) == OPEN_MODE_RDONLY) @@ -618,4 +684,4 @@ extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) { static Fatfs_factory factory; return &factory; -} \ No newline at end of file +} diff --git a/repos/libports/src/lib/vfs/jitterentropy/vfs_jitterentropy.h b/repos/libports/src/lib/vfs/jitterentropy/vfs_jitterentropy.h index 48b8b479fa..766ac24efe 100644 --- a/repos/libports/src/lib/vfs/jitterentropy/vfs_jitterentropy.h +++ b/repos/libports/src/lib/vfs/jitterentropy/vfs_jitterentropy.h @@ -49,6 +49,54 @@ class Jitterentropy_file_system : public Vfs::Single_file_system return true; } + class Jitterentropy_vfs_handle : public Single_vfs_handle + { + private: + + struct rand_data *_ec_stir; + bool &_initialized; + + public: + + Jitterentropy_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + struct rand_data *ec_stir, + bool &initialized) + : Single_vfs_handle(ds, fs, alloc, 0), + _ec_stir(ec_stir), + _initialized(initialized) { } + + Read_result read(char *dst, Vfs::file_size count, + Vfs::file_size &out_count) override + { + if (!_initialized) + return READ_ERR_IO; + + enum { MAX_BUF_LEN = 256 }; + char buf[MAX_BUF_LEN]; + + size_t len = count > MAX_BUF_LEN ? MAX_BUF_LEN : count; + + if (jent_read_entropy(_ec_stir, buf, len) < 0) + return READ_ERR_IO; + + Genode::memcpy(dst, buf, len); + + out_count = len; + + return READ_OK; + } + + Write_result write(char const *src, Vfs::file_size count, + Vfs::file_size &out_count) override + { + return WRITE_ERR_IO; + } + + bool read_ready() { return true; } + }; + public: Jitterentropy_file_system(Genode::Allocator &alloc, @@ -68,41 +116,23 @@ class Jitterentropy_file_system : public Vfs::Single_file_system static char const *name() { return "jitterentropy"; } char const *type() override { return "jitterentropy"; } + /********************************* + ** Directory service interface ** + *********************************/ - /******************************** - ** File I/O service interface ** - ********************************/ - - Write_result write(Vfs::Vfs_handle *, char const *, - Vfs::file_size count, - Vfs::file_size &count_out) override + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Genode::Allocator &alloc) override { - return WRITE_ERR_IO; + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) + Jitterentropy_vfs_handle(*this, *this, alloc, _ec_stir, + _initialized); + return OPEN_OK; } - Read_result read(Vfs::Vfs_handle *vfs_handle, char *dst, - Vfs::file_size count, - Vfs::file_size &out_count) override - { - if (!_initialized) - return READ_ERR_IO; - - enum { MAX_BUF_LEN = 256 }; - char buf[MAX_BUF_LEN]; - - size_t len = count > MAX_BUF_LEN ? MAX_BUF_LEN : count; - - if (jent_read_entropy(_ec_stir, buf, len) < 0) - return READ_ERR_IO; - - Genode::memcpy(dst, buf, len); - - out_count = len; - - return READ_OK; - } - - bool read_ready(Vfs::Vfs_handle *) override { return true; } }; #endif /* _JITTERENTROPY_FILE_SYSTEM_H_ */ diff --git a/repos/libports/src/server/fatfs_fs/main.cc b/repos/libports/src/server/fatfs_fs/main.cc index 8a1ec658be..becee163a7 100644 --- a/repos/libports/src/server/fatfs_fs/main.cc +++ b/repos/libports/src/server/fatfs_fs/main.cc @@ -108,6 +108,10 @@ class Fatfs_fs::Session_component : public Session_rpc_object case Packet_descriptor::READ_READY: /* not supported */ break; + + case Packet_descriptor::SYNC: + /* not supported */ + break; } packet.length(res_length); diff --git a/repos/libports/src/server/fuse_fs/fuse_fs_main.cc b/repos/libports/src/server/fuse_fs/fuse_fs_main.cc index 7c9b5eb973..91c8889f4f 100644 --- a/repos/libports/src/server/fuse_fs/fuse_fs_main.cc +++ b/repos/libports/src/server/fuse_fs/fuse_fs_main.cc @@ -95,6 +95,10 @@ class Fuse_fs::Session_component : public Session_rpc_object case Packet_descriptor::READ_READY: /* not supported */ break; + + case Packet_descriptor::SYNC: + Fuse::sync_fs(); + break; } packet.length(res_length); @@ -431,11 +435,6 @@ class Fuse_fs::Session_component : public Session_rpc_object throw Invalid_handle(); } } - - void sync(Node_handle) override - { - Fuse::sync_fs(); - } }; diff --git a/repos/os/include/file_system_session/client.h b/repos/os/include/file_system_session/client.h index dbb2846c92..4a15352857 100644 --- a/repos/os/include/file_system_session/client.h +++ b/repos/os/include/file_system_session/client.h @@ -111,11 +111,6 @@ class File_system::Session_client : public Genode::Rpc_client { call(from_dir, from_name, to_dir, to_name); } - - void sync(Node_handle node) override - { - call(node); - } }; #endif /* _INCLUDE__FILE_SYSTEM_SESSION__CLIENT_H_ */ diff --git a/repos/os/include/file_system_session/file_system_session.h b/repos/os/include/file_system_session/file_system_session.h index e5f303268e..e6ec64df78 100644 --- a/repos/os/include/file_system_session/file_system_session.h +++ b/repos/os/include/file_system_session/file_system_session.h @@ -114,7 +114,20 @@ class File_system::Packet_descriptor : public Genode::Packet_descriptor { public: - enum Opcode { READ, WRITE, CONTENT_CHANGED, READ_READY }; + enum Opcode { + READ, + WRITE, + CONTENT_CHANGED, + READ_READY, + + /** + * Synchronize file system + * + * This is only needed by file systems that maintain an internal + * cache, which needs to be flushed on certain occasions. + */ + SYNC + }; private: @@ -374,17 +387,6 @@ struct File_system::Session : public Genode::Session virtual void move(Dir_handle, Name const &from, Dir_handle, Name const &to) = 0; - /** - * Synchronize file system - * - * This is only needed by file systems that maintain an internal - * cache, which needs to be flushed on certain occasions. - * - * \throw Invalid_handle node handle is invalid - * - */ - virtual void sync(Node_handle) { } - /******************* ** RPC interface ** @@ -433,13 +435,10 @@ struct File_system::Session : public Genode::Session GENODE_TYPE_LIST(Invalid_handle, Invalid_name, Lookup_failed, Permission_denied), Dir_handle, Name const &, Dir_handle, Name const &); - GENODE_RPC_THROW(Rpc_sync, void, sync, - GENODE_TYPE_LIST(Invalid_handle), - Node_handle); GENODE_RPC_INTERFACE(Rpc_tx_cap, Rpc_file, Rpc_symlink, Rpc_dir, Rpc_node, Rpc_close, Rpc_status, Rpc_control, Rpc_unlink, - Rpc_truncate, Rpc_move, Rpc_sync); + Rpc_truncate, Rpc_move); }; #endif /* _INCLUDE__FILE_SYSTEM_SESSION__FILE_SYSTEM_SESSION_H_ */ diff --git a/repos/os/include/vfs/dir_file_system.h b/repos/os/include/vfs/dir_file_system.h index aad2d6e1b7..af824e5a97 100644 --- a/repos/os/include/vfs/dir_file_system.h +++ b/repos/os/include/vfs/dir_file_system.h @@ -16,6 +16,7 @@ #ifndef _INCLUDE__VFS__DIR_FILE_SYSTEM_H_ #define _INCLUDE__VFS__DIR_FILE_SYSTEM_H_ +#include #include #include @@ -31,6 +32,34 @@ class Vfs::Dir_file_system : public File_system private: + struct Dir_vfs_handle : Vfs_handle + { + struct Sync_dir_handle_element; + + typedef Genode::Registry Sync_dir_handle_registry; + + struct Sync_dir_handle_element : Sync_dir_handle_registry::Element + { + Vfs_handle &vfs_handle; + Sync_dir_handle_element(Sync_dir_handle_registry ®istry, + Vfs_handle &vfs_handle) + : Sync_dir_handle_registry::Element(registry, *this), + vfs_handle(vfs_handle) { } + }; + + Absolute_path path; + File_system *fs_for_complete_read { nullptr }; + Vfs_handle *fs_dir_handle { nullptr }; + Sync_dir_handle_registry sync_dir_handle_registry; + + Dir_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + char const *path) + : Vfs_handle(ds, fs, alloc, 0), + path(path) { } + }; + /* pointer to first child file system */ File_system *_first_file_system; @@ -130,6 +159,9 @@ class Vfs::Dir_file_system : public File_system if (_root()) return path; + if (strcmp(path, "/") == 0) + return path; + /* skip heading slash in path if present */ if (path[0] == '/') path++; @@ -150,49 +182,6 @@ class Vfs::Dir_file_system : public File_system return path; } - /** - * The 'path' is relative to the child file systems. - */ - Dirent_result _dirent_of_file_systems(char const *path, file_offset index, Dirent &out) - { - int base = 0; - for (File_system *fs = _first_file_system; fs; fs = fs->next) { - - /* - * Determine number of matching directory entries within - * the current file system. - */ - int const fs_num_dirent = fs->num_dirent(path); - - /* - * Query directory entry if index lies with the file - * system. - */ - if (index - base < fs_num_dirent) { - index = index - base; - return fs->dirent(path, index, out);; - } - - /* adjust base index for next file system */ - base += fs_num_dirent; - } - - out.type = DIRENT_TYPE_END; - return DIRENT_OK; - } - - void _dirent_of_this_dir_node(file_offset index, Dirent &out) - { - if (index == 0) { - strncpy(out.name, _name, sizeof(out.name)); - - out.type = DIRENT_TYPE_DIRECTORY; - out.fileno = 1; - } else { - out.type = DIRENT_TYPE_END; - } - } - /* * Accumulate number of directory entries that match in any of * our sub file systems. @@ -206,6 +195,100 @@ class Vfs::Dir_file_system : public File_system return cnt; } + bool _queue_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle) + { + file_offset index = dir_vfs_handle->seek() / sizeof(Dirent); + + char const *sub_path = _sub_path(dir_vfs_handle->path.base()); + + if (strlen(sub_path) == 0) + sub_path = "/"; + + int base = 0; + for (File_system *fs = _first_file_system; fs; fs = fs->next) { + + /* + * Determine number of matching directory entries within + * the current file system. + */ + int const fs_num_dirent = fs->num_dirent(sub_path); + + /* + * Query directory entry if index lies with the file + * system. + */ + if (index - base < fs_num_dirent) { + + dir_vfs_handle->fs_for_complete_read = fs; + + Opendir_result opendir_result = + fs->opendir(sub_path, false, + &dir_vfs_handle->fs_dir_handle, + dir_vfs_handle->alloc()); + + /* + * Errors of this kind can only be communicated by + * 'complete_read()' + */ + if (opendir_result != OPENDIR_OK) + return true; + + dir_vfs_handle->fs_dir_handle->context = + dir_vfs_handle->context; + + index = index - base; + dir_vfs_handle->fs_dir_handle->seek(index * sizeof(Dirent)); + + bool result = fs->queue_read(dir_vfs_handle->fs_dir_handle, + sizeof(Dirent)); + + return result; + } + + /* adjust base index for next file system */ + base += fs_num_dirent; + } + + return true; + } + + Read_result _complete_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle, + char *dst, file_size count, + file_size &out_count) + { + if (!dir_vfs_handle->fs_for_complete_read || + !dir_vfs_handle->fs_dir_handle) { + + /* + * no fs was found for the given index or + * fs->opendir() failed + */ + + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + Dirent *dirent = (Dirent*)dst; + *dirent = Dirent(); + + out_count = sizeof(Dirent); + + return READ_OK; + } + + Read_result result = dir_vfs_handle->fs_for_complete_read-> + complete_read(dir_vfs_handle->fs_dir_handle, + dst, count, out_count); + + if (result != READ_OK) + return result; + + dir_vfs_handle->fs_for_complete_read->close(dir_vfs_handle->fs_dir_handle); + dir_vfs_handle->fs_dir_handle = nullptr; + dir_vfs_handle->fs_for_complete_read = nullptr; + + return result; + } + public: Dir_file_system(Genode::Env &env, @@ -330,28 +413,6 @@ class Vfs::Dir_file_system : public File_system return STAT_ERR_NO_ENTRY; } - Dirent_result dirent(char const *path, file_offset index, Dirent &out) override - { - if (_root()) - return _dirent_of_file_systems(path, index, out); - - if (strcmp(path, "/") == 0) { - _dirent_of_this_dir_node(index, out); - return DIRENT_OK; - } - - /* path contains at least one element */ - - /* remove current element from path */ - path = _sub_path(path); - - /* path does not lie within our tree */ - if (!path) - return DIRENT_ERR_INVALID_PATH; - - return _dirent_of_file_systems(*path ? path : "/", index, out); - } - file_size num_dirent(char const *path) override { if (_root()) { @@ -383,7 +444,11 @@ class Vfs::Dir_file_system : public File_system */ bool directory(char const *path) override { + if (strcmp(path, "/") == 0) + return true; + path = _sub_path(path); + if (!path) return false; @@ -472,6 +537,65 @@ class Vfs::Dir_file_system : public File_system return OPEN_ERR_UNACCESSIBLE; } + Opendir_result opendir(char const *path, bool create, + Vfs_handle **out_handle, Allocator &alloc) override + { + /* path equals "/" (for reading the name of this directory) */ + if (strcmp(path, "/") == 0) { + if (create) + return OPENDIR_ERR_PERMISSION_DENIED; + *out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc, + path); + return OPENDIR_OK; + } + + char const *sub_path = _sub_path(path); + + if (!sub_path) + return OPENDIR_ERR_LOOKUP_FAILED; + + if (create) { + auto opendir_fn = [&] (File_system &fs, char const *path) + { + Vfs_handle *tmp_handle; + Opendir_result opendir_result = + fs.opendir(path, true, &tmp_handle, alloc); + if (opendir_result == OPENDIR_OK) + fs.close(tmp_handle); + return opendir_result; + }; + + Opendir_result opendir_result = + _dir_op(OPENDIR_ERR_LOOKUP_FAILED, + OPENDIR_ERR_PERMISSION_DENIED, + OPENDIR_OK, + path, opendir_fn); + + if (opendir_result != OPENDIR_OK) + return opendir_result; + } + + *out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc, + path); + + return OPENDIR_OK; + } + + Openlink_result openlink(char const *path, bool create, + Vfs_handle **out_handle, + Allocator &alloc) override + { + auto openlink_fn = [&] (File_system &fs, char const *path) + { + return fs.openlink(path, create, out_handle, alloc); + }; + + return _dir_op(OPENLINK_ERR_LOOKUP_FAILED, + OPENLINK_ERR_PERMISSION_DENIED, + OPENLINK_OK, + path, openlink_fn); + } + void close(Vfs_handle *handle) override { if (handle && (&handle->ds() == this)) @@ -489,18 +613,6 @@ class Vfs::Dir_file_system : public File_system path, unlink_fn); } - Readlink_result readlink(char const *path, char *buf, file_size buf_size, - file_size &out_len) override - { - auto readlink_fn = [&] (File_system &fs, char const *path) - { - return fs.readlink(path, buf, buf_size, out_len); - }; - - return _dir_op(READLINK_ERR_NO_ENTRY, READLINK_ERR_NO_ENTRY, READLINK_OK, - path, readlink_fn); - } - Rename_result rename(char const *from_path, char const *to_path) override { from_path = _sub_path(from_path); @@ -535,29 +647,6 @@ class Vfs::Dir_file_system : public File_system return final; } - Symlink_result symlink(char const *from, char const *to) override - { - auto symlink_fn = [&] (File_system &fs, char const *to) - { - return fs.symlink(from, to); - }; - - return _dir_op(SYMLINK_ERR_NO_ENTRY, SYMLINK_ERR_NO_PERM, SYMLINK_OK, - to, symlink_fn); - } - - Mkdir_result mkdir(char const *path, unsigned mode) override - { - auto mkdir_fn = [&] (File_system &fs, char const *path) - { - return fs.mkdir(path, mode); - }; - - return _dir_op(MKDIR_ERR_NO_ENTRY, MKDIR_ERR_NO_PERM, MKDIR_OK, - path, mkdir_fn); - } - - /*************************** ** File_system interface ** ***************************/ @@ -565,25 +654,6 @@ class Vfs::Dir_file_system : public File_system char const *name() const { return "dir"; } char const *type() override { return "dir"; } - /** - * Synchronize all file systems - */ - void sync(char const *path) override - { - if (strcmp("/", path, 2) == 0) { - for (File_system *fs = _first_file_system; fs; fs = fs->next) - fs->sync("/"); - return; - } - - path = _sub_path(path); - if (!path) - return; - - for (File_system *fs = _first_file_system; fs; fs = fs->next) - fs->sync(path); - } - void apply_config(Genode::Xml_node const &node) override { using namespace Genode; @@ -614,11 +684,56 @@ class Vfs::Dir_file_system : public File_system return WRITE_ERR_INVALID; } - Read_result read(Vfs_handle *, char *, file_size, file_size &) override + bool queue_read(Vfs_handle *vfs_handle, file_size count) override { - return READ_ERR_INVALID; + Dir_vfs_handle *dir_vfs_handle = + static_cast(vfs_handle); + + if (_root()) + return _queue_read_of_file_systems(dir_vfs_handle); + + if (strcmp(dir_vfs_handle->path.base(), "/") == 0) + return true; + + return _queue_read_of_file_systems(dir_vfs_handle); } + Read_result complete_read(Vfs_handle *vfs_handle, + char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + Dir_vfs_handle *dir_vfs_handle = + static_cast(vfs_handle); + + if (_root()) + return _complete_read_of_file_systems(dir_vfs_handle, dst, count, out_count); + + if (strcmp(dir_vfs_handle->path.base(), "/") == 0) { + Dirent *dirent = (Dirent*)dst; + file_offset index = vfs_handle->seek() / sizeof(Dirent); + + if (index == 0) { + strncpy(dirent->name, _name, sizeof(dirent->name)); + + dirent->type = DIRENT_TYPE_DIRECTORY; + dirent->fileno = 1; + } else { + dirent->type = DIRENT_TYPE_END; + } + + out_count = sizeof(Dirent); + + return READ_OK; + } + + return _complete_read_of_file_systems(dir_vfs_handle, dst, count, out_count); + } + Ftruncate_result ftruncate(Vfs_handle *, file_size) override { return FTRUNCATE_ERR_NO_PERM; @@ -639,6 +754,84 @@ class Vfs::Dir_file_system : public File_system return handle->fs().notify_read_ready(handle); } + + bool queue_sync(Vfs_handle *vfs_handle) override + { + Dir_vfs_handle *dir_vfs_handle = + static_cast(vfs_handle); + + char const *sub_path = _sub_path(dir_vfs_handle->path.base()); + + if (strlen(sub_path) == 0) + sub_path = "/"; + + /* + * Call 'opendir()' on each file system and, if successful, + * 'queue_sync()'. If one file system's 'queue_sync()' returns + * false, this function returns false. Any VFS handles returned by + * 'opendir()' are kept in a registry. + */ + for (File_system *fs = _first_file_system; fs; fs = fs->next) { + + bool fs_dir_already_open = false; + + dir_vfs_handle->sync_dir_handle_registry.for_each( + [&] (Dir_vfs_handle::Sync_dir_handle_element &sync_dir_handle_element) { + if (&sync_dir_handle_element.vfs_handle.fs() == fs) { + fs_dir_already_open = true; + return; + } + } + ); + + if (fs_dir_already_open) + continue; + + Vfs_handle *sync_dir_handle; + + if (fs->opendir(sub_path, false, &sync_dir_handle, + dir_vfs_handle->alloc()) != OPENDIR_OK) + continue; + + sync_dir_handle->context = dir_vfs_handle->context; + + new (dir_vfs_handle->alloc()) + Dir_vfs_handle::Sync_dir_handle_element( + dir_vfs_handle->sync_dir_handle_registry, + *sync_dir_handle); + + if (!sync_dir_handle->fs().queue_sync(sync_dir_handle)) + return false; + } + + return true; + } + + Sync_result complete_sync(Vfs_handle *vfs_handle) override + { + Sync_result result = SYNC_OK; + + Dir_vfs_handle *dir_vfs_handle = + static_cast(vfs_handle); + + dir_vfs_handle->sync_dir_handle_registry.for_each( + [&] (Dir_vfs_handle::Sync_dir_handle_element &sync_dir_handle_element) { + + Vfs_handle *vfs_handle = &sync_dir_handle_element.vfs_handle; + + result = + vfs_handle->fs().complete_sync(vfs_handle); + + if (result != SYNC_OK) + return; + + vfs_handle->ds().close(vfs_handle); + destroy(vfs_handle->alloc(), &sync_dir_handle_element); + } + ); + + return result; + } }; #endif /* _INCLUDE__VFS__DIR_FILE_SYSTEM_H_ */ diff --git a/repos/os/include/vfs/directory_service.h b/repos/os/include/vfs/directory_service.h index 5c2b69bc15..d91f54759f 100644 --- a/repos/os/include/vfs/directory_service.h +++ b/repos/os/include/vfs/directory_service.h @@ -55,6 +55,8 @@ struct Vfs::Directory_service OPEN_ERR_EXISTS, OPEN_ERR_NAME_TOO_LONG, OPEN_ERR_NO_SPACE, + OPEN_ERR_OUT_OF_RAM, + OPEN_ERR_OUT_OF_CAPS, OPEN_OK }; @@ -63,8 +65,48 @@ struct Vfs::Directory_service Vfs_handle **handle, Allocator &alloc) = 0; + enum Opendir_result + { + OPENDIR_ERR_LOOKUP_FAILED, + OPENDIR_ERR_NAME_TOO_LONG, + OPENDIR_ERR_NODE_ALREADY_EXISTS, + OPENDIR_ERR_NO_SPACE, + OPENDIR_ERR_OUT_OF_RAM, + OPENDIR_ERR_OUT_OF_CAPS, + OPENDIR_ERR_PERMISSION_DENIED, + OPENDIR_OK + }; + + virtual Opendir_result opendir(char const *path, bool create, + Vfs_handle **handle, Allocator &alloc) + { + return OPENDIR_ERR_LOOKUP_FAILED; + } + + enum Openlink_result + { + OPENLINK_ERR_LOOKUP_FAILED, + OPENLINK_ERR_NAME_TOO_LONG, + OPENLINK_ERR_NODE_ALREADY_EXISTS, + OPENLINK_ERR_NO_SPACE, + OPENLINK_ERR_OUT_OF_RAM, + OPENLINK_ERR_OUT_OF_CAPS, + OPENLINK_ERR_PERMISSION_DENIED, + OPENLINK_OK + }; + + virtual Openlink_result openlink(char const *path, bool create, + Vfs_handle **handle, Allocator &alloc) + { + return OPENLINK_ERR_PERMISSION_DENIED; + } + /** * Close handle resources and deallocate handle + * + * Note: it might be necessary to call 'sync()' before 'close()' + * to ensure that previously written data has been completely + * processed. */ virtual void close(Vfs_handle *handle) = 0; @@ -97,6 +139,10 @@ struct Vfs::Directory_service enum Stat_result { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS, STAT_ERR_NO_PERM, STAT_OK }; + /* + * Note: it might be necessary to call 'sync()' before 'stat()' + * to get the correct file size. + */ virtual Stat_result stat(char const *path, Stat &) = 0; @@ -104,8 +150,6 @@ struct Vfs::Directory_service ** Dirent ** ************/ - enum Dirent_result { DIRENT_ERR_INVALID_PATH, DIRENT_ERR_NO_PERM, DIRENT_OK }; - enum { DIRENT_MAX_NAME_LEN = 128 }; enum Dirent_type { @@ -125,8 +169,6 @@ struct Vfs::Directory_service char name[DIRENT_MAX_NAME_LEN] = { 0 }; }; - virtual Dirent_result dirent(char const *path, file_offset index, Dirent &) = 0; - /************ ** Unlink ** @@ -138,16 +180,6 @@ struct Vfs::Directory_service virtual Unlink_result unlink(char const *path) = 0; - /************** - ** Readlink ** - **************/ - - enum Readlink_result { READLINK_ERR_NO_ENTRY, READLINK_ERR_NO_PERM, READLINK_OK }; - - virtual Readlink_result readlink(char const *path, char *buf, - file_size buf_size, file_size &out_len) = 0; - - /************ ** Rename ** ************/ @@ -158,28 +190,6 @@ struct Vfs::Directory_service virtual Rename_result rename(char const *from, char const *to) = 0; - /*********** - ** Mkdir ** - ***********/ - - enum Mkdir_result { MKDIR_ERR_EXISTS, MKDIR_ERR_NO_ENTRY, - MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM, - MKDIR_ERR_NAME_TOO_LONG, MKDIR_OK}; - - virtual Mkdir_result mkdir(char const *path, unsigned mode) = 0; - - - /************* - ** Symlink ** - *************/ - - enum Symlink_result { SYMLINK_ERR_EXISTS, SYMLINK_ERR_NO_ENTRY, - SYMLINK_ERR_NO_SPACE, SYMLINK_ERR_NO_PERM, - SYMLINK_ERR_NAME_TOO_LONG, SYMLINK_OK }; - - virtual Symlink_result symlink(char const *from, char const *to) = 0; - - /** * Return number of directory entries located at given path */ @@ -188,13 +198,6 @@ struct Vfs::Directory_service virtual bool directory(char const *path) = 0; virtual char const *leaf_path(char const *path) = 0; - - /** - * Synchronize file system - * - * This method flushes any delayed operations from the file system. - */ - virtual void sync(char const *path) { } }; #endif /* _INCLUDE__VFS__DIRECTORY_SERVICE_H_ */ diff --git a/repos/os/include/vfs/file_io_service.h b/repos/os/include/vfs/file_io_service.h index ad7fcb9dcf..7bf947ec7c 100644 --- a/repos/os/include/vfs/file_io_service.h +++ b/repos/os/include/vfs/file_io_service.h @@ -40,6 +40,12 @@ struct Vfs::File_io_service ** Write ** ***********/ + /* + * Exception, thrown when 'alloc_packet()' or 'submit_packet()' failed in the + * VFS plugin and the caller should wait for an IO response and try again. + */ + struct Insufficient_buffer { }; + enum Write_result { WRITE_ERR_AGAIN, WRITE_ERR_WOULD_BLOCK, WRITE_ERR_INVALID, WRITE_ERR_IO, WRITE_ERR_INTERRUPT, WRITE_OK }; @@ -58,25 +64,19 @@ struct Vfs::File_io_service READ_ERR_INTERRUPT, READ_QUEUED, READ_OK }; - virtual Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) = 0; - /** - * Read from handle with potential queueing of operation + * Queue read operation * * \return false if queue is full */ - virtual bool queue_read(Vfs_handle *vfs_handle, char *dst, file_size count, - Read_result &out_result, file_size &out_count) + virtual bool queue_read(Vfs_handle *vfs_handle, file_size count) { - out_result = read(vfs_handle, dst, count, out_count); return true; } virtual Read_result complete_read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) - { return read(vfs_handle, dst, count, out_count); } + file_size &out_count) = 0; /** * Return true if the handle has readable data @@ -163,6 +163,27 @@ struct Vfs::File_io_service virtual void register_read_ready_sigh(Vfs_handle *vfs_handle, Signal_context_capability sigh) { } + + /********** + ** Sync ** + **********/ + + enum Sync_result { SYNC_QUEUED, SYNC_OK }; + + /** + * Queue sync operation + * + * \return false if queue is full + */ + virtual bool queue_sync(Vfs_handle *vfs_handle) + { + return true; + } + + virtual Sync_result complete_sync(Vfs_handle *vfs_handle) + { + return SYNC_OK; + } }; #endif /* _INCLUDE__VFS__FILE_IO_SERVICE_H_ */ diff --git a/repos/os/include/vfs/single_file_system.h b/repos/os/include/vfs/single_file_system.h index 7c44f690de..c7de89b1e6 100644 --- a/repos/os/include/vfs/single_file_system.h +++ b/repos/os/include/vfs/single_file_system.h @@ -38,6 +38,77 @@ class Vfs::Single_file_system : public File_system protected: + struct Single_vfs_handle : Vfs_handle + { + using Vfs_handle::Vfs_handle; + + virtual Read_result read(char *dst, file_size count, + file_size &out_count) = 0; + + virtual Write_result write(char const *src, file_size count, + file_size &out_count) = 0; + + virtual bool read_ready() = 0; + }; + + struct Single_vfs_dir_handle : Single_vfs_handle + { + private: + + Node_type _node_type; + char const *_filename; + + public: + + Single_vfs_dir_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Node_type node_type, + char const *filename) + : Single_vfs_handle(ds, fs, alloc, 0), + _node_type(node_type), + _filename(filename) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + file_size index = seek() / sizeof(Dirent); + + Dirent *out = (Dirent*)dst; + + if (index == 0) { + out->fileno = (Genode::addr_t)this; + switch (_node_type) { + case NODE_TYPE_FILE: out->type = DIRENT_TYPE_FILE; break; + case NODE_TYPE_SYMLINK: out->type = DIRENT_TYPE_SYMLINK; break; + case NODE_TYPE_CHAR_DEVICE: out->type = DIRENT_TYPE_CHARDEV; break; + case NODE_TYPE_BLOCK_DEVICE: out->type = DIRENT_TYPE_BLOCKDEV; break; + } + strncpy(out->name, _filename, sizeof(out->name)); + } else { + out->type = DIRENT_TYPE_END; + } + + out_count = sizeof(Dirent); + + return READ_OK; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + return WRITE_ERR_INVALID; + } + + bool read_ready() override { return true; } + }; + bool _root(const char *path) { return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0); @@ -95,27 +166,6 @@ class Vfs::Single_file_system : public File_system return STAT_OK; } - Dirent_result dirent(char const *path, file_offset index, Dirent &out) override - { - if (!_root(path)) - return DIRENT_ERR_INVALID_PATH; - - if (index == 0) { - out.fileno = (Genode::addr_t)this; - switch (_node_type) { - case NODE_TYPE_FILE: out.type = DIRENT_TYPE_FILE; break; - case NODE_TYPE_SYMLINK: out.type = DIRENT_TYPE_SYMLINK; break; - case NODE_TYPE_CHAR_DEVICE: out.type = DIRENT_TYPE_CHARDEV; break; - case NODE_TYPE_BLOCK_DEVICE: out.type = DIRENT_TYPE_BLOCKDEV; break; - } - strncpy(out.name, _filename, sizeof(out.name)); - } else { - out.type = DIRENT_TYPE_END; - } - - return DIRENT_OK; - } - file_size num_dirent(char const *path) override { if (_root(path)) @@ -137,15 +187,20 @@ class Vfs::Single_file_system : public File_system return _single_file(path) ? path : 0; } - Open_result open(char const *path, unsigned, - Vfs_handle **out_handle, - Allocator &alloc) override + Opendir_result opendir(char const *path, bool create, + Vfs_handle **out_handle, + Allocator &alloc) override { - if (!_single_file(path)) - return OPEN_ERR_UNACCESSIBLE; + if (!_root(path)) + return OPENDIR_ERR_LOOKUP_FAILED; - *out_handle = new (alloc) Vfs_handle(*this, *this, alloc, 0); - return OPEN_OK; + if (create) + return OPENDIR_ERR_PERMISSION_DENIED; + + *out_handle = + new (alloc) Single_vfs_dir_handle(*this, *this, alloc, + _node_type, _filename); + return OPENDIR_OK; } void close(Vfs_handle *handle) override @@ -159,12 +214,6 @@ class Vfs::Single_file_system : public File_system return UNLINK_ERR_NO_PERM; } - Readlink_result readlink(char const *, char *, file_size, - file_size &) override - { - return READLINK_ERR_NO_ENTRY; - } - Rename_result rename(char const *from, char const *to) override { if (_single_file(from) || _single_file(to)) @@ -172,21 +221,47 @@ class Vfs::Single_file_system : public File_system return RENAME_ERR_NO_ENTRY; } - Mkdir_result mkdir(char const *, unsigned) override - { - return MKDIR_ERR_NO_PERM; - } - - Symlink_result symlink(char const *, char const *) override - { - return SYMLINK_ERR_NO_ENTRY; - } - /******************************** ** File I/O service interface ** ********************************/ + Read_result complete_read(Vfs_handle *vfs_handle, char *dst, + file_size count, + file_size &out_count) override + { + Single_vfs_handle *handle = + static_cast(vfs_handle); + + if (handle) + return handle->read(dst, count, out_count); + + return READ_ERR_INVALID; + } + + Write_result write(Vfs_handle *vfs_handle, char const *src, file_size count, + file_size &out_count) override + { + Single_vfs_handle *handle = + static_cast(vfs_handle); + + if (handle) + return handle->write(src, count, out_count); + + return WRITE_ERR_INVALID; + } + + bool read_ready(Vfs_handle *vfs_handle) override + { + Single_vfs_handle *handle = + static_cast(vfs_handle); + + if (handle) + return handle->read_ready(); + + return false; + } + Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size) override { return FTRUNCATE_ERR_NO_PERM; diff --git a/repos/os/include/vfs/vfs_handle.h b/repos/os/include/vfs/vfs_handle.h index 87227a6ef4..8c6299ee6d 100644 --- a/repos/os/include/vfs/vfs_handle.h +++ b/repos/os/include/vfs/vfs_handle.h @@ -37,7 +37,7 @@ class Vfs::Vfs_handle /** * Opaque handle context */ - struct Context { }; + struct Context : List::Element { }; Context *context = nullptr; diff --git a/repos/os/src/app/cli_monitor/main.cc b/repos/os/src/app/cli_monitor/main.cc index 0346928f1c..578a0c5705 100644 --- a/repos/os/src/app/cli_monitor/main.cc +++ b/repos/os/src/app/cli_monitor/main.cc @@ -159,7 +159,7 @@ struct Cli_monitor::Main Vfs::Dir_file_system _root_dir { _env, _heap, _vfs_config(), io_response_handler, _global_file_system_factory }; - Subsystem_config_registry _subsystem_config_registry { _root_dir, _heap }; + Subsystem_config_registry _subsystem_config_registry { _root_dir, _heap, _env.ep() }; template struct Registered : T diff --git a/repos/os/src/app/cli_monitor/subsystem_config_registry.h b/repos/os/src/app/cli_monitor/subsystem_config_registry.h index 850a7bce17..f94c522e42 100644 --- a/repos/os/src/app/cli_monitor/subsystem_config_registry.h +++ b/repos/os/src/app/cli_monitor/subsystem_config_registry.h @@ -32,8 +32,9 @@ class Cli_monitor::Subsystem_config_registry private: - Vfs::File_system &_fs; - Genode::Allocator &_alloc; + Vfs::File_system &_fs; + Genode::Allocator &_alloc; + Genode::Entrypoint &_ep; enum { CONFIG_BUF_SIZE = 32*1024 }; char _config_buf[CONFIG_BUF_SIZE]; @@ -62,9 +63,10 @@ class Cli_monitor::Subsystem_config_registry /** * Constructor */ - Subsystem_config_registry(Vfs::File_system &fs, Genode::Allocator &alloc) + Subsystem_config_registry(Vfs::File_system &fs, Genode::Allocator &alloc, + Genode::Entrypoint &ep) : - _fs(fs), _alloc(alloc) + _fs(fs), _alloc(alloc), _ep(ep) { } /** @@ -103,8 +105,17 @@ class Cli_monitor::Subsystem_config_registry } Vfs::file_size out_count = 0; - Vfs::File_io_service::Read_result read_result = - handle->fs().read(handle, _config_buf, sizeof(_config_buf), out_count); + + handle->fs().queue_read(handle, sizeof(_config_buf)); + + Vfs::File_io_service::Read_result read_result; + + while ((read_result = + handle->fs().complete_read(handle, _config_buf, + sizeof(_config_buf), + out_count)) == + Vfs::File_io_service::READ_QUEUED) + _ep.wait_and_dispatch_one_io_signal(); if (read_result != Vfs::File_io_service::READ_OK) { error("could not read '", path, "', err=", (int)read_result); @@ -133,22 +144,35 @@ class Cli_monitor::Subsystem_config_registry { using Genode::error; + Vfs::Vfs_handle *dir_handle; + + if (_fs.opendir(_subsystems_path(), false, &dir_handle, _alloc) != + Vfs::Directory_service::OPENDIR_OK) { + error("could not access directory '", _subsystems_path(), "'"); + return; + } + /* iterate over the directory entries */ for (unsigned i = 0;; i++) { Vfs::Directory_service::Dirent dirent; - Vfs::Directory_service::Dirent_result dirent_result = - _fs.dirent(_subsystems_path(), i, dirent); + dir_handle->seek(i * sizeof(dirent)); + dir_handle->fs().queue_read(dir_handle, sizeof(dirent)); - if (dirent_result != Vfs::Directory_service::DIRENT_OK) { - error("could not access directory '", _subsystems_path(), "'"); + Vfs::file_size out_count; + while (dir_handle->fs().complete_read(dir_handle, + (char*)&dirent, + sizeof(dirent), + out_count) == + Vfs::File_io_service::READ_QUEUED) + _ep.wait_and_dispatch_one_io_signal(); + + if (dirent.type == Vfs::Directory_service::DIRENT_TYPE_END) { + _fs.close(dir_handle); return; } - if (dirent.type == Vfs::Directory_service::DIRENT_TYPE_END) - return; - unsigned const subsystem_suffix = _subsystem_suffix(dirent); /* if file has a matching suffix, apply 'fn' */ @@ -163,6 +187,8 @@ class Cli_monitor::Subsystem_config_registry } catch (Nonexistent_subsystem_config) { } } } + + _fs.close(dir_handle); } }; diff --git a/repos/os/src/lib/vfs/block_file_system.h b/repos/os/src/lib/vfs/block_file_system.h index c801df7274..881c2c722c 100644 --- a/repos/os/src/lib/vfs/block_file_system.h +++ b/repos/os/src/lib/vfs/block_file_system.h @@ -53,62 +53,272 @@ class Vfs::Block_file_system : public Single_file_system Genode::Signal_context _signal_context; Genode::Signal_context_capability _source_submit_cap; - file_size _block_io(file_size nr, void *buf, file_size sz, - bool write, bool bulk = false) + class Block_vfs_handle : public Single_vfs_handle { - Block::Packet_descriptor::Opcode op; - op = write ? Block::Packet_descriptor::WRITE : Block::Packet_descriptor::READ; + private: - file_size packet_size = bulk ? sz : _block_size; - file_size packet_count = bulk ? (sz / _block_size) : 1; + Genode::Allocator &_alloc; + Label &_label; + Lock &_lock; + char *_block_buffer; + unsigned &_block_buffer_count; + Genode::Allocator_avl &_tx_block_alloc; + Block::Connection &_block; + Genode::size_t &_block_size; + Block::sector_t &_block_count; + Block::Session::Operations &_block_ops; + Block::Session::Tx::Source *_tx_source; + bool &_readable; + bool &_writeable; + Genode::Signal_receiver &_signal_receiver; + Genode::Signal_context &_signal_context; + Genode::Signal_context_capability &_source_submit_cap; - Block::Packet_descriptor packet; + file_size _block_io(file_size nr, void *buf, file_size sz, + bool write, bool bulk = false) + { + Block::Packet_descriptor::Opcode op; + op = write ? Block::Packet_descriptor::WRITE : Block::Packet_descriptor::READ; - /* sanity check */ - if (packet_count > _block_buffer_count) { - packet_size = _block_buffer_count * _block_size; - packet_count = _block_buffer_count; - } + file_size packet_size = bulk ? sz : _block_size; + file_size packet_count = bulk ? (sz / _block_size) : 1; - while (true) { - try { - Lock::Guard guard(_lock); + Block::Packet_descriptor packet; - packet = _tx_source->alloc_packet(packet_size); - break; - } catch (Block::Session::Tx::Source::Packet_alloc_failed) { - if (!_tx_source->ready_to_submit()) - _signal_receiver.wait_for_signal(); - else { - if (packet_count > 1) { - packet_size /= 2; - packet_count /= 2; + /* sanity check */ + if (packet_count > _block_buffer_count) { + packet_size = _block_buffer_count * _block_size; + packet_count = _block_buffer_count; + } + + while (true) { + try { + Lock::Guard guard(_lock); + + packet = _tx_source->alloc_packet(packet_size); + break; + } catch (Block::Session::Tx::Source::Packet_alloc_failed) { + if (!_tx_source->ready_to_submit()) + _signal_receiver.wait_for_signal(); + else { + if (packet_count > 1) { + packet_size /= 2; + packet_count /= 2; + } + } } } + Lock::Guard guard(_lock); + + Block::Packet_descriptor p(packet, op, nr, packet_count); + + if (write) + Genode::memcpy(_tx_source->packet_content(p), buf, packet_size); + + _tx_source->submit_packet(p); + p = _tx_source->get_acked_packet(); + + if (!p.succeeded()) { + Genode::error("Could not read block(s)"); + _tx_source->release_packet(p); + return 0; + } + + if (!write) + Genode::memcpy(buf, _tx_source->packet_content(p), packet_size); + + _tx_source->release_packet(p); + return packet_size; } - } - Lock::Guard guard(_lock); - Block::Packet_descriptor p(packet, op, nr, packet_count); + public: - if (write) - Genode::memcpy(_tx_source->packet_content(p), buf, packet_size); + Block_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Label &label, + Lock &lock, + char *block_buffer, + unsigned &block_buffer_count, + Genode::Allocator_avl &tx_block_alloc, + Block::Connection &block, + Genode::size_t &block_size, + Block::sector_t &block_count, + Block::Session::Operations &block_ops, + Block::Session::Tx::Source *tx_source, + bool &readable, + bool &writeable, + Genode::Signal_receiver &signal_receiver, + Genode::Signal_context &signal_context, + Genode::Signal_context_capability &source_submit_cap) + : Single_vfs_handle(ds, fs, alloc, 0), + _alloc(alloc), + _label(label), + _lock(lock), + _block_buffer(block_buffer), + _block_buffer_count(block_buffer_count), + _tx_block_alloc(tx_block_alloc), + _block(block), + _block_size(block_size), + _block_count(block_count), + _block_ops(block_ops), + _tx_source(tx_source), + _readable(readable), + _writeable(writeable), + _signal_receiver(signal_receiver), + _signal_context(signal_context), + _source_submit_cap(source_submit_cap) + { } - _tx_source->submit_packet(p); - p = _tx_source->get_acked_packet(); + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (!_readable) { + Genode::error("block device is not readable"); + return READ_ERR_INVALID; + } - if (!p.succeeded()) { - Genode::error("Could not read block(s)"); - _tx_source->release_packet(p); - return 0; - } + file_size seek_offset = seek(); - if (!write) - Genode::memcpy(buf, _tx_source->packet_content(p), packet_size); + file_size read = 0; + while (count > 0) { + file_size displ = 0; + file_size length = 0; + file_size nbytes = 0; + file_size blk_nr = seek_offset / _block_size; - _tx_source->release_packet(p); - return packet_size; - } + displ = seek_offset % _block_size; + + if ((displ + count) > _block_size) + length = (_block_size - displ); + else + length = count; + + /* + * We take a shortcut and read the blocks all at once if the + * offset is aligned on a block boundary and we the count is a + * multiple of the block size, e.g. 4K reads will be read at + * once. + * + * XXX this is quite hackish because we have to omit partial + * blocks at the end. + */ + if (displ == 0 && (count % _block_size) >= 0 && !(count < _block_size)) { + file_size bytes_left = count - (count % _block_size); + + nbytes = _block_io(blk_nr, dst + read, bytes_left, false, true); + if (nbytes == 0) { + Genode::error("error while reading block:", blk_nr, " from block device"); + return READ_ERR_INVALID; + } + + read += nbytes; + count -= nbytes; + seek_offset += nbytes; + + continue; + } + + nbytes = _block_io(blk_nr, _block_buffer, _block_size, false); + if ((unsigned)nbytes != _block_size) { + Genode::error("error while reading block:", blk_nr, " from block device"); + return READ_ERR_INVALID; + } + + Genode::memcpy(dst + read, _block_buffer + displ, length); + + read += length; + count -= length; + seek_offset += length; + } + + out_count = read; + + return READ_OK; + + } + + Write_result write(char const *buf, file_size count, + file_size &out_count) override + { + if (!_writeable) { + Genode::error("block device is not writeable"); + return WRITE_ERR_INVALID; + } + + file_size seek_offset = seek(); + + file_size written = 0; + while (count > 0) { + file_size displ = 0; + file_size length = 0; + file_size nbytes = 0; + file_size blk_nr = seek_offset / _block_size; + + displ = seek_offset % _block_size; + + if ((displ + count) > _block_size) + length = (_block_size - displ); + else + length = count; + + /* + * We take a shortcut and write as much as possible without + * using the block buffer if the offset is aligned on a block + * boundary and the count is a multiple of the block size, + * e.g. 4K writes will be written at once. + * + * XXX this is quite hackish because we have to omit partial + * blocks at the end. + */ + if (displ == 0 && (count % _block_size) >= 0 && !(count < _block_size)) { + file_size bytes_left = count - (count % _block_size); + + nbytes = _block_io(blk_nr, (void*)(buf + written), + bytes_left, true, true); + if (nbytes == 0) { + Genode::error("error while write block:", blk_nr, " to block device"); + return WRITE_ERR_INVALID; + } + + written += nbytes; + count -= nbytes; + seek_offset += nbytes; + + continue; + } + + /* + * The offset is not aligned on a block boundary. Therefore + * we need to read the block to the block buffer first and + * put the buffer content at the right offset before we can + * write the whole block back. In addition if length is less + * than block size, we also have to read the block first. + */ + if (displ > 0 || length < _block_size) + _block_io(blk_nr, _block_buffer, _block_size, false); + + Genode::memcpy(_block_buffer + displ, buf + written, length); + + nbytes = _block_io(blk_nr, _block_buffer, _block_size, true); + if ((unsigned)nbytes != _block_size) { + Genode::error("error while writing block:", blk_nr, " to block_device"); + return WRITE_ERR_INVALID; + } + + written += length; + count -= length; + seek_offset += length; + } + + out_count = written; + + return WRITE_OK; + + } + + bool read_ready() { return true; } + }; public: @@ -155,6 +365,31 @@ class Vfs::Block_file_system : public Single_file_system ** Directory service interface ** *********************************/ + Open_result open(char const *path, unsigned, + Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) Block_vfs_handle(*this, *this, alloc, + _label, _lock, + _block_buffer, + _block_buffer_count, + _tx_block_alloc, + _block, + _block_size, + _block_count, + _block_ops, + _tx_source, + _readable, + _writeable, + _signal_receiver, + _signal_context, + _source_submit_cap); + return OPEN_OK; + } + Stat_result stat(char const *path, Stat &out) override { Stat_result const result = Single_file_system::stat(path, out); @@ -167,153 +402,6 @@ class Vfs::Block_file_system : public Single_file_system ** File I/O service interface ** ********************************/ - Write_result write(Vfs_handle *vfs_handle, char const *buf, - file_size count, file_size &out_count) override - { - if (!_writeable) { - Genode::error("block device is not writeable"); - return WRITE_ERR_INVALID; - } - - file_size seek_offset = vfs_handle->seek(); - - file_size written = 0; - while (count > 0) { - file_size displ = 0; - file_size length = 0; - file_size nbytes = 0; - file_size blk_nr = seek_offset / _block_size; - - displ = seek_offset % _block_size; - - if ((displ + count) > _block_size) - length = (_block_size - displ); - else - length = count; - - /* - * We take a shortcut and write as much as possible without - * using the block buffer if the offset is aligned on a block - * boundary and the count is a multiple of the block size, - * e.g. 4K writes will be written at once. - * - * XXX this is quite hackish because we have to omit partial - * blocks at the end. - */ - if (displ == 0 && (count % _block_size) >= 0 && !(count < _block_size)) { - file_size bytes_left = count - (count % _block_size); - - nbytes = _block_io(blk_nr, (void*)(buf + written), - bytes_left, true, true); - if (nbytes == 0) { - Genode::error("error while write block:", blk_nr, " to block device"); - return WRITE_ERR_INVALID; - } - - written += nbytes; - count -= nbytes; - seek_offset += nbytes; - - continue; - } - - /* - * The offset is not aligned on a block boundary. Therefore - * we need to read the block to the block buffer first and - * put the buffer content at the right offset before we can - * write the whole block back. In addition if length is less - * than block size, we also have to read the block first. - */ - if (displ > 0 || length < _block_size) - _block_io(blk_nr, _block_buffer, _block_size, false); - - Genode::memcpy(_block_buffer + displ, buf + written, length); - - nbytes = _block_io(blk_nr, _block_buffer, _block_size, true); - if ((unsigned)nbytes != _block_size) { - Genode::error("error while writing block:", blk_nr, " to block_device"); - return WRITE_ERR_INVALID; - } - - written += length; - count -= length; - seek_offset += length; - } - - out_count = written; - - return WRITE_OK; - } - - Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) override - { - if (!_readable) { - Genode::error("block device is not readable"); - return READ_ERR_INVALID; - } - - file_size seek_offset = vfs_handle->seek(); - - file_size read = 0; - while (count > 0) { - file_size displ = 0; - file_size length = 0; - file_size nbytes = 0; - file_size blk_nr = seek_offset / _block_size; - - displ = seek_offset % _block_size; - - if ((displ + count) > _block_size) - length = (_block_size - displ); - else - length = count; - - /* - * We take a shortcut and read the blocks all at once if the - * offset is aligned on a block boundary and we the count is a - * multiple of the block size, e.g. 4K reads will be read at - * once. - * - * XXX this is quite hackish because we have to omit partial - * blocks at the end. - */ - if (displ == 0 && (count % _block_size) >= 0 && !(count < _block_size)) { - file_size bytes_left = count - (count % _block_size); - - nbytes = _block_io(blk_nr, dst + read, bytes_left, false, true); - if (nbytes == 0) { - Genode::error("error while reading block:", blk_nr, " from block device"); - return READ_ERR_INVALID; - } - - read += nbytes; - count -= nbytes; - seek_offset += nbytes; - - continue; - } - - nbytes = _block_io(blk_nr, _block_buffer, _block_size, false); - if ((unsigned)nbytes != _block_size) { - Genode::error("error while reading block:", blk_nr, " from block device"); - return READ_ERR_INVALID; - } - - Genode::memcpy(dst + read, _block_buffer + displ, length); - - read += length; - count -= length; - seek_offset += length; - } - - out_count = read; - - return READ_OK; - } - - bool read_ready(Vfs_handle *) override { return true; } - Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size) override { return FTRUNCATE_OK; diff --git a/repos/os/src/lib/vfs/fs_file_system.h b/repos/os/src/lib/vfs/fs_file_system.h index 8d89e706de..69b9474da1 100644 --- a/repos/os/src/lib/vfs/fs_file_system.h +++ b/repos/os/src/lib/vfs/fs_file_system.h @@ -61,24 +61,262 @@ class Vfs::Fs_file_system : public File_system Read_ready_state read_ready_state = Read_ready_state::IDLE; enum class Queued_state { IDLE, QUEUED, ACK }; - Queued_state queued_read_state = Queued_state::IDLE; - Queued_state queued_write_state = Queued_state::IDLE; + Queued_state queued_read_state = Queued_state::IDLE; + Queued_state queued_sync_state = Queued_state::IDLE; ::File_system::Packet_descriptor queued_read_packet; - ::File_system::Packet_descriptor queued_write_packet; + ::File_system::Packet_descriptor queued_sync_packet; }; - struct Fs_vfs_handle : Vfs_handle, ::File_system::Node, Handle_space::Element, Handle_state + struct Fs_vfs_handle : Vfs_handle, ::File_system::Node, + Handle_space::Element, Handle_state { - Fs_vfs_handle(File_system &fs, Allocator &alloc, int status_flags, - Handle_space &space, Handle_space::Id id) + ::File_system::Connection &_fs; + Io_response_handler &_io_handler; + + bool _queue_read(file_size count, file_size const seek_offset) + { + if (queued_read_state != Handle_state::Queued_state::IDLE) + return false; + + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + /* if not ready to submit suggest retry */ + if (!source.ready_to_submit()) return false; + + file_size const max_packet_size = source.bulk_buffer_size() / 2; + file_size const clipped_count = min(max_packet_size, count); + + ::File_system::Packet_descriptor p; + try { + p = source.alloc_packet(clipped_count); + } catch (::File_system::Session::Tx::Source::Packet_alloc_failed) { + return false; + } + + ::File_system::Packet_descriptor const + packet(p, file_handle(), + ::File_system::Packet_descriptor::READ, + clipped_count, seek_offset); + + read_ready_state = Handle_state::Read_ready_state::IDLE; + queued_read_state = Handle_state::Queued_state::QUEUED; + + /* pass packet to server side */ + source.submit_packet(packet); + + return true; + } + + Read_result _complete_read(void *dst, file_size count, + file_size &out_count) + { + if (queued_read_state != Handle_state::Queued_state::ACK) + return READ_QUEUED; + + /* obtain result packet descriptor with updated status info */ + ::File_system::Packet_descriptor const + packet = queued_read_packet; + + file_size const read_num_bytes = min(packet.length(), count); + + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + memcpy(dst, source.packet_content(packet), read_num_bytes); + + queued_read_state = Handle_state::Queued_state::IDLE; + queued_read_packet = ::File_system::Packet_descriptor(); + + out_count = read_num_bytes; + + source.release_packet(packet); + + /* + * Notify anyone who might have failed on + * 'alloc_packet()' or 'submit_packet()' + */ + _io_handler.handle_io_response(nullptr); + + return READ_OK; + } + + Fs_vfs_handle(File_system &fs, Allocator &alloc, + int status_flags, Handle_space &space, + ::File_system::Node_handle node_handle, + ::File_system::Connection &fs_connection, + Io_response_handler &io_handler) : Vfs_handle(fs, fs, alloc, status_flags), - Handle_space::Element(*this, space, id) + Handle_space::Element(*this, space, node_handle), + _fs(fs_connection), _io_handler(io_handler) { } ::File_system::File_handle file_handle() const { return ::File_system::File_handle { id().value }; } + + virtual bool queue_read(file_size count) + { + Genode::error("Fs_vfs_handle::queue_read() called"); + return true; + } + + virtual Read_result complete_read(char *dst, file_size count, + file_size &out_count) + { + Genode::error("Fs_vfs_handle::complete_read() called"); + return READ_ERR_INVALID; + } + + bool queue_sync() + { + if (queued_sync_state != Handle_state::Queued_state::IDLE) + return true; + + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + /* if not ready to submit suggest retry */ + if (!source.ready_to_submit()) return false; + + ::File_system::Packet_descriptor p; + try { + p = source.alloc_packet(0); + } catch (::File_system::Session::Tx::Source::Packet_alloc_failed) { + return false; + } + + ::File_system::Packet_descriptor const + packet(p, file_handle(), + ::File_system::Packet_descriptor::SYNC, 0); + + queued_sync_state = Handle_state::Queued_state::QUEUED; + + /* pass packet to server side */ + source.submit_packet(packet); + + return true; + } + + Sync_result complete_sync() + { + if (queued_sync_state != Handle_state::Queued_state::ACK) + return SYNC_QUEUED; + + /* obtain result packet descriptor */ + ::File_system::Packet_descriptor const + packet = queued_sync_packet; + + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + queued_sync_state = Handle_state::Queued_state::IDLE; + queued_sync_packet = ::File_system::Packet_descriptor(); + + source.release_packet(packet); + + /* + * Notify anyone who might have failed on + * 'alloc_packet()' or 'submit_packet()' + */ + _io_handler.handle_io_response(nullptr); + + return SYNC_OK; + } + }; + + struct Fs_vfs_file_handle : Fs_vfs_handle + { + using Fs_vfs_handle::Fs_vfs_handle; + + bool queue_read(file_size count) override + { + return _queue_read(count, seek()); + } + + Read_result complete_read(char *dst, file_size count, + file_size &out_count) override + { + return _complete_read(dst, count, out_count); + } + }; + + struct Fs_vfs_dir_handle : Fs_vfs_handle + { + enum { DIRENT_SIZE = sizeof(::File_system::Directory_entry) }; + + using Fs_vfs_handle::Fs_vfs_handle; + + bool queue_read(file_size count) override + { + if (count < sizeof(Dirent)) + return true; + + return _queue_read(DIRENT_SIZE, + (seek() / sizeof(Dirent) * DIRENT_SIZE)); + } + + Read_result complete_read(char *dst, file_size count, + file_size &out_count) override + { + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + using ::File_system::Directory_entry; + + Directory_entry entry; + file_size entry_out_count; + + Read_result read_result = + _complete_read(&entry, DIRENT_SIZE, entry_out_count); + + if (read_result != READ_OK) + return read_result; + + Dirent *dirent = (Dirent*)dst; + + if (entry_out_count < DIRENT_SIZE) { + /* no entry found for the given index, or error */ + *dirent = Dirent(); + out_count = sizeof(Dirent); + return READ_OK; + } + + /* + * The default value has no meaning because the switch below + * assigns a value in each possible branch. But it is needed to + * keep the compiler happy. + */ + Dirent_type type = DIRENT_TYPE_END; + + /* copy-out payload into destination buffer */ + switch (entry.type) { + case Directory_entry::TYPE_DIRECTORY: type = DIRENT_TYPE_DIRECTORY; break; + case Directory_entry::TYPE_FILE: type = DIRENT_TYPE_FILE; break; + case Directory_entry::TYPE_SYMLINK: type = DIRENT_TYPE_SYMLINK; break; + } + + dirent->fileno = entry.inode; + dirent->type = type; + strncpy(dirent->name, entry.name, sizeof(dirent->name)); + + out_count = sizeof(Dirent); + + return READ_OK; + } + }; + + struct Fs_vfs_symlink_handle : Fs_vfs_handle + { + using Fs_vfs_handle::Fs_vfs_handle; + + bool queue_read(file_size count) override + { + return _queue_read(count, seek()); + } + + Read_result complete_read(char *dst, file_size count, + file_size &out_count) override + { + return _complete_read(dst, count, out_count); + } }; /** @@ -86,26 +324,33 @@ class Vfs::Fs_file_system : public File_system */ struct Fs_handle_guard : Fs_vfs_handle { - ::File_system::Session &_fs_session; - ::File_system::Node_handle _fs_handle; + ::File_system::Session &_fs_session; Fs_handle_guard(File_system &fs, ::File_system::Session &fs_session, ::File_system::Node_handle fs_handle, - Handle_space &space) + Handle_space &space, + ::File_system::Connection &fs_connection, + Io_response_handler &io_handler) : - Fs_vfs_handle(fs, *(Allocator*)nullptr, 0, space, Handle_space::Id(fs_handle)), - _fs_session(fs_session), _fs_handle(fs_handle) + Fs_vfs_handle(fs, *(Allocator*)nullptr, 0, space, fs_handle, + fs_connection, io_handler), + _fs_session(fs_session) { } - ~Fs_handle_guard() { _fs_session.close(_fs_handle); } + ~Fs_handle_guard() + { + _fs_session.close(file_handle()); + } }; struct Post_signal_hook : Genode::Entrypoint::Post_signal_hook { - Genode::Entrypoint &_ep; - Io_response_handler &_io_handler; - Vfs_handle::Context *_context = nullptr; + Genode::Entrypoint &_ep; + Io_response_handler &_io_handler; + List _context_list; + Lock _list_lock; + bool _null_context_armed { false }; Post_signal_hook(Genode::Entrypoint &ep, Io_response_handler &io_handler) @@ -113,21 +358,57 @@ class Vfs::Fs_file_system : public File_system void arm(Vfs_handle::Context *context) { - _context = context; + if (!context) { + + if (!_null_context_armed) { + _null_context_armed = true; + _ep.schedule_post_signal_hook(this); + } + + return; + } + + { + Lock::Guard list_guard(_list_lock); + + for (Vfs_handle::Context *list_context = _context_list.first(); + list_context; + list_context = list_context->next()) + { + if (list_context == context) { + /* already in list */ + return; + } + } + + _context_list.insert(context); + } + _ep.schedule_post_signal_hook(this); } void function() override { - /* - * XXX The current implementation executes the post signal hook - * for the last armed context only. When changing this, - * beware that the called handle_io_response() may change - * this object in a signal handler. - */ + Vfs_handle::Context *context = nullptr; - _io_handler.handle_io_response(_context); - _context = nullptr; + for (;;) { + + { + Lock::Guard list_guard(_list_lock); + + context = _context_list.first(); + _context_list.remove(context); + } + + if (!context) { + if (!_null_context_armed) + break; + else + _null_context_armed = false; + } + + _io_handler.handle_io_response(context); + } } }; @@ -191,37 +472,27 @@ class Vfs::Fs_file_system : public File_system file_size const max_packet_size = source.bulk_buffer_size() / 2; count = min(max_packet_size, count); - /* XXX check if alloc_packet() and submit_packet() will succeed! */ + if (!source.ready_to_submit()) + throw Insufficient_buffer(); - Packet_descriptor packet_in(source.alloc_packet(count), - handle.file_handle(), - Packet_descriptor::WRITE, - count, - seek_offset); + try { + Packet_descriptor packet_in(source.alloc_packet(count), + handle.file_handle(), + Packet_descriptor::WRITE, + count, + seek_offset); - memcpy(source.packet_content(packet_in), buf, count); + memcpy(source.packet_content(packet_in), buf, count); - /* wait until packet was acknowledged */ - handle.queued_write_state = Handle_state::Queued_state::QUEUED; - - /* pass packet to server side */ - source.submit_packet(packet_in); - - while (handle.queued_write_state != Handle_state::Queued_state::ACK) { - _env.ep().wait_and_dispatch_one_io_signal(); + /* pass packet to server side */ + source.submit_packet(packet_in); + } catch (::File_system::Session::Tx::Source::Packet_alloc_failed) { + throw Insufficient_buffer(); + } catch (...) { + Genode::error("unhandled exception"); + return 0; } - - /* obtain result packet descriptor with updated status info */ - Packet_descriptor const packet_out = handle.queued_write_packet; - - handle.queued_write_state = Handle_state::Queued_state::IDLE; - handle.queued_write_packet = Packet_descriptor(); - - file_size const write_num_bytes = min(packet_out.length(), count); - - source.release_packet(packet_out); - - return write_num_bytes; + return count; } void _handle_ack() @@ -241,26 +512,42 @@ class Vfs::Fs_file_system : public File_system switch (packet.operation()) { case Packet_descriptor::READ_READY: handle.read_ready_state = Handle_state::Read_ready_state::READY; + _post_signal_hook.arm(handle.context); break; case Packet_descriptor::READ: handle.queued_read_packet = packet; handle.queued_read_state = Handle_state::Queued_state::ACK; + _post_signal_hook.arm(handle.context); break; case Packet_descriptor::WRITE: - handle.queued_write_packet = packet; - handle.queued_write_state = Handle_state::Queued_state::ACK; + /* + * Notify anyone who might have failed on + * 'alloc_packet()' or 'submit_packet()' + */ + _post_signal_hook.arm(nullptr); + break; case Packet_descriptor::CONTENT_CHANGED: + _post_signal_hook.arm(handle.context); + break; + + case Packet_descriptor::SYNC: + handle.queued_sync_packet = packet; + handle.queued_sync_state = Handle_state::Queued_state::ACK; + _post_signal_hook.arm(handle.context); break; } - - _post_signal_hook.arm(handle.context); }); } catch (Handle_space::Unknown_id) { Genode::warning("ack for unknown VFS handle"); } + + if (packet.operation() == Packet_descriptor::WRITE) { + Lock::Guard guard(_lock); + source.release_packet(packet); + } } } @@ -293,61 +580,11 @@ class Vfs::Fs_file_system : public File_system Dataspace_capability dataspace(char const *path) override { - Lock::Guard guard(_lock); - - Absolute_path dir_path(path); - dir_path.strip_last_element(); - - Absolute_path file_name(path); - file_name.keep_only_last_element(); - - Ram_dataspace_capability ds_cap; - char *local_addr = 0; - - try { - ::File_system::Dir_handle dir = _fs.dir(dir_path.base(), - false); - Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space); - - ::File_system::File_handle file = - _fs.file(dir, file_name.base() + 1, - ::File_system::READ_ONLY, false); - Fs_handle_guard file_guard(*this, _fs, file, _handle_space); - - ::File_system::Status status = _fs.status(file); - - Ram_dataspace_capability ds_cap = - _env.ram().alloc(status.size); - - local_addr = _env.rm().attach(ds_cap); - - ::File_system::Session::Tx::Source &source = *_fs.tx(); - file_size const max_packet_size = source.bulk_buffer_size() / 2; - - for (file_size seek_offset = 0; seek_offset < status.size; - seek_offset += max_packet_size) { - - file_size const count = min(max_packet_size, status.size - - seek_offset); - - _read(file_guard, local_addr + seek_offset, count, seek_offset); - } - - _env.rm().detach(local_addr); - - return ds_cap; - } catch(...) { - _env.rm().detach(local_addr); - _env.ram().free(ds_cap); - return Dataspace_capability(); - } + /* cannot be implemented without blocking */ + return Dataspace_capability(); } - void release(char const *path, Dataspace_capability ds_cap) override - { - if (ds_cap.valid()) - _env.ram().free(static_cap_cast(ds_cap)); - } + void release(char const *path, Dataspace_capability ds_cap) override { } Stat_result stat(char const *path, Stat &out) override { @@ -355,7 +592,8 @@ class Vfs::Fs_file_system : public File_system try { ::File_system::Node_handle node = _fs.node(path); - Fs_handle_guard node_guard(*this, _fs, node, _handle_space); + Fs_handle_guard node_guard(*this, _fs, node, _handle_space, + _fs, _io_handler); status = _fs.status(node); } catch (::File_system::Lookup_failed) { return STAT_ERR_NO_ENTRY; } @@ -380,49 +618,6 @@ class Vfs::Fs_file_system : public File_system return STAT_OK; } - Dirent_result dirent(char const *path, file_offset index, Dirent &out) override - { - Lock::Guard guard(_lock); - - using ::File_system::Directory_entry; - - if (strcmp(path, "") == 0) - path = "/"; - - Genode::Constructible<::File_system::Dir_handle> dir_handle; - try { dir_handle.construct(_fs.dir(path, false)); } - catch (::File_system::Lookup_failed) { return DIRENT_ERR_INVALID_PATH; } - catch (::File_system::Name_too_long) { return DIRENT_ERR_INVALID_PATH; } - catch (...) { return DIRENT_ERR_NO_PERM; } - - Fs_handle_guard dir_guard(*this, _fs, *dir_handle, _handle_space); - Directory_entry entry; - - enum { DIRENT_SIZE = sizeof(Directory_entry) }; - - _read(dir_guard, &entry, DIRENT_SIZE, index*DIRENT_SIZE); - - /* - * The default value has no meaning because the switch below - * assigns a value in each possible branch. But it is needed to - * keep the compiler happy. - */ - Dirent_type type = DIRENT_TYPE_END; - - /* copy-out payload into destination buffer */ - switch (entry.type) { - case Directory_entry::TYPE_DIRECTORY: type = DIRENT_TYPE_DIRECTORY; break; - case Directory_entry::TYPE_FILE: type = DIRENT_TYPE_FILE; break; - case Directory_entry::TYPE_SYMLINK: type = DIRENT_TYPE_SYMLINK; break; - } - - out.fileno = entry.inode; - out.type = type; - strncpy(out.name, entry.name, sizeof(out.name)); - - return DIRENT_OK; - } - Unlink_result unlink(char const *path) override { Absolute_path dir_path(path); @@ -433,7 +628,8 @@ class Vfs::Fs_file_system : public File_system try { ::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false); - Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space); + Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space, _fs, + _io_handler); _fs.unlink(dir, file_name.base() + 1); } @@ -446,35 +642,6 @@ class Vfs::Fs_file_system : public File_system return UNLINK_OK; } - Readlink_result readlink(char const *path, char *buf, file_size buf_size, - file_size &out_len) override - { - /* - * Canonicalize path (i.e., path must start with '/') - */ - Absolute_path abs_path(path); - abs_path.strip_last_element(); - - Absolute_path symlink_name(path); - symlink_name.keep_only_last_element(); - - try { - ::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false); - Fs_handle_guard from_dir_guard(*this, _fs, dir_handle, _handle_space); - - ::File_system::Symlink_handle symlink_handle = - _fs.symlink(dir_handle, symlink_name.base() + 1, false); - Fs_handle_guard symlink_guard(*this, _fs, symlink_handle, _handle_space); - - out_len = _read(symlink_guard, buf, buf_size, 0); - - return READLINK_OK; - } - catch (::File_system::Lookup_failed) { return READLINK_ERR_NO_ENTRY; } - catch (::File_system::Invalid_handle) { return READLINK_ERR_NO_ENTRY; } - catch (...) { return READLINK_ERR_NO_PERM; } - } - Rename_result rename(char const *from_path, char const *to_path) override { if ((strcmp(from_path, to_path) == 0) && leaf_path(from_path)) @@ -493,10 +660,16 @@ class Vfs::Fs_file_system : public File_system to_file_name.keep_only_last_element(); try { - ::File_system::Dir_handle from_dir = _fs.dir(from_dir_path.base(), false); - Fs_handle_guard from_dir_guard(*this, _fs, from_dir, _handle_space); - ::File_system::Dir_handle to_dir = _fs.dir(to_dir_path.base(), false); - Fs_handle_guard to_dir_guard(*this, _fs, to_dir, _handle_space); + ::File_system::Dir_handle from_dir = + _fs.dir(from_dir_path.base(), false); + + Fs_handle_guard from_dir_guard(*this, _fs, from_dir, + _handle_space, _fs, _io_handler); + + ::File_system::Dir_handle to_dir = _fs.dir(to_dir_path.base(), + false); + Fs_handle_guard to_dir_guard(*this, _fs, to_dir, _handle_space, + _fs, _io_handler); _fs.move(from_dir, from_file_name.base() + 1, to_dir, to_file_name.base() + 1); @@ -507,76 +680,6 @@ class Vfs::Fs_file_system : public File_system return RENAME_OK; } - Mkdir_result mkdir(char const *path, unsigned mode) override - { - /* - * Canonicalize path (i.e., path must start with '/') - */ - Absolute_path abs_path(path); - - try { - _fs.close(_fs.dir(abs_path.base(), true)); - } - catch (::File_system::Permission_denied) { return MKDIR_ERR_NO_PERM; } - catch (::File_system::Node_already_exists) { return MKDIR_ERR_EXISTS; } - catch (::File_system::Lookup_failed) { return MKDIR_ERR_NO_ENTRY; } - catch (::File_system::Name_too_long) { return MKDIR_ERR_NAME_TOO_LONG; } - catch (::File_system::No_space) { return MKDIR_ERR_NO_SPACE; } - catch (::File_system::Out_of_ram) { return MKDIR_ERR_NO_ENTRY; } - catch (::File_system::Out_of_caps) { return MKDIR_ERR_NO_ENTRY; } - - return MKDIR_OK; - } - - Symlink_result symlink(char const *from, char const *to) override - { - auto const from_len = strlen(from); - - /* - * We write to the symlink via the packet stream. Hence we need - * to serialize with other packet-stream operations. - */ - Lock::Guard guard(_lock); - - /* - * Canonicalize path (i.e., path must start with '/') - */ - Absolute_path abs_path(to); - abs_path.strip_last_element(); - - Absolute_path symlink_name(to); - symlink_name.keep_only_last_element(); - - try { - ::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false); - Fs_handle_guard from_dir_guard(*this, _fs, dir_handle, _handle_space); - - ::File_system::Symlink_handle symlink_handle = - _fs.symlink(dir_handle, symlink_name.base() + 1, true); - Fs_handle_guard symlink_guard(*this, _fs, symlink_handle, _handle_space); - - auto const n = _write(symlink_guard, from, from_len, 0); - - /* - * a convention at the VFS server is to return an invalid - * result length when the target is too long - */ - if (n != from_len) { - return n ? SYMLINK_ERR_NAME_TOO_LONG : SYMLINK_ERR_NO_PERM; - } - } - catch (::File_system::Invalid_handle) { return SYMLINK_ERR_NO_ENTRY; } - catch (::File_system::Node_already_exists) { return SYMLINK_ERR_EXISTS; } - catch (::File_system::Invalid_name) { return SYMLINK_ERR_NAME_TOO_LONG; } - catch (::File_system::Lookup_failed) { return SYMLINK_ERR_NO_ENTRY; } - catch (::File_system::Permission_denied) { return SYMLINK_ERR_NO_PERM; } - catch (::File_system::No_space) { return SYMLINK_ERR_NO_SPACE; } - catch (::File_system::Out_of_ram) { return SYMLINK_ERR_NO_ENTRY; } - catch (::File_system::Out_of_caps) { return SYMLINK_ERR_NO_ENTRY; } - - return SYMLINK_OK; - } - file_size num_dirent(char const *path) override { if (strcmp(path, "") == 0) @@ -584,7 +687,8 @@ class Vfs::Fs_file_system : public File_system ::File_system::Node_handle node; try { node = _fs.node(path); } catch (...) { return 0; } - Fs_handle_guard node_guard(*this, _fs, node, _handle_space); + Fs_handle_guard node_guard(*this, _fs, node, _handle_space, _fs, + _io_handler); ::File_system::Status status = _fs.status(node); @@ -595,7 +699,8 @@ class Vfs::Fs_file_system : public File_system { try { ::File_system::Node_handle node = _fs.node(path); - Fs_handle_guard node_guard(*this, _fs, node, _handle_space); + Fs_handle_guard node_guard(*this, _fs, node, _handle_space, + _fs, _io_handler); ::File_system::Status status = _fs.status(node); @@ -616,7 +721,8 @@ class Vfs::Fs_file_system : public File_system return path; } - Open_result open(char const *path, unsigned vfs_mode, Vfs_handle **out_handle, Genode::Allocator& alloc) override + Open_result open(char const *path, unsigned vfs_mode, Vfs_handle **out_handle, + Genode::Allocator& alloc) override { Lock::Guard guard(_lock); @@ -639,14 +745,16 @@ class Vfs::Fs_file_system : public File_system try { ::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false); - Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space); + Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space, _fs, + _io_handler); - ::File_system::File_handle file = _fs.file(dir, file_name.base() + 1, + ::File_system::File_handle file = _fs.file(dir, + file_name.base() + 1, mode, create); - Handle_space::Id id { file }; *out_handle = new (alloc) - Fs_vfs_handle(*this, alloc, vfs_mode, _handle_space, id); + Fs_vfs_file_handle(*this, alloc, vfs_mode, _handle_space, + file, _fs, _io_handler); } catch (::File_system::Lookup_failed) { return OPEN_ERR_UNACCESSIBLE; } catch (::File_system::Permission_denied) { return OPEN_ERR_NO_PERM; } @@ -655,12 +763,79 @@ class Vfs::Fs_file_system : public File_system catch (::File_system::Invalid_name) { return OPEN_ERR_NAME_TOO_LONG; } catch (::File_system::Name_too_long) { return OPEN_ERR_NAME_TOO_LONG; } catch (::File_system::No_space) { return OPEN_ERR_NO_SPACE; } - catch (::File_system::Out_of_ram) { return OPEN_ERR_NO_PERM; } - catch (::File_system::Out_of_caps) { return OPEN_ERR_NO_PERM; } + catch (::File_system::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (::File_system::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } return OPEN_OK; } + Opendir_result opendir(char const *path, bool create, + Vfs_handle **out_handle, Allocator &alloc) override + { + Lock::Guard guard(_lock); + + Absolute_path dir_path(path); + + try { + ::File_system::Dir_handle dir = _fs.dir(dir_path.base(), create); + + *out_handle = new (alloc) + Fs_vfs_dir_handle(*this, alloc, ::File_system::READ_ONLY, + _handle_space, dir, _fs, _io_handler); + } + catch (::File_system::Lookup_failed) { return OPENDIR_ERR_LOOKUP_FAILED; } + catch (::File_system::Name_too_long) { return OPENDIR_ERR_NAME_TOO_LONG; } + catch (::File_system::Node_already_exists) { return OPENDIR_ERR_NODE_ALREADY_EXISTS; } + catch (::File_system::No_space) { return OPENDIR_ERR_NO_SPACE; } + catch (::File_system::Out_of_ram) { return OPENDIR_ERR_OUT_OF_RAM; } + catch (::File_system::Out_of_caps) { return OPENDIR_ERR_OUT_OF_CAPS; } + catch (::File_system::Permission_denied) { return OPENDIR_ERR_PERMISSION_DENIED; } + + return OPENDIR_OK; + } + + Openlink_result openlink(char const *path, bool create, + Vfs_handle **out_handle, Allocator &alloc) override + { + Lock::Guard guard(_lock); + + /* + * Canonicalize path (i.e., path must start with '/') + */ + Absolute_path abs_path(path); + abs_path.strip_last_element(); + + Absolute_path symlink_name(path); + symlink_name.keep_only_last_element(); + + try { + ::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), + false); + + Fs_handle_guard from_dir_guard(*this, _fs, dir_handle, + _handle_space, _fs, _io_handler); + + ::File_system::Symlink_handle symlink_handle = + _fs.symlink(dir_handle, symlink_name.base() + 1, create); + + *out_handle = new (alloc) + Fs_vfs_symlink_handle(*this, alloc, + ::File_system::READ_ONLY, + _handle_space, symlink_handle, _fs, + _io_handler); + + return OPENLINK_OK; + } + catch (::File_system::Invalid_handle) { return OPENLINK_ERR_LOOKUP_FAILED; } + catch (::File_system::Invalid_name) { return OPENLINK_ERR_LOOKUP_FAILED; } + catch (::File_system::Lookup_failed) { return OPENLINK_ERR_LOOKUP_FAILED; } + catch (::File_system::Node_already_exists) { return OPENLINK_ERR_NODE_ALREADY_EXISTS; } + catch (::File_system::No_space) { return OPENLINK_ERR_NO_SPACE; } + catch (::File_system::Out_of_ram) { return OPENLINK_ERR_OUT_OF_RAM; } + catch (::File_system::Out_of_caps) { return OPENLINK_ERR_OUT_OF_CAPS; } + catch (::File_system::Permission_denied) { return OPENLINK_ERR_PERMISSION_DENIED; } + } + void close(Vfs_handle *vfs_handle) override { if (!vfs_handle) return; @@ -669,10 +844,8 @@ class Vfs::Fs_file_system : public File_system Fs_vfs_handle *fs_handle = static_cast(vfs_handle); - if (fs_handle) { - _fs.close(fs_handle->file_handle()); - destroy(fs_handle->alloc(), fs_handle); - } + _fs.close(fs_handle->file_handle()); + destroy(fs_handle->alloc(), fs_handle); } @@ -683,15 +856,6 @@ class Vfs::Fs_file_system : public File_system static char const *name() { return "fs"; } char const *type() override { return "fs"; } - void sync(char const *path) override - { - try { - ::File_system::Node_handle node = _fs.node(path); - _fs.sync(node); - _fs.close(node); - } catch (...) { } - } - /******************************** ** File I/O service interface ** @@ -709,60 +873,13 @@ class Vfs::Fs_file_system : public File_system return WRITE_OK; } - Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) override - { - Lock::Guard guard(_lock); - - Fs_vfs_handle &handle = static_cast(*vfs_handle); - - /* reset the ready_ready state */ - handle.read_ready_state = Handle_state::Read_ready_state::IDLE; - - out_count = _read(handle, dst, count, handle.seek()); - - return READ_OK; - } - - bool queue_read(Vfs_handle *vfs_handle, char *dst, file_size count, - Read_result &out_result, file_size &out_count) override + bool queue_read(Vfs_handle *vfs_handle, file_size count) override { Lock::Guard guard(_lock); Fs_vfs_handle *handle = static_cast(vfs_handle); - if (handle->queued_read_state != Handle_state::Queued_state::IDLE) - return false; - - ::File_system::Session::Tx::Source &source = *_fs.tx(); - - /* if not ready to submit suggest retry */ - if (!source.ready_to_submit()) return false; - - file_size const max_packet_size = source.bulk_buffer_size() / 2; - file_size const clipped_count = min(max_packet_size, count); - - ::File_system::Packet_descriptor p; - try { - p = source.alloc_packet(clipped_count); - } catch (::File_system::Session::Tx::Source::Packet_alloc_failed) { - return false; - } - - ::File_system::Packet_descriptor const - packet(p, handle->file_handle(), - ::File_system::Packet_descriptor::READ, - clipped_count, handle->seek()); - - handle->read_ready_state = Handle_state::Read_ready_state::IDLE; - handle->queued_read_state = Handle_state::Queued_state::QUEUED; - - out_result = READ_QUEUED; - - /* pass packet to server side */ - source.submit_packet(packet); - - return true; + return handle->queue_read(count); } Read_result complete_read(Vfs_handle *vfs_handle, char *dst, file_size count, @@ -770,29 +887,11 @@ class Vfs::Fs_file_system : public File_system { Lock::Guard guard(_lock); + out_count = 0; + Fs_vfs_handle *handle = static_cast(vfs_handle); - if (handle->queued_read_state != Handle_state::Queued_state::ACK) - return READ_QUEUED; - - /* obtain result packet descriptor with updated status info */ - ::File_system::Packet_descriptor const - packet = handle->queued_read_packet; - - file_size const read_num_bytes = min(packet.length(), count); - - ::File_system::Session::Tx::Source &source = *_fs.tx(); - - memcpy(dst, source.packet_content(packet), read_num_bytes); - - handle->queued_read_state = Handle_state::Queued_state::IDLE; - handle->queued_read_packet = ::File_system::Packet_descriptor(); - - out_count = read_num_bytes; - - source.release_packet(packet); - - return READ_OK; + return handle->complete_read(dst, count, out_count); } bool read_ready(Vfs_handle *vfs_handle) override @@ -844,6 +943,24 @@ class Vfs::Fs_file_system : public File_system return FTRUNCATE_OK; } + + bool queue_sync(Vfs_handle *vfs_handle) override + { + Lock::Guard guard(_lock); + + Fs_vfs_handle *handle = static_cast(vfs_handle); + + return handle->queue_sync(); + } + + Sync_result complete_sync(Vfs_handle *vfs_handle) override + { + Lock::Guard guard(_lock); + + Fs_vfs_handle *handle = static_cast(vfs_handle); + + return handle->complete_sync(); + } }; #endif /* _INCLUDE__VFS__FS_FILE_SYSTEM_H_ */ diff --git a/repos/os/src/lib/vfs/inline_file_system.h b/repos/os/src/lib/vfs/inline_file_system.h index 5b025dab69..7fad30c4f0 100644 --- a/repos/os/src/lib/vfs/inline_file_system.h +++ b/repos/os/src/lib/vfs/inline_file_system.h @@ -29,6 +29,64 @@ class Vfs::Inline_file_system : public Single_file_system char const * const _base; file_size const _size; + class Inline_vfs_handle : public Single_vfs_handle + { + private: + + char const * const _base; + file_size const _size; + + public: + + Inline_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + char const * const base, + file_size const size) + : Single_vfs_handle(ds, fs, alloc, 0), + _base(base), _size(size) + { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + /* file read limit is the size of the dataspace */ + file_size const max_size = _size; + + /* current read offset */ + file_size const read_offset = seek(); + + /* maximum read offset, clamped to dataspace size */ + file_size const end_offset = min(count + read_offset, max_size); + + /* source address within the dataspace */ + char const *src = _base + read_offset; + + /* check if end of file is reached */ + if (read_offset >= end_offset) { + out_count = 0; + return READ_OK; + } + + /* copy-out bytes from ROM dataspace */ + file_size const num_bytes = end_offset - read_offset; + + memcpy(dst, src, num_bytes); + + out_count = num_bytes; + return READ_OK; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + out_count = 0; + return WRITE_ERR_INVALID; + } + + bool read_ready() { return true; } + }; + public: Inline_file_system(Genode::Env&, @@ -48,56 +106,24 @@ class Vfs::Inline_file_system : public Single_file_system ** Directory service interface ** ********************************/ + Open_result open(char const *path, unsigned, + Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) Inline_vfs_handle(*this, *this, alloc, + _base, _size); + return OPEN_OK; + } + Stat_result stat(char const *path, Stat &out) override { Stat_result result = Single_file_system::stat(path, out); out.size = _size; return result; } - - - /******************************** - ** File I/O service interface ** - ********************************/ - - Write_result write(Vfs_handle *, char const *, file_size, - file_size &count_out) override - { - count_out = 0; - return WRITE_ERR_INVALID; - } - - Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) override - { - /* file read limit is the size of the dataspace */ - file_size const max_size = _size; - - /* current read offset */ - file_size const read_offset = vfs_handle->seek(); - - /* maximum read offset, clamped to dataspace size */ - file_size const end_offset = min(count + read_offset, max_size); - - /* source address within the dataspace */ - char const *src = _base + read_offset; - - /* check if end of file is reached */ - if (read_offset >= end_offset) { - out_count = 0; - return READ_OK; - } - - /* copy-out bytes from ROM dataspace */ - file_size const num_bytes = end_offset - read_offset; - - memcpy(dst, src, num_bytes); - - out_count = num_bytes; - return READ_OK; - } - - bool read_ready(Vfs_handle *) override { return true; } }; #endif /* _INCLUDE__VFS__INLINE_FILE_SYSTEM_H_ */ diff --git a/repos/os/src/lib/vfs/log_file_system.h b/repos/os/src/lib/vfs/log_file_system.h index ef48cb9dc5..e193e10387 100644 --- a/repos/os/src/lib/vfs/log_file_system.h +++ b/repos/os/src/lib/vfs/log_file_system.h @@ -46,6 +46,50 @@ class Vfs::Log_file_system : public Single_file_system Genode::Log_session &_log; + class Log_vfs_handle : public Single_vfs_handle + { + private: + + Genode::Log_session &_log; + + public: + + Log_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Genode::Log_session &log) + : Single_vfs_handle(ds, fs, alloc, 0), + _log(log) { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + return READ_OK; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + out_count = count; + + /* count does not include the trailing '\0' */ + while (count > 0) { + char tmp[Genode::Log_session::MAX_STRING_LEN]; + int const curr_count = min(count, sizeof(tmp) - 1); + memcpy(tmp, src, curr_count); + tmp[curr_count > 0 ? curr_count : 0] = 0; + _log.write(tmp); + count -= curr_count; + src += curr_count; + } + + return WRITE_OK; + } + + bool read_ready() { return false; } + }; + public: Log_file_system(Genode::Env &env, @@ -61,37 +105,21 @@ class Vfs::Log_file_system : public Single_file_system static const char *name() { return "log"; } char const *type() override { return "log"; } - /******************************** - ** File I/O service interface ** - ********************************/ + /********************************* + ** Directory service interface ** + *********************************/ - Write_result write(Vfs_handle *, char const *src, file_size count, - file_size &out_count) override + Open_result open(char const *path, unsigned, + Vfs_handle **out_handle, + Allocator &alloc) override { - out_count = count; + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; - /* count does not include the trailing '\0' */ - while (count > 0) { - char tmp[Genode::Log_session::MAX_STRING_LEN]; - int const curr_count = min(count, sizeof(tmp) - 1); - memcpy(tmp, src, curr_count); - tmp[curr_count > 0 ? curr_count : 0] = 0; - _log.write(tmp); - count -= curr_count; - src += curr_count; - } - - return WRITE_OK; + *out_handle = new (alloc) Log_vfs_handle(*this, *this, alloc, + _log); + return OPEN_OK; } - - Read_result read(Vfs_handle *, char *, file_size, - file_size &out_count) override - { - out_count = 0; - return READ_OK; - } - - bool read_ready(Vfs_handle *) override { return false; } }; #endif /* _INCLUDE__VFS__LOG_FILE_SYSTEM_H_ */ diff --git a/repos/os/src/lib/vfs/null_file_system.h b/repos/os/src/lib/vfs/null_file_system.h index a2d723b56e..8194c5e83c 100644 --- a/repos/os/src/lib/vfs/null_file_system.h +++ b/repos/os/src/lib/vfs/null_file_system.h @@ -33,28 +33,51 @@ struct Vfs::Null_file_system : Single_file_system static char const *name() { return "null"; } char const *type() override { return "null"; } + struct Null_vfs_handle : Single_vfs_handle + { + Null_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc) + : Single_vfs_handle(ds, fs, alloc, 0) { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + out_count = 0; + + return READ_OK; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + out_count = count; + + return WRITE_OK; + } + + bool read_ready() { return false; } + }; + + /********************************* + ** Directory service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) Null_vfs_handle(*this, *this, alloc); + return OPEN_OK; + } + /******************************** ** File I/O service interface ** ********************************/ - Write_result write(Vfs_handle *handle, char const *, file_size count, - file_size &out_count) override - { - out_count = count; - - return WRITE_OK; - } - - Read_result read(Vfs_handle *vfs_handle, char *, file_size, - file_size &out_count) override - { - out_count = 0; - - return READ_OK; - } - - bool read_ready(Vfs_handle *) override { return false; } - Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size) override { return FTRUNCATE_OK; diff --git a/repos/os/src/lib/vfs/ram_file_system.h b/repos/os/src/lib/vfs/ram_file_system.h index 10d4e10b80..5958dd1ec0 100644 --- a/repos/os/src/lib/vfs/ram_file_system.h +++ b/repos/os/src/lib/vfs/ram_file_system.h @@ -57,6 +57,7 @@ class Vfs_ram::Node : public Genode::Avl_node, public Genode::Lock private: char _name[MAX_NAME_LEN]; + int _open_handles = 0; /** * Generate unique inode number @@ -81,6 +82,46 @@ class Vfs_ram::Node : public Genode::Avl_node, public Genode::Lock virtual Vfs::file_size length() = 0; + /** + * Increment reference counter + */ + void open() { ++_open_handles; } + + bool close_but_keep() + { + if (--_open_handles < 0) { + inode = 0; + return false; + } + return true; + } + + virtual size_t read(char *dst, size_t len, file_size seek_offset) + { + Genode::error("Vfs_ram::Node::read() called"); + return 0; + } + + virtual Vfs::File_io_service::Read_result complete_read(char *dst, + file_size count, + file_size seek_offset, + file_size &out_count) + { + Genode::error("Vfs_ram::Node::complete_read() called"); + return Vfs::File_io_service::READ_ERR_INVALID; + } + + virtual size_t write(char const *src, size_t len, file_size seek_offset) + { + Genode::error("Vfs_ram::Node::write() called"); + return 0; + } + + virtual void truncate(file_size size) + { + Genode::error("Vfs_ram::Node::truncate() called"); + } + /************************ ** Avl node interface ** ************************/ @@ -128,7 +169,6 @@ class Vfs_ram::Node : public Genode::Avl_node, public Genode::Lock ~Guard() { node->unlock(); } }; - }; @@ -143,28 +183,13 @@ class Vfs_ram::File : public Vfs_ram::Node Chunk_level_0 _chunk; file_size _length = 0; - int _open_handles = 0; public: File(char const *name, Allocator &alloc) : Node(name), _chunk(alloc, 0) { } - /** - * Increment reference counter - */ - void open() { ++_open_handles; } - - bool close_but_keep() - { - if (--_open_handles < 0) { - inode = 0; - return false; - } - return true; - } - - size_t read(char *dst, size_t len, file_size seek_offset) + size_t read(char *dst, size_t len, file_size seek_offset) override { file_size const chunk_used_size = _chunk.used_size(); @@ -198,7 +223,16 @@ class Vfs_ram::File : public Vfs_ram::Node return len; } - size_t write(char const *src, size_t len, file_size seek_offset) + Vfs::File_io_service::Read_result complete_read(char *dst, + file_size count, + file_size seek_offset, + file_size &out_count) override + { + out_count = read(dst, count, seek_offset); + return Vfs::File_io_service::READ_OK; + } + + size_t write(char const *src, size_t len, file_size seek_offset) override { if (seek_offset == (file_size)(~0)) seek_offset = _chunk.used_size(); @@ -221,7 +255,7 @@ class Vfs_ram::File : public Vfs_ram::Node file_size length() { return _length; } - void truncate(file_size size) + void truncate(file_size size) override { if (size < _chunk.used_size()) _chunk.truncate(size); @@ -246,6 +280,13 @@ class Vfs_ram::Symlink : public Vfs_ram::Node void set(char const *target, size_t len) { + for (size_t i = 0; i < len; ++i) { + if (target[i] == '\0') { + len = i; + break; + } + } + _len = len; memcpy(_target, target, _len); } @@ -256,6 +297,25 @@ class Vfs_ram::Symlink : public Vfs_ram::Node memcpy(buf, _target, out); return out; } + + Vfs::File_io_service::Read_result complete_read(char *dst, + file_size count, + file_size seek_offset, + file_size &out_count) override + { + out_count = get(dst, count); + return Vfs::File_io_service::READ_OK; + } + + size_t write(char const *src, size_t len, file_size) override + { + if (len > MAX_PATH_LEN) + return 0; + + set(src, len); + + return len; + } }; @@ -305,35 +365,51 @@ class Vfs_ram::Directory : public Vfs_ram::Node file_size length() override { return _count; } - void dirent(file_offset index, Directory_service::Dirent &dirent) + Vfs::File_io_service::Read_result complete_read(char *dst, + file_size count, + file_size seek_offset, + file_size &out_count) override { + typedef Vfs::Directory_service::Dirent Dirent; + + if (count < sizeof(Dirent)) + return Vfs::File_io_service::READ_ERR_INVALID; + + file_offset index = seek_offset / sizeof(Dirent); + + Dirent *dirent = (Dirent*)dst; + *dirent = Dirent(); + out_count = sizeof(Dirent); + Node *node = _entries.first(); if (node) node = node->index(index); if (!node) { - dirent.type = Directory_service::DIRENT_TYPE_END; - return; + dirent->type = Directory_service::DIRENT_TYPE_END; + return Vfs::File_io_service::READ_OK; } - dirent.fileno = node->inode; - strncpy(dirent.name, node->name(), sizeof(dirent.name)); + dirent->fileno = node->inode; + strncpy(dirent->name, node->name(), sizeof(dirent->name)); File *file = dynamic_cast(node); if (file) { - dirent.type = Directory_service::DIRENT_TYPE_FILE; - return; + dirent->type = Directory_service::DIRENT_TYPE_FILE; + return Vfs::File_io_service::READ_OK; } Directory *dir = dynamic_cast(node); if (dir) { - dirent.type = Directory_service::DIRENT_TYPE_DIRECTORY; - return; + dirent->type = Directory_service::DIRENT_TYPE_DIRECTORY; + return Vfs::File_io_service::READ_OK; } Symlink *symlink = dynamic_cast(node); if (symlink) { - dirent.type = Directory_service::DIRENT_TYPE_SYMLINK; - return; + dirent->type = Directory_service::DIRENT_TYPE_SYMLINK; + return Vfs::File_io_service::READ_OK; } + + return Vfs::File_io_service::READ_ERR_INVALID; } }; @@ -344,15 +420,15 @@ class Vfs::Ram_file_system : public Vfs::File_system struct Ram_vfs_handle : Vfs_handle { - Vfs_ram::File &file; + Vfs_ram::Node &node; Ram_vfs_handle(Ram_file_system &fs, Allocator &alloc, int status_flags, - Vfs_ram::File &node) - : Vfs_handle(fs, fs, alloc, status_flags), file(node) + Vfs_ram::Node &node) + : Vfs_handle(fs, fs, alloc, status_flags), node(node) { - file.open(); + node.open(); } }; @@ -459,27 +535,6 @@ class Vfs::Ram_file_system : public Vfs::File_system char const *leaf_path(char const *path) { return lookup(path) ? path : nullptr; } - Mkdir_result mkdir(char const *path, unsigned mode) override - { - using namespace Vfs_ram; - - Directory *parent = lookup_parent(path); - if (!parent) return MKDIR_ERR_NO_ENTRY; - Node::Guard guard(parent); - - char const *name = basename(path); - - if (strlen(name) >= MAX_NAME_LEN) - return MKDIR_ERR_NAME_TOO_LONG; - - if (parent->child(name)) return MKDIR_ERR_EXISTS; - - try { parent->adopt(new (_alloc) Directory(name)); } - catch (Out_of_memory) { return MKDIR_ERR_NO_SPACE; } - - return MKDIR_OK; - } - Open_result open(char const *path, unsigned mode, Vfs_handle **handle, Allocator &alloc) override @@ -514,14 +569,104 @@ class Vfs::Ram_file_system : public Vfs::File_system return OPEN_OK; } + Opendir_result opendir(char const *path, bool create, + Vfs_handle **handle, + Allocator &alloc) override + { + using namespace Vfs_ram; + + Directory *parent = lookup_parent(path); + if (!parent) return OPENDIR_ERR_LOOKUP_FAILED; + Node::Guard guard(parent); + + char const *name = basename(path); + + Directory *dir; + + if (create) { + + if (strlen(name) >= MAX_NAME_LEN) + return OPENDIR_ERR_NAME_TOO_LONG; + + if (parent->child(name)) + return OPENDIR_ERR_NODE_ALREADY_EXISTS; + + try { + dir = new (_alloc) Directory(name); + } catch (Out_of_memory) { return OPENDIR_ERR_NO_SPACE; } + + parent->adopt(dir); + + } else { + + Node *node = lookup(path); + if (!node) return OPENDIR_ERR_LOOKUP_FAILED; + + dir = dynamic_cast(node); + if (!dir) return OPENDIR_ERR_LOOKUP_FAILED; + } + + *handle = new (alloc) Ram_vfs_handle(*this, alloc, + Ram_vfs_handle::STATUS_RDONLY, + *dir); + + return OPENDIR_OK; + } + + Openlink_result openlink(char const *path, bool create, + Vfs_handle **handle, Allocator &alloc) override + { + using namespace Vfs_ram; + + Directory *parent = lookup_parent(path); + if (!parent) return OPENLINK_ERR_LOOKUP_FAILED; + Node::Guard guard(parent); + + char const *name = basename(path); + + Symlink *link; + + Node *node = parent->child(name); + + if (create) { + + if (node) + return OPENLINK_ERR_NODE_ALREADY_EXISTS; + + if (strlen(name) >= MAX_NAME_LEN) + return OPENLINK_ERR_NAME_TOO_LONG; + + try { link = new (_alloc) Symlink(name); } + catch (Out_of_memory) { return OPENLINK_ERR_NO_SPACE; } + + link->lock(); + parent->adopt(link); + link->unlock(); + + } else { + + if (!node) return OPENLINK_ERR_LOOKUP_FAILED; + Node::Guard guard(node); + + link = dynamic_cast(node); + if (!link) return OPENLINK_ERR_LOOKUP_FAILED; + } + + *handle = new (alloc) Ram_vfs_handle(*this, alloc, + Ram_vfs_handle::STATUS_RDWR, + *link); + + return OPENLINK_OK; + } + void close(Vfs_handle *vfs_handle) override { Ram_vfs_handle *ram_handle = static_cast(vfs_handle); if (ram_handle) { - if (!ram_handle->file.close_but_keep()) - destroy(_alloc, &ram_handle->file); + if (!ram_handle->node.close_but_keep()) + destroy(_alloc, &ram_handle->node); destroy(vfs_handle->alloc(), ram_handle); } } @@ -560,80 +705,6 @@ class Vfs::Ram_file_system : public Vfs::File_system return STAT_ERR_NO_ENTRY; } - Dirent_result dirent(char const *path, file_offset index, Dirent &dirent) override - { - using namespace Vfs_ram; - - Node *node = lookup(path); - if (!node) return DIRENT_ERR_INVALID_PATH; - Node::Guard guard(node); - - Directory *dir = dynamic_cast(node); - if (!dir) return DIRENT_ERR_INVALID_PATH; - - dir->dirent(index, dirent); - return DIRENT_OK; - } - - Symlink_result symlink(char const *target, char const *path) override - { - using namespace Vfs_ram; - - auto const target_len = strlen(target); - if (target_len > MAX_PATH_LEN) - return SYMLINK_ERR_NAME_TOO_LONG; - - Symlink *link; - Directory *parent = lookup_parent(path); - if (!parent) return SYMLINK_ERR_NO_ENTRY; - Node::Guard guard(parent); - - char const *name = basename(path); - - Node *node = parent->child(name); - if (node) { - node->lock(); - link = dynamic_cast(node); - if (!link) { - node->unlock(); - return SYMLINK_ERR_EXISTS; - } - } else { - if (strlen(name) >= MAX_NAME_LEN) - return SYMLINK_ERR_NAME_TOO_LONG; - - try { link = new (_alloc) Symlink(name); } - catch (Out_of_memory) { return SYMLINK_ERR_NO_SPACE; } - - link->lock(); - parent->adopt(link); - } - - if (*target) - link->set(target, target_len); - link->unlock(); - return SYMLINK_OK; - } - - Readlink_result readlink(char const *path, char *buf, - file_size buf_size, file_size &out_len) override - { - using namespace Vfs_ram; - Directory *parent = lookup_parent(path); - if (!parent) return READLINK_ERR_NO_ENTRY; - Node::Guard parent_guard(parent); - - Node *node = parent->child(basename(path)); - if (!node) return READLINK_ERR_NO_ENTRY; - Node::Guard guard(node); - - Symlink *link = dynamic_cast(node); - if (!link) return READLINK_ERR_NO_ENTRY; - - out_len = link->get(buf, buf_size); - return READLINK_OK; - } - Rename_result rename(char const *from, char const *to) override { using namespace Vfs_ram; @@ -748,26 +819,23 @@ class Vfs::Ram_file_system : public Vfs::File_system Ram_vfs_handle const *handle = static_cast(vfs_handle); - Vfs_ram::Node::Guard guard(&handle->file); - out = handle->file.write(buf, len, handle->seek()); + Vfs_ram::Node::Guard guard(&handle->node); + out = handle->node.write(buf, len, handle->seek()); return WRITE_OK; } - Read_result read(Vfs_handle *vfs_handle, - char *buf, file_size len, - file_size &out) override + Read_result complete_read(Vfs_handle *vfs_handle, char *dst, file_size count, + file_size &out_count) override { - if ((vfs_handle->status_flags() & OPEN_MODE_ACCMODE) == OPEN_MODE_WRONLY) - return READ_ERR_INVALID; + out_count = 0; Ram_vfs_handle const *handle = static_cast(vfs_handle); - Vfs_ram::Node::Guard guard(&handle->file); + Vfs_ram::Node::Guard guard(&handle->node); - out = handle->file.read(buf, len, handle->seek()); - return READ_OK; + return handle->node.complete_read(dst, count, handle->seek(), out_count); } bool read_ready(Vfs_handle *) override { return true; } @@ -780,9 +848,9 @@ class Vfs::Ram_file_system : public Vfs::File_system Ram_vfs_handle const *handle = static_cast(vfs_handle); - Vfs_ram::Node::Guard guard(&handle->file); + Vfs_ram::Node::Guard guard(&handle->node); - try { handle->file.truncate(len); } + try { handle->node.truncate(len); } catch (Vfs_ram::Out_of_memory) { return FTRUNCATE_ERR_NO_SPACE; } return FTRUNCATE_OK; } diff --git a/repos/os/src/lib/vfs/rom_file_system.h b/repos/os/src/lib/vfs/rom_file_system.h index 8147da12a0..a31ba0624c 100644 --- a/repos/os/src/lib/vfs/rom_file_system.h +++ b/repos/os/src/lib/vfs/rom_file_system.h @@ -46,6 +46,60 @@ class Vfs::Rom_file_system : public Single_file_system Genode::Attached_rom_dataspace _rom; + class Rom_vfs_handle : public Single_vfs_handle + { + private: + + Genode::Attached_rom_dataspace &_rom; + + public: + + Rom_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Genode::Attached_rom_dataspace &rom) + : Single_vfs_handle(ds, fs, alloc, 0), _rom(rom) { } + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + /* file read limit is the size of the dataspace */ + file_size const max_size = _rom.size(); + + /* current read offset */ + file_size const read_offset = seek(); + + /* maximum read offset, clamped to dataspace size */ + file_size const end_offset = min(count + read_offset, max_size); + + /* source address within the dataspace */ + char const *src = _rom.local_addr() + read_offset; + + /* check if end of file is reached */ + if (read_offset >= end_offset) { + out_count = 0; + return READ_OK; + } + + /* copy-out bytes from ROM dataspace */ + file_size const num_bytes = end_offset - read_offset; + + memcpy(dst, src, num_bytes); + + out_count = num_bytes; + return READ_OK; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + out_count = 0; + return WRITE_ERR_INVALID; + } + + bool read_ready() { return true; } + }; + public: Rom_file_system(Genode::Env &env, @@ -65,27 +119,23 @@ class Vfs::Rom_file_system : public Single_file_system ** Directory-service interface ** ********************************/ - Dataspace_capability dataspace(char const *path) override - { - return _rom.cap(); - } - - /* - * Overwrite the default open function to update the ROM dataspace - * each time when opening the corresponding file. - */ Open_result open(char const *path, unsigned, Vfs_handle **out_handle, Allocator &alloc) override { - Open_result const result = - Single_file_system::open(path, 0, out_handle, alloc); + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; _rom.update(); - return result; + *out_handle = new (alloc) Rom_vfs_handle(*this, *this, alloc, _rom); + return OPEN_OK; } + Dataspace_capability dataspace(char const *path) override + { + return _rom.cap(); + } /******************************** ** File I/O service interface ** @@ -100,50 +150,6 @@ class Vfs::Rom_file_system : public Single_file_system return result; } - - - /******************************** - ** File I/O service interface ** - ********************************/ - - Write_result write(Vfs_handle *, char const *, file_size, - file_size &count_out) override - { - count_out = 0; - return WRITE_ERR_INVALID; - } - - Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) override - { - /* file read limit is the size of the dataspace */ - file_size const max_size = _rom.size(); - - /* current read offset */ - file_size const read_offset = vfs_handle->seek(); - - /* maximum read offset, clamped to dataspace size */ - file_size const end_offset = min(count + read_offset, max_size); - - /* source address within the dataspace */ - char const *src = _rom.local_addr() + read_offset; - - /* check if end of file is reached */ - if (read_offset >= end_offset) { - out_count = 0; - return READ_OK; - } - - /* copy-out bytes from ROM dataspace */ - file_size const num_bytes = end_offset - read_offset; - - memcpy(dst, src, num_bytes); - - out_count = num_bytes; - return READ_OK; - } - - bool read_ready(Vfs_handle *) override { return true; } }; #endif /* _INCLUDE__VFS__ROM_FILE_SYSTEM_H_ */ diff --git a/repos/os/src/lib/vfs/rtc_file_system.h b/repos/os/src/lib/vfs/rtc_file_system.h index 291acb0b4c..f387525cb3 100644 --- a/repos/os/src/lib/vfs/rtc_file_system.h +++ b/repos/os/src/lib/vfs/rtc_file_system.h @@ -28,6 +28,63 @@ class Vfs::Rtc_file_system : public Single_file_system Rtc::Connection _rtc; + class Rtc_vfs_handle : public Single_vfs_handle + { + private: + + Rtc::Connection &_rtc; + + public: + + Rtc_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc, + Rtc::Connection &rtc) + : Single_vfs_handle(ds, fs, alloc, 0), + _rtc(rtc) { } + + /** + * Read the current time from the Rtc session + * + * On each read the current time is queried and afterwards formated + * as '%Y-%m-%d %H:%M\n'. + */ + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + enum { TIMESTAMP_LEN = 17 }; + + if (seek() >= TIMESTAMP_LEN) { + out_count = 0; + return READ_OK; + } + + Rtc::Timestamp ts = _rtc.current_time(); + + char buf[TIMESTAMP_LEN+1]; + char *b = buf; + unsigned n = Genode::snprintf(buf, sizeof(buf), "%04u-%02u-%02u %02u:%02u\n", + ts.year, ts.month, ts.day, ts.hour, ts.minute); + n -= seek(); + b += seek(); + + file_size len = count > n ? n : count; + Genode::memcpy(dst, b, len); + out_count = len; + + return READ_OK; + + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + return WRITE_ERR_IO; + } + + bool read_ready() { return true; } + }; + public: Rtc_file_system(Genode::Env &env, @@ -46,6 +103,18 @@ class Vfs::Rtc_file_system : public Single_file_system ** Directory-service interface ** *********************************/ + Open_result open(char const *path, unsigned, + Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) Rtc_vfs_handle(*this, *this, alloc, + _rtc); + return OPEN_OK; + } + Stat_result stat(char const *path, Stat &out) override { Stat_result result = Single_file_system::stat(path, out); @@ -53,53 +122,6 @@ class Vfs::Rtc_file_system : public Single_file_system return result; } - - - /******************************** - ** File I/O service interface ** - ********************************/ - - Write_result write(Vfs_handle *, char const *, file_size, - file_size &) override - { - return WRITE_ERR_IO; - } - - /** - * Read the current time from the Rtc session - * - * On each read the current time is queried and afterwards formated - * as '%Y-%m-%d %H:%M\n'. - */ - Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) override - { - enum { TIMESTAMP_LEN = 17 }; - - file_size seek = vfs_handle->seek(); - - if (seek >= TIMESTAMP_LEN) { - out_count = 0; - return READ_OK; - } - - Rtc::Timestamp ts = _rtc.current_time(); - - char buf[TIMESTAMP_LEN+1]; - char *b = buf; - unsigned n = Genode::snprintf(buf, sizeof(buf), "%04u-%02u-%02u %02u:%02u\n", - ts.year, ts.month, ts.day, ts.hour, ts.minute); - n -= seek; - b += seek; - - file_size len = count > n ? n : count; - Genode::memcpy(dst, b, len); - out_count = len; - - return READ_OK; - } - - bool read_ready(Vfs_handle *) override { return true; } }; #endif /* _INCLUDE__VFS__RTC_FILE_SYSTEM_H_ */ diff --git a/repos/os/src/lib/vfs/symlink_file_system.h b/repos/os/src/lib/vfs/symlink_file_system.h index 7de74ee588..ff7a42ad25 100644 --- a/repos/os/src/lib/vfs/symlink_file_system.h +++ b/repos/os/src/lib/vfs/symlink_file_system.h @@ -47,28 +47,30 @@ class Vfs::Symlink_file_system : public Single_file_system ** Directory-service interface ** *********************************/ - Symlink_result symlink(char const *from, char const *to) override { - return SYMLINK_ERR_EXISTS; } - - Readlink_result readlink(char const *path, - char *buf, - file_size buf_len, - file_size &out_len) override - { - if (!_single_file(path)) - return READLINK_ERR_NO_ENTRY; - out_len = min(buf_len, (file_size)_target.length()-1); - memcpy(buf, _target.string(), out_len); - if (out_len < buf_len) - buf[out_len] = '\0'; - return READLINK_OK; - } - Open_result open(char const *, unsigned, Vfs_handle **out_handle, Allocator&) override { return OPEN_ERR_UNACCESSIBLE; } - void close(Vfs_handle *) override { } + Openlink_result openlink(char const *path, bool create, + Vfs_handle **out_handle, Allocator &alloc) override + { + if (!_single_file(path)) + return OPENLINK_ERR_LOOKUP_FAILED; + + if (create) + return OPENLINK_ERR_NODE_ALREADY_EXISTS; + + *out_handle = new (alloc) Vfs_handle(*this, *this, alloc, 0); + return OPENLINK_OK; + } + + void close(Vfs_handle *vfs_handle) override + { + if (!vfs_handle) + return; + + destroy(vfs_handle->alloc(), vfs_handle); + } /******************************** @@ -79,8 +81,15 @@ class Vfs::Symlink_file_system : public Single_file_system file_size &) override { return WRITE_ERR_INVALID; } - Read_result read(Vfs_handle *, char *, file_size, file_size &) override { - return READ_ERR_INVALID; } + Read_result complete_read(Vfs_handle *, char *buf, file_size buf_len, + file_size &out_len) override + { + out_len = min(buf_len, (file_size)_target.length()-1); + memcpy(buf, _target.string(), out_len); + if (out_len < buf_len) + buf[out_len] = '\0'; + return READ_OK; + } bool read_ready(Vfs_handle *) override { return false; } diff --git a/repos/os/src/lib/vfs/tar_file_system.h b/repos/os/src/lib/vfs/tar_file_system.h index 43cbb643e9..b3eca3be0d 100644 --- a/repos/os/src/lib/vfs/tar_file_system.h +++ b/repos/os/src/lib/vfs/tar_file_system.h @@ -86,23 +86,126 @@ class Vfs::Tar_file_system : public File_system void *data() const { return (char *)this + BLOCK_LEN; } }; + class Node; class Tar_vfs_handle : public Vfs_handle { - private: + protected: - Record const *_record; + Node const *_node; public: - Tar_vfs_handle(File_system &fs, Allocator &alloc, int status_flags, Record const *record) - : Vfs_handle(fs, fs, alloc, status_flags), _record(record) + Tar_vfs_handle(File_system &fs, Allocator &alloc, int status_flags, + Node const *node) + : Vfs_handle(fs, fs, alloc, status_flags), _node(node) { } - Record const *record() const { return _record; } + virtual Read_result read(char *dst, file_size count, + file_size &out_count) = 0; }; + struct Tar_vfs_file_handle : Tar_vfs_handle + { + using Tar_vfs_handle::Tar_vfs_handle; + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + file_size const record_size = _node->record->size(); + + file_size const record_bytes_left = record_size >= seek() + ? record_size - seek() : 0; + + count = min(record_bytes_left, count); + + char const *data = (char *)_node->record->data() + seek(); + + memcpy(dst, data, count); + + out_count = count; + return READ_OK; + } + }; + + struct Tar_vfs_dir_handle : Tar_vfs_handle + { + using Tar_vfs_handle::Tar_vfs_handle; + + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + if (count < sizeof(Dirent)) + return READ_ERR_INVALID; + + Dirent *dirent = (Dirent*)dst; + + /* initialize */ + *dirent = Dirent(); + + file_offset index = seek() / sizeof(Dirent); + + Node const *node = _node->lookup_child(index); + + if (!node) + return READ_OK; + + dirent->fileno = (Genode::addr_t)node; + + Record const *record = node->record; + + while (record && (record->type() == Record::TYPE_HARDLINK)) { + Tar_file_system &tar_fs = static_cast(fs()); + Node const *target = tar_fs.dereference(record->linked_name()); + record = target ? target->record : 0; + } + + if (record) { + switch (record->type()) { + case Record::TYPE_FILE: + dirent->type = DIRENT_TYPE_FILE; break; + case Record::TYPE_SYMLINK: + dirent->type = DIRENT_TYPE_SYMLINK; break; + case Record::TYPE_DIR: + dirent->type = DIRENT_TYPE_DIRECTORY; break; + + default: + Genode::error("unhandled record type ", record->type(), " " + "for ", node->name); + } + } else { + /* If no record exists, assume it is a directory */ + dirent->type = DIRENT_TYPE_DIRECTORY; + } + + strncpy(dirent->name, node->name, sizeof(dirent->name)); + + out_count = sizeof(Dirent); + + return READ_OK; + } + }; + + struct Tar_vfs_symlink_handle : Tar_vfs_handle + { + using Tar_vfs_handle::Tar_vfs_handle; + + Read_result read(char *buf, file_size buf_size, + file_size &out_count) override + { + Record const *record = _node->record; + + file_size const count = min(buf_size, 100ULL); + + memcpy(buf, record->linked_name(), count); + + out_count = count; + + return READ_OK; + } + }; + struct Scanner_policy_path_element { static bool identifier_char(char c, unsigned /* i */) @@ -430,52 +533,6 @@ class Vfs::Tar_file_system : public File_system return STAT_OK; } - Dirent_result dirent(char const *path, file_offset index, Dirent &out) override - { - Node const *node = dereference(path); - - if (!node) - return DIRENT_ERR_INVALID_PATH; - - node = node->lookup_child(index); - - if (!node) { - out.type = DIRENT_TYPE_END; - return DIRENT_OK; - } - - out.fileno = (Genode::addr_t)node; - - Record const *record = node->record; - - while (record && (record->type() == Record::TYPE_HARDLINK)) { - Node const *target = dereference(record->linked_name()); - record = target ? target->record : 0; - } - - if (record) { - switch (record->type()) { - case Record::TYPE_FILE: - out.type = DIRENT_TYPE_FILE; break; - case Record::TYPE_SYMLINK: - out.type = DIRENT_TYPE_SYMLINK; break; - case Record::TYPE_DIR: - out.type = DIRENT_TYPE_DIRECTORY; break; - - default: - Genode::error("unhandled record type ", record->type(), " " - "for ", node->name); - } - } else { - /* If no record exists, assume it is a directory */ - out.type = DIRENT_TYPE_DIRECTORY; - } - - strncpy(out.name, node->name, sizeof(out.name)); - - return DIRENT_OK; - } - Unlink_result unlink(char const *path) override { Node const *node = dereference(path); @@ -485,24 +542,6 @@ class Vfs::Tar_file_system : public File_system return UNLINK_ERR_NO_PERM; } - Readlink_result readlink(char const *path, char *buf, file_size buf_size, - file_size &out_len) override - { - Node const *node = dereference(path); - Record const *record = node ? node->record : 0; - - if (!record || (record->type() != Record::TYPE_SYMLINK)) - return READLINK_ERR_NO_ENTRY; - - file_size const count = min(buf_size, 100ULL); - - memcpy(buf, record->linked_name(), count); - - out_len = count; - - return READLINK_OK; - } - Rename_result rename(char const *from, char const *to) override { if (_root_node.lookup(from) || _root_node.lookup(to)) @@ -510,16 +549,6 @@ class Vfs::Tar_file_system : public File_system return RENAME_ERR_NO_ENTRY; } - Mkdir_result mkdir(char const *, unsigned) override - { - return MKDIR_ERR_NO_PERM; - } - - Symlink_result symlink(char const *, char const *) override - { - return SYMLINK_ERR_NO_ENTRY; - } - file_size num_dirent(char const *path) override { return _cached_num_dirent.num_dirent(path); @@ -548,17 +577,46 @@ class Vfs::Tar_file_system : public File_system return node ? path : 0; } - Open_result open(char const *path, unsigned, Vfs_handle **out_handle, Genode::Allocator& alloc) override + Open_result open(char const *path, unsigned, Vfs_handle **out_handle, + Genode::Allocator& alloc) override { Node const *node = dereference(path); if (!node || !node->record || node->record->type() != Record::TYPE_FILE) return OPEN_ERR_UNACCESSIBLE; - *out_handle = new (alloc) Tar_vfs_handle(*this, alloc, 0, node->record); + *out_handle = new (alloc) Tar_vfs_file_handle(*this, alloc, 0, node); return OPEN_OK; } + Opendir_result opendir(char const *path, bool create, + Vfs_handle **out_handle, + Genode::Allocator& alloc) override + { + Node const *node = dereference(path); + + if (!node || + (node->record && (node->record->type() != Record::TYPE_DIR))) + return OPENDIR_ERR_LOOKUP_FAILED; + + *out_handle = new (alloc) Tar_vfs_dir_handle(*this, alloc, 0, node); + + return OPENDIR_OK; + } + + Openlink_result openlink(char const *path, bool create, + Vfs_handle **out_handle, Allocator &alloc) + { + Node const *node = dereference(path); + if (!node || !node->record || + node->record->type() != Record::TYPE_SYMLINK) + return OPENLINK_ERR_LOOKUP_FAILED; + + *out_handle = new (alloc) Tar_vfs_symlink_handle(*this, alloc, 0, node); + + return OPENLINK_OK; + } + void close(Vfs_handle *vfs_handle) override { Tar_vfs_handle *tar_handle = @@ -587,24 +645,17 @@ class Vfs::Tar_file_system : public File_system return WRITE_ERR_INVALID; } - Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) override + Read_result complete_read(Vfs_handle *vfs_handle, char *dst, + file_size count, file_size &out_count) override { - Tar_vfs_handle const *handle = static_cast(vfs_handle); + out_count = 0; - file_size const record_size = handle->record()->size(); + Tar_vfs_handle *handle = static_cast(vfs_handle); - file_size const record_bytes_left = record_size >= handle->seek() - ? record_size - handle->seek() : 0; + if (!handle) + return READ_ERR_INVALID; - count = min(record_bytes_left, count); - - char const *data = (char *)handle->record()->data() + handle->seek(); - - memcpy(dst, data, count); - - out_count = count; - return READ_OK; + return handle->read(dst, count, out_count); } Ftruncate_result ftruncate(Vfs_handle *handle, file_size) override diff --git a/repos/os/src/lib/vfs/terminal_file_system.h b/repos/os/src/lib/vfs/terminal_file_system.h index 421c8fcf8a..606cedc2ed 100644 --- a/repos/os/src/lib/vfs/terminal_file_system.h +++ b/repos/os/src/lib/vfs/terminal_file_system.h @@ -137,20 +137,6 @@ class Vfs::Terminal_file_system : public Single_file_system return WRITE_OK; } - Read_result read(Vfs_handle *, char *dst, file_size count, - file_size &out_count) override - { - out_count = _terminal.read(dst, count); - return READ_OK; - } - - bool queue_read(Vfs_handle *vfs_handle, char *dst, file_size count, - Read_result &out_result, file_size &out_count) override - { - out_result = _read(vfs_handle, dst, count, out_count); - return true; - } - Read_result complete_read(Vfs_handle *vfs_handle, char *dst, file_size count, file_size &out_count) override { diff --git a/repos/os/src/lib/vfs/zero_file_system.h b/repos/os/src/lib/vfs/zero_file_system.h index d4a094019c..b36078d3db 100644 --- a/repos/os/src/lib/vfs/zero_file_system.h +++ b/repos/os/src/lib/vfs/zero_file_system.h @@ -33,28 +33,48 @@ struct Vfs::Zero_file_system : Single_file_system static char const *name() { return "zero"; } char const *type() override { return "zero"; } - /******************************** - ** File I/O service interface ** - ********************************/ - - Write_result write(Vfs_handle *, char const *, file_size count, - file_size &count_out) override + struct Zero_vfs_handle : Single_vfs_handle { - count_out = count; + Zero_vfs_handle(Directory_service &ds, + File_io_service &fs, + Genode::Allocator &alloc) + : Single_vfs_handle(ds, fs, alloc, 0) { } - return WRITE_OK; + Read_result read(char *dst, file_size count, + file_size &out_count) override + { + memset(dst, 0, count); + out_count = count; + + return READ_OK; + } + + Write_result write(char const *src, file_size count, + file_size &out_count) override + { + out_count = count; + + return WRITE_OK; + } + + bool read_ready() { return true; } + }; + + /********************************* + ** Directory service interface ** + *********************************/ + + Open_result open(char const *path, unsigned, + Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) Zero_vfs_handle(*this, *this, alloc); + return OPEN_OK; } - Read_result read(Vfs_handle *vfs_handle, char *dst, file_size count, - file_size &out_count) override - { - memset(dst, 0, count); - out_count = count; - - return READ_OK; - } - - bool read_ready(Vfs_handle *) override { return true; } }; #endif /* _INCLUDE__VFS__ZERO_FILE_SYSTEM_H_ */ diff --git a/repos/os/src/server/fs_log/session.h b/repos/os/src/server/fs_log/session.h index 681754c100..b0cd47d26a 100644 --- a/repos/os/src/server/fs_log/session.h +++ b/repos/os/src/server/fs_log/session.h @@ -56,8 +56,19 @@ class Fs_log::Session_component : public Genode::Rpc_object ~Session_component() { - _fs.sync(_handle); - _fs.close(_handle); + /* sync */ + + File_system::Session::Tx::Source &source = *_fs.tx(); + + File_system::Packet_descriptor packet = source.get_acked_packet(); + + if (packet.operation() == File_system::Packet_descriptor::SYNC) + _fs.close(packet.handle()); + + packet = File_system::Packet_descriptor( + packet, _handle, File_system::Packet_descriptor::SYNC, 0, 0); + + source.submit_packet(packet); } @@ -78,9 +89,13 @@ class Fs_log::Session_component : public Genode::Rpc_object File_system::Session::Tx::Source &source = *_fs.tx(); - File_system::Packet_descriptor packet( - source.get_acked_packet(), - _handle, File_system::Packet_descriptor::WRITE, + File_system::Packet_descriptor packet = source.get_acked_packet(); + + if (packet.operation() == File_system::Packet_descriptor::SYNC) + _fs.close(packet.handle()); + + packet = File_system::Packet_descriptor( + packet, _handle, File_system::Packet_descriptor::WRITE, msg_len, File_system::SEEK_TAIL); char *buf = source.packet_content(packet); diff --git a/repos/os/src/server/fs_report/main.cc b/repos/os/src/server/fs_report/main.cc index c89d43b32a..3c7f0a4bee 100644 --- a/repos/os/src/server/fs_report/main.cc +++ b/repos/os/src/server/fs_report/main.cc @@ -34,25 +34,29 @@ namespace Fs_report { typedef Genode::Path Path; -static bool create_parent_dir(Vfs::Directory_service &vfs, Path const &child) +static bool create_parent_dir(Vfs::Directory_service &vfs, Path const &child, + Genode::Allocator &alloc) { - typedef Vfs::Directory_service::Mkdir_result Mkdir_result; + typedef Vfs::Directory_service::Opendir_result Opendir_result; Path parent = child; parent.strip_last_element(); if (parent == "/") return true; - Mkdir_result res = vfs.mkdir(parent.base(), 0); - if (res == Mkdir_result::MKDIR_ERR_NO_ENTRY) { - if (!create_parent_dir(vfs, parent)) + Vfs_handle *dir_handle; + Opendir_result res = vfs.opendir(parent.base(), true, &dir_handle, alloc); + if (res == Opendir_result::OPENDIR_ERR_LOOKUP_FAILED) { + if (!create_parent_dir(vfs, parent, alloc)) return false; - res = vfs.mkdir(parent.base(), 0); + res = vfs.opendir(parent.base(), true, &dir_handle, alloc); } switch (res) { - case Mkdir_result::MKDIR_OK: - case Mkdir_result::MKDIR_ERR_EXISTS: + case Opendir_result::OPENDIR_OK: + vfs.close(dir_handle); + return true; + case Opendir_result::OPENDIR_ERR_NODE_ALREADY_EXISTS: return true; default: return false; @@ -67,7 +71,8 @@ class Fs_report::Session_component : public Genode::Rpc_object Path _leaf_path; - Attached_ram_dataspace _ds; + Attached_ram_dataspace _ds; + Genode::Entrypoint &_ep; Vfs_handle *_handle; file_size _file_size = 0; @@ -80,13 +85,13 @@ class Fs_report::Session_component : public Genode::Rpc_object Vfs::File_system &vfs, Genode::Session_label const &label, size_t buffer_size) - : _ds(env.ram(), env.rm(), buffer_size) + : _ds(env.ram(), env.rm(), buffer_size), _ep(env.ep()) { typedef Vfs::Directory_service::Open_result Open_result; Path path = path_from_label(label.string()); - create_parent_dir(vfs, path); + create_parent_dir(vfs, path, alloc); Open_result res = vfs.open( path.base(), @@ -155,7 +160,12 @@ class Fs_report::Session_component : public Genode::Rpc_object _success = true; /* flush to notify watchers */ - _handle->ds().sync(_leaf_path.base()); + while (!_handle->fs().queue_sync(_handle)) + _ep.wait_and_dispatch_one_io_signal(); + + while (_handle->fs().complete_sync(_handle) == + Vfs::File_io_service::SYNC_QUEUED) + _ep.wait_and_dispatch_one_io_signal(); } void response_sigh(Genode::Signal_context_capability) override { } diff --git a/repos/os/src/server/lx_fs/main.cc b/repos/os/src/server/lx_fs/main.cc index 76f1d4323f..62b31fa696 100644 --- a/repos/os/src/server/lx_fs/main.cc +++ b/repos/os/src/server/lx_fs/main.cc @@ -91,6 +91,16 @@ class Lx_fs::Session_component : public Session_rpc_object case Packet_descriptor::READ_READY: /* not supported */ break; + + case Packet_descriptor::SYNC: + + /** + * We could call sync(2) here but for now we forward just the + * reminder because besides testing, there is currently no + * use-case. + */ + Genode::warning("SYNC not implemented!"); + break; } packet.length(res_length); @@ -327,13 +337,6 @@ class Lx_fs::Session_component : public Session_rpc_object { Genode::error(__func__, " not implemented"); } - - /** - * We could call sync(2) here but for now we forward just the - * reminder because besides testing, there is currently no - * use-case. - */ - void sync(Node_handle) override { Genode::warning("sync() not implemented!"); } }; diff --git a/repos/os/src/server/ram_fs/main.cc b/repos/os/src/server/ram_fs/main.cc index 91432c5bbd..5a3cf49b0e 100644 --- a/repos/os/src/server/ram_fs/main.cc +++ b/repos/os/src/server/ram_fs/main.cc @@ -95,6 +95,10 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object case Packet_descriptor::READ_READY: /* not supported */ break; + + case Packet_descriptor::SYNC: + open_node.node().notify_listeners(); + break; } packet.length(res_length); @@ -150,7 +154,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object static void _assert_valid_path(char const *path) { if (!path || path[0] != '/') { - Genode::warning("malformed path ''", path, "'"); + Genode::warning("malformed path '", Genode::Cstring(path), "'"); throw Lookup_failed(); } } @@ -471,19 +475,6 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object throw Invalid_handle(); } } - - void sync(Node_handle handle) override - { - auto sync_fn = [&] (Open_node &open_node) { - open_node.node().notify_listeners(); - }; - - try { - _open_node_registry.apply(handle, sync_fn); - } catch (Id_space::Unknown_id const &) { - throw Invalid_handle(); - } - } }; diff --git a/repos/os/src/server/trace_fs/main.cc b/repos/os/src/server/trace_fs/main.cc index fa994a4ccf..65a7cdae4b 100644 --- a/repos/os/src/server/trace_fs/main.cc +++ b/repos/os/src/server/trace_fs/main.cc @@ -677,6 +677,10 @@ class Trace_fs::Session_component : public Session_rpc_object case Packet_descriptor::READ_READY: /* not supported */ break; + + case Packet_descriptor::SYNC: + /* not supported */ + break; } packet.length(res_length); diff --git a/repos/os/src/server/vfs/assert.h b/repos/os/src/server/vfs/assert.h index 566f4e3199..5a86785f5a 100644 --- a/repos/os/src/server/vfs/assert.h +++ b/repos/os/src/server/vfs/assert.h @@ -22,20 +22,6 @@ namespace File_system { using namespace Vfs; - static inline void assert_mkdir(Directory_service::Mkdir_result r) - { - typedef Directory_service::Mkdir_result Result; - - switch (r) { - case Result::MKDIR_ERR_NAME_TOO_LONG: throw Name_too_long(); - case Result::MKDIR_ERR_NO_ENTRY: throw Lookup_failed(); - case Result::MKDIR_ERR_NO_SPACE: throw No_space(); - case Result::MKDIR_ERR_NO_PERM: throw Permission_denied(); - case Result::MKDIR_ERR_EXISTS: throw Node_already_exists(); - case Result::MKDIR_OK: break; - } - } - static inline void assert_open(Directory_service::Open_result r) { typedef Directory_service::Open_result Result; @@ -46,32 +32,41 @@ namespace File_system { case Result::OPEN_ERR_NO_SPACE: throw No_space(); case Result::OPEN_ERR_NO_PERM: throw Permission_denied(); case Result::OPEN_ERR_EXISTS: throw Node_already_exists(); + case Result::OPEN_ERR_OUT_OF_RAM: throw Out_of_ram(); + case Result::OPEN_ERR_OUT_OF_CAPS: throw Out_of_caps(); case Result::OPEN_OK: break; } } - static inline void assert_symlink(Directory_service::Symlink_result r) + static inline void assert_opendir(Directory_service::Opendir_result r) { - typedef Directory_service::Symlink_result Result; + typedef Directory_service::Opendir_result Result; switch (r) { - case Result::SYMLINK_ERR_NAME_TOO_LONG: throw Invalid_name(); - case Result::SYMLINK_ERR_NO_ENTRY: throw Lookup_failed(); - case Result::SYMLINK_ERR_NO_SPACE: throw No_space(); - case Result::SYMLINK_ERR_NO_PERM: throw Permission_denied(); - case Result::SYMLINK_ERR_EXISTS: throw Node_already_exists(); - case Result::SYMLINK_OK: break; + case Result::OPENDIR_ERR_LOOKUP_FAILED: throw Lookup_failed(); + case Result::OPENDIR_ERR_NAME_TOO_LONG: throw Invalid_name(); + case Result::OPENDIR_ERR_NODE_ALREADY_EXISTS: throw Node_already_exists(); + case Result::OPENDIR_ERR_NO_SPACE: throw No_space(); + case Result::OPENDIR_ERR_OUT_OF_RAM: throw Out_of_ram(); + case Result::OPENDIR_ERR_OUT_OF_CAPS: throw Out_of_caps(); + case Result::OPENDIR_ERR_PERMISSION_DENIED: throw Permission_denied(); + case Result::OPENDIR_OK: break; } } - static inline void assert_readlink(Directory_service::Readlink_result r) + static inline void assert_openlink(Directory_service::Openlink_result r) { - typedef Directory_service::Readlink_result Result; + typedef Directory_service::Openlink_result Result; switch (r) { - case Result::READLINK_ERR_NO_ENTRY: throw Lookup_failed(); - case Result::READLINK_ERR_NO_PERM: throw Permission_denied(); - case Result::READLINK_OK: break; + case Result::OPENLINK_ERR_LOOKUP_FAILED: throw Lookup_failed(); + case Result::OPENLINK_ERR_NAME_TOO_LONG: throw Invalid_name(); + case Result::OPENLINK_ERR_NODE_ALREADY_EXISTS: throw Node_already_exists(); + case Result::OPENLINK_ERR_NO_SPACE: throw No_space(); + case Result::OPENLINK_ERR_OUT_OF_RAM: throw Out_of_ram(); + case Result::OPENLINK_ERR_OUT_OF_CAPS: throw Out_of_caps(); + case Result::OPENLINK_ERR_PERMISSION_DENIED: throw Permission_denied(); + case Result::OPENLINK_OK: break; } } diff --git a/repos/os/src/server/vfs/main.cc b/repos/os/src/server/vfs/main.cc index 6726daffaa..e3b3ad7b2f 100644 --- a/repos/os/src/server/vfs/main.cc +++ b/repos/os/src/server/vfs/main.cc @@ -43,7 +43,7 @@ namespace Vfs_server { class Vfs_server::Session_component : public File_system::Session_rpc_object, - public File_io_handler + public Node_io_handler { private: @@ -113,13 +113,13 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, ** Packet-stream processing ** ******************************/ - struct Not_read_ready { }; + struct Not_ready { }; struct Dont_ack { }; /** * Perform packet operation * - * \throw Not_read_ready + * \throw Not_ready * \throw Dont_ack */ void _process_packet_op(Packet_descriptor &packet) @@ -142,46 +142,36 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, case Packet_descriptor::READ: try { - _apply(static_cast(packet.handle().value), [&] (File &node) { + _apply(packet.handle(), [&] (Node &node) { if (!node.read_ready()) { node.notify_read_ready(true); - throw Not_read_ready(); + throw Not_ready(); } - if (node.mode&READ_ONLY) - res_length = node.read(_vfs, (char *)content, length, seek); + if (node.mode() & READ_ONLY) + res_length = node.read((char *)content, length, seek); }); } - catch (Not_read_ready) { throw; } - catch (Operation_incomplete) { throw Not_read_ready(); } - catch (...) { - - try { - _apply(packet.handle(), [&] (Node &node) { - if (!node.read_ready()) - throw Not_read_ready(); - - if (node.mode&READ_ONLY) - res_length = node.read(_vfs, (char *)content, length, seek); - }); - } - catch (Not_read_ready) { throw; } - catch (Operation_incomplete) { throw Not_read_ready(); } - catch (...) { } - } + catch (Not_ready) { throw; } + catch (Operation_incomplete) { throw Not_ready(); } + catch (...) { } break; case Packet_descriptor::WRITE: + try { _apply(packet.handle(), [&] (Node &node) { - if (node.mode&WRITE_ONLY) - res_length = node.write(_vfs, (char const *)content, length, seek); + if (node.mode() & WRITE_ONLY) + res_length = node.write((char const *)content, length, seek); }); + } catch (Operation_incomplete) { + throw Not_ready(); } catch (...) { } break; case Packet_descriptor::READ_READY: + try { _apply(static_cast(packet.handle().value), [] (File &node) { if (!node.read_ready()) { @@ -197,6 +187,20 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, case Packet_descriptor::CONTENT_CHANGED: /* The VFS does not track file changes yet */ throw Dont_ack(); + + case Packet_descriptor::SYNC: + + /** + * Sync the VFS and send any pending signals on the node. + */ + try { + _apply(packet.handle(), [&] (Node &node) { + node.sync(); + }); + } catch (Operation_incomplete) { + throw Not_ready(); + } catch (...) { Genode::error("SYNC: unhandled exception"); } + break; } packet.length(res_length); @@ -208,7 +212,7 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, try { _process_packet_op(packet); return true; - } catch (Not_read_ready) { + } catch (Not_ready) { _backlog_packet = packet; } @@ -218,8 +222,10 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, bool _process_backlog() { /* indicate success if there's no backlog */ - if (!_backlog_packet.size()) + if (!_backlog_packet.size() && + (_backlog_packet.operation() != Packet_descriptor::SYNC)) { return true; + } /* only start processing if acknowledgement is possible */ if (!tx_sink()->ready_to_ack()) @@ -358,7 +364,7 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, _tx.sigh_packet_avail(_process_packet_handler); _tx.sigh_ready_to_ack(_process_packet_handler); - _root.construct(_node_space, vfs, root_path, false); + _root.construct(_node_space, vfs, _alloc, *this, root_path, false); } /** @@ -380,17 +386,27 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, _ram.upgrade(new_quota); } - /* File_io_handler interface */ - void handle_file_io(File &file) override + /* + * Called by the IO response handler for events which are not + * node-specific, for example after 'release_packet()' to signal + * that a previously failed 'alloc_packet()' may succeed now. + */ + void handle_general_io() { - if (file.notify_read_ready() && file.read_ready() + _process_packets(); + } + + /* Node_io_handler interface */ + void handle_node_io(Node &node) override + { + if (node.notify_read_ready() && node.read_ready() && tx_sink()->ready_to_ack()) { Packet_descriptor packet(Packet_descriptor(), - Node_handle { file.id().value }, + Node_handle { node.id().value }, Packet_descriptor::READ_READY, 0, 0); tx_sink()->acknowledge_packet(packet); - file.notify_read_ready(false); + node.notify_read_ready(false); } _process_packets(); } @@ -420,7 +436,8 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, throw Lookup_failed(); Directory *dir; - try { dir = new (_alloc) Directory(_node_space, _vfs, path_str, create); } + try { dir = new (_alloc) Directory(_node_space, _vfs, _alloc, + *this, path_str, create); } catch (Out_of_memory) { throw Out_of_ram(); } return Dir_handle(dir->id().value); @@ -474,7 +491,8 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, Node *node; - try { node = new (_alloc) Node(_node_space, path_str, STAT_ONLY); } + try { node = new (_alloc) Node(_node_space, path_str, STAT_ONLY, + *this); } catch (Out_of_memory) { throw Out_of_ram(); } return Node_handle { node->id().value }; @@ -571,16 +589,6 @@ class Vfs_server::Session_component : public File_system::Session_rpc_object, }); } - /** - * Sync the VFS and send any pending signals on the node. - */ - void sync(Node_handle handle) override - { - _apply(handle, [&] (Node &node) { - _vfs.sync(node.path()); - }); - } - void control(Node_handle, Control) override { } }; @@ -589,13 +597,35 @@ struct Vfs_server::Io_response_handler : Vfs::Io_response_handler { Session_registry &_session_registry; + bool _in_progress { false }; + bool _handle_general_io { false }; + Io_response_handler(Session_registry &session_registry) : _session_registry(session_registry) { } void handle_io_response(Vfs::Vfs_handle::Context *context) override { + if (_in_progress) { + /* called recursively, context is nullptr in this case */ + _handle_general_io = true; + return; + } + + _in_progress = true; + if (Vfs_server::Node *node = static_cast(context)) node->handle_io_response(); + else + _handle_general_io = true; + + while (_handle_general_io) { + _handle_general_io = false; + _session_registry.for_each([ ] (Registered_session &r) { + r.handle_general_io(); + }); + } + + _in_progress = false; } }; diff --git a/repos/os/src/server/vfs/node.h b/repos/os/src/server/vfs/node.h index afb3cfb03d..f127debaa2 100644 --- a/repos/os/src/server/vfs/node.h +++ b/repos/os/src/server/vfs/node.h @@ -36,9 +36,9 @@ namespace Vfs_server { typedef Genode::Id_space Node_space; - struct File_io_handler + struct Node_io_handler { - virtual void handle_file_io(File &file) = 0; + virtual void handle_node_io(Node &node) = 0; }; /** @@ -83,155 +83,27 @@ namespace Vfs_server { } -struct Vfs_server::Node : File_system::Node_base, Node_space::Element, - Vfs::Vfs_handle::Context +class Vfs_server::Node : public File_system::Node_base, public Node_space::Element, + public Vfs::Vfs_handle::Context { - Path const _path; - Mode const mode; - - Node(Node_space &space, char const *node_path, Mode node_mode) - : - Node_space::Element(*this, space), - _path(node_path), mode(node_mode) - { } - - virtual ~Node() { } - - char const *path() { return _path.base(); } - - virtual size_t read(Vfs::File_system&, char*, size_t, seek_off_t) { return 0; } - virtual size_t write(Vfs::File_system&, char const*, size_t, seek_off_t) { return 0; } - virtual bool read_ready() { return false; } - virtual void handle_io_response() { } -}; - -struct Vfs_server::Symlink : Node -{ - Symlink(Node_space &space, - Vfs::File_system &vfs, - char const *link_path, - Mode mode, - bool create) - : Node(space, link_path, mode) - { - if (create) - assert_symlink(vfs.symlink("", link_path)); - } - - - /******************** - ** Node interface ** - ********************/ - - size_t read(Vfs::File_system &vfs, char *dst, size_t len, seek_off_t seek_offset) - { - Vfs::file_size res = 0; - vfs.readlink(path(), dst, len, res); - return res; - } - - size_t write(Vfs::File_system &vfs, char const *src, size_t len, seek_off_t seek_offset) - { - /* - * if the symlink target is too long return a short result - * because a competent File_system client will error on a - * length mismatch - */ - - if (len > MAX_PATH_LEN) { - return len >> 1; - } - - /* ensure symlink gets something null-terminated */ - Genode::String target(Genode::Cstring(src, len)); - size_t const target_len = target.length()-1; - - switch (vfs.symlink(target.string(), path())) { - case Directory_service::SYMLINK_OK: break; - case Directory_service::SYMLINK_ERR_NAME_TOO_LONG: - return target_len >> 1; - default: return 0; - } - - mark_as_updated(); - notify_listeners(); - return target_len; - } - - bool read_ready() override { return true; } -}; - - -class Vfs_server::File : public Node -{ - private: - - File_io_handler &_file_io_handler; - - Vfs::Vfs_handle *_handle; - char const *_leaf_path; /* offset pointer to Node::_path */ - - bool _notify_read_ready = false; - - enum class Op_state { - IDLE, READ_QUEUED - } op_state = Op_state::IDLE; - public: - File(Node_space &space, - Vfs::File_system &vfs, - Genode::Allocator &alloc, - File_io_handler &file_io_handler, - char const *file_path, - Mode fs_mode, - bool create) - : - Node(space, file_path, fs_mode), - _file_io_handler(file_io_handler) + enum Op_state { IDLE, READ_QUEUED, SYNC_QUEUED }; + + private: + + Path const _path; + Mode const _mode; + bool _notify_read_ready = false; + + protected: + + Node_io_handler &_node_io_handler; + Vfs::Vfs_handle *_handle { nullptr }; + Op_state op_state { Op_state::IDLE }; + + size_t _read(char *dst, size_t len, seek_off_t seek_offset) { - unsigned vfs_mode = - (fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0); - - assert_open(vfs.open(file_path, vfs_mode, &_handle, alloc)); - _leaf_path = vfs.leaf_path(path()); - _handle->context = this; - } - - ~File() { _handle->ds().close(_handle); } - - void truncate(file_size_t size) - { - assert_truncate(_handle->fs().ftruncate(_handle, size)); - mark_as_updated(); - } - - void notify_read_ready(bool requested) - { - if (requested) - _handle->fs().notify_read_ready(_handle); - _notify_read_ready = requested; - } - - bool notify_read_ready() const { return _notify_read_ready; } - - - /******************** - ** Node interface ** - ********************/ - - size_t read(Vfs::File_system&, char *dst, size_t len, - seek_off_t seek_offset) override - { - if (seek_offset == SEEK_TAIL) { - typedef Directory_service::Stat_result Result; - Vfs::Directory_service::Stat st; - - /* if stat fails, try and see if the VFS will seek to the end */ - seek_offset = (_handle->ds().stat(_leaf_path, st) == Result::STAT_OK) ? - ((len < st.size) ? (st.size - len) : 0) : SEEK_TAIL; - } - _handle->seek(seek_offset); typedef Vfs::File_io_service::Read_result Result; @@ -242,34 +114,14 @@ class Vfs_server::File : public Node switch (op_state) { case Op_state::IDLE: - if (!_handle->fs().queue_read(_handle, dst, len, out_result, out_count)) + if (!_handle->fs().queue_read(_handle, len)) throw Operation_incomplete(); - switch (out_result) { - case Result::READ_OK: - op_state = Op_state::IDLE; - return out_count; - - case Result::READ_ERR_WOULD_BLOCK: - case Result::READ_ERR_AGAIN: - case Result::READ_ERR_INTERRUPT: - op_state = Op_state::IDLE; - throw Operation_incomplete(); - - case Result::READ_ERR_IO: - case Result::READ_ERR_INVALID: - op_state = Op_state::IDLE; - /* FIXME revise error handling */ - return 0; - - case Result::READ_QUEUED: - op_state = Op_state::READ_QUEUED; - break; - } /* fall through */ case Op_state::READ_QUEUED: - out_result = _handle->fs().complete_read(_handle, dst, len, out_count); + out_result = _handle->fs().complete_read(_handle, dst, len, + out_count); switch (out_result) { case Result::READ_OK: op_state = Op_state::IDLE; @@ -292,16 +144,206 @@ class Vfs_server::File : public Node throw Operation_incomplete(); } break; + + case Op_state::SYNC_QUEUED: + throw Operation_incomplete(); } return 0; } - size_t write(Vfs::File_system&, char const *src, size_t len, - seek_off_t seek_offset) override + size_t _write(char const *src, size_t len, + seek_off_t seek_offset) { Vfs::file_size res = 0; + _handle->seek(seek_offset); + + try { + _handle->fs().write(_handle, src, len, res); + } catch (Vfs::File_io_service::Insufficient_buffer) { + throw Operation_incomplete(); + } + + if (res) + mark_as_updated(); + + return res; + } + + public: + + Node(Node_space &space, char const *node_path, Mode node_mode, + Node_io_handler &node_io_handler) + : + Node_space::Element(*this, space), + _path(node_path), _mode(node_mode), + _node_io_handler(node_io_handler) + { } + + virtual ~Node() { } + + char const *path() { return _path.base(); } + Mode mode() const { return _mode; } + + virtual size_t read(char *dst, size_t len, seek_off_t seek_offset) + { return 0; } + + virtual size_t write(char const *src, size_t len, + seek_off_t seek_offset) { return 0; } + + bool read_ready() { return _handle->fs().read_ready(_handle); } + + void handle_io_response() + { + _node_io_handler.handle_node_io(*this); + } + + void notify_read_ready(bool requested) + { + if (requested) + _handle->fs().notify_read_ready(_handle); + _notify_read_ready = requested; + } + + bool notify_read_ready() const { return _notify_read_ready; } + + void sync() + { + typedef Vfs::File_io_service::Sync_result Result; + Result out_result = Result::SYNC_OK; + + switch (op_state) { + case Op_state::IDLE: + + if (!_handle->fs().queue_sync(_handle)) + throw Operation_incomplete(); + + /* fall through */ + + case Op_state::SYNC_QUEUED: + out_result = _handle->fs().complete_sync(_handle); + switch (out_result) { + case Result::SYNC_OK: + op_state = Op_state::IDLE; + return; + + case Result::SYNC_QUEUED: + op_state = Op_state::SYNC_QUEUED; + throw Operation_incomplete(); + } + break; + + case Op_state::READ_QUEUED: + throw Operation_incomplete(); + } + } +}; + +struct Vfs_server::Symlink : Node +{ + Symlink(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Node_io_handler &node_io_handler, + char const *link_path, + Mode mode, + bool create) + : Node(space, link_path, mode, node_io_handler) + { + assert_openlink(vfs.openlink(link_path, create, &_handle, alloc)); + _handle->context = this; + } + + + /******************** + ** Node interface ** + ********************/ + + size_t read(char *dst, size_t len, seek_off_t seek_offset) + { + if (seek_offset != 0) { + /* partial read is not supported */ + return 0; + } + + return _read(dst, len, 0); + } + + size_t write(char const *src, size_t len, seek_off_t seek_offset) + { + /* + * if the symlink target is too long return a short result + * because a competent File_system client will error on a + * length mismatch + */ + + if (len > MAX_PATH_LEN) { + return len >> 1; + } + + /* ensure symlink gets something null-terminated */ + Genode::String target(Genode::Cstring(src, len)); + size_t const target_len = target.length()-1; + + file_size out_count; + + if (_handle->fs().write(_handle, target.string(), target_len, out_count) != + File_io_service::WRITE_OK) + return 0; + + mark_as_updated(); + notify_listeners(); + return out_count; + } +}; + + +class Vfs_server::File : public Node +{ + private: + + char const *_leaf_path; /* offset pointer to Node::_path */ + + public: + + File(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Node_io_handler &node_io_handler, + char const *file_path, + Mode fs_mode, + bool create) + : + Node(space, file_path, fs_mode, node_io_handler) + { + unsigned vfs_mode = + (fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0); + + assert_open(vfs.open(file_path, vfs_mode, &_handle, alloc)); + _leaf_path = vfs.leaf_path(path()); + _handle->context = this; + } + + ~File() { _handle->ds().close(_handle); } + + size_t read(char *dst, size_t len, seek_off_t seek_offset) override + { + if (seek_offset == SEEK_TAIL) { + typedef Directory_service::Stat_result Result; + Vfs::Directory_service::Stat st; + + /* if stat fails, try and see if the VFS will seek to the end */ + seek_offset = (_handle->ds().stat(_leaf_path, st) == Result::STAT_OK) ? + ((len < st.size) ? (st.size - len) : 0) : SEEK_TAIL; + } + + return _read(dst, len, seek_offset); + } + + size_t write(char const *src, size_t len, + seek_off_t seek_offset) override + { if (seek_offset == SEEK_TAIL) { typedef Directory_service::Stat_result Result; Vfs::Directory_service::Stat st; @@ -311,35 +353,35 @@ class Vfs_server::File : public Node st.size : SEEK_TAIL; } - _handle->seek(seek_offset); - _handle->fs().write(_handle, src, len, res); - if (res) - mark_as_updated(); - return res; + return _write(src, len, seek_offset); } - bool read_ready() override { return _handle->fs().read_ready(_handle); } - - void handle_io_response() override + void truncate(file_size_t size) { - _file_io_handler.handle_file_io(*this); + assert_truncate(_handle->fs().ftruncate(_handle, size)); + mark_as_updated(); } }; struct Vfs_server::Directory : Node { - Directory(Node_space &space, Vfs::File_system &vfs, char const *dir_path, bool create) - : Node(space, dir_path, READ_ONLY) + Directory(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Node_io_handler &node_io_handler, + char const *dir_path, + bool create) + : Node(space, dir_path, READ_ONLY, node_io_handler) { - if (create) - assert_mkdir(vfs.mkdir(dir_path, 0)); + assert_opendir(vfs.opendir(dir_path, create, &_handle, alloc)); + _handle->context = this; } Node_space::Id file(Node_space &space, Vfs::File_system &vfs, Genode::Allocator &alloc, - File_io_handler &file_io_handler, + Node_io_handler &node_io_handler, char const *file_path, Mode mode, bool create) @@ -350,7 +392,7 @@ struct Vfs_server::Directory : Node File *file; try { file = new (alloc) - File(space, vfs, alloc, file_io_handler, path_str, mode, create); + File(space, vfs, alloc, node_io_handler, path_str, mode, create); } catch (Out_of_memory) { throw Out_of_ram(); } if (create) @@ -368,13 +410,9 @@ struct Vfs_server::Directory : Node Path subpath(link_path, path()); char const *path_str = subpath.base(); - if (!create) { - Vfs::file_size out; - assert_readlink(vfs.readlink(path_str, nullptr, 0, out)); - } - Symlink *link; - try { link = new (alloc) Symlink(space, vfs, path_str, mode, create); } + try { link = new (alloc) Symlink(space, vfs, alloc, _node_io_handler, + path_str, mode, create); } catch (Out_of_memory) { throw Out_of_ram(); } if (create) mark_as_updated(); @@ -386,7 +424,7 @@ struct Vfs_server::Directory : Node ** Node interface ** ********************/ - size_t read(Vfs::File_system &vfs, char *dst, size_t len, seek_off_t seek_offset) + size_t read(char *dst, size_t len, seek_off_t seek_offset) override { Directory_service::Dirent vfs_dirent; size_t blocksize = sizeof(File_system::Directory_entry); @@ -396,8 +434,10 @@ struct Vfs_server::Directory : Node size_t remains = len; while (remains >= blocksize) { - if (vfs.dirent(path(), index++, vfs_dirent) - != Vfs::Directory_service::DIRENT_OK) + + if ((_read((char*)&vfs_dirent, sizeof(vfs_dirent), + index * sizeof(vfs_dirent)) < sizeof(vfs_dirent)) || + (vfs_dirent.type == Vfs::Directory_service::DIRENT_TYPE_END)) return len - remains; File_system::Directory_entry *fs_dirent = (Directory_entry *)dst; @@ -422,7 +462,11 @@ struct Vfs_server::Directory : Node return len - remains; } - bool read_ready() override { return true; } + size_t write(char const *src, size_t len, + seek_off_t seek_offset) override + { + return 0; + } }; #endif /* _VFS__NODE_H_ */ diff --git a/repos/os/src/test/vfs_stress/main.cc b/repos/os/src/test/vfs_stress/main.cc index a4be07ccc4..78fbe90175 100644 --- a/repos/os/src/test/vfs_stress/main.cc +++ b/repos/os/src/test/vfs_stress/main.cc @@ -45,26 +45,6 @@ using namespace Genode; -inline void assert_mkdir(Vfs::Directory_service::Mkdir_result r) -{ - typedef Vfs::Directory_service::Mkdir_result Result; - - switch (r) { - case Result::MKDIR_OK: return; - case Result::MKDIR_ERR_EXISTS: - error("MKDIR_ERR_EXISTS"); break; - case Result::MKDIR_ERR_NO_ENTRY: - error("MKDIR_ERR_NO_ENTRY"); break; - case Result::MKDIR_ERR_NO_SPACE: - error("MKDIR_ERR_NO_SPACE"); break; - case Result::MKDIR_ERR_NO_PERM: - error("MKDIR_ERR_NO_PERM"); break; - case Result::MKDIR_ERR_NAME_TOO_LONG: - error("MKDIR_ERR_NAME_TOO_LONG"); break; - } - throw Exception(); -} - inline void assert_open(Vfs::Directory_service::Open_result r) { typedef Vfs::Directory_service::Open_result Result; @@ -80,6 +60,33 @@ inline void assert_open(Vfs::Directory_service::Open_result r) error("OPEN_ERR_NO_PERM"); break; case Result::OPEN_ERR_EXISTS: error("OPEN_ERR_EXISTS"); break; + case Result::OPEN_ERR_OUT_OF_RAM: + error("OPEN_ERR_OUT_OF_RAM"); break; + case Result::OPEN_ERR_OUT_OF_CAPS: + error("OPEN_ERR_OUT_OF_CAPS"); break; + } + throw Exception(); +} + +inline void assert_opendir(Vfs::Directory_service::Opendir_result r) +{ + typedef Vfs::Directory_service::Opendir_result Result; + switch (r) { + case Result::OPENDIR_OK: return; + case Result::OPENDIR_ERR_LOOKUP_FAILED: + error("OPENDIR_ERR_LOOKUP_FAILED"); break; + case Result::OPENDIR_ERR_NAME_TOO_LONG: + error("OPENDIR_ERR_NAME_TOO_LONG"); break; + case Result::OPENDIR_ERR_NODE_ALREADY_EXISTS: + error("OPENDIR_ERR_NODE_ALREADY_EXISTS"); break; + case Result::OPENDIR_ERR_NO_SPACE: + error("OPENDIR_ERR_NO_SPACE"); break; + case Result::OPENDIR_ERR_OUT_OF_RAM: + error("OPENDIR_ERR_OUT_OF_RAM"); break; + case Result::OPENDIR_ERR_OUT_OF_CAPS: + error("OPENDIR_ERR_OUT_OF_CAPS"); break; + case Result::OPENDIR_ERR_PERMISSION_DENIED: + error("OPENDIR_ERR_PERMISSION_DENIED"); break; } throw Exception(); } @@ -163,7 +170,9 @@ struct Mkdir_test : public Stress_test if (++depth > MAX_DEPTH) return; path.append("/b"); - assert_mkdir(vfs.mkdir(path.base(), 0)); + Vfs::Vfs_handle *dir_handle; + assert_opendir(vfs.opendir(path.base(), true, &dir_handle, alloc)); + vfs.close(dir_handle); ++count; mkdir_b(depth); } @@ -174,15 +183,19 @@ struct Mkdir_test : public Stress_test size_t path_len = strlen(path.base()); + Vfs::Vfs_handle *dir_handle; + path.append("/b"); - assert_mkdir(vfs.mkdir(path.base(), 0)); + assert_opendir(vfs.opendir(path.base(), true, &dir_handle, alloc)); + vfs.close(dir_handle); ++count; mkdir_b(depth); path.base()[path_len] = '\0'; path.append("/a"); - assert_mkdir(vfs.mkdir(path.base(), 0)); + assert_opendir(vfs.opendir(path.base(), true, &dir_handle, alloc)); + vfs.close(dir_handle); ++count; mkdir_a(depth); } @@ -266,6 +279,8 @@ struct Populate_test : public Stress_test struct Write_test : public Stress_test { + Genode::Entrypoint &_ep; + void write(int depth) { if (++depth > MAX_DEPTH) return; @@ -285,6 +300,10 @@ struct Write_test : public Stress_test file_size n; assert_write(handle->fs().write( handle, path.base(), path_len, n)); + handle->fs().queue_sync(handle); + while (handle->fs().complete_sync(handle) == + Vfs::File_io_service::SYNC_QUEUED) + _ep.wait_and_dispatch_one_io_signal(); count += n; } @@ -307,8 +326,9 @@ struct Write_test : public Stress_test } } - Write_test(Vfs::File_system &vfs, Genode::Allocator &alloc, char const *parent) - : Stress_test(vfs, alloc, parent) + Write_test(Vfs::File_system &vfs, Genode::Allocator &alloc, + char const *parent, Genode::Entrypoint &ep) + : Stress_test(vfs, alloc, parent), _ep(ep) { size_t path_len = strlen(path.base()); try { @@ -332,6 +352,8 @@ struct Write_test : public Stress_test struct Read_test : public Stress_test { + Genode::Entrypoint &_ep; + void read(int depth) { if (++depth > MAX_DEPTH) return; @@ -350,7 +372,17 @@ struct Read_test : public Stress_test char tmp[MAX_PATH_LEN]; file_size n; - assert_read(handle->fs().read(handle, tmp, sizeof(tmp), n)); + handle->fs().queue_read(handle, sizeof(tmp)); + + Vfs::File_io_service::Read_result read_result; + + while ((read_result = + handle->fs().complete_read(handle, tmp, sizeof(tmp), n)) == + Vfs::File_io_service::READ_QUEUED) + _ep.wait_and_dispatch_one_io_signal(); + + assert_read(read_result); + if (strcmp(path.base(), tmp, n)) error("read returned bad data"); count += n; @@ -375,8 +407,9 @@ struct Read_test : public Stress_test } } - Read_test(Vfs::File_system &vfs, Genode::Allocator &alloc, char const *parent) - : Stress_test(vfs, alloc, parent) + Read_test(Vfs::File_system &vfs, Genode::Allocator &alloc, char const *parent, + Genode::Entrypoint &ep) + : Stress_test(vfs, alloc, parent), _ep(ep) { size_t path_len = strlen(path.base()); try { @@ -400,14 +433,27 @@ struct Read_test : public Stress_test struct Unlink_test : public Stress_test { + Genode::Entrypoint &_ep; + void empty_dir(char const *path) { ::Path subpath(path); subpath.append("/"); + Vfs::Vfs_handle *dir_handle; + assert_opendir(vfs.opendir(path, false, &dir_handle, alloc)); + Vfs::Directory_service::Dirent dirent; for (Vfs::file_size i = vfs.num_dirent(path); i;) { - vfs.dirent(path, --i, dirent); + dir_handle->seek(--i * sizeof(dirent)); + dir_handle->fs().queue_read(dir_handle, sizeof(dirent)); + Vfs::file_size out_count; + + while (dir_handle->fs().complete_read(dir_handle, (char*)&dirent, + sizeof(dirent), out_count) == + Vfs::File_io_service::READ_QUEUED) + _ep.wait_and_dispatch_one_io_signal(); + subpath.append(dirent.name); switch (dirent.type) { case Vfs::Directory_service::DIRENT_TYPE_END: @@ -428,10 +474,13 @@ struct Unlink_test : public Stress_test subpath.strip_last_element(); } } + + vfs.close(dir_handle); } - Unlink_test(Vfs::File_system &vfs, Genode::Allocator &alloc, char const *parent) - : Stress_test(vfs, alloc, parent) + Unlink_test(Vfs::File_system &vfs, Genode::Allocator &alloc, + char const *parent, Genode::Entrypoint &ep) + : Stress_test(vfs, alloc, parent), _ep(ep) { typedef Vfs::Directory_service::Unlink_result Result; try { @@ -485,6 +534,20 @@ void Component::construct(Genode::Env &env) Vfs::Dir_file_system vfs_root(env, heap, config_xml.sub_node("vfs"), io_response_handler, global_file_system_factory); + + Vfs::Vfs_handle *vfs_root_handle; + vfs_root.opendir("/", false, &vfs_root_handle, heap); + + auto vfs_root_sync = [&] () + { + while (!vfs_root_handle->fs().queue_sync(vfs_root_handle)) + env.ep().wait_and_dispatch_one_io_signal(); + + while (vfs_root_handle->fs().complete_sync(vfs_root_handle) == + Vfs::File_io_service::SYNC_QUEUED) + env.ep().wait_and_dispatch_one_io_signal(); + }; + char path[Vfs::MAX_PATH_LEN]; MAX_DEPTH = config_xml.attribute_value("depth", 16U); @@ -507,13 +570,15 @@ void Component::construct(Genode::Env &env) for (int i = 0; i < ROOT_TREE_COUNT; ++i) { snprintf(path, 3, "/%d", i); - vfs_root.mkdir(path, 0); + Vfs::Vfs_handle *dir_handle; + vfs_root.opendir(path, true, &dir_handle, heap); + vfs_root.close(dir_handle); Mkdir_test test(vfs_root, heap, path); count += test.wait(); } elapsed_ms = timer.elapsed_ms() - elapsed_ms; - vfs_root.sync("/"); + vfs_root_sync(); log("created ",count," empty directories, ", (elapsed_ms*1000)/count,"μs/op , ", @@ -537,7 +602,7 @@ void Component::construct(Genode::Env &env) elapsed_ms = timer.elapsed_ms() - elapsed_ms; - vfs_root.sync("/"); + vfs_root_sync(); log("created ",count," empty files, ", (elapsed_ms*1000)/count,"μs/op, ", @@ -561,14 +626,14 @@ void Component::construct(Genode::Env &env) for (int i = 0; i < ROOT_TREE_COUNT; ++i) { snprintf(path, 3, "/%d", i); - Write_test test(vfs_root, heap, path); + Write_test test(vfs_root, heap, path, env.ep()); count += test.wait(); } elapsed_ms = timer.elapsed_ms() - elapsed_ms; - vfs_root.sync("/"); + vfs_root_sync(); log("wrote ",count," bytes ", count/elapsed_ms,"kB/s, ", @@ -593,13 +658,13 @@ void Component::construct(Genode::Env &env) for (int i = 0; i < ROOT_TREE_COUNT; ++i) { snprintf(path, 3, "/%d", i); - Read_test test(vfs_root, heap, path); + Read_test test(vfs_root, heap, path, env.ep()); count += test.wait(); } elapsed_ms = timer.elapsed_ms() - elapsed_ms; - vfs_root.sync("/"); + vfs_root_sync(); log("read ",count," bytes, ", count/elapsed_ms,"kB/s, ", @@ -625,14 +690,14 @@ void Component::construct(Genode::Env &env) for (int i = 0; i < ROOT_TREE_COUNT; ++i) { snprintf(path, 3, "/%d", i); - Unlink_test test(vfs_root, heap, path); + Unlink_test test(vfs_root, heap, path, env.ep()); count += test.wait(); } elapsed_ms = timer.elapsed_ms() - elapsed_ms; - vfs_root.sync("/"); + vfs_root_sync(); log("unlinked ",count," files in ",elapsed_ms,"ms, ", env.ram().used_ram().value/1024,"KiB consumed"); diff --git a/repos/ports/include/noux_session/sysio.h b/repos/ports/include/noux_session/sysio.h index 307d2247bc..cfa8f7008d 100644 --- a/repos/ports/include/noux_session/sysio.h +++ b/repos/ports/include/noux_session/sysio.h @@ -309,6 +309,14 @@ struct Noux::Sysio enum Clock_Id { CLOCK_ID_SECOND }; enum Fcntl_error { FCNTL_ERR_CMD_INVALID = Vfs::Directory_service::NUM_GENERAL_ERRORS }; + enum Mkdir_error { MKDIR_ERR_EXISTS, MKDIR_ERR_NO_ENTRY, + MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM, + MKDIR_ERR_NAME_TOO_LONG }; + enum Readlink_error { READLINK_ERR_NO_ENTRY, READLINK_ERR_NO_PERM }; + enum Symlink_error { SYMLINK_ERR_EXISTS, SYMLINK_ERR_NO_ENTRY, + SYMLINK_ERR_NO_SPACE, SYMLINK_ERR_NO_PERM, + SYMLINK_ERR_NAME_TOO_LONG }; + enum Execve_error { EXECVE_NONEXISTENT = Vfs::Directory_service::NUM_GENERAL_ERRORS, EXECVE_NOMEM }; enum Fork_error { FORK_NOMEM = Vfs::Directory_service::NUM_GENERAL_ERRORS }; enum Select_error { SELECT_ERR_INTERRUPT }; @@ -362,15 +370,15 @@ struct Noux::Sysio Vfs::File_io_service::Ftruncate_result ftruncate; Vfs::Directory_service::Open_result open; Vfs::Directory_service::Unlink_result unlink; - Vfs::Directory_service::Readlink_result readlink; Vfs::Directory_service::Rename_result rename; - Vfs::Directory_service::Mkdir_result mkdir; - Vfs::Directory_service::Symlink_result symlink; Vfs::File_io_service::Read_result read; Vfs::File_io_service::Write_result write; Vfs::File_io_service::Ioctl_result ioctl; Fcntl_error fcntl; + Mkdir_error mkdir; + Readlink_error readlink; + Symlink_error symlink; Execve_error execve; Select_error select; Accept_error accept; diff --git a/repos/ports/src/lib/libc_noux/plugin.cc b/repos/ports/src/lib/libc_noux/plugin.cc index faa53170a8..a5b32a246f 100644 --- a/repos/ports/src/lib/libc_noux/plugin.cc +++ b/repos/ports/src/lib/libc_noux/plugin.cc @@ -1209,14 +1209,12 @@ namespace { Genode::strncpy(sysio()->symlink_in.newpath, newpath, sizeof(sysio()->symlink_in.newpath)); if (!noux_syscall(Noux::Session::SYSCALL_SYMLINK)) { warning("symlink syscall failed for path \"", newpath, "\""); - typedef Vfs::Directory_service::Symlink_result Result; switch (sysio()->error.symlink) { - case Result::SYMLINK_ERR_NO_ENTRY: errno = ENOENT; return -1; - case Result::SYMLINK_ERR_EXISTS: errno = EEXIST; return -1; - case Result::SYMLINK_ERR_NO_SPACE: errno = ENOSPC; return -1; - case Result::SYMLINK_ERR_NO_PERM: errno = EPERM; return -1; - case Result::SYMLINK_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return -1; - case Result::SYMLINK_OK: break; + case Noux::Sysio::SYMLINK_ERR_NO_ENTRY: errno = ENOENT; return -1; + case Noux::Sysio::SYMLINK_ERR_EXISTS: errno = EEXIST; return -1; + case Noux::Sysio::SYMLINK_ERR_NO_SPACE: errno = ENOSPC; return -1; + case Noux::Sysio::SYMLINK_ERR_NO_PERM: errno = EPERM; return -1; + case Noux::Sysio::SYMLINK_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; return -1; } } @@ -1762,11 +1760,9 @@ namespace { if (!noux_syscall(Noux::Session::SYSCALL_READLINK)) { warning("readlink syscall failed for path \"", path, "\""); - typedef Vfs::Directory_service::Readlink_result Result; switch (sysio()->error.readlink) { - case Result::READLINK_ERR_NO_ENTRY: errno = ENOENT; return -1; - case Result::READLINK_ERR_NO_PERM: errno = EPERM; return -1; - case Result::READLINK_OK: break; + case Noux::Sysio::READLINK_ERR_NO_ENTRY: errno = ENOENT; return -1; + case Noux::Sysio::READLINK_ERR_NO_PERM: errno = EPERM; return -1; } } @@ -1808,12 +1804,12 @@ namespace { if (!noux_syscall(Noux::Session::SYSCALL_MKDIR)) { warning("mkdir syscall failed for \"", path, "\" mode=", Hex(mode)); switch (sysio()->error.mkdir) { - case Vfs::Directory_service::MKDIR_ERR_EXISTS: errno = EEXIST; break; - case Vfs::Directory_service::MKDIR_ERR_NO_ENTRY: errno = ENOENT; break; - case Vfs::Directory_service::MKDIR_ERR_NO_SPACE: errno = ENOSPC; break; - case Vfs::Directory_service::MKDIR_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; break; - case Vfs::Directory_service::MKDIR_ERR_NO_PERM: errno = EPERM; break; - default: errno = EPERM; break; + case Noux::Sysio::MKDIR_ERR_EXISTS: errno = EEXIST; break; + case Noux::Sysio::MKDIR_ERR_NO_ENTRY: errno = ENOENT; break; + case Noux::Sysio::MKDIR_ERR_NO_SPACE: errno = ENOSPC; break; + case Noux::Sysio::MKDIR_ERR_NAME_TOO_LONG: errno = ENAMETOOLONG; break; + case Noux::Sysio::MKDIR_ERR_NO_PERM: errno = EPERM; break; + default: errno = EPERM; break; } return -1; } diff --git a/repos/ports/src/noux/child.h b/repos/ports/src/noux/child.h index 22a839b669..4ada464fcb 100644 --- a/repos/ports/src/noux/child.h +++ b/repos/ports/src/noux/child.h @@ -126,6 +126,8 @@ class Noux::Child : public Rpc_object, Vfs::Dir_file_system &_root_dir; + Vfs_io_waiter_registry &_vfs_io_waiter_registry; + Destruct_queue &_destruct_queue; void _handle_destruct() { _destruct_queue.insert(this); } @@ -180,7 +182,8 @@ class Noux::Child : public Rpc_object, */ Empty_rom_factory _empty_rom_factory { _heap, _ep }; Empty_rom_service _empty_rom_service { _empty_rom_factory }; - Local_rom_factory _rom_factory { _heap, _env, _ep, _root_dir, _ds_registry }; + Local_rom_factory _rom_factory { _heap, _env, _ep, _root_dir, + _vfs_io_waiter_registry, _ds_registry }; Local_rom_service _rom_service { _rom_factory }; /** @@ -321,6 +324,7 @@ class Noux::Child : public Rpc_object, int pid, Env &env, Vfs::Dir_file_system &root_dir, + Vfs_io_waiter_registry &vfs_io_waiter_registry, Args const &args, Sysio::Env const &sysio_env, Allocator &heap, @@ -342,6 +346,7 @@ class Noux::Child : public Rpc_object, _pid_allocator(pid_allocator), _env(env), _root_dir(root_dir), + _vfs_io_waiter_registry(vfs_io_waiter_registry), _destruct_queue(destruct_queue), _heap(heap), _ref_pd (ref_pd), _ref_pd_cap (ref_pd_cap), @@ -521,6 +526,7 @@ class Noux::Child : public Rpc_object, pid(), _env, _root_dir, + _vfs_io_waiter_registry, args, env, _heap, diff --git a/repos/ports/src/noux/local_rom_service.h b/repos/ports/src/noux/local_rom_service.h index c6ae9df316..87708d2b9d 100644 --- a/repos/ports/src/noux/local_rom_service.h +++ b/repos/ports/src/noux/local_rom_service.h @@ -36,19 +36,23 @@ class Noux::Local_rom_factory : public Local_rom_service::Factory { private: - Allocator &_alloc; - Env &_env; - Rpc_entrypoint &_ep; - Vfs::Dir_file_system &_root_dir; - Dataspace_registry &_registry; + Allocator &_alloc; + Env &_env; + Rpc_entrypoint &_ep; + Vfs::Dir_file_system &_root_dir; + Vfs_io_waiter_registry &_vfs_io_waiter_registry; + Dataspace_registry &_registry; public: Local_rom_factory(Allocator &alloc, Env &env, Rpc_entrypoint &ep, Vfs::Dir_file_system &root_dir, + Vfs_io_waiter_registry &vfs_io_waiter_registry, Dataspace_registry ®istry) : - _alloc(alloc), _env(env), _ep(ep), _root_dir(root_dir), _registry(registry) + _alloc(alloc), _env(env), _ep(ep), _root_dir(root_dir), + _vfs_io_waiter_registry(vfs_io_waiter_registry), + _registry(registry) { } Rom_session_component &create(Args const &args, Affinity) override @@ -58,7 +62,9 @@ class Noux::Local_rom_factory : public Local_rom_service::Factory label_from_args(args.string()).last_element(); return *new (_alloc) - Rom_session_component(_alloc, _env, _ep, _root_dir, _registry, rom_name); + Rom_session_component(_alloc, _env, _ep, _root_dir, + _vfs_io_waiter_registry, _registry, + rom_name); } catch (Rom_connection::Rom_connection_failed) { throw Service_denied(); } diff --git a/repos/ports/src/noux/main.cc b/repos/ports/src/noux/main.cc index f34b28df3b..82da53a437 100644 --- a/repos/ports/src/noux/main.cc +++ b/repos/ports/src/noux/main.cc @@ -97,6 +97,7 @@ connect_stdio(Genode::Env &env, Genode::Constructible &terminal, Genode::Xml_node config, Vfs::Dir_file_system &root, + Noux::Vfs_io_waiter_registry &vfs_io_waiter_registry, Noux::Terminal_io_channel::Type type, Genode::Allocator &alloc) { @@ -142,7 +143,7 @@ connect_stdio(Genode::Env &env, return *new (alloc) Vfs_io_channel(path.string(), root.leaf_path(path.string()), &root, - vfs_handle, env.ep()); + vfs_handle, vfs_io_waiter_registry, env.ep()); } @@ -215,7 +216,21 @@ struct Noux::Main struct Io_response_handler : Vfs::Io_response_handler { - void handle_io_response(Vfs::Vfs_handle::Context *) override { } + Vfs_io_waiter_registry io_waiter_registry; + + void handle_io_response(Vfs::Vfs_handle::Context *context) override + { + if (context) { + Vfs_handle_context *vfs_handle_context = static_cast(context); + vfs_handle_context->vfs_io_waiter.wakeup(); + return; + } + + io_waiter_registry.for_each([] (Vfs_io_waiter &r) { + r.wakeup(); + }); + } + } _io_response_handler; Vfs::Dir_file_system _root_dir { _env, _heap, _config.xml().sub_node("fstab"), @@ -266,6 +281,7 @@ struct Noux::Main _pid_allocator.alloc(), _env, _root_dir, + _io_response_handler.io_waiter_registry, _args_of_init_process(), env_string_of_init_process(_config.xml()), _heap, @@ -284,9 +300,15 @@ struct Noux::Main typedef Terminal_io_channel Tio; /* just a local abbreviation */ Shared_pointer - _channel_0 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, Tio::STDIN, _heap), _heap }, - _channel_1 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, Tio::STDOUT, _heap), _heap }, - _channel_2 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, Tio::STDERR, _heap), _heap }; + _channel_0 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, + _io_response_handler.io_waiter_registry, + Tio::STDIN, _heap), _heap }, + _channel_1 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, + _io_response_handler.io_waiter_registry, + Tio::STDOUT, _heap), _heap }, + _channel_2 { &connect_stdio(_env, _terminal, _config.xml(), _root_dir, + _io_response_handler.io_waiter_registry, + Tio::STDERR, _heap), _heap }; Main(Env &env) : _env(env) { diff --git a/repos/ports/src/noux/rom_session_component.h b/repos/ports/src/noux/rom_session_component.h index 0d15fe9203..870e69c938 100644 --- a/repos/ports/src/noux/rom_session_component.h +++ b/repos/ports/src/noux/rom_session_component.h @@ -18,13 +18,117 @@ #include #include +/* Noux includes */ +#include "vfs_io_channel.h" + namespace Noux { + struct Vfs_dataspace; struct Rom_dataspace_info; class Rom_session_component; } +/** + * Dataspace obtained from the VFS + */ +struct Noux::Vfs_dataspace +{ + typedef Child_policy::Name Name; + + Vfs::Dir_file_system &root_dir; + Vfs_io_waiter_registry &vfs_io_waiter_registry; + + Name const name; + Genode::Ram_session &ram; + Genode::Region_map &rm; + Genode::Allocator &alloc; + + Dataspace_capability ds; + bool got_ds_from_vfs { true }; + + Vfs_dataspace(Vfs::Dir_file_system &root_dir, + Vfs_io_waiter_registry &vfs_io_waiter_registry, + Name const &name, + Genode::Ram_session &ram, Genode::Region_map &rm, + Genode::Allocator &alloc) + : + root_dir(root_dir), vfs_io_waiter_registry(vfs_io_waiter_registry), + name(name), ram(ram), rm(rm), alloc(alloc) + { + ds = root_dir.dataspace(name.string()); + + if (!ds.valid()) { + + got_ds_from_vfs = false; + + Vfs::Directory_service::Stat stat_out; + if (root_dir.stat(name.string(), stat_out) != Vfs::Directory_service::STAT_OK) + return; + + Vfs::Vfs_handle *file; + if (root_dir.open(name.string(), + Vfs::Directory_service::OPEN_MODE_RDONLY, + &file, + alloc) != Vfs::Directory_service::OPEN_OK) + return; + + Vfs_handle_context read_context; + file->context = &read_context; + + ds = ram.alloc(stat_out.size); + + char *addr = rm.attach(static_cap_cast(ds)); + + for (Vfs::file_size bytes_read = 0; bytes_read < stat_out.size; ) { + + Registered_no_delete + vfs_io_waiter(vfs_io_waiter_registry); + + while (!file->fs().queue_read(file, stat_out.size - bytes_read)) + vfs_io_waiter.wait_for_io(); + + Vfs::File_io_service::Read_result read_result; + + Vfs::file_size out_count; + + for (;;) { + read_result = file->fs().complete_read(file, addr + bytes_read, + stat_out.size, + out_count); + if (read_result != Vfs::File_io_service::READ_QUEUED) + break; + + read_context.vfs_io_waiter.wait_for_io(); + } + + if (read_result != Vfs::File_io_service::READ_OK) { + Genode::error("Error reading dataspace from VFS"); + rm.detach(addr); + ram.free(static_cap_cast(ds)); + root_dir.close(file); + return; + } + + bytes_read += out_count; + file->advance_seek(out_count); + } + + rm.detach(addr); + root_dir.close(file); + } + } + + ~Vfs_dataspace() + { + if (got_ds_from_vfs) + root_dir.release(name.string(), ds); + else + ram.free(static_cap_cast(ds)); + } +}; + + struct Noux::Rom_dataspace_info : Dataspace_info { Rom_dataspace_info(Dataspace_capability ds) : Dataspace_info(ds) { } @@ -64,28 +168,11 @@ class Noux::Rom_session_component : public Rpc_object private: - Allocator &_alloc; - Rpc_entrypoint &_ep; - Vfs::Dir_file_system &_root_dir; - Dataspace_registry &_ds_registry; - - /** - * Dataspace obtained from the VFS - */ - struct Vfs_dataspace - { - Vfs::Dir_file_system &root_dir; - - Name const name; - Dataspace_capability const ds; - - Vfs_dataspace(Vfs::Dir_file_system &root_dir, Name const &name) - : - root_dir(root_dir), name(name), ds(root_dir.dataspace(name.string())) - { } - - ~Vfs_dataspace() { root_dir.release(name.string(), ds); } - }; + Allocator &_alloc; + Rpc_entrypoint &_ep; + Vfs::Dir_file_system &_root_dir; + Vfs_io_waiter_registry &_vfs_io_waiter_registry; + Dataspace_registry &_ds_registry; Constructible _rom_from_vfs; @@ -97,7 +184,8 @@ class Noux::Rom_session_component : public Rpc_object Dataspace_capability _init_ds_cap(Env &env, Name const &name) { if (name.string()[0] == '/') { - _rom_from_vfs.construct(_root_dir, name); + _rom_from_vfs.construct(_root_dir, _vfs_io_waiter_registry, + name, env.ram(), env.rm(), _alloc); return _rom_from_vfs->ds; } @@ -112,10 +200,12 @@ class Noux::Rom_session_component : public Rpc_object Rom_session_component(Allocator &alloc, Env &env, Rpc_entrypoint &ep, Vfs::Dir_file_system &root_dir, + Vfs_io_waiter_registry &vfs_io_waiter_registry, Dataspace_registry &ds_registry, Name const &name) : - _alloc(alloc), _ep(ep), _root_dir(root_dir), _ds_registry(ds_registry), - _ds_cap(_init_ds_cap(env, name)) + _alloc(alloc), _ep(ep), _root_dir(root_dir), + _vfs_io_waiter_registry(vfs_io_waiter_registry), + _ds_registry(ds_registry), _ds_cap(_init_ds_cap(env, name)) { _ep.manage(this); _ds_registry.insert(new (alloc) Rom_dataspace_info(_ds_cap)); diff --git a/repos/ports/src/noux/syscall.cc b/repos/ports/src/noux/syscall.cc index 1e75ca469d..822fd40163 100644 --- a/repos/ports/src/noux/syscall.cc +++ b/repos/ports/src/noux/syscall.cc @@ -117,6 +117,11 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) size_t path_len = strlen(_sysio.stat_in.path); uint32_t path_hash = hash_path(_sysio.stat_in.path, path_len); + /* XXX: remove sync */ + + Registered_no_delete + vfs_io_waiter(_vfs_io_waiter_registry); + Vfs::Directory_service::Stat stat_out; _sysio.error.stat = _root_dir.stat(_sysio.stat_in.path, stat_out); @@ -178,9 +183,46 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_OPEN: { Vfs::Vfs_handle *vfs_handle = 0; - _sysio.error.open = _root_dir.open(_sysio.open_in.path, - _sysio.open_in.mode, - &vfs_handle, _heap); + + 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; @@ -198,8 +240,10 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) Shared_pointer channel(new (_heap) Vfs_io_channel(_sysio.open_in.path, leaf_path, &_root_dir, - vfs_handle, _env.ep()), - _heap); + vfs_handle, + _vfs_io_waiter_registry, + _env.ep()), + _heap); _sysio.open_out.fd = add_io_channel(channel); result = true; @@ -235,29 +279,31 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) * could be a script that uses an interpreter which maybe * does not exist. */ - Dataspace_capability binary_ds = - _root_dir.dataspace(_sysio.execve_in.filename); + Genode::Reconstructible binary_ds { + _root_dir, _vfs_io_waiter_registry, + _sysio.execve_in.filename, _env.ram(), _env.rm(), _heap + }; - if (!binary_ds.valid()) { + if (!binary_ds->ds.valid()) { _sysio.error.execve = Sysio::EXECVE_NONEXISTENT; break; } Child_env child_env(_env.rm(), - _sysio.execve_in.filename, binary_ds, + _sysio.execve_in.filename, binary_ds->ds, _sysio.execve_in.args, _sysio.execve_in.env); - _root_dir.release(_sysio.execve_in.filename, binary_ds); + binary_ds.construct(_root_dir, _vfs_io_waiter_registry, + child_env.binary_name(), _env.ram(), + _env.rm(), _heap); - binary_ds = _root_dir.dataspace(child_env.binary_name()); - - if (!binary_ds.valid()) { + if (!binary_ds->ds.valid()) { _sysio.error.execve = Sysio::EXECVE_NONEXISTENT; break; } - _root_dir.release(child_env.binary_name(), binary_ds); + binary_ds.destruct(); try { _parent_execve.execve_child(*this, @@ -495,6 +541,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) new_pid, _env, _root_dir, + _vfs_io_waiter_registry, _args, _sysio_env.env(), _heap, @@ -595,18 +642,62 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_READLINK: { - Vfs::file_size out_count = 0; + Vfs::Vfs_handle *symlink_handle { 0 }; - _sysio.error.readlink = - _root_dir.readlink(_sysio.readlink_in.path, - _sysio.readlink_out.chunk, - min(_sysio.readlink_in.bufsiz, - sizeof(_sysio.readlink_out.chunk)), - out_count); + 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; + + symlink_handle->context = &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(); + } + + symlink_handle->ds().close(symlink_handle); _sysio.readlink_out.count = out_count; - result = (_sysio.error.readlink == Vfs::Directory_service::READLINK_OK); break; } @@ -619,19 +710,108 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) break; case SYSCALL_MKDIR: + { + Vfs::Vfs_handle *dir_handle { 0 }; - _sysio.error.mkdir = _root_dir.mkdir(_sysio.mkdir_in.path, 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; + } - result = (_sysio.error.mkdir == Vfs::Directory_service::MKDIR_OK); break; + } case SYSCALL_SYMLINK: + { - _sysio.error.symlink = _root_dir.symlink(_sysio.symlink_in.oldpath, - _sysio.symlink_in.newpath); + 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(); + } + } + + 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; + + symlink_handle->context = &sync_context; + + while (symlink_handle->fs().complete_sync(symlink_handle) == + Vfs::File_io_service::SYNC_QUEUED) + sync_context.vfs_io_waiter.wait_for_io(); + + symlink_handle->ds().close(symlink_handle); - result = (_sysio.error.symlink == Vfs::Directory_service::SYMLINK_OK); break; + } case SYSCALL_USERINFO: { @@ -737,8 +917,33 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_SYNC: { - _root_dir.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; + + sync_handle->context = &sync_context; + + while (sync_handle->fs().complete_sync(sync_handle) == + Vfs::File_io_service::SYNC_QUEUED) + sync_context.vfs_io_waiter.wait_for_io(); + + sync_handle->ds().close(sync_handle); + break; } diff --git a/repos/ports/src/noux/vfs_io_channel.h b/repos/ports/src/noux/vfs_io_channel.h index 1be82a57d3..ff33d26155 100644 --- a/repos/ports/src/noux/vfs_io_channel.h +++ b/repos/ports/src/noux/vfs_io_channel.h @@ -18,8 +18,38 @@ #include #include -namespace Noux { struct Vfs_io_channel; } +namespace Noux { + class Vfs_io_waiter; + struct Vfs_handle_context; + struct Vfs_io_channel; + typedef Registry> + Vfs_io_waiter_registry; +} + +class Noux::Vfs_io_waiter +{ + private: + + Genode::Semaphore _sem; + + public: + + void wait_for_io() + { + _sem.down(); + } + + void wakeup() + { + _sem.up(); + } +}; + +struct Noux::Vfs_handle_context : Vfs::Vfs_handle::Context +{ + Vfs_io_waiter vfs_io_waiter; +}; struct Noux::Vfs_io_channel : Io_channel { @@ -32,30 +62,58 @@ struct Noux::Vfs_io_channel : Io_channel Vfs::Vfs_handle *_fh; + Vfs_io_waiter_registry &_vfs_io_waiter_registry; + Absolute_path _path; Absolute_path _leaf_path; + Vfs_handle_context _context; + Vfs_io_channel(char const *path, char const *leaf_path, Vfs::Dir_file_system *root_dir, Vfs::Vfs_handle *vfs_handle, + Vfs_io_waiter_registry &vfs_io_waiter_registry, Entrypoint &ep) : _read_avail_handler(ep, *this, &Vfs_io_channel::_handle_read_avail), - _fh(vfs_handle), _path(path), _leaf_path(leaf_path) + _fh(vfs_handle), _vfs_io_waiter_registry(vfs_io_waiter_registry), + _path(path), _leaf_path(leaf_path) { + _fh->context = &_context; _fh->fs().register_read_ready_sigh(_fh, _read_avail_handler); } ~Vfs_io_channel() { + Registered_no_delete + vfs_io_waiter(_vfs_io_waiter_registry); + + while (!_fh->fs().queue_sync(_fh)) + vfs_io_waiter.wait_for_io(); + + while (_fh->fs().complete_sync(_fh) == Vfs::File_io_service::SYNC_QUEUED) + _context.vfs_io_waiter.wait_for_io(); + _fh->ds().close(_fh); } bool write(Sysio &sysio, size_t &offset) override { + Vfs::file_size count = sysio.write_in.count; Vfs::file_size out_count = 0; - sysio.error.write = _fh->fs().write(_fh, sysio.write_in.chunk, - sysio.write_in.count, out_count); + Registered_no_delete + vfs_io_waiter(_vfs_io_waiter_registry); + + for (;;) { + try { + sysio.error.write = _fh->fs().write(_fh, sysio.write_in.chunk, + count, out_count); + break; + } catch (Vfs::File_io_service::Insufficient_buffer) { + vfs_io_waiter.wait_for_io(); + } + } + if (sysio.error.write != Vfs::File_io_service::WRITE_OK) return false; @@ -73,7 +131,21 @@ struct Noux::Vfs_io_channel : Io_channel Vfs::file_size out_count = 0; - sysio.error.read = _fh->fs().read(_fh, sysio.read_out.chunk, count, out_count); + Registered_no_delete + vfs_io_waiter(_vfs_io_waiter_registry); + + while (!_fh->fs().queue_read(_fh, count)) + vfs_io_waiter.wait_for_io(); + + for (;;) { + + sysio.error.read = _fh->fs().complete_read(_fh, sysio.read_out.chunk, count, out_count); + + if (sysio.error.read != Vfs::File_io_service::READ_QUEUED) + break; + + _context.vfs_io_waiter.wait_for_io(); + } if (sysio.error.read != Vfs::File_io_service::READ_OK) return false; @@ -91,17 +163,25 @@ struct Noux::Vfs_io_channel : Io_channel * 'sysio.stat_in' is not used in '_fh->ds().stat()', * so no 'sysio' member translation is needed here */ + + Registered_no_delete + vfs_io_waiter(_vfs_io_waiter_registry); + + while (!_fh->fs().queue_sync(_fh)) + vfs_io_waiter.wait_for_io(); + + while (_fh->fs().complete_sync(_fh) == Vfs::File_io_service::SYNC_QUEUED) + _context.vfs_io_waiter.wait_for_io(); + Vfs::Directory_service::Stat stat; sysio.error.stat = _fh->ds().stat(_leaf_path.base(), stat); sysio.fstat_out.st = stat; return (sysio.error.stat == Vfs::Directory_service::STAT_OK); - } bool ftruncate(Sysio &sysio) override { - sysio.error.ftruncate = _fh->fs().ftruncate(_fh, sysio.ftruncate_in.length); return (sysio.error.ftruncate == Vfs::File_io_service::FTRUNCATE_OK); @@ -157,8 +237,39 @@ struct Noux::Vfs_io_channel : Io_channel */ Vfs::Directory_service::Dirent dirent; - if (!_fh->ds().dirent(_path.base(), index - 2, dirent)) - return false; + + Vfs::file_size noux_dirent_seek = _fh->seek(); + _fh->seek((index - 2) * sizeof(dirent)); + + Vfs::file_size const count = sizeof(dirent); + + Registered_no_delete + vfs_io_waiter(_vfs_io_waiter_registry); + + while (!_fh->fs().queue_read(_fh, count)) + vfs_io_waiter.wait_for_io(); + + Vfs::File_io_service::Read_result read_result; + Vfs::file_size out_count = 0; + + for (;;) { + + read_result = _fh->fs().complete_read(_fh, (char*)&dirent, + count, out_count); + + if (read_result != Vfs::File_io_service::READ_QUEUED) + break; + + _context.vfs_io_waiter.wait_for_io(); + } + + if ((read_result != Vfs::File_io_service::READ_OK) || + (out_count != sizeof(dirent))) { + dirent = Vfs::Directory_service::Dirent(); + } + + _fh->seek(noux_dirent_seek); + sysio.dirent_out.entry = dirent; _fh->advance_seek(sizeof(Sysio::Dirent));