# HG changeset patch # User Paul Boddie # Date 1628271126 -7200 # Node ID 1bab9ddcac25d50f77e5c162bb5722c71056da44 # Parent eca2519bb8ee755c92fe48441e897c2bb7554188 Added support for listing directories in ext2-based filesystems. diff -r eca2519bb8ee -r 1bab9ddcac25 conf/dstest_file_readdir.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_readdir.cfg Fri Aug 06 19:32:06 2021 +0200 @@ -0,0 +1,51 @@ +-- vim:set ft=lua: + +local L4 = require("L4"); + +local l = L4.default_loader; + +local pipe_server = l:new_channel(); + +l:startv({ + caps = { + server = pipe_server:svr(), + }, + log = { "pipes", "r" }, + }, + "rom/dstest_pipe_server", "10"); + +local block_server = l:new_channel(); + +l:startv({ + caps = { + server = block_server:svr(), + }, + log = { "blocksvr", "r" }, + }, + "rom/dstest_block_server", "10"); + +local ext2svr = l:new_channel(); + +l:startv({ + caps = { + blocksvr = block_server, + pipes = pipe_server, + ext2svr = ext2svr:svr(), + }, + log = { "ext2svr", "y" }, + }, + "rom/dstest_ext2_server", "blocksvr", "rom/e2test.fs", "10", "ext2svr"); + +-- Obtain user filesystems with umask 0022 (18). + +local open_for_user = 6; +local ext2svr_paulb = L4.cast(L4.Proto.Factory, ext2svr):create(open_for_user, 1000, 1000, 18); + +l:startv({ + caps = { + server = ext2svr_paulb, + }, + log = { "client", "g" }, + }, + -- program, file to create + "rom/dstest_file_readdir", "home/paulb/many"); diff -r eca2519bb8ee -r 1bab9ddcac25 conf/dstest_file_readdir.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file_readdir.list Fri Aug 06 19:32:06 2021 +0200 @@ -0,0 +1,28 @@ +entry dstest_file_readdir +roottask moe rom/dstest_file_readdir.cfg +module dstest_file_readdir.cfg +module e2test.fs +module l4re +module ned +module dstest_file_readdir +module dstest_ext2_server +module dstest_block_server +module dstest_pipe_server +module lib4re-c.so +module lib4re-c-util.so +module lib4re.so +module lib4re-util.so +module libc_be_l4refile.so +module libc_be_l4re.so +module libc_be_socket_noop.so +module libc_support_misc.so +module libdl.so +module libipc.so +module libl4sys-direct.so +module libl4sys.so +module libl4util.so +module libld-l4.so +module libpthread.so +module libstdc++.so +module libsupc++.so +module libuc_c.so diff -r eca2519bb8ee -r 1bab9ddcac25 libfsserver/include/fsserver/ext2_file_opener.h --- a/libfsserver/include/fsserver/ext2_file_opener.h Fri Aug 06 19:30:20 2021 +0200 +++ b/libfsserver/include/fsserver/ext2_file_opener.h Fri Aug 06 19:32:06 2021 +0200 @@ -23,12 +23,19 @@ #include +#include #include #include #include +/* Forward declarations. */ + +struct Ext2FileOpenerDir; + + + /* Support for providing access to files. */ class Ext2FileOpener : public OpenerResource @@ -43,6 +50,10 @@ virtual bool accessing_file(const char *path, flags_t flags, fileid_t fileid); + /* Convenience methods obtaining different pager types. */ + + virtual long get_directory(const char *path, flags_t flags, fileid_t fileid, offset_t *size, l4_cap_idx_t *cap); + public: explicit Ext2FileOpener(FilePaging *paging, Ext2FileOperations *ops, user_t user) : OpenerResource(paging), _ops(ops), _user(user) @@ -56,6 +67,24 @@ virtual long get_fileid(const char *path, flags_t flags, fileid_t *fileid); virtual long make_accessor(const char *path, flags_t flags, fileid_t fileid, Accessor **accessor); + + /* Helper methods. */ + + void read_directory(fileid_t fileid, file_t *writer); + + int write_directory_entry(struct Ext2FileOpenerDir *dir); +}; + + + +/* Helper structures. */ + +struct Ext2FileOpenerDir +{ + Ext2FileOpener *opener; + file_t *writer; + struct ext2_dir_entry *entry; + long offset; }; // vim: tabstop=4 expandtab shiftwidth=4 diff -r eca2519bb8ee -r 1bab9ddcac25 libfsserver/include/fsserver/ext2_file_operations.h --- a/libfsserver/include/fsserver/ext2_file_operations.h Fri Aug 06 19:30:20 2021 +0200 +++ b/libfsserver/include/fsserver/ext2_file_operations.h Fri Aug 06 19:32:06 2021 +0200 @@ -64,6 +64,12 @@ offset_t read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size); void write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size); + + long directory_iterate(ext2_ino_t dir, + int func(struct ext2_dir_entry *, int, int, char *, void *), + void *priv_data); + + long read_inode(ext2_ino_t ino_file, struct ext2_inode *inode); }; // vim: tabstop=4 expandtab shiftwidth=4 diff -r eca2519bb8ee -r 1bab9ddcac25 libfsserver/lib/files/ext2_file_opener.cc --- a/libfsserver/lib/files/ext2_file_opener.cc Fri Aug 06 19:30:20 2021 +0200 +++ b/libfsserver/lib/files/ext2_file_opener.cc Fri Aug 06 19:32:06 2021 +0200 @@ -19,13 +19,83 @@ * Boston, MA 02110-1301, USA */ +#include +#include + +#include + #include - +#include #include #include "ext2_file_accessor.h" #include "ext2_file_opener.h" + + +/* Common definitions. */ + +#define DIRENT_CORE_SIZE (sizeof(struct dirent) - sizeof(((struct dirent *) 0)->d_name)) + +/* File type conversion. */ + +static int convert_file_type(int type) +{ + switch (type) + { + case EXT2_FT_REG_FILE: return DT_REG; + case EXT2_FT_DIR: return DT_DIR; + case EXT2_FT_CHRDEV: return DT_CHR; + case EXT2_FT_BLKDEV: return DT_BLK; + case EXT2_FT_FIFO: return DT_FIFO; + case EXT2_FT_SOCK: return DT_SOCK; + case EXT2_FT_SYMLINK: return DT_LNK; + default: return DT_UNKNOWN; + } +} + +/* Helper function to ensure alignment in generated entries. */ + +static int pad_align(int value) +{ + return value + (sizeof(unsigned int) - (value % sizeof(unsigned int))); +} + +/* Callback function. */ + +static int read_directory_entry(struct ext2_dir_entry *dir_entry, int offset, + int blocksize, char *buf, void *priv_data) +{ + (void) offset; (void) blocksize; (void) buf; + + struct Ext2FileOpenerDir *dir = reinterpret_cast(priv_data); + + dir->entry = dir_entry; + dir->offset = offset; + return dir->opener->write_directory_entry(dir); +} + +/* Thread payload. */ + +static void _read_directory(Ext2FileOpener *opener, fileid_t fileid, file_t *writer) +{ + /* Subscribe to space and closure notifications on the pipe. */ + + long err = client_set_blocking(writer, NOTIFY_SPACE_AVAILABLE); + + if (err) + { + client_close(writer); + return; + } + + opener->read_directory(fileid, writer); + + client_close(writer); +} + + + Ext2FileOpener::~Ext2FileOpener() { } @@ -46,6 +116,93 @@ return _ops->is_file((ext2_ino_t) fileid); } +// NOTE: This is mostly the same as the HostFileOpener implementation. + +long Ext2FileOpener::get_directory(const char *path, flags_t flags, fileid_t fileid, offset_t *size, l4_cap_idx_t *cap) +{ + (void) path; (void) flags; + + file_t *reader, *writer; + + // NOTE: Might be more appropriate to use lower-level file operations to + // NOTE: avoid unnecessary mapping of the reader's memory region. + + long err = client_pipe(&reader, &writer); + + if (err) + return err; + + *size = reader->size; + *cap = reader->ref; + + /* Discard the reader structure but do not close the reader itself. */ + + delete reader; + + /* Spawn a independent thread for reading the directory details and writing + them to the pipe. */ + + std::thread(_read_directory, this, fileid, writer).detach(); + + return L4_EOK; +} + +/* Thread payload helper method. */ + +void Ext2FileOpener::read_directory(fileid_t fileid, file_t *writer) +{ + /* Initialise directory reading state: opener, writer, entry, offset. */ + + struct Ext2FileOpenerDir dir = {this, writer, NULL, 0}; + + /* Call the handler function for each directory entry. */ + + _ops->directory_iterate((ext2_ino_t) fileid, read_directory_entry, &dir); +} + +/* Callback method for directory entry output. */ + +int Ext2FileOpener::write_directory_entry(struct Ext2FileOpenerDir *dir) +{ + struct ext2_inode inode; + + /* Obtain the inode details for metadata. */ + + if (_ops->read_inode(dir->entry->inode, &inode)) + return DIRENT_ABORT; + + /* Align the size of the entry to avoid problems on architectures which + require aligned accesses and where the compiler needs to assume an + aligned structure. */ + + offset_t namelen = ext2fs_dirent_name_len(dir->entry); + offset_t reclen = pad_align(DIRENT_CORE_SIZE + namelen); + + /* Construct a directory entry structure of the calculated size. */ + + char buffer[reclen]; + struct dirent *dirent = (struct dirent *) buffer; + + dirent->d_ino = dir->entry->inode; + dirent->d_off = dir->offset; + dirent->d_reclen = reclen; + dirent->d_type = convert_file_type(ext2fs_dirent_file_type(dir->entry)); + + /* Copy the name, padding the memory after it to the alignment boundary. */ + + memcpy(dirent->d_name, dir->entry->name, namelen); + memset(dirent->d_name + namelen, 0, reclen - namelen); + + /* Write the structure to the pipe. */ + + offset_t nwritten = 0; + + while (nwritten < reclen) + nwritten += client_write(dir->writer, (const void *) (dirent + nwritten), reclen - nwritten); + + return 0; +} + /* Return a file identifier for the given 'path'. */ long Ext2FileOpener::get_fileid(const char *path, flags_t flags, fileid_t *fileid) diff -r eca2519bb8ee -r 1bab9ddcac25 libfsserver/lib/files/ext2_file_operations.cc --- a/libfsserver/lib/files/ext2_file_operations.cc Fri Aug 06 19:30:20 2021 +0200 +++ b/libfsserver/lib/files/ext2_file_operations.cc Fri Aug 06 19:32:06 2021 +0200 @@ -155,4 +155,28 @@ ext2fs_file_write(file, addr, size, NULL); } +/* Initiate iteration over a directory, with the given 'func' being called + with directory entry details for each entry. */ + +long Ext2FileOperations::directory_iterate(ext2_ino_t dir, + int func(struct ext2_dir_entry *, int, int, + char *, void *), + void *priv_data) +{ + if (ext2fs_dir_iterate(_fs, dir, 0, 0, func, priv_data)) + return -L4_EIO; + + return L4_EOK; +} + +long Ext2FileOperations::read_inode(ext2_ino_t ino_file, struct ext2_inode *inode) +{ + std::lock_guard guard(_lock); + + if (ext2fs_read_inode(_fs, ino_file, inode)) + return -L4_EIO; + + return L4_EOK; +} + // vim: tabstop=4 expandtab shiftwidth=4