mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-29 13:44:26 +00:00
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.
This commit is contained in:
parent
763f628831
commit
30dc1d83da
133
libports/run/libc_ffat_fs.run
Normal file
133
libports/run/libc_ffat_fs.run
Normal file
@ -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 {
|
||||||
|
<config>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="ROM"/>
|
||||||
|
<service name="RAM"/>
|
||||||
|
<service name="CAP"/>
|
||||||
|
<service name="IRQ"/>
|
||||||
|
<service name="IO_MEM"/>
|
||||||
|
<service name="IO_PORT"/>
|
||||||
|
<service name="PD"/>
|
||||||
|
<service name="RM"/>
|
||||||
|
<service name="CPU"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
<service name="SIGNAL"/>
|
||||||
|
</parent-provides>
|
||||||
|
<default-route>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</default-route>
|
||||||
|
<start name="timer">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides> <service name="Timer"/> </provides>
|
||||||
|
</start>
|
||||||
|
<start name="ffat_fs">
|
||||||
|
<resource name="RAM" quantum="4M"/>
|
||||||
|
<provides> <service name="File_system"/> </provides>
|
||||||
|
<config> <policy label="" root="/" writeable="yes" /> </config>
|
||||||
|
</start>
|
||||||
|
<start name="test-libc_fs">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
</start>
|
||||||
|
}
|
||||||
|
|
||||||
|
append_if [have_spec pci] config {
|
||||||
|
<start name="pci_drv">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
<provides> <service name="PCI"/> </provides>
|
||||||
|
</start>
|
||||||
|
<start name="atapi_drv">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides> <service name="Block"/> </provides>
|
||||||
|
<config ata="yes" />
|
||||||
|
</start>
|
||||||
|
}
|
||||||
|
|
||||||
|
append_if [expr [have_spec pl180] || [have_spec omap4]] config {
|
||||||
|
<start name="sd_card_drv">
|
||||||
|
<resource name="RAM" quantum="1M" />
|
||||||
|
<provides><service name="Block"/></provides>
|
||||||
|
</start>
|
||||||
|
}
|
||||||
|
|
||||||
|
append config {
|
||||||
|
</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 :
|
@ -121,7 +121,7 @@ class Plugin_context : public Libc::Plugin_context,
|
|||||||
: _type(TYPE_FILE), _node_handle(handle), _seek_offset(~0), in_flight(false) { }
|
: _type(TYPE_FILE), _node_handle(handle), _seek_offset(~0), in_flight(false) { }
|
||||||
|
|
||||||
Plugin_context(File_system::Dir_handle handle)
|
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)
|
Plugin_context(File_system::Symlink_handle handle)
|
||||||
: _type(TYPE_SYMLINK), _node_handle(handle), _seek_offset(~0), in_flight(false) { }
|
: _type(TYPE_SYMLINK), _node_handle(handle), _seek_offset(~0), in_flight(false) { }
|
||||||
@ -346,6 +346,11 @@ class Plugin : public Libc::Plugin
|
|||||||
return -1;
|
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,
|
ssize_t getdirentries(Libc::File_descriptor *fd, char *buf,
|
||||||
::size_t nbytes, ::off_t *basep)
|
::size_t nbytes, ::off_t *basep)
|
||||||
{
|
{
|
||||||
@ -356,16 +361,9 @@ class Plugin : public Libc::Plugin
|
|||||||
return -1;
|
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;
|
Directory_entry entry;
|
||||||
size_t num_bytes = read(fd, &entry, sizeof(entry));
|
size_t num_bytes = read(fd, &entry, sizeof(entry));
|
||||||
|
|
||||||
context(fd)->seek_offset(orig_seek_offset);
|
|
||||||
|
|
||||||
/* detect end of directory entries */
|
/* detect end of directory entries */
|
||||||
if (num_bytes == 0)
|
if (num_bytes == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@ -384,7 +382,7 @@ class Plugin : public Libc::Plugin
|
|||||||
case Directory_entry::TYPE_SYMLINK: dirent->d_type = DT_LNK; break;
|
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);
|
dirent->d_reclen = sizeof(struct dirent);
|
||||||
|
|
||||||
Genode::strncpy(dirent->d_name, entry.name, sizeof(dirent->d_name));
|
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)
|
int mkdir(const char *path, mode_t mode)
|
||||||
{
|
{
|
||||||
|
Canonical_path canonical_path(path);
|
||||||
|
|
||||||
try {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (File_system::Permission_denied) { errno = EPERM; }
|
catch (File_system::Permission_denied) { errno = EPERM; }
|
||||||
@ -470,8 +472,7 @@ class Plugin : public Libc::Plugin
|
|||||||
if (path.str[i] == '/')
|
if (path.str[i] == '/')
|
||||||
last_slash = i;
|
last_slash = i;
|
||||||
|
|
||||||
char dir_path[256];
|
char dir_path[256] = "/";
|
||||||
dir_path[0] = 0;
|
|
||||||
if (last_slash > 0)
|
if (last_slash > 0)
|
||||||
Genode::strncpy(dir_path, path.str,
|
Genode::strncpy(dir_path, path.str,
|
||||||
Genode::min(sizeof(dir_path), last_slash + 1));
|
Genode::min(sizeof(dir_path), last_slash + 1));
|
||||||
|
122
libports/src/server/ffat_fs/directory.h
Normal file
122
libports/src/server/ffat_fs/directory.h
Normal file
@ -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 <ffat/ff.h>
|
||||||
|
} }
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <node.h>
|
||||||
|
|
||||||
|
|
||||||
|
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_ */
|
169
libports/src/server/ffat_fs/file.h
Normal file
169
libports/src/server/ffat_fs/file.h
Normal file
@ -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 <ffat/ff.h>
|
||||||
|
} }
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <node.h>
|
||||||
|
|
||||||
|
|
||||||
|
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_ */
|
1048
libports/src/server/ffat_fs/main.cc
Normal file
1048
libports/src/server/ffat_fs/main.cc
Normal file
File diff suppressed because it is too large
Load Diff
58
libports/src/server/ffat_fs/node.h
Normal file
58
libports/src/server/ffat_fs/node.h
Normal file
@ -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 <ffat/ff.h>
|
||||||
|
} }
|
||||||
|
|
||||||
|
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_ */
|
130
libports/src/server/ffat_fs/node_handle_registry.h
Normal file
130
libports/src/server/ffat_fs/node_handle_registry.h
Normal file
@ -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<typename T> struct Node_type;
|
||||||
|
template<> struct Node_type<Node_handle> { typedef Node Type; };
|
||||||
|
template<> struct Node_type<Dir_handle> { typedef Directory Type; };
|
||||||
|
template<> struct Node_type<File_handle> { typedef File Type; };
|
||||||
|
template<> struct Node_type<Symlink_handle> { typedef Symlink Type; };
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type trait for determining the handle type for a given node type
|
||||||
|
*/
|
||||||
|
template<typename T> struct Handle_type;
|
||||||
|
template<> struct Handle_type<Node> { typedef Node_handle Type; };
|
||||||
|
template<> struct Handle_type<Directory> { typedef Dir_handle Type; };
|
||||||
|
template<> struct Handle_type<File> { typedef File_handle Type; };
|
||||||
|
template<> struct Handle_type<Symlink> { 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 NODE_TYPE>
|
||||||
|
typename Handle_type<NODE_TYPE>::Type alloc(NODE_TYPE *node)
|
||||||
|
{
|
||||||
|
typedef typename Handle_type<NODE_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 HANDLE_TYPE>
|
||||||
|
typename Node_type<HANDLE_TYPE>::Type *lookup(HANDLE_TYPE handle)
|
||||||
|
{
|
||||||
|
Lock::Guard guard(_lock);
|
||||||
|
|
||||||
|
if (!_in_range(handle.value))
|
||||||
|
throw Invalid_handle();
|
||||||
|
|
||||||
|
typedef typename Node_type<HANDLE_TYPE>::Type Node;
|
||||||
|
Node *node = dynamic_cast<Node *>(_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_ */
|
4
libports/src/server/ffat_fs/target.mk
Normal file
4
libports/src/server/ffat_fs/target.mk
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
TARGET = ffat_fs
|
||||||
|
SRC_CC = main.cc
|
||||||
|
LIBS = cxx env server signal ffat_block
|
||||||
|
INC_DIR += $(PRG_DIR)
|
126
libports/src/server/ffat_fs/util.h
Normal file
126
libports/src/server/ffat_fs/util.h
Normal file
@ -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 <util/string.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_ */
|
@ -128,7 +128,7 @@ namespace File_system {
|
|||||||
*/
|
*/
|
||||||
enum Mode { STAT_ONLY = 0, READ_ONLY = 1, WRITE_ONLY = 2, READ_WRITE = 3 };
|
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<MAX_NAME_LEN> Name;
|
typedef Rpc_in_buffer<MAX_NAME_LEN> Name;
|
||||||
typedef Rpc_in_buffer<MAX_PATH_LEN> Path;
|
typedef Rpc_in_buffer<MAX_PATH_LEN> Path;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user