2016-03-30 20:14:15 +02:00
|
|
|
/*
|
|
|
|
* \brief Internal nodes of VFS server
|
|
|
|
* \author Emery Hemingway
|
2017-02-01 21:07:14 +01:00
|
|
|
* \author Christian Helmuth
|
2016-03-30 20:14:15 +02:00
|
|
|
* \date 2016-03-29
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-01 21:07:14 +01:00
|
|
|
* Copyright (C) 2016-2017 Genode Labs GmbH
|
2016-03-30 20:14:15 +02:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 13:23:52 +01:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2016-03-30 20:14:15 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _VFS__NODE_H_
|
|
|
|
#define _VFS__NODE_H_
|
|
|
|
|
|
|
|
/* Genode includes */
|
|
|
|
#include <file_system/node.h>
|
|
|
|
#include <vfs/file_system.h>
|
|
|
|
#include <os/path.h>
|
2017-01-04 15:27:42 +01:00
|
|
|
#include <base/id_space.h>
|
2016-03-30 20:14:15 +02:00
|
|
|
|
|
|
|
/* Local includes */
|
|
|
|
#include "assert.h"
|
|
|
|
|
|
|
|
namespace Vfs_server {
|
|
|
|
|
|
|
|
using namespace File_system;
|
|
|
|
using namespace Vfs;
|
|
|
|
|
|
|
|
struct Node;
|
|
|
|
struct Directory;
|
|
|
|
struct File;
|
|
|
|
struct Symlink;
|
|
|
|
|
2017-01-04 15:27:42 +01:00
|
|
|
typedef Genode::Id_space<Node> Node_space;
|
|
|
|
|
2017-02-01 21:07:14 +01:00
|
|
|
struct File_io_handler
|
|
|
|
{
|
|
|
|
virtual void handle_file_io(File &file) = 0;
|
|
|
|
};
|
|
|
|
|
2017-02-12 10:58:27 +01:00
|
|
|
/**
|
|
|
|
* Read/write operation incomplete exception
|
|
|
|
*
|
|
|
|
* The operation can be retried later.
|
|
|
|
*/
|
|
|
|
struct Operation_incomplete { };
|
|
|
|
|
2016-03-30 20:14:15 +02:00
|
|
|
/* 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 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; };
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Type trait for determining the handle type for a given node type
|
|
|
|
*/
|
|
|
|
template<typename T> struct Handle_type;
|
|
|
|
template<> struct Handle_type<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; };
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that the file objects are created at the
|
|
|
|
* VFS in the local node constructors, this is to
|
Streamline exception types
This patch reduces the number of exception types by facilitating
globally defined exceptions for common usage patterns shared by most
services. In particular, RPC functions that demand a session-resource
upgrade not longer reflect this condition via a session-specific
exception but via the 'Out_of_ram' or 'Out_of_caps' types.
Furthermore, the 'Parent::Service_denied', 'Parent::Unavailable',
'Root::Invalid_args', 'Root::Unavailable', 'Service::Invalid_args',
'Service::Unavailable', and 'Local_service::Factory::Denied' types have
been replaced by the single 'Service_denied' exception type defined in
'session/session.h'.
This consolidation eases the error handling (there are fewer exceptions
to handle), alleviates the need to convert exceptions along the
session-creation call chain, and avoids possible aliasing problems
(catching the wrong type with the same name but living in a different
scope).
2017-05-07 22:03:22 +02:00
|
|
|
* ensure that Out_of_ram is thrown before
|
2016-03-30 20:14:15 +02:00
|
|
|
* the VFS is modified.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-01 21:07:14 +01:00
|
|
|
struct Vfs_server::Node : File_system::Node_base, Node_space::Element,
|
|
|
|
Vfs::Vfs_handle::Context
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
|
|
|
Path const _path;
|
|
|
|
Mode const mode;
|
|
|
|
|
2017-01-04 15:27:42 +01:00
|
|
|
Node(Node_space &space, char const *node_path, Mode node_mode)
|
|
|
|
:
|
|
|
|
Node_space::Element(*this, space),
|
|
|
|
_path(node_path), mode(node_mode)
|
|
|
|
{ }
|
2016-03-30 20:14:15 +02:00
|
|
|
|
|
|
|
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; }
|
2017-02-01 21:07:14 +01:00
|
|
|
virtual bool read_ready() { return false; }
|
|
|
|
virtual void handle_io_response() { }
|
2016-03-30 20:14:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Vfs_server::Symlink : Node
|
|
|
|
{
|
2017-01-04 15:27:42 +01:00
|
|
|
Symlink(Node_space &space,
|
|
|
|
Vfs::File_system &vfs,
|
2016-03-30 20:14:15 +02:00
|
|
|
char const *link_path,
|
|
|
|
Mode mode,
|
|
|
|
bool create)
|
2017-01-04 15:27:42 +01:00
|
|
|
: Node(space, link_path, mode)
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
/* ensure symlink gets something null-terminated */
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
Genode::String<MAX_PATH_LEN> target(Genode::Cstring(src, len));
|
2016-03-30 20:14:15 +02:00
|
|
|
|
|
|
|
if (vfs.symlink(target.string(), path()) == Directory_service::SYMLINK_OK)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
mark_as_updated();
|
|
|
|
notify_listeners();
|
|
|
|
return target.length();
|
|
|
|
}
|
2017-02-01 21:07:14 +01:00
|
|
|
|
|
|
|
bool read_ready() override { return true; }
|
2016-03-30 20:14:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Vfs_server::File : public Node
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2017-02-01 21:07:14 +01:00
|
|
|
File_io_handler &_file_io_handler;
|
|
|
|
|
2016-03-30 20:14:15 +02:00
|
|
|
Vfs::Vfs_handle *_handle;
|
2016-04-26 16:28:07 +02:00
|
|
|
char const *_leaf_path; /* offset pointer to Node::_path */
|
2016-03-30 20:14:15 +02:00
|
|
|
|
2017-02-12 10:58:27 +01:00
|
|
|
bool _notify_read_ready = false;
|
2016-05-25 22:11:09 +02:00
|
|
|
|
2017-02-12 10:58:27 +01:00
|
|
|
enum class Op_state {
|
|
|
|
IDLE, READ_QUEUED
|
|
|
|
} op_state = Op_state::IDLE;
|
|
|
|
|
|
|
|
public:
|
2017-02-01 21:07:14 +01:00
|
|
|
|
|
|
|
File(Node_space &space,
|
2017-01-04 15:27:42 +01:00
|
|
|
Vfs::File_system &vfs,
|
2016-03-30 20:14:15 +02:00
|
|
|
Genode::Allocator &alloc,
|
2017-02-01 21:07:14 +01:00
|
|
|
File_io_handler &file_io_handler,
|
2016-03-30 20:14:15 +02:00
|
|
|
char const *file_path,
|
|
|
|
Mode fs_mode,
|
|
|
|
bool create)
|
2017-02-01 21:07:14 +01:00
|
|
|
:
|
|
|
|
Node(space, file_path, fs_mode),
|
|
|
|
_file_io_handler(file_io_handler)
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
|
|
|
unsigned vfs_mode =
|
|
|
|
(fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0);
|
|
|
|
|
2016-04-26 16:28:07 +02:00
|
|
|
assert_open(vfs.open(file_path, vfs_mode, &_handle, alloc));
|
2017-02-01 21:07:14 +01:00
|
|
|
_leaf_path = vfs.leaf_path(path());
|
|
|
|
_handle->context = this;
|
2016-03-30 20:14:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
~File() { _handle->ds().close(_handle); }
|
|
|
|
|
|
|
|
void truncate(file_size_t size)
|
|
|
|
{
|
|
|
|
assert_truncate(_handle->fs().ftruncate(_handle, size));
|
|
|
|
mark_as_updated();
|
|
|
|
}
|
|
|
|
|
2017-02-12 10:58:27 +01:00
|
|
|
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; }
|
|
|
|
|
2016-03-30 20:14:15 +02:00
|
|
|
|
|
|
|
/********************
|
|
|
|
** Node interface **
|
|
|
|
********************/
|
|
|
|
|
2017-02-01 21:07:14 +01:00
|
|
|
size_t read(Vfs::File_system&, char *dst, size_t len,
|
|
|
|
seek_off_t seek_offset) override
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
2016-04-26 16:28:07 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-03-30 20:14:15 +02:00
|
|
|
_handle->seek(seek_offset);
|
2017-02-12 10:58:27 +01:00
|
|
|
|
|
|
|
typedef Vfs::File_io_service::Read_result Result;
|
|
|
|
|
|
|
|
Vfs::file_size out_count = 0;
|
|
|
|
Result out_result = Result::READ_OK;
|
|
|
|
|
|
|
|
switch (op_state) {
|
|
|
|
case Op_state::IDLE:
|
|
|
|
|
|
|
|
if (!_handle->fs().queue_read(_handle, dst, len, out_result, out_count))
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
throw Operation_incomplete();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2016-03-30 20:14:15 +02:00
|
|
|
}
|
|
|
|
|
2017-02-01 21:07:14 +01:00
|
|
|
size_t write(Vfs::File_system&, char const *src, size_t len,
|
|
|
|
seek_off_t seek_offset) override
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
|
|
|
Vfs::file_size res = 0;
|
|
|
|
|
2016-04-26 16:28:07 +02:00
|
|
|
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) ?
|
|
|
|
st.size : SEEK_TAIL;
|
|
|
|
}
|
|
|
|
|
2016-03-30 20:14:15 +02:00
|
|
|
_handle->seek(seek_offset);
|
|
|
|
_handle->fs().write(_handle, src, len, res);
|
|
|
|
if (res)
|
|
|
|
mark_as_updated();
|
|
|
|
return res;
|
|
|
|
}
|
2017-02-01 21:07:14 +01:00
|
|
|
|
|
|
|
bool read_ready() override { return _handle->fs().read_ready(_handle); }
|
|
|
|
|
|
|
|
void handle_io_response() override
|
|
|
|
{
|
|
|
|
_file_io_handler.handle_file_io(*this);
|
|
|
|
}
|
2016-03-30 20:14:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Vfs_server::Directory : Node
|
|
|
|
{
|
2017-01-04 15:27:42 +01:00
|
|
|
Directory(Node_space &space, Vfs::File_system &vfs, char const *dir_path, bool create)
|
|
|
|
: Node(space, dir_path, READ_ONLY)
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
|
|
|
if (create)
|
|
|
|
assert_mkdir(vfs.mkdir(dir_path, 0));
|
|
|
|
}
|
|
|
|
|
2017-01-04 15:27:42 +01:00
|
|
|
Node_space::Id file(Node_space &space,
|
|
|
|
Vfs::File_system &vfs,
|
|
|
|
Genode::Allocator &alloc,
|
2017-02-01 21:07:14 +01:00
|
|
|
File_io_handler &file_io_handler,
|
2017-01-04 15:27:42 +01:00
|
|
|
char const *file_path,
|
|
|
|
Mode mode,
|
|
|
|
bool create)
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
|
|
|
Path subpath(file_path, path());
|
|
|
|
char const *path_str = subpath.base();
|
|
|
|
|
|
|
|
File *file;
|
2017-02-01 21:07:14 +01:00
|
|
|
try {
|
|
|
|
file = new (alloc)
|
|
|
|
File(space, vfs, alloc, file_io_handler, path_str, mode, create);
|
Streamline exception types
This patch reduces the number of exception types by facilitating
globally defined exceptions for common usage patterns shared by most
services. In particular, RPC functions that demand a session-resource
upgrade not longer reflect this condition via a session-specific
exception but via the 'Out_of_ram' or 'Out_of_caps' types.
Furthermore, the 'Parent::Service_denied', 'Parent::Unavailable',
'Root::Invalid_args', 'Root::Unavailable', 'Service::Invalid_args',
'Service::Unavailable', and 'Local_service::Factory::Denied' types have
been replaced by the single 'Service_denied' exception type defined in
'session/session.h'.
This consolidation eases the error handling (there are fewer exceptions
to handle), alleviates the need to convert exceptions along the
session-creation call chain, and avoids possible aliasing problems
(catching the wrong type with the same name but living in a different
scope).
2017-05-07 22:03:22 +02:00
|
|
|
} catch (Out_of_memory) { throw Out_of_ram(); }
|
2017-02-01 21:07:14 +01:00
|
|
|
|
2016-03-30 20:14:15 +02:00
|
|
|
if (create)
|
|
|
|
mark_as_updated();
|
2017-01-04 15:27:42 +01:00
|
|
|
return file->id();
|
2016-03-30 20:14:15 +02:00
|
|
|
}
|
|
|
|
|
2017-01-04 15:27:42 +01:00
|
|
|
Node_space::Id symlink(Node_space &space,
|
|
|
|
Vfs::File_system &vfs,
|
|
|
|
Genode::Allocator &alloc,
|
|
|
|
char const *link_path,
|
|
|
|
Mode mode,
|
|
|
|
bool create)
|
2016-03-30 20:14:15 +02:00
|
|
|
{
|
|
|
|
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;
|
2017-01-04 15:27:42 +01:00
|
|
|
try { link = new (alloc) Symlink(space, vfs, path_str, mode, create); }
|
Streamline exception types
This patch reduces the number of exception types by facilitating
globally defined exceptions for common usage patterns shared by most
services. In particular, RPC functions that demand a session-resource
upgrade not longer reflect this condition via a session-specific
exception but via the 'Out_of_ram' or 'Out_of_caps' types.
Furthermore, the 'Parent::Service_denied', 'Parent::Unavailable',
'Root::Invalid_args', 'Root::Unavailable', 'Service::Invalid_args',
'Service::Unavailable', and 'Local_service::Factory::Denied' types have
been replaced by the single 'Service_denied' exception type defined in
'session/session.h'.
This consolidation eases the error handling (there are fewer exceptions
to handle), alleviates the need to convert exceptions along the
session-creation call chain, and avoids possible aliasing problems
(catching the wrong type with the same name but living in a different
scope).
2017-05-07 22:03:22 +02:00
|
|
|
catch (Out_of_memory) { throw Out_of_ram(); }
|
2016-03-30 20:14:15 +02:00
|
|
|
if (create)
|
|
|
|
mark_as_updated();
|
2017-01-04 15:27:42 +01:00
|
|
|
return link->id();
|
2016-03-30 20:14:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/********************
|
|
|
|
** Node interface **
|
|
|
|
********************/
|
|
|
|
|
|
|
|
size_t read(Vfs::File_system &vfs, char *dst, size_t len, seek_off_t seek_offset)
|
|
|
|
{
|
|
|
|
Directory_service::Dirent vfs_dirent;
|
|
|
|
size_t blocksize = sizeof(File_system::Directory_entry);
|
|
|
|
|
|
|
|
unsigned index = (seek_offset / blocksize);
|
|
|
|
|
|
|
|
size_t remains = len;
|
|
|
|
|
|
|
|
while (remains >= blocksize) {
|
|
|
|
if (vfs.dirent(path(), index++, vfs_dirent)
|
|
|
|
!= Vfs::Directory_service::DIRENT_OK)
|
|
|
|
return len - remains;
|
|
|
|
|
|
|
|
File_system::Directory_entry *fs_dirent = (Directory_entry *)dst;
|
|
|
|
fs_dirent->inode = vfs_dirent.fileno;
|
|
|
|
switch (vfs_dirent.type) {
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_DIRECTORY:
|
|
|
|
fs_dirent->type = File_system::Directory_entry::TYPE_DIRECTORY;
|
|
|
|
break;
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_SYMLINK:
|
|
|
|
fs_dirent->type = File_system::Directory_entry::TYPE_SYMLINK;
|
|
|
|
break;
|
|
|
|
case Vfs::Directory_service::DIRENT_TYPE_FILE:
|
|
|
|
default:
|
|
|
|
fs_dirent->type = File_system::Directory_entry::TYPE_FILE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strncpy(fs_dirent->name, vfs_dirent.name, MAX_NAME_LEN);
|
|
|
|
|
|
|
|
remains -= blocksize;
|
|
|
|
dst += blocksize;
|
|
|
|
}
|
|
|
|
return len - remains;
|
|
|
|
}
|
2017-02-01 21:07:14 +01:00
|
|
|
|
|
|
|
bool read_ready() override { return true; }
|
2016-03-30 20:14:15 +02:00
|
|
|
};
|
|
|
|
|
2016-05-25 22:11:09 +02:00
|
|
|
#endif /* _VFS__NODE_H_ */
|