From 30dc1d83da7af00b97376ab9b7e96d7af1e1a7bc Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Tue, 10 Jul 2012 13:13:03 +0200 Subject: [PATCH] FAT file system service This patch implements a service which provides access to files and directories of a FAT file system via the 'File_system' interface. Fixes #251. --- libports/run/libc_ffat_fs.run | 133 +++ libports/src/lib/libc_fs/plugin.cc | 25 +- libports/src/server/ffat_fs/directory.h | 122 ++ libports/src/server/ffat_fs/file.h | 169 +++ libports/src/server/ffat_fs/main.cc | 1048 +++++++++++++++++ libports/src/server/ffat_fs/node.h | 58 + .../src/server/ffat_fs/node_handle_registry.h | 130 ++ libports/src/server/ffat_fs/target.mk | 4 + libports/src/server/ffat_fs/util.h | 126 ++ .../file_system_session/file_system_session.h | 2 +- 10 files changed, 1804 insertions(+), 13 deletions(-) create mode 100644 libports/run/libc_ffat_fs.run create mode 100644 libports/src/server/ffat_fs/directory.h create mode 100644 libports/src/server/ffat_fs/file.h create mode 100644 libports/src/server/ffat_fs/main.cc create mode 100644 libports/src/server/ffat_fs/node.h create mode 100644 libports/src/server/ffat_fs/node_handle_registry.h create mode 100644 libports/src/server/ffat_fs/target.mk create mode 100644 libports/src/server/ffat_fs/util.h diff --git a/libports/run/libc_ffat_fs.run b/libports/run/libc_ffat_fs.run new file mode 100644 index 000000000..235940432 --- /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 85205bb82..70e754159 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 000000000..f305e3b06 --- /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 000000000..f40ac1696 --- /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 000000000..d8d9542d3 --- /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 000000000..ef8aeadf6 --- /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 000000000..12acf5eb1 --- /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 000000000..5fc1b0d3b --- /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 000000000..f7988b10e --- /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 1c191bdbc..8eb7f70fb 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;