diff --git a/libports/run/libc_ffat_fs.run b/libports/run/libc_ffat_fs.run
new file mode 100644
index 0000000000..2359404325
--- /dev/null
+++ b/libports/run/libc_ffat_fs.run
@@ -0,0 +1,133 @@
+#
+# \brief Test for using the libc_fs plugin with the FFAT file system
+# \author Christian Prochaska
+# \date 2012-07-03
+#
+
+if {[catch { exec which mkfs.vfat } ]} {
+ puts stderr "Error: mkfs.vfat not installed, aborting test"; exit }
+
+if {[have_spec linux]} {
+ puts "Run script does not support this platform"; exit }
+
+#
+# Build
+#
+
+build {
+ core init
+ drivers/pci
+ drivers/atapi
+ drivers/timer
+ drivers/sd_card
+ server/ffat_fs
+ test/libc_fs
+}
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+append_if [have_spec pci] config {
+
+
+
+
+
+
+
+
+
+}
+
+append_if [expr [have_spec pl180] || [have_spec omap4]] config {
+
+
+
+
+}
+
+append config {
+
+}
+
+install_config $config
+
+#
+# Boot modules
+#
+
+# generic modules
+set boot_modules {
+ core init timer ffat_fs
+ ld.lib.so libc.lib.so libc_log.lib.so libc_fs.lib.so
+ test-libc_fs
+}
+
+append_if [have_spec pci] boot_modules { pci_drv atapi_drv }
+append_if [have_spec pl180] boot_modules { sd_card_drv }
+append_if [have_spec omap4] boot_modules { sd_card_drv }
+
+build_boot_image $boot_modules
+
+#
+# Execute test case
+#
+
+set disk_image "bin/test.hda"
+set cmd "dd if=/dev/zero of=$disk_image bs=1024 count=65536"
+puts "creating disk image: $cmd"
+catch { exec sh -c $cmd }
+
+set cmd "mkfs.vfat -F32 $disk_image"
+puts "formating disk image with vfat file system: $cmd"
+catch { exec sh -c $cmd }
+
+#
+# Qemu
+#
+append qemu_args " -m 128 -nographic "
+append_if [have_spec pci] qemu_args " -hda $disk_image -boot order=d "
+append_if [have_spec pl180] qemu_args " -drive file=$disk_image,if=sd,cache=writeback "
+
+run_genode_until {.*child exited with exit value 0.*} 60
+
+exec rm -f $disk_image
+
+puts "\ntest succeeded\n"
+
+# vi: set ft=tcl :
diff --git a/libports/src/lib/libc_fs/plugin.cc b/libports/src/lib/libc_fs/plugin.cc
index 85205bb823..70e754159c 100644
--- a/libports/src/lib/libc_fs/plugin.cc
+++ b/libports/src/lib/libc_fs/plugin.cc
@@ -121,7 +121,7 @@ class Plugin_context : public Libc::Plugin_context,
: _type(TYPE_FILE), _node_handle(handle), _seek_offset(~0), in_flight(false) { }
Plugin_context(File_system::Dir_handle handle)
- : _type(TYPE_DIR), _node_handle(handle), _seek_offset(~0), in_flight(false) { }
+ : _type(TYPE_DIR), _node_handle(handle), _seek_offset(0), in_flight(false) { }
Plugin_context(File_system::Symlink_handle handle)
: _type(TYPE_SYMLINK), _node_handle(handle), _seek_offset(~0), in_flight(false) { }
@@ -346,6 +346,11 @@ class Plugin : public Libc::Plugin
return -1;
}
+ /*
+ * *basep does not get initialized by the libc and is therefore
+ * useless for determining a specific directory index. This
+ * function uses the plugin-internal seek offset instead.
+ */
ssize_t getdirentries(Libc::File_descriptor *fd, char *buf,
::size_t nbytes, ::off_t *basep)
{
@@ -356,16 +361,9 @@ class Plugin : public Libc::Plugin
return -1;
}
- unsigned const curr_offset = *basep;
- unsigned const curr_index = curr_offset / sizeof(struct dirent);
-
- seek_off_t const orig_seek_offset = context(fd)->seek_offset();
- context(fd)->seek_offset(curr_index*sizeof(Directory_entry));
Directory_entry entry;
size_t num_bytes = read(fd, &entry, sizeof(entry));
- context(fd)->seek_offset(orig_seek_offset);
-
/* detect end of directory entries */
if (num_bytes == 0)
return 0;
@@ -384,7 +382,7 @@ class Plugin : public Libc::Plugin
case Directory_entry::TYPE_SYMLINK: dirent->d_type = DT_LNK; break;
}
- dirent->d_fileno = curr_index + 1;
+ dirent->d_fileno = 1 + (context(fd)->seek_offset() / sizeof(struct dirent));
dirent->d_reclen = sizeof(struct dirent);
Genode::strncpy(dirent->d_name, entry.name, sizeof(dirent->d_name));
@@ -423,8 +421,12 @@ class Plugin : public Libc::Plugin
int mkdir(const char *path, mode_t mode)
{
+ Canonical_path canonical_path(path);
+
try {
- file_system()->dir(path, true);
+ File_system::Dir_handle const handle =
+ file_system()->dir(canonical_path.str, true);
+ file_system()->close(handle);
return 0;
}
catch (File_system::Permission_denied) { errno = EPERM; }
@@ -470,8 +472,7 @@ class Plugin : public Libc::Plugin
if (path.str[i] == '/')
last_slash = i;
- char dir_path[256];
- dir_path[0] = 0;
+ char dir_path[256] = "/";
if (last_slash > 0)
Genode::strncpy(dir_path, path.str,
Genode::min(sizeof(dir_path), last_slash + 1));
diff --git a/libports/src/server/ffat_fs/directory.h b/libports/src/server/ffat_fs/directory.h
new file mode 100644
index 0000000000..f305e3b068
--- /dev/null
+++ b/libports/src/server/ffat_fs/directory.h
@@ -0,0 +1,122 @@
+/*
+ * \brief FFAT file-system directory node
+ * \author Christian Prochaska
+ * \date 2012-07-04
+ */
+
+#ifndef _DIRECTORY_H_
+#define _DIRECTORY_H_
+
+/* ffat includes */
+namespace Ffat { extern "C" {
+#include
+} }
+
+/* local includes */
+#include
+
+
+namespace File_system {
+
+ class Directory : public Node
+ {
+ private:
+
+ Ffat::DIR _ffat_dir;
+ int64_t _prev_index;
+
+ public:
+
+ Directory(const char *name)
+ : Node(name),
+ _prev_index(-1) { }
+
+ void ffat_dir(Ffat::DIR ffat_dir) { _ffat_dir = ffat_dir; }
+ Ffat::DIR *ffat_dir() { return &_ffat_dir; }
+
+ size_t read(char *dst, size_t len, seek_off_t seek_offset)
+ {
+ bool verbose = false;
+
+ if (verbose)
+ PDBG("len = %zu, seek_offset = %llu", len, seek_offset);
+
+ if (len < sizeof(Directory_entry)) {
+ PERR("read buffer too small for directory entry");
+ return 0;
+ }
+
+ if (seek_offset % sizeof(Directory_entry)) {
+ PERR("seek offset not alighed to sizeof(Directory_entry)");
+ return 0;
+ }
+
+ Directory_entry *e = (Directory_entry *)(dst);
+
+ using namespace Ffat;
+
+ FILINFO ffat_file_info;
+ ffat_file_info.lfname = e->name;
+ ffat_file_info.lfsize = sizeof(e->name);
+
+ int64_t index = seek_offset / sizeof(Directory_entry);
+
+ if (index != (_prev_index + 1)) {
+ /* rewind and iterate from the beginning */
+ f_readdir(&_ffat_dir, 0);
+ for (int i = 0; i < index; i++)
+ f_readdir(&_ffat_dir, &ffat_file_info);
+ }
+
+ _prev_index = index;
+
+ FRESULT res = f_readdir(&_ffat_dir, &ffat_file_info);
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_INVALID_OBJECT:
+ PERR("f_readdir() failed with error code FR_INVALID_OBJECT");
+ return 0;
+ case FR_DISK_ERR:
+ PERR("f_readdir() failed with error code FR_DISK_ERR");
+ return 0;
+ case FR_INT_ERR:
+ PERR("f_readdir() failed with error code FR_INT_ERR");
+ return 0;
+ case FR_NOT_READY:
+ PERR("f_readdir() failed with error code FR_NOT_READY");
+ return 0;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_readdir() returned an unexpected error code");
+ return 0;
+ }
+
+ if (ffat_file_info.fname[0] == 0) { /* no (more) entries */
+ return 0;
+ }
+
+ if (e->name[0] == 0) /* use short file name */
+ strncpy(e->name, ffat_file_info.fname, sizeof(e->name));
+
+ if (verbose)
+ PDBG("found dir entry: %s", e->name);
+
+ if ((ffat_file_info.fattrib & AM_DIR) == AM_DIR)
+ e->type = Directory_entry::TYPE_DIRECTORY;
+ else
+ e->type = Directory_entry::TYPE_FILE;
+
+ return sizeof(Directory_entry);
+ }
+
+ size_t write(char const *src, size_t len, seek_off_t)
+ {
+ /* writing to directory nodes is not supported */
+ return 0;
+ }
+
+ };
+}
+
+#endif /* _DIRECTORY_H_ */
diff --git a/libports/src/server/ffat_fs/file.h b/libports/src/server/ffat_fs/file.h
new file mode 100644
index 0000000000..f40ac16968
--- /dev/null
+++ b/libports/src/server/ffat_fs/file.h
@@ -0,0 +1,169 @@
+/*
+ * \brief FFAT file-system file node
+ * \author Christian Prochaska
+ * \date 2012-07-04
+ */
+
+#ifndef _FILE_H_
+#define _FILE_H_
+
+/* ffat includes */
+namespace Ffat { extern "C" {
+#include
+} }
+
+/* local includes */
+#include
+
+
+namespace File_system {
+
+ class File : public Node
+ {
+ private:
+
+ Ffat::FIL _ffat_fil;
+
+ public:
+
+ File(const char *name) : Node(name) { }
+
+ void ffat_fil(Ffat::FIL ffat_fil) { _ffat_fil = ffat_fil; }
+ Ffat::FIL *ffat_fil() { return &_ffat_fil; }
+
+ size_t read(char *dst, size_t len, seek_off_t seek_offset)
+ {
+ bool verbose = false;
+
+ using namespace Ffat;
+
+ if (verbose)
+ PDBG("len = %zu, seek_offset = %llu", len, seek_offset);
+
+ UINT result;
+
+ if (seek_offset == (seek_off_t)(~0))
+ seek_offset = _ffat_fil.fsize;
+
+ FRESULT res = f_lseek(&_ffat_fil, seek_offset);
+
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_INVALID_OBJECT:
+ PERR("f_lseek() failed with error code FR_INVALID_OBJECT");
+ return 0;
+ case FR_DISK_ERR:
+ PERR("f_lseek() failed with error code FR_DISK_ERR");
+ return 0;
+ case FR_INT_ERR:
+ PERR("f_lseek() failed with error code FR_INT_ERR");
+ return 0;
+ case FR_NOT_READY:
+ PERR("f_lseek() failed with error code FR_NOT_READY");
+ return 0;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_lseek() returned an unexpected error code");
+ return 0;
+ }
+
+ res = f_read(&_ffat_fil, dst, len, &result);
+
+ switch(res) {
+ case FR_OK:
+ if (verbose)
+ PDBG("result = %zu", result);
+ return result;
+ case FR_DENIED:
+ PDBG("f_read() failed with error code FR_DENIED");
+ return 0;
+ case FR_INVALID_OBJECT:
+ PERR("f_read() failed with error code FR_INVALID_OBJECT");
+ return 0;
+ case FR_DISK_ERR:
+ PERR("f_read() failed with error code FR_DISK_ERR");
+ return 0;
+ case FR_INT_ERR:
+ PERR("f_read() failed with error code FR_INT_ERR");
+ return 0;
+ case FR_NOT_READY:
+ PERR("f_read() failed with error code FR_NOT_READY");
+ return 0;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_read() returned an unexpected error code");
+ return 0;
+ }
+ }
+
+ size_t write(char const *src, size_t len, seek_off_t seek_offset)
+ {
+ bool verbose = false;
+
+ if (verbose)
+ PDBG("len = %zu, seek_offset = %llu", len, seek_offset);
+
+ using namespace Ffat;
+
+ UINT result;
+
+ if (seek_offset == (seek_off_t)(~0))
+ seek_offset = _ffat_fil.fsize;
+
+ FRESULT res = f_lseek(&_ffat_fil, seek_offset);
+
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_INVALID_OBJECT:
+ PERR("f_lseek() failed with error code FR_INVALID_OBJECT");
+ return 0;
+ case FR_DISK_ERR:
+ PERR("f_lseek() failed with error code FR_DISK_ERR");
+ return 0;
+ case FR_INT_ERR:
+ PERR("f_lseek() failed with error code FR_INT_ERR");
+ return 0;
+ case FR_NOT_READY:
+ PERR("f_lseek() failed with error code FR_NOT_READY");
+ return 0;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_lseek() returned an unexpected error code");
+ return 0;
+ }
+
+ res = f_write(&_ffat_fil, src, len, &result);
+
+ switch(res) {
+ case FR_OK:
+ if (verbose)
+ PDBG("result = %zu", result);
+ return result;
+ case FR_DENIED:
+ PERR("f_write() failed with error code FR_DENIED");
+ return 0;
+ case FR_INVALID_OBJECT:
+ PERR("f_write() failed with error code FR_INVALID_OBJECT");
+ return 0;
+ case FR_DISK_ERR:
+ PERR("f_write() failed with error code FR_DISK_ERR");
+ return 0;
+ case FR_INT_ERR:
+ PERR("f_write() failed with error code FR_INT_ERR");
+ return 0;
+ case FR_NOT_READY:
+ PERR("f_write() failed with error code FR_NOT_READY");
+ return 0;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_write() returned an unexpected error code");
+ return 0;
+ }
+ }
+
+ };
+}
+
+#endif /* _FILE_H_ */
diff --git a/libports/src/server/ffat_fs/main.cc b/libports/src/server/ffat_fs/main.cc
new file mode 100644
index 0000000000..d8d9542d35
--- /dev/null
+++ b/libports/src/server/ffat_fs/main.cc
@@ -0,0 +1,1048 @@
+/*
+ * \brief FFAT file system
+ * \author Christian Prochaska
+ * \date 2012-07-03
+ */
+
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+#include
+#include
+#include
+
+/* ffat includes */
+namespace Ffat { extern "C" {
+#include
+} }
+
+using namespace Genode;
+
+
+static Lock _ffat_lock;
+typedef Lock_guard Ffat_lock_guard;
+
+
+static bool const verbose = false;
+#define PDBGV(...) if (verbose) PDBG(__VA_ARGS__)
+
+
+/*************************************
+ ** Helpers for dispatching signals **
+ *************************************/
+
+namespace Genode {
+
+ struct Signal_dispatcher_base : Signal_context
+ {
+ virtual void dispatch(int num) = 0;
+ };
+
+
+ template
+ class Signal_dispatcher : private Signal_dispatcher_base,
+ public Signal_context_capability
+ {
+ private:
+
+ T &obj;
+ void (T::*member) (int);
+ Signal_receiver &sig_rec;
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param sig_rec signal receiver to associate the signal
+ * handler with
+ * \param obj,member object and member function to call when
+ * the signal occurs
+ */
+ Signal_dispatcher(Signal_receiver &sig_rec,
+ T &obj, void (T::*member)(int))
+ :
+ Signal_context_capability(sig_rec.manage(this)),
+ obj(obj), member(member),
+ sig_rec(sig_rec)
+ { }
+
+ ~Signal_dispatcher() { sig_rec.dissolve(this); }
+
+ void dispatch(int num) { (obj.*member)(num); }
+ };
+}
+
+
+/*************************
+ ** File-system service **
+ *************************/
+
+namespace File_system {
+
+ class Session_component : public Session_rpc_object
+ {
+ private:
+
+ Directory &_root;
+ Node_handle_registry _handle_registry;
+ bool _writable;
+
+ Signal_dispatcher _process_packet_dispatcher;
+
+
+ /******************************
+ ** Packet-stream processing **
+ ******************************/
+
+ /**
+ * Perform packet operation
+ *
+ * \return true on success, false on failure
+ */
+ void _process_packet_op(Packet_descriptor &packet, Node &node)
+ {
+ void * const content = tx_sink()->packet_content(packet);
+ size_t const length = packet.length();
+ seek_off_t const offset = packet.position();
+
+ if (!content || (packet.length() > packet.size())) {
+ packet.succeeded(false);
+ return;
+ }
+
+ /* resulting length */
+ size_t res_length = 0;
+
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ switch (packet.operation()) {
+
+ case Packet_descriptor::READ:
+ PDBGV("READ");
+ res_length = node.read((char *)content, length, offset);
+ break;
+
+ case Packet_descriptor::WRITE:
+ PDBGV("WRITE");
+ res_length = node.write((char const *)content, length, offset);
+ break;
+ }
+
+ packet.length(res_length);
+ packet.succeeded(res_length > 0);
+ }
+
+ void _process_packet()
+ {
+ Packet_descriptor packet = tx_sink()->get_packet();
+
+ /* assume failure by default */
+ packet.succeeded(false);
+
+ try {
+ Node *node = _handle_registry.lookup(packet.handle());
+ _process_packet_op(packet, *node);
+ }
+ catch (Invalid_handle) { PERR("Invalid_handle"); }
+ catch (Size_limit_reached) { PERR("Size_limit_reached"); }
+
+ /*
+ * The 'acknowledge_packet' function cannot block because we
+ * checked for 'ready_to_ack' in '_process_packets'.
+ */
+ tx_sink()->acknowledge_packet(packet);
+ }
+
+ /**
+ * Called by signal dispatcher, executed in the context of the main
+ * thread (not serialized with the RPC functions)
+ */
+ void _process_packets(int)
+ {
+ while (tx_sink()->packet_avail()) {
+
+ /*
+ * Make sure that the '_process_packet' function does not
+ * block.
+ *
+ * If the acknowledgement queue is full, we defer packet
+ * processing until the client processed pending
+ * acknowledgements and thereby emitted a ready-to-ack
+ * signal. Otherwise, the call of 'acknowledge_packet()'
+ * in '_process_packet' would infinitely block the context
+ * of the main thread. The main thread is however needed
+ * for receiving any subsequent 'ready-to-ack' signals.
+ */
+ if (!tx_sink()->ready_to_ack())
+ return;
+
+ _process_packet();
+ }
+ }
+
+ /**
+ * Check if string represents a valid path (most start with '/')
+ */
+ static void _assert_valid_path(char const *path)
+ {
+ if (!valid_path(path)) {
+ PWRN("malformed path '%s'", path);
+ throw Lookup_failed();
+ }
+ }
+
+ /**
+ * Return true if both '_root.name()' and 'path'
+ * are "/"
+ */
+ bool is_ffat_root(const char *path)
+ {
+ return (is_root(_root.name()) && is_root(path));
+ }
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Session_component(size_t tx_buf_size, Rpc_entrypoint &ep,
+ Signal_receiver &sig_rec,
+ Directory &root, bool writable)
+ :
+ Session_rpc_object(env()->ram_session()->alloc(tx_buf_size), ep),
+ _root(root),
+ _writable(writable),
+ _process_packet_dispatcher(sig_rec, *this,
+ &Session_component::_process_packets)
+ {
+ /*
+ * Register '_process_packets' dispatch function as signal
+ * handler for packet-avail and ready-to-ack signals.
+ */
+ _tx.sigh_packet_avail(_process_packet_dispatcher);
+ _tx.sigh_ready_to_ack(_process_packet_dispatcher);
+ }
+
+ /**
+ * Destructor
+ */
+ ~Session_component()
+ {
+ Dataspace_capability ds = tx_sink()->dataspace();
+ env()->ram_session()->free(static_cap_cast(ds));
+ }
+
+
+ /***************************
+ ** File_system interface **
+ ***************************/
+
+ File_handle file(Dir_handle dir_handle, Name const &name,
+ Mode mode, bool create)
+ {
+ PDBGV("_root = %s, dir_name = %s, name = %s, create = %d",
+ _root.name(),
+ _handle_registry.lookup(dir_handle)->name(),
+ name.string(),
+ create);
+
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ if (!valid_filename(name.string()))
+ throw Invalid_name();
+
+ using namespace Ffat;
+
+ FIL ffat_fil;
+ BYTE ffat_flags = 0;
+
+ if (!_writable)
+ if (create || (mode != STAT_ONLY && mode != READ_ONLY))
+ throw Permission_denied();
+
+ if (create)
+ ffat_flags |= FA_CREATE_ALWAYS; /* overwrite existing file */
+
+ if ((mode == READ_ONLY) || (mode == READ_WRITE))
+ ffat_flags |= FA_READ;
+
+ if ((mode == WRITE_ONLY) || (mode == READ_WRITE))
+ ffat_flags |= FA_WRITE;
+
+ f_chdir(_root.name());
+ f_chdir(&_handle_registry.lookup(dir_handle)->name()[1]);
+
+ FRESULT res = f_open(&ffat_fil, name.string(), ffat_flags);
+
+ switch(res) {
+ case FR_OK: {
+ File *file_node = new (env()->heap()) File(name.string());
+ file_node->ffat_fil(ffat_fil);
+ return _handle_registry.alloc(file_node);
+ }
+ case FR_NO_FILE:
+ case FR_NO_PATH:
+ throw Lookup_failed();
+ case FR_INVALID_NAME:
+ case FR_INVALID_DRIVE:
+ throw Invalid_name();
+ case FR_EXIST:
+ throw Node_already_exists();
+ case FR_DENIED:
+ case FR_WRITE_PROTECTED:
+ throw Permission_denied();
+ case FR_NOT_READY:
+ PERR("f_open() failed with error code FR_NOT_READY");
+ throw Lookup_failed();
+ case FR_DISK_ERR:
+ PERR("f_open() failed with error code FR_DISK_ERR");
+ throw Lookup_failed();
+ case FR_INT_ERR:
+ PERR("f_open() failed with error code FR_INT_ERR");
+ throw Lookup_failed();
+ case FR_NOT_ENABLED:
+ PERR("f_open() failed with error code FR_NOT_ENABLED");
+ throw Lookup_failed();
+ case FR_NO_FILESYSTEM:
+ PERR("f_open() failed with error code FR_NO_FILESYSTEM");
+ throw Lookup_failed();
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_open() returned an unexpected error code");
+ throw Lookup_failed();
+ }
+ }
+
+ Symlink_handle symlink(Dir_handle, Name const &name, bool create)
+ {
+ /* not supported */
+ return Symlink_handle(-1);
+ }
+
+ Dir_handle dir(Path const &path, bool create)
+ {
+ PDBGV("_root = %s, path = %s, create = %d",
+ _root.name(), path.string(), create);
+
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ if (create && !_writable)
+ throw Permission_denied();
+
+ _assert_valid_path(path.string());
+
+ /*
+ * The 'Directory' constructor removes trailing slashes,
+ * except for "/"
+ */
+ Directory *dir_node = new (env()->heap()) Directory(path.string());
+
+ using namespace Ffat;
+
+ f_chdir(_root.name());
+
+ if (create) {
+
+ if (is_root(dir_node->name()))
+ throw Node_already_exists();
+
+ FRESULT res = f_mkdir(&dir_node->name()[1]);
+
+ try {
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_NO_PATH:
+ PDBGV("f_mkdir() failed with error code FR_NO_PATH");
+ throw Lookup_failed();
+ case FR_INVALID_NAME:
+ PDBGV("f_mkdir() failed with error code FR_INVALID_NAME");
+ throw Name_too_long();
+ case FR_INVALID_DRIVE:
+ PDBGV("f_mkdir() failed with error code FR_INVALID_DRIVE");
+ throw Name_too_long();
+ case FR_DENIED:
+ case FR_WRITE_PROTECTED:
+ throw Permission_denied();
+ case FR_EXIST:
+ throw Node_already_exists();
+ case FR_NOT_READY:
+ PERR("f_mkdir() failed with error code FR_NOT_READY");
+ throw Lookup_failed();
+ case FR_DISK_ERR:
+ PERR("f_mkdir() failed with error code FR_DISK_ERR");
+ throw Lookup_failed();
+ case FR_INT_ERR:
+ PERR("f_mkdir() failed with error code FR_INT_ERR");
+ throw Lookup_failed();
+ case FR_NOT_ENABLED:
+ PERR("f_mkdir() failed with error code FR_NOT_ENABLED");
+ throw Lookup_failed();
+ case FR_NO_FILESYSTEM:
+ PERR("f_mkdir() failed with error code FR_NO_FILESYSTEM");
+ throw Lookup_failed();
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_mkdir() returned an unexpected error code");
+ throw Lookup_failed();
+ }
+ } catch (Exception e) {
+ PDBGV("exception occured while trying to create directory");
+ destroy(env()->heap(), dir_node);
+ throw e;
+ }
+ }
+
+ Ffat::DIR ffat_dir;
+ FRESULT f_opendir_res = f_opendir(&ffat_dir, &dir_node->name()[1]);
+
+ try {
+ switch(f_opendir_res) {
+ case FR_OK:
+ dir_node->ffat_dir(ffat_dir);
+ return _handle_registry.alloc(dir_node);
+ case FR_NO_PATH:
+ PDBGV("f_opendir() failed with error code FR_NO_PATH");
+ throw Lookup_failed();
+ case FR_INVALID_NAME:
+ PDBGV("f_opendir() failed with error code FR_INVALID_NAME");
+ throw Name_too_long();
+ case FR_INVALID_DRIVE:
+ PDBGV("f_opendir() failed with error code FR_INVALID_DRIVE");
+ throw Name_too_long();
+ case FR_NOT_READY:
+ PERR("f_opendir() failed with error code FR_NOT_READY");
+ throw Lookup_failed();
+ case FR_DISK_ERR:
+ PERR("f_opendir() failed with error code FR_DISK_ERR");
+ throw Lookup_failed();
+ case FR_INT_ERR:
+ PERR("f_opendir() failed with error code FR_INT_ERR");
+ throw Lookup_failed();
+ case FR_NOT_ENABLED:
+ PERR("f_opendir() failed with error code FR_NOT_ENABLED");
+ throw Lookup_failed();
+ case FR_NO_FILESYSTEM:
+ PERR("f_opendir() failed with error code FR_NO_FILESYSTEM");
+ throw Lookup_failed();
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_opendir() returned an unexpected error code");
+ throw Lookup_failed();
+ }
+ } catch (Exception e) {
+ destroy(env()->heap(), dir_node);
+ throw e;
+ }
+ }
+
+ Node_handle node(Path const &path)
+ {
+ PDBGV("path = %s", path.string());
+
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ if (!valid_path(path.string()) &&
+ !valid_filename(path.string()))
+ throw Lookup_failed();
+
+ /*
+ * The Node constructor removes trailing slashes,
+ * except for "/"
+ */
+ Node *node = new (env()->heap()) Node(path.string());
+
+ /* f_stat() does not work for "/" */
+ if (!is_ffat_root(node->name())) {
+
+ using namespace Ffat;
+
+ FILINFO file_info;
+ /* the long file name is not used in this function */
+ file_info.lfname = 0;
+ file_info.lfsize = 0;
+
+ FRESULT res;
+
+ /*
+ * f_stat() does not work on an empty relative name,
+ * so in this case the absolute root name is used
+ */
+ if (!is_root(node->name())) {
+ f_chdir(_root.name());
+ res = f_stat(&node->name()[1], &file_info);
+ } else
+ res = f_stat(_root.name(), &file_info);
+
+ try {
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_NO_FILE:
+ case FR_NO_PATH:
+ throw Lookup_failed();
+ case FR_INVALID_NAME:
+ case FR_INVALID_DRIVE:
+ throw Lookup_failed();
+ case FR_DISK_ERR:
+ PERR("f_stat() failed with error code FR_DISK_ERR");
+ throw Lookup_failed();
+ case FR_INT_ERR:
+ PERR("f_stat() failed with error code FR_INT_ERR");
+ throw Lookup_failed();
+ case FR_NOT_READY:
+ PERR("f_stat() failed with error code FR_NOT_READY");
+ throw Lookup_failed();
+ case FR_NOT_ENABLED:
+ PERR("f_stat() failed with error code FR_NOT_ENABLED");
+ throw Lookup_failed();
+ case FR_NO_FILESYSTEM:
+ PERR("f_stat() failed with error code FR_NO_FILESYSTEM");
+ throw Lookup_failed();
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_stat() returned an unexpected error code");
+ throw Lookup_failed();
+ }
+ } catch (Exception e) {
+ destroy(env()->heap(), node);
+ throw e;
+ }
+ }
+
+ return _handle_registry.alloc(node);
+ }
+
+ void close(Node_handle handle)
+ {
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ Node *node;
+
+ try {
+ node = _handle_registry.lookup(handle);
+ } catch(Invalid_handle) {
+ throw Lookup_failed();
+ }
+
+ PDBGV("name = %s", node->name());
+
+ /* free the handle */
+ _handle_registry.free(handle);
+
+ File *file = dynamic_cast(node);
+ if (file) {
+ using namespace Ffat;
+
+ FRESULT res = f_close(file->ffat_fil());
+
+ /* free the node */
+ destroy(env()->heap(), file);
+
+ switch(res) {
+ case FR_OK:
+ return;
+ case FR_INVALID_OBJECT:
+ PERR("f_close() failed with error code FR_INVALID_OBJECT");
+ return;
+ case FR_DISK_ERR:
+ PERR("f_close() failed with error code FR_DISK_ERR");
+ return;
+ case FR_INT_ERR:
+ PERR("f_close() failed with error code FR_INT_ERR");
+ return;
+ case FR_NOT_READY:
+ PERR("f_close() failed with error code FR_NOT_READY");
+ return;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_close() returned an unexpected error code");
+ return;
+ }
+ }
+ }
+
+ Status status(Node_handle node_handle)
+ {
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ Status status;
+ status.inode = 1;
+ status.size = 0;
+ status.mode = 0;
+
+ Node *node = _handle_registry.lookup(node_handle);
+
+ PDBGV("name = %s", node->name());
+
+ using namespace Ffat;
+
+ const char *ffat_name;
+
+ /*
+ * f_stat() does not work on an empty relative name,
+ * so in this case the absolute root name is used
+ */
+ if (!is_root(node->name())) {
+ f_chdir(_root.name());
+ if (node->name()[0] == '/')
+ ffat_name = &node->name()[1];
+ else
+ ffat_name = node->name();
+ } else
+ ffat_name = _root.name();
+
+ /* f_stat() does not work for the '/' directory */
+ if (!is_ffat_root(node->name())) {
+
+ FILINFO ffat_file_info;
+ /* the long file name is not used in this function */
+ ffat_file_info.lfname = 0;
+ ffat_file_info.lfsize = 0;
+
+ FRESULT res = f_stat(ffat_name, &ffat_file_info);
+
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_NO_FILE:
+ PERR("f_stat() failed with error code FR_NO_FILE");
+ return status;
+ case FR_NO_PATH:
+ PERR("f_stat() failed with error code FR_NO_PATH");
+ return status;
+ case FR_INVALID_NAME:
+ PERR("f_stat() failed with error code FR_INVALID_NAME");
+ return status;
+ case FR_INVALID_DRIVE:
+ PERR("f_stat() failed with error code FR_INVALID_DRIVE");
+ return status;
+ case FR_DISK_ERR:
+ PERR("f_stat() failed with error code FR_DISK_ERR");
+ return status;
+ case FR_INT_ERR:
+ PERR("f_stat() failed with error code FR_INT_ERR");
+ return status;
+ case FR_NOT_READY:
+ PERR("f_stat() failed with error code FR_NOT_READY");
+ return status;
+ case FR_NOT_ENABLED:
+ PERR("f_stat() failed with error code FR_NOT_ENABLED");
+ return status;
+ case FR_NO_FILESYSTEM:
+ PERR("f_stat() failed with error code FR_NO_FILESYSTEM");
+ return status;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_stat() returned an unexpected error code");
+ return status;
+ }
+
+ if ((ffat_file_info.fattrib & AM_DIR) == AM_DIR) {
+ PDBGV("MODE_DIRECTORY");
+ status.mode = File_system::Status::MODE_DIRECTORY; }
+ else {
+ PDBGV("MODE_FILE");
+ status.mode = File_system::Status::MODE_FILE;
+ status.size = ffat_file_info.fsize;
+ }
+
+ } else {
+ PDBGV("MODE_DIRECTORY");
+ status.mode = File_system::Status::MODE_DIRECTORY;
+ }
+
+ if (status.mode == File_system::Status::MODE_DIRECTORY) {
+
+ /* determine the number of directory entries */
+
+ Ffat::DIR ffat_dir;
+ FRESULT f_opendir_res = f_opendir(&ffat_dir, ffat_name);
+
+ if (f_opendir_res != FR_OK)
+ return status;
+
+ FILINFO ffat_file_info;
+ /* the long file name is not used in this function */
+ ffat_file_info.lfname = 0;
+ ffat_file_info.lfsize = 0;
+
+ int num_direntries = -1;
+ do {
+ ++num_direntries;
+ FRESULT res = f_readdir(&ffat_dir, &ffat_file_info);
+ if (res != FR_OK)
+ return status;
+ } while (ffat_file_info.fname[0] != 0);
+
+ status.size = num_direntries * sizeof(Directory_entry);
+ }
+
+ return status;
+ }
+
+ void control(Node_handle, Control) { }
+
+ void unlink(Dir_handle dir_handle, Name const &name)
+ {
+ PDBGV("name = %s", name.string());
+
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ if (!valid_filename(name.string()))
+ throw Invalid_name();
+
+ if (!_writable)
+ throw Permission_denied();
+
+ using namespace Ffat;
+
+ f_chdir(_root.name());
+
+ f_chdir(&_handle_registry.lookup(dir_handle)->name()[1]);
+
+ FRESULT res = f_unlink(name.string());
+
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_NO_FILE:
+ case FR_NO_PATH:
+ throw Lookup_failed();
+ case FR_INVALID_NAME:
+ case FR_INVALID_DRIVE:
+ throw Invalid_name();
+ case FR_DENIED:
+ case FR_WRITE_PROTECTED:
+ throw Permission_denied();
+ case FR_DISK_ERR:
+ PERR("f_unlink() failed with error code FR_DISK_ERR");
+ return;
+ case FR_INT_ERR:
+ PERR("f_unlink() failed with error code FR_INT_ERR");
+ return;
+ case FR_NOT_READY:
+ PERR("f_unlink() failed with error code FR_NOT_READY");
+ return;
+ case FR_NOT_ENABLED:
+ PERR("f_unlink() failed with error code FR_NOT_ENABLED");
+ return;
+ case FR_NO_FILESYSTEM:
+ PERR("f_unlink() failed with error code FR_NO_FILESYSTEM");
+ return;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_unlink() returned an unexpected error code");
+ return;
+ }
+ }
+
+ void truncate(File_handle file_handle, file_size_t size)
+ {
+ PDBGV("truncate()");
+
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ if (!_writable)
+ throw Permission_denied();
+
+ File *file = _handle_registry.lookup(file_handle);
+
+ using namespace Ffat;
+
+ FRESULT res = f_truncate(file->ffat_fil());
+
+ switch(res) {
+ case FR_OK:
+ return;
+ case FR_INVALID_OBJECT:
+ PERR("f_truncate() failed with error code FR_INVALID_OBJECT");
+ return;
+ case FR_DISK_ERR:
+ PERR("f_truncate() failed with error code FR_DISK_ERR");
+ return;
+ case FR_INT_ERR:
+ PERR("f_truncate() failed with error code FR_INT_ERR");
+ return;
+ case FR_NOT_READY:
+ PERR("f_truncate() failed with error code FR_NOT_READY");
+ return;
+ case FR_TIMEOUT:
+ PERR("f_truncate() failed with error code FR_TIMEOUT");
+ return;
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_truncate() returned an unexpected error code");
+ return;
+ }
+ }
+
+ void move(Dir_handle from_dir_handle, Name const &from_name,
+ Dir_handle to_dir_handle, Name const &to_name)
+ {
+ PDBGV("from_name = %s, to_name = %s", from_name.string(), to_name.string());
+
+ Ffat_lock_guard ffat_lock_guard(_ffat_lock);
+
+ if (!_writable)
+ throw Permission_denied();
+
+ if (!valid_filename(from_name.string()))
+ throw Lookup_failed();
+
+ if (!valid_filename(to_name.string()))
+ throw Invalid_name();
+
+ Directory *from_dir = _handle_registry.lookup(from_dir_handle);
+ Directory *to_dir = _handle_registry.lookup(to_dir_handle);
+
+ using namespace Ffat;
+
+ f_chdir(_root.name());
+
+ char from_path[2*(_MAX_LFN + 1)];
+ char to_path[2*(_MAX_LFN + 1)];
+
+ strncpy(from_path, &from_dir->name()[1], _MAX_LFN);
+ strncpy(&from_path[strlen(from_path)], "/", 2);
+ strncpy(&from_path[strlen(from_path)], from_name.string(), _MAX_LFN + 1);
+
+ strncpy(to_path, &to_dir->name()[1], _MAX_LFN);
+ strncpy(&to_path[strlen(to_path)], "/", 2);
+ strncpy(&to_path[strlen(to_path)], to_name.string(), _MAX_LFN + 1);
+
+ PDBGV("from_path = %s", from_path);
+ PDBGV("to_path = %s", to_path);
+
+ FRESULT res = f_rename(from_path, to_path);
+
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_NO_FILE:
+ case FR_NO_PATH:
+ throw Lookup_failed();
+ case FR_INVALID_NAME:
+ case FR_INVALID_DRIVE:
+ throw Invalid_name();
+ case FR_EXIST:
+ PERR("f_rename() failed with error code FR_EXIST");
+ throw Invalid_name();
+ case FR_DENIED:
+ case FR_WRITE_PROTECTED:
+ throw Permission_denied();
+ case FR_DISK_ERR:
+ PERR("f_rename() failed with error code FR_DISK_ERR");
+ throw Lookup_failed();
+ case FR_INT_ERR:
+ PERR("f_rename() failed with error code FR_INT_ERR");
+ throw Lookup_failed();
+ case FR_NOT_READY:
+ PERR("f_rename() failed with error code FR_NOT_READY");
+ throw Lookup_failed();
+ case FR_NOT_ENABLED:
+ PERR("f_rename() failed with error code FR_NOT_ENABLED");
+ throw Lookup_failed();
+ case FR_NO_FILESYSTEM:
+ PERR("f_rename() failed with error code FR_NO_FILESYSTEM");
+ throw Lookup_failed();
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_rename() returned an unexpected error code");
+ throw Lookup_failed();
+ }
+
+ }
+ };
+
+
+ class Root : public Root_component
+ {
+ private:
+
+ Rpc_entrypoint &_channel_ep;
+ Signal_receiver &_sig_rec;
+ Directory &_root_dir;
+
+ protected:
+
+ Session_component *_create_session(const char *args)
+ {
+ /*
+ * Determine client-specific policy defined implicitly by
+ * the client's label.
+ */
+ Directory *session_root_dir = 0;
+ bool writeable = false;
+
+ enum { ROOT_MAX_LEN = 256 };
+ char root[ROOT_MAX_LEN];
+ root[0] = 0;
+
+ try {
+ Session_policy policy(args);
+
+ /*
+ * Determine directory that is used as root directory of
+ * the session.
+ */
+ try {
+ policy.attribute("root").value(root, sizeof(root));
+ if (is_root(root)) {
+ session_root_dir = &_root_dir;
+ } else {
+ /*
+ * Make sure the root path is specified with a
+ * leading path delimiter. For performing the
+ * lookup, we skip the first character.
+ */
+ if (root[0] != '/')
+ throw Lookup_failed();
+
+ /* Check if the root path exists */
+
+ using namespace Ffat;
+
+ FRESULT res = f_chdir(root);
+
+ switch(res) {
+ case FR_OK:
+ break;
+ case FR_NO_PATH:
+ throw Lookup_failed();
+ case FR_INVALID_NAME:
+ case FR_INVALID_DRIVE:
+ throw Lookup_failed();
+ case FR_NOT_READY:
+ PERR("f_chdir() failed with error code FR_NOT_READY");
+ throw Root::Unavailable();
+ case FR_DISK_ERR:
+ PERR("f_chdir() failed with error code FR_DISK_ERR");
+ throw Root::Unavailable();
+ case FR_INT_ERR:
+ PERR("f_chdir() failed with error code FR_INT_ERR");
+ throw Root::Unavailable();
+ case FR_NOT_ENABLED:
+ PERR("f_chdir() failed with error code FR_NOT_ENABLED");
+ throw Root::Unavailable();
+ case FR_NO_FILESYSTEM:
+ PERR("f_chdir() failed with error code FR_NO_FILESYSTEM");
+ throw Root::Unavailable();
+ default:
+ /* not supposed to occur according to the libffat documentation */
+ PERR("f_chdir() returned an unexpected error code");
+ throw Root::Unavailable();
+ }
+
+ session_root_dir = new (env()->heap()) Directory(root);
+ }
+ } catch (Xml_node::Nonexistent_attribute) {
+ PERR("Missing \"root\" attribute in policy definition");
+ throw Root::Unavailable();
+ } catch (Lookup_failed) {
+ PERR("Session root directory \"%s\" does not exist", root);
+ throw Root::Unavailable();
+ }
+
+ /*
+ * Determine if write access is permitted for the session.
+ */
+ try {
+ writeable = policy.attribute("writeable").has_value("yes");
+ } catch (Xml_node::Nonexistent_attribute) { }
+
+ } catch (Session_policy::No_policy_defined) {
+ PERR("Invalid session request, no matching policy");
+ throw Root::Unavailable();
+ }
+
+ size_t ram_quota =
+ Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
+ size_t tx_buf_size =
+ Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
+
+ /*
+ * Check if donated ram quota suffices for session data,
+ * and communication buffer.
+ */
+ size_t session_size = sizeof(Session_component) + tx_buf_size;
+ if (max((size_t)4096, session_size) > ram_quota) {
+ PERR("insufficient 'ram_quota', got %zd, need %zd",
+ ram_quota, session_size);
+ throw Root::Quota_exceeded();
+ }
+ return new (md_alloc())
+ Session_component(tx_buf_size, _channel_ep, _sig_rec,
+ *session_root_dir, writeable);
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param session_ep session entrypoint
+ * \param sig_rec signal receiver used for handling the
+ * data-flow signals of packet streams
+ * \param md_alloc meta-data allocator
+ */
+ Root(Rpc_entrypoint &session_ep, Allocator &md_alloc,
+ Signal_receiver &sig_rec, Directory &root_dir)
+ :
+ Root_component(&session_ep, &md_alloc),
+ _channel_ep(session_ep), _sig_rec(sig_rec), _root_dir(root_dir)
+ { }
+ };
+};
+
+
+int main(int, char **)
+{
+ using namespace File_system;
+ using namespace Ffat;
+
+ static Ffat::FATFS _fatfs;
+
+ /* mount the file system */
+ PDBGV("Mounting device %u ...\n\n", 0);
+
+ if (f_mount(0, &_fatfs) != Ffat::FR_OK) {
+ PERR("Mount failed\n");
+ return -1;
+ }
+
+ enum { STACK_SIZE = 3*sizeof(addr_t)*1024 };
+ static Cap_connection cap;
+ static Rpc_entrypoint ep(&cap, STACK_SIZE, "ffat_fs_ep");
+ static Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
+ static Signal_receiver sig_rec;
+
+ static Directory root_dir("/");
+
+ static File_system::Root root(ep, sliced_heap, sig_rec, root_dir);
+
+ env()->parent()->announce(ep.manage(&root));
+
+ for (;;) {
+ Signal s = sig_rec.wait_for_signal();
+ static_cast(s.context())->dispatch(s.num());
+ }
+
+ return 0;
+}
diff --git a/libports/src/server/ffat_fs/node.h b/libports/src/server/ffat_fs/node.h
new file mode 100644
index 0000000000..ef8aeadf63
--- /dev/null
+++ b/libports/src/server/ffat_fs/node.h
@@ -0,0 +1,58 @@
+/*
+ * \brief FFAT file-system node
+ * \author Christian Prochaska
+ * \date 2012-07-04
+ */
+
+#ifndef _NODE_H_
+#define _NODE_H_
+
+/* ffat includes */
+namespace Ffat { extern "C" {
+#include
+} }
+
+namespace File_system {
+
+ class Node
+ {
+ protected:
+
+ char _name[_MAX_LFN + 1];
+
+ public:
+
+ Node(const char *name)
+ {
+ strncpy(_name, name, sizeof(_name));
+
+ /* remove any trailing slashes, except for "/" */
+ size_t index = strlen(_name) - 1;
+ while ((index > 0) && (_name[index] == '/'))
+ _name[index--] = 0;
+ }
+
+ char const *name() const { return _name; }
+
+ /*
+ * A generic Node object can be created to represent a file or
+ * directory by its name without opening it, so the functions
+ * of this class must not be abstract.
+ */
+
+ virtual size_t read(char *dst, size_t len, seek_off_t)
+ {
+ PERR("read() called on generic Node object");
+ return 0;
+ }
+
+ virtual size_t write(char const *src, size_t len, seek_off_t)
+ {
+ PERR("write() called on generic Node object");
+ return 0;
+ }
+ };
+
+}
+
+#endif /* _NODE_H_ */
diff --git a/libports/src/server/ffat_fs/node_handle_registry.h b/libports/src/server/ffat_fs/node_handle_registry.h
new file mode 100644
index 0000000000..12acf5eb13
--- /dev/null
+++ b/libports/src/server/ffat_fs/node_handle_registry.h
@@ -0,0 +1,130 @@
+/*
+ * \brief Facility for managing the session-local node-handle namespace
+ * \author Norman Feske
+ * \date 2012-04-11
+ */
+
+#ifndef _NODE_HANDLE_REGISTRY_H_
+#define _NODE_HANDLE_REGISTRY_H_
+
+namespace File_system {
+
+ class Node;
+ class Directory;
+ class File;
+ class Symlink;
+
+ /**
+ * Type trait for determining the node type for a given handle type
+ */
+ template struct Node_type;
+ template<> struct Node_type { typedef Node Type; };
+ template<> struct Node_type { typedef Directory Type; };
+ template<> struct Node_type { typedef File Type; };
+ template<> struct Node_type { typedef Symlink Type; };
+
+
+ /**
+ * Type trait for determining the handle type for a given node type
+ */
+ template struct Handle_type;
+ template<> struct Handle_type { typedef Node_handle Type; };
+ template<> struct Handle_type { typedef Dir_handle Type; };
+ template<> struct Handle_type { typedef File_handle Type; };
+ template<> struct Handle_type { typedef Symlink_handle Type; };
+
+
+ class Node_handle_registry
+ {
+ private:
+
+ /* maximum number of open nodes per session */
+ enum { MAX_NODE_HANDLES = 128U };
+
+ Lock mutable _lock;
+
+ Node *_nodes[MAX_NODE_HANDLES];
+
+ /**
+ * Allocate node handle
+ *
+ * \throw Out_of_node_handles
+ */
+ int _alloc(Node *node)
+ {
+ Lock::Guard guard(_lock);
+
+ for (unsigned i = 0; i < MAX_NODE_HANDLES; i++)
+ if (!_nodes[i]) {
+ _nodes[i] = node;
+ return i;
+ }
+
+ throw Out_of_node_handles();
+ }
+
+ bool _in_range(int handle) const
+ {
+ return ((handle >= 0) && (handle < MAX_NODE_HANDLES));
+ }
+
+ public:
+
+ Node_handle_registry()
+ {
+ for (unsigned i = 0; i < MAX_NODE_HANDLES; i++)
+ _nodes[i] = 0;
+ }
+
+ template
+ typename Handle_type::Type alloc(NODE_TYPE *node)
+ {
+ typedef typename Handle_type::Type Handle;
+ return Handle(_alloc(node));
+ }
+
+ /**
+ * Release node handle
+ */
+ void free(Node_handle handle)
+ {
+ Lock::Guard guard(_lock);
+
+ if (_in_range(handle.value))
+ _nodes[handle.value] = 0;
+ }
+
+ /**
+ * Lookup node using its handle as key
+ *
+ * \throw Invalid_handle
+ */
+ template
+ typename Node_type::Type *lookup(HANDLE_TYPE handle)
+ {
+ Lock::Guard guard(_lock);
+
+ if (!_in_range(handle.value))
+ throw Invalid_handle();
+
+ typedef typename Node_type::Type Node;
+ Node *node = dynamic_cast(_nodes[handle.value]);
+ if (!node)
+ throw Invalid_handle();
+
+ return node;
+ }
+
+ bool refer_to_same_node(Node_handle h1, Node_handle h2) const
+ {
+ Lock::Guard guard(_lock);
+
+ if (!_in_range(h1.value) || !_in_range(h2.value))
+ throw Invalid_handle();
+
+ return _nodes[h1.value] == _nodes[h2.value];
+ }
+ };
+}
+
+#endif /* _NODE_HANDLE_REGISTRY_H_ */
diff --git a/libports/src/server/ffat_fs/target.mk b/libports/src/server/ffat_fs/target.mk
new file mode 100644
index 0000000000..5fc1b0d3b2
--- /dev/null
+++ b/libports/src/server/ffat_fs/target.mk
@@ -0,0 +1,4 @@
+TARGET = ffat_fs
+SRC_CC = main.cc
+LIBS = cxx env server signal ffat_block
+INC_DIR += $(PRG_DIR)
diff --git a/libports/src/server/ffat_fs/util.h b/libports/src/server/ffat_fs/util.h
new file mode 100644
index 0000000000..f7988b10e3
--- /dev/null
+++ b/libports/src/server/ffat_fs/util.h
@@ -0,0 +1,126 @@
+/*
+ * \brief Utilities
+ * \author Norman Feske
+ * \author Christian Prochaska
+ * \date 2012-04-11
+ */
+
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+/* Genode includes */
+#include
+
+
+/**
+ * Return base-name portion of null-terminated path string
+ */
+static inline char const *basename(char const *path)
+{
+ char const *start = path;
+
+ for (; *path; path++)
+ if (*path == '/')
+ start = path + 1;
+
+ return start;
+}
+
+
+/**
+ * Return true if specified path is a base name (contains no path delimiters)
+ */
+static inline bool is_basename(char const *path)
+{
+ for (; *path; path++)
+ if (*path == '/')
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Return true if character 'c' occurs in null-terminated string 'str'
+ */
+static inline bool string_contains(char const *str, char c)
+{
+ for (; *str; str++)
+ if (*str == c)
+ return true;
+ return false;
+}
+
+
+/**
+ * Return true if null-terminated string 'substr' occurs in null-terminated
+ * string 'str'
+ */
+static bool string_contains(char const *str, char const *substr)
+{
+ using namespace Genode;
+
+ size_t str_len = strlen(str);
+ size_t substr_len = strlen(substr);
+
+ if (str_len < substr_len)
+ return false;
+
+ for (size_t i = 0; i <= (str_len - substr_len); i++)
+ if (strcmp(&str[i], substr, substr_len) == 0)
+ return true;
+
+ return false;
+}
+
+
+/**
+ * Return true if 'str' is a valid file name
+ */
+static inline bool valid_filename(char const *str)
+{
+ if (!str) return false;
+
+ /* must have at least one character */
+ if (str[0] == 0) return false;
+
+ /* must not contain '/' or '\' or ':' */
+ if (string_contains(str, '/') ||
+ string_contains(str, '\\') ||
+ string_contains(str, ':'))
+ return false;
+
+ return true;
+}
+
+/**
+ * Return true if 'str' is a valid path
+ */
+static inline bool valid_path(char const *str)
+{
+ if (!str) return false;
+
+ /* must start with '/' */
+ if (str[0] != '/')
+ return false;
+
+ /* must not contain '\' or ':' */
+ if (string_contains(str, '\\') ||
+ string_contains(str, ':'))
+ return false;
+
+ /* must not contain "/../" */
+ if (string_contains(str, "/../")) return false;
+
+ return true;
+}
+
+/**
+ * Return true if 'str' is "/"
+ */
+static inline bool is_root(const char *str)
+{
+ return (Genode::strcmp(str, "/") == 0);
+}
+
+#endif /* _UTIL_H_ */
diff --git a/os/include/file_system_session/file_system_session.h b/os/include/file_system_session/file_system_session.h
index 1c191bdbcd..8eb7f70fb5 100644
--- a/os/include/file_system_session/file_system_session.h
+++ b/os/include/file_system_session/file_system_session.h
@@ -128,7 +128,7 @@ namespace File_system {
*/
enum Mode { STAT_ONLY = 0, READ_ONLY = 1, WRITE_ONLY = 2, READ_WRITE = 3 };
- enum { MAX_NAME_LEN = 128, MAX_PATH_LEN = 1024 };
+ enum { MAX_NAME_LEN = 256, MAX_PATH_LEN = 1024 };
typedef Rpc_in_buffer Name;
typedef Rpc_in_buffer Path;