genode/repos/os/include/os/vfs.h

646 lines
15 KiB
C
Raw Normal View History

2018-03-26 13:44:04 +02:00
/*
2017-07-05 10:40:29 +02:00
* \brief Front-end API for accessing a component-local virtual file system
* \author Norman Feske
* \date 2017-07-04
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__OS__VFS_H_
#define _INCLUDE__OS__VFS_H_
2017-07-05 10:40:29 +02:00
/* Genode includes */
#include <base/env.h>
#include <base/allocator.h>
#include <vfs/simple_env.h>
2017-07-05 10:40:29 +02:00
#include <vfs/dir_file_system.h>
#include <vfs/file_system_factory.h>
namespace Genode {
struct Directory;
struct Root_directory;
struct File;
struct Readonly_file;
struct File_content;
struct Watcher;
template <typename>
struct Watch_handler;
2017-07-05 10:40:29 +02:00
}
2018-03-26 13:44:04 +02:00
struct Genode::Directory : Noncopyable, Interface
2017-07-05 10:40:29 +02:00
{
public:
struct Open_failed : Exception { };
struct Read_dir_failed : Exception { };
class Entry
{
private:
2018-03-26 13:44:04 +02:00
Vfs::Directory_service::Dirent _dirent { };
2017-07-05 10:40:29 +02:00
friend class Directory;
Entry() { }
using Dirent_type = Vfs::Directory_service::Dirent_type;
2017-07-05 10:40:29 +02:00
public:
void print(Output &out) const
{
using Genode::print;
using Vfs::Directory_service;
print(out, _dirent.name.buf, " (");
2017-07-05 10:40:29 +02:00
switch (_dirent.type) {
case Dirent_type::TRANSACTIONAL_FILE: print(out, "file"); break;
case Dirent_type::CONTINUOUS_FILE: print(out, "file"); break;
case Dirent_type::DIRECTORY: print(out, "dir"); break;
case Dirent_type::SYMLINK: print(out, "symlink"); break;
default: print(out, "other"); break;
2017-07-05 10:40:29 +02:00
}
print(out, ")");
}
typedef String<Vfs::Directory_service::Dirent::Name::MAX_LEN> Name;
2017-07-05 10:40:29 +02:00
Name name() const { return Name(Cstring(_dirent.name.buf)); }
Vfs::Directory_service::Dirent_type type() const { return _dirent.type; }
bool dir() const { return _dirent.type == Dirent_type::DIRECTORY; }
2017-07-05 10:40:29 +02:00
};
enum { MAX_PATH_LEN = 256 };
typedef String<MAX_PATH_LEN> Path;
2017-07-05 10:40:29 +02:00
static Path join(Path const &x, Path const &y)
{
char const *p = y.string();
while (*p == '/') ++p;
if (x == "/")
return Path("/", p);
return Path(x, "/", p);
}
2017-07-05 10:40:29 +02:00
private:
2018-03-26 13:44:04 +02:00
/*
* Noncopyable
*/
Directory(Directory const &);
Directory &operator = (Directory const &);
2017-07-05 10:40:29 +02:00
Path const _path;
Vfs::File_system &_fs;
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
2017-08-15 20:51:53 +02:00
Entrypoint &_ep;
2017-07-05 10:40:29 +02:00
Allocator &_alloc;
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
2017-08-15 20:51:53 +02:00
Vfs::Vfs_handle *_handle = nullptr;
2017-07-05 10:40:29 +02:00
friend class Readonly_file;
friend class Root_directory;
friend class Watcher;
2017-07-05 10:40:29 +02:00
/*
* Operations such as 'file_size' that are expected to be 'const' at
* the API level, do internally require I/O with the outside world,
* with involves non-const access to the VFS. This helper allows a
* 'const' method to perform I/O at the VFS.
*/
Vfs::File_system &_nonconst_fs() const
{
return const_cast<Vfs::File_system &>(_fs);
}
Vfs::Directory_service::Stat_result _stat(Path const &rel_path,
Vfs::Directory_service::Stat &out) const
2017-07-05 10:40:29 +02:00
{
return _nonconst_fs().stat(join(_path, rel_path).string(), out);
2017-07-05 10:40:29 +02:00
}
public:
struct Nonexistent_file : Exception { };
struct Nonexistent_directory : Exception { };
/**
* Constructor used by 'Root_directory'
*
* \throw Open_failed
*/
Directory(Vfs::Env &vfs_env)
: _path(""), _fs(vfs_env.root_dir()),
_ep(vfs_env.env().ep()), _alloc(vfs_env.alloc())
{
if (_fs.opendir("/", false, &_handle, _alloc) !=
Vfs::Directory_service::OPENDIR_OK)
throw Nonexistent_directory();
}
2017-07-05 10:40:29 +02:00
/**
* Open sub directory
*
* \throw Nonexistent_directory
*/
Directory(Directory const &other, Path const &rel_path)
: _path(join(other._path, rel_path)), _fs(other._fs), _ep(other._ep),
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
2017-08-15 20:51:53 +02:00
_alloc(other._alloc)
2017-07-05 10:40:29 +02:00
{
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
2017-08-15 20:51:53 +02:00
if (_fs.opendir(_path.string(), false, &_handle, _alloc) !=
Vfs::Directory_service::OPENDIR_OK)
2017-07-05 10:40:29 +02:00
throw Nonexistent_directory();
}
~Directory() { if (_handle) _handle->ds().close(_handle); }
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
2017-08-15 20:51:53 +02:00
2017-07-05 10:40:29 +02:00
template <typename FN>
void for_each_entry(FN const &fn)
{
for (unsigned i = 0;; i++) {
Entry entry;
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
2017-08-15 20:51:53 +02:00
_handle->seek(i * sizeof(entry._dirent));
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;
2017-07-05 10:40:29 +02:00
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
2017-08-15 20:51:53 +02:00
_ep.wait_and_dispatch_one_io_signal();
}
if ((read_result != Vfs::File_io_service::READ_OK) ||
(out_count < sizeof(entry._dirent))) {
2017-07-05 10:40:29 +02:00
error("could not access directory '", _path, "'");
throw Read_dir_failed();
}
if (entry._dirent.type == Vfs::Directory_service::Dirent_type::END)
2017-07-05 10:40:29 +02:00
return;
fn(entry);
}
}
template <typename FN>
void for_each_entry(FN const &fn) const
{
auto const_fn = [&] (Entry const &e) { fn(e); };
const_cast<Directory &>(*this).for_each_entry(const_fn);
}
2017-07-05 10:40:29 +02:00
bool file_exists(Path const &rel_path) const
{
Vfs::Directory_service::Stat stat { };
if (_stat(rel_path, stat) != Vfs::Directory_service::STAT_OK)
return false;
return stat.type == Vfs::Node_type::TRANSACTIONAL_FILE
|| stat.type == Vfs::Node_type::CONTINUOUS_FILE;
2017-07-05 10:40:29 +02:00
}
bool directory_exists(Path const &rel_path) const
{
Vfs::Directory_service::Stat stat { };
if (_stat(rel_path, stat) != Vfs::Directory_service::STAT_OK)
return false;
return stat.type == Vfs::Node_type::DIRECTORY;
}
2017-07-05 10:40:29 +02:00
/**
* Return size of file at specified directory-relative path
*
* \throw Nonexistent_file file at path does not exist or
* the access to the file is denied
*
*/
Vfs::file_size file_size(Path const &rel_path) const
{
Vfs::Directory_service::Stat stat { };
if (_stat(rel_path, stat) != Vfs::Directory_service::STAT_OK)
2017-07-05 10:40:29 +02:00
throw Nonexistent_file();
if (stat.type == Vfs::Node_type::TRANSACTIONAL_FILE
|| stat.type == Vfs::Node_type::CONTINUOUS_FILE)
return stat.size;
throw Nonexistent_file();
2017-07-05 10:40:29 +02:00
}
/**
* Return symlink content at specified directory-relative path
*
* \throw Nonexistent_file symlink at path does not exist or
* access is denied
*
*/
Path read_symlink(Path const &rel_path) const
{
using namespace Vfs;
Vfs_handle *link_handle;
auto open_res = _nonconst_fs().openlink(
join(_path, rel_path).string(),
false, &link_handle, _alloc);
if (open_res != Directory_service::OPENLINK_OK)
throw Nonexistent_file();
Vfs_handle::Guard guard(link_handle);
char buf[MAX_PATH_LEN];
Vfs::file_size count = sizeof(buf)-1;
Vfs::file_size out_count = 0;
while (!link_handle->fs().queue_read(link_handle, count)) {
_ep.wait_and_dispatch_one_io_signal();
}
File_io_service::Read_result result;
for (;;) {
result = link_handle->fs().complete_read(
link_handle, buf, count, out_count);
if (result != File_io_service::READ_QUEUED)
break;
_ep.wait_and_dispatch_one_io_signal();
};
if (result != File_io_service::READ_OK)
throw Nonexistent_file();
return Path(Genode::Cstring(buf, out_count));
}
void unlink(Path const &rel_path)
{
_fs.unlink(join(_path, rel_path).string());
}
2017-07-05 10:40:29 +02:00
};
struct Genode::Root_directory : public Vfs::Simple_env,
public Directory
2017-07-05 10:40:29 +02:00
{
Root_directory(Genode::Env &env, Allocator &alloc, Xml_node config)
2017-07-05 10:40:29 +02:00
:
Vfs::Simple_env(env, alloc, config), Directory((Vfs::Simple_env&)*this)
2017-07-05 10:40:29 +02:00
{ }
void apply_config(Xml_node config) { root_dir().apply_config(config); }
2017-07-05 10:40:29 +02:00
};
2018-03-26 13:44:04 +02:00
struct Genode::File : Noncopyable, Interface
2017-07-05 10:40:29 +02:00
{
struct Open_failed : Exception { };
struct Truncated_during_read : Exception { };
typedef Directory::Path Path;
};
class Genode::Readonly_file : public File
{
private:
2018-03-26 13:44:04 +02:00
/*
* Noncopyable
*/
Readonly_file(Readonly_file const &);
Readonly_file &operator = (Readonly_file const &);
Vfs::Vfs_handle mutable *_handle = nullptr;
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
2017-08-15 20:51:53 +02:00
Genode::Entrypoint &_ep;
2017-07-05 10:40:29 +02:00
void _open(Vfs::File_system &fs, Allocator &alloc, Path const path)
{
Vfs::Directory_service::Open_result res =
fs.open(path.string(), Vfs::Directory_service::OPEN_MODE_RDONLY,
&_handle, alloc);
if (res != Vfs::Directory_service::OPEN_OK) {
error("failed to open file '", path, "'");
throw Open_failed();
}
}
/**
* Strip off constness of 'Directory const &'
*
* Since the 'Readonly_file' API provides an abstraction over the
* low-level VFS operations, the intuitive meaning of 'const' is
* different between the 'Readonly_file' API and the VFS.
*
* At the VFS level, opening a file changes the internal state of the
* VFS. Hence the operation is non-const. However, the user of the
* 'Readonly_file' API expects the constness of a directory to
* correspond to whether the directory can be modified or not. In the
* case of instantiating a 'Readonly_file', one would expect that a
* 'Directory const &' would suffice. The fact that - under the hood -
* the 'Readonly_file' has to perform the nonconst 'open' operation at
* the VFS is of not of interest.
*/
static Directory &_mutable(Directory const &dir)
{
return const_cast<Directory &>(dir);
}
2017-07-05 10:40:29 +02:00
public:
/**
* Constructor
*
* \throw File::Open_failed
*/
Readonly_file(Directory const &dir, Path const &rel_path)
: _ep(_mutable(dir)._ep)
2017-07-05 10:40:29 +02:00
{
_open(_mutable(dir)._fs, _mutable(dir)._alloc,
Directory::join(dir._path, rel_path));
2017-07-05 10:40:29 +02:00
}
~Readonly_file() { _handle->ds().close(_handle); }
struct At { Vfs::file_size value; };
2017-07-05 10:40:29 +02:00
/**
* Read number of 'bytes' from file into local memory buffer 'dst'
*
* \throw Truncated_during_read
*/
size_t read(At at, char *dst, size_t bytes) const
2017-07-05 10:40:29 +02:00
{
Vfs::file_size out_count = 0;
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
2017-08-15 20:51:53 +02:00
_handle->seek(at.value);
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
2017-08-15 20:51:53 +02:00
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();
};
2017-07-05 10:40:29 +02:00
/*
* XXX handle READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK, READ_QUEUED
*/
if (result != Vfs::File_io_service::READ_OK)
throw Truncated_during_read();
return out_count;
}
/**
* Read number of 'bytes' from the start of the file into local memory
* buffer 'dst'
*
* \throw Truncated_during_read
*/
size_t read(char *dst, size_t bytes) const
{
return read(At{0}, dst, bytes);
}
2017-07-05 10:40:29 +02:00
};
class Genode::File_content
{
private:
class Buffer
{
private:
2017-07-05 10:40:29 +02:00
/*
* Noncopyable
*/
Buffer(Buffer const &);
Buffer &operator = (Buffer const &);
2017-07-05 10:40:29 +02:00
public:
Allocator &alloc;
size_t const size;
char * const ptr = size ? (char *)alloc.alloc(size) : nullptr;
Buffer(Allocator &alloc, size_t size) : alloc(alloc), size(size) { }
~Buffer() { if (ptr) alloc.free(ptr, size); }
} _buffer;
2018-03-26 13:44:04 +02:00
2017-07-05 10:40:29 +02:00
public:
typedef Directory::Nonexistent_file Nonexistent_file;
typedef File::Truncated_during_read Truncated_during_read;
typedef Directory::Path Path;
struct Limit { size_t value; };
/**
* Constructor
*
* \throw Nonexistent_file
* \throw Truncated_during_read number of readable bytes differs
* from file status information
*/
File_content(Allocator &alloc, Directory const &dir, Path const &rel_path,
2017-07-05 10:40:29 +02:00
Limit limit)
:
_buffer(alloc, min(dir.file_size(rel_path), (Vfs::file_size)limit.value))
2017-07-05 10:40:29 +02:00
{
if (Readonly_file(dir, rel_path).read(_buffer.ptr, _buffer.size) != _buffer.size)
2017-07-05 10:40:29 +02:00
throw Truncated_during_read();
}
/**
* Call functor 'fn' with content as 'Xml_node' argument
*
* If the file does not contain valid XML, 'fn' is called with an
* '<empty/>' node as argument.
*/
template <typename FN>
void xml(FN const &fn) const
{
try {
if (_buffer.size) {
fn(Xml_node(_buffer.ptr, _buffer.size));
return;
}
}
catch (Xml_node::Invalid_syntax) { }
fn(Xml_node("<empty/>"));
2017-07-05 10:40:29 +02:00
}
/**
* Call functor 'fn' with each line of the file as argument
*
* \param STRING string type used for the line
*/
template <typename STRING, typename FN>
void for_each_line(FN const &fn) const
{
if (_buffer.size == 0)
return;
char const *src = _buffer.ptr;
2017-07-05 10:40:29 +02:00
char const *curr_line = src;
size_t curr_line_len = 0;
for (size_t n = 0; ; n++) {
2017-07-05 10:40:29 +02:00
char const c = *src++;
bool const end_of_data = (c == 0 || n == _buffer.size);
2017-07-05 10:40:29 +02:00
bool const end_of_line = (c == '\n');
if (!end_of_data && !end_of_line) {
curr_line_len++;
continue;
}
if (!end_of_data || curr_line_len > 0)
fn(STRING(Cstring(curr_line, curr_line_len)));
2017-07-05 10:40:29 +02:00
if (end_of_data)
break;
2017-07-05 10:40:29 +02:00
curr_line = src;
curr_line_len = 0;
}
}
/**
* Call functor 'fn' with the data pointer and size in bytes
*
* If the buffer has a size of zero, 'fn' is not called.
*/
template <typename FN>
void bytes(FN const &fn) const
{
if (_buffer.size)
fn((char const *)_buffer.ptr, _buffer.size);
}
2017-07-05 10:40:29 +02:00
};
class Genode::Watcher
{
private:
/*
* Noncopyable
*/
Watcher(Watcher const &);
Watcher &operator = (Watcher const &);
Vfs::Vfs_watch_handle mutable *_handle { nullptr };
void _watch(Vfs::File_system &fs, Allocator &alloc, Directory::Path const path,
Vfs::Watch_response_handler &handler)
{
Vfs::Directory_service::Watch_result res =
fs.watch(path.string(), &_handle, alloc);
if (res == Vfs::Directory_service::WATCH_OK)
_handle->handler(&handler);
else
error("failed to watch '", path, "'");
}
static Directory &_mutable(Directory const &dir)
{
return const_cast<Directory &>(dir);
}
public:
Watcher(Directory const &dir, Directory::Path const &rel_path,
Vfs::Watch_response_handler &handler)
{
_watch(_mutable(dir)._fs, _mutable(dir)._alloc,
Directory::join(dir._path, rel_path), handler);
}
Watcher(Vfs::File_system &fs, Directory::Path const &rel_path,
Genode::Allocator &alloc, Vfs::Watch_response_handler &handler)
{
_watch(fs, alloc, rel_path, handler);
}
~Watcher() { _handle->fs().close(_handle); }
};
template <typename T>
class Genode::Watch_handler : public Vfs::Watch_response_handler,
private Watcher
{
private:
T &_obj;
void (T::*_member) ();
public:
Watch_handler(Directory &dir, Directory::Path const &rel_path,
T &obj, void (T::*member)())
:
Watcher(dir, rel_path, *this), _obj(obj), _member(member)
{ }
Watch_handler(Vfs::File_system &fs, Directory::Path const &rel_path,
Genode::Allocator &alloc, T &obj, void (T::*member)())
:
Watcher(fs,rel_path, alloc, *this), _obj(obj), _member(member)
{ }
void watch_response() override { (_obj.*_member)(); }
};
#endif /* _INCLUDE__OS__VFS_H_ */