875 lines
21 KiB
C++
875 lines
21 KiB
C++
/*
|
|
* \brief Internal nodes of VFS server
|
|
* \author Emery Hemingway
|
|
* \author Christian Helmuth
|
|
* \date 2016-03-29
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2016-2019 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 _VFS__NODE_H_
|
|
#define _VFS__NODE_H_
|
|
|
|
/* Genode includes */
|
|
#include <file_system/node.h>
|
|
#include <vfs/file_system.h>
|
|
#include <os/path.h>
|
|
#include <base/id_space.h>
|
|
|
|
/* Local includes */
|
|
#include "assert.h"
|
|
|
|
namespace Vfs_server {
|
|
|
|
using namespace File_system;
|
|
using namespace Vfs;
|
|
|
|
typedef Vfs::File_io_service::Write_result Write_result;
|
|
typedef Vfs::File_io_service::Read_result Read_result;
|
|
typedef Vfs::File_io_service::Sync_result Sync_result;
|
|
|
|
typedef ::File_system::Session::Tx::Sink Packet_stream;
|
|
|
|
class Node;
|
|
class Io_node;
|
|
class Watch_node;
|
|
class Directory;
|
|
class File;
|
|
class Symlink;
|
|
|
|
typedef Genode::Id_space<Node> Node_space;
|
|
typedef Genode::Fifo<Node> Node_queue;
|
|
|
|
/* Vfs::MAX_PATH is shorter than File_system::MAX_PATH */
|
|
enum { MAX_PATH_LEN = Vfs::MAX_PATH_LEN };
|
|
|
|
typedef Genode::Path<MAX_PATH_LEN> Path;
|
|
|
|
typedef Genode::Allocator::Out_of_memory Out_of_memory;
|
|
|
|
/**
|
|
* Type trait for determining the node type for a given handle type
|
|
*/
|
|
template<typename T> struct Node_type;
|
|
template<> struct Node_type<Node_handle> { typedef Io_node Type; };
|
|
template<> struct Node_type<Dir_handle> { typedef Directory Type; };
|
|
template<> struct Node_type<File_handle> { typedef File Type; };
|
|
template<> struct Node_type<Symlink_handle> { typedef Symlink Type; };
|
|
template<> struct Node_type<Watch_handle> { typedef Watch_node Type; };
|
|
|
|
/**
|
|
* Type trait for determining the handle type for a given node type
|
|
*/
|
|
template<typename T> struct Handle_type;
|
|
template<> struct Handle_type<Io_node> { typedef Node_handle Type; };
|
|
template<> struct Handle_type<Directory> { typedef Dir_handle Type; };
|
|
template<> struct Handle_type<File> { typedef File_handle Type; };
|
|
template<> struct Handle_type<Symlink> { typedef Symlink_handle Type; };
|
|
template<> struct Handle_type<Watch> { typedef Watch_handle Type; };
|
|
|
|
/*
|
|
* Note that the file objects are created at the
|
|
* VFS in the local node constructors, this is to
|
|
* ensure that in the case of file creating that the
|
|
* Out_of_ram exception is thrown before the VFS is
|
|
* modified.
|
|
*/
|
|
}
|
|
|
|
|
|
class Vfs_server::Node : public ::File_system::Node_base,
|
|
private Node_space::Element,
|
|
private Node_queue::Element
|
|
{
|
|
private:
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Node(Node const &);
|
|
Node &operator = (Node const &);
|
|
|
|
Path const _path;
|
|
|
|
protected:
|
|
|
|
/*
|
|
* Global queue of nodes that await
|
|
* some response from the VFS libray
|
|
*
|
|
* A global collection is perhaps dangerous
|
|
* but ensures fairness across sessions
|
|
*/
|
|
Node_queue &_response_queue;
|
|
|
|
/* stream used for reply packets */
|
|
Packet_stream &_stream;
|
|
|
|
public:
|
|
|
|
friend Node_queue;
|
|
using Node_queue::Element::enqueued;
|
|
|
|
Node(Node_space &space,
|
|
char const *node_path,
|
|
Node_queue &response_queue,
|
|
Packet_stream &stream)
|
|
: Node_space::Element(*this, space),
|
|
_path(node_path),
|
|
_response_queue(response_queue),
|
|
_stream(stream)
|
|
{ }
|
|
|
|
virtual ~Node()
|
|
{
|
|
if (enqueued())
|
|
_response_queue.remove(*this);
|
|
}
|
|
|
|
using Node_space::Element::id;
|
|
|
|
char const *path() const { return _path.base(); }
|
|
|
|
/**
|
|
* Process pending activity, called by post-signal hook
|
|
*
|
|
* Default implementation is to return true so that the
|
|
* node is removed from the pending handle queue.
|
|
*/
|
|
virtual bool process_io() { return true; }
|
|
|
|
/**
|
|
* Print for debugging
|
|
*/
|
|
void print(Genode::Output &out) const {
|
|
out.out_string(_path.base()); }
|
|
};
|
|
|
|
|
|
/**
|
|
* Super-class for nodes that process read/write packets
|
|
*/
|
|
class Vfs_server::Io_node : public Vfs_server::Node,
|
|
public Vfs::Io_response_handler{
|
|
public:
|
|
|
|
enum Op_state { IDLE, READ_QUEUED, SYNC_QUEUED };
|
|
|
|
private:
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Io_node(Io_node const &);
|
|
Io_node &operator = (Io_node const &);
|
|
|
|
Mode const _mode;
|
|
|
|
bool _packet_queued = false;
|
|
bool _packet_op_pending = false;
|
|
|
|
protected:
|
|
|
|
Vfs::Vfs_handle &_handle;
|
|
|
|
/**
|
|
* Packets that have been removed from the
|
|
* packet stream are transfered here
|
|
*/
|
|
Packet_descriptor _packet { };
|
|
|
|
/**
|
|
* Abstract read implementation
|
|
*
|
|
* Returns true if the pending packet
|
|
* shall be returned to client
|
|
*/
|
|
bool _vfs_read(char *dst, file_size count,
|
|
file_offset seek_offset, file_size &out_count)
|
|
{
|
|
if (!(_mode & READ_ONLY)) return true;
|
|
|
|
_handle.seek(seek_offset);
|
|
|
|
if (!_packet_op_pending) {
|
|
/* if the read cannot be queued with the VFS then stop here */
|
|
if (!_handle.fs().queue_read(&_handle, count)) {
|
|
return false;
|
|
}
|
|
_packet_op_pending = true;
|
|
}
|
|
|
|
Read_result result = _handle.fs().complete_read(
|
|
&_handle, dst, count, out_count);
|
|
|
|
switch (result) {
|
|
case Read_result::READ_OK:
|
|
_packet.succeeded(true);
|
|
break;
|
|
|
|
case Read_result::READ_ERR_IO:
|
|
case Read_result::READ_ERR_INVALID:
|
|
_packet.length(out_count);
|
|
break;
|
|
|
|
case Read_result::READ_ERR_WOULD_BLOCK:
|
|
case Read_result::READ_ERR_AGAIN:
|
|
case Read_result::READ_ERR_INTERRUPT:
|
|
case Read_result::READ_QUEUED:
|
|
/* packet is still pending */
|
|
return false;
|
|
}
|
|
|
|
/* packet is processed */
|
|
_packet_op_pending = false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Abstract write implementation
|
|
*
|
|
* Returns true if the pending packet
|
|
* shall be returned to client
|
|
*/
|
|
bool _vfs_write(char const *src, file_size count,
|
|
file_offset seek_offset, file_size &out_count)
|
|
{
|
|
if (!(_mode & WRITE_ONLY))
|
|
return true;
|
|
|
|
_handle.seek(seek_offset);
|
|
|
|
try {
|
|
Write_result result = _handle.fs().write(
|
|
&_handle, src, count, out_count);
|
|
|
|
if (result == Write_result::WRITE_OK) {
|
|
mark_as_updated();
|
|
_packet.succeeded(true);
|
|
}
|
|
}
|
|
catch (Vfs::File_io_service::Insufficient_buffer)
|
|
{
|
|
/* packet is pending */
|
|
return false;
|
|
}
|
|
|
|
/* packet is processed */
|
|
return true;
|
|
|
|
/* No further error handling! */
|
|
}
|
|
|
|
inline
|
|
void _drop_packet()
|
|
{
|
|
_packet = Packet_descriptor();
|
|
_packet_queued = false;
|
|
}
|
|
|
|
inline
|
|
void _ack_packet(size_t count)
|
|
{
|
|
_packet.length(count);
|
|
_stream.acknowledge_packet(_packet);
|
|
_packet = Packet_descriptor();
|
|
_packet_queued = false;
|
|
}
|
|
|
|
/**
|
|
* Abstract sync implementation
|
|
*/
|
|
bool _sync()
|
|
{
|
|
if (!_packet_op_pending) {
|
|
/* if the sync cannot be queued with the VFS then stop here */
|
|
if (!_handle.fs().queue_sync(&_handle)) {
|
|
return false;
|
|
}
|
|
_packet_op_pending = true;
|
|
}
|
|
|
|
Sync_result result = _handle.fs().complete_sync(&_handle);
|
|
|
|
switch (result) {
|
|
case Sync_result::SYNC_OK:
|
|
_packet.succeeded(true);
|
|
break;
|
|
|
|
case Sync_result::SYNC_ERR_INVALID:
|
|
break;
|
|
|
|
case Sync_result::SYNC_QUEUED:
|
|
/* packet still pending */
|
|
return false;
|
|
}
|
|
|
|
/* packet processed */
|
|
_ack_packet(0);
|
|
_packet_op_pending = false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Virtual methods for specialized node-type I/O
|
|
*/
|
|
virtual bool _read() = 0;
|
|
virtual bool _write() = 0;
|
|
|
|
public:
|
|
|
|
Io_node(Node_space &space, char const *node_path, Mode node_mode,
|
|
Node_queue &response_queue, Packet_stream &stream,
|
|
Vfs_handle &handle)
|
|
: Node(space, node_path, response_queue, stream),
|
|
_mode(node_mode), _handle(handle)
|
|
{
|
|
_handle.handler(this);
|
|
}
|
|
|
|
virtual ~Io_node()
|
|
{
|
|
_handle.handler(nullptr);
|
|
_handle.close();
|
|
}
|
|
|
|
using Node_space::Element::id;
|
|
|
|
/**
|
|
* Process the packet that is queued at this handle
|
|
*
|
|
* Return true if the node was processed and is now idle.
|
|
*/
|
|
bool process_io() override
|
|
{
|
|
if (!_packet_queued) return true;
|
|
if (!_stream.ready_to_ack())
|
|
return false;
|
|
|
|
bool result = true;
|
|
|
|
switch (_packet.operation()) {
|
|
case Packet_descriptor::READ: result = _read(); break;
|
|
case Packet_descriptor::WRITE: result = _write(); break;
|
|
case Packet_descriptor::SYNC: result = _sync(); break;
|
|
|
|
case Packet_descriptor::READ_READY:
|
|
/*
|
|
* the read-ready pending state is managed
|
|
* by the VFS, this packet can be discarded
|
|
*/
|
|
_drop_packet();
|
|
|
|
if (_handle.fs().read_ready(&_handle)) {
|
|
/* if the handle is ready, send a packet back immediately */
|
|
read_ready_response();
|
|
} else {
|
|
/* register to send READ_READY later */
|
|
_handle.fs().notify_read_ready(&_handle);
|
|
}
|
|
|
|
break;
|
|
|
|
case Packet_descriptor::CONTENT_CHANGED:
|
|
/* discard this packet */
|
|
_drop_packet();
|
|
break;
|
|
|
|
case Packet_descriptor::WRITE_TIMESTAMP:
|
|
try {
|
|
_packet.with_timestamp([&] (File_system::Timestamp const time) {
|
|
Vfs::Timestamp ts { .value = time.value };
|
|
_handle.fs().update_modification_timestamp(&_handle, ts);
|
|
});
|
|
_packet.succeeded(true);
|
|
_ack_packet(0);
|
|
} catch (Vfs::File_io_service::Insufficient_buffer) {
|
|
/* packet is pending */
|
|
result = false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Process a packet by queuing it locally or sending
|
|
* an immediate response. Return false if no progress
|
|
* can be made.
|
|
*
|
|
* Called by packet stream signal handler
|
|
*/
|
|
bool process_packet(Packet_descriptor const &packet)
|
|
{
|
|
/* attempt to clear any pending packet */
|
|
if (!process_io())
|
|
return false;
|
|
|
|
/* otherwise store the packet locally and process */
|
|
_packet = packet;
|
|
_packet_queued = true;
|
|
process_io();
|
|
return true;
|
|
}
|
|
|
|
Mode mode() const { return _mode; }
|
|
|
|
|
|
/****************************************
|
|
** Vfs::Io_response_handler interface **
|
|
****************************************/
|
|
|
|
/**
|
|
* Called by the VFS plugin of this handle
|
|
*/
|
|
void read_ready_response() override
|
|
{
|
|
if (!_stream.ready_to_ack()) {
|
|
/* log a message to catch loops */
|
|
Genode::warning("deferring READ_READY response");
|
|
_handle.fs().notify_read_ready(&_handle);
|
|
return;
|
|
}
|
|
|
|
/* Send packet immediately, though this could be queued */
|
|
Packet_descriptor packet(Packet_descriptor(),
|
|
Node_handle { id().value },
|
|
Packet_descriptor::READ_READY,
|
|
0, 0);
|
|
packet.succeeded(true);
|
|
_stream.acknowledge_packet(packet);
|
|
}
|
|
|
|
/**
|
|
* Called by the VFS plugin of this handle
|
|
*/
|
|
void io_progress_response() override
|
|
{
|
|
/*
|
|
* do not process packet immediately,
|
|
* queue to maintain ordering (priorities?)
|
|
*/
|
|
if (!enqueued())
|
|
_response_queue.enqueue(*this);
|
|
}
|
|
};
|
|
|
|
|
|
class Vfs_server::Watch_node final : public Vfs_server::Node,
|
|
public Vfs::Watch_response_handler
|
|
{
|
|
private:
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Watch_node(Watch_node const &);
|
|
Watch_node &operator = (Watch_node const &);
|
|
|
|
Vfs::Vfs_watch_handle &_watch_handle;
|
|
|
|
public:
|
|
|
|
Watch_node(Node_space &space, char const *path,
|
|
Vfs::Vfs_watch_handle &handle,
|
|
Node_queue &response_queue,
|
|
Packet_stream &stream)
|
|
: Node(space, path, response_queue, stream),
|
|
_watch_handle(handle)
|
|
{
|
|
_watch_handle.handler(this);
|
|
}
|
|
|
|
~Watch_node() {
|
|
_watch_handle.close(); }
|
|
|
|
|
|
/*******************************************
|
|
** Vfs::Watch_response_handler interface **
|
|
*******************************************/
|
|
|
|
void watch_response() override
|
|
{
|
|
/* send a packet immediately otherwise defer */
|
|
if (!process_io() && !enqueued())
|
|
_response_queue.enqueue(*this);
|
|
}
|
|
|
|
|
|
/********************************
|
|
** Vfs_server::Node interface **
|
|
********************************/
|
|
|
|
/**
|
|
* Called by global I/O progress handler
|
|
*/
|
|
bool process_io() override
|
|
{
|
|
if (!_stream.ready_to_ack()) return false;
|
|
|
|
Packet_descriptor packet(Packet_descriptor(),
|
|
Node_handle { id().value },
|
|
Packet_descriptor::CONTENT_CHANGED,
|
|
0, 0);
|
|
packet.succeeded(true);
|
|
_stream.acknowledge_packet(packet);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
struct Vfs_server::Symlink : Io_node
|
|
{
|
|
protected:
|
|
|
|
/********************
|
|
** Node interface **
|
|
********************/
|
|
|
|
bool _read() override
|
|
{
|
|
if (_packet.position() != 0) {
|
|
/* partial read is not supported */
|
|
_ack_packet(0);
|
|
return true;
|
|
}
|
|
|
|
file_size out_count = 0;
|
|
bool result = _vfs_read(_stream.packet_content(_packet),
|
|
_packet.length(), 0, out_count);
|
|
if (result)
|
|
_ack_packet(out_count);
|
|
return result;
|
|
}
|
|
|
|
bool _write() override
|
|
{
|
|
if (_packet.position() != 0) {
|
|
/* partial write is not supported */
|
|
_ack_packet(0);
|
|
return true;
|
|
}
|
|
|
|
file_size count = _packet.length();
|
|
|
|
/*
|
|
* if the symlink target is too long return a short result
|
|
* because a competent File_system client will error on a
|
|
* length mismatch
|
|
*/
|
|
if (count > MAX_PATH_LEN) {
|
|
_ack_packet(1);
|
|
return true;
|
|
}
|
|
|
|
/* ensure symlink gets something null-terminated */
|
|
Genode::String<MAX_PATH_LEN+1> target(Genode::Cstring(
|
|
_stream.packet_content(_packet), count));
|
|
size_t const target_len = target.length()-1;
|
|
|
|
file_size out_count = 0;
|
|
bool result = _vfs_write(target.string(), target_len, 0, out_count);
|
|
|
|
if (result) {
|
|
_ack_packet(out_count);
|
|
if (out_count > 0) {
|
|
mark_as_updated();
|
|
notify_listeners();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static
|
|
Vfs_handle &_open(Vfs::File_system &vfs, Genode::Allocator &alloc,
|
|
char const *link_path, bool create)
|
|
{
|
|
Vfs_handle *h = nullptr;
|
|
assert_openlink(vfs.openlink(link_path, create, &h, alloc));
|
|
return *h;
|
|
}
|
|
|
|
public:
|
|
|
|
Symlink(Node_space &space,
|
|
Vfs::File_system &vfs,
|
|
Genode::Allocator &alloc,
|
|
Node_queue &response_queue,
|
|
Packet_stream &stream,
|
|
char const *link_path,
|
|
Mode mode,
|
|
bool create)
|
|
: Io_node(space, link_path, mode, response_queue, stream,
|
|
_open(vfs, alloc, link_path, create))
|
|
{ }
|
|
};
|
|
|
|
|
|
class Vfs_server::File : public Io_node
|
|
{
|
|
private:
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
File(File const &);
|
|
File &operator = (File const &);
|
|
|
|
char const *_leaf_path = nullptr; /* offset pointer to Node::_path */
|
|
|
|
inline
|
|
seek_off_t seek_tail(file_size count)
|
|
{
|
|
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 */
|
|
return (_handle.ds().stat(_leaf_path, st) == Result::STAT_OK)
|
|
? ((count < st.size) ? (st.size - count) : 0)
|
|
: (seek_off_t)SEEK_TAIL;
|
|
}
|
|
|
|
protected:
|
|
|
|
bool _read() override
|
|
{
|
|
file_size out_count = 0;
|
|
file_size count = _packet.length();
|
|
seek_off_t seek_offset = _packet.position();
|
|
|
|
if (seek_offset == (seek_off_t)SEEK_TAIL)
|
|
seek_offset = seek_tail(count);
|
|
|
|
bool result = _vfs_read(_stream.packet_content(_packet),
|
|
count, seek_offset, out_count);
|
|
if (result)
|
|
_ack_packet(out_count);
|
|
return result;
|
|
}
|
|
|
|
bool _write() override
|
|
{
|
|
file_size out_count = 0;
|
|
file_size count = _packet.length();
|
|
seek_off_t seek_offset = _packet.position();
|
|
|
|
if (seek_offset == (seek_off_t)SEEK_TAIL)
|
|
seek_offset = seek_tail(count);
|
|
|
|
bool result = _vfs_write(_stream.packet_content(_packet),
|
|
count, seek_offset, out_count);
|
|
if (result) {
|
|
_ack_packet(out_count);
|
|
if (out_count > 0) {
|
|
mark_as_updated();
|
|
notify_listeners();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static
|
|
Vfs_handle &_open(Vfs::File_system &vfs, Genode::Allocator &alloc,
|
|
char const *file_path, Mode fs_mode, bool create)
|
|
{
|
|
Vfs_handle *h = nullptr;
|
|
unsigned vfs_mode = (fs_mode-1) |
|
|
(create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0);
|
|
|
|
assert_open(vfs.open(file_path, vfs_mode, &h, alloc));
|
|
return *h;
|
|
}
|
|
|
|
public:
|
|
|
|
File(Node_space &space,
|
|
Vfs::File_system &vfs,
|
|
Genode::Allocator &alloc,
|
|
Node_queue &response_queue,
|
|
Packet_stream &stream,
|
|
char const *file_path,
|
|
Mode fs_mode,
|
|
bool create)
|
|
:
|
|
Io_node(space, file_path, fs_mode, response_queue, stream,
|
|
_open(vfs, alloc, file_path, fs_mode, create))
|
|
{
|
|
_leaf_path = vfs.leaf_path(path());
|
|
}
|
|
|
|
void truncate(file_size_t size)
|
|
{
|
|
assert_truncate(_handle.fs().ftruncate(&_handle, size));
|
|
mark_as_updated();
|
|
}
|
|
};
|
|
|
|
|
|
struct Vfs_server::Directory : Io_node
|
|
{
|
|
protected:
|
|
|
|
/********************
|
|
** Node interface **
|
|
********************/
|
|
|
|
bool _read() override
|
|
{
|
|
if (_packet.length() < sizeof(Directory_entry)) {
|
|
_ack_packet(0);
|
|
return true;
|
|
}
|
|
|
|
seek_off_t const seek_offset = _packet.position();
|
|
|
|
size_t const blocksize = sizeof(::File_system::Directory_entry);
|
|
|
|
unsigned const index = (seek_offset / blocksize);
|
|
|
|
file_size out_count = 0;
|
|
|
|
Directory_service::Dirent vfs_dirent { };
|
|
|
|
bool const result = _vfs_read((char*)&vfs_dirent,
|
|
sizeof(vfs_dirent),
|
|
index * sizeof(vfs_dirent),
|
|
out_count);
|
|
vfs_dirent.sanitize();
|
|
|
|
if (result) {
|
|
if (out_count != sizeof(vfs_dirent)) {
|
|
_ack_packet(0);
|
|
return true;
|
|
}
|
|
|
|
auto fs_dirent_type = [&] (Vfs::Directory_service::Dirent_type type)
|
|
{
|
|
using From = Vfs::Directory_service::Dirent_type;
|
|
using To = ::File_system::Node_type;
|
|
|
|
/*
|
|
* This should never be taken because 'END' is checked as a
|
|
* precondition prior the call to of this function.
|
|
*/
|
|
To const default_result = To::CONTINUOUS_FILE;
|
|
|
|
switch (type) {
|
|
case From::END: return default_result;
|
|
case From::DIRECTORY: return To::DIRECTORY;
|
|
case From::SYMLINK: return To::SYMLINK;
|
|
case From::CONTINUOUS_FILE: return To::CONTINUOUS_FILE;
|
|
case From::TRANSACTIONAL_FILE: return To::TRANSACTIONAL_FILE;
|
|
}
|
|
return default_result;
|
|
};
|
|
|
|
if (vfs_dirent.type == Vfs::Directory_service::Dirent_type::END) {
|
|
_ack_packet(0);
|
|
|
|
} else {
|
|
|
|
::File_system::Directory_entry &fs_dirent =
|
|
*(Directory_entry *)_stream.packet_content(_packet);
|
|
|
|
fs_dirent = {
|
|
.inode = vfs_dirent.fileno,
|
|
.type = fs_dirent_type(vfs_dirent.type),
|
|
.rwx = {
|
|
.readable = vfs_dirent.rwx.readable,
|
|
.writeable = vfs_dirent.rwx.writeable,
|
|
.executable = vfs_dirent.rwx.executable },
|
|
.name = { vfs_dirent.name.buf }
|
|
};
|
|
|
|
_ack_packet(sizeof(Directory_entry));
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _write() override
|
|
{
|
|
_ack_packet(0);
|
|
return true;
|
|
}
|
|
|
|
static
|
|
Vfs_handle &_open(Vfs::File_system &vfs, Genode::Allocator &alloc,
|
|
char const *dir_path, bool create)
|
|
{
|
|
Vfs_handle *h = nullptr;
|
|
assert_opendir(vfs.opendir(dir_path, create, &h, alloc));
|
|
return *h;
|
|
}
|
|
|
|
public:
|
|
|
|
Directory(Node_space &space,
|
|
Vfs::File_system &vfs,
|
|
Genode::Allocator &alloc,
|
|
Node_queue &response_queue,
|
|
Packet_stream &stream,
|
|
char const *dir_path,
|
|
bool create)
|
|
: Io_node(space, dir_path, READ_ONLY, response_queue, stream,
|
|
_open(vfs, alloc, dir_path, create))
|
|
{ }
|
|
|
|
/**
|
|
* Open a file handle at this directory
|
|
*/
|
|
Node_space::Id file(Node_space &space,
|
|
Vfs::File_system &vfs,
|
|
Genode::Allocator &alloc,
|
|
char const *file_path,
|
|
Mode mode,
|
|
bool create)
|
|
{
|
|
Path subpath(file_path, path());
|
|
char const *path_str = subpath.base();
|
|
|
|
File *file;
|
|
try {
|
|
file = new (alloc) File(space, vfs, alloc,
|
|
_response_queue, _stream,
|
|
path_str, mode, create);
|
|
} catch (Out_of_memory) { throw Out_of_ram(); }
|
|
|
|
if (create)
|
|
mark_as_updated();
|
|
return file->id();
|
|
}
|
|
|
|
/**
|
|
* Open a symlink handle at this directory
|
|
*/
|
|
Node_space::Id symlink(Node_space &space,
|
|
Vfs::File_system &vfs,
|
|
Genode::Allocator &alloc,
|
|
char const *link_path,
|
|
Mode mode,
|
|
bool create)
|
|
{
|
|
Path subpath(link_path, path());
|
|
char const *path_str = subpath.base();
|
|
|
|
Symlink *link;
|
|
try { link = new (alloc) Symlink(space, vfs, alloc,
|
|
_response_queue, _stream,
|
|
path_str, mode, create); }
|
|
catch (Out_of_memory) { throw Out_of_ram(); }
|
|
if (create)
|
|
mark_as_updated();
|
|
return link->id();
|
|
}
|
|
};
|
|
|
|
#endif /* _VFS__NODE_H_ */
|