From 8ba26c612a153ee8b32aefc41f1d5c7f6fc2fb5c Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 31 Mar 2018 17:18:47 +0200 Subject: [PATCH] FatFS watch support Implement watch support using a watch handle list. Test at run/fs_rom_update_fat. Ref #1934 --- repos/libports/run/fs_rom_update_fat.run | 139 ++++++++++ repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc | 256 +++++++++++++++--- 2 files changed, 362 insertions(+), 33 deletions(-) create mode 100644 repos/libports/run/fs_rom_update_fat.run diff --git a/repos/libports/run/fs_rom_update_fat.run b/repos/libports/run/fs_rom_update_fat.run new file mode 100644 index 000000000..e81b561a4 --- /dev/null +++ b/repos/libports/run/fs_rom_update_fat.run @@ -0,0 +1,139 @@ +# +# Build +# +set build_components { + core init + app/rom_logger + app/rom_to_file + drivers/timer + lib/vfs/fatfs + server/dynamic_rom + server/fs_rom + server/ram_blk + server/vfs +} + +build $build_components + +create_boot_directory + +set mkfs_cmd [check_installed mkfs.vfat] + +catch { exec $mkfs_cmd -C bin/fat.img -n "ROM_UPDATE" 64 } + +# +# Generate config +# +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +# generic modules +set boot_modules { + core ld.lib.so init + dynamic_rom + fat.img + fs_rom + ram_blk + rom_logger + rom_to_file + timer + vfs + vfs_fatfs.lib.so +} + +build_boot_image $boot_modules + +append qemu_args " -nographic" + +run_genode_until {.*.*} 60 + +file delete bin/fat.img diff --git a/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc b/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc index 7636616a0..967d3ff45 100644 --- a/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc +++ b/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc @@ -43,7 +43,12 @@ class Fatfs::File_system : public Vfs::File_system typedef Genode::Path Path; struct Fatfs_file_handle; - typedef Genode::List Fatfs_file_handles; + struct Fatfs_dir_handle; + struct Fatfs_file_watch_handle; + struct Fatfs_dir_watch_handle; + typedef Genode::List Fatfs_file_handles; + typedef Genode::List Fatfs_dir_watch_handles; + typedef Genode::List Fatfs_watch_handles; /** * The FatFS library does not support opening a file @@ -53,9 +58,13 @@ class Fatfs::File_system : public Vfs::File_system struct File : Genode::Avl_node { - Path path; - Fatfs::FIL fil; - Fatfs_file_handles handles; + Path path; + Fatfs::FIL fil; + Fatfs_file_handles handles; + Fatfs_watch_handles watchers; + + bool opened() const { + return (handles.first() || watchers.first()); } /************************ ** Avl node interface ** @@ -84,9 +93,30 @@ class Fatfs::File_system : public Vfs::File_system file_size &out_count) = 0; }; + struct Fatfs_file_watch_handle : Vfs_watch_handle, Fatfs_watch_handles::Element + { + File *file; + + Fatfs_file_watch_handle(Vfs::File_system &fs, + Allocator &alloc, + File &file) + : Vfs_watch_handle(fs, alloc), file(&file) { } + }; + + struct Fatfs_dir_watch_handle : Vfs_watch_handle, Fatfs_dir_watch_handles::Element + { + Path const path; + + Fatfs_dir_watch_handle(Vfs::File_system &fs, + Allocator &alloc, + Path const &path) + : Vfs_watch_handle(fs, alloc), path(path) { } + }; + struct Fatfs_file_handle : Fatfs_handle, Fatfs_file_handles::Element { File *file = nullptr; + bool modifying = false; Fatfs_file_handle(File_system &fs, Allocator &alloc, int status_flags) : Fatfs_handle(fs, fs, alloc, status_flags) { } @@ -126,10 +156,12 @@ class Fatfs::File_system : public Vfs::File_system struct Fatfs_dir_handle : Fatfs_handle { + file_size cur_index = 0; + Path const path; DIR dir; - Fatfs_dir_handle(File_system &fs, Allocator &alloc) - : Fatfs_handle(fs, fs, alloc, 0) { } + Fatfs_dir_handle(File_system &fs, Allocator &alloc, char const *path) + : Fatfs_handle(fs, fs, alloc, 0), path(path) { } Read_result complete_read(char *buf, file_size buf_size, @@ -143,6 +175,11 @@ class Fatfs::File_system : public Vfs::File_system return READ_ERR_INVALID; file_size dir_index = seek() / sizeof(Dirent); + if (dir_index < cur_index) { + /* reset the seek position */ + f_readdir(&dir, nullptr); + cur_index = 0; + } Dirent *vfs_dir = (Dirent*)buf; @@ -150,15 +187,18 @@ class Fatfs::File_system : public Vfs::File_system FRESULT res; vfs_dir->fileno = 1; /* inode 0 is a pending unlink */ - do { + while (cur_index <= dir_index) { res = f_readdir (&dir, &info); if ((res != FR_OK) || (!info.fname[0])) { + f_readdir(&dir, nullptr); + cur_index = 0; vfs_dir->type = DIRENT_TYPE_END; vfs_dir->name[0] = '\0'; out_count = sizeof(Dirent); return READ_OK; } - } while (dir_index-- > 0); + cur_index++; + } vfs_dir->type = (info.fattrib & AM_DIR) ? DIRENT_TYPE_DIRECTORY : DIRENT_TYPE_FILE; @@ -170,11 +210,15 @@ class Fatfs::File_system : public Vfs::File_system } }; - Genode::Env &_env; - Genode::Allocator &_alloc; + Genode::Env &_env; + Genode::Allocator &_alloc; + Vfs::Io_response_handler &_io_handler; FATFS _fatfs; + /* List of all open directory handles */ + Fatfs_dir_watch_handles _dir_watchers; + /* Tree of open FatFS file objects */ Genode::Avl_tree _open_files; @@ -190,6 +234,30 @@ class Fatfs::File_system : public Vfs::File_system _open_files.first()->lookup(path) : nullptr; } + /** + * Notify the application for each handle on a given file. + */ + void _notify(File &file) + { + for (Fatfs_file_watch_handle *h = file.watchers.first(); h; h = h->next()) + _io_handler.handle_watch_response(h->context()); + } + + /** + * Notify the application for each handle on the parent + * directory of a given path. + */ + void _notify_parent_of(char const *path) + { + Path parent(path); + parent.strip_last_element(); + + for (Fatfs_dir_watch_handle *h = _dir_watchers.first(); h; h = h->next()) { + if (h->path == parent) + _io_handler.handle_watch_response(h->context()); + } + } + /** * Close an open FatFS file */ @@ -215,21 +283,31 @@ class Fatfs::File_system : public Vfs::File_system void _close_all(File &file) { /* invalidate handles */ - for (Fatfs_file_handle *handle = file.handles.first(); + for (auto *handle = file.handles.first(); handle; handle = file.handles.first()) { handle->file = nullptr; file.handles.remove(handle); } + + for (auto *handle = file.watchers.first(); + handle; handle = file.watchers.first()) + { + handle->file = nullptr; + file.watchers.remove(handle); + if (auto *ctx = handle->context()) + _io_handler.handle_watch_response(ctx); + } _close(file); } public: - File_system(Genode::Env &env, - Genode::Allocator &alloc, - Genode::Xml_node config) - : _env(env), _alloc(alloc) + File_system(Genode::Env &env, + Genode::Allocator &alloc, + Genode::Xml_node config, + Vfs::Io_response_handler &io_handler) + : _env(env), _alloc(alloc), _io_handler(io_handler) { { static unsigned codepage = 0; @@ -302,10 +380,10 @@ class Fatfs::File_system : public Vfs::File_system Fatfs_file_handle *handle; File *file = _opened_file(path); + bool create = vfs_mode & OPEN_MODE_CREATE; if (file && create) { - Genode::error("OPEN_ERR_EXISTS"); return OPEN_ERR_EXISTS; } @@ -340,6 +418,9 @@ class Fatfs::File_system : public Vfs::File_system _next_file = nullptr; } + if (create) + _notify_parent_of(path); + file->handles.insert(handle); handle->file = file; *vfs_handle = handle; @@ -353,7 +434,7 @@ class Fatfs::File_system : public Vfs::File_system Fatfs_dir_handle *handle; /* attempt allocation before modifying blocks */ - handle = new (alloc) Fatfs_dir_handle(*this, alloc); + handle = new (alloc) Fatfs_dir_handle(*this, alloc, path); if (create) { FRESULT res = f_mkdir((const TCHAR*)path); @@ -385,28 +466,29 @@ class Fatfs::File_system : public Vfs::File_system void close(Vfs_handle *vfs_handle) override { { - Fatfs_file_handle *handle; - - handle = dynamic_cast(vfs_handle); + auto *handle = dynamic_cast(vfs_handle); + bool notify = false; if (handle) { File *file = handle->file; if (file) { file->handles.remove(handle); - if (!file->handles.first()) + if (file->opened()) { + notify = handle->modifying; + } else { _close(*file); - else - f_sync(&file->fil); + } } destroy(handle->alloc(), handle); + + if (notify) + _notify(*file); return; } } { - Fatfs_dir_handle *handle; - - handle = dynamic_cast(vfs_handle); + auto *handle = dynamic_cast(vfs_handle); if (handle) { f_closedir(&handle->dir); @@ -415,6 +497,76 @@ class Fatfs::File_system : public Vfs::File_system } } + Watch_result watch(char const *path, + Vfs_watch_handle **handle, + Allocator &alloc) override + { + /* + * checking for the presence of an open file is + * cheaper than calling dircetory and reading blocks + */ + File *file = _opened_file(path); + + if (!file && directory(path)) { + auto *watch_handle = new (alloc) + Fatfs_dir_watch_handle(*this, alloc, path); + _dir_watchers.insert(watch_handle); + *handle = watch_handle; + return WATCH_OK; + } else { + if (!file) { + if (!_next_file) + _next_file = new (_alloc) File(); + + file = _next_file; + FRESULT fres = f_open( + &file->fil, (TCHAR const *)path, + FA_READ | FA_WRITE | FA_OPEN_EXISTING); + if (fres != FR_OK) { + return WATCH_ERR_UNACCESSIBLE; + } + + file->path.import(path); + _open_files.insert(file); + _next_file = nullptr; + } + + auto *watch_handle = new (alloc) + Fatfs_file_watch_handle(*this, alloc, *file); + file->watchers.insert(watch_handle); + *handle = watch_handle; + return WATCH_OK; + } + return WATCH_ERR_UNACCESSIBLE; + } + + void close(Vfs_watch_handle *vfs_handle) override + { + { + auto *handle = + dynamic_cast(vfs_handle); + + if (handle) { + File *file = handle->file; + if (file) + file->watchers.remove(handle); + destroy(handle->alloc(), handle); + return; + } + } + + { + auto *handle = + dynamic_cast(vfs_handle); + + if (handle) { + _dir_watchers.remove(handle); + destroy(handle->alloc(), handle); + } + } + } + + Genode::Dataspace_capability dataspace(char const *path) override { Genode::warning(__func__, " not implemented in FAT plugin"); @@ -441,6 +593,8 @@ class Fatfs::File_system : public Vfs::File_system bool directory(char const *path) override { + if (path[0] == '/' && path[1] == '\0') return true; + FILINFO fno; return f_stat((const TCHAR*)path, &fno) == FR_OK ? @@ -502,20 +656,26 @@ class Fatfs::File_system : public Vfs::File_system Unlink_result unlink(char const *path) override { /* close the file if it is open */ - if (File *file = _opened_file(path)) + if (File *file = _opened_file(path)) { + _notify(*file); _close_all(*file); + } switch (f_unlink((const TCHAR*)path)) { - case FR_OK: return UNLINK_OK; + case FR_OK: break; case FR_NO_FILE: case FR_NO_PATH: return UNLINK_ERR_NO_ENTRY; default: return UNLINK_ERR_NO_PERM; } + + _notify_parent_of(path); + return UNLINK_OK; } Rename_result rename(char const *from, char const *to) override { if (File *to_file = _opened_file(to)) { + _notify(*to_file); _close_all(*to_file); f_unlink((TCHAR const *)to); } else { @@ -529,15 +689,22 @@ class Fatfs::File_system : public Vfs::File_system } } - if (File *from_file = _opened_file(from)) + if (File *from_file = _opened_file(from)) { + _notify(*from_file); _close_all(*from_file); + } switch (f_rename((const TCHAR*)from, (const TCHAR*)to)) { - case FR_OK: return RENAME_OK; + case FR_OK: break; case FR_NO_FILE: case FR_NO_PATH: return RENAME_ERR_NO_ENTRY; default: return RENAME_ERR_NO_PERM; } + + _notify_parent_of(from); + if (Genode::strcmp(from, to) != 0) + _notify_parent_of(to); + return RENAME_OK; } @@ -578,11 +745,13 @@ class Fatfs::File_system : public Vfs::File_system UINT bw = 0; fres = f_write(fil, buf, buf_size, &bw); f_sync(fil); + handle->modifying = true; out_count = bw; } switch (fres) { - case FR_OK: return WRITE_OK; + case FR_OK: + return WRITE_OK; case FR_INVALID_OBJECT: return WRITE_ERR_INVALID; case FR_TIMEOUT: return WRITE_ERR_WOULD_BLOCK; default: return WRITE_ERR_IO; @@ -622,11 +791,32 @@ class Fatfs::File_system : public Vfs::File_system handle->seek(len); } + handle->modifying = true; + return res == FR_OK ? FTRUNCATE_OK : FTRUNCATE_ERR_NO_PERM; } bool read_ready(Vfs_handle *) override { return true; } + + /** + * Notify other handles if this handle has modified its file. + * + * Files are flushed to blocks after every write. + */ + Sync_result complete_sync(Vfs_handle *vfs_handle) override + { + Fatfs_file_handle *handle = + static_cast(vfs_handle); + if (handle && handle->file && handle->modifying) { + File &file = *handle->file; + handle->modifying = false; + file.handles.remove(handle); + _notify(file); + file.handles.insert(handle); + } + return SYNC_OK; + } }; @@ -640,11 +830,11 @@ struct Fatfs_factory : Vfs::File_system_factory Vfs::File_system *create(Genode::Env &env, Genode::Allocator &alloc, Genode::Xml_node node, - Vfs::Io_response_handler &, + Vfs::Io_response_handler &handler, Vfs::File_system &) override { return new (alloc) - Fatfs::File_system(env, alloc, node); + Fatfs::File_system(env, alloc, node, handler); } };