VFS: nonblocking interface

The VFS library can be used in single-threaded or multi-threaded
environments and depending on that, signals are handled by the same thread
which uses the VFS library or possibly by a different thread. If a VFS
plugin needs to block to wait for a signal, there is currently no way
which works reliably in both environments.

For this reason, this commit makes the interface of the VFS library
nonblocking, similar to the File_system session interface.

The most important changes are:

- Directories are created and opened with the 'opendir()' function and the
  directory entries are read with the recently introduced 'queue_read()'
  and 'complete_read()' functions.

- Symbolic links are created and opened with the 'openlink()' function and
  the link target is read with the 'queue_read()' and 'complete_read()'
  functions and written with the 'write()' function.

- The 'write()' function does not wait for signals anymore. This can have
  the effect that data written by a VFS library user has not been
  processed by a file system server yet when the library user asks for the
  size of the file or closes it (both done with RPC functions at the file
  system server). For this reason, a user of the VFS library should
  request synchronization before calling 'stat()' or 'close()'. To make
  sure that a file system server has processed all write request packets
  which a client submitted before the synchronization request,
  synchronization is now requested at the file system server with a
  synchronization packet instead of an RPC function. Because of this
  change, the synchronization interface of the VFS library is now split
  into 'queue_sync()' and 'complete_sync()' functions.

Fixes #2399
This commit is contained in:
Christian Prochaska 2017-08-15 20:51:53 +02:00 committed by Christian Helmuth
parent 8312950e2f
commit b0935ef9b2
49 changed files with 4361 additions and 2307 deletions

View File

@ -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<List_element<Lxip_vfs_handle> > Lxip_vfs_handles;
typedef Genode::List<List_element<Lxip_vfs_file_handle> > 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<Lxip_vfs_handle> file_le { this };
List_element<Lxip_vfs_handle> 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<Vfs::Lxip_vfs_handle> *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<Lxip_vfs_file_handle> file_le { this };
List_element<Lxip_vfs_file_handle> 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<Vfs::Lxip_vfs_file_handle> *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<Vfs::Lxip_vfs_handle> *le = handles.first();
for (Genode::List_element<Vfs::Lxip_vfs_file_handle> *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<Vfs::Directory*>(node))
out.type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY;
out->type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY;
if (dynamic_cast<Vfs::File*>(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::Lxip_vfs_handle *>(vfs_handle)->file;
Vfs::Node &node =
static_cast<Vfs::Lxip_vfs_handle *>(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<Vfs::Directory*>(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<Vfs::File*>(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<Vfs::Directory*>(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::Lxip_vfs_handle*>(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<Vfs::Lxip_vfs_file_handle*>(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::Lxip_vfs_handle *>(vfs_handle)->file;
static_cast<Vfs::Lxip_vfs_file_handle *>(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::Lxip_vfs_handle *>(vfs_handle);
Lxip_vfs_file_handle *handle =
static_cast<Vfs::Lxip_vfs_file_handle *>(vfs_handle);
if (dynamic_cast<Lxip_file*>(&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::Lxip_vfs_handle *>(vfs_handle);
Lxip_vfs_file_handle *handle =
static_cast<Vfs::Lxip_vfs_file_handle *>(vfs_handle);
return handle->file.poll(false, nullptr);
}

View File

@ -59,7 +59,29 @@ class Vfs::Rump_file_system : public File_system
typedef Genode::Path<MAX_PATH_LEN> 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<Genode::Ram_dataspace>(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<Rump_vfs_handle *>(vfs_handle);
Rump_vfs_file_handle *rump_handle =
static_cast<Rump_vfs_file_handle *>(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<Rump_vfs_handle *>(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<Rump_vfs_handle *>(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<Rump_vfs_handle *>(vfs_handle);
Rump_vfs_file_handle *handle =
dynamic_cast<Rump_vfs_file_handle *>(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);
}
};

View File

@ -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<Session_component>

View File

@ -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 <typename FN>
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

View File

@ -18,6 +18,7 @@
#include <gems/file.h>
/* libc includes */
#include <libc/component.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@ -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);
});
}

View File

@ -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;
}

View File

@ -18,6 +18,7 @@
/* Genode includes */
#include <libc/component.h>
#include "task.h"
/* libc includes */
#include <fcntl.h>
@ -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)

View File

@ -43,8 +43,8 @@ class Fatfs::File_system : public Vfs::File_system
typedef Genode::Path<FF_MAX_LFN> Path;
struct Fatfs_handle;
typedef Genode::List<Fatfs_handle> Fatfs_handles;
struct Fatfs_file_handle;
typedef Genode::List<Fatfs_file_handle> 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<File>
{
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<Fatfs_handle *>(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<Fatfs_file_handle *>(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<Fatfs_dir_handle *>(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<Fatfs_file_handle *>(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<Fatfs_handle *>(vfs_handle);
Fatfs_file_handle *handle = static_cast<Fatfs_file_handle *>(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<Fatfs_handle *>(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<Fatfs_file_handle *>(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<Fatfs_handle *>(vfs_handle);
Fatfs_file_handle *handle = static_cast<Fatfs_file_handle *>(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;
}
}

View File

@ -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_ */

View File

@ -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);

View File

@ -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();
}
};

View File

@ -111,11 +111,6 @@ class File_system::Session_client : public Genode::Rpc_client<Session>
{
call<Rpc_move>(from_dir, from_name, to_dir, to_name);
}
void sync(Node_handle node) override
{
call<Rpc_sync>(node);
}
};
#endif /* _INCLUDE__FILE_SYSTEM_SESSION__CLIENT_H_ */

View File

@ -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_ */

View File

@ -16,6 +16,7 @@
#ifndef _INCLUDE__VFS__DIR_FILE_SYSTEM_H_
#define _INCLUDE__VFS__DIR_FILE_SYSTEM_H_
#include <base/registry.h>
#include <vfs/file_system_factory.h>
#include <vfs/vfs_handle.h>
@ -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_element> 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 &registry,
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<Dir_vfs_handle*>(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<Dir_vfs_handle*>(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<Dir_vfs_handle*>(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<Dir_vfs_handle*>(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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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<Single_vfs_handle*>(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<Single_vfs_handle*>(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<Single_vfs_handle*>(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;

View File

@ -37,7 +37,7 @@ class Vfs::Vfs_handle
/**
* Opaque handle context
*/
struct Context { };
struct Context : List<Context>::Element { };
Context *context = nullptr;

View File

@ -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 <typename T>
struct Registered : T

View File

@ -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);
}
};

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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;

View File

@ -57,6 +57,7 @@ class Vfs_ram::Node : public Genode::Avl_node<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<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<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<File *>(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<Directory *>(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<Symlink *>(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<Directory *>(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<Symlink *>(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<Ram_vfs_handle *>(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<Directory *>(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<Symlink *>(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<Symlink *>(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<Ram_vfs_handle *>(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<Ram_vfs_handle *>(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<Ram_vfs_handle *>(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;
}

View File

@ -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<char>() + 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<char>() + 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_ */

View File

@ -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_ */

View File

@ -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; }

View File

@ -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<Tar_file_system&>(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<Tar_vfs_handle *>(vfs_handle);
out_count = 0;
file_size const record_size = handle->record()->size();
Tar_vfs_handle *handle = static_cast<Tar_vfs_handle *>(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

View File

@ -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
{

View File

@ -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_ */

View File

@ -56,8 +56,19 @@ class Fs_log::Session_component : public Genode::Rpc_object<Genode::Log_session>
~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<Genode::Log_session>
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);

View File

@ -34,25 +34,29 @@ namespace Fs_report {
typedef Genode::Path<Session_label::capacity()> 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<Report::Session>
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<Report::Session>
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<Path>(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<Report::Session>
_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 { }

View File

@ -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!"); }
};

View File

@ -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<Open_node>(handle, sync_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
};

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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<File_handle>(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<File_handle>(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<Vfs_server::Node *>(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;
}
};

View File

@ -36,9 +36,9 @@ namespace Vfs_server {
typedef Genode::Id_space<Node> 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<MAX_PATH_LEN+1> 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<MAX_PATH_LEN+1> 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_ */

View File

@ -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");

View File

@ -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;

View File

@ -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;
}

View File

@ -126,6 +126,8 @@ class Noux::Child : public Rpc_object<Session>,
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<Session>,
*/
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<Session>,
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<Session>,
_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<Session>,
pid(),
_env,
_root_dir,
_vfs_io_waiter_registry,
args,
env,
_heap,

View File

@ -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 &registry)
:
_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(); }

View File

@ -97,6 +97,7 @@ connect_stdio(Genode::Env &env,
Genode::Constructible<Terminal::Connection> &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<Vfs_handle_context*>(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<Io_channel>
_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)
{

View File

@ -18,13 +18,117 @@
#include <rom_session/connection.h>
#include <base/rpc_server.h>
/* 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<Genode::Ram_dataspace>(ds));
for (Vfs::file_size bytes_read = 0; bytes_read < stat_out.size; ) {
Registered_no_delete<Vfs_io_waiter>
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<Genode::Ram_dataspace>(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<Genode::Ram_dataspace>(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<Rom_session>
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<Vfs_dataspace> _rom_from_vfs;
@ -97,7 +184,8 @@ class Noux::Rom_session_component : public Rpc_object<Rom_session>
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>
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));

View File

@ -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(_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<Io_channel>
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<Vfs_dataspace> 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<sizeof(_sysio.execve_in.args)>
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(_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(_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(_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;
}

View File

@ -18,8 +18,38 @@
#include <io_channel.h>
#include <vfs/dir_file_system.h>
namespace Noux { struct Vfs_io_channel; }
namespace Noux {
class Vfs_io_waiter;
struct Vfs_handle_context;
struct Vfs_io_channel;
typedef Registry<Registered_no_delete<Vfs_io_waiter>>
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(_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(_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(_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(_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(_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));