lwext4_fs: add Ext2/3/4 fs server using lwext4

Fixes #124.
This commit is contained in:
Josef Söntgen
2018-02-11 11:40:23 +01:00
committed by Christian Helmuth
parent b35caec726
commit 6a33ecac84
17 changed files with 1521 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
Lwext4 file system server

View File

@@ -0,0 +1 @@
_/src/lwext4_fs

View File

@@ -0,0 +1 @@
2018-11-27 786449fb4897510db9d96f975499afd6a1767592

View File

@@ -0,0 +1,12 @@
<runtime ram="8M" caps="100" binary="lwext4_fs">
<requires> <block/> </requires>
<config/>
<content>
<rom label="ld.lib.so"/>
<rom label="lwext4_fs"/>
</content>
</runtime>

View File

@@ -0,0 +1,21 @@
PORT_DIR := $(call port_dir,$(REP_DIR)/ports/lwext4)
MIRROR_FROM_REP_DIR := lib/mk/lwext4.mk \
lib/import/import-lwext4.mk \
src/server/lwext4_fs \
include/lwext4
content: $(MIRROR_FROM_REP_DIR) src/lib/lwext4
$(MIRROR_FROM_REP_DIR):
$(mirror_from_rep_dir)
src/lib/lwext4:
mkdir -p $@
cp -r $(PORT_DIR)/src/lib/lwext4/* $@
cp -r $(REP_DIR)/src/lib/lwext4/* $@
content: LICENSE
LICENSE:
( echo "Lwext4 is subject to GNU General Public License version 2, see:"; \
echo " src/lib/lwext4/LICENSE" ) > $@

View File

@@ -0,0 +1 @@
2018-11-27 6ec429b6a4a6e364c916fae3de25663ce3fccdde

View File

@@ -0,0 +1,8 @@
base
os
block_session
file_system
file_system_session
timer_session
report_session
vfs

109
run/lwext4_fs.run Normal file
View File

@@ -0,0 +1,109 @@
assert_spec linux
#
# Check used commands
#
set mke4fs [installed_command mkfs.ext4]
set dd [installed_command dd]
#
# Build
#
set build_components {
core init
drivers/timer
server/lwext4_fs
server/lx_block
server/report_rom
test/libc_vfs
}
build $build_components
#
# Build EXT2-file-system image
#
set image_size 262144
catch { exec $dd if=/dev/zero of=bin/ext4.raw bs=1M seek=$image_size count=0 }
catch { exec $mke4fs -F bin/ext4.raw }
create_boot_directory
#
# Generate config
#
append config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="report_rom">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config verbose="yes"/>
</start>
<start name="lx_block" ld="no">
<resource name="RAM" quantum="1G"/>
<provides><service name="Block"/></provides>
<config file="ext4.raw" block_size="512" writeable="yes"/>
</start>
<start name="lwext4_fs" caps="100">
<resource name="RAM" quantum="4M" />
<provides><service name="File_system"/></provides>
<config cache_write_back="yes">
<report stats="yes"/>
<policy label_prefix="test-libc_vfs" root="/" writeable="yes"/>
</config>
</start>
<start name="test-libc_vfs">
<resource name="RAM" quantum="4M"/>
<config>
<large seek="yes"/>
<vfs>
<dir name="dev"> <log/> </dir>
<fs/>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
</config>
</start>
</config>}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core ld.lib.so init timer report_rom lx_block
lwext4_fs ext4.raw libc.lib.so test-libc_vfs vfs.lib.so
}
build_boot_image $boot_modules
append qemu_args " -nographic"
run_genode_until {.*child "test-libc_vfs" exited with exit value 0.*} 600
#exec rm -f bin/ext4.raw

View File

@@ -0,0 +1,128 @@
/*
* \brief Lwext4 file system directory node
* \author Josef Soentgen
* \date 2017-08-01
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_
/* lwext4 includes */
#include <ext4.h>
/* local includes */
#include <node.h>
namespace Lwext4_fs {
using namespace Genode;
class Directory;
}
class Lwext4_fs::Directory : public Node
{
private:
ext4_dir _dir { };
int64_t _prev_index { -1 };
void _open(char const *path, bool create)
{
/* always try to open the directory first */
int err = ext4_dir_open(&_dir, path);
if (err && !create) {
error("ext4_dir_open failed: ", err);
throw Permission_denied();
} else if (err && create) {
err = ext4_dir_mk(path);
if (err) {
error("ext4_dir_mk failed: ", err);
throw Permission_denied();
}
err = ext4_mode_set(path, 0777);
if (err) {
error("ext4_mode_set failed: ", err);
ext4_dir_rm(path);
throw Permission_denied();
}
} else if (!err && create) {
/* it already exists but we were advised to create it */
ext4_dir_close(&_dir);
throw Node_already_exists();
}
}
public:
Directory(char const *name, bool create = false) : Node(name, create)
{
_open(name, create);
}
size_t read(char *dest, size_t len, seek_off_t seek_offset)
{
if (len < sizeof(Directory_entry)) {
error("read buffer too small for directory entry");
return 0;
}
if (seek_offset % sizeof(Directory_entry)) {
error("seek offset not alighed to sizeof(Directory_entry)");
return 0;
}
Directory_entry * const e = (Directory_entry *)(dest);
int64_t const index = seek_offset / sizeof(Directory_entry);
/*
* Manipulate ext4_dir struct directly which AFAICT is
* okay and let lwext4 deal with it to safe CPU time.
*/
if (index != (_prev_index + 1)) {
_dir.next_off = index > 0 ? index : 0;
}
ext4_direntry const *dentry = nullptr;
while (true) {
dentry = ext4_dir_entry_next(&_dir);
if (!dentry) { break; }
/* ignore entries without proper inode */
if (!dentry->inode) {
warning("skip dentry with empty inode");
continue;
}
size_t const len = (size_t)(dentry->name_length + 1) > sizeof(e->name)
? sizeof(e->name) : dentry->name_length + 1;
strncpy(e->name, reinterpret_cast<char const*>(dentry->name), len);
e->inode = dentry->inode;
break;
}
if (!dentry) { throw Node::Eof(); }
_prev_index = index;
switch (dentry->inode_type) {
case EXT4_DE_DIR: e->type = Directory_entry::TYPE_DIRECTORY; break;
case EXT4_DE_SYMLINK: e->type = Directory_entry::TYPE_SYMLINK; break;
default: e->type = Directory_entry::TYPE_FILE; break;
}
return sizeof(Directory_entry);
}
size_t write(char const *src, size_t len, seek_off_t) { return 0; }
};
#endif /* _DIRECTORY_H_ */

120
src/server/lwext4_fs/file.h Normal file
View File

@@ -0,0 +1,120 @@
/*
* \brief Lwext4 file system file node
* \author Josef Soentgen
* \date 2017-08-01
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FILE_H_
#define _FILE_H_
/* local includes */
#include <node.h>
/* lwext4 includes */
#include <ext4.h>
namespace Lwext4_fs {
using namespace Genode;
class File;
}
class Lwext4_fs::File : public Node
{
private:
ext4_file _file;
public:
File(const char *name, Mode mode, bool create) : Node(name, create)
{
int flags = 0;
if (create) { flags |= O_CREAT; }
switch (mode) {
case READ_ONLY: flags |= O_RDONLY; break;
case WRITE_ONLY: flags |= O_WRONLY; break;
case READ_WRITE: flags |= O_RDWR; break;
default: break;
}
int err = ext4_fopen2(&_file, name, flags);
if (err) {
error("ext4_fopen2: error: ", err);
throw Permission_denied();
}
if (create) {
err = ext4_mode_set(name, 0666);
if (err) {
error("ext4_mode_set: error: ", err);
throw Permission_denied();
}
}
}
~File()
{
ext4_fclose(&_file);
}
size_t read(char *dest, size_t len, seek_off_t seek_offset) override
{
bool const to_end = seek_offset == (seek_off_t)(~0);
int err = ext4_fseek(&_file, to_end ? 0 : seek_offset,
to_end ? SEEK_END : SEEK_SET);
if (err) {
error(__func__, ": invalid seek offset");
return 0;
}
Genode::size_t bytes = 0;
err = ext4_fread(&_file, dest, len, &bytes);
if (err) {
error(__func__, ": error: ", err);
return 0;
}
if (!bytes) { throw Node::Eof(); }
return bytes;
}
size_t write(char const *src, size_t len, seek_off_t seek_offset) override
{
bool const to_end = seek_offset == (seek_off_t)(~0);
int err = ext4_fseek(&_file, to_end ? 0 : seek_offset,
to_end ? SEEK_END : SEEK_SET);
if (err) {
error(__func__, ": invalid seek offset: ", seek_offset);
return 0;
}
Genode::size_t bytes = 0;
err = ext4_fwrite(&_file, src, len, &bytes);
if (err) {
error(__func__, ": error: ", err);
return 0;
}
return bytes;
}
void truncate(file_size_t size) override
{
int const err = ext4_ftruncate(&_file, size);
if (err) { error(__func__, ": error: ", err); }
}
};
#endif /* _FILE_H_ */

View File

@@ -0,0 +1,125 @@
/*
* \brief Lwext4 file system
* \author Josef Soentgen
* \date 2017-08-01
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/log.h>
#include <util/string.h>
/* library includes */
#include <ext4.h>
/* local includes */
#include <file_system.h>
static char const *_fs_name = "ext4";
static char const *_fs_mp = "/";
static bool _cache_write_back = false;
void File_system::init(ext4_blockdev *bd)
{
int err = ext4_device_register(bd, _fs_name);
if (err) { throw Init_failed(); }
}
void File_system::mount_fs(Genode::Xml_node config)
{
int err = ext4_mount(_fs_name, _fs_mp, false);
if (err) {
Genode::error("could not mount file system, err: ", err);
throw Mount_failed();
}
_cache_write_back = config.attribute_value("cache_write_back", false);
if (_cache_write_back) {
err = ext4_cache_write_back(_fs_mp, 1);
if (err) {
Genode::warning("could not enable cache write-back mode, err: ", err);
_cache_write_back = false;
}
}
err = ext4_recover(_fs_mp);
if (err && err != ENOTSUP) {
Genode::error("could not recover file system (FSCK needed!), err:", err);
throw Mount_failed();
}
err = ext4_journal_start(_fs_mp);
if (err) {
Genode::error("could not start journal, err: ", err);
throw Mount_failed();
}
}
void File_system::unmount_fs()
{
int err = ext4_journal_stop(_fs_mp);
if (err) {
Genode::error("could not stop journal, err: ", err);
// throw Genode::Exception();
}
if (_cache_write_back) {
err = ext4_cache_write_back(_fs_mp, 0);
if (err) {
Genode::error("could not disable cache write-back mode, err: ", err);
}
}
err = ext4_umount(_fs_mp);
if (err) {
Genode::error("could not unmount file system, err: ", err);
throw Unmount_failed();
}
}
void File_system::sync()
{
int const err = ext4_cache_flush(_fs_mp);
if (err) {
Genode::error("could not flush cache, err: ", err);
throw Sync_failed();
}
}
void File_system::stats_update(Genode::Reporter &reporter)
{
using namespace Genode;
struct ext4_mount_stats stats { };
int const err = ext4_mount_point_stats(_fs_mp, &stats);
if (err) {
Genode::error("could not get mount point stats");
return;
}
try {
Reporter::Xml_generator xml(reporter, [&] () {
xml.node("blocks", [&] () {
xml.attribute("used", stats.blocks_count-stats.free_blocks_count);
xml.attribute("avail", stats.free_blocks_count);
xml.attribute("size", stats.block_size);
});
xml.node("inodes", [&] () {
xml.attribute("used", stats.inodes_count);
xml.attribute("avail", stats.free_inodes_count);
});
});
} catch (...) { }
}

View File

@@ -0,0 +1,38 @@
/**
* \brief Lwext4 file system initialization
* \author Josef Soentgen
* \date 2017-08-01
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FILE_SYSTEM_H_
#define _FILE_SYSTEM_H_
/* Genode includes */
#include <base/exception.h>
#include <os/reporter.h>
#include <util/xml_node.h>
struct ext4_blockdev;
namespace File_system {
struct Init_failed : Genode::Exception { };
struct Mount_failed : Genode::Exception { };
struct Unmount_failed : Genode::Exception { };
struct Sync_failed : Genode::Exception { };
void init(ext4_blockdev*);
void mount_fs(Genode::Xml_node);
void unmount_fs();
void sync();
void stats_update(Genode::Reporter &);
}
#endif /* _FILE_SYSTEM_H_ */

View File

@@ -0,0 +1,682 @@
/**
* \brief Lwext4 file system interface implementation
* \author Josef Soentgen
* \date 2017-08-01
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/heap.h>
#include <file_system/util.h>
#include <file_system_session/rpc_object.h>
#include <os/session_policy.h>
#include <root/component.h>
/* library includes */
#include <lwext4/init.h>
#include <ext4.h>
/* local includes */
#include <directory.h>
#include <file.h>
#include <file_system.h>
#include <open_node.h>
#include <symlink.h>
namespace Lwext4_fs {
using File_system::Packet_descriptor;
using File_system::Path;
struct Main;
struct Root;
struct Session_component;
}
class Lwext4_fs::Session_component : public File_system::Session_rpc_object
{
private:
typedef File_system::Open_node<Node> Open_node;
Genode::Env &_env;
Allocator &_md_alloc;
Directory &_root;
Id_space<File_system::Node> _open_node_registry;
bool _writable;
Signal_handler<Session_component> _process_packet_handler;
Genode::Reporter _stats_reporter { _env , "file_system_stats", "stats" };
/******************************
** Packet-stream processing **
******************************/
/**
* Perform packet operation
*
* \return true on success, false on failure
*/
void _process_packet_op(Packet_descriptor &packet, Open_node &open_node)
{
void * const content = tx_sink()->packet_content(packet);
size_t const length = packet.length();
/* resulting length */
size_t res_length = 0;
bool succeeded = false;
switch (packet.operation()) {
case Packet_descriptor::READ:
if (content && (packet.length() <= packet.size())) {
try {
res_length = open_node.node().read((char *)content, length,
packet.position());
succeeded = res_length;
} catch (Node::Eof) { succeeded = true; }
}
break;
case Packet_descriptor::WRITE:
if (content && (packet.length() <= packet.size())) {
res_length = open_node.node().write((char const *)content,
length, packet.position());
if (res_length != length) {
Genode::error("partial write detected ",
res_length, " vs ", length);
/* do not acknowledge */
return;
}
succeeded = true;
}
break;
case Packet_descriptor::CONTENT_CHANGED:
open_node.register_notify(*tx_sink());
/* notify_listeners may bounce the packet back*/
open_node.node().notify_listeners();
/* otherwise defer acknowledgement of this packet */
return;
case Packet_descriptor::READ_READY:
succeeded = true;
/* not supported */
break;
case Packet_descriptor::SYNC:
/* for future failure handling */
try { File_system::sync(); }
catch (...) { }
File_system::stats_update(_stats_reporter);
succeeded = true;
break;
}
packet.length(res_length);
packet.succeeded(succeeded);
tx_sink()->acknowledge_packet(packet);
}
void _process_packet()
{
Packet_descriptor packet = tx_sink()->get_packet();
/* assume failure by default */
packet.succeeded(false);
auto process_packet_fn = [&] (Open_node &open_node) {
_process_packet_op(packet, open_node);
};
try {
_open_node_registry.apply<Open_node>(packet.handle(), process_packet_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
Genode::error("Invalid_handle");
tx_sink()->acknowledge_packet(packet);
}
}
void _process_packets()
{
while (tx_sink()->packet_avail()) {
if (!tx_sink()->ready_to_ack())
return;
_process_packet();
}
}
static void _assert_valid_path(char const *path)
{
if (!path || path[0] != '/')
throw Lookup_failed();
}
public:
/**
* Constructor
*/
Session_component(Genode::Env &env,
size_t tx_buf_size,
char const *root_dir,
bool writeable,
Allocator &md_alloc,
bool report_stats)
:
Session_rpc_object(env.ram().alloc(tx_buf_size), env.rm(), env.ep().rpc_ep()),
_env(env),
_md_alloc(md_alloc),
_root(*new (&_md_alloc) Directory(root_dir, false)),
_writable(writeable),
_process_packet_handler(env.ep(), *this, &Session_component::_process_packets)
{
_tx.sigh_packet_avail(_process_packet_handler);
_tx.sigh_ready_to_ack(_process_packet_handler);
_stats_reporter.enabled(report_stats);
}
/**
* Destructor
*/
~Session_component()
{
Dataspace_capability ds = tx_sink()->dataspace();
_env.ram().free(static_cap_cast<Ram_dataspace>(ds));
destroy(&_md_alloc, &_root);
}
/***************************
** File_system interface **
***************************/
File_handle file(Dir_handle dir_handle, Name const &name, Mode mode, bool create)
{
if (!valid_name(name.string()))
throw Invalid_name();
auto file_fn = [&] (Open_node &open_node) {
Node &dir = open_node.node();
if (!_writable) {
if (create || (mode != STAT_ONLY && mode != READ_ONLY)) {
throw Permission_denied();
}
}
/* should already contain _root_dir */
Absolute_path absolute_path(dir.name());
try {
absolute_path.append("/");
absolute_path.append(name.string());
} catch (Path_base::Path_too_long) {
throw Invalid_name();
}
File *file = new (&_md_alloc) File(absolute_path.base(),
mode, create);
Open_node *open_file =
new (&_md_alloc) Open_node(*file, _open_node_registry);
return open_file->id();
};
try {
return File_handle {
_open_node_registry.apply<Open_node>(dir_handle, file_fn).value
};
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create)
{
if (!valid_name(name.string())) { throw Invalid_name(); }
auto file_fn = [&] (Open_node &open_node) {
Node &dir = open_node.node();
if (!_writable && create) { throw Permission_denied(); }
Absolute_path absolute_path(_root.name());
try {
absolute_path.append(dir.name());
absolute_path.append("/");
absolute_path.append(name.string());
} catch (Path_base::Path_too_long) {
throw Invalid_name();
}
Symlink *link = new (&_md_alloc) Symlink(absolute_path.base(), create);
Open_node *open_file =
new (&_md_alloc) Open_node(*link, _open_node_registry);
return open_file->id();
};
try {
return Symlink_handle {
_open_node_registry.apply<Open_node>(dir_handle, file_fn).value
};
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Dir_handle dir(Path const &path, bool create)
{
char const *path_str = path.string();
_assert_valid_path(path_str);
/* skip leading '/' */
path_str++;
if (!_writable && create)
throw Permission_denied();
if (!path.valid_string())
throw Name_too_long();
Absolute_path absolute_path(_root.name());
try {
absolute_path.append(path_str);
absolute_path.remove_trailing('/');
} catch (Path_base::Path_too_long) {
throw Name_too_long();
}
Directory *dir = new (&_md_alloc) Directory(absolute_path.base(), create);
Open_node *open_dir =
new (_md_alloc) Open_node(*dir, _open_node_registry);
return Dir_handle { open_dir->id().value };
}
Node_handle node(Path const &path)
{
char const *path_str = path.string();
_assert_valid_path(path_str);
Absolute_path absolute_path(_root.name());
try {
absolute_path.append(path.string());
absolute_path.remove_trailing('/');
} catch (Path_base::Path_too_long) {
throw Lookup_failed();
}
try {
Node *node = new (&_md_alloc) Node(absolute_path.base());
Open_node *open_node =
new (_md_alloc) Open_node(*node, _open_node_registry);
return open_node->id();
} catch (...) { throw; }
}
void close(Node_handle handle)
{
auto close_fn = [&] (Open_node &open_node) {
try {
Absolute_path absolute_path(_root.name());
absolute_path.append(open_node.node().name());
} catch (Path_base::Path_too_long) { }
Node &node = open_node.node();
destroy(_md_alloc, &open_node);
destroy(_md_alloc, &node);
};
try {
_open_node_registry.apply<Open_node>(handle, close_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
Status status(Node_handle node_handle)
{
auto status_fn = [&] (Open_node &open_node) {
return open_node.node().status();
};
try {
return _open_node_registry.apply<Open_node>(node_handle, status_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
void control(Node_handle, Control) override { }
void unlink(Dir_handle dir_handle, Name const &name)
{
if (!valid_name(name.string()))
throw Invalid_name();
if (!_writable)
throw Permission_denied();
auto unlink_fn = [&] (Open_node &open_node) {
Absolute_path absolute_path(_root.name());
try {
absolute_path.append(open_node.node().name());
absolute_path.append("/");
absolute_path.append(name.string());
} catch (Path_base::Path_too_long) {
throw Invalid_name();
}
/* XXX do not call ext4_ functions directly */
{
struct ext4_inode _inode;
unsigned int _ino;
int err = ext4_raw_inode_fill(absolute_path.base(), &_ino, &_inode);
/* silent error because the look up is allowed to fail */
if (err) { throw Lookup_failed(); }
unsigned int const v = _inode.mode & 0xf000;
switch (v) {
case EXT4_INODE_MODE_DIRECTORY:
err = ext4_dir_rm(absolute_path.base());
break;
case EXT4_INODE_MODE_FILE:
default:
err = ext4_fremove(absolute_path.base());
break;
}
if (err) {
Genode::error("unlink: error: ", err);
throw Invalid_name();
}
}
};
try {
_open_node_registry.apply<Open_node>(dir_handle, unlink_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
void truncate(File_handle file_handle, file_size_t size)
{
if (!_writable)
throw Permission_denied();
auto truncate_fn = [&] (Open_node &open_node) {
open_node.node().truncate(size);
};
try {
_open_node_registry.apply<Open_node>(file_handle, truncate_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
void move(Dir_handle from_dir_handle, Name const &from_name,
Dir_handle to_dir_handle, Name const &to_name)
{
if (!_writable) { throw Permission_denied(); }
auto move_fn = [&] (Open_node &open_from_dir_node) {
auto inner_move_fn = [&] (Open_node &open_to_dir_node) {
Node &from_dir = open_from_dir_node.node();
Node &to_dir = open_to_dir_node.node();
char const *from_str = from_name.string();
char const *to_str = to_name.string();
Absolute_path absolute_from_path(_root.name());
Absolute_path absolute_to_path(_root.name());
try {
absolute_from_path.append(from_dir.name());
absolute_from_path.append("/");
absolute_from_path.append(from_name.string());
absolute_to_path.append(to_dir.name());
absolute_to_path.append("/");
absolute_to_path.append(to_name.string());
} catch (Path_base::Path_too_long) {
throw Invalid_name();
}
char const *from_base = absolute_from_path.base();
char const *to_base = absolute_to_path.base();
if (!(valid_name(from_str) && valid_name(to_str)))
throw Lookup_failed();
/* lwext4 will complain if target and source are the same */
int const err = ext4_frename(from_base, to_base);
if (err && err != EEXIST) {
Genode::error("move: error: ", err);
throw Permission_denied();
}
};
try {
_open_node_registry.apply<Open_node>(to_dir_handle, inner_move_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
};
try {
_open_node_registry.apply<Open_node>(from_dir_handle, move_fn);
} catch (Id_space<File_system::Node>::Unknown_id const &) {
throw Invalid_handle();
}
}
};
class Lwext4_fs::Root : public Root_component<Session_component>
{
private:
Genode::Env &_env;
int _sessions { 0 };
bool _report_stats { false };
bool _verbose { false };
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
Genode::Signal_handler<Lwext4_fs::Root> _config_sigh {
_env.ep(), *this, &Lwext4_fs::Root::_handle_config_update };
void _handle_config_update()
{
_config_rom.update();
if (!_config_rom.valid()) { return; }
Genode::Xml_node config = _config_rom.xml();
_verbose = config.attribute_value("verbose", false);
try {
_report_stats = config.sub_node("report")
.attribute_value("stats", false);
} catch (...) { }
}
protected:
Session_component *_create_session(const char *args)
{
if (!_config_rom.valid()) {
Genode::error("no valid config found");
throw Service_denied();
}
using namespace Genode;
/*
* Determine client-specific policy defined implicitly by
* the client's label.
*/
Genode::Path<MAX_PATH_LEN> session_root;
bool writeable = false;
Session_label const label = label_from_args(args);
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota").aligned_size();
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").aligned_size();
if (!tx_buf_size)
throw Service_denied();
/*
* Check if donated ram quota suffices for session data,
* and communication buffer.
*/
size_t const session_size =
max((size_t)4096, sizeof(Session_component)) +
tx_buf_size;
if (session_size > ram_quota) {
Genode::error("insufficient 'ram_quota' from ", label.string(),
" got ", ram_quota, "need ", session_size);
throw Insufficient_ram_quota();
}
ram_quota -= session_size;
char tmp[MAX_PATH_LEN];
try {
Session_policy policy(label, _config_rom.xml());
/* determine policy root offset */
try {
policy.attribute("root").value(tmp, sizeof(tmp));
session_root.import(tmp, "/mnt");
} catch (Xml_node::Nonexistent_attribute) { }
/*
* Determine if the session is writeable.
* Policy overrides client argument, both default to false.
*/
if (policy.attribute_value("writeable", false))
writeable = Arg_string::find_arg(args, "writeable").bool_value(false);
}
catch (Session_policy::No_policy_defined) { throw Service_denied(); }
/* apply client's root offset */
Arg_string::find_arg(args, "root").string(tmp, sizeof(tmp), "/");
if (Genode::strcmp("/", tmp, sizeof(tmp))) {
session_root.append("/");
session_root.append(tmp);
}
session_root.remove_trailing('/');
char const *root_dir = session_root.base();
try {
if (++_sessions == 1) {
File_system::mount_fs(_config_rom.xml());
}
} catch (...) { throw Service_denied(); }
try {
return new (md_alloc())
Session_component(_env, tx_buf_size, root_dir, writeable, *md_alloc(),
_report_stats);
} catch (Lookup_failed) {
Genode::error("File system root directory \"", root_dir, "\" does not exist");
throw Service_denied();
}
}
void _destroy_session(Session_component *session)
{
Genode::destroy(md_alloc(), session);
try {
if (--_sessions == 0) { File_system::unmount_fs(); }
} catch (...) { }
}
public:
/**
* Constructor
*/
Root(Genode::Env &env, Allocator &md_alloc)
:
Root_component<Session_component>(env.ep(), md_alloc),
_env(env)
{
_config_rom.sigh(_config_sigh);
_handle_config_update();
}
};
struct Lwext4_fs::Main
{
Genode::Env &_env;
Heap _heap { _env.ram(), _env.rm() };
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
Root fs_root { _env, _sliced_heap };
Main(Genode::Env &env) : _env(env)
{
Lwext4::malloc_init(_env, _heap);
ext4_blockdev *bd = Lwext4::block_init(_env, _heap);
File_system::init(bd);
env.parent().announce(env.ep().manage(fs_root));
Genode::log("--- lwext4 started ---");
}
};
void Component::construct(Genode::Env &env)
{
env.exec_static_constructors();
static Lwext4_fs::Main inst(env);
}

110
src/server/lwext4_fs/node.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* \brief Lwext4 file system node
* \author Josef Soentgen
* \date 2017-08-01
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _NODE_H_
#define _NODE_H_
/* Genode includes */
#include <base/log.h>
#include <file_system/node.h>
#include <os/path.h>
/* lwext4 includes */
#include <ext4.h>
#include <ext4_inode.h>
namespace Lwext4_fs {
using namespace File_system;
typedef Genode::Path<2047 + 1> Absolute_path;
class Node;
}
#define NODE_DEBUG_MSG() \
Genode::error(__func__, " called on generic Node object")
class Lwext4_fs::Node : public Node_base
{
protected:
struct ext4_inode _inode;
unsigned int _ino;
Absolute_path _name;
public:
struct Eof : Genode::Exception { };
Node(char const *name, bool create = false) : _name(name)
{
if (!create) {
int const err = ext4_raw_inode_fill(_name.base(), &_ino, &_inode);
/* silent error because the look up is allowed to fail */
if (err) { throw Lookup_failed(); }
}
}
virtual Status status()
{
int err = ext4_raw_inode_fill(_name.base(), &_ino, &_inode);
if (err) {
Genode::error(__func__, " ext4_raw_inode_fill: error: ", err);
throw Lookup_failed();
}
struct ext4_sblock *sb;
err = ext4_get_sblock(_name.base(), &sb);
if (err) {
Genode::error(__func__, " ext4_get_sblock: error: ", err);
throw Lookup_failed();
}
Status status;
status.size = ext4_inode_get_size(sb, &_inode);
status.inode = _ino;
unsigned int const v = ext4_inode_get_mode(sb, &_inode) & 0xf000;
switch (v) {
case EXT4_INODE_MODE_DIRECTORY: status.mode = Status::MODE_DIRECTORY; break;
case EXT4_INODE_MODE_SOFTLINK: status.mode = Status::MODE_SYMLINK; break;
case EXT4_INODE_MODE_FILE:
default: status.mode = Status::MODE_FILE; break;
}
return status;
}
char const *name() { return _name.base(); }
virtual size_t read(char *, size_t, seek_off_t)
{
NODE_DEBUG_MSG();
return 0;
}
virtual size_t write(char const *, size_t, seek_off_t)
{
NODE_DEBUG_MSG();
return 0;
}
virtual void truncate(file_size_t) { NODE_DEBUG_MSG(); }
};
#undef NODE_DEBUG_MSG
#endif /* _NODE_H_ */

View File

@@ -0,0 +1,95 @@
/*
* \brief Representation of an open file system node within the component (deprecated)
* \author Christian Prochaska
* \date 2017-06-09
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _OPEN_NODE_H_
#define _OPEN_NODE_H_
/* Genode includes */
#include <file_system/listener.h>
#include <file_system_session/file_system_session.h>
namespace File_system {
/*
* \param NODE component-specific node type
*/
template <typename NODE> class Open_node;
}
template <typename NODE>
class File_system::Open_node : public File_system::Node
{
private:
Genode::Id_space<File_system::Node>::Element _element;
NODE &_node;
Genode::Constructible<File_system::Listener> _listener;
Listener::Version const _version_when_opened = _node.curr_version();
/*
* Flag to track whether the underlying file-system node was
* modified via this 'Open_node'. That is, if closing the 'Open_node'
* should notify listeners of the file.
*/
bool _was_written = false;
public:
Open_node(NODE &node, Genode::Id_space<File_system::Node> &id_space)
: _element(*this, id_space), _node(node) { }
~Open_node()
{
if (_listener.constructed()) {
_node.remove_listener(&*_listener);
_listener.destruct();
}
/*
* Notify remaining listeners about the changed file
*/
if (_was_written)
_node.notify_listeners();
}
NODE &node() { return _node; }
File_system::Listener &listener() { return *_listener; }
Genode::Id_space<File_system::Node>::Id id() { return _element.id(); }
/**
* Register packet stream sink to be notified of node changes
*/
void register_notify(File_system::Sink &sink)
{
/*
* If there was already a handler registered for the node,
* remove the old handler.
*/
if (_listener.constructed()) {
_node.remove_listener(&*_listener);
_listener.destruct();
}
/*
* Register new handler
*/
_listener.construct(sink, id(), _version_when_opened);
_node.add_listener(&*_listener);
}
void mark_as_written() { _was_written = true; }
};
#endif /* _OPEN_NODE_H_ */

View File

@@ -0,0 +1,55 @@
/**
* \brief Lwext4 file system symlink node
* \author Josef Soentgen
* \date 2017-08-01
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SYMLINK_H_
#define _SYMLINK_H_
/* Genode includes */
#include <file_system/util.h>
#include <os/path.h>
/* lwext4 includes */
#include <ext4.h>
/* local includes */
#include <node.h>
namespace Lwext4_fs {
class Symlink;
}
class Lwext4_fs::Symlink : public Node
{
public:
Symlink(char const *name, bool create) : Node(name, create) { }
size_t write(char const *src, size_t len, seek_off_t) override
{
/* src may not be null-terminated */
Genode::String<MAX_PATH_LEN> target(Genode::Cstring(src, len));
int const err = ext4_fsymlink(target.string(), Node::name());
/* on success return len to make _process_packet happy */
return err == -1 ? 0 : len;
}
size_t read(char *dst, size_t len, seek_off_t) override
{
size_t bytes = 0;
int const err = ext4_readlink(Node::name(), dst, len, &bytes);
return err == -1 ? 0 : bytes;
}
};
#endif /* _SYMLINK_H_ */

View File

@@ -0,0 +1,12 @@
TARGET = lwext4_fs
SRC_CC = main.cc file_system.cc
LIBS = base lwext4
INC_DIR += $(PRG_DIR)
CC_OPT += -DCONFIG_USE_DEFAULT_CFG=1
CC_OPT += -DCONFIG_HAVE_OWN_ERRNO=1
CC_OPT += -DCONFIG_HAVE_OWN_ASSERT=1
CC_OPT += -DCONFIG_BLOCK_DEV_CACHE_SIZE=256
CC_CXX_WARN_STRICT =