mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 06:33:31 +00:00
Remove COW VFS plugin
The COW plugin provided only partial copy-on-write semantics and had problems detecting recursive requests. Conversely, the import plugin has much simpler behavior that is easy to test because it mirrors that of the ram_fs server. Ref #2745
This commit is contained in:
parent
8a5e762c24
commit
db23b276f2
@ -1,5 +0,0 @@
|
||||
SRC_CC = vfs_cow.cc
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/lib/vfs/cow
|
||||
|
||||
SHARED_LIB = yes
|
@ -1,9 +0,0 @@
|
||||
MIRROR_FROM_REP_DIR := lib/mk/vfs_cow.mk src/lib/vfs/cow
|
||||
|
||||
content: $(MIRROR_FROM_REP_DIR) LICENSE
|
||||
|
||||
$(MIRROR_FROM_REP_DIR):
|
||||
$(mirror_from_rep_dir)
|
||||
|
||||
LICENSE:
|
||||
cp $(GENODE_DIR)/LICENSE $@
|
@ -1 +0,0 @@
|
||||
2018-07-25 641bf4f2645b2d000ce771aebf85b60eb0d2d3ad
|
@ -1,4 +0,0 @@
|
||||
base
|
||||
os
|
||||
so
|
||||
vfs
|
@ -1,107 +0,0 @@
|
||||
#
|
||||
# Build
|
||||
#
|
||||
set build_components {
|
||||
core init drivers/timer
|
||||
lib/vfs/cow
|
||||
server/fs_report
|
||||
server/fs_rom
|
||||
server/vfs
|
||||
test/fs_report
|
||||
}
|
||||
|
||||
build $build_components
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
append config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="CPU"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="LOG"/>
|
||||
<service name="PD"/>
|
||||
<service name="ROM"/>
|
||||
</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="config_fs">
|
||||
<binary name="vfs"/>
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="File_system"/> </provides>
|
||||
<config>
|
||||
<vfs>
|
||||
<dir name="inline">
|
||||
<dir name="test-fs_report">
|
||||
<inline name="devices">
|
||||
<devices version="initial"/>
|
||||
</inline>
|
||||
</dir>
|
||||
</dir>
|
||||
<dir name="ram"> <ram/> </dir>
|
||||
<dir name="cow">
|
||||
<cow ro="/inline" rw="/ram"/>
|
||||
</dir>
|
||||
</vfs>
|
||||
<policy label_prefix="fs_report -> " root="/cow" writeable="yes"/>
|
||||
<policy label_prefix="fs_rom -> " root="/cow/test-fs_report"/>
|
||||
</config>
|
||||
</start>
|
||||
<!--
|
||||
All reports sent by 'test-fs_report' are prefixed with the label of the
|
||||
component. Hence, they are written to the 'test-fs_report/' subdirectory.
|
||||
-->
|
||||
<start name="fs_report">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="Report"/> </provides>
|
||||
<config> <vfs> <fs/> </vfs> </config>
|
||||
</start>
|
||||
<start name="fs_rom">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="ROM"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-fs_report">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<config/>
|
||||
<route>
|
||||
<service name="ROM" label="devices"> <child name="fs_rom"/> </service>
|
||||
<service name="ROM" label="focus"> <child name="fs_rom"/> </service>
|
||||
<service name="Report" label="devices"> <child name="fs_report"/> </service>
|
||||
<service name="Report" label="focus"> <child name="fs_report"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
</config>}
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
set boot_modules {
|
||||
core ld.lib.so init timer
|
||||
fs_report
|
||||
fs_rom
|
||||
test-fs_report
|
||||
vfs
|
||||
vfs_cow.lib.so
|
||||
vfs.lib.so
|
||||
}
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
append qemu_args " -nographic"
|
||||
|
||||
run_genode_until {child "test-fs_report" exited with exit value 0.*\n} 30
|
@ -1,2 +0,0 @@
|
||||
TARGET = dummy-vfs_cow
|
||||
LIBS = vfs_cow
|
@ -1,500 +0,0 @@
|
||||
/*
|
||||
* \brief Copy-on-write file-system
|
||||
* \author Emery Hemingway
|
||||
* \date 2018-03-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 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 _SRC__VFS__COW_FILE_SYSTEM_H_
|
||||
#define _SRC__VFS__COW_FILE_SYSTEM_H_
|
||||
|
||||
#include <vfs/env.h>
|
||||
#include <vfs/file_system_factory.h>
|
||||
#include <vfs/vfs_handle.h>
|
||||
#include <vfs/types.h>
|
||||
|
||||
|
||||
namespace Vfs_cow {
|
||||
using namespace Vfs;
|
||||
using namespace Genode;
|
||||
class File_system;
|
||||
}
|
||||
|
||||
|
||||
class Vfs_cow::File_system : public Vfs::File_system
|
||||
{
|
||||
private:
|
||||
|
||||
static Absolute_path _config_path(Genode::Xml_node &node, char const *key)
|
||||
{
|
||||
Genode::String<Absolute_path::capacity()> str{};
|
||||
node.attribute(key).value(&str);
|
||||
return Absolute_path(str.string());
|
||||
}
|
||||
|
||||
Genode::Allocator &_alloc;
|
||||
Vfs::File_system &_root_dir;
|
||||
Genode::Entrypoint &_ep;
|
||||
|
||||
Absolute_path const _ro_root_path;
|
||||
Absolute_path const _rw_root_path;
|
||||
|
||||
inline Absolute_path _ro_path(char const *path) const {
|
||||
return Absolute_path(path+1, _ro_root_path.string()); }
|
||||
|
||||
inline Absolute_path _rw_path(char const *path) const {
|
||||
return Absolute_path(path+1, _rw_root_path.string()); }
|
||||
|
||||
inline bool _leaf(Absolute_path const &path) {
|
||||
return _root_dir.leaf_path(path.string()) != nullptr; }
|
||||
|
||||
inline bool _ro_leaf(char const *path) {
|
||||
return _leaf(_ro_path(path)); }
|
||||
|
||||
inline bool _rw_leaf(char const *path) {
|
||||
return _leaf(_ro_path(path)); }
|
||||
|
||||
void _mkdirs(Absolute_path const &path)
|
||||
{
|
||||
Vfs_handle *dir_handle = nullptr;
|
||||
Opendir_result res = _root_dir.opendir(
|
||||
path.string(), true, &dir_handle, _alloc);
|
||||
if (res == OPENDIR_ERR_LOOKUP_FAILED) {
|
||||
Absolute_path parent(path);
|
||||
parent.strip_last_element();
|
||||
_mkdirs(parent);
|
||||
_root_dir.opendir(path.string(), true, &dir_handle, _alloc);
|
||||
}
|
||||
if (dir_handle) dir_handle->ds().close(dir_handle);
|
||||
}
|
||||
|
||||
bool _copy(Absolute_path const &from, Absolute_path const &to)
|
||||
{
|
||||
Vfs_handle *roh = nullptr;
|
||||
Vfs_handle *rwh = nullptr;
|
||||
|
||||
_root_dir.open(
|
||||
from.string(), OPEN_MODE_RDONLY, &roh, _alloc);
|
||||
if (!roh)
|
||||
return false;
|
||||
|
||||
_root_dir.open(
|
||||
to.string(), OPEN_MODE_WRONLY|OPEN_MODE_CREATE, &rwh, _alloc);
|
||||
|
||||
if (!rwh) {
|
||||
roh->ds().close(roh);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!roh || !rwh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[1<<14];
|
||||
Stat sb { };
|
||||
_root_dir.stat(from.string(), sb);
|
||||
file_size remain = sb.size;
|
||||
|
||||
while (remain > 0) {
|
||||
file_size rn = 0;
|
||||
file_size wn = 0;
|
||||
|
||||
while (!roh->fs().queue_read(roh, sizeof(buf))) {
|
||||
warning("COW: blocking for replication...");
|
||||
_ep.wait_and_dispatch_one_io_signal();
|
||||
}
|
||||
|
||||
Read_result rres = roh->fs().complete_read(
|
||||
roh, buf, sizeof(buf), rn);
|
||||
switch (rres) {
|
||||
case READ_OK: break;
|
||||
case READ_QUEUED: continue;
|
||||
default: remain = -1; continue;
|
||||
}
|
||||
|
||||
Write_result wres = rwh->fs().write(rwh, buf, rn, wn);
|
||||
switch (wres) {
|
||||
case WRITE_OK: break;
|
||||
case WRITE_ERR_AGAIN:
|
||||
case WRITE_ERR_WOULD_BLOCK:
|
||||
continue;
|
||||
default:
|
||||
_root_dir.unlink(from.string());
|
||||
remain = -1; continue;
|
||||
}
|
||||
|
||||
roh->advance_seek(wn);
|
||||
rwh->advance_seek(wn);
|
||||
remain -= wn;
|
||||
}
|
||||
|
||||
roh->ds().close(roh);
|
||||
rwh->ds().close(rwh);
|
||||
bool res = (remain == 0);
|
||||
if (res)
|
||||
log("COW: replicated from ", from, " to ", to);
|
||||
else
|
||||
error("COW: replication from ", from, " to ", to, " failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
struct Cow_dir_handle : Vfs::Vfs_handle
|
||||
{
|
||||
Vfs_handle &ro;
|
||||
Vfs_handle &rw;
|
||||
|
||||
Absolute_path const rw_leaf;
|
||||
|
||||
Cow_dir_handle(Vfs::File_system &fs,
|
||||
Genode::Allocator &alloc,
|
||||
Vfs_handle &roh,
|
||||
Vfs_handle &rwh,
|
||||
char const *rw_leaf)
|
||||
: Vfs_handle(fs, fs, alloc, 0), ro(roh), rw(rwh), rw_leaf(rw_leaf) { }
|
||||
|
||||
~Cow_dir_handle()
|
||||
{
|
||||
ro.ds().close(&ro);
|
||||
rw.ds().close(&rw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an operation to the RW or RO handle
|
||||
* depending on current seek position.
|
||||
*/
|
||||
template <typename FN>
|
||||
void apply_seek(FN const &fn)
|
||||
{
|
||||
file_size const index = seek() / sizeof(Dirent);
|
||||
file_size const rw_dirents = rw.ds().num_dirent(rw_leaf.string());
|
||||
|
||||
/* read from RW directory first */
|
||||
if (index < rw_dirents) {
|
||||
rw.seek(index);
|
||||
fn(rw);
|
||||
} else {
|
||||
ro.seek(index - rw_dirents);
|
||||
fn(ro);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
File_system(Vfs::Env &vfs_env, Genode::Xml_node config)
|
||||
:
|
||||
_alloc(vfs_env.alloc()),
|
||||
_root_dir(vfs_env.root_dir()),
|
||||
_ep(vfs_env.env().ep()),
|
||||
_ro_root_path(_config_path(config, "ro")),
|
||||
_rw_root_path(_config_path(config, "rw"))
|
||||
{ }
|
||||
|
||||
const char* type() override { return "cow"; }
|
||||
|
||||
static const char* name() { return "cow"; }
|
||||
|
||||
/***********************
|
||||
** Directory service **
|
||||
***********************/
|
||||
|
||||
Genode::Dataspace_capability dataspace(const char *path) override
|
||||
{
|
||||
auto rw_path = _rw_path(path);
|
||||
if (_leaf(rw_path)) {
|
||||
return _root_dir.dataspace(rw_path.string());
|
||||
} else {
|
||||
auto ro_path = _ro_path(path);
|
||||
return _root_dir.dataspace(ro_path.string());
|
||||
}
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds) override
|
||||
{
|
||||
_root_dir.release(_ro_path(path).string(), ds);
|
||||
_root_dir.release(_rw_path(path).string(), ds);
|
||||
}
|
||||
|
||||
Open_result open(const char *path,
|
||||
unsigned int mode,
|
||||
Vfs::Vfs_handle **out,
|
||||
Genode::Allocator &alloc) override
|
||||
{
|
||||
auto ro_path = _ro_path(path);
|
||||
auto rw_path = _rw_path(path);
|
||||
|
||||
if (mode & OPEN_MODE_CREATE) {
|
||||
if (_leaf(ro_path)) {
|
||||
return OPEN_ERR_EXISTS;
|
||||
} else {
|
||||
return _root_dir.open(
|
||||
rw_path.string(), mode, out, alloc);
|
||||
}
|
||||
}
|
||||
|
||||
Open_result rw_res = _root_dir.open(
|
||||
rw_path.string(), mode, out, alloc);
|
||||
|
||||
if (rw_res == OPEN_ERR_UNACCESSIBLE) {
|
||||
_copy(ro_path, rw_path);
|
||||
rw_res = _root_dir.open(
|
||||
rw_path.string(), mode, out, alloc);
|
||||
}
|
||||
|
||||
return rw_res;
|
||||
}
|
||||
|
||||
Opendir_result opendir(char const *path, bool create,
|
||||
Vfs_handle **out, Allocator &alloc) override
|
||||
{
|
||||
auto ro_path = _ro_path(path);
|
||||
auto rw_path = _rw_path(path);
|
||||
Opendir_result res = OPENDIR_ERR_PERMISSION_DENIED;
|
||||
|
||||
if (!_leaf(ro_path)) {
|
||||
return _root_dir.opendir(
|
||||
rw_path.string(), create, out, alloc);
|
||||
}
|
||||
|
||||
if (create)
|
||||
return OPENDIR_ERR_NODE_ALREADY_EXISTS;
|
||||
|
||||
{
|
||||
Vfs_handle *roh = nullptr;
|
||||
Vfs_handle *rwh = nullptr;
|
||||
|
||||
res = _root_dir.opendir(
|
||||
ro_path.string(), false, &roh, alloc);
|
||||
|
||||
if (res != OPENDIR_OK)
|
||||
return res;
|
||||
|
||||
char const *rw_leaf = _root_dir.leaf_path(rw_path.string());
|
||||
if (!rw_leaf) {
|
||||
_mkdirs(rw_path);
|
||||
rw_leaf = _root_dir.leaf_path(rw_path.string());
|
||||
}
|
||||
|
||||
res = _root_dir.opendir(
|
||||
rw_path.string(), false, &rwh, alloc);
|
||||
|
||||
if (res != OPENDIR_OK) {
|
||||
roh->ds().close(roh);
|
||||
return res;
|
||||
}
|
||||
|
||||
*out = new (alloc)
|
||||
Cow_dir_handle(*this, alloc, *roh, *rwh, rw_leaf);
|
||||
return OPENDIR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void close(Vfs::Vfs_handle *vfs_handle) override
|
||||
{
|
||||
if (&vfs_handle->ds() == this) {
|
||||
Cow_dir_handle *h = static_cast<Cow_dir_handle*>(vfs_handle);
|
||||
destroy(h->alloc(), h);
|
||||
} else {
|
||||
Genode::error("unknown handle");
|
||||
}
|
||||
}
|
||||
|
||||
Watch_result watch(char const *path,
|
||||
Vfs_watch_handle **out,
|
||||
Allocator &alloc) override
|
||||
{
|
||||
auto const rw_path = _rw_path(path);
|
||||
if (!_leaf(rw_path)) {
|
||||
if (_root_dir.directory(_ro_path(path).string())) {
|
||||
_mkdirs(rw_path);
|
||||
} else {
|
||||
return WATCH_ERR_UNACCESSIBLE;
|
||||
}
|
||||
}
|
||||
return _root_dir.watch(rw_path.string(), out, alloc);
|
||||
}
|
||||
|
||||
Stat_result stat(const char *path, Vfs::Directory_service::Stat &buf) override
|
||||
{
|
||||
Stat_result res = _root_dir.stat(_rw_path(path).string(), buf);
|
||||
if (res != STAT_OK)
|
||||
res = _root_dir.stat(_ro_path(path).string(), buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
Unlink_result unlink(const char *path) override
|
||||
{
|
||||
if (_ro_leaf(path))
|
||||
return UNLINK_ERR_NO_PERM;
|
||||
return _root_dir.unlink(_rw_path(path).string());
|
||||
}
|
||||
|
||||
Rename_result rename(const char *from , const char *to) override
|
||||
{
|
||||
return _root_dir.rename(
|
||||
_rw_path(from).string(), _rw_path(to).string());
|
||||
}
|
||||
|
||||
file_size num_dirent(const char *path) override
|
||||
{
|
||||
/* return a simple sum */
|
||||
return
|
||||
_root_dir.num_dirent(_rw_path(path).string())+
|
||||
_root_dir.num_dirent(_ro_path(path).string());
|
||||
}
|
||||
|
||||
bool directory(char const *path) override
|
||||
{
|
||||
return _root_dir.directory(_ro_path(path).string()) ?
|
||||
true : _root_dir.directory(_rw_path(path).string());
|
||||
}
|
||||
|
||||
const char* leaf_path(const char *path) override
|
||||
{
|
||||
char const *res = _root_dir.leaf_path(_ro_path(path).string());
|
||||
if (res == nullptr)
|
||||
res = _root_dir.leaf_path(_rw_path(path).string());
|
||||
return res;
|
||||
}
|
||||
|
||||
/**********************
|
||||
** File I/O service **
|
||||
**********************/
|
||||
|
||||
Write_result write(Vfs_handle*,
|
||||
const char*, file_size,
|
||||
file_size&) override
|
||||
{
|
||||
return WRITE_ERR_INVALID;
|
||||
}
|
||||
|
||||
bool queue_read(Vfs_handle *vfs_handle, file_size len) override
|
||||
{
|
||||
bool res = true;
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle)
|
||||
handle->apply_seek([&] (Vfs_handle &dir) {
|
||||
res = dir.fs().queue_read(&dir, len);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
Read_result complete_read(Vfs_handle *vfs_handle,
|
||||
char *buf, file_size len,
|
||||
file_size &out) override
|
||||
{
|
||||
Read_result res = READ_ERR_INVALID;
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle)
|
||||
handle->apply_seek([&] (Vfs_handle &dir) {
|
||||
res = dir.fs().complete_read(&dir, buf, len, out);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
bool read_ready(Vfs_handle *vfs_handle) override
|
||||
{
|
||||
bool res = true;
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle)
|
||||
handle->apply_seek([&] (Vfs_handle &dir) {
|
||||
res = dir.fs().read_ready(&dir);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
bool notify_read_ready(Vfs_handle *vfs_handle) override
|
||||
{
|
||||
bool res = true;
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle)
|
||||
handle->apply_seek([&] (Vfs_handle &dir) {
|
||||
res = dir.fs().notify_read_ready(&dir);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle*, file_size) override {
|
||||
return FTRUNCATE_ERR_NO_PERM; }
|
||||
|
||||
bool check_unblock(Vfs_handle *vfs_handle, bool rd, bool wr, bool ex) override
|
||||
{
|
||||
bool res = true;
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle)
|
||||
handle->apply_seek([&] (Vfs_handle &dir) {
|
||||
res = dir.fs().check_unblock(&dir, rd, wr, ex);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
void register_read_ready_sigh(Vfs_handle *vfs_handle, Signal_context_capability sigh) override
|
||||
{
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle) {
|
||||
handle->rw.fs().register_read_ready_sigh(&handle->rw, sigh);
|
||||
handle->ro.fs().register_read_ready_sigh(&handle->ro, sigh);
|
||||
}
|
||||
}
|
||||
|
||||
bool queue_sync(Vfs_handle *vfs_handle) override
|
||||
{
|
||||
bool res = true;
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle)
|
||||
handle->apply_seek([&] (Vfs_handle &dir) {
|
||||
res = dir.fs().queue_sync(&dir);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
Sync_result complete_sync(Vfs_handle *vfs_handle) override
|
||||
{
|
||||
Sync_result res = SYNC_OK;
|
||||
Cow_dir_handle *handle =
|
||||
dynamic_cast<Cow_dir_handle*>(vfs_handle);
|
||||
if (handle)
|
||||
handle->apply_seek([&] (Vfs_handle &dir) {
|
||||
res = dir.fs().complete_sync(&dir);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**************************
|
||||
** VFS plugin interface **
|
||||
**************************/
|
||||
|
||||
extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
|
||||
{
|
||||
struct Factory : Vfs::File_system_factory
|
||||
{
|
||||
Vfs::File_system *create(Vfs::Env &vfs_env,
|
||||
Genode::Xml_node node) override
|
||||
{
|
||||
return new (vfs_env.alloc())
|
||||
Vfs_cow::File_system(vfs_env, node);
|
||||
}
|
||||
};
|
||||
|
||||
static Factory factory;
|
||||
return &factory;
|
||||
}
|
||||
|
||||
|
||||
#endif /* _SRC__VFS__COW_FILE_SYSTEM_H_ */
|
Loading…
Reference in New Issue
Block a user