mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
Move VFS from noux to 'os/include/vfs'
By separating the VFS from Noux, we become able to reuse the VFS for libc-using programs. The most substantial change is the removal of Noux::Sysio data structures from the VFS. Instead, the Noux::Sysio refers to the VFS types now. The new VFS library is located at 'os/include/vfs/'. Furthermore, the patch removes various code duplications related to pseudo file systems that provide a single file (block, zero, random, terminal, null). The new 'Single_file_system' holds the common boilerplate code for those. Issue #999
This commit is contained in:
parent
d7d62c53a6
commit
c3c6a82d13
328
os/include/vfs/block_file_system.h
Normal file
328
os/include/vfs/block_file_system.h
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
* \brief Block device file system
|
||||
* \author Josef Soentgen
|
||||
* \author Norman Feske
|
||||
* \date 2013-12-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__BLOCK_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__BLOCK_FILE_SYSTEM_H_
|
||||
|
||||
#include <base/allocator_avl.h>
|
||||
#include <block_session/connection.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
namespace Vfs { class Block_file_system; }
|
||||
|
||||
|
||||
class Vfs::Block_file_system : public Single_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
struct Label
|
||||
{
|
||||
enum { LABEL_MAX_LEN = 64 };
|
||||
char string[LABEL_MAX_LEN];
|
||||
|
||||
Label(Xml_node config)
|
||||
{
|
||||
string[0] = 0;
|
||||
try { config.attribute("label").value(string, sizeof(string)); }
|
||||
catch (...) { }
|
||||
}
|
||||
} _label;
|
||||
|
||||
/*
|
||||
* Serialize access to packet stream of the block session
|
||||
*/
|
||||
Lock _lock;
|
||||
|
||||
char *_block_buffer;
|
||||
unsigned _block_buffer_count;
|
||||
|
||||
Genode::Allocator_avl _tx_block_alloc;
|
||||
Block::Connection _block;
|
||||
size_t _block_size;
|
||||
Block::sector_t _block_count;
|
||||
Block::Session::Operations _block_ops;
|
||||
Block::Session::Tx::Source *_tx_source;
|
||||
|
||||
bool _readable;
|
||||
bool _writeable;
|
||||
|
||||
size_t _block_io(size_t nr, void *buf, size_t sz, bool write, bool bulk = false)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Block::Packet_descriptor::Opcode op;
|
||||
op = write ? Block::Packet_descriptor::WRITE : Block::Packet_descriptor::READ;
|
||||
|
||||
size_t packet_size = bulk ? sz : _block_size;
|
||||
size_t packet_count = bulk ? (sz / _block_size) : 1;
|
||||
|
||||
/* sanity check */
|
||||
if (packet_count > _block_buffer_count) {
|
||||
packet_size = _block_buffer_count * _block_size;
|
||||
packet_count = _block_buffer_count;
|
||||
}
|
||||
|
||||
Block::Packet_descriptor p(_tx_source->alloc_packet(packet_size), 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()) {
|
||||
PERR("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;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Block_file_system(Xml_node config)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_BLOCK_DEVICE, name(), config),
|
||||
_label(config),
|
||||
_block_buffer(0),
|
||||
_block_buffer_count(1),
|
||||
_tx_block_alloc(env()->heap()),
|
||||
_block(&_tx_block_alloc, 128*1024, _label.string),
|
||||
_tx_source(_block.tx()),
|
||||
_readable(false),
|
||||
_writeable(false)
|
||||
{
|
||||
try { config.attribute("block_buffer_count").value(&_block_buffer_count); }
|
||||
catch (...) { }
|
||||
|
||||
_block.info(&_block_count, &_block_size, &_block_ops);
|
||||
|
||||
_readable = _block_ops.supported(Block::Packet_descriptor::READ);
|
||||
_writeable = _block_ops.supported(Block::Packet_descriptor::WRITE);
|
||||
|
||||
_block_buffer = new (env()->heap()) char[_block_buffer_count * _block_size];
|
||||
}
|
||||
|
||||
~Block_file_system()
|
||||
{
|
||||
destroy(env()->heap(), _block_buffer);
|
||||
}
|
||||
|
||||
static char const *name() { return "block"; }
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory service interface **
|
||||
*********************************/
|
||||
|
||||
Stat_result stat(char const *path, Stat &out) override
|
||||
{
|
||||
Stat_result const result = Single_file_system::stat(path, out);
|
||||
out.size = _block_size*_block_count;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs_handle *vfs_handle, char const *buf, size_t count,
|
||||
size_t &out_count) override
|
||||
{
|
||||
if (!_writeable) {
|
||||
PERR("block device is not writeable");
|
||||
return WRITE_ERR_INVALID;
|
||||
}
|
||||
|
||||
size_t seek_offset = vfs_handle->seek();
|
||||
|
||||
size_t written = 0;
|
||||
while (count > 0) {
|
||||
size_t displ = 0;
|
||||
size_t length = 0;
|
||||
size_t nbytes = 0;
|
||||
size_t 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)) {
|
||||
size_t bytes_left = count - (count % _block_size);
|
||||
|
||||
nbytes = _block_io(blk_nr, (void*)(buf + written),
|
||||
bytes_left, true, true);
|
||||
if (nbytes == 0) {
|
||||
PERR("error while write block:%zu from block device",
|
||||
blk_nr);
|
||||
return WRITE_ERR_INVALID;
|
||||
}
|
||||
|
||||
written += nbytes;
|
||||
count -= 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) {
|
||||
PWRN("offset:%zd block_size:%zd displacement:%zd length:%zu",
|
||||
seek_offset, _block_size, displ, length);
|
||||
|
||||
_block_io(blk_nr, _block_buffer, _block_size, false);
|
||||
/* rewind seek offset to account for the block read */
|
||||
seek_offset -= _block_size;
|
||||
}
|
||||
|
||||
Genode::memcpy(_block_buffer + displ, buf + written, length);
|
||||
|
||||
nbytes = _block_io(blk_nr, _block_buffer, _block_size, true);
|
||||
if ((unsigned)nbytes != _block_size) {
|
||||
PERR("error while writing block:%zu from Block_device",
|
||||
blk_nr);
|
||||
return WRITE_ERR_INVALID;
|
||||
}
|
||||
|
||||
written += length;
|
||||
count -= length;
|
||||
}
|
||||
|
||||
out_count = written;
|
||||
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
Read_result read(Vfs_handle *vfs_handle, char *dst, size_t count,
|
||||
size_t &out_count) override
|
||||
{
|
||||
if (!_readable) {
|
||||
PERR("block device is not readable");
|
||||
return READ_ERR_INVALID;
|
||||
}
|
||||
|
||||
size_t seek_offset = vfs_handle->seek();
|
||||
|
||||
size_t read = 0;
|
||||
while (count > 0) {
|
||||
size_t displ = 0;
|
||||
size_t length = 0;
|
||||
size_t nbytes = 0;
|
||||
size_t 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 t he
|
||||
* 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)) {
|
||||
size_t bytes_left = count - (count % _block_size);
|
||||
|
||||
nbytes = _block_io(blk_nr, dst + read, bytes_left, false, true);
|
||||
if (nbytes == 0) {
|
||||
PERR("error while reading block:%zu from block device",
|
||||
blk_nr);
|
||||
return READ_ERR_INVALID;
|
||||
}
|
||||
|
||||
read += nbytes;
|
||||
count -= nbytes;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (displ > 0)
|
||||
PWRN("offset:%zd is not aligned to block_size:%zu"
|
||||
" displacement:%zu", seek_offset, _block_size,
|
||||
displ);
|
||||
|
||||
nbytes = _block_io(blk_nr, _block_buffer, _block_size, false);
|
||||
if ((unsigned)nbytes != _block_size) {
|
||||
PERR("error while reading block:%zu from block device",
|
||||
blk_nr);
|
||||
return READ_ERR_INVALID;
|
||||
}
|
||||
|
||||
Genode::memcpy(dst + read, _block_buffer + displ, length);
|
||||
|
||||
read += length;
|
||||
count -= length;
|
||||
}
|
||||
|
||||
out_count = read;
|
||||
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle *vfs_handle, size_t) override
|
||||
{
|
||||
return FTRUNCATE_OK;
|
||||
}
|
||||
|
||||
Ioctl_result ioctl(Vfs_handle *vfs_handle, Ioctl_opcode opcode, Ioctl_arg,
|
||||
Ioctl_out &out) override
|
||||
{
|
||||
switch (opcode) {
|
||||
case IOCTL_OP_DIOCGMEDIASIZE:
|
||||
|
||||
out.diocgmediasize.size = _block_count * _block_size;
|
||||
return IOCTL_OK;
|
||||
|
||||
default:
|
||||
|
||||
PDBG("invalid ioctl request %d", opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
/* never reached */
|
||||
return IOCTL_ERR_INVALID;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__BLOCK_FILE_SYSTEM_H_ */
|
540
os/include/vfs/dir_file_system.h
Normal file
540
os/include/vfs/dir_file_system.h
Normal file
@ -0,0 +1,540 @@
|
||||
/*
|
||||
* \brief Directory file system
|
||||
* \author Norman Feske
|
||||
* \date 2012-04-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__DIR_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__DIR_FILE_SYSTEM_H_
|
||||
|
||||
#include <vfs/file_system_factory.h>
|
||||
#include <vfs/vfs_handle.h>
|
||||
|
||||
|
||||
namespace Vfs { class Dir_file_system; }
|
||||
|
||||
|
||||
class Vfs::Dir_file_system : public File_system
|
||||
{
|
||||
public:
|
||||
|
||||
enum { MAX_NAME_LEN = 128 };
|
||||
|
||||
private:
|
||||
|
||||
/* pointer to first child file system */
|
||||
File_system *_first_file_system;
|
||||
|
||||
/* add new file system to the list of children */
|
||||
void _append_file_system(File_system *fs)
|
||||
{
|
||||
if (!_first_file_system) {
|
||||
_first_file_system = fs;
|
||||
return;
|
||||
}
|
||||
|
||||
File_system *curr = _first_file_system;
|
||||
while (curr->next)
|
||||
curr = curr->next;
|
||||
|
||||
curr->next = fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory name
|
||||
*/
|
||||
char _name[MAX_NAME_LEN];
|
||||
|
||||
bool _is_root() const { return _name[0] == 0; }
|
||||
|
||||
/**
|
||||
* Perform operation on a file system
|
||||
*
|
||||
* \param fn functor that takes a file-system reference and
|
||||
* the path as arguments
|
||||
*/
|
||||
template <typename RES, typename FN>
|
||||
RES _dir_op(RES const no_entry, RES const no_perm, RES const ok,
|
||||
char const *path, FN const &fn)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!path)
|
||||
return no_entry;
|
||||
|
||||
/*
|
||||
* Prevent operation if path equals directory name defined
|
||||
* via the static VFS configuration.
|
||||
*/
|
||||
if (strlen(path) == 0)
|
||||
return no_perm;
|
||||
|
||||
/*
|
||||
* If any of the sub file systems returns a permission error and
|
||||
* there exists no sub file system that takes the request, we
|
||||
* return the permission error.
|
||||
*/
|
||||
bool permission_denied = false;
|
||||
|
||||
/*
|
||||
* The given path refers to at least one of our sub directories.
|
||||
* Propagate the request into all of our file systems. If at least
|
||||
* one operation succeeds, we return success.
|
||||
*/
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
|
||||
RES const err = fn(*fs, path);
|
||||
|
||||
if (err == ok)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Keep the most meaningful error code. When using stacked file
|
||||
* systems, most child file systems will eventually return no
|
||||
* entry (or leave the error code unchanged). If any of those
|
||||
* file systems has anything more interesting to tell (in
|
||||
* particular no perm), return this information.
|
||||
*/
|
||||
if (err != no_entry && err != no_perm)
|
||||
return err;
|
||||
|
||||
if (err == no_perm)
|
||||
permission_denied = true;
|
||||
}
|
||||
|
||||
/* none of our file systems could successfully operate on the path */
|
||||
return permission_denied ? no_perm : no_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return portion of the path after the element corresponding to
|
||||
* the current directory.
|
||||
*/
|
||||
char const *_sub_path(char const *path) const
|
||||
{
|
||||
/* do not strip anything from the path when we are root */
|
||||
if (_is_root())
|
||||
return path;
|
||||
|
||||
/* skip heading slash in path if present */
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
|
||||
size_t const name_len = strlen(_name);
|
||||
if (strcmp(path, _name, name_len) != 0)
|
||||
return 0;
|
||||
path += name_len;
|
||||
|
||||
/*
|
||||
* The first characters of the first path element are equal to
|
||||
* the current directory name. Let's check if the length of the
|
||||
* first path element matches the name length.
|
||||
*/
|
||||
if (*path != 0 && *path != '/')
|
||||
return 0;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The 'path' is relative to the child file systems.
|
||||
*/
|
||||
Dirent_result _dirent_of_file_systems(char const *path, off_t 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;
|
||||
Dirent_result const err = fs->dirent(path, index, out);
|
||||
out.fileno += base;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 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(off_t 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.
|
||||
*/
|
||||
size_t _sum_dirents_of_file_systems(char const *path)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
cnt += fs->num_dirent(path);
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Dir_file_system(Xml_node node, File_system_factory &fs_factory)
|
||||
:
|
||||
_first_file_system(0)
|
||||
{
|
||||
/* remember directory name */
|
||||
if (node.has_type("fstab") || node.has_type("vfs"))
|
||||
_name[0] = 0;
|
||||
else
|
||||
node.attribute("name").value(_name, sizeof(_name));
|
||||
|
||||
for (unsigned i = 0; i < node.num_sub_nodes(); i++) {
|
||||
|
||||
Xml_node sub_node = node.sub_node(i);
|
||||
|
||||
/* traverse into <dir> nodes */
|
||||
if (sub_node.has_type("dir")) {
|
||||
_append_file_system(new (env()->heap())
|
||||
Dir_file_system(sub_node, fs_factory));
|
||||
continue;
|
||||
}
|
||||
|
||||
File_system *fs = fs_factory.create(sub_node);
|
||||
if (fs) {
|
||||
_append_file_system(fs);
|
||||
continue;
|
||||
}
|
||||
|
||||
char type_name[64];
|
||||
sub_node.type_name(type_name, sizeof(type_name));
|
||||
PWRN("unknown fstab node type <%s>", type_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path) override
|
||||
{
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return Dataspace_capability();
|
||||
|
||||
/*
|
||||
* Query sub file systems for dataspace using the path local to
|
||||
* the respective file system
|
||||
*/
|
||||
File_system *fs = _first_file_system;
|
||||
for (; fs; fs = fs->next) {
|
||||
Dataspace_capability ds = fs->dataspace(path);
|
||||
if (ds.valid())
|
||||
return ds;
|
||||
}
|
||||
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap) override
|
||||
{
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next)
|
||||
fs->release(path, ds_cap);
|
||||
}
|
||||
|
||||
Stat_result stat(char const *path, Stat &out) override
|
||||
{
|
||||
path = _sub_path(path);
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!path)
|
||||
return STAT_ERR_NO_ENTRY;
|
||||
|
||||
/*
|
||||
* If path equals directory name, return information about the
|
||||
* current directory.
|
||||
*/
|
||||
if (strlen(path) == 0 || (strcmp(path, "/") == 0)) {
|
||||
out.size = 0;
|
||||
out.mode = STAT_MODE_DIRECTORY | 0755;
|
||||
out.uid = 0;
|
||||
out.gid = 0;
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* The given path refers to one of our sub directories.
|
||||
* Propagate the request into our file systems.
|
||||
*/
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
|
||||
Stat_result const err = fs->stat(path, out);
|
||||
|
||||
if (err == STAT_OK)
|
||||
return err;
|
||||
|
||||
if (err != STAT_ERR_NO_ENTRY)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* none of our file systems felt responsible for the path */
|
||||
return STAT_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
Dirent_result dirent(char const *path, off_t index, Dirent &out) override
|
||||
{
|
||||
if (_is_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, index, out);
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path) override
|
||||
{
|
||||
if (_is_root()) {
|
||||
return _sum_dirents_of_file_systems(path);
|
||||
|
||||
} else {
|
||||
|
||||
if (strcmp(path, "/") == 0)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* The path contains at least one element. Remove current
|
||||
* element from path.
|
||||
*/
|
||||
path = _sub_path(path);
|
||||
|
||||
/*
|
||||
* If the resulting 'path' is non-NULL, the path lies
|
||||
* within our tree. In this case, determine the sum of
|
||||
* matching dirents of all our file systems. Otherwise,
|
||||
* the specified path lies outside our directory node.
|
||||
*/
|
||||
return path ? _sum_dirents_of_file_systems(path) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_directory(char const *path) override
|
||||
{
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
if (strlen(path) == 0)
|
||||
return true;
|
||||
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next)
|
||||
if (fs->is_directory(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path) override
|
||||
{
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return 0;
|
||||
|
||||
if (strlen(path) == 0)
|
||||
return path;
|
||||
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
char const *leaf_path = fs->leaf_path(path);
|
||||
if (leaf_path)
|
||||
return leaf_path;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Open_result open(char const *path, unsigned mode, Vfs_handle **out_handle) override
|
||||
{
|
||||
/*
|
||||
* If 'path' is a directory, we create a 'Vfs_handle'
|
||||
* for the root directory so that subsequent 'dirent' calls
|
||||
* are subjected to the stacked file-system layout.
|
||||
*/
|
||||
if (is_directory(path)) {
|
||||
*out_handle = new (env()->heap()) Vfs_handle(*this, *this, 0);
|
||||
return OPEN_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* If 'path' refers to a non-directory node, create a
|
||||
* 'Vfs_handle' local to the file system that provides the
|
||||
* file.
|
||||
*/
|
||||
|
||||
path = _sub_path(path);
|
||||
|
||||
/* check if path does not match directory name */
|
||||
if (!path)
|
||||
return OPEN_ERR_UNACCESSIBLE;
|
||||
|
||||
/* path equals directory name */
|
||||
if (strlen(path) == 0) {
|
||||
*out_handle = new (env()->heap()) Vfs_handle(*this, *this, 0);
|
||||
return OPEN_OK;
|
||||
}
|
||||
|
||||
/* path refers to any of our sub file systems */
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
|
||||
Open_result const err = fs->open(path, mode, out_handle);
|
||||
|
||||
if (err == OPEN_OK)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* path does not match any existing file or directory */
|
||||
return OPEN_ERR_UNACCESSIBLE;
|
||||
}
|
||||
|
||||
Unlink_result unlink(char const *path) override
|
||||
{
|
||||
auto unlink_fn = [] (File_system &fs, char const *path)
|
||||
{
|
||||
return fs.unlink(path);
|
||||
};
|
||||
|
||||
return _dir_op(UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM, UNLINK_OK,
|
||||
path, unlink_fn);
|
||||
}
|
||||
|
||||
Readlink_result readlink(char const *path, char *buf, size_t buf_size,
|
||||
size_t &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
|
||||
{
|
||||
/*
|
||||
* Check if destination path resides within the same file
|
||||
* system instance as the source path.
|
||||
*/
|
||||
to_path = _sub_path(to_path);
|
||||
if (!to_path)
|
||||
return RENAME_ERR_CROSS_FS;
|
||||
|
||||
auto rename_fn = [&] (File_system &fs, char const *from_path)
|
||||
{
|
||||
return fs.rename(from_path, to_path);
|
||||
};
|
||||
|
||||
return _dir_op(RENAME_ERR_NO_ENTRY, RENAME_ERR_NO_PERM, RENAME_OK,
|
||||
from_path, rename_fn);
|
||||
}
|
||||
|
||||
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 **
|
||||
***************************/
|
||||
|
||||
char const *name() const { return "dir"; }
|
||||
|
||||
/**
|
||||
* Synchronize all file systems
|
||||
*/
|
||||
void sync() override
|
||||
{
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next)
|
||||
fs->sync();
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs_handle *handle, char const *, size_t, size_t &) override
|
||||
{
|
||||
return WRITE_ERR_INVALID;
|
||||
}
|
||||
|
||||
Read_result read(Vfs_handle *, char *, size_t, size_t &) override
|
||||
{
|
||||
return READ_ERR_INVALID;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle *, size_t) override
|
||||
{
|
||||
return FTRUNCATE_ERR_NO_PERM;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__DIR_FILE_SYSTEM_H_ */
|
179
os/include/vfs/directory_service.h
Normal file
179
os/include/vfs/directory_service.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* \brief Directory-service interface
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__DIRECTORY_SERVICE_H_
|
||||
#define _INCLUDE__VFS__DIRECTORY_SERVICE_H_
|
||||
|
||||
#include <vfs/types.h>
|
||||
|
||||
namespace Vfs {
|
||||
class Vfs_handle;
|
||||
struct Directory_service;
|
||||
}
|
||||
|
||||
|
||||
struct Vfs::Directory_service
|
||||
{
|
||||
virtual Dataspace_capability dataspace(char const *path) = 0;
|
||||
virtual void release(char const *path, Dataspace_capability) = 0;
|
||||
|
||||
|
||||
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
|
||||
|
||||
|
||||
/**********
|
||||
** Open **
|
||||
**********/
|
||||
|
||||
/**
|
||||
* Flags of 'mode' argument of open syscall
|
||||
*/
|
||||
enum {
|
||||
OPEN_MODE_RDONLY = 0,
|
||||
OPEN_MODE_WRONLY = 1,
|
||||
OPEN_MODE_RDWR = 2,
|
||||
OPEN_MODE_ACCMODE = 3,
|
||||
OPEN_MODE_CREATE = 0x0800, /* libc O_EXCL */
|
||||
};
|
||||
|
||||
enum Open_result
|
||||
{
|
||||
OPEN_ERR_UNACCESSIBLE,
|
||||
OPEN_ERR_NO_PERM,
|
||||
OPEN_ERR_EXISTS,
|
||||
OPEN_OK
|
||||
};
|
||||
|
||||
virtual Open_result open(char const *path, unsigned mode, Vfs_handle **) = 0;
|
||||
|
||||
|
||||
/**********
|
||||
** Stat **
|
||||
**********/
|
||||
|
||||
/**
|
||||
* These values are the same as in the FreeBSD libc
|
||||
*/
|
||||
enum {
|
||||
STAT_MODE_SYMLINK = 0120000,
|
||||
STAT_MODE_FILE = 0100000,
|
||||
STAT_MODE_DIRECTORY = 0040000,
|
||||
STAT_MODE_CHARDEV = 0020000,
|
||||
STAT_MODE_BLOCKDEV = 0060000,
|
||||
};
|
||||
|
||||
struct Stat
|
||||
{
|
||||
size_t size;
|
||||
unsigned mode;
|
||||
unsigned uid;
|
||||
unsigned gid;
|
||||
unsigned long inode;
|
||||
unsigned device;
|
||||
};
|
||||
|
||||
enum Stat_result { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS, STAT_OK };
|
||||
|
||||
virtual Stat_result stat(char const *path, Stat &) = 0;
|
||||
|
||||
|
||||
/************
|
||||
** Dirent **
|
||||
************/
|
||||
|
||||
enum Dirent_result { DIRENT_ERR_INVALID_PATH, DIRENT_OK };
|
||||
|
||||
enum { DIRENT_MAX_NAME_LEN = 128 };
|
||||
|
||||
enum Dirent_type {
|
||||
DIRENT_TYPE_FILE,
|
||||
DIRENT_TYPE_DIRECTORY,
|
||||
DIRENT_TYPE_FIFO,
|
||||
DIRENT_TYPE_CHARDEV,
|
||||
DIRENT_TYPE_BLOCKDEV,
|
||||
DIRENT_TYPE_SYMLINK,
|
||||
DIRENT_TYPE_END
|
||||
};
|
||||
|
||||
struct Dirent
|
||||
{
|
||||
int fileno;
|
||||
Dirent_type type;
|
||||
char name[DIRENT_MAX_NAME_LEN];
|
||||
};
|
||||
|
||||
virtual Dirent_result dirent(char const *path, off_t index, Dirent &) = 0;
|
||||
|
||||
|
||||
/************
|
||||
** Unlink **
|
||||
************/
|
||||
|
||||
enum Unlink_result { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM, UNLINK_OK };
|
||||
|
||||
virtual Unlink_result unlink(char const *path) = 0;
|
||||
|
||||
|
||||
/**************
|
||||
** Readlink **
|
||||
**************/
|
||||
|
||||
enum Readlink_result { READLINK_ERR_NO_ENTRY, READLINK_OK };
|
||||
|
||||
virtual Readlink_result readlink(char const *path, char *buf, size_t buf_size,
|
||||
size_t &out_len) = 0;
|
||||
|
||||
|
||||
/************
|
||||
** Rename **
|
||||
************/
|
||||
|
||||
enum Rename_result { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS,
|
||||
RENAME_ERR_NO_PERM, RENAME_OK };
|
||||
|
||||
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_NAME_TOO_LONG, SYMLINK_ERR_NO_PERM,
|
||||
SYMLINK_OK };
|
||||
|
||||
virtual Symlink_result symlink(char const *from, char const *to) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Return number of directory entries located at given path
|
||||
*/
|
||||
virtual size_t num_dirent(char const *path) = 0;
|
||||
|
||||
virtual bool is_directory(char const *path) = 0;
|
||||
|
||||
virtual char const *leaf_path(char const *path) = 0;
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__DIRECTORY_SERVICE_H_ */
|
123
os/include/vfs/file_io_service.h
Normal file
123
os/include/vfs/file_io_service.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* \brief Interface for operations provided by file I/O service
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__FILE_IO_SERVICE_H_
|
||||
#define _INCLUDE__VFS__FILE_IO_SERVICE_H_
|
||||
|
||||
namespace Vfs {
|
||||
class Vfs_handle;
|
||||
struct File_io_service;
|
||||
}
|
||||
|
||||
|
||||
struct Vfs::File_io_service
|
||||
{
|
||||
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
|
||||
|
||||
|
||||
/***********
|
||||
** Write **
|
||||
***********/
|
||||
|
||||
enum Write_result { WRITE_ERR_AGAIN, WRITE_ERR_WOULD_BLOCK,
|
||||
WRITE_ERR_INVALID, WRITE_ERR_IO,
|
||||
WRITE_ERR_INTERRUPT, WRITE_OK };
|
||||
|
||||
virtual Write_result write(Vfs_handle *vfs_handle,
|
||||
char const *buf, size_t buf_size,
|
||||
size_t &out_count) = 0;
|
||||
|
||||
|
||||
/**********
|
||||
** Read **
|
||||
**********/
|
||||
|
||||
enum Read_result { READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK,
|
||||
READ_ERR_INVALID, READ_ERR_IO,
|
||||
READ_ERR_INTERRUPT, READ_OK };
|
||||
|
||||
virtual Read_result read(Vfs_handle *vfs_handle, char *dst, size_t count,
|
||||
size_t &out_count) = 0;
|
||||
|
||||
|
||||
/***************
|
||||
** Ftruncate **
|
||||
***************/
|
||||
|
||||
enum Ftruncate_result { FTRUNCATE_ERR_NO_PERM = NUM_GENERAL_ERRORS,
|
||||
FTRUNCATE_ERR_INTERRUPT, FTRUNCATE_OK };
|
||||
|
||||
virtual Ftruncate_result ftruncate(Vfs_handle *vfs_handle, size_t len) = 0;
|
||||
|
||||
|
||||
/***********
|
||||
** Ioctl **
|
||||
***********/
|
||||
|
||||
enum Ioctl_result { IOCTL_ERR_INVALID, IOCTL_ERR_NOTTY, IOCTL_OK };
|
||||
|
||||
enum Ioctl_opcode { IOCTL_OP_UNDEFINED, IOCTL_OP_TIOCGWINSZ,
|
||||
IOCTL_OP_TIOCSETAF, IOCTL_OP_TIOCSETAW,
|
||||
IOCTL_OP_FIONBIO, IOCTL_OP_DIOCGMEDIASIZE };
|
||||
|
||||
enum Ioctl_value { IOCTL_VAL_NULL, IOCTL_VAL_ECHO, IOCTL_VAL_ECHONL };
|
||||
|
||||
typedef unsigned long Ioctl_arg;
|
||||
|
||||
struct Ioctl_out
|
||||
{
|
||||
union
|
||||
{
|
||||
/* if request was 'IOCTL_OP_TIOCGSIZE' */
|
||||
struct {
|
||||
int rows;
|
||||
int columns;
|
||||
} tiocgwinsz;
|
||||
|
||||
/* if request was 'IOCTL_OP_DIOCGMEDIASIZE' */
|
||||
struct {
|
||||
/* disk size rounded up to sector size in bytes*/
|
||||
int size;
|
||||
|
||||
} diocgmediasize;
|
||||
};
|
||||
};
|
||||
|
||||
virtual Ioctl_result ioctl(Vfs_handle *vfs_handle, Ioctl_opcode, Ioctl_arg,
|
||||
Ioctl_out &out)
|
||||
{
|
||||
/*
|
||||
* This method is only needed in file systems which actually implement a
|
||||
* device and is therefore false by default.
|
||||
*/
|
||||
return IOCTL_ERR_INVALID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if an unblocking condition of the file is satisfied
|
||||
*
|
||||
* \param rd if true, check for data available for reading
|
||||
* \param wr if true, check for readiness for writing
|
||||
* \param ex if true, check for exceptions
|
||||
*/
|
||||
virtual bool check_unblock(Vfs_handle *vfs_handle,
|
||||
bool rd, bool wr, bool ex)
|
||||
{ return true; }
|
||||
|
||||
virtual void register_read_ready_sigh(Vfs_handle *vfs_handle,
|
||||
Signal_context_capability sigh)
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__FILE_IO_SERVICE_H_ */
|
42
os/include/vfs/file_system.h
Normal file
42
os/include/vfs/file_system.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* \brief VFS file-system back-end interface
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__FILE_SYSTEM_H_
|
||||
|
||||
#include <vfs/directory_service.h>
|
||||
#include <vfs/file_io_service.h>
|
||||
|
||||
namespace Vfs { struct File_system; }
|
||||
|
||||
|
||||
struct Vfs::File_system : Directory_service, File_io_service
|
||||
{
|
||||
/**
|
||||
* Our next sibling within the same 'Dir_file_system'
|
||||
*/
|
||||
struct File_system *next;
|
||||
|
||||
File_system() : next(0) { }
|
||||
|
||||
/**
|
||||
* Synchronize file system
|
||||
*
|
||||
* This function is only used by a Fs_file_system because such a file
|
||||
* system may employ a backend, which maintains a internal cache, that
|
||||
* needs to be flushed.
|
||||
*/
|
||||
virtual void sync() { }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__FILE_SYSTEM_H_ */
|
27
os/include/vfs/file_system_factory.h
Normal file
27
os/include/vfs/file_system_factory.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* \brief Interface for creating file-system instances
|
||||
* \author Norman Feske
|
||||
* \date 2014-04-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__FILE_SYSTEM_FACTORY_H_
|
||||
#define _INCLUDE__VFS__FILE_SYSTEM_FACTORY_H_
|
||||
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
namespace Vfs { class File_system_factory; }
|
||||
|
||||
|
||||
struct Vfs::File_system_factory
|
||||
{
|
||||
virtual File_system *create(Xml_node node) = 0;
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__FILE_SYSTEM_FACTORY_H_ */
|
628
os/include/vfs/fs_file_system.h
Normal file
628
os/include/vfs/fs_file_system.h
Normal file
@ -0,0 +1,628 @@
|
||||
/*
|
||||
* \brief Adapter from Genode 'File_system' session to VFS
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__FS_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__FS_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/allocator_avl.h>
|
||||
#include <file_system_session/connection.h>
|
||||
|
||||
namespace Vfs { class Fs_file_system; }
|
||||
|
||||
|
||||
class Vfs::Fs_file_system : public File_system
|
||||
{
|
||||
enum { verbose = false };
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Lock used to serialize the interaction with the packet stream of the
|
||||
* file-system session.
|
||||
*
|
||||
* XXX Once, we change the VFS file-system interface to use
|
||||
* asynchronous read/write operations, we can possibly remove it.
|
||||
*/
|
||||
Lock _lock;
|
||||
|
||||
Genode::Allocator_avl _fs_packet_alloc;
|
||||
|
||||
struct Label
|
||||
{
|
||||
enum { LABEL_MAX_LEN = 64 };
|
||||
char string[LABEL_MAX_LEN];
|
||||
|
||||
Label(Xml_node config)
|
||||
{
|
||||
string[0] = 0;
|
||||
try { config.attribute("label").value(string, sizeof(string)); }
|
||||
catch (...) { }
|
||||
}
|
||||
} _label;
|
||||
|
||||
::File_system::Connection _fs;
|
||||
|
||||
class Fs_vfs_handle : public Vfs_handle
|
||||
{
|
||||
private:
|
||||
|
||||
::File_system::File_handle const _handle;
|
||||
|
||||
public:
|
||||
|
||||
Fs_vfs_handle(File_system &fs, int status_flags,
|
||||
::File_system::File_handle handle)
|
||||
: Vfs_handle(fs, fs, status_flags), _handle(handle)
|
||||
{ }
|
||||
|
||||
~Fs_vfs_handle()
|
||||
{
|
||||
Fs_file_system &fs = static_cast<Fs_file_system &>(ds());
|
||||
fs._fs.close(_handle);
|
||||
}
|
||||
|
||||
::File_system::File_handle file_handle() const { return _handle; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper for managing the lifetime of temporary open node handles
|
||||
*/
|
||||
struct Fs_handle_guard
|
||||
{
|
||||
::File_system::Session &_fs;
|
||||
::File_system::Node_handle _handle;
|
||||
|
||||
Fs_handle_guard(::File_system::Session &fs,
|
||||
::File_system::Node_handle handle)
|
||||
: _fs(fs), _handle(handle) { }
|
||||
|
||||
~Fs_handle_guard() { _fs.close(_handle); }
|
||||
};
|
||||
|
||||
size_t _read(::File_system::Node_handle node_handle, void *buf,
|
||||
size_t const count, size_t const seek_offset)
|
||||
{
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
|
||||
size_t const max_packet_size = source.bulk_buffer_size() / 2;
|
||||
size_t const clipped_count = min(max_packet_size, count);
|
||||
|
||||
::File_system::Packet_descriptor const
|
||||
packet_in(source.alloc_packet(clipped_count),
|
||||
0,
|
||||
node_handle,
|
||||
::File_system::Packet_descriptor::READ,
|
||||
clipped_count,
|
||||
seek_offset);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet_in);
|
||||
|
||||
/* obtain result packet descriptor with updated status info */
|
||||
::File_system::Packet_descriptor const
|
||||
packet_out = source.get_acked_packet();
|
||||
|
||||
size_t const read_num_bytes = min(packet_out.length(), count);
|
||||
|
||||
memcpy(buf, source.packet_content(packet_out), read_num_bytes);
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
source.release_packet(packet_out);
|
||||
|
||||
return read_num_bytes;
|
||||
}
|
||||
|
||||
size_t _write(::File_system::Node_handle node_handle,
|
||||
const char *buf, size_t count, size_t seek_offset)
|
||||
{
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
|
||||
size_t const max_packet_size = source.bulk_buffer_size() / 2;
|
||||
count = min(max_packet_size, count);
|
||||
|
||||
::File_system::Packet_descriptor
|
||||
packet(source.alloc_packet(count),
|
||||
0,
|
||||
node_handle,
|
||||
::File_system::Packet_descriptor::WRITE,
|
||||
count,
|
||||
seek_offset);
|
||||
|
||||
memcpy(source.packet_content(packet), buf, count);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
source.get_acked_packet();
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
source.release_packet(packet);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
* XXX read label from config
|
||||
*/
|
||||
Fs_file_system(Xml_node config)
|
||||
:
|
||||
_fs_packet_alloc(env()->heap()),
|
||||
_label(config),
|
||||
_fs(_fs_packet_alloc, 128*1024, _label.string)
|
||||
{ }
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Absolute_path dir_path(path);
|
||||
dir_path.strip_last_element();
|
||||
|
||||
Absolute_path file_name(path);
|
||||
file_name.keep_only_last_element();
|
||||
|
||||
Ram_dataspace_capability ds_cap;
|
||||
char *local_addr = 0;
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir = _fs.dir(dir_path.base(),
|
||||
false);
|
||||
Fs_handle_guard dir_guard(_fs, dir);
|
||||
|
||||
::File_system::File_handle file =
|
||||
_fs.file(dir, file_name.base() + 1,
|
||||
::File_system::READ_ONLY, false);
|
||||
Fs_handle_guard file_guard(_fs, file);
|
||||
|
||||
::File_system::Status status = _fs.status(file);
|
||||
|
||||
Ram_dataspace_capability ds_cap =
|
||||
env()->ram_session()->alloc(status.size);
|
||||
|
||||
local_addr = env()->rm_session()->attach(ds_cap);
|
||||
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
size_t const max_packet_size = source.bulk_buffer_size() / 2;
|
||||
|
||||
for (size_t seek_offset = 0; seek_offset < status.size;
|
||||
seek_offset += max_packet_size) {
|
||||
|
||||
size_t const count = min(max_packet_size, status.size - seek_offset);
|
||||
|
||||
::File_system::Packet_descriptor
|
||||
packet(source.alloc_packet(count),
|
||||
0,
|
||||
file,
|
||||
::File_system::Packet_descriptor::READ,
|
||||
count,
|
||||
seek_offset);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
source.get_acked_packet();
|
||||
|
||||
memcpy(local_addr + seek_offset, source.packet_content(packet), count);
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
source.release_packet(packet);
|
||||
}
|
||||
|
||||
env()->rm_session()->detach(local_addr);
|
||||
|
||||
return ds_cap;
|
||||
} catch(...) {
|
||||
env()->rm_session()->detach(local_addr);
|
||||
env()->ram_session()->free(ds_cap);
|
||||
return Dataspace_capability();
|
||||
}
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap) override
|
||||
{
|
||||
env()->ram_session()->free(static_cap_cast<Genode::Ram_dataspace>(ds_cap));
|
||||
}
|
||||
|
||||
Stat_result stat(char const *path, Stat &out) override
|
||||
{
|
||||
::File_system::Status status;
|
||||
|
||||
try {
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
Fs_handle_guard node_guard(_fs, node);
|
||||
status = _fs.status(node);
|
||||
} catch (...) {
|
||||
if (verbose)
|
||||
PDBG("stat failed for path '%s'", path);
|
||||
return STAT_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
memset(&out, 0, sizeof(out));
|
||||
|
||||
out.size = status.size;
|
||||
out.mode = STAT_MODE_FILE | 0777;
|
||||
|
||||
if (status.is_symlink())
|
||||
out.mode = STAT_MODE_SYMLINK | 0777;
|
||||
|
||||
if (status.is_directory())
|
||||
out.mode = STAT_MODE_DIRECTORY | 0777;
|
||||
|
||||
out.uid = 0;
|
||||
out.gid = 0;
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
Dirent_result dirent(char const *path, off_t index, Dirent &out) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
|
||||
if (strcmp(path, "") == 0)
|
||||
path = "/";
|
||||
|
||||
::File_system::Dir_handle dir_handle = _fs.dir(path, false);
|
||||
Fs_handle_guard dir_guard(_fs, dir_handle);
|
||||
|
||||
enum { DIRENT_SIZE = sizeof(::File_system::Directory_entry) };
|
||||
|
||||
::File_system::Packet_descriptor
|
||||
packet(source.alloc_packet(DIRENT_SIZE),
|
||||
0,
|
||||
dir_handle,
|
||||
::File_system::Packet_descriptor::READ,
|
||||
DIRENT_SIZE,
|
||||
index*DIRENT_SIZE);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
source.get_acked_packet();
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
typedef ::File_system::Directory_entry Directory_entry;
|
||||
|
||||
/* copy-out payload into destination buffer */
|
||||
Directory_entry const *entry =
|
||||
(Directory_entry *)source.packet_content(packet);
|
||||
|
||||
/*
|
||||
* The default value has no meaning because the switch below
|
||||
* assigns a value in each possible branch. But it is needed to
|
||||
* keep the compiler happy.
|
||||
*/
|
||||
Dirent_type type = DIRENT_TYPE_END;
|
||||
|
||||
switch (entry->type) {
|
||||
case Directory_entry::TYPE_DIRECTORY: type = DIRENT_TYPE_DIRECTORY; break;
|
||||
case Directory_entry::TYPE_FILE: type = DIRENT_TYPE_FILE; break;
|
||||
case Directory_entry::TYPE_SYMLINK: type = DIRENT_TYPE_SYMLINK; break;
|
||||
}
|
||||
|
||||
out.type = type;
|
||||
out.fileno = index + 1;
|
||||
|
||||
strncpy(out.name, entry->name, sizeof(out.name));
|
||||
|
||||
source.release_packet(packet);
|
||||
|
||||
return DIRENT_OK;
|
||||
}
|
||||
|
||||
Unlink_result unlink(char const *path) override
|
||||
{
|
||||
Absolute_path dir_path(path);
|
||||
dir_path.strip_last_element();
|
||||
|
||||
Absolute_path file_name(path);
|
||||
file_name.keep_only_last_element();
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
|
||||
Fs_handle_guard dir_guard(_fs, dir);
|
||||
|
||||
_fs.unlink(dir, file_name.base() + 1);
|
||||
}
|
||||
catch (...) {
|
||||
return UNLINK_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
return UNLINK_OK;
|
||||
}
|
||||
|
||||
Readlink_result readlink(char const *path, char *buf, size_t buf_size,
|
||||
size_t &out_len) override
|
||||
{
|
||||
/*
|
||||
* Canonicalize path (i.e., path must start with '/')
|
||||
*/
|
||||
Absolute_path abs_path(path);
|
||||
abs_path.strip_last_element();
|
||||
|
||||
Absolute_path symlink_name(path);
|
||||
symlink_name.keep_only_last_element();
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false);
|
||||
Fs_handle_guard from_dir_guard(_fs, dir_handle);
|
||||
|
||||
::File_system::Symlink_handle symlink_handle =
|
||||
_fs.symlink(dir_handle, symlink_name.base() + 1, false);
|
||||
Fs_handle_guard symlink_guard(_fs, symlink_handle);
|
||||
|
||||
out_len = _read(symlink_handle, buf, buf_size, 0);
|
||||
|
||||
return READLINK_OK;
|
||||
} catch (...) { }
|
||||
|
||||
return READLINK_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
Rename_result rename(char const *from_path, char const *to_path) override
|
||||
{
|
||||
Absolute_path from_dir_path(from_path);
|
||||
from_dir_path.strip_last_element();
|
||||
|
||||
Absolute_path from_file_name(from_path);
|
||||
from_file_name.keep_only_last_element();
|
||||
|
||||
Absolute_path to_dir_path(to_path);
|
||||
to_dir_path.strip_last_element();
|
||||
|
||||
Absolute_path to_file_name(to_path);
|
||||
to_file_name.keep_only_last_element();
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle from_dir = _fs.dir(from_dir_path.base(), false);
|
||||
Fs_handle_guard from_dir_guard(_fs, from_dir);
|
||||
|
||||
::File_system::Dir_handle to_dir = _fs.dir(to_dir_path.base(), false);
|
||||
Fs_handle_guard to_dir_guard(_fs, to_dir);
|
||||
|
||||
_fs.move(from_dir, from_file_name.base() + 1,
|
||||
to_dir, to_file_name.base() + 1);
|
||||
|
||||
} catch (...) {
|
||||
return RENAME_ERR_NO_ENTRY; }
|
||||
|
||||
return RENAME_OK;
|
||||
}
|
||||
|
||||
Mkdir_result mkdir(char const *path, unsigned mode) override
|
||||
{
|
||||
/*
|
||||
* Canonicalize path (i.e., path must start with '/')
|
||||
*/
|
||||
Absolute_path abs_path(path);
|
||||
|
||||
try {
|
||||
_fs.dir(abs_path.base(), true);
|
||||
return MKDIR_OK;
|
||||
}
|
||||
catch (::File_system::Permission_denied) { return MKDIR_ERR_NO_PERM; }
|
||||
catch (::File_system::Node_already_exists) { return MKDIR_ERR_EXISTS; }
|
||||
catch (::File_system::Lookup_failed) { return MKDIR_ERR_NO_ENTRY; }
|
||||
catch (::File_system::Name_too_long) { return MKDIR_ERR_NAME_TOO_LONG; }
|
||||
catch (::File_system::No_space) { return MKDIR_ERR_NO_SPACE; }
|
||||
|
||||
return MKDIR_ERR_NO_PERM;
|
||||
}
|
||||
|
||||
Symlink_result symlink(char const *from, char const *to) override
|
||||
{
|
||||
/*
|
||||
* We write to the symlink via the packet stream. Hence we need
|
||||
* to serialize with other packet-stream operations.
|
||||
*/
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
/*
|
||||
* Canonicalize path (i.e., path must start with '/')
|
||||
*/
|
||||
Absolute_path abs_path(to);
|
||||
abs_path.strip_last_element();
|
||||
|
||||
Absolute_path symlink_name(to);
|
||||
symlink_name.keep_only_last_element();
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false);
|
||||
Fs_handle_guard from_dir_guard(_fs, dir_handle);
|
||||
|
||||
::File_system::Symlink_handle symlink_handle =
|
||||
_fs.symlink(dir_handle, symlink_name.base() + 1, true);
|
||||
Fs_handle_guard symlink_guard(_fs, symlink_handle);
|
||||
|
||||
_write(symlink_handle, from, strlen(from) + 1, 0);
|
||||
return SYMLINK_OK;
|
||||
}
|
||||
catch (::File_system::Invalid_handle) { return SYMLINK_ERR_NO_ENTRY; }
|
||||
catch (::File_system::Node_already_exists) { return SYMLINK_ERR_EXISTS; }
|
||||
catch (::File_system::Invalid_name) { return SYMLINK_ERR_NAME_TOO_LONG; }
|
||||
catch (::File_system::Lookup_failed) { return SYMLINK_ERR_NO_ENTRY; }
|
||||
catch (::File_system::Permission_denied) { return SYMLINK_ERR_NO_PERM; }
|
||||
|
||||
return SYMLINK_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path) override
|
||||
{
|
||||
if (strcmp(path, "") == 0)
|
||||
path = "/";
|
||||
|
||||
/*
|
||||
* XXX handle exceptions
|
||||
*/
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
Fs_handle_guard node_guard(_fs, node);
|
||||
|
||||
::File_system::Status status = _fs.status(node);
|
||||
|
||||
return status.size / sizeof(::File_system::Directory_entry);
|
||||
}
|
||||
|
||||
bool is_directory(char const *path) override
|
||||
{
|
||||
try {
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
Fs_handle_guard node_guard(_fs, node);
|
||||
|
||||
::File_system::Status status = _fs.status(node);
|
||||
|
||||
return status.is_directory();
|
||||
}
|
||||
catch (...) { return false; }
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path) override
|
||||
{
|
||||
/* check if node at path exists within file system */
|
||||
try {
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
_fs.close(node);
|
||||
}
|
||||
catch (...) {
|
||||
return 0; }
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
Open_result open(char const *path, unsigned vfs_mode, Vfs_handle **out_handle) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Absolute_path dir_path(path);
|
||||
dir_path.strip_last_element();
|
||||
|
||||
Absolute_path file_name(path);
|
||||
file_name.keep_only_last_element();
|
||||
|
||||
::File_system::Mode mode;
|
||||
|
||||
switch (vfs_mode & OPEN_MODE_ACCMODE) {
|
||||
default: mode = ::File_system::STAT_ONLY; break;
|
||||
case OPEN_MODE_RDONLY: mode = ::File_system::READ_ONLY; break;
|
||||
case OPEN_MODE_WRONLY: mode = ::File_system::WRITE_ONLY; break;
|
||||
case OPEN_MODE_RDWR: mode = ::File_system::READ_WRITE; break;
|
||||
}
|
||||
|
||||
bool const create = vfs_mode & OPEN_MODE_CREATE;
|
||||
|
||||
if (create)
|
||||
if (verbose)
|
||||
PDBG("creation of file %s requested", file_name.base() + 1);
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
|
||||
Fs_handle_guard dir_guard(_fs, dir);
|
||||
|
||||
::File_system::File_handle file = _fs.file(dir, file_name.base() + 1,
|
||||
mode, create);
|
||||
|
||||
*out_handle = new (env()->heap()) Fs_vfs_handle(*this, vfs_mode, file);
|
||||
return OPEN_OK;
|
||||
}
|
||||
catch (::File_system::Permission_denied) { return OPEN_ERR_NO_PERM; }
|
||||
catch (::File_system::Invalid_handle) { return OPEN_ERR_NO_PERM; }
|
||||
catch (::File_system::Lookup_failed) { return OPEN_ERR_UNACCESSIBLE; }
|
||||
catch (::File_system::Node_already_exists) { return OPEN_ERR_EXISTS; }
|
||||
|
||||
return OPEN_ERR_UNACCESSIBLE;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "fs"; }
|
||||
|
||||
void sync() override
|
||||
{
|
||||
_fs.sync();
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs_handle *vfs_handle, char const *buf, size_t buf_size,
|
||||
size_t &out_count) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
|
||||
|
||||
out_count = _write(handle->file_handle(), buf, buf_size, handle->seek());
|
||||
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
Read_result read(Vfs_handle *vfs_handle, char *dst, size_t count,
|
||||
size_t &out_count) override
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
|
||||
|
||||
::File_system::Status status = _fs.status(handle->file_handle());
|
||||
size_t const file_size = status.size;
|
||||
|
||||
size_t const file_bytes_left = file_size >= handle->seek()
|
||||
? file_size - handle->seek() : 0;
|
||||
|
||||
count = min(count, file_bytes_left);
|
||||
|
||||
out_count = _read(handle->file_handle(), dst, count, handle->seek());
|
||||
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle *vfs_handle, size_t len) override
|
||||
{
|
||||
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
|
||||
|
||||
try {
|
||||
_fs.truncate(handle->file_handle(), len);
|
||||
}
|
||||
catch (::File_system::Invalid_handle) { return FTRUNCATE_ERR_NO_PERM; }
|
||||
catch (::File_system::Permission_denied) { return FTRUNCATE_ERR_NO_PERM; }
|
||||
|
||||
return FTRUNCATE_OK;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__FS_FILE_SYSTEM_H_ */
|
57
os/include/vfs/null_file_system.h
Normal file
57
os/include/vfs/null_file_system.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* \brief null filesystem
|
||||
* \author Josef Soentgen
|
||||
* \author Norman Feske
|
||||
* \date 2012-07-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__NULL_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__NULL_FILE_SYSTEM_H_
|
||||
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
namespace Vfs { class Null_file_system; }
|
||||
|
||||
|
||||
struct Vfs::Null_file_system : Single_file_system
|
||||
{
|
||||
Null_file_system(Xml_node config)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config)
|
||||
{ }
|
||||
|
||||
static char const *name() { return "null"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs_handle *handle, char const *, size_t count, size_t &out_count) override
|
||||
{
|
||||
out_count = count;
|
||||
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
Read_result read(Vfs_handle *vfs_handle, char *, size_t, size_t &out_count) override
|
||||
{
|
||||
out_count = 0;
|
||||
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle *vfs_handle, size_t) override
|
||||
{
|
||||
return FTRUNCATE_OK;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__NULL_FILE_SYSTEM_H_ */
|
174
os/include/vfs/single_file_system.h
Normal file
174
os/include/vfs/single_file_system.h
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* \brief File system that hosts a single node
|
||||
* \author Norman Feske
|
||||
* \date 2014-04-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__SINGLE_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__SINGLE_FILE_SYSTEM_H_
|
||||
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
namespace Vfs { class Single_file_system; }
|
||||
|
||||
|
||||
class Vfs::Single_file_system : public File_system
|
||||
{
|
||||
public:
|
||||
|
||||
enum Node_type { NODE_TYPE_FILE, NODE_TYPE_CHAR_DEVICE, NODE_TYPE_BLOCK_DEVICE };
|
||||
|
||||
private:
|
||||
|
||||
Node_type const _node_type;
|
||||
|
||||
enum { FILENAME_MAX_LEN = 64 };
|
||||
char _filename[FILENAME_MAX_LEN];
|
||||
|
||||
bool _is_root(const char *path)
|
||||
{
|
||||
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
||||
}
|
||||
|
||||
bool _is_single_file(const char *path)
|
||||
{
|
||||
return (strlen(path) == (strlen(_filename) + 1)) &&
|
||||
(strcmp(&path[1], _filename) == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Single_file_system(Node_type node_type, char const *type_name, Xml_node config)
|
||||
:
|
||||
_node_type(node_type)
|
||||
{
|
||||
strncpy(_filename, type_name, sizeof(_filename));
|
||||
|
||||
try { config.attribute("name").value(_filename, sizeof(_filename)); }
|
||||
catch (...) { }
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path) override
|
||||
{
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap) override { }
|
||||
|
||||
Stat_result stat(char const *path, Stat &out) override
|
||||
{
|
||||
out = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (_is_root(path)) {
|
||||
out.mode = STAT_MODE_DIRECTORY;
|
||||
|
||||
} else if (_is_single_file(path)) {
|
||||
switch (_node_type) {
|
||||
case NODE_TYPE_FILE: out.mode = STAT_MODE_FILE; break;
|
||||
case NODE_TYPE_CHAR_DEVICE: out.mode = STAT_MODE_CHARDEV; break;
|
||||
case NODE_TYPE_BLOCK_DEVICE: out.mode = STAT_MODE_BLOCKDEV; break;
|
||||
}
|
||||
} else {
|
||||
return STAT_ERR_NO_ENTRY;
|
||||
}
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
Dirent_result dirent(char const *path, off_t index, Dirent &out) override
|
||||
{
|
||||
if (!_is_root(path))
|
||||
return DIRENT_ERR_INVALID_PATH;
|
||||
|
||||
if (index == 0) {
|
||||
switch (_node_type) {
|
||||
case NODE_TYPE_FILE: out.type = DIRENT_TYPE_FILE; 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;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path) override
|
||||
{
|
||||
if (_is_root(path))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_directory(char const *path) override
|
||||
{
|
||||
if (_is_root(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path) override
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Open_result open(char const *path, unsigned, Vfs_handle **out_handle) override
|
||||
{
|
||||
if (!_is_single_file(path))
|
||||
return OPEN_ERR_UNACCESSIBLE;
|
||||
|
||||
*out_handle = new (env()->heap()) Vfs_handle(*this, *this, 0);
|
||||
return OPEN_OK;
|
||||
}
|
||||
|
||||
Unlink_result unlink(char const *) override
|
||||
{
|
||||
return UNLINK_ERR_NO_PERM;
|
||||
}
|
||||
|
||||
Readlink_result readlink(char const *, char *, size_t, size_t &) 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;
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle *vfs_handle, size_t) override
|
||||
{
|
||||
return FTRUNCATE_ERR_NO_PERM;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__SINGLE_FILE_SYSTEM_H_ */
|
638
os/include/vfs/tar_file_system.h
Normal file
638
os/include/vfs/tar_file_system.h
Normal file
@ -0,0 +1,638 @@
|
||||
/*
|
||||
* \brief TAR file system
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__TAR_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__TAR_FILE_SYSTEM_H_
|
||||
|
||||
#include <rom_session/connection.h>
|
||||
#include <vfs/file_system.h>
|
||||
#include <vfs/vfs_handle.h>
|
||||
|
||||
namespace Vfs { class Tar_file_system; }
|
||||
|
||||
|
||||
class Vfs::Tar_file_system : public File_system
|
||||
{
|
||||
enum { verbose = false };
|
||||
|
||||
struct Rom_name
|
||||
{
|
||||
enum { ROM_NAME_MAX_LEN = 64 };
|
||||
char name[ROM_NAME_MAX_LEN];
|
||||
|
||||
Rom_name(Xml_node config) {
|
||||
config.attribute("name").value(name, sizeof(name));
|
||||
}
|
||||
} _rom_name;
|
||||
|
||||
Genode::Rom_connection _rom;
|
||||
|
||||
char *_tar_base;
|
||||
size_t _tar_size;
|
||||
|
||||
class Record
|
||||
{
|
||||
private:
|
||||
|
||||
char _name[100];
|
||||
char _mode[8];
|
||||
char _uid[8];
|
||||
char _gid[8];
|
||||
char _size[12];
|
||||
char _mtime[12];
|
||||
char _checksum[8];
|
||||
char _type[1];
|
||||
char _linked_name[100];
|
||||
|
||||
/**
|
||||
* Convert ASCII-encoded octal number to unsigned value
|
||||
*/
|
||||
template <typename T>
|
||||
unsigned long _read(T const &field) const
|
||||
{
|
||||
/*
|
||||
* Copy-out ASCII string to temporary buffer that is
|
||||
* large enough to host an additional zero.
|
||||
*/
|
||||
char buf[sizeof(field) + 1];
|
||||
strncpy(buf, field, sizeof(buf));
|
||||
|
||||
unsigned long value = 0;
|
||||
ascii_to(buf, &value, 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* length of on data block in tar */
|
||||
enum { BLOCK_LEN = 512 };
|
||||
|
||||
/* record type values */
|
||||
enum { TYPE_FILE = 0, TYPE_HARDLINK = 1,
|
||||
TYPE_SYMLINK = 2, TYPE_DIR = 5 };
|
||||
|
||||
size_t size() const { return _read(_size); }
|
||||
unsigned uid() const { return _read(_uid); }
|
||||
unsigned gid() const { return _read(_gid); }
|
||||
unsigned mode() const { return _read(_mode); }
|
||||
unsigned type() const { return _read(_type); }
|
||||
char const *name() const { return _name; }
|
||||
char const *linked_name() const { return _linked_name; }
|
||||
|
||||
void *data() const { return (char *)this + BLOCK_LEN; }
|
||||
};
|
||||
|
||||
|
||||
class Tar_vfs_handle : public Vfs_handle
|
||||
{
|
||||
private:
|
||||
|
||||
Record const *_record;
|
||||
|
||||
public:
|
||||
|
||||
Tar_vfs_handle(File_system &fs, int status_flags, Record const *record)
|
||||
: Vfs_handle(fs, fs, status_flags), _record(record)
|
||||
{ }
|
||||
|
||||
Record const *record() const { return _record; }
|
||||
};
|
||||
|
||||
|
||||
struct Scanner_policy_path_element
|
||||
{
|
||||
static bool identifier_char(char c, unsigned /* i */)
|
||||
{
|
||||
return (c != '/') && (c != 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef Genode::Token<Scanner_policy_path_element> Path_element_token;
|
||||
|
||||
|
||||
struct Node : List<Node>, List<Node>::Element
|
||||
{
|
||||
char const *name;
|
||||
Record const *record;
|
||||
|
||||
Node(char const *name, Record const *record) : name(name), record(record) { }
|
||||
|
||||
Node *lookup(char const *name)
|
||||
{
|
||||
Absolute_path lookup_path(name);
|
||||
|
||||
if (verbose)
|
||||
PDBG("lookup_path = %s", lookup_path.base());
|
||||
|
||||
Node *parent_node = this;
|
||||
Node *child_node;
|
||||
|
||||
Path_element_token t(lookup_path.base());
|
||||
|
||||
while (t) {
|
||||
|
||||
if (t.type() != Path_element_token::IDENT) {
|
||||
t = t.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
char path_element[MAX_PATH_LEN];
|
||||
|
||||
t.string(path_element, sizeof(path_element));
|
||||
|
||||
if (verbose)
|
||||
PDBG("path_element = %s", path_element);
|
||||
|
||||
for (child_node = parent_node->first(); child_node; child_node = child_node->next()) {
|
||||
if (verbose)
|
||||
PDBG("comparing with node %s", child_node->name);
|
||||
if (strcmp(child_node->name, path_element) == 0) {
|
||||
if (verbose)
|
||||
PDBG("found matching child node");
|
||||
parent_node = child_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!child_node)
|
||||
return 0;
|
||||
|
||||
t = t.next();
|
||||
}
|
||||
|
||||
return parent_node;
|
||||
}
|
||||
|
||||
|
||||
Node *lookup_child(int index)
|
||||
{
|
||||
for (Node *child_node = first(); child_node; child_node = child_node->next(), index--) {
|
||||
if (index == 0)
|
||||
return child_node;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t num_dirent()
|
||||
{
|
||||
size_t count = 0;
|
||||
for (Node *child_node = first(); child_node; child_node = child_node->next(), count++) ;
|
||||
return count;
|
||||
}
|
||||
|
||||
} _root_node;
|
||||
|
||||
|
||||
/*
|
||||
* Create a Node for a tar record and insert it into the node list
|
||||
*/
|
||||
class Add_node_action
|
||||
{
|
||||
private:
|
||||
|
||||
Node &_root_node;
|
||||
|
||||
public:
|
||||
|
||||
Add_node_action(Node &root_node) : _root_node(root_node) { }
|
||||
|
||||
void operator()(Record const *record)
|
||||
{
|
||||
Absolute_path current_path(record->name());
|
||||
|
||||
if (verbose)
|
||||
PDBG("current_path = %s", current_path.base());
|
||||
|
||||
char path_element[MAX_PATH_LEN];
|
||||
|
||||
Path_element_token t(current_path.base());
|
||||
|
||||
Node *parent_node = &_root_node;
|
||||
Node *child_node;
|
||||
|
||||
while(t) {
|
||||
|
||||
if (t.type() != Path_element_token::IDENT) {
|
||||
t = t.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
Absolute_path remaining_path(t.start());
|
||||
|
||||
t.string(path_element, sizeof(path_element));
|
||||
|
||||
for (child_node = parent_node->first(); child_node; child_node = child_node->next()) {
|
||||
if (strcmp(child_node->name, path_element) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (child_node) {
|
||||
|
||||
if (verbose)
|
||||
PDBG("found node for %s", path_element);
|
||||
|
||||
if (remaining_path.has_single_element()) {
|
||||
/* Found a node for the record to be inserted.
|
||||
* This is usually a directory node without
|
||||
* record. */
|
||||
child_node->record = record;
|
||||
}
|
||||
} else {
|
||||
if (remaining_path.has_single_element()) {
|
||||
|
||||
if (verbose)
|
||||
PDBG("creating node for %s", path_element);
|
||||
|
||||
/*
|
||||
* TODO: find 'path_element' in 'record->name'
|
||||
* and use the location in the record as name
|
||||
* pointer to save some memory
|
||||
*/
|
||||
size_t name_size = strlen(path_element) + 1;
|
||||
char *name = (char*)env()->heap()->alloc(name_size);
|
||||
strncpy(name, path_element, name_size);
|
||||
child_node = new (env()->heap()) Node(name, record);
|
||||
} else {
|
||||
|
||||
if (verbose)
|
||||
PDBG("creating node without record for %s", path_element);
|
||||
|
||||
/* create a directory node without record */
|
||||
size_t name_size = strlen(path_element) + 1;
|
||||
char *name = (char*)env()->heap()->alloc(name_size);
|
||||
strncpy(name, path_element, name_size);
|
||||
child_node = new (env()->heap()) Node(name, 0);
|
||||
}
|
||||
parent_node->insert(child_node);
|
||||
}
|
||||
|
||||
parent_node = child_node;
|
||||
t = t.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Tar_record_action>
|
||||
void _for_each_tar_record_do(Tar_record_action tar_record_action)
|
||||
{
|
||||
/* measure size of archive in blocks */
|
||||
unsigned block_id = 0, block_cnt = _tar_size/Record::BLOCK_LEN;
|
||||
|
||||
/* scan metablocks of archive */
|
||||
while (block_id < block_cnt) {
|
||||
|
||||
Record *record = (Record *)(_tar_base + block_id*Record::BLOCK_LEN);
|
||||
|
||||
tar_record_action(record);
|
||||
|
||||
size_t file_size = record->size();
|
||||
|
||||
/* some datablocks */ /* one metablock */
|
||||
block_id = block_id + (file_size / Record::BLOCK_LEN) + 1;
|
||||
|
||||
/* round up */
|
||||
if (file_size % Record::BLOCK_LEN != 0) block_id++;
|
||||
|
||||
/* check for end of tar archive */
|
||||
if (block_id*Record::BLOCK_LEN >= _tar_size)
|
||||
break;
|
||||
|
||||
/* lookout for empty eof-blocks */
|
||||
if (*(_tar_base + (block_id*Record::BLOCK_LEN)) == 0x00)
|
||||
if (*(_tar_base + (block_id*Record::BLOCK_LEN + 1)) == 0x00)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Num_dirent_cache
|
||||
{
|
||||
Lock lock;
|
||||
Node &root_node;
|
||||
bool valid; /* true after first lookup */
|
||||
char key[256]; /* key used for lookup */
|
||||
size_t cached_num_dirent; /* cached value */
|
||||
|
||||
Num_dirent_cache(Node &root_node)
|
||||
: root_node(root_node), valid(false), cached_num_dirent(0) { }
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
Lock::Guard guard(lock);
|
||||
|
||||
/* check for cache miss */
|
||||
if (!valid || strcmp(path, key) != 0) {
|
||||
Node *node = root_node.lookup(path);
|
||||
if (!node)
|
||||
return 0;
|
||||
strncpy(key, path, sizeof(key));
|
||||
cached_num_dirent = node->num_dirent();
|
||||
valid = true;
|
||||
}
|
||||
return cached_num_dirent;
|
||||
}
|
||||
} _cached_num_dirent;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Tar_file_system(Xml_node config)
|
||||
:
|
||||
_rom_name(config), _rom(_rom_name.name),
|
||||
_tar_base(env()->rm_session()->attach(_rom.dataspace())),
|
||||
_tar_size(Dataspace_client(_rom.dataspace()).size()),
|
||||
_root_node("", 0),
|
||||
_cached_num_dirent(_root_node)
|
||||
{
|
||||
PINF("tar archive '%s' local at %p, size is %zd",
|
||||
_rom_name.name, _tar_base, _tar_size);
|
||||
|
||||
_for_each_tar_record_do(Add_node_action(_root_node));
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path) override
|
||||
{
|
||||
/*
|
||||
* Walk hardlinks until we reach a file
|
||||
*/
|
||||
Record const *record = 0;
|
||||
for (;;) {
|
||||
Node *node = _root_node.lookup(path);
|
||||
|
||||
if (!node)
|
||||
return Dataspace_capability();
|
||||
|
||||
record = node->record;
|
||||
|
||||
if (record) {
|
||||
if (record->type() == Record::TYPE_HARDLINK) {
|
||||
path = record->linked_name();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record->type() == Record::TYPE_FILE)
|
||||
break;
|
||||
|
||||
PERR("TAR record \"%s\" has unsupported type %d",
|
||||
record->name(), record->type());
|
||||
}
|
||||
|
||||
PERR("TAR record \"%s\" has unsupported type %d",
|
||||
path, Record::TYPE_DIR);
|
||||
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
try {
|
||||
Ram_dataspace_capability ds_cap =
|
||||
env()->ram_session()->alloc(record->size());
|
||||
|
||||
void *local_addr = env()->rm_session()->attach(ds_cap);
|
||||
memcpy(local_addr, record->data(), record->size());
|
||||
env()->rm_session()->detach(local_addr);
|
||||
|
||||
return ds_cap;
|
||||
}
|
||||
catch (...) { PDBG("Could not create new dataspace"); }
|
||||
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *, Dataspace_capability ds_cap) override
|
||||
{
|
||||
env()->ram_session()->free(static_cap_cast<Genode::Ram_dataspace>(ds_cap));
|
||||
}
|
||||
|
||||
Stat_result stat(char const *path, Stat &out) override
|
||||
{
|
||||
if (verbose)
|
||||
PDBG("path = %s", path);
|
||||
|
||||
Node const *node = 0;
|
||||
Record const *record = 0;
|
||||
|
||||
/*
|
||||
* Walk hardlinks until we reach a file
|
||||
*/
|
||||
for (;;) {
|
||||
node = _root_node.lookup(path);
|
||||
|
||||
if (!node)
|
||||
return STAT_ERR_NO_ENTRY;
|
||||
|
||||
record = node->record;
|
||||
|
||||
if (record) {
|
||||
if (record->type() == Record::TYPE_HARDLINK) {
|
||||
path = record->linked_name();
|
||||
continue;
|
||||
} else
|
||||
break;
|
||||
} else {
|
||||
if (verbose)
|
||||
PDBG("found a virtual directoty node");
|
||||
|
||||
memset(&out, 0, sizeof(out));
|
||||
out.mode = STAT_MODE_DIRECTORY;
|
||||
return STAT_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert TAR record modes to stat modes */
|
||||
unsigned mode = record->mode();
|
||||
switch (record->type()) {
|
||||
case Record::TYPE_FILE: mode |= STAT_MODE_FILE; break;
|
||||
case Record::TYPE_SYMLINK: mode |= STAT_MODE_SYMLINK; break;
|
||||
case Record::TYPE_DIR: mode |= STAT_MODE_DIRECTORY; break;
|
||||
|
||||
default:
|
||||
if (verbose)
|
||||
PDBG("unhandled record type %d", record->type());
|
||||
}
|
||||
|
||||
memset(&out, 0, sizeof(out));
|
||||
out.mode = mode;
|
||||
out.size = record->size();
|
||||
out.uid = record->uid();
|
||||
out.gid = record->gid();
|
||||
out.inode = (unsigned long)node;
|
||||
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
Dirent_result dirent(char const *path, off_t index, Dirent &out) override
|
||||
{
|
||||
Node *node = _root_node.lookup(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 = (unsigned long)node;
|
||||
|
||||
Record const *record = node->record;
|
||||
|
||||
if (record) {
|
||||
switch (record->type()) {
|
||||
case 0: out.type = DIRENT_TYPE_FILE; break;
|
||||
case 2: out.type = DIRENT_TYPE_SYMLINK; break;
|
||||
case 5: out.type = DIRENT_TYPE_DIRECTORY; break;
|
||||
|
||||
default:
|
||||
if (verbose)
|
||||
PDBG("unhandled record type %d", record->type());
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(out.name, node->name, sizeof(out.name));
|
||||
|
||||
return DIRENT_OK;
|
||||
}
|
||||
|
||||
Unlink_result unlink(char const *) override { return UNLINK_ERR_NO_PERM; }
|
||||
|
||||
Readlink_result readlink(char const *path, char *buf, size_t buf_size,
|
||||
size_t &out_len) override
|
||||
{
|
||||
Node *node = _root_node.lookup(path);
|
||||
Record const *record = node ? node->record : 0;
|
||||
|
||||
if (!record || (record->type() != Record::TYPE_SYMLINK))
|
||||
return READLINK_ERR_NO_ENTRY;
|
||||
|
||||
size_t const count = min(buf_size, (size_t)100);
|
||||
|
||||
memcpy(buf, record->linked_name(), count);
|
||||
|
||||
out_len = count;
|
||||
|
||||
return READLINK_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path) override
|
||||
{
|
||||
return _cached_num_dirent.num_dirent(path);
|
||||
}
|
||||
|
||||
bool is_directory(char const *path) override
|
||||
{
|
||||
Node *node = _root_node.lookup(path);
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
Record const *record = node->record;
|
||||
|
||||
return record ? (record->type() == Record::TYPE_DIR) : true;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path) override
|
||||
{
|
||||
/*
|
||||
* Check if path exists within the file system. If this is the
|
||||
* case, return the whole path, which is relative to the root
|
||||
* of this file system.
|
||||
*/
|
||||
Node *node = _root_node.lookup(path);
|
||||
return node ? path : 0;
|
||||
}
|
||||
|
||||
Open_result open(char const *path, unsigned, Vfs_handle **out_handle) override
|
||||
{
|
||||
Node *node = _root_node.lookup(path);
|
||||
|
||||
if (!node)
|
||||
return OPEN_ERR_UNACCESSIBLE;
|
||||
|
||||
*out_handle = new (env()->heap())
|
||||
Tar_vfs_handle(*this, 0, node->record);
|
||||
|
||||
return OPEN_OK;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "tar"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs_handle *, char const *, size_t, size_t &) override
|
||||
{
|
||||
PDBG("called\n");
|
||||
return WRITE_ERR_INVALID;
|
||||
}
|
||||
|
||||
Read_result read(Vfs_handle *vfs_handle, char *dst, size_t count,
|
||||
size_t &out_count) override
|
||||
{
|
||||
Tar_vfs_handle const *handle = static_cast<Tar_vfs_handle *>(vfs_handle);
|
||||
|
||||
size_t const record_size = handle->record()->size();
|
||||
|
||||
size_t const record_bytes_left = record_size >= handle->seek()
|
||||
? record_size - handle->seek() : 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle *handle, size_t) override
|
||||
{
|
||||
PDBG("called\n");
|
||||
return FTRUNCATE_ERR_NO_PERM;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__TAR_FILE_SYSTEM_H_ */
|
105
os/include/vfs/terminal_file_system.h
Normal file
105
os/include/vfs/terminal_file_system.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* \brief Terminal file system
|
||||
* \author Christian Prochaska
|
||||
* \author Norman Feske
|
||||
* \date 2012-05-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__TERMINAL_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__TERMINAL_FILE_SYSTEM_H_
|
||||
|
||||
#include <terminal_session/connection.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
namespace Vfs { class Terminal_file_system; }
|
||||
|
||||
|
||||
class Vfs::Terminal_file_system : public Single_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
struct Label
|
||||
{
|
||||
enum { LABEL_MAX_LEN = 64 };
|
||||
char string[LABEL_MAX_LEN];
|
||||
|
||||
Label(Xml_node config)
|
||||
{
|
||||
string[0] = 0;
|
||||
try { config.attribute("label").value(string, sizeof(string)); }
|
||||
catch (...) { }
|
||||
}
|
||||
} _label;
|
||||
|
||||
Terminal::Connection _terminal;
|
||||
|
||||
public:
|
||||
|
||||
Terminal_file_system(Xml_node config)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config),
|
||||
_label(config),
|
||||
_terminal(_label.string)
|
||||
{
|
||||
/*
|
||||
* Wait for connection-established signal
|
||||
*/
|
||||
|
||||
/* create signal receiver, just for the single signal */
|
||||
Genode::Signal_context sig_ctx;
|
||||
Genode::Signal_receiver sig_rec;
|
||||
Signal_context_capability sig_cap = sig_rec.manage(&sig_ctx);
|
||||
|
||||
/* register signal handler */
|
||||
_terminal.connected_sigh(sig_cap);
|
||||
|
||||
/* wati for signal */
|
||||
sig_rec.wait_for_signal();
|
||||
sig_rec.dissolve(&sig_ctx);
|
||||
}
|
||||
|
||||
static const char *name() { return "terminal"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs_handle *, char const *buf, size_t buf_size, size_t &out_count) override
|
||||
{
|
||||
out_count = _terminal.write(buf, buf_size);
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
Read_result read(Vfs_handle *, char *dst, size_t count, size_t &out_count) override
|
||||
{
|
||||
out_count = _terminal.read(dst, count);
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
bool check_unblock(Vfs_handle *vfs_handle, bool rd, bool wr, bool ex) override
|
||||
{
|
||||
if (rd && (_terminal.avail() > 0))
|
||||
return true;
|
||||
|
||||
if (wr)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void register_read_ready_sigh(Vfs_handle *vfs_handle,
|
||||
Signal_context_capability sigh) override
|
||||
{
|
||||
_terminal.read_avail_sigh(sigh);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__TERMINAL_FILE_SYSTEM_H_ */
|
54
os/include/vfs/types.h
Normal file
54
os/include/vfs/types.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* \brief Types used by VFS
|
||||
* \author Norman Feske
|
||||
* \date 2014-04-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__TYPES_H_
|
||||
#define _INCLUDE__VFS__TYPES_H_
|
||||
|
||||
#include <util/list.h>
|
||||
#include <util/misc_math.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <util/string.h>
|
||||
#include <base/lock.h>
|
||||
#include <base/env.h>
|
||||
#include <base/signal.h>
|
||||
#include <base/printf.h>
|
||||
#include <dataspace/client.h>
|
||||
#include <os/path.h>
|
||||
|
||||
namespace Vfs {
|
||||
|
||||
enum { MAX_PATH_LEN = 512 };
|
||||
|
||||
using Genode::Ram_dataspace_capability;
|
||||
using Genode::Dataspace_capability;
|
||||
using Genode::Dataspace_client;
|
||||
using Genode::env;
|
||||
using Genode::min;
|
||||
using Genode::ascii_to;
|
||||
using Genode::strncpy;
|
||||
using Genode::strcmp;
|
||||
using Genode::strlen;
|
||||
using Genode::off_t;
|
||||
using Genode::memcpy;
|
||||
using Genode::memset;
|
||||
using Genode::size_t;
|
||||
using Genode::Lock;
|
||||
using Genode::List;
|
||||
using Genode::Xml_node;
|
||||
using Genode::Signal_context_capability;
|
||||
using Genode::static_cap_cast;
|
||||
|
||||
typedef Genode::Path<MAX_PATH_LEN> Absolute_path;
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__VFS__TYPES_H_ */
|
64
os/include/vfs/vfs_handle.h
Normal file
64
os/include/vfs/vfs_handle.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* \brief Representation of an open file
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__VFS_HANDLE_H_
|
||||
#define _INCLUDE__VFS__VFS_HANDLE_H_
|
||||
|
||||
#include <vfs/file_io_service.h>
|
||||
#include <vfs/directory_service.h>
|
||||
|
||||
namespace Vfs { class Vfs_handle; }
|
||||
|
||||
|
||||
class Vfs::Vfs_handle
|
||||
{
|
||||
private:
|
||||
|
||||
Directory_service &_ds;
|
||||
File_io_service &_fs;
|
||||
int _status_flags;
|
||||
size_t _seek;
|
||||
|
||||
public:
|
||||
|
||||
enum { STATUS_RDONLY = 0, STATUS_WRONLY = 1, STATUS_RDWR = 2 };
|
||||
|
||||
Vfs_handle(Directory_service &ds, File_io_service &fs, int status_flags)
|
||||
:
|
||||
_ds(ds), _fs(fs), _status_flags(status_flags), _seek(0)
|
||||
{ }
|
||||
|
||||
virtual ~Vfs_handle() { }
|
||||
|
||||
Directory_service &ds() { return _ds; }
|
||||
File_io_service &fs() { return _fs; }
|
||||
|
||||
int status_flags() const { return _status_flags; }
|
||||
|
||||
/**
|
||||
* Return seek offset in bytes
|
||||
*/
|
||||
size_t seek() const { return _seek; }
|
||||
|
||||
/**
|
||||
* Set seek offset in bytes
|
||||
*/
|
||||
void seek(off_t seek) { _seek = seek; }
|
||||
|
||||
/**
|
||||
* Advance seek offset by 'incr' bytes
|
||||
*/
|
||||
void advance_seek(size_t incr) { _seek += incr; }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__VFS_HANDLE_H_ */
|
53
os/include/vfs/zero_file_system.h
Normal file
53
os/include/vfs/zero_file_system.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* \brief zero filesystem
|
||||
* \author Josef Soentgen
|
||||
* \author Norman Feske
|
||||
* \date 2012-07-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__VFS__ZERO_FILE_SYSTEM_H_
|
||||
#define _INCLUDE__VFS__ZERO_FILE_SYSTEM_H_
|
||||
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
namespace Vfs { class Zero_file_system; }
|
||||
|
||||
|
||||
struct Vfs::Zero_file_system : Single_file_system
|
||||
{
|
||||
Zero_file_system(Xml_node config)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config)
|
||||
{ }
|
||||
|
||||
static char const *name() { return "zero"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
Write_result write(Vfs_handle *, char const *, size_t count, size_t &count_out) override
|
||||
{
|
||||
count_out = count;
|
||||
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
Read_result read(Vfs_handle *vfs_handle, char *dst, size_t count, size_t &out_count) override
|
||||
{
|
||||
memset(dst, 0, count);
|
||||
out_count = count;
|
||||
|
||||
return READ_OK;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__VFS__ZERO_FILE_SYSTEM_H_ */
|
@ -21,6 +21,7 @@
|
||||
/* Genode includes */
|
||||
#include <os/ring_buffer.h>
|
||||
#include <util/misc_math.h>
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
|
||||
#define SYSIO_DECL(syscall_name, args, results) \
|
||||
@ -81,25 +82,16 @@ namespace Noux {
|
||||
STAT_MODE_BLOCKDEV = 0060000,
|
||||
};
|
||||
|
||||
struct Stat
|
||||
{
|
||||
size_t size;
|
||||
unsigned mode;
|
||||
unsigned uid;
|
||||
unsigned gid;
|
||||
unsigned long inode;
|
||||
unsigned device;
|
||||
};
|
||||
typedef Vfs::Directory_service::Stat Stat;
|
||||
|
||||
/**
|
||||
* Argument structure used for ioctl syscall
|
||||
*/
|
||||
struct Ioctl_in
|
||||
{
|
||||
enum Opcode { OP_UNDEFINED, OP_TIOCGWINSZ, OP_TIOCSETAF,
|
||||
OP_TIOCSETAW, OP_FIONBIO, OP_DIOCGMEDIASIZE };
|
||||
typedef Vfs::File_io_service::Ioctl_opcode Opcode;
|
||||
|
||||
enum Val { VAL_NULL, VAL_ECHO, VAL_ECHONL };
|
||||
typedef Vfs::File_io_service::Ioctl_value Value;
|
||||
|
||||
Opcode request;
|
||||
int argp;
|
||||
@ -108,45 +100,14 @@ namespace Noux {
|
||||
/**
|
||||
* Structure carrying the result values of 'ioctl' syscalls
|
||||
*/
|
||||
struct Ioctl_out
|
||||
{
|
||||
union
|
||||
{
|
||||
/* if request was 'OP_TIOCGSIZE' */
|
||||
struct {
|
||||
int rows;
|
||||
int columns;
|
||||
} tiocgwinsz;
|
||||
|
||||
/* if request was 'OP_DIOCGMEDIASIZE' */
|
||||
struct {
|
||||
/* disk size rounded up to sector size in bytes*/
|
||||
int size;
|
||||
|
||||
} diocgmediasize;
|
||||
};
|
||||
};
|
||||
typedef Vfs::File_io_service::Ioctl_out Ioctl_out;
|
||||
|
||||
enum Lseek_whence { LSEEK_SET, LSEEK_CUR, LSEEK_END };
|
||||
|
||||
enum { DIRENT_MAX_NAME_LEN = 128 };
|
||||
enum { DIRENT_MAX_NAME_LEN = Vfs::Directory_service::DIRENT_MAX_NAME_LEN };
|
||||
|
||||
enum Dirent_type {
|
||||
DIRENT_TYPE_FILE,
|
||||
DIRENT_TYPE_DIRECTORY,
|
||||
DIRENT_TYPE_FIFO,
|
||||
DIRENT_TYPE_CHARDEV,
|
||||
DIRENT_TYPE_BLOCKDEV,
|
||||
DIRENT_TYPE_SYMLINK,
|
||||
DIRENT_TYPE_END
|
||||
};
|
||||
|
||||
struct Dirent
|
||||
{
|
||||
int fileno;
|
||||
Dirent_type type;
|
||||
char name[DIRENT_MAX_NAME_LEN];
|
||||
};
|
||||
typedef Vfs::Directory_service::Dirent_type Dirent_type;
|
||||
typedef Vfs::Directory_service::Dirent Dirent;
|
||||
|
||||
enum Fcntl_cmd {
|
||||
FCNTL_CMD_GET_FILE_STATUS_FLAGS,
|
||||
@ -304,34 +265,9 @@ namespace Noux {
|
||||
*/
|
||||
enum Clock_Id { CLOCK_ID_SECOND };
|
||||
|
||||
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
|
||||
enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS };
|
||||
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS };
|
||||
enum Ftruncate_error { FTRUNCATE_ERR_NO_PERM = NUM_GENERAL_ERRORS,
|
||||
FTRUNCATE_ERR_INTERRUPT };
|
||||
enum Open_error { OPEN_ERR_UNACCESSIBLE, OPEN_ERR_NO_PERM,
|
||||
OPEN_ERR_EXISTS };
|
||||
enum Execve_error { EXECVE_NONEXISTENT = NUM_GENERAL_ERRORS };
|
||||
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
||||
enum Execve_error { EXECVE_NONEXISTENT = Vfs::Directory_service::NUM_GENERAL_ERRORS };
|
||||
enum Select_error { SELECT_ERR_INTERRUPT };
|
||||
enum Unlink_error { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM };
|
||||
enum Readlink_error { READLINK_ERR_NO_ENTRY };
|
||||
enum Rename_error { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS,
|
||||
RENAME_ERR_NO_PERM };
|
||||
enum Mkdir_error { MKDIR_ERR_EXISTS, MKDIR_ERR_NO_ENTRY,
|
||||
MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM,
|
||||
MKDIR_ERR_NAME_TOO_LONG};
|
||||
enum Symlink_error { SYMLINK_ERR_EXISTS, SYMLINK_ERR_NO_ENTRY,
|
||||
SYMLINK_ERR_NAME_TOO_LONG};
|
||||
|
||||
enum Read_error { READ_ERR_AGAIN, READ_ERR_WOULD_BLOCK,
|
||||
READ_ERR_INVALID, READ_ERR_IO,
|
||||
READ_ERR_INTERRUPT };
|
||||
|
||||
enum Write_error { WRITE_ERR_AGAIN, WRITE_ERR_WOULD_BLOCK,
|
||||
WRITE_ERR_INVALID, WRITE_ERR_IO,
|
||||
WRITE_ERR_INTERRUPT };
|
||||
|
||||
enum Ioctl_error { IOCTL_ERR_INVALID, IOCTL_ERR_NOTTY };
|
||||
|
||||
/**
|
||||
* Socket related errors
|
||||
@ -375,33 +311,35 @@ namespace Noux {
|
||||
enum Kill_error { KILL_ERR_SRCH };
|
||||
|
||||
union {
|
||||
General_error general;
|
||||
Stat_error stat;
|
||||
Fcntl_error fcntl;
|
||||
Ftruncate_error ftruncate;
|
||||
Open_error open;
|
||||
Execve_error execve;
|
||||
Select_error select;
|
||||
Unlink_error unlink;
|
||||
Readlink_error readlink;
|
||||
Rename_error rename;
|
||||
Mkdir_error mkdir;
|
||||
Symlink_error symlink;
|
||||
Read_error read;
|
||||
Write_error write;
|
||||
Ioctl_error ioctl;
|
||||
Accept_error accept;
|
||||
Bind_error bind;
|
||||
Connect_error connect;
|
||||
Listen_error listen;
|
||||
Recv_error recv;
|
||||
Send_error send;
|
||||
Shutdown_error shutdown;
|
||||
Socket_error socket;
|
||||
Clock_error clock;
|
||||
Utimes_error utimes;
|
||||
Wait4_error wait4;
|
||||
Kill_error kill;
|
||||
Vfs::Directory_service::General_error general;
|
||||
Vfs::Directory_service::Stat_result stat;
|
||||
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;
|
||||
Execve_error execve;
|
||||
Select_error select;
|
||||
Accept_error accept;
|
||||
Bind_error bind;
|
||||
Connect_error connect;
|
||||
Listen_error listen;
|
||||
Recv_error recv;
|
||||
Send_error send;
|
||||
Shutdown_error shutdown;
|
||||
Socket_error socket;
|
||||
Clock_error clock;
|
||||
Utimes_error utimes;
|
||||
Wait4_error wait4;
|
||||
Kill_error kill;
|
||||
|
||||
} error;
|
||||
|
||||
union {
|
||||
@ -435,7 +373,7 @@ namespace Noux {
|
||||
{ Chunk chunk; size_t count; });
|
||||
|
||||
SYSIO_DECL(readlink, { Path path; size_t bufsiz; },
|
||||
{ Chunk chunk; ssize_t count; });
|
||||
{ Chunk chunk; size_t count; });
|
||||
|
||||
SYSIO_DECL(execve, { Path filename; Args args; Env env; }, { });
|
||||
|
||||
|
@ -962,7 +962,8 @@ namespace {
|
||||
if (verbose)
|
||||
PWRN("stat syscall failed for path \"%s\"", path);
|
||||
switch (sysio()->error.stat) {
|
||||
case Noux::Sysio::STAT_ERR_NO_ENTRY: errno = ENOENT; break;
|
||||
case Vfs::Directory_service::STAT_OK: /* never reached */
|
||||
case Vfs::Directory_service::STAT_ERR_NO_ENTRY: errno = ENOENT; break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -988,7 +989,7 @@ namespace {
|
||||
opened = true;
|
||||
else
|
||||
switch (sysio()->error.open) {
|
||||
case Noux::Sysio::OPEN_ERR_UNACCESSIBLE:
|
||||
case Vfs::Directory_service::OPEN_ERR_UNACCESSIBLE:
|
||||
if (!(flags & O_CREAT)) {
|
||||
errno = ENOENT;
|
||||
return 0;
|
||||
@ -1000,16 +1001,18 @@ namespace {
|
||||
opened = true;
|
||||
else
|
||||
switch (sysio()->error.open) {
|
||||
case Noux::Sysio::OPEN_ERR_EXISTS:
|
||||
/* file has been created by someone else in the meantime */
|
||||
break;
|
||||
case Noux::Sysio::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
||||
default: errno = ENOENT; return 0;
|
||||
case Vfs::Directory_service::OPEN_ERR_EXISTS:
|
||||
/* file has been created by someone else in the meantime */
|
||||
break;
|
||||
case Vfs::Directory_service::OPEN_ERR_NO_PERM:
|
||||
errno = EPERM; return 0;
|
||||
default:
|
||||
errno = ENOENT; return 0;
|
||||
}
|
||||
break;
|
||||
case Noux::Sysio::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
||||
case Noux::Sysio::OPEN_ERR_EXISTS: errno = EEXIST; return 0;
|
||||
default: errno = ENOENT; return 0;
|
||||
case Vfs::Directory_service::OPEN_ERR_NO_PERM: errno = EPERM; return 0;
|
||||
case Vfs::Directory_service::OPEN_ERR_EXISTS: errno = EEXIST; return 0;
|
||||
default: errno = ENOENT; return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1070,13 +1073,13 @@ namespace {
|
||||
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_WRITE)) {
|
||||
switch (sysio()->error.write) {
|
||||
case Noux::Sysio::WRITE_ERR_AGAIN: errno = EAGAIN; break;
|
||||
case Noux::Sysio::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
||||
case Noux::Sysio::WRITE_ERR_INVALID: errno = EINVAL; break;
|
||||
case Noux::Sysio::WRITE_ERR_IO: errno = EIO; break;
|
||||
case Noux::Sysio::WRITE_ERR_INTERRUPT: errno = EINTR; break;
|
||||
case Vfs::File_io_service::WRITE_ERR_AGAIN: errno = EAGAIN; break;
|
||||
case Vfs::File_io_service::WRITE_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
||||
case Vfs::File_io_service::WRITE_ERR_INVALID: errno = EINVAL; break;
|
||||
case Vfs::File_io_service::WRITE_ERR_IO: errno = EIO; break;
|
||||
case Vfs::File_io_service::WRITE_ERR_INTERRUPT: errno = EINTR; break;
|
||||
default:
|
||||
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
|
||||
if (sysio()->error.general == Vfs::Directory_service::ERR_FD_INVALID)
|
||||
errno = EBADF;
|
||||
else
|
||||
errno = 0;
|
||||
@ -1106,13 +1109,13 @@ namespace {
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_READ)) {
|
||||
|
||||
switch (sysio()->error.read) {
|
||||
case Noux::Sysio::READ_ERR_AGAIN: errno = EAGAIN; break;
|
||||
case Noux::Sysio::READ_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
||||
case Noux::Sysio::READ_ERR_INVALID: errno = EINVAL; break;
|
||||
case Noux::Sysio::READ_ERR_IO: errno = EIO; break;
|
||||
case Noux::Sysio::READ_ERR_INTERRUPT: errno = EINTR; break;
|
||||
case Vfs::File_io_service::READ_ERR_AGAIN: errno = EAGAIN; break;
|
||||
case Vfs::File_io_service::READ_ERR_WOULD_BLOCK: errno = EWOULDBLOCK; break;
|
||||
case Vfs::File_io_service::READ_ERR_INVALID: errno = EINVAL; break;
|
||||
case Vfs::File_io_service::READ_ERR_IO: errno = EIO; break;
|
||||
case Vfs::File_io_service::READ_ERR_INTERRUPT: errno = EINTR; break;
|
||||
default:
|
||||
if (sysio()->error.general == Noux::Sysio::ERR_FD_INVALID)
|
||||
if (sysio()->error.general == Vfs::Directory_service::ERR_FD_INVALID)
|
||||
errno = EBADF;
|
||||
else
|
||||
errno = 0;
|
||||
@ -1159,12 +1162,12 @@ namespace {
|
||||
* Marshal ioctl arguments
|
||||
*/
|
||||
sysio()->ioctl_in.fd = noux_fd(fd->context);
|
||||
sysio()->ioctl_in.request = Noux::Sysio::Ioctl_in::OP_UNDEFINED;
|
||||
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_UNDEFINED;
|
||||
|
||||
switch (request) {
|
||||
|
||||
case TIOCGWINSZ:
|
||||
sysio()->ioctl_in.request = Noux::Sysio::Ioctl_in::OP_TIOCGWINSZ;
|
||||
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_TIOCGWINSZ;
|
||||
break;
|
||||
|
||||
case TIOCGETA:
|
||||
@ -1192,7 +1195,7 @@ namespace {
|
||||
|
||||
case TIOCSETAF:
|
||||
{
|
||||
sysio()->ioctl_in.request = Noux::Sysio::Ioctl_in::OP_TIOCSETAF;
|
||||
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_TIOCSETAF;
|
||||
|
||||
::termios *termios = (::termios *)argp;
|
||||
|
||||
@ -1200,11 +1203,11 @@ namespace {
|
||||
* For now only enabling/disabling of ECHO is supported
|
||||
*/
|
||||
if (termios->c_lflag & (ECHO | ECHONL)) {
|
||||
sysio()->ioctl_in.argp = (Noux::Sysio::Ioctl_in::VAL_ECHO |
|
||||
Noux::Sysio::Ioctl_in::VAL_ECHONL);
|
||||
sysio()->ioctl_in.argp = (Vfs::File_io_service::IOCTL_VAL_ECHO |
|
||||
Vfs::File_io_service::IOCTL_VAL_ECHONL);
|
||||
}
|
||||
else {
|
||||
sysio()->ioctl_in.argp = Noux::Sysio::Ioctl_in::VAL_NULL;
|
||||
sysio()->ioctl_in.argp = Vfs::File_io_service::IOCTL_VAL_NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1212,7 +1215,7 @@ namespace {
|
||||
|
||||
case TIOCSETAW:
|
||||
{
|
||||
sysio()->ioctl_in.request = Noux::Sysio::Ioctl_in::OP_TIOCSETAW;
|
||||
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_TIOCSETAW;
|
||||
sysio()->ioctl_in.argp = argp ? *(int*)argp : 0;
|
||||
|
||||
break;
|
||||
@ -1223,7 +1226,7 @@ namespace {
|
||||
if (verbose)
|
||||
PDBG("FIONBIO - *argp=%d", *argp);
|
||||
|
||||
sysio()->ioctl_in.request = Noux::Sysio::Ioctl_in::OP_FIONBIO;
|
||||
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_FIONBIO;
|
||||
sysio()->ioctl_in.argp = argp ? *(int*)argp : 0;
|
||||
|
||||
break;
|
||||
@ -1231,7 +1234,7 @@ namespace {
|
||||
|
||||
case DIOCGMEDIASIZE:
|
||||
{
|
||||
sysio()->ioctl_in.request = Noux::Sysio::Ioctl_in::OP_DIOCGMEDIASIZE;
|
||||
sysio()->ioctl_in.request = Vfs::File_io_service::IOCTL_OP_DIOCGMEDIASIZE;
|
||||
sysio()->ioctl_in.argp = 0;
|
||||
|
||||
break;
|
||||
@ -1243,7 +1246,7 @@ namespace {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sysio()->ioctl_in.request == Noux::Sysio::Ioctl_in::OP_UNDEFINED) {
|
||||
if (sysio()->ioctl_in.request == Vfs::File_io_service::IOCTL_OP_UNDEFINED) {
|
||||
errno = ENOTTY;
|
||||
return -1;
|
||||
}
|
||||
@ -1251,8 +1254,8 @@ namespace {
|
||||
/* perform syscall */
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_IOCTL)) {
|
||||
switch (sysio()->error.ioctl) {
|
||||
case Noux::Sysio::IOCTL_ERR_INVALID: errno = EINVAL; break;
|
||||
case Noux::Sysio::IOCTL_ERR_NOTTY: errno = ENOTTY; break;
|
||||
case Vfs::File_io_service::IOCTL_ERR_INVALID: errno = EINVAL; break;
|
||||
case Vfs::File_io_service::IOCTL_ERR_NOTTY: errno = ENOTTY; break;
|
||||
default: errno = 0; break;
|
||||
}
|
||||
|
||||
@ -1373,8 +1376,9 @@ namespace {
|
||||
sysio()->ftruncate_in.length = length;
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_FTRUNCATE)) {
|
||||
switch (sysio()->error.ftruncate) {
|
||||
case Noux::Sysio::FTRUNCATE_ERR_NO_PERM: errno = EPERM; break;
|
||||
case Noux::Sysio::FTRUNCATE_ERR_INTERRUPT: errno = EINTR; break;
|
||||
case Vfs::File_io_service::FTRUNCATE_OK: /* never reached */
|
||||
case Vfs::File_io_service::FTRUNCATE_ERR_NO_PERM: errno = EPERM; break;
|
||||
case Vfs::File_io_service::FTRUNCATE_ERR_INTERRUPT: errno = EINTR; break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -1453,8 +1457,8 @@ namespace {
|
||||
case Noux::Sysio::FCNTL_ERR_CMD_INVALID: errno = EINVAL; break;
|
||||
default:
|
||||
switch (sysio()->error.general) {
|
||||
case Noux::Sysio::ERR_FD_INVALID: errno = EINVAL; break;
|
||||
case Noux::Sysio::NUM_GENERAL_ERRORS: break;
|
||||
case Vfs::Directory_service::ERR_FD_INVALID: errno = EINVAL; break;
|
||||
case Vfs::Directory_service::NUM_GENERAL_ERRORS: break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
@ -1481,23 +1485,23 @@ namespace {
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_DIRENT)) {
|
||||
switch (sysio()->error.general) {
|
||||
|
||||
case Noux::Sysio::ERR_FD_INVALID:
|
||||
case Vfs::Directory_service::ERR_FD_INVALID:
|
||||
errno = EBADF;
|
||||
PERR("dirent: ERR_FD_INVALID");
|
||||
return -1;
|
||||
|
||||
case Noux::Sysio::NUM_GENERAL_ERRORS: return -1;
|
||||
case Vfs::Directory_service::NUM_GENERAL_ERRORS: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (sysio()->dirent_out.entry.type) {
|
||||
case Noux::Sysio::DIRENT_TYPE_DIRECTORY: dirent->d_type = DT_DIR; break;
|
||||
case Noux::Sysio::DIRENT_TYPE_FILE: dirent->d_type = DT_REG; break;
|
||||
case Noux::Sysio::DIRENT_TYPE_SYMLINK: dirent->d_type = DT_LNK; break;
|
||||
case Noux::Sysio::DIRENT_TYPE_FIFO: dirent->d_type = DT_FIFO; break;
|
||||
case Noux::Sysio::DIRENT_TYPE_CHARDEV: dirent->d_type = DT_CHR; break;
|
||||
case Noux::Sysio::DIRENT_TYPE_BLOCKDEV: dirent->d_type = DT_BLK; break;
|
||||
case Noux::Sysio::DIRENT_TYPE_END: return 0;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_DIRECTORY: dirent->d_type = DT_DIR; break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_FILE: dirent->d_type = DT_REG; break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_SYMLINK: dirent->d_type = DT_LNK; break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_FIFO: dirent->d_type = DT_FIFO; break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_CHARDEV: dirent->d_type = DT_CHR; break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_BLOCKDEV: dirent->d_type = DT_BLK; break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_END: return 0;
|
||||
}
|
||||
|
||||
dirent->d_fileno = sysio()->dirent_out.entry.fileno;
|
||||
@ -1529,12 +1533,12 @@ namespace {
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_LSEEK)) {
|
||||
switch (sysio()->error.general) {
|
||||
|
||||
case Noux::Sysio::ERR_FD_INVALID:
|
||||
case Vfs::Directory_service::ERR_FD_INVALID:
|
||||
errno = EBADF;
|
||||
PERR("lseek: ERR_FD_INVALID");
|
||||
return -1;
|
||||
|
||||
case Noux::Sysio::NUM_GENERAL_ERRORS: return -1;
|
||||
case Vfs::Directory_service::NUM_GENERAL_ERRORS: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1549,8 +1553,8 @@ namespace {
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_UNLINK)) {
|
||||
PWRN("unlink syscall failed for path \"%s\"", path);
|
||||
switch (sysio()->error.unlink) {
|
||||
case Noux::Sysio::UNLINK_ERR_NO_ENTRY: errno = ENOENT; break;
|
||||
default: errno = EPERM; break;
|
||||
case Vfs::Directory_service::UNLINK_ERR_NO_ENTRY: errno = ENOENT; break;
|
||||
default: errno = EPERM; break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -1592,10 +1596,10 @@ namespace {
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_RENAME)) {
|
||||
PWRN("rename syscall failed for \"%s\" -> \"%s\"", from_path, to_path);
|
||||
switch (sysio()->error.rename) {
|
||||
case Noux::Sysio::RENAME_ERR_NO_ENTRY: errno = ENOENT; break;
|
||||
case Noux::Sysio::RENAME_ERR_CROSS_FS: errno = EXDEV; break;
|
||||
case Noux::Sysio::RENAME_ERR_NO_PERM: errno = EPERM; break;
|
||||
default: errno = EPERM; break;
|
||||
case Vfs::Directory_service::RENAME_ERR_NO_ENTRY: errno = ENOENT; break;
|
||||
case Vfs::Directory_service::RENAME_ERR_CROSS_FS: errno = EXDEV; break;
|
||||
case Vfs::Directory_service::RENAME_ERR_NO_PERM: errno = EPERM; break;
|
||||
default: errno = EPERM; break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -1611,12 +1615,12 @@ namespace {
|
||||
if (!noux_syscall(Noux::Session::SYSCALL_MKDIR)) {
|
||||
PWRN("mkdir syscall failed for \"%s\" mode=0x%x", path, (int)mode);
|
||||
switch (sysio()->error.mkdir) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,465 +0,0 @@
|
||||
/*
|
||||
* \brief Block device file system
|
||||
* \author Josef Soentgen
|
||||
* \date 2013-12-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__BLOCK_FILE_SYSTEM_H_
|
||||
#define _NOUX__BLOCK_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/allocator_avl.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/lock.h>
|
||||
#include <base/stdint.h>
|
||||
#include <block_session/connection.h>
|
||||
#include <util/string.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "file_system.h"
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Block_file_system : public File_system
|
||||
{
|
||||
private:
|
||||
|
||||
struct Label
|
||||
{
|
||||
enum { LABEL_MAX_LEN = 64 };
|
||||
char string[LABEL_MAX_LEN];
|
||||
|
||||
Label(Xml_node config)
|
||||
{
|
||||
string[0] = 0;
|
||||
try { config.attribute("label").value(string, sizeof(string)); }
|
||||
catch (...) { }
|
||||
}
|
||||
} _label;
|
||||
|
||||
char *_block_buffer;
|
||||
unsigned _block_buffer_count;
|
||||
|
||||
Genode::Allocator_avl _tx_block_alloc;
|
||||
Block::Connection _block;
|
||||
size_t _block_size;
|
||||
Block::sector_t _block_count;
|
||||
Block::Session::Operations _block_ops;
|
||||
Block::Session::Tx::Source *_tx_source;
|
||||
|
||||
off_t _seek_offset;
|
||||
|
||||
bool _readable;
|
||||
bool _writeable;
|
||||
|
||||
|
||||
enum { FILENAME_MAX_LEN = 64 };
|
||||
char _filename[FILENAME_MAX_LEN];
|
||||
|
||||
bool _is_root(const char *path)
|
||||
{
|
||||
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
||||
}
|
||||
|
||||
bool _is_block_file(const char *path)
|
||||
{
|
||||
return (strlen(path) == (strlen(_filename) + 1)) &&
|
||||
(strcmp(&path[1], _filename) == 0);
|
||||
}
|
||||
|
||||
size_t _block_io(size_t nr, void *buf, size_t sz, bool write, bool bulk = false)
|
||||
{
|
||||
Block::Packet_descriptor::Opcode op;
|
||||
op = write ? Block::Packet_descriptor::WRITE : Block::Packet_descriptor::READ;
|
||||
|
||||
size_t packet_size = bulk ? sz : _block_size;
|
||||
size_t packet_count = bulk ? (sz / _block_size) : 1;
|
||||
|
||||
/* sanity check */
|
||||
if (packet_count > _block_buffer_count) {
|
||||
packet_size = _block_buffer_count * _block_size;
|
||||
packet_count = _block_buffer_count;
|
||||
}
|
||||
|
||||
//if (verbose)
|
||||
PDBG("%5s: block:%zu size:%zu packets:%zu",
|
||||
write ? "write" : "read", nr, sz, packet_count);
|
||||
|
||||
Block::Packet_descriptor p(_tx_source->alloc_packet(packet_size), 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()) {
|
||||
PERR("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;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Block_file_system(Xml_node config)
|
||||
:
|
||||
_label(config),
|
||||
_block_buffer(0),
|
||||
_block_buffer_count(1),
|
||||
_tx_block_alloc(env()->heap()),
|
||||
_block(&_tx_block_alloc, 128*1024, _label.string),
|
||||
_tx_source(_block.tx()),
|
||||
_seek_offset(0),
|
||||
_readable(false),
|
||||
_writeable(false)
|
||||
{
|
||||
_filename[0] = 0;
|
||||
try { config.attribute("name").value(_filename, sizeof(_filename)); }
|
||||
catch (...) { }
|
||||
|
||||
try { config.attribute("block_buffer_count").value(&_block_buffer_count); }
|
||||
catch (...) { }
|
||||
|
||||
_block.info(&_block_count, &_block_size, &_block_ops);
|
||||
|
||||
_readable = _block_ops.supported(Block::Packet_descriptor::READ);
|
||||
_writeable = _block_ops.supported(Block::Packet_descriptor::WRITE);
|
||||
|
||||
_block_buffer = new (env()->heap()) char[_block_buffer_count * _block_size];
|
||||
|
||||
//if (verbose)
|
||||
PDBG("number of blocks: %llu with block size: %zu (bytes)"
|
||||
" , readable: %d writeable: %d",
|
||||
_block_count, _block_size, _readable, _writeable);
|
||||
}
|
||||
|
||||
~Block_file_system()
|
||||
{
|
||||
destroy(env()->heap(), _block_buffer);
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
Sysio::Stat st = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (_is_root(path))
|
||||
st.mode = Sysio::STAT_MODE_DIRECTORY;
|
||||
else if (_is_block_file(path)) {
|
||||
st.mode = Sysio::STAT_MODE_BLOCKDEV;
|
||||
} else {
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
sysio->stat_out.st = st;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
if (_is_root(path)) {
|
||||
if (index == 0) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_BLOCKDEV;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
_filename,
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
} else {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
if (!_is_block_file(path)) {
|
||||
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "block"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
if (!_writeable) {
|
||||
PERR("block device is not writeable");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _seek_offset = vfs_handle->seek();
|
||||
size_t count = sysio->write_in.count;
|
||||
const char *_buf = (const char*) sysio->write_in.chunk;
|
||||
|
||||
size_t written = 0;
|
||||
while (count > 0) {
|
||||
size_t displ = 0;
|
||||
size_t length = 0;
|
||||
size_t nbytes = 0;
|
||||
size_t 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)) {
|
||||
size_t bytes_left = count - (count % _block_size);
|
||||
|
||||
nbytes = _block_io(blk_nr, (void*)(_buf + written),
|
||||
bytes_left, true, true);
|
||||
if (nbytes == 0) {
|
||||
PERR("error while reading block:%zu from block device",
|
||||
blk_nr);
|
||||
return false;
|
||||
}
|
||||
|
||||
written += nbytes;
|
||||
count -= 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) {
|
||||
PWRN("offset:%zd block_size:%zd displacement:%zd length:%zu",
|
||||
_seek_offset, _block_size, displ, length);
|
||||
|
||||
_block_io(blk_nr, _block_buffer, _block_size, false);
|
||||
/* rewind seek offset to account for the block read */
|
||||
_seek_offset -= _block_size;
|
||||
}
|
||||
|
||||
Genode::memcpy(_block_buffer + displ, _buf + written, length);
|
||||
|
||||
nbytes = _block_io(blk_nr, _block_buffer, _block_size, true);
|
||||
if ((unsigned)nbytes != _block_size) {
|
||||
PERR("error while reading block:%zu from Block_device",
|
||||
blk_nr);
|
||||
return false;
|
||||
}
|
||||
|
||||
written += length;
|
||||
count -= length;
|
||||
}
|
||||
|
||||
sysio->write_out.count = written;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
if (!_readable) {
|
||||
PERR("block device is not readable");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _seek_offset = vfs_handle->seek();
|
||||
size_t count = sysio->read_in.count;
|
||||
char *_buf = (char*) sysio->read_out.chunk;
|
||||
|
||||
size_t read = 0;
|
||||
while (count > 0) {
|
||||
size_t displ = 0;
|
||||
size_t length = 0;
|
||||
size_t nbytes = 0;
|
||||
size_t 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 t he
|
||||
* 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)) {
|
||||
size_t bytes_left = count - (count % _block_size);
|
||||
|
||||
nbytes = _block_io(blk_nr, _buf + read, bytes_left, false, true);
|
||||
if (nbytes == 0) {
|
||||
PERR("error while reading block:%zu from block device",
|
||||
blk_nr);
|
||||
return false;
|
||||
}
|
||||
|
||||
read += nbytes;
|
||||
count -= nbytes;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (displ > 0)
|
||||
PWRN("offset:%zd is not aligned to block_size:%zu"
|
||||
" displacement:%zu", _seek_offset, _block_size,
|
||||
displ);
|
||||
|
||||
nbytes = _block_io(blk_nr, _block_buffer, _block_size, false);
|
||||
if ((unsigned)nbytes != _block_size) {
|
||||
PERR("error while reading block:%zu from block device",
|
||||
blk_nr);
|
||||
return false;
|
||||
}
|
||||
|
||||
Genode::memcpy(_buf + read, _block_buffer + displ, length);
|
||||
|
||||
read += length;
|
||||
count -= length;
|
||||
}
|
||||
|
||||
sysio->read_out.count = read;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; }
|
||||
|
||||
bool check_unblock(Vfs_handle *vfs_handle, bool rd, bool wr, bool ex)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ioctl(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
switch (sysio->ioctl_in.request) {
|
||||
case Sysio::Ioctl_in::OP_DIOCGMEDIASIZE:
|
||||
{
|
||||
sysio->ioctl_out.diocgmediasize.size = _block_count * _block_size;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
PDBG("invalid ioctl request %d", sysio->ioctl_in.request);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* never reached */
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__BLOCK_FILE_SYSTEM_H_ */
|
@ -22,7 +22,7 @@
|
||||
|
||||
/* Noux includes */
|
||||
#include <file_descriptor_registry.h>
|
||||
#include <dir_file_system.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
#include <noux_session/capability.h>
|
||||
#include <args.h>
|
||||
#include <environment.h>
|
||||
@ -183,10 +183,10 @@ namespace Noux {
|
||||
enum { NAME_MAX_LEN = 128 };
|
||||
char _name[NAME_MAX_LEN];
|
||||
|
||||
Dir_file_system * const _root_dir;
|
||||
Dataspace_capability const _binary_ds;
|
||||
Vfs::Dir_file_system * const _root_dir;
|
||||
Dataspace_capability const _binary_ds;
|
||||
|
||||
Elf(char const * const binary_name, Dir_file_system * root_dir,
|
||||
Elf(char const * const binary_name, Vfs::Dir_file_system * root_dir,
|
||||
Dataspace_capability binary_ds)
|
||||
:
|
||||
_root_dir(root_dir), _binary_ds(binary_ds)
|
||||
@ -285,7 +285,7 @@ namespace Noux {
|
||||
io->unregister_wake_up_notifier(¬ifier);
|
||||
}
|
||||
|
||||
Dir_file_system * const root_dir() { return _elf._root_dir; }
|
||||
Vfs::Dir_file_system * const root_dir() { return _elf._root_dir; }
|
||||
|
||||
/**
|
||||
* Method for handling noux network related system calls
|
||||
@ -310,22 +310,22 @@ namespace Noux {
|
||||
* looked up at the virtual file
|
||||
* system
|
||||
*/
|
||||
Child(char const *binary_name,
|
||||
Parent_exit *parent_exit,
|
||||
Kill_broadcaster &kill_broadcaster,
|
||||
Parent_execve &parent_execve,
|
||||
int pid,
|
||||
Signal_receiver *sig_rec,
|
||||
Dir_file_system *root_dir,
|
||||
Args const &args,
|
||||
Sysio::Env const &env,
|
||||
Cap_session *cap_session,
|
||||
Service_registry &parent_services,
|
||||
Rpc_entrypoint &resources_ep,
|
||||
bool forked,
|
||||
Allocator *destruct_alloc,
|
||||
Destruct_queue &destruct_queue,
|
||||
bool verbose)
|
||||
Child(char const *binary_name,
|
||||
Parent_exit *parent_exit,
|
||||
Kill_broadcaster &kill_broadcaster,
|
||||
Parent_execve &parent_execve,
|
||||
int pid,
|
||||
Signal_receiver *sig_rec,
|
||||
Vfs::Dir_file_system *root_dir,
|
||||
Args const &args,
|
||||
Sysio::Env const &env,
|
||||
Cap_session *cap_session,
|
||||
Service_registry &parent_services,
|
||||
Rpc_entrypoint &resources_ep,
|
||||
bool forked,
|
||||
Allocator *destruct_alloc,
|
||||
Destruct_queue &destruct_queue,
|
||||
bool verbose)
|
||||
:
|
||||
Family_member(pid),
|
||||
Destruct_queue::Element<Child>(destruct_alloc),
|
||||
|
@ -1,640 +0,0 @@
|
||||
/*
|
||||
* \brief Directory file system
|
||||
* \author Norman Feske
|
||||
* \date 2012-04-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__DIR_FILE_SYSTEM_H_
|
||||
#define _NOUX__DIR_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/lock.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include <file_system_registry.h>
|
||||
#include <vfs_handle.h>
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Dir_file_system : public File_system
|
||||
{
|
||||
public:
|
||||
|
||||
enum { MAX_NAME_LEN = 128 };
|
||||
|
||||
private:
|
||||
|
||||
Lock _lock;
|
||||
|
||||
/* pointer to first child file system */
|
||||
File_system *_first_file_system;
|
||||
|
||||
/* add new file system to the list of children */
|
||||
void _append_file_system(File_system *fs)
|
||||
{
|
||||
if (!_first_file_system) {
|
||||
_first_file_system = fs;
|
||||
return;
|
||||
}
|
||||
|
||||
File_system *curr = _first_file_system;
|
||||
while (curr->next)
|
||||
curr = curr->next;
|
||||
|
||||
curr->next = fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directory name
|
||||
*/
|
||||
char _name[MAX_NAME_LEN];
|
||||
|
||||
bool _is_root() const { return _name[0] == 0; }
|
||||
|
||||
public:
|
||||
|
||||
Dir_file_system(Xml_node node, File_system_registry &fs_registry)
|
||||
:
|
||||
_first_file_system(0)
|
||||
{
|
||||
/* remember directory name */
|
||||
if (node.has_type("fstab"))
|
||||
_name[0] = 0;
|
||||
else
|
||||
node.attribute("name").value(_name, sizeof(_name));
|
||||
|
||||
for (unsigned i = 0; i < node.num_sub_nodes(); i++) {
|
||||
|
||||
Xml_node sub_node = node.sub_node(i);
|
||||
|
||||
/* traverse into <dir> nodes */
|
||||
if (sub_node.has_type("dir")) {
|
||||
_append_file_system(new Dir_file_system(sub_node,
|
||||
fs_registry));
|
||||
continue;
|
||||
}
|
||||
|
||||
File_system_registry::Entry *fs = fs_registry.lookup(sub_node);
|
||||
if (fs) {
|
||||
_append_file_system(fs->create(sub_node));
|
||||
continue;
|
||||
}
|
||||
|
||||
char type_name[64];
|
||||
sub_node.type_name(type_name, sizeof(type_name));
|
||||
PWRN("unknown fstab node type <%s>", type_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return portion of the path after the element corresponding to
|
||||
* the current directory.
|
||||
*/
|
||||
char const *_sub_path(char const *path) const
|
||||
{
|
||||
/* do not strip anything from the path when we are root */
|
||||
if (_is_root())
|
||||
return path;
|
||||
|
||||
/* skip heading slash in path if present */
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
|
||||
size_t const name_len = strlen(_name);
|
||||
if (strcmp(path, _name, name_len) != 0)
|
||||
return 0;
|
||||
path += name_len;
|
||||
|
||||
/*
|
||||
* The first characters of the first path element are equal to
|
||||
* the current directory name. Let's check if the length of the
|
||||
* first path element matches the name length.
|
||||
*/
|
||||
if (*path != 0 && *path != '/')
|
||||
return 0;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return Dataspace_capability();
|
||||
|
||||
/*
|
||||
* Query sub file systems for dataspace using the path local to
|
||||
* the respective file system
|
||||
*/
|
||||
File_system *fs = _first_file_system;
|
||||
for (; fs; fs = fs->next) {
|
||||
Dataspace_capability ds = fs->dataspace(path);
|
||||
if (ds.valid())
|
||||
return ds;
|
||||
}
|
||||
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next)
|
||||
fs->release(path, ds_cap);
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If path equals directory name, return information about the
|
||||
* current directory.
|
||||
*/
|
||||
if (strlen(path) == 0) {
|
||||
sysio->stat_out.st.size = 0;
|
||||
sysio->stat_out.st.mode = Sysio::STAT_MODE_DIRECTORY | 0755;
|
||||
sysio->stat_out.st.uid = 0;
|
||||
sysio->stat_out.st.gid = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The given path refers to one of our sub directories.
|
||||
* Propagate the request into our file systems.
|
||||
*/
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
if (fs->stat(sysio, path)) {
|
||||
return true;
|
||||
} else {
|
||||
/*
|
||||
* Keep the most meaningful error code. When using
|
||||
* stacked file systems, most child file systems will
|
||||
* eventually return 'STAT_ERR_NO_ENTRY' (or leave
|
||||
* the error code unchanged). If any of those file
|
||||
* systems has anything more interesting to tell,
|
||||
* return this information.
|
||||
*/
|
||||
if (sysio->error.stat != Sysio::STAT_ERR_NO_ENTRY)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* none of our file systems felt responsible for the path */
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The 'path' is relative to the child file systems.
|
||||
*/
|
||||
bool _dirent_of_file_systems(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
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;
|
||||
bool const res = fs->dirent(sysio, path, index);
|
||||
sysio->dirent_out.entry.fileno += base;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* adjust base index for next file system */
|
||||
base += fs_num_dirent;
|
||||
}
|
||||
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _dirent_of_this_dir_node(Sysio *sysio, off_t index)
|
||||
{
|
||||
if (index == 0) {
|
||||
strncpy(sysio->dirent_out.entry.name, _name,
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_DIRECTORY;
|
||||
sysio->dirent_out.entry.fileno = 1;
|
||||
} else
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (_is_root()) {
|
||||
return _dirent_of_file_systems(sysio, path, index);
|
||||
|
||||
} else {
|
||||
|
||||
if (strcmp(path, "/") == 0)
|
||||
return _dirent_of_this_dir_node(sysio, index);
|
||||
|
||||
/* path contains at least one element */
|
||||
|
||||
/* remove current element from path */
|
||||
path = _sub_path(path);
|
||||
if (path) {
|
||||
return _dirent_of_file_systems(sysio, path, index);
|
||||
|
||||
} else {
|
||||
/* path does not lie within our tree */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Accumulate number of directory entries that match in any of
|
||||
* our sub file systems.
|
||||
*/
|
||||
size_t _sum_dirents_of_file_systems(char const *path)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
cnt += fs->num_dirent(path);
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
if (_is_root()) {
|
||||
return _sum_dirents_of_file_systems(path);
|
||||
|
||||
} else {
|
||||
|
||||
if (strcmp(path, "/") == 0)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* The path contains at least one element. Remove current
|
||||
* element from path.
|
||||
*/
|
||||
path = _sub_path(path);
|
||||
|
||||
/*
|
||||
* If the resulting 'path' is non-NULL, the path lies
|
||||
* within our tree. In this case, determine the sum of
|
||||
* matching dirents of all our file systems. Otherwise,
|
||||
* the specified path lies outside our directory node.
|
||||
*/
|
||||
return path ? _sum_dirents_of_file_systems(path) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
if (strlen(path) == 0)
|
||||
return true;
|
||||
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next)
|
||||
if (fs->is_directory(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
path = _sub_path(path);
|
||||
if (!path)
|
||||
return 0;
|
||||
|
||||
if (strlen(path) == 0)
|
||||
return path;
|
||||
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
char const *leaf_path = fs->leaf_path(path);
|
||||
if (leaf_path)
|
||||
return leaf_path;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
/*
|
||||
* If 'path' is a directory, we create a 'Vfs_handle'
|
||||
* for the root directory so that subsequent 'dirent' calls
|
||||
* are subjected to the stacked file-system layout.
|
||||
*/
|
||||
if (is_directory(path))
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
|
||||
/*
|
||||
* If 'path' refers to a non-directory node, create a
|
||||
* 'Vfs_handle' local to the file system that provides the
|
||||
* file.
|
||||
*/
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
path = _sub_path(path);
|
||||
|
||||
/* check if path does not match directory name */
|
||||
if (!path)
|
||||
return 0;
|
||||
|
||||
/* path equals directory name */
|
||||
if (strlen(path) == 0)
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
|
||||
/* path refers to any of our sub file systems */
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
Vfs_handle *vfs_handle = fs->open(sysio, path);
|
||||
if (vfs_handle)
|
||||
return vfs_handle;
|
||||
}
|
||||
|
||||
/* path does not match any existing file or directory */
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
|
||||
sysio->error.unlink = Sysio::UNLINK_ERR_NO_ENTRY;
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Prevent unlinking if path equals directory name defined
|
||||
* via the static fstab configuration.
|
||||
*/
|
||||
if (strlen(path) == 0) {
|
||||
sysio->error.unlink = Sysio::UNLINK_ERR_NO_PERM;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* The given path refers to at least one of our sub
|
||||
* directories. Propagate the request into all of our file
|
||||
* systems. If at least one unlink operation succeeded, we
|
||||
* return success.
|
||||
*/
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next)
|
||||
if (fs->unlink(sysio, path)) {
|
||||
return true;
|
||||
} else {
|
||||
/*
|
||||
* Keep the most meaningful error code. When using
|
||||
* stacked file systems, most child file systems will
|
||||
* eventually return 'UNLINK_ERR_NO_ENTRY' (or leave
|
||||
* the error code unchanged). If any of those file
|
||||
* systems has anything more interesting to tell
|
||||
* (in particular 'UNLINK_ERR_NO_PERM'), return
|
||||
* this information.
|
||||
*/
|
||||
if (sysio->error.unlink != Sysio::UNLINK_ERR_NO_ENTRY)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* none of our file systems could successfully unlink the path */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
|
||||
sysio->error.readlink = Sysio::READLINK_ERR_NO_ENTRY;
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
/* path refers to any of our sub file systems */
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
if (fs->readlink(sysio, path)) {
|
||||
return true;
|
||||
} else {
|
||||
/*
|
||||
* Keep the most meaningful error code. When using
|
||||
* stacked file systems, most child file systems will
|
||||
* eventually return 'READLINK_ERR_NO_ENTRY' (or leave
|
||||
* the error code unchanged). If any of those file
|
||||
* systems has anything more interesting to tell,
|
||||
* return this information.
|
||||
*/
|
||||
if (sysio->error.readlink != Sysio::READLINK_ERR_NO_ENTRY)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* none of our file systems could read the link */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
from_path = _sub_path(from_path);
|
||||
|
||||
sysio->error.rename = Sysio::RENAME_ERR_NO_ENTRY;
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!from_path)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Prevent renaming if path equals directory name defined
|
||||
* via the static fstab configuration.
|
||||
*/
|
||||
if (strlen(from_path) == 0) {
|
||||
sysio->error.rename = Sysio::RENAME_ERR_NO_PERM;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if destination path resides within the same file
|
||||
* system instance as the source path.
|
||||
*/
|
||||
to_path = _sub_path(to_path);
|
||||
if (!to_path) {
|
||||
sysio->error.rename = Sysio::RENAME_ERR_CROSS_FS;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* path refers to any of our sub file systems */
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
if (fs->rename(sysio, from_path, to_path)) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* Keep the most meaningful error code. When using
|
||||
* stacked file systems, most child file systems will
|
||||
* eventually return 'RENAME_ERR_NO_ENTRY' (or leave
|
||||
* the error code unchanged). If any of those file
|
||||
* systems has anything more interesting to tell,
|
||||
* return this information.
|
||||
*/
|
||||
if (sysio->error.rename != Sysio::RENAME_ERR_NO_ENTRY)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* none of our file systems could successfully rename the path */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
|
||||
sysio->error.symlink = Sysio::SYMLINK_ERR_NO_ENTRY;
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Prevent symlink of path that equals directory name defined
|
||||
* via the static fstab configuration.
|
||||
*/
|
||||
if (strlen(path) == 0) {
|
||||
sysio->error.symlink = Sysio::SYMLINK_ERR_EXISTS;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* path refers to any of our sub file systems */
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
if (fs->symlink(sysio, path)) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* Keep the most meaningful error code. When using
|
||||
* stacked file systems, most child file systems will
|
||||
* eventually return 'SYMLINK_ERR_NO_ENTRY' (or leave
|
||||
* the error code unchanged). If any of those file
|
||||
* systems has anything more interesting to tell,
|
||||
* return this information.
|
||||
*/
|
||||
if (sysio->error.symlink != Sysio::SYMLINK_ERR_NO_ENTRY)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* none of our file systems could create the symlink */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
path = _sub_path(path);
|
||||
|
||||
sysio->error.mkdir = Sysio::MKDIR_ERR_NO_ENTRY;
|
||||
|
||||
/* path does not match directory name */
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Prevent mkdir of path that equals directory name defined
|
||||
* via the static fstab configuration.
|
||||
*/
|
||||
if (strlen(path) == 0) {
|
||||
sysio->error.mkdir = Sysio::MKDIR_ERR_EXISTS;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* path refers to any of our sub file systems */
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next) {
|
||||
if (fs->mkdir(sysio, path)) {
|
||||
return true;
|
||||
} else {
|
||||
/*
|
||||
* Keep the most meaningful error code. When using
|
||||
* stacked file systems, most child file systems will
|
||||
* eventually return 'MKDIR_ERR_NO_ENTRY' (or leave
|
||||
* the error code unchanged). If any of those file
|
||||
* systems has anything more interesting to tell,
|
||||
* return this information.
|
||||
*/
|
||||
if (sysio->error.mkdir != Sysio::MKDIR_ERR_NO_ENTRY)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* none of our file systems could create the directory */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
char const *name() const { return "dir"; }
|
||||
|
||||
/**
|
||||
* Synchronize all file systems
|
||||
*/
|
||||
void sync()
|
||||
{
|
||||
for (File_system *fs = _first_file_system; fs; fs = fs->next)
|
||||
fs->sync();
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle) { return false; }
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__DIR_FILE_SYSTEM_H_ */
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* \brief Directory-service interface
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__DIRECTORY_SERVICE_H_
|
||||
#define _NOUX__DIRECTORY_SERVICE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <dataspace/capability.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Vfs_handle;
|
||||
|
||||
/**
|
||||
* Abstract interface to stateless directory service
|
||||
*/
|
||||
struct Directory_service
|
||||
{
|
||||
virtual Dataspace_capability dataspace(char const *path) = 0;
|
||||
virtual void release(char const *path, Dataspace_capability) = 0;
|
||||
|
||||
virtual Vfs_handle *open(Sysio *sysio, char const *path) = 0;
|
||||
|
||||
virtual bool stat(Sysio *sysio, char const *path) = 0;
|
||||
virtual bool dirent(Sysio *sysio, char const *path, off_t index) = 0;
|
||||
virtual bool unlink(Sysio *sysio, char const *path) = 0;
|
||||
virtual bool readlink(Sysio *sysio, char const *path) = 0;
|
||||
virtual bool rename(Sysio *sysio, char const *from_path,
|
||||
char const *to_path) = 0;
|
||||
virtual bool mkdir(Sysio *sysio, char const *path) = 0;
|
||||
virtual bool symlink(Sysio *sysio, char const *path) = 0;
|
||||
|
||||
/**
|
||||
* Return number of directory entries located at given path
|
||||
*/
|
||||
virtual size_t num_dirent(char const *path) = 0;
|
||||
|
||||
virtual bool is_directory(char const *path) = 0;
|
||||
|
||||
virtual char const *leaf_path(char const *path) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__DIRECTORY_SERVICE_H_ */
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* \brief Interface for operations provided by file I/O service
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__FILE_IO_SERVICE_H_
|
||||
#define _NOUX__FILE_IO_SERVICE_H_
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Vfs_handle;
|
||||
class Sysio;
|
||||
|
||||
/**
|
||||
* Abstract file-system interface
|
||||
*/
|
||||
struct File_io_service
|
||||
{
|
||||
virtual bool write(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
|
||||
virtual bool read(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
|
||||
virtual bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
|
||||
|
||||
/**
|
||||
* This method is only needed in file-systems which actually implement
|
||||
* a device and is therefore false by default.
|
||||
*/
|
||||
virtual bool ioctl(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
|
||||
|
||||
/**
|
||||
* Return true if an unblocking condition of the file is satisfied
|
||||
*
|
||||
* \param rd if true, check for data available for reading
|
||||
* \param wr if true, check for readiness for writing
|
||||
* \param ex if true, check for exceptions
|
||||
*/
|
||||
virtual bool check_unblock(Vfs_handle *vfs_handle,
|
||||
bool rd, bool wr, bool ex)
|
||||
{ return true; }
|
||||
|
||||
virtual void register_read_ready_sigh(Vfs_handle *vfs_handle,
|
||||
Signal_context_capability sigh)
|
||||
{ }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__FILE_IO_SERVICE_H_ */
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* \brief File-system related interfaces for Noux
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__FILE_SYSTEM_H_
|
||||
#define _NOUX__FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/list.h>
|
||||
#include <util/string.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <directory_service.h>
|
||||
#include <file_io_service.h>
|
||||
#include <path.h>
|
||||
#include <noux_session/sysio.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
struct File_system : Directory_service, File_io_service
|
||||
{
|
||||
/**
|
||||
* Our next sibling within the same 'Dir_file_system'
|
||||
*/
|
||||
struct File_system *next;
|
||||
|
||||
File_system() : next(0) { }
|
||||
|
||||
/**
|
||||
* Synchronize file system
|
||||
*
|
||||
* This function is only used by a Fs_file_system because such a
|
||||
* file system may employ a backend, which maintains a internal
|
||||
* cache, that needs to be flushed.
|
||||
*/
|
||||
virtual void sync() { }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__FILE_SYSTEM_H_ */
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* \brief Registry of file systems
|
||||
* \author Norman Feske
|
||||
* \date 2014-04-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__FILE_SYSTEM_REGISTRY_H_
|
||||
#define _NOUX__FILE_SYSTEM_REGISTRY_H_
|
||||
|
||||
/* Noux includes */
|
||||
#include <file_system.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class File_system_registry
|
||||
{
|
||||
public:
|
||||
|
||||
struct Entry : List<Entry>::Element
|
||||
{
|
||||
virtual File_system *create(Xml_node node) = 0;
|
||||
|
||||
virtual bool matches(Xml_node node) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
List<Entry> _list;
|
||||
|
||||
public:
|
||||
|
||||
void insert(Entry &entry)
|
||||
{
|
||||
_list.insert(&entry);
|
||||
}
|
||||
|
||||
Entry *lookup(Xml_node node)
|
||||
{
|
||||
for (Entry *e = _list.first(); e; e = e->next())
|
||||
if (e->matches(node))
|
||||
return e;
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__FILE_SYSTEM_REGISTRY_H_ */
|
@ -1,647 +0,0 @@
|
||||
/*
|
||||
* \brief Adapter from Genode 'File_system' session to Noux file system
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__FS_FILE_SYSTEM_H_
|
||||
#define _NOUX__FS_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/allocator_avl.h>
|
||||
#include <base/env.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include <file_system_session/connection.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Fs_file_system : public File_system
|
||||
{
|
||||
enum { verbose = false };
|
||||
|
||||
private:
|
||||
|
||||
Lock _lock;
|
||||
|
||||
Allocator_avl _fs_packet_alloc;
|
||||
|
||||
struct Label
|
||||
{
|
||||
enum { LABEL_MAX_LEN = 64 };
|
||||
char string[LABEL_MAX_LEN];
|
||||
|
||||
Label(Xml_node config)
|
||||
{
|
||||
string[0] = 0;
|
||||
try { config.attribute("label").value(string, sizeof(string)); }
|
||||
catch (...) { }
|
||||
}
|
||||
} _label;
|
||||
|
||||
::File_system::Connection _fs;
|
||||
|
||||
class Fs_vfs_handle : public Vfs_handle
|
||||
{
|
||||
private:
|
||||
|
||||
::File_system::File_handle const _handle;
|
||||
|
||||
public:
|
||||
|
||||
Fs_vfs_handle(File_system *fs, int status_flags,
|
||||
::File_system::File_handle handle)
|
||||
: Vfs_handle(fs, fs, status_flags), _handle(handle)
|
||||
{ }
|
||||
|
||||
~Fs_vfs_handle()
|
||||
{
|
||||
Fs_file_system *fs = static_cast<Fs_file_system *>(ds());
|
||||
fs->_fs.close(_handle);
|
||||
}
|
||||
|
||||
::File_system::File_handle file_handle() const { return _handle; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper for managing the lifetime of temporary open node handles
|
||||
*/
|
||||
struct Fs_handle_guard
|
||||
{
|
||||
::File_system::Session &_fs;
|
||||
::File_system::Node_handle _handle;
|
||||
|
||||
Fs_handle_guard(::File_system::Session &fs,
|
||||
::File_system::Node_handle handle)
|
||||
: _fs(fs), _handle(handle) { }
|
||||
|
||||
~Fs_handle_guard() { _fs.close(_handle); }
|
||||
};
|
||||
|
||||
size_t _read(::File_system::Node_handle node_handle, void *buf,
|
||||
size_t count, size_t seek_offset)
|
||||
{
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
|
||||
size_t const max_packet_size = source.bulk_buffer_size() / 2;
|
||||
count = min(max_packet_size, count);
|
||||
|
||||
::File_system::Packet_descriptor
|
||||
packet(source.alloc_packet(count),
|
||||
0,
|
||||
node_handle,
|
||||
::File_system::Packet_descriptor::READ,
|
||||
count,
|
||||
seek_offset);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
source.get_acked_packet();
|
||||
|
||||
memcpy(buf, source.packet_content(packet), count);
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
source.release_packet(packet);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t _write(::File_system::Node_handle node_handle,
|
||||
const char *buf, size_t count, size_t seek_offset)
|
||||
{
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
|
||||
size_t const max_packet_size = source.bulk_buffer_size() / 2;
|
||||
count = min(max_packet_size, count);
|
||||
|
||||
::File_system::Packet_descriptor
|
||||
packet(source.alloc_packet(count),
|
||||
0,
|
||||
node_handle,
|
||||
::File_system::Packet_descriptor::WRITE,
|
||||
count,
|
||||
seek_offset);
|
||||
|
||||
memcpy(source.packet_content(packet), buf, count);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
source.get_acked_packet();
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
source.release_packet(packet);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
* XXX read label from config
|
||||
*/
|
||||
Fs_file_system(Xml_node config)
|
||||
:
|
||||
_fs_packet_alloc(env()->heap()),
|
||||
_label(config),
|
||||
_fs(_fs_packet_alloc, 128*1024, _label.string)
|
||||
{ }
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Absolute_path dir_path(path);
|
||||
dir_path.strip_last_element();
|
||||
|
||||
Absolute_path file_name(path);
|
||||
file_name.keep_only_last_element();
|
||||
|
||||
Ram_dataspace_capability ds_cap;
|
||||
char *local_addr = 0;
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir = _fs.dir(dir_path.base(),
|
||||
false);
|
||||
Fs_handle_guard dir_guard(_fs, dir);
|
||||
|
||||
::File_system::File_handle file =
|
||||
_fs.file(dir, file_name.base() + 1,
|
||||
::File_system::READ_ONLY, false);
|
||||
Fs_handle_guard file_guard(_fs, file);
|
||||
|
||||
::File_system::Status status = _fs.status(file);
|
||||
|
||||
Ram_dataspace_capability ds_cap =
|
||||
env()->ram_session()->alloc(status.size);
|
||||
|
||||
local_addr = env()->rm_session()->attach(ds_cap);
|
||||
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
size_t const max_packet_size = source.bulk_buffer_size() / 2;
|
||||
|
||||
for (size_t seek_offset = 0; seek_offset < status.size;
|
||||
seek_offset += max_packet_size) {
|
||||
|
||||
size_t const count = min(max_packet_size, status.size - seek_offset);
|
||||
|
||||
::File_system::Packet_descriptor
|
||||
packet(source.alloc_packet(count),
|
||||
0,
|
||||
file,
|
||||
::File_system::Packet_descriptor::READ,
|
||||
count,
|
||||
seek_offset);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
source.get_acked_packet();
|
||||
|
||||
memcpy(local_addr + seek_offset, source.packet_content(packet), count);
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
source.release_packet(packet);
|
||||
}
|
||||
|
||||
env()->rm_session()->detach(local_addr);
|
||||
|
||||
return ds_cap;
|
||||
} catch(...) {
|
||||
env()->rm_session()->detach(local_addr);
|
||||
env()->ram_session()->free(ds_cap);
|
||||
return Dataspace_capability();
|
||||
}
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
env()->ram_session()->free(static_cap_cast<Ram_dataspace>(ds_cap));
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
::File_system::Status status;
|
||||
|
||||
try {
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
Fs_handle_guard node_guard(_fs, node);
|
||||
status = _fs.status(node);
|
||||
} catch (...) {
|
||||
if (verbose)
|
||||
PDBG("stat failed for path '%s'", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&sysio->stat_out.st, 0, sizeof(sysio->stat_out.st));
|
||||
|
||||
sysio->stat_out.st.size = status.size;
|
||||
|
||||
sysio->stat_out.st.mode = Sysio::STAT_MODE_FILE | 0777;
|
||||
|
||||
if (status.is_symlink())
|
||||
sysio->stat_out.st.mode = Sysio::STAT_MODE_SYMLINK | 0777;
|
||||
|
||||
if (status.is_directory())
|
||||
sysio->stat_out.st.mode = Sysio::STAT_MODE_DIRECTORY | 0777;
|
||||
|
||||
sysio->stat_out.st.uid = 0;
|
||||
sysio->stat_out.st.gid = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
::File_system::Session::Tx::Source &source = *_fs.tx();
|
||||
|
||||
if (strcmp(path, "") == 0)
|
||||
path = "/";
|
||||
|
||||
::File_system::Dir_handle dir_handle = _fs.dir(path, false);
|
||||
Fs_handle_guard dir_guard(_fs, dir_handle);
|
||||
|
||||
enum { DIRENT_SIZE = sizeof(::File_system::Directory_entry) };
|
||||
|
||||
::File_system::Packet_descriptor
|
||||
packet(source.alloc_packet(DIRENT_SIZE),
|
||||
0,
|
||||
dir_handle,
|
||||
::File_system::Packet_descriptor::READ,
|
||||
DIRENT_SIZE,
|
||||
index*DIRENT_SIZE);
|
||||
|
||||
/* pass packet to server side */
|
||||
source.submit_packet(packet);
|
||||
source.get_acked_packet();
|
||||
|
||||
/*
|
||||
* XXX check if acked packet belongs to request,
|
||||
* needed for thread safety
|
||||
*/
|
||||
|
||||
typedef ::File_system::Directory_entry Directory_entry;
|
||||
|
||||
/* copy-out payload into destination buffer */
|
||||
Directory_entry const *entry =
|
||||
(Directory_entry *)source.packet_content(packet);
|
||||
|
||||
/*
|
||||
* The default value has no meaning because the switch below
|
||||
* assigns a value in each possible branch. But it is needed to
|
||||
* keep the compiler happy.
|
||||
*/
|
||||
Sysio::Dirent_type type = Sysio::DIRENT_TYPE_END;
|
||||
|
||||
switch (entry->type) {
|
||||
case Directory_entry::TYPE_DIRECTORY: type = Sysio::DIRENT_TYPE_DIRECTORY; break;
|
||||
case Directory_entry::TYPE_FILE: type = Sysio::DIRENT_TYPE_FILE; break;
|
||||
case Directory_entry::TYPE_SYMLINK: type = Sysio::DIRENT_TYPE_SYMLINK; break;
|
||||
}
|
||||
|
||||
sysio->dirent_out.entry.type = type;
|
||||
sysio->dirent_out.entry.fileno = index + 1;
|
||||
|
||||
strncpy(sysio->dirent_out.entry.name, entry->name,
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
|
||||
source.release_packet(packet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Absolute_path dir_path(path);
|
||||
dir_path.strip_last_element();
|
||||
|
||||
Absolute_path file_name(path);
|
||||
file_name.keep_only_last_element();
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
|
||||
Fs_handle_guard dir_guard(_fs, dir);
|
||||
|
||||
_fs.unlink(dir, file_name.base() + 1);
|
||||
|
||||
} catch (...) {
|
||||
sysio->error.unlink = Sysio::UNLINK_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/*
|
||||
* Canonicalize path (i.e., path must start with '/')
|
||||
*/
|
||||
Absolute_path abs_path(path);
|
||||
abs_path.strip_last_element();
|
||||
|
||||
Absolute_path symlink_name(path);
|
||||
symlink_name.keep_only_last_element();
|
||||
|
||||
Sysio::Readlink_error error = Sysio::READLINK_ERR_NO_ENTRY;
|
||||
try {
|
||||
::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false);
|
||||
Fs_handle_guard from_dir_guard(_fs, dir_handle);
|
||||
|
||||
::File_system::Symlink_handle symlink_handle =
|
||||
_fs.symlink(dir_handle, symlink_name.base() + 1, false);
|
||||
Fs_handle_guard symlink_guard(_fs, symlink_handle);
|
||||
|
||||
sysio->readlink_out.count = _read(symlink_handle,
|
||||
sysio->readlink_out.chunk,
|
||||
min(sysio->readlink_in.bufsiz,
|
||||
sizeof(sysio->readlink_out.chunk)),
|
||||
0);
|
||||
|
||||
return true;
|
||||
} catch (...) { }
|
||||
|
||||
sysio->error.readlink = error;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
Absolute_path from_dir_path(from_path);
|
||||
from_dir_path.strip_last_element();
|
||||
|
||||
Absolute_path from_file_name(from_path);
|
||||
from_file_name.keep_only_last_element();
|
||||
|
||||
Absolute_path to_dir_path(to_path);
|
||||
to_dir_path.strip_last_element();
|
||||
|
||||
Absolute_path to_file_name(to_path);
|
||||
to_file_name.keep_only_last_element();
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle from_dir = _fs.dir(from_dir_path.base(), false);
|
||||
Fs_handle_guard from_dir_guard(_fs, from_dir);
|
||||
|
||||
::File_system::Dir_handle to_dir = _fs.dir(to_dir_path.base(), false);
|
||||
Fs_handle_guard to_dir_guard(_fs, to_dir);
|
||||
|
||||
_fs.move(from_dir, from_file_name.base() + 1,
|
||||
to_dir, to_file_name.base() + 1);
|
||||
|
||||
} catch (...) {
|
||||
sysio->error.unlink = Sysio::UNLINK_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
/*
|
||||
* Canonicalize path (i.e., path must start with '/')
|
||||
*/
|
||||
Absolute_path abs_path(path);
|
||||
|
||||
Sysio::Mkdir_error error = Sysio::MKDIR_ERR_NO_PERM;
|
||||
try {
|
||||
_fs.dir(abs_path.base(), true);
|
||||
return true;
|
||||
}
|
||||
catch (::File_system::Permission_denied) { error = Sysio::MKDIR_ERR_NO_PERM; }
|
||||
catch (::File_system::Node_already_exists) { error = Sysio::MKDIR_ERR_EXISTS; }
|
||||
catch (::File_system::Lookup_failed) { error = Sysio::MKDIR_ERR_NO_ENTRY; }
|
||||
catch (::File_system::Name_too_long) { error = Sysio::MKDIR_ERR_NAME_TOO_LONG; }
|
||||
catch (::File_system::No_space) { error = Sysio::MKDIR_ERR_NO_SPACE; }
|
||||
|
||||
sysio->error.mkdir = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/*
|
||||
* Canonicalize path (i.e., path must start with '/')
|
||||
*/
|
||||
Absolute_path abs_path(path);
|
||||
abs_path.strip_last_element();
|
||||
|
||||
Absolute_path symlink_name(path);
|
||||
symlink_name.keep_only_last_element();
|
||||
|
||||
Sysio::Symlink_error error = Sysio::SYMLINK_ERR_NO_ENTRY;
|
||||
try {
|
||||
::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false);
|
||||
Fs_handle_guard from_dir_guard(_fs, dir_handle);
|
||||
|
||||
::File_system::Symlink_handle symlink_handle =
|
||||
_fs.symlink(dir_handle, symlink_name.base() + 1, true);
|
||||
Fs_handle_guard symlink_guard(_fs, symlink_handle);
|
||||
|
||||
_write(symlink_handle, sysio->symlink_in.oldpath,
|
||||
strlen(sysio->symlink_in.oldpath) + 1, 0);
|
||||
return true;
|
||||
}
|
||||
catch (::File_system::Invalid_handle) { error = Sysio::SYMLINK_ERR_NO_ENTRY; }
|
||||
catch (::File_system::Node_already_exists) { error = Sysio::SYMLINK_ERR_EXISTS; }
|
||||
catch (::File_system::Invalid_name) { error = Sysio::SYMLINK_ERR_NAME_TOO_LONG; }
|
||||
catch (::File_system::Lookup_failed) { error = Sysio::SYMLINK_ERR_NO_ENTRY; }
|
||||
|
||||
sysio->error.symlink = error;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
if (strcmp(path, "") == 0)
|
||||
path = "/";
|
||||
|
||||
/*
|
||||
* XXX handle exceptions
|
||||
*/
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
Fs_handle_guard node_guard(_fs, node);
|
||||
|
||||
::File_system::Status status = _fs.status(node);
|
||||
|
||||
return status.size / sizeof(::File_system::Directory_entry);
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
try {
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
Fs_handle_guard node_guard(_fs, node);
|
||||
|
||||
::File_system::Status status = _fs.status(node);
|
||||
|
||||
return status.is_directory();
|
||||
}
|
||||
catch (...) { return false; }
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
/* check if node at path exists within file system */
|
||||
try {
|
||||
::File_system::Node_handle node = _fs.node(path);
|
||||
_fs.close(node);
|
||||
}
|
||||
catch (...) {
|
||||
return 0; }
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Absolute_path dir_path(path);
|
||||
dir_path.strip_last_element();
|
||||
|
||||
Absolute_path file_name(path);
|
||||
file_name.keep_only_last_element();
|
||||
|
||||
::File_system::Mode mode;
|
||||
|
||||
switch (sysio->open_in.mode & Sysio::OPEN_MODE_ACCMODE) {
|
||||
default: mode = ::File_system::STAT_ONLY; break;
|
||||
case Sysio::OPEN_MODE_RDONLY: mode = ::File_system::READ_ONLY; break;
|
||||
case Sysio::OPEN_MODE_WRONLY: mode = ::File_system::WRITE_ONLY; break;
|
||||
case Sysio::OPEN_MODE_RDWR: mode = ::File_system::READ_WRITE; break;
|
||||
}
|
||||
|
||||
bool const create = sysio->open_in.mode & Sysio::OPEN_MODE_CREATE;
|
||||
|
||||
if (create)
|
||||
if (verbose)
|
||||
PDBG("creation of file %s requested", file_name.base() + 1);
|
||||
|
||||
Sysio::Open_error error = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
|
||||
try {
|
||||
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
|
||||
Fs_handle_guard dir_guard(_fs, dir);
|
||||
|
||||
::File_system::File_handle file = _fs.file(dir, file_name.base() + 1,
|
||||
mode, create);
|
||||
|
||||
return new (env()->heap()) Fs_vfs_handle(this, sysio->open_in.mode, file);
|
||||
}
|
||||
catch (::File_system::Permission_denied) {
|
||||
error = Sysio::OPEN_ERR_NO_PERM; }
|
||||
catch (::File_system::Invalid_handle) {
|
||||
error = Sysio::OPEN_ERR_NO_PERM; }
|
||||
catch (::File_system::Lookup_failed) {
|
||||
error = Sysio::OPEN_ERR_UNACCESSIBLE; }
|
||||
catch (::File_system::Node_already_exists) {
|
||||
error = Sysio::OPEN_ERR_EXISTS; }
|
||||
|
||||
sysio->error.open = error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "fs"; }
|
||||
|
||||
void sync()
|
||||
{
|
||||
_fs.sync();
|
||||
}
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
|
||||
|
||||
size_t const count = min(sizeof(sysio->write_in.chunk),
|
||||
sysio->write_in.count);
|
||||
|
||||
sysio->write_out.count = _write(handle->file_handle(),
|
||||
sysio->write_in.chunk, count,
|
||||
handle->seek());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
|
||||
|
||||
::File_system::Status status = _fs.status(handle->file_handle());
|
||||
size_t const file_size = status.size;
|
||||
|
||||
size_t const file_bytes_left = file_size >= handle->seek()
|
||||
? file_size - handle->seek() : 0;
|
||||
|
||||
size_t const count = min(file_bytes_left,
|
||||
min(sizeof(sysio->read_out.chunk),
|
||||
sysio->read_in.count));
|
||||
|
||||
sysio->read_out.count = _read(handle->file_handle(),
|
||||
sysio->read_out.chunk,
|
||||
count, handle->seek());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
|
||||
|
||||
Sysio::Ftruncate_error error;
|
||||
|
||||
try {
|
||||
_fs.truncate(handle->file_handle(), sysio->ftruncate_in.length);
|
||||
return true;
|
||||
} catch (::File_system::Invalid_handle) {
|
||||
/* should not happen */
|
||||
error = Sysio::FTRUNCATE_ERR_NO_PERM;
|
||||
} catch (::File_system::Permission_denied) {
|
||||
error = Sysio::FTRUNCATE_ERR_NO_PERM;
|
||||
}
|
||||
|
||||
sysio->error.ftruncate = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__FS_FILE_SYSTEM_H_ */
|
@ -19,6 +19,7 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/lock.h>
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
@ -72,14 +73,14 @@ namespace Noux {
|
||||
|
||||
virtual Io_channel_backend* backend() { return 0; }
|
||||
|
||||
virtual bool write(Sysio *sysio, size_t &count) { return false; }
|
||||
virtual bool read(Sysio *sysio) { return false; }
|
||||
virtual bool fstat(Sysio *sysio) { return false; }
|
||||
virtual bool ftruncate(Sysio *sysio) { return false; }
|
||||
virtual bool fcntl(Sysio *sysio) { return false; }
|
||||
virtual bool dirent(Sysio *sysio) { return false; }
|
||||
virtual bool ioctl(Sysio *sysio) { return false; }
|
||||
virtual bool lseek(Sysio *sysio) { return false; }
|
||||
virtual bool write(Sysio *sysio, size_t &offset) { return false; }
|
||||
virtual bool read(Sysio *sysio) { return false; }
|
||||
virtual bool fstat(Sysio *sysio) { return false; }
|
||||
virtual bool ftruncate(Sysio *sysio) { return false; }
|
||||
virtual bool fcntl(Sysio *sysio) { return false; }
|
||||
virtual bool dirent(Sysio *sysio) { return false; }
|
||||
virtual bool ioctl(Sysio *sysio) { return false; }
|
||||
virtual bool lseek(Sysio *sysio) { return false; }
|
||||
|
||||
/**
|
||||
* Return true if an unblocking condition of the channel is satisfied
|
||||
|
@ -25,22 +25,21 @@
|
||||
#include <terminal_io_channel.h>
|
||||
#include <dummy_input_io_channel.h>
|
||||
#include <pipe_io_channel.h>
|
||||
#include <dir_file_system.h>
|
||||
#include <user_info.h>
|
||||
#include <io_receptor_registry.h>
|
||||
#include <destruct_queue.h>
|
||||
#include <kill_broadcaster.h>
|
||||
#include <file_system_registry.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
|
||||
/* supported file systems */
|
||||
#include <tar_file_system.h>
|
||||
#include <fs_file_system.h>
|
||||
#include <terminal_file_system.h>
|
||||
#include <null_file_system.h>
|
||||
#include <zero_file_system.h>
|
||||
#include <stdio_file_system.h>
|
||||
#include <vfs/tar_file_system.h>
|
||||
#include <vfs/fs_file_system.h>
|
||||
#include <vfs/terminal_file_system.h>
|
||||
#include <vfs/null_file_system.h>
|
||||
#include <vfs/zero_file_system.h>
|
||||
#include <vfs/block_file_system.h>
|
||||
#include <random_file_system.h>
|
||||
#include <block_file_system.h>
|
||||
#include <stdio_file_system.h>
|
||||
|
||||
|
||||
static const bool verbose_quota = false;
|
||||
@ -164,7 +163,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
{
|
||||
size_t const count_in = _sysio->write_in.count;
|
||||
|
||||
for (size_t count = 0; count != count_in; ) {
|
||||
for (size_t offset = 0; offset != count_in; ) {
|
||||
|
||||
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->write_in.fd);
|
||||
|
||||
@ -174,20 +173,19 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
if (io->check_unblock(false, true, false)) {
|
||||
/*
|
||||
* 'io->write' is expected to update
|
||||
* '_sysio->write_out.count' and 'count'
|
||||
* '_sysio->write_out.count' and 'offset'
|
||||
*/
|
||||
result = io->write(_sysio, count);
|
||||
result = io->write(_sysio, offset);
|
||||
if (result == false)
|
||||
break;
|
||||
} else {
|
||||
if (result == false) {
|
||||
/* nothing was written yet */
|
||||
_sysio->error.write = Sysio::WRITE_ERR_INTERRUPT;
|
||||
_sysio->error.write = Vfs::File_io_service::WRITE_ERR_INTERRUPT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -201,7 +199,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
if (io->check_unblock(true, false, false))
|
||||
result = io->read(_sysio);
|
||||
else
|
||||
_sysio->error.read = Sysio::READ_ERR_INTERRUPT;
|
||||
_sysio->error.read = Vfs::File_io_service::READ_ERR_INTERRUPT;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -215,7 +213,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
if (io->check_unblock(false, true, false))
|
||||
result = io->ftruncate(_sysio);
|
||||
else
|
||||
_sysio->error.ftruncate = Sysio::FTRUNCATE_ERR_INTERRUPT;
|
||||
_sysio->error.ftruncate = Vfs::File_io_service::FTRUNCATE_ERR_INTERRUPT;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -230,9 +228,12 @@ 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);
|
||||
|
||||
result = root_dir()->stat(_sysio, _sysio->stat_in.path);
|
||||
_sysio->error.stat = root_dir()->stat(_sysio->stat_in.path,
|
||||
_sysio->stat_out.st);
|
||||
|
||||
/**
|
||||
result = (_sysio->error.stat == Vfs::Directory_service::STAT_OK);
|
||||
|
||||
/*
|
||||
* Instead of using the uid/gid given by the actual file system
|
||||
* we use the ones specificed in the config.
|
||||
*/
|
||||
@ -285,7 +286,10 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
|
||||
case SYSCALL_OPEN:
|
||||
{
|
||||
Vfs_handle *vfs_handle = root_dir()->open(_sysio, _sysio->open_in.path);
|
||||
Vfs::Vfs_handle *vfs_handle = 0;
|
||||
_sysio->error.open = root_dir()->open(_sysio->open_in.path,
|
||||
_sysio->open_in.mode,
|
||||
&vfs_handle);
|
||||
if (!vfs_handle)
|
||||
break;
|
||||
|
||||
@ -297,7 +301,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
* path because path operations always refer to the global
|
||||
* root.
|
||||
*/
|
||||
if (vfs_handle->ds() == root_dir())
|
||||
if (&vfs_handle->ds() == root_dir())
|
||||
leaf_path = _sysio->open_in.path;
|
||||
|
||||
Shared_pointer<Io_channel>
|
||||
@ -678,29 +682,43 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
|
||||
case SYSCALL_UNLINK:
|
||||
|
||||
result = root_dir()->unlink(_sysio, _sysio->unlink_in.path);
|
||||
_sysio->error.unlink = root_dir()->unlink(_sysio->unlink_in.path);
|
||||
|
||||
result = (_sysio->error.unlink == Vfs::Directory_service::UNLINK_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_READLINK:
|
||||
|
||||
result = root_dir()->readlink(_sysio, _sysio->readlink_in.path);
|
||||
break;
|
||||
_sysio->error.readlink = root_dir()->readlink(_sysio->readlink_in.path,
|
||||
_sysio->readlink_out.chunk,
|
||||
min(_sysio->readlink_in.bufsiz,
|
||||
sizeof(_sysio->readlink_out.chunk)),
|
||||
_sysio->readlink_out.count);
|
||||
|
||||
result = (_sysio->error.readlink == Vfs::Directory_service::READLINK_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_RENAME:
|
||||
|
||||
result = root_dir()->rename(_sysio, _sysio->rename_in.from_path,
|
||||
_sysio->rename_in.to_path);
|
||||
_sysio->error.rename = root_dir()->rename(_sysio->rename_in.from_path,
|
||||
_sysio->rename_in.to_path);
|
||||
|
||||
result = (_sysio->error.rename == Vfs::Directory_service::RENAME_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_MKDIR:
|
||||
|
||||
result = root_dir()->mkdir(_sysio, _sysio->mkdir_in.path);
|
||||
_sysio->error.mkdir = root_dir()->mkdir(_sysio->mkdir_in.path, 0);
|
||||
|
||||
result = (_sysio->error.mkdir == Vfs::Directory_service::MKDIR_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_SYMLINK:
|
||||
|
||||
result = root_dir()->symlink(_sysio, _sysio->symlink_in.newpath);
|
||||
_sysio->error.symlink = root_dir()->symlink(_sysio->symlink_in.oldpath,
|
||||
_sysio->symlink_in.newpath);
|
||||
|
||||
result = (_sysio->error.symlink == Vfs::Directory_service::SYMLINK_OK);
|
||||
break;
|
||||
|
||||
case SYSCALL_USERINFO:
|
||||
@ -845,7 +863,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
}
|
||||
|
||||
catch (Invalid_fd) {
|
||||
_sysio->error.general = Sysio::ERR_FD_INVALID;
|
||||
_sysio->error.general = Vfs::Directory_service::ERR_FD_INVALID;
|
||||
PERR("Invalid file descriptor"); }
|
||||
|
||||
catch (...) { PERR("Unexpected exception"); }
|
||||
@ -1004,20 +1022,56 @@ void *operator new (Genode::size_t size) {
|
||||
return Genode::env()->heap()->alloc(size); }
|
||||
|
||||
|
||||
template <typename FILE_SYSTEM>
|
||||
struct File_system_factory : Noux::File_system_registry::Entry
|
||||
{
|
||||
Noux::File_system *create(Genode::Xml_node node) override
|
||||
{
|
||||
return new FILE_SYSTEM(node);
|
||||
}
|
||||
|
||||
bool matches(Genode::Xml_node node) override
|
||||
{
|
||||
char buf[100];
|
||||
node.type_name(buf, sizeof(buf));
|
||||
return node.has_type(FILE_SYSTEM::name());
|
||||
}
|
||||
class File_system_factory : public Vfs::File_system_factory
|
||||
{
|
||||
public:
|
||||
|
||||
struct Entry_base : Genode::List<Entry_base>::Element
|
||||
{
|
||||
char const * const name;
|
||||
|
||||
Entry_base(char const *name) : name(name) { }
|
||||
|
||||
virtual Vfs::File_system *create(Genode::Xml_node node) = 0;
|
||||
|
||||
bool matches(Genode::Xml_node node) const
|
||||
{
|
||||
return node.has_type(name);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename FILE_SYSTEM>
|
||||
struct Entry : Entry_base
|
||||
{
|
||||
Entry(char const *name) : Entry_base(name) { }
|
||||
|
||||
Vfs::File_system *create(Genode::Xml_node node) override
|
||||
{
|
||||
return new FILE_SYSTEM(node);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Genode::List<Entry_base> _list;
|
||||
|
||||
public:
|
||||
|
||||
template <typename FS>
|
||||
void add_fs_type()
|
||||
{
|
||||
_list.insert(new Entry<FS>(FS::name()));
|
||||
}
|
||||
|
||||
Vfs::File_system *create(Genode::Xml_node node) override
|
||||
{
|
||||
for (Entry_base *e = _list.first(); e; e = e->next())
|
||||
if (e->matches(node))
|
||||
return e->create(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1046,19 +1100,19 @@ int main(int argc, char **argv)
|
||||
} catch (Xml_node::Nonexistent_attribute) { }
|
||||
|
||||
/* register file systems */
|
||||
static File_system_registry fs_registry;
|
||||
fs_registry.insert(*new File_system_factory<Tar_file_system>());
|
||||
fs_registry.insert(*new File_system_factory<Fs_file_system>());
|
||||
fs_registry.insert(*new File_system_factory<Terminal_file_system>());
|
||||
fs_registry.insert(*new File_system_factory<Null_file_system>());
|
||||
fs_registry.insert(*new File_system_factory<Zero_file_system>());
|
||||
fs_registry.insert(*new File_system_factory<Stdio_file_system>());
|
||||
fs_registry.insert(*new File_system_factory<Random_file_system>());
|
||||
fs_registry.insert(*new File_system_factory<Block_file_system>());
|
||||
static File_system_factory fs_factory;
|
||||
fs_factory.add_fs_type<Vfs::Tar_file_system>();
|
||||
fs_factory.add_fs_type<Vfs::Fs_file_system>();
|
||||
fs_factory.add_fs_type<Vfs::Terminal_file_system>();
|
||||
fs_factory.add_fs_type<Vfs::Null_file_system>();
|
||||
fs_factory.add_fs_type<Vfs::Zero_file_system>();
|
||||
fs_factory.add_fs_type<Vfs::Block_file_system>();
|
||||
fs_factory.add_fs_type<Stdio_file_system>();
|
||||
fs_factory.add_fs_type<Random_file_system>();
|
||||
|
||||
/* initialize virtual file system */
|
||||
static Dir_file_system
|
||||
root_dir(config()->xml_node().sub_node("fstab"), fs_registry);
|
||||
static Vfs::Dir_file_system
|
||||
root_dir(config()->xml_node().sub_node("fstab"), fs_factory);
|
||||
|
||||
/* set user information */
|
||||
try {
|
||||
|
@ -83,9 +83,9 @@ namespace Noux {
|
||||
|
||||
switch (errno) {
|
||||
/* case EAGAIN: sysio->error.read = Sysio::READ_ERR_AGAIN; break; */
|
||||
case EWOULDBLOCK: sysio->error.read = Sysio::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio->error.read = Sysio::READ_ERR_INVALID; break;
|
||||
case EIO: sysio->error.read = Sysio::READ_ERR_IO; break;
|
||||
case EWOULDBLOCK: sysio->error.read = Vfs::File_io_service::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio->error.read = Vfs::File_io_service::READ_ERR_INVALID; break;
|
||||
case EIO: sysio->error.read = Vfs::File_io_service::READ_ERR_IO; break;
|
||||
default:
|
||||
PDBG("unhandled errno: %d", errno);
|
||||
break;
|
||||
@ -107,9 +107,9 @@ namespace Noux {
|
||||
|
||||
switch (errno) {
|
||||
/* case EAGAIN: sysio->error.read = Sysio::READ_ERR_AGAIN; break; */
|
||||
case EWOULDBLOCK: sysio->error.read = Sysio::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio->error.read = Sysio::READ_ERR_INVALID; break;
|
||||
case EIO: sysio->error.read = Sysio::READ_ERR_IO; break;
|
||||
case EWOULDBLOCK: sysio->error.read = Vfs::File_io_service::READ_ERR_WOULD_BLOCK; break;
|
||||
case EINVAL: sysio->error.read = Vfs::File_io_service::READ_ERR_INVALID; break;
|
||||
case EIO: sysio->error.read = Vfs::File_io_service::READ_ERR_IO; break;
|
||||
default:
|
||||
PDBG("unhandled errno: %d", errno);
|
||||
break;
|
||||
@ -145,7 +145,7 @@ namespace Noux {
|
||||
|
||||
switch (sysio->ioctl_in.request) {
|
||||
|
||||
case Sysio::Ioctl_in::OP_FIONBIO: request = FIONBIO; break;
|
||||
case Vfs::File_io_service::IOCTL_OP_FIONBIO: request = FIONBIO; break;
|
||||
default:
|
||||
PDBG("invalid ioctl request: %d", sysio->ioctl_in.request);
|
||||
return false;
|
||||
|
@ -1,190 +0,0 @@
|
||||
/*
|
||||
* \brief null filesystem
|
||||
* \author Josef Soentgen
|
||||
* \date 2012-07-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__NULL_FILE_SYSTEM_H_
|
||||
#define _NOUX__NULL_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <util/string.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "file_system.h"
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Null_file_system : public File_system
|
||||
{
|
||||
private:
|
||||
|
||||
const char *_filename() { return "null"; }
|
||||
|
||||
bool _is_root(const char *path)
|
||||
{
|
||||
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
||||
}
|
||||
|
||||
bool _is_null_file(const char *path)
|
||||
{
|
||||
return (strlen(path) == (strlen(_filename()) + 1)) &&
|
||||
(strcmp(&path[1], _filename()) == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Null_file_system(Xml_node) { }
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
Sysio::Stat st = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (_is_root(path))
|
||||
st.mode = Sysio::STAT_MODE_DIRECTORY;
|
||||
else if (_is_null_file(path)) {
|
||||
st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
} else {
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
sysio->stat_out.st = st;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
if (_is_root(path)) {
|
||||
if (index == 0) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_CHARDEV;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
_filename(),
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
} else {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
if (!_is_null_file(path)) {
|
||||
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "null"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle)
|
||||
{
|
||||
sysio->write_out.count = sysio->write_in.count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
sysio->read_out.count = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__NULL_FILE_SYSTEM_H_ */
|
@ -15,11 +15,8 @@
|
||||
#define _NOUX__PATH_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/path.h>
|
||||
#include <vfs/types.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
|
||||
namespace Noux { typedef Path<Sysio::MAX_PATH_LEN> Absolute_path; }
|
||||
namespace Noux { using Vfs::Absolute_path; }
|
||||
|
||||
#endif /* _NOUX__PATH_H_ */
|
||||
|
@ -229,12 +229,12 @@ namespace Noux {
|
||||
_pipe->writer_close();
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
return wr && _pipe->any_space_avail_for_writing();
|
||||
}
|
||||
|
||||
bool write(Sysio *sysio, size_t &count)
|
||||
bool write(Sysio *sysio, size_t &offset) override
|
||||
{
|
||||
/*
|
||||
* If the write operation is larger than the space available in
|
||||
@ -246,18 +246,19 @@ namespace Noux {
|
||||
*/
|
||||
|
||||
/* dimension the pipe write operation to the not yet written data */
|
||||
size_t curr_count = _pipe->write(sysio->write_in.chunk + count,
|
||||
sysio->write_in.count - count);
|
||||
count += curr_count;
|
||||
size_t curr_count = _pipe->write(sysio->write_in.chunk + offset,
|
||||
sysio->write_in.count - offset);
|
||||
offset += curr_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio)
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
sysio->fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************
|
||||
** Signal_dispatcher_base interface **
|
||||
**************************************/
|
||||
@ -265,7 +266,7 @@ namespace Noux {
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new STDIN input
|
||||
*/
|
||||
void dispatch(unsigned)
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
@ -293,7 +294,7 @@ namespace Noux {
|
||||
_pipe->reader_close();
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
/* unblock if the writer has already closed its pipe end */
|
||||
if (_pipe->writer_is_gone())
|
||||
@ -302,7 +303,7 @@ namespace Noux {
|
||||
return (rd && _pipe->data_avail_for_reading());
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio)
|
||||
bool read(Sysio *sysio) override
|
||||
{
|
||||
size_t const max_count =
|
||||
min(sysio->read_in.count,
|
||||
@ -314,7 +315,7 @@ namespace Noux {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio)
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
sysio->fstat_out.st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
return true;
|
||||
@ -327,7 +328,7 @@ namespace Noux {
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new STDIN input
|
||||
*/
|
||||
void dispatch(unsigned)
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "file_system.h"
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2010, 2012
|
||||
@ -229,154 +229,19 @@ namespace Noux {
|
||||
};
|
||||
|
||||
|
||||
class Random_file_system : public File_system
|
||||
class Random_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
Arc4random *_arc4random;
|
||||
|
||||
const char *_filename() { return "urandom"; }
|
||||
|
||||
bool _is_root(const char *path)
|
||||
{
|
||||
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
||||
}
|
||||
|
||||
bool _is_device_random_file(const char *path)
|
||||
{
|
||||
return (strlen(path) == (strlen(_filename()) + 1)) &&
|
||||
(strcmp(&path[1], _filename()) == 0);
|
||||
}
|
||||
Arc4random _arc4random;
|
||||
|
||||
public:
|
||||
|
||||
Random_file_system(Xml_node node)
|
||||
{
|
||||
void *bytes = 0;
|
||||
size_t nbytes = 0;
|
||||
_arc4random = new (env()->heap()) Arc4random(bytes, nbytes);
|
||||
}
|
||||
|
||||
~Random_file_system()
|
||||
{
|
||||
destroy(env()->heap(), _arc4random);
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
Sysio::Stat st = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (_is_root(path))
|
||||
st.mode = Sysio::STAT_MODE_DIRECTORY;
|
||||
else if (_is_device_random_file(path)) {
|
||||
st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
} else {
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
sysio->stat_out.st = st;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
if (_is_root(path)) {
|
||||
if (index == 0) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_CHARDEV;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
_filename(),
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
} else {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
if (!_is_device_random_file(path)) {
|
||||
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
Random_file_system(Xml_node config)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config),
|
||||
_arc4random(0, 0)
|
||||
{ }
|
||||
|
||||
static char const *name() { return "random"; }
|
||||
|
||||
@ -385,24 +250,26 @@ namespace Noux {
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle)
|
||||
Write_result write(Vfs::Vfs_handle *, char const *, size_t buf_size, size_t &out_count) override
|
||||
{
|
||||
sysio->write_out.count = sysio->write_in.count;
|
||||
out_count = buf_size;
|
||||
|
||||
return true;
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
Read_result read(Vfs::Vfs_handle *vfs_handle, char *dst, size_t count,
|
||||
size_t &out_count) override
|
||||
{
|
||||
size_t nbytes = sysio->read_in.count;
|
||||
_arc4random.get(dst, count);
|
||||
out_count = count;
|
||||
|
||||
_arc4random->get(sysio->read_out.chunk, nbytes);
|
||||
sysio->read_out.count = nbytes;
|
||||
|
||||
return true;
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; }
|
||||
Ftruncate_result ftruncate(Vfs::Vfs_handle *, size_t) override
|
||||
{
|
||||
return FTRUNCATE_OK;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -17,167 +17,30 @@
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <util/string.h>
|
||||
#include <vfs/single_file_system.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "file_system.h"
|
||||
#include "terminal_connection.h"
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Stdio_file_system : public File_system
|
||||
class Stdio_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
enum { FILENAME_MAX_LEN = 64 };
|
||||
char _filename[FILENAME_MAX_LEN];
|
||||
|
||||
Terminal::Session_client *_terminal;
|
||||
bool _echo;
|
||||
|
||||
|
||||
bool _is_root(const char *path)
|
||||
{
|
||||
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
||||
}
|
||||
|
||||
bool _is_stdio_file(const char *path)
|
||||
{
|
||||
return (strlen(path) == (strlen(_filename) + 1)) &&
|
||||
(strcmp(&path[1], _filename) == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Stdio_file_system(Xml_node config,
|
||||
Terminal::Session_client *terminal = terminal())
|
||||
Stdio_file_system(Xml_node config)
|
||||
:
|
||||
_terminal(terminal),
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config),
|
||||
_terminal(terminal()),
|
||||
_echo(true)
|
||||
{
|
||||
_filename[0] = '\0';
|
||||
|
||||
try { config.attribute("name").value(_filename, sizeof(_filename)); }
|
||||
catch (...) { }
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
Sysio::Stat st = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (_is_root(path))
|
||||
st.mode = Sysio::STAT_MODE_DIRECTORY;
|
||||
else if (_is_stdio_file(path)) {
|
||||
st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
} else {
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
sysio->stat_out.st = st;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
if (_is_root(path)) {
|
||||
if (index == 0) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_CHARDEV;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
_filename,
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
} else {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
if (!_is_stdio_file(path)) {
|
||||
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
{ }
|
||||
|
||||
static char const *name() { return "stdio"; }
|
||||
|
||||
@ -186,55 +49,53 @@ namespace Noux {
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle)
|
||||
Write_result write(Vfs::Vfs_handle *, char const *buf, size_t buf_size,
|
||||
size_t &out_count) override
|
||||
{
|
||||
sysio->write_out.count = _terminal->write(sysio->write_in.chunk, sysio->write_in.count);
|
||||
out_count = _terminal->write(buf, buf_size);
|
||||
|
||||
return true;
|
||||
return WRITE_OK;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
Read_result read(Vfs::Vfs_handle *, char *dst, size_t count, size_t &out_count) override
|
||||
{
|
||||
sysio->read_out.count = _terminal->read(sysio->read_out.chunk, sysio->read_in.count);
|
||||
out_count = _terminal->read(dst, count);
|
||||
|
||||
if (_echo)
|
||||
_terminal->write(sysio->read_out.chunk, sysio->read_in.count);
|
||||
_terminal->write(dst, count);
|
||||
|
||||
return true;
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; }
|
||||
|
||||
bool ioctl(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
Ftruncate_result ftruncate(Vfs::Vfs_handle *, size_t) override
|
||||
{
|
||||
switch (sysio->ioctl_in.request) {
|
||||
return FTRUNCATE_OK;
|
||||
}
|
||||
|
||||
case Sysio::Ioctl_in::OP_TIOCSETAF:
|
||||
Ioctl_result ioctl(Vfs::Vfs_handle *vfs_handle, Ioctl_opcode opcode,
|
||||
Ioctl_arg arg, Ioctl_out &out) override
|
||||
{
|
||||
switch (opcode) {
|
||||
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAF:
|
||||
{
|
||||
if (sysio->ioctl_in.argp & (Sysio::Ioctl_in::VAL_ECHO)) {
|
||||
_echo = true;
|
||||
}
|
||||
else {
|
||||
_echo = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
_echo = (arg & (Vfs::File_io_service::IOCTL_VAL_ECHO));
|
||||
return IOCTL_OK;
|
||||
}
|
||||
|
||||
case Sysio::Ioctl_in::OP_TIOCSETAW:
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAW:
|
||||
{
|
||||
PDBG("OP_TIOCSETAW not implemented");
|
||||
return false;
|
||||
return IOCTL_ERR_INVALID;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
PDBG("invalid ioctl(request=0x%x), %d", sysio->ioctl_in.request,
|
||||
Sysio::Ioctl_in::OP_TIOCSETAW);
|
||||
PDBG("invalid ioctl(request=0x%x)", opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return IOCTL_ERR_INVALID;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,651 +0,0 @@
|
||||
/*
|
||||
* \brief TAR file system
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__TAR_FILE_SYSTEM_H_
|
||||
#define _NOUX__TAR_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/lock.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <dataspace/client.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include <file_system.h>
|
||||
#include <vfs_handle.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Tar_file_system : public File_system
|
||||
{
|
||||
enum { verbose = false };
|
||||
|
||||
Lock _lock;
|
||||
|
||||
struct Rom_name
|
||||
{
|
||||
enum { ROM_NAME_MAX_LEN = 64 };
|
||||
char name[ROM_NAME_MAX_LEN];
|
||||
|
||||
Rom_name(Xml_node config) {
|
||||
config.attribute("name").value(name, sizeof(name));
|
||||
}
|
||||
} _rom_name;
|
||||
|
||||
Rom_connection _rom;
|
||||
|
||||
char *_tar_base;
|
||||
size_t _tar_size;
|
||||
|
||||
class Record
|
||||
{
|
||||
private:
|
||||
|
||||
char _name[100];
|
||||
char _mode[8];
|
||||
char _uid[8];
|
||||
char _gid[8];
|
||||
char _size[12];
|
||||
char _mtime[12];
|
||||
char _checksum[8];
|
||||
char _type[1];
|
||||
char _linked_name[100];
|
||||
|
||||
/**
|
||||
* Convert ASCII-encoded octal number to unsigned value
|
||||
*/
|
||||
template <typename T>
|
||||
unsigned long _read(T const &field) const
|
||||
{
|
||||
/*
|
||||
* Copy-out ASCII string to temporary buffer that is
|
||||
* large enough to host an additional zero.
|
||||
*/
|
||||
char buf[sizeof(field) + 1];
|
||||
strncpy(buf, field, sizeof(buf));
|
||||
|
||||
unsigned long value = 0;
|
||||
ascii_to(buf, &value, 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* length of on data block in tar */
|
||||
enum { BLOCK_LEN = 512 };
|
||||
|
||||
/* record type values */
|
||||
enum { TYPE_FILE = 0, TYPE_HARDLINK = 1,
|
||||
TYPE_SYMLINK = 2, TYPE_DIR = 5 };
|
||||
|
||||
size_t size() const { return _read(_size); }
|
||||
unsigned uid() const { return _read(_uid); }
|
||||
unsigned gid() const { return _read(_gid); }
|
||||
unsigned mode() const { return _read(_mode); }
|
||||
unsigned type() const { return _read(_type); }
|
||||
char const *name() const { return _name; }
|
||||
char const *linked_name() const { return _linked_name; }
|
||||
|
||||
void *data() const { return (char *)this + BLOCK_LEN; }
|
||||
};
|
||||
|
||||
|
||||
class Tar_vfs_handle : public Vfs_handle
|
||||
{
|
||||
private:
|
||||
|
||||
Record const *_record;
|
||||
|
||||
public:
|
||||
|
||||
Tar_vfs_handle(File_system *fs, int status_flags, Record const *record)
|
||||
: Vfs_handle(fs, fs, status_flags), _record(record)
|
||||
{ }
|
||||
|
||||
Record const *record() const { return _record; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* path element token
|
||||
*/
|
||||
|
||||
struct Scanner_policy_path_element
|
||||
{
|
||||
static bool identifier_char(char c, unsigned /* i */)
|
||||
{
|
||||
return (c != '/') && (c != 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef Genode::Token<Scanner_policy_path_element> Path_element_token;
|
||||
|
||||
|
||||
struct Node : List<Node>, List<Node>::Element
|
||||
{
|
||||
char const *name;
|
||||
Record const *record;
|
||||
|
||||
Node(char const *name, Record const *record) : name(name), record(record) { }
|
||||
|
||||
Node *lookup(char const *name)
|
||||
{
|
||||
Absolute_path lookup_path(name);
|
||||
|
||||
if (verbose)
|
||||
PDBG("lookup_path = %s", lookup_path.base());
|
||||
|
||||
Node *parent_node = this;
|
||||
Node *child_node;
|
||||
|
||||
Path_element_token t(lookup_path.base());
|
||||
|
||||
while (t) {
|
||||
|
||||
if (t.type() != Path_element_token::IDENT) {
|
||||
t = t.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
char path_element[Sysio::MAX_PATH_LEN];
|
||||
|
||||
t.string(path_element, sizeof(path_element));
|
||||
|
||||
if (verbose)
|
||||
PDBG("path_element = %s", path_element);
|
||||
|
||||
for (child_node = parent_node->first(); child_node; child_node = child_node->next()) {
|
||||
if (verbose)
|
||||
PDBG("comparing with node %s", child_node->name);
|
||||
if (strcmp(child_node->name, path_element) == 0) {
|
||||
if (verbose)
|
||||
PDBG("found matching child node");
|
||||
parent_node = child_node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!child_node)
|
||||
return 0;
|
||||
|
||||
t = t.next();
|
||||
}
|
||||
|
||||
return parent_node;
|
||||
}
|
||||
|
||||
|
||||
Node *lookup_child(int index)
|
||||
{
|
||||
for (Node *child_node = first(); child_node; child_node = child_node->next(), index--) {
|
||||
if (index == 0)
|
||||
return child_node;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t num_dirent()
|
||||
{
|
||||
size_t count = 0;
|
||||
for (Node *child_node = first(); child_node; child_node = child_node->next(), count++) ;
|
||||
return count;
|
||||
}
|
||||
|
||||
} _root_node;
|
||||
|
||||
|
||||
/*
|
||||
* Create a Node for a tar record and insert it into the node list
|
||||
*/
|
||||
class Add_node_action
|
||||
{
|
||||
private:
|
||||
|
||||
Node &_root_node;
|
||||
|
||||
public:
|
||||
|
||||
Add_node_action(Node &root_node) : _root_node(root_node) { }
|
||||
|
||||
void operator()(Record const *record)
|
||||
{
|
||||
Absolute_path current_path(record->name());
|
||||
|
||||
if (verbose)
|
||||
PDBG("current_path = %s", current_path.base());
|
||||
|
||||
char path_element[Sysio::MAX_PATH_LEN];
|
||||
|
||||
Path_element_token t(current_path.base());
|
||||
|
||||
Node *parent_node = &_root_node;
|
||||
Node *child_node;
|
||||
|
||||
while(t) {
|
||||
|
||||
if (t.type() != Path_element_token::IDENT) {
|
||||
t = t.next();
|
||||
continue;
|
||||
}
|
||||
|
||||
Absolute_path remaining_path(t.start());
|
||||
|
||||
t.string(path_element, sizeof(path_element));
|
||||
|
||||
for (child_node = parent_node->first(); child_node; child_node = child_node->next()) {
|
||||
if (strcmp(child_node->name, path_element) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (child_node) {
|
||||
|
||||
if (verbose)
|
||||
PDBG("found node for %s", path_element);
|
||||
|
||||
if (remaining_path.has_single_element()) {
|
||||
/* Found a node for the record to be inserted.
|
||||
* This is usually a directory node without
|
||||
* record. */
|
||||
child_node->record = record;
|
||||
}
|
||||
} else {
|
||||
if (remaining_path.has_single_element()) {
|
||||
|
||||
if (verbose)
|
||||
PDBG("creating node for %s", path_element);
|
||||
|
||||
/*
|
||||
* TODO: find 'path_element' in 'record->name'
|
||||
* and use the location in the record as name
|
||||
* pointer to save some memory
|
||||
*/
|
||||
size_t name_size = strlen(path_element) + 1;
|
||||
char *name = (char*)env()->heap()->alloc(name_size);
|
||||
strncpy(name, path_element, name_size);
|
||||
child_node = new (env()->heap()) Node(name, record);
|
||||
} else {
|
||||
|
||||
if (verbose)
|
||||
PDBG("creating node without record for %s", path_element);
|
||||
|
||||
/* create a directory node without record */
|
||||
size_t name_size = strlen(path_element) + 1;
|
||||
char *name = (char*)env()->heap()->alloc(name_size);
|
||||
strncpy(name, path_element, name_size);
|
||||
child_node = new (env()->heap()) Node(name, 0);
|
||||
}
|
||||
parent_node->insert(child_node);
|
||||
}
|
||||
|
||||
parent_node = child_node;
|
||||
t = t.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Tar_record_action>
|
||||
void _for_each_tar_record_do(Tar_record_action tar_record_action)
|
||||
{
|
||||
/* measure size of archive in blocks */
|
||||
unsigned block_id = 0, block_cnt = _tar_size/Record::BLOCK_LEN;
|
||||
|
||||
/* scan metablocks of archive */
|
||||
while (block_id < block_cnt) {
|
||||
|
||||
Record *record = (Record *)(_tar_base + block_id*Record::BLOCK_LEN);
|
||||
|
||||
tar_record_action(record);
|
||||
|
||||
size_t file_size = record->size();
|
||||
|
||||
/* some datablocks */ /* one metablock */
|
||||
block_id = block_id + (file_size / Record::BLOCK_LEN) + 1;
|
||||
|
||||
/* round up */
|
||||
if (file_size % Record::BLOCK_LEN != 0) block_id++;
|
||||
|
||||
/* check for end of tar archive */
|
||||
if (block_id*Record::BLOCK_LEN >= _tar_size)
|
||||
break;
|
||||
|
||||
/* lookout for empty eof-blocks */
|
||||
if (*(_tar_base + (block_id*Record::BLOCK_LEN)) == 0x00)
|
||||
if (*(_tar_base + (block_id*Record::BLOCK_LEN + 1)) == 0x00)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Num_dirent_cache
|
||||
{
|
||||
Lock lock;
|
||||
Node &root_node;
|
||||
bool valid; /* true after first lookup */
|
||||
char key[256]; /* key used for lookup */
|
||||
size_t cached_num_dirent; /* cached value */
|
||||
|
||||
Num_dirent_cache(Node &root_node)
|
||||
: root_node(root_node), valid(false), cached_num_dirent(0) { }
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
Lock::Guard guard(lock);
|
||||
|
||||
/* check for cache miss */
|
||||
if (!valid || strcmp(path, key) != 0) {
|
||||
Node *node = root_node.lookup(path);
|
||||
if (!node)
|
||||
return 0;
|
||||
strncpy(key, path, sizeof(key));
|
||||
cached_num_dirent = node->num_dirent();
|
||||
valid = true;
|
||||
}
|
||||
return cached_num_dirent;
|
||||
}
|
||||
} _cached_num_dirent;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Tar_file_system(Xml_node config)
|
||||
:
|
||||
_rom_name(config), _rom(_rom_name.name),
|
||||
_tar_base(env()->rm_session()->attach(_rom.dataspace())),
|
||||
_tar_size(Dataspace_client(_rom.dataspace()).size()),
|
||||
_root_node("", 0),
|
||||
_cached_num_dirent(_root_node)
|
||||
{
|
||||
PINF("tar archive '%s' local at %p, size is %zd",
|
||||
_rom_name.name, _tar_base, _tar_size);
|
||||
|
||||
_for_each_tar_record_do(Add_node_action(_root_node));
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
/*
|
||||
* Walk hardlinks until we reach a file
|
||||
*/
|
||||
Record const *record = 0;
|
||||
for (;;) {
|
||||
Node *node = _root_node.lookup(path);
|
||||
|
||||
if (!node)
|
||||
return Dataspace_capability();
|
||||
|
||||
record = node->record;
|
||||
|
||||
if (record) {
|
||||
if (record->type() == Record::TYPE_HARDLINK) {
|
||||
path = record->linked_name();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record->type() == Record::TYPE_FILE)
|
||||
break;
|
||||
|
||||
PERR("TAR record \"%s\" has unsupported type %d",
|
||||
record->name(), record->type());
|
||||
}
|
||||
|
||||
PERR("TAR record \"%s\" has unsupported type %d",
|
||||
path, Record::TYPE_DIR);
|
||||
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
try {
|
||||
Ram_dataspace_capability ds_cap =
|
||||
env()->ram_session()->alloc(record->size());
|
||||
|
||||
void *local_addr = env()->rm_session()->attach(ds_cap);
|
||||
memcpy(local_addr, record->data(), record->size());
|
||||
env()->rm_session()->detach(local_addr);
|
||||
|
||||
return ds_cap;
|
||||
}
|
||||
catch (...) { PDBG("Could not create new dataspace"); }
|
||||
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *, Dataspace_capability ds_cap)
|
||||
{
|
||||
env()->ram_session()->free(static_cap_cast<Ram_dataspace>(ds_cap));
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
if (verbose)
|
||||
PDBG("path = %s", path);
|
||||
|
||||
Node const *node = 0;
|
||||
Record const *record = 0;
|
||||
|
||||
/*
|
||||
* Walk hardlinks until we reach a file
|
||||
*/
|
||||
for (;;) {
|
||||
node = _root_node.lookup(path);
|
||||
|
||||
if (!node) {
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
record = node->record;
|
||||
|
||||
if (record) {
|
||||
if (record->type() == Record::TYPE_HARDLINK) {
|
||||
path = record->linked_name();
|
||||
continue;
|
||||
} else
|
||||
break;
|
||||
} else {
|
||||
if (verbose)
|
||||
PDBG("found a virtual directoty node");
|
||||
memset(&sysio->stat_out.st, 0, sizeof(sysio->stat_out.st));
|
||||
sysio->stat_out.st.mode = Sysio::STAT_MODE_DIRECTORY;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert TAR record modes to stat modes */
|
||||
unsigned mode = record->mode();
|
||||
switch (record->type()) {
|
||||
case Record::TYPE_FILE: mode |= Sysio::STAT_MODE_FILE; break;
|
||||
case Record::TYPE_SYMLINK: mode |= Sysio::STAT_MODE_SYMLINK; break;
|
||||
case Record::TYPE_DIR: mode |= Sysio::STAT_MODE_DIRECTORY; break;
|
||||
|
||||
default:
|
||||
if (verbose)
|
||||
PDBG("unhandled record type %d", record->type());
|
||||
}
|
||||
|
||||
memset(&sysio->stat_out.st, 0, sizeof(sysio->stat_out.st));
|
||||
sysio->stat_out.st.mode = mode;
|
||||
sysio->stat_out.st.size = record->size();
|
||||
sysio->stat_out.st.uid = record->uid();
|
||||
sysio->stat_out.st.gid = record->gid();
|
||||
sysio->stat_out.st.inode = (unsigned long)node;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Node *node = _root_node.lookup(path);
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
node = node->lookup_child(index);
|
||||
|
||||
if (!node) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
sysio->dirent_out.entry.fileno = (unsigned long)node;
|
||||
|
||||
Record const *record = node->record;
|
||||
|
||||
if (record) {
|
||||
switch (record->type()) {
|
||||
case 0: sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_FILE; break;
|
||||
case 2: sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_SYMLINK; break;
|
||||
case 5: sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_DIRECTORY; break;
|
||||
|
||||
default:
|
||||
if (verbose)
|
||||
PDBG("unhandled record type %d", record->type());
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
node->name,
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unlink(Sysio *, char const *) { return false; }
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
Node *node = _root_node.lookup(path);
|
||||
Record const *record = node ? node->record : 0;
|
||||
|
||||
if (!record || (record->type() != Record::TYPE_SYMLINK)) {
|
||||
sysio->error.readlink = Sysio::READLINK_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t const count = min(sysio->readlink_in.bufsiz,
|
||||
min(sizeof(sysio->readlink_out.chunk),
|
||||
(size_t)100));
|
||||
|
||||
memcpy(sysio->readlink_out.chunk, record->linked_name(), count);
|
||||
|
||||
sysio->readlink_out.count = count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rename(Sysio *, char const *, char const *) { return false; }
|
||||
|
||||
bool mkdir(Sysio *, char const *) { return false; }
|
||||
|
||||
bool symlink(Sysio *, char const *) { return false; }
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
return _cached_num_dirent.num_dirent(path);
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
Node *node = _root_node.lookup(path);
|
||||
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
Record const *record = node->record;
|
||||
|
||||
return record ? (record->type() == Record::TYPE_DIR) : true;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
/*
|
||||
* Check if path exists within the file system. If this is the
|
||||
* case, return the whole path, which is relative to the root
|
||||
* of this file system.
|
||||
*/
|
||||
Node *node = _root_node.lookup(path);
|
||||
return node ? path : 0;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
Lock::Guard guard(_lock);
|
||||
|
||||
Node *node = _root_node.lookup(path);
|
||||
if (node)
|
||||
return new (env()->heap())
|
||||
Tar_vfs_handle(this, 0, node->record);
|
||||
|
||||
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "tar"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle)
|
||||
{
|
||||
PDBG("called\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
Tar_vfs_handle const *handle = static_cast<Tar_vfs_handle *>(vfs_handle);
|
||||
|
||||
size_t const record_size = handle->record()->size();
|
||||
|
||||
size_t const record_bytes_left = record_size >= handle->seek()
|
||||
? record_size - handle->seek() : 0;
|
||||
|
||||
size_t const count = min(record_bytes_left,
|
||||
min(sizeof(sysio->read_out.chunk),
|
||||
sysio->read_in.count));
|
||||
|
||||
char const *data = (char *)handle->record()->data() + handle->seek();
|
||||
|
||||
memcpy(sysio->read_out.chunk, data, count);
|
||||
|
||||
sysio->read_out.count = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *handle)
|
||||
{
|
||||
PDBG("called\n");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__TAR_FILE_SYSTEM_H_ */
|
@ -22,13 +22,9 @@
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "file_system.h"
|
||||
#include <vfs/file_system.h>
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
Terminal::Connection *terminal();
|
||||
|
||||
}
|
||||
namespace Noux { Terminal::Connection *terminal(); }
|
||||
|
||||
#endif /* _NOUX__TERMINAL_CONNECTION_H_ */
|
||||
|
@ -1,233 +0,0 @@
|
||||
/*
|
||||
* \brief Terminal file system
|
||||
* \author Christian Prochaska
|
||||
* \date 2012-05-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__TERMINAL_FILE_SYSTEM_H_
|
||||
#define _NOUX__TERMINAL_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/lock.h>
|
||||
#include <terminal_session/connection.h>
|
||||
#include <util/string.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "file_system.h"
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Terminal_file_system : public File_system
|
||||
{
|
||||
private:
|
||||
|
||||
Terminal::Session_client _terminal;
|
||||
|
||||
enum { FILENAME_MAX_LEN = 64 };
|
||||
char _filename[FILENAME_MAX_LEN];
|
||||
|
||||
bool _is_root(const char *path)
|
||||
{
|
||||
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
||||
}
|
||||
|
||||
bool _is_terminal_file(const char *path)
|
||||
{
|
||||
return (strlen(path) == (strlen(_filename) + 1)) &&
|
||||
(strcmp(&path[1], _filename) == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Terminal_file_system(Xml_node config)
|
||||
: _terminal(env()->parent()->session<Terminal::Session>
|
||||
("ram_quota=8192, label=\"noux(terminal_fs)\""))
|
||||
{
|
||||
_filename[0] = 0;
|
||||
try { config.attribute("name").value(_filename, sizeof(_filename)); }
|
||||
catch (...) { }
|
||||
|
||||
/*
|
||||
* Wait for connection-established signal
|
||||
*/
|
||||
|
||||
/* create signal receiver, just for the single signal */
|
||||
Signal_context sig_ctx;
|
||||
Signal_receiver sig_rec;
|
||||
Signal_context_capability sig_cap = sig_rec.manage(&sig_ctx);
|
||||
|
||||
/* register signal handler */
|
||||
_terminal.connected_sigh(sig_cap);
|
||||
|
||||
/* wati for signal */
|
||||
sig_rec.wait_for_signal();
|
||||
sig_rec.dissolve(&sig_ctx);
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
Sysio::Stat st = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (_is_root(path))
|
||||
st.mode = Sysio::STAT_MODE_DIRECTORY;
|
||||
else if (_is_terminal_file(path)) {
|
||||
st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
} else {
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
sysio->stat_out.st = st;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
if (_is_root(path)) {
|
||||
if (index == 0) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_CHARDEV;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
_filename,
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
} else {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
if (!_is_terminal_file(path)) {
|
||||
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "terminal"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle)
|
||||
{
|
||||
sysio->write_out.count = _terminal.write(sysio->write_in.chunk, sysio->write_in.count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
sysio->read_out.count = _terminal.read(sysio->read_out.chunk, sysio->read_in.count);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return true; }
|
||||
|
||||
bool check_unblock(Vfs_handle *vfs_handle, bool rd, bool wr, bool ex)
|
||||
{
|
||||
if (rd && (_terminal.avail() > 0))
|
||||
return true;
|
||||
|
||||
if (wr)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void register_read_ready_sigh(Vfs_handle *vfs_handle,
|
||||
Signal_context_capability sigh)
|
||||
{
|
||||
_terminal.read_avail_sigh(sigh);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__TERMINAL_FILE_SYSTEM_H_ */
|
@ -58,14 +58,20 @@ namespace Noux {
|
||||
|
||||
~Terminal_io_channel() { sig_rec.dissolve(this); }
|
||||
|
||||
bool write(Sysio *sysio, size_t &count)
|
||||
bool write(Sysio *sysio, size_t &offset) override
|
||||
{
|
||||
terminal.write(sysio->write_in.chunk, sysio->write_in.count);
|
||||
count = sysio->write_in.count;
|
||||
size_t const count = min(sysio->write_in.count,
|
||||
sizeof(sysio->write_in.chunk));
|
||||
|
||||
terminal.write(sysio->write_in.chunk, count);
|
||||
|
||||
sysio->write_out.count = count;
|
||||
offset = count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio)
|
||||
bool read(Sysio *sysio) override
|
||||
{
|
||||
if (type != STDIN) {
|
||||
PERR("attempt to read from terminal output channel");
|
||||
@ -114,7 +120,7 @@ namespace Noux {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fcntl(Sysio *sysio)
|
||||
bool fcntl(Sysio *sysio) override
|
||||
{
|
||||
/**
|
||||
* Actually it is "inappropiate" to use fcntl() directly on terminals
|
||||
@ -136,7 +142,7 @@ namespace Noux {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio)
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
/*
|
||||
* Supply stat values such that libc is happy. I.e., the libc
|
||||
@ -147,7 +153,7 @@ namespace Noux {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
/* never block for writing */
|
||||
if (wr) return true;
|
||||
@ -159,11 +165,11 @@ namespace Noux {
|
||||
return (rd && (type == STDIN) && !read_buffer.empty());
|
||||
}
|
||||
|
||||
bool ioctl(Sysio *sysio)
|
||||
bool ioctl(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->ioctl_in.request) {
|
||||
|
||||
case Sysio::Ioctl_in::OP_TIOCGWINSZ:
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCGWINSZ:
|
||||
{
|
||||
Terminal::Session::Size size = terminal.size();
|
||||
sysio->ioctl_out.tiocgwinsz.rows = size.lines();
|
||||
@ -171,13 +177,13 @@ namespace Noux {
|
||||
return true;
|
||||
}
|
||||
|
||||
case Sysio::Ioctl_in::OP_TIOCSETAF:
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAF:
|
||||
{
|
||||
PDBG("OP_TIOCSETAF not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
case Sysio::Ioctl_in::OP_TIOCSETAW:
|
||||
case Vfs::File_io_service::IOCTL_OP_TIOCSETAW:
|
||||
{
|
||||
PDBG("OP_TIOCSETAW not implemented");
|
||||
return false;
|
||||
@ -198,7 +204,7 @@ namespace Noux {
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new STDIN input
|
||||
*/
|
||||
void dispatch(unsigned)
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
while ((read_buffer.avail_capacity() > 0) &&
|
||||
terminal.avail()) {
|
||||
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* \brief Representation of an open file
|
||||
* \author Norman Feske
|
||||
* \date 2011-02-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__VFS_HANDLE_H_
|
||||
#define _NOUX__VFS_HANDLE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <file_io_service.h>
|
||||
#include <directory_service.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Sysio;
|
||||
|
||||
class Vfs_io_channel;
|
||||
|
||||
class Vfs_handle
|
||||
{
|
||||
private:
|
||||
|
||||
Directory_service *_ds;
|
||||
File_io_service *_fs;
|
||||
int _status_flags;
|
||||
size_t _seek;
|
||||
|
||||
friend class Vfs_io_channel; /* for modifying '_seek' */
|
||||
|
||||
static Directory_service *_pseudo_directory_service()
|
||||
{
|
||||
struct Pseudo_directory_service : public Directory_service
|
||||
{
|
||||
static bool _msg(char const *sc) {
|
||||
PERR("%s not supported by directory service", sc); return false; }
|
||||
|
||||
Dataspace_capability dataspace(char const *)
|
||||
{
|
||||
_msg("dataspace");
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *, Dataspace_capability) { }
|
||||
|
||||
bool stat(Sysio *, char const *) { return _msg("stat"); }
|
||||
Vfs_handle *open(Sysio *, char const *) { _msg("open"); return 0; }
|
||||
bool dirent(Sysio *, char const *, off_t) { return _msg("dirent"); }
|
||||
bool unlink(Sysio *, char const *) { return _msg("unlink"); }
|
||||
bool readlink(Sysio *, char const *) { return _msg("readlink"); }
|
||||
bool rename(Sysio *, char const *, char const *) { return _msg("rename"); }
|
||||
bool mkdir(Sysio *, char const *) { return _msg("mkdir"); }
|
||||
bool symlink(Sysio *, char const *) { return _msg("symlink"); }
|
||||
size_t num_dirent(char const *) { return 0; }
|
||||
bool is_directory(char const *) { return false; }
|
||||
char const *leaf_path(char const *path) { return 0; }
|
||||
};
|
||||
static Pseudo_directory_service ds;
|
||||
return &ds;
|
||||
}
|
||||
|
||||
static File_io_service *_pseudo_file_io_service()
|
||||
{
|
||||
struct Pseudo_file_io_service : public File_io_service
|
||||
{
|
||||
static bool _msg(char const *sc) {
|
||||
PERR("%s not supported by file system", sc); return false; }
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle) { return _msg("write"); }
|
||||
bool read(Sysio *sysio, Vfs_handle *handle) { return _msg("read"); }
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *handle) { return _msg("ftruncate"); }
|
||||
bool ioctl(Sysio *sysio, Vfs_handle *handle) { return _msg("ioctl"); }
|
||||
};
|
||||
static Pseudo_file_io_service fs;
|
||||
return &fs;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
enum { STATUS_RDONLY = 0, STATUS_WRONLY = 1, STATUS_RDWR = 2 };
|
||||
|
||||
Vfs_handle(Directory_service *ds, File_io_service *fs, int status_flags)
|
||||
:
|
||||
_ds(ds ? ds : _pseudo_directory_service()),
|
||||
_fs(fs ? fs : _pseudo_file_io_service()),
|
||||
_status_flags(status_flags),
|
||||
_seek(0)
|
||||
{ }
|
||||
|
||||
virtual ~Vfs_handle() { }
|
||||
|
||||
Directory_service *ds() { return _ds; }
|
||||
File_io_service *fs() { return _fs; }
|
||||
|
||||
int status_flags() { return _status_flags; }
|
||||
|
||||
size_t seek() const { return _seek; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__VFS_HANDLE_H_ */
|
@ -16,14 +16,13 @@
|
||||
|
||||
/* Noux includes */
|
||||
#include <io_channel.h>
|
||||
#include <file_system.h>
|
||||
#include <dir_file_system.h>
|
||||
#include <vfs/dir_file_system.h>
|
||||
|
||||
namespace Noux {
|
||||
|
||||
struct Vfs_io_channel : Io_channel, Signal_dispatcher_base
|
||||
{
|
||||
Vfs_handle *_fh;
|
||||
Vfs::Vfs_handle *_fh;
|
||||
|
||||
Absolute_path _path;
|
||||
Absolute_path _leaf_path;
|
||||
@ -31,12 +30,12 @@ namespace Noux {
|
||||
Signal_receiver &_sig_rec;
|
||||
|
||||
Vfs_io_channel(char const *path, char const *leaf_path,
|
||||
Dir_file_system *root_dir, Vfs_handle *vfs_handle,
|
||||
Vfs::Dir_file_system *root_dir, Vfs::Vfs_handle *vfs_handle,
|
||||
Signal_receiver &sig_rec)
|
||||
: _fh(vfs_handle), _path(path), _leaf_path(leaf_path),
|
||||
_sig_rec(sig_rec)
|
||||
{
|
||||
_fh->fs()->register_read_ready_sigh(_fh, _sig_rec.manage(this));
|
||||
_fh->fs().register_read_ready_sigh(_fh, _sig_rec.manage(this));
|
||||
}
|
||||
|
||||
~Vfs_io_channel()
|
||||
@ -45,42 +44,63 @@ namespace Noux {
|
||||
destroy(env()->heap(), _fh);
|
||||
}
|
||||
|
||||
bool write(Sysio *sysio, size_t &count)
|
||||
bool write(Sysio *sysio, size_t &offset) override
|
||||
{
|
||||
if (!_fh->fs()->write(sysio, _fh))
|
||||
return false;
|
||||
size_t out_count = 0;
|
||||
|
||||
sysio->error.write = _fh->fs().write(_fh, sysio->write_in.chunk,
|
||||
sysio->write_in.count, out_count);
|
||||
if (sysio->error.write != Vfs::File_io_service::WRITE_OK)
|
||||
return false;
|
||||
|
||||
_fh->advance_seek(out_count);
|
||||
|
||||
sysio->write_out.count = out_count;
|
||||
offset = out_count;
|
||||
|
||||
count = sysio->write_out.count;
|
||||
_fh->_seek += count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio)
|
||||
bool read(Sysio *sysio) override
|
||||
{
|
||||
if (!_fh->fs()->read(sysio, _fh))
|
||||
size_t count = min(sysio->read_in.count, sizeof(sysio->read_out.chunk));
|
||||
|
||||
size_t out_count = 0;
|
||||
|
||||
sysio->error.read = _fh->fs().read(_fh, sysio->read_out.chunk, count, out_count);
|
||||
|
||||
if (sysio->error.read != Vfs::File_io_service::READ_OK)
|
||||
return false;
|
||||
|
||||
_fh->_seek += sysio->read_out.count;
|
||||
sysio->read_out.count = out_count;
|
||||
|
||||
_fh->advance_seek(out_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fstat(Sysio *sysio)
|
||||
bool fstat(Sysio *sysio) override
|
||||
{
|
||||
/*
|
||||
* 'sysio.stat_in' is not used in '_fh->ds()->stat()',
|
||||
* 'sysio.stat_in' is not used in '_fh->ds().stat()',
|
||||
* so no 'sysio' member translation is needed here
|
||||
*/
|
||||
bool result = _fh->ds()->stat(sysio, _leaf_path.base());
|
||||
sysio->fstat_out.st = sysio->stat_out.st;
|
||||
return result;
|
||||
sysio->error.stat = _fh->ds().stat(_leaf_path.base(),
|
||||
sysio->fstat_out.st);
|
||||
|
||||
return (sysio->error.stat == Vfs::Directory_service::STAT_OK);
|
||||
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio)
|
||||
bool ftruncate(Sysio *sysio) override
|
||||
{
|
||||
return _fh->fs()->ftruncate(sysio, _fh);
|
||||
|
||||
sysio->error.ftruncate = _fh->fs().ftruncate(_fh, sysio->ftruncate_in.length);
|
||||
|
||||
return (sysio->error.ftruncate == Vfs::File_io_service::FTRUNCATE_OK);
|
||||
}
|
||||
|
||||
bool fcntl(Sysio *sysio)
|
||||
bool fcntl(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->fcntl_in.cmd) {
|
||||
|
||||
@ -103,20 +123,20 @@ namespace Noux {
|
||||
* to directories). Hence, '_path' is the absolute path of the
|
||||
* directory to inspect.
|
||||
*/
|
||||
bool dirent(Sysio *sysio)
|
||||
bool dirent(Sysio *sysio) override
|
||||
{
|
||||
/*
|
||||
* Return artificial dir entries for "." and ".."
|
||||
*/
|
||||
unsigned const index = _fh->seek() / sizeof(Sysio::Dirent);
|
||||
if (index < 2) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_DIRECTORY;
|
||||
sysio->dirent_out.entry.type = Vfs::Directory_service::DIRENT_TYPE_DIRECTORY;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
index ? ".." : ".",
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
|
||||
sysio->dirent_out.entry.fileno = 1;
|
||||
_fh->_seek += sizeof(Sysio::Dirent);
|
||||
_fh->advance_seek(sizeof(Sysio::Dirent));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -125,10 +145,11 @@ namespace Noux {
|
||||
* Align index range to zero when calling the directory service.
|
||||
*/
|
||||
|
||||
if (!_fh->ds()->dirent(sysio, _path.base(), index - 2))
|
||||
if (!_fh->ds().dirent(_path.base(), index - 2,
|
||||
sysio->dirent_out.entry))
|
||||
return false;
|
||||
|
||||
_fh->_seek += sizeof(Sysio::Dirent);
|
||||
_fh->advance_seek(sizeof(Sysio::Dirent));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -147,32 +168,36 @@ namespace Noux {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ioctl(Sysio *sysio)
|
||||
bool ioctl(Sysio *sysio) override
|
||||
{
|
||||
return _fh->fs()->ioctl(sysio, _fh);
|
||||
Vfs::File_system::Ioctl_arg arg = (Vfs::File_system::Ioctl_arg)sysio->ioctl_in.argp;
|
||||
|
||||
sysio->error.ioctl = _fh->fs().ioctl(_fh, sysio->ioctl_in.request, arg, sysio->ioctl_out);
|
||||
|
||||
return (sysio->error.ioctl == Vfs::File_io_service::IOCTL_OK);
|
||||
}
|
||||
|
||||
bool lseek(Sysio *sysio)
|
||||
bool lseek(Sysio *sysio) override
|
||||
{
|
||||
switch (sysio->lseek_in.whence) {
|
||||
case Sysio::LSEEK_SET: _fh->_seek = sysio->lseek_in.offset; break;
|
||||
case Sysio::LSEEK_CUR: _fh->_seek += sysio->lseek_in.offset; break;
|
||||
case Sysio::LSEEK_SET: _fh->seek(sysio->lseek_in.offset); break;
|
||||
case Sysio::LSEEK_CUR: _fh->advance_seek(sysio->lseek_in.offset); break;
|
||||
case Sysio::LSEEK_END:
|
||||
off_t offset = sysio->lseek_in.offset;
|
||||
sysio->fstat_in.fd = sysio->lseek_in.fd;
|
||||
_fh->_seek = size(sysio) + offset;
|
||||
_fh->seek(size(sysio) + offset);
|
||||
break;
|
||||
}
|
||||
sysio->lseek_out.offset = _fh->_seek;
|
||||
sysio->lseek_out.offset = _fh->seek();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const
|
||||
bool check_unblock(bool rd, bool wr, bool ex) const override
|
||||
{
|
||||
return _fh->fs()->check_unblock(_fh, rd, wr, ex);
|
||||
return _fh->fs().check_unblock(_fh, rd, wr, ex);
|
||||
}
|
||||
|
||||
bool path(char *path, size_t len)
|
||||
bool path(char *path, size_t len) override
|
||||
{
|
||||
strncpy(path, _path.base(), len);
|
||||
path[len - 1] = '\0';
|
||||
@ -180,6 +205,7 @@ namespace Noux {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************
|
||||
** Signal_dispatcher_base interface **
|
||||
**************************************/
|
||||
@ -187,7 +213,7 @@ namespace Noux {
|
||||
/**
|
||||
* Called by Noux main loop on the occurrence of new input
|
||||
*/
|
||||
void dispatch(unsigned)
|
||||
void dispatch(unsigned) override
|
||||
{
|
||||
Io_channel::invoke_all_notifiers();
|
||||
}
|
||||
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* \brief zero filesystem
|
||||
* \author Josef Soentgen
|
||||
* \date 2012-07-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _NOUX__ZERO_FILE_SYSTEM_H_
|
||||
#define _NOUX__ZERO_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <util/string.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <noux_session/sysio.h>
|
||||
#include "file_system.h"
|
||||
|
||||
|
||||
namespace Noux {
|
||||
|
||||
class Zero_file_system : public File_system
|
||||
{
|
||||
private:
|
||||
|
||||
const char *_filename() { return "zero"; }
|
||||
|
||||
bool _is_root(const char *path)
|
||||
{
|
||||
return (strcmp(path, "") == 0) || (strcmp(path, "/") == 0);
|
||||
}
|
||||
|
||||
bool _is_device_zero_file(const char *path)
|
||||
{
|
||||
return (strlen(path) == (strlen(_filename()) + 1)) &&
|
||||
(strcmp(&path[1], _filename()) == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Zero_file_system(Xml_node) { }
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Dataspace_capability dataspace(char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return Dataspace_capability();
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds_cap)
|
||||
{
|
||||
/* not supported */
|
||||
}
|
||||
|
||||
bool stat(Sysio *sysio, char const *path)
|
||||
{
|
||||
Sysio::Stat st = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
if (_is_root(path))
|
||||
st.mode = Sysio::STAT_MODE_DIRECTORY;
|
||||
else if (_is_device_zero_file(path)) {
|
||||
st.mode = Sysio::STAT_MODE_CHARDEV;
|
||||
} else {
|
||||
sysio->error.stat = Sysio::STAT_ERR_NO_ENTRY;
|
||||
return false;
|
||||
}
|
||||
|
||||
sysio->stat_out.st = st;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dirent(Sysio *sysio, char const *path, off_t index)
|
||||
{
|
||||
if (_is_root(path)) {
|
||||
if (index == 0) {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_CHARDEV;
|
||||
strncpy(sysio->dirent_out.entry.name,
|
||||
_filename(),
|
||||
sizeof(sysio->dirent_out.entry.name));
|
||||
} else {
|
||||
sysio->dirent_out.entry.type = Sysio::DIRENT_TYPE_END;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_dirent(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_directory(char const *path)
|
||||
{
|
||||
if (_is_root(path))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char const *leaf_path(char const *path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
Vfs_handle *open(Sysio *sysio, char const *path)
|
||||
{
|
||||
if (!_is_device_zero_file(path)) {
|
||||
sysio->error.open = Sysio::OPEN_ERR_UNACCESSIBLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new (env()->heap()) Vfs_handle(this, this, 0);
|
||||
}
|
||||
|
||||
bool unlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename(Sysio *sysio, char const *from_path, char const *to_path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mkdir(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool symlink(Sysio *sysio, char const *path)
|
||||
{
|
||||
/* not supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
/***************************
|
||||
** File_system interface **
|
||||
***************************/
|
||||
|
||||
static char const *name() { return "zero"; }
|
||||
|
||||
|
||||
/********************************
|
||||
** File I/O service interface **
|
||||
********************************/
|
||||
|
||||
bool write(Sysio *sysio, Vfs_handle *handle)
|
||||
{
|
||||
sysio->write_out.count = sysio->write_in.count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(Sysio *sysio, Vfs_handle *vfs_handle)
|
||||
{
|
||||
size_t nbytes = sysio->read_in.count;
|
||||
|
||||
memset(sysio->read_out.chunk, 0, nbytes);
|
||||
|
||||
sysio->read_out.count = nbytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _NOUX__ZERO_FILE_SYSTEM_H_ */
|
Loading…
x
Reference in New Issue
Block a user