1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libfsserver/lib/directories/ext2_directory.cc Fri Sep 10 23:57:02 2021 +0200
1.3 @@ -0,0 +1,184 @@
1.4 +/*
1.5 + * An object for a directory provided by an Ext2-compatible filesystem.
1.6 + *
1.7 + * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <dirent.h>
1.26 +#include <string.h>
1.27 +
1.28 +#include <thread>
1.29 +
1.30 +#include <fsclient/client.h>
1.31 +
1.32 +#include "ext2_directory.h"
1.33 +
1.34 +
1.35 +
1.36 +/* Common definitions. */
1.37 +
1.38 +#define DIRENT_CORE_SIZE (sizeof(struct dirent) - sizeof(((struct dirent *) 0)->d_name))
1.39 +
1.40 +/* File type conversion. */
1.41 +
1.42 +static int convert_file_type(int type)
1.43 +{
1.44 + switch (type)
1.45 + {
1.46 + case EXT2_FT_REG_FILE: return DT_REG;
1.47 + case EXT2_FT_DIR: return DT_DIR;
1.48 + case EXT2_FT_CHRDEV: return DT_CHR;
1.49 + case EXT2_FT_BLKDEV: return DT_BLK;
1.50 + case EXT2_FT_FIFO: return DT_FIFO;
1.51 + case EXT2_FT_SOCK: return DT_SOCK;
1.52 + case EXT2_FT_SYMLINK: return DT_LNK;
1.53 + default: return DT_UNKNOWN;
1.54 + }
1.55 +}
1.56 +
1.57 +/* Helper function to ensure alignment in generated entries. */
1.58 +
1.59 +static int pad_align(int value)
1.60 +{
1.61 + return value + (sizeof(unsigned int) - (value % sizeof(unsigned int)));
1.62 +}
1.63 +
1.64 +/* Callback function. */
1.65 +
1.66 +static int read_directory_entry(struct ext2_dir_entry *dir_entry, int offset,
1.67 + int blocksize, char *buf, void *priv_data)
1.68 +{
1.69 + (void) offset; (void) blocksize; (void) buf;
1.70 +
1.71 + struct Ext2DirectoryState *dir = reinterpret_cast<struct Ext2DirectoryState *>(priv_data);
1.72 +
1.73 + dir->entry = dir_entry;
1.74 + dir->offset = offset;
1.75 + return dir->directory->write_directory_entry(dir);
1.76 +}
1.77 +
1.78 +/* Thread payload. */
1.79 +
1.80 +static void _read_directory(Ext2Directory *directory, file_t *writer)
1.81 +{
1.82 + directory->read_directory(writer);
1.83 +
1.84 + client_close(writer);
1.85 +}
1.86 +
1.87 +
1.88 +
1.89 +Ext2Directory::~Ext2Directory()
1.90 +{
1.91 +}
1.92 +
1.93 +/* Open the directory for directory entry reading.
1.94 + NOTE: This is mostly the same as the HostDirectory implementation. */
1.95 +
1.96 +long Ext2Directory::_opendir(flags_t flags, offset_t *size, l4_cap_idx_t *cap,
1.97 + object_flags_t *object_flags)
1.98 +{
1.99 + file_t *reader, *writer;
1.100 +
1.101 + // Mapping of the reader's memory region should be avoided because no use
1.102 + // of the reader will be made here.
1.103 +
1.104 + long err = client_pipe(&reader, &writer, 0);
1.105 +
1.106 + if (err)
1.107 + return err;
1.108 +
1.109 + *size = reader->size;
1.110 + *cap = reader->ref;
1.111 + *object_flags = 0; /* does not support mmap, has no fixed size */
1.112 +
1.113 + /* Spawn a independent thread for reading the directory details and writing
1.114 + them to the pipe. */
1.115 +
1.116 + std::thread(_read_directory, this, writer).detach();
1.117 +
1.118 + /* Discard the reader structure but preserve the capability. */
1.119 +
1.120 + reader->ref = L4_INVALID_CAP;
1.121 + client_close(reader);
1.122 +
1.123 + /* Return an indication that the capability will be propagated and not
1.124 + retained. This is explicitly supported by the directory resource. */
1.125 +
1.126 + return IPC_MESSAGE_SENT;
1.127 +}
1.128 +
1.129 +/* Thread payload helper method. */
1.130 +
1.131 +void Ext2Directory::read_directory(file_t *writer)
1.132 +{
1.133 + /* Initialise directory reading state: directory, writer, entry, offset. */
1.134 +
1.135 + struct Ext2DirectoryState dir = {this, writer, NULL, 0};
1.136 +
1.137 + /* Call the handler function for each directory entry. */
1.138 +
1.139 + _ops->directory_iterate((ext2_ino_t) _fileid, read_directory_entry, &dir);
1.140 +}
1.141 +
1.142 +/* Callback method for directory entry output. */
1.143 +
1.144 +int Ext2Directory::write_directory_entry(struct Ext2DirectoryState *dir)
1.145 +{
1.146 + struct ext2_inode inode;
1.147 +
1.148 + /* Obtain the inode details for metadata. */
1.149 +
1.150 + if (_ops->read_inode(dir->entry->inode, &inode))
1.151 + return DIRENT_ABORT;
1.152 +
1.153 + /* Align the size of the entry to avoid problems on architectures which
1.154 + require aligned accesses and where the compiler needs to assume an
1.155 + aligned structure. */
1.156 +
1.157 + offset_t namelen = ext2fs_dirent_name_len(dir->entry);
1.158 + offset_t reclen = pad_align(DIRENT_CORE_SIZE + namelen);
1.159 +
1.160 + /* Construct a directory entry structure of the calculated size. */
1.161 +
1.162 + char buffer[reclen];
1.163 + struct dirent *dirent = (struct dirent *) buffer;
1.164 +
1.165 + dirent->d_ino = dir->entry->inode;
1.166 + dirent->d_off = dir->offset;
1.167 + dirent->d_reclen = reclen;
1.168 + dirent->d_type = convert_file_type(ext2fs_dirent_file_type(dir->entry));
1.169 +
1.170 + /* Copy the name, padding the memory after it to the alignment boundary. */
1.171 +
1.172 + memcpy(dirent->d_name, dir->entry->name, namelen);
1.173 + memset(dirent->d_name + namelen, 0, reclen - namelen);
1.174 +
1.175 + /* Write the structure to the pipe. */
1.176 +
1.177 + offset_t nwritten = client_write(dir->writer, (const void *) dirent, reclen);
1.178 +
1.179 + /* Stop writing if the pipe is closed. */
1.180 +
1.181 + if (nwritten < reclen)
1.182 + return DIRENT_ABORT;
1.183 +
1.184 + return 0;
1.185 +}
1.186 +
1.187 +// vim: tabstop=4 expandtab shiftwidth=4