paul@202 | 1 | /* |
paul@202 | 2 | * An object for a directory provided by an Ext2-compatible filesystem. |
paul@202 | 3 | * |
paul@417 | 4 | * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> |
paul@202 | 5 | * |
paul@202 | 6 | * This program is free software; you can redistribute it and/or |
paul@202 | 7 | * modify it under the terms of the GNU General Public License as |
paul@202 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@202 | 9 | * the License, or (at your option) any later version. |
paul@202 | 10 | * |
paul@202 | 11 | * This program is distributed in the hope that it will be useful, |
paul@202 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@202 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@202 | 14 | * GNU General Public License for more details. |
paul@202 | 15 | * |
paul@202 | 16 | * You should have received a copy of the GNU General Public License |
paul@202 | 17 | * along with this program; if not, write to the Free Software |
paul@202 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@202 | 19 | * Boston, MA 02110-1301, USA |
paul@202 | 20 | */ |
paul@202 | 21 | |
paul@202 | 22 | #include <dirent.h> |
paul@202 | 23 | #include <string.h> |
paul@202 | 24 | |
paul@202 | 25 | #include <fsclient/client.h> |
paul@202 | 26 | |
paul@202 | 27 | #include "ext2_directory_accessor.h" |
paul@202 | 28 | |
paul@202 | 29 | |
paul@202 | 30 | |
paul@202 | 31 | /* Common definitions. */ |
paul@202 | 32 | |
paul@202 | 33 | #define DIRENT_CORE_SIZE (sizeof(struct dirent) - sizeof(((struct dirent *) 0)->d_name)) |
paul@202 | 34 | |
paul@202 | 35 | /* File type conversion. */ |
paul@202 | 36 | |
paul@202 | 37 | static int convert_file_type(int type) |
paul@202 | 38 | { |
paul@202 | 39 | switch (type) |
paul@202 | 40 | { |
paul@202 | 41 | case EXT2_FT_REG_FILE: return DT_REG; |
paul@202 | 42 | case EXT2_FT_DIR: return DT_DIR; |
paul@202 | 43 | case EXT2_FT_CHRDEV: return DT_CHR; |
paul@202 | 44 | case EXT2_FT_BLKDEV: return DT_BLK; |
paul@202 | 45 | case EXT2_FT_FIFO: return DT_FIFO; |
paul@202 | 46 | case EXT2_FT_SOCK: return DT_SOCK; |
paul@202 | 47 | case EXT2_FT_SYMLINK: return DT_LNK; |
paul@202 | 48 | default: return DT_UNKNOWN; |
paul@202 | 49 | } |
paul@202 | 50 | } |
paul@202 | 51 | |
paul@202 | 52 | /* Helper function to ensure alignment in generated entries. */ |
paul@202 | 53 | |
paul@202 | 54 | static int pad_align(int value) |
paul@202 | 55 | { |
paul@417 | 56 | return value + (sizeof(l4_umword_t) - (value % sizeof(l4_umword_t))); |
paul@202 | 57 | } |
paul@202 | 58 | |
paul@202 | 59 | /* Callback function. */ |
paul@202 | 60 | |
paul@202 | 61 | static int read_directory_entry(struct ext2_dir_entry *dir_entry, int offset, |
paul@202 | 62 | int blocksize, char *buf, void *priv_data) |
paul@202 | 63 | { |
paul@202 | 64 | (void) offset; (void) blocksize; (void) buf; |
paul@202 | 65 | |
paul@202 | 66 | struct Ext2DirectoryState *dir = reinterpret_cast<struct Ext2DirectoryState *>(priv_data); |
paul@202 | 67 | |
paul@202 | 68 | dir->entry = dir_entry; |
paul@202 | 69 | dir->offset = offset; |
paul@202 | 70 | return dir->directory->write_directory_entry(dir); |
paul@202 | 71 | } |
paul@202 | 72 | |
paul@202 | 73 | |
paul@202 | 74 | |
paul@202 | 75 | /* Accessor methods. */ |
paul@202 | 76 | |
paul@202 | 77 | Ext2DirectoryAccessor::Ext2DirectoryAccessor(Ext2FileOperations *ops, |
paul@202 | 78 | fileid_t fileid) |
paul@202 | 79 | : _ops(ops), _fileid(fileid) |
paul@202 | 80 | { |
paul@202 | 81 | } |
paul@202 | 82 | |
paul@202 | 83 | Ext2DirectoryAccessor::~Ext2DirectoryAccessor() |
paul@202 | 84 | { |
paul@202 | 85 | } |
paul@202 | 86 | |
paul@202 | 87 | /* Thread payload helper method. */ |
paul@202 | 88 | |
paul@202 | 89 | void Ext2DirectoryAccessor::read_directory(file_t *writer) |
paul@202 | 90 | { |
paul@202 | 91 | /* Initialise directory reading state: directory, writer, entry, offset. */ |
paul@202 | 92 | |
paul@202 | 93 | struct Ext2DirectoryState dir = {this, writer, NULL, 0}; |
paul@202 | 94 | |
paul@202 | 95 | /* Call the handler function for each directory entry. */ |
paul@202 | 96 | |
paul@202 | 97 | _ops->directory_iterate((ext2_ino_t) _fileid, read_directory_entry, &dir); |
paul@202 | 98 | } |
paul@202 | 99 | |
paul@202 | 100 | /* Callback method for directory entry output. */ |
paul@202 | 101 | |
paul@202 | 102 | int Ext2DirectoryAccessor::write_directory_entry(struct Ext2DirectoryState *dir) |
paul@202 | 103 | { |
paul@202 | 104 | struct ext2_inode inode; |
paul@202 | 105 | |
paul@202 | 106 | /* Obtain the inode details for metadata. */ |
paul@202 | 107 | |
paul@202 | 108 | if (_ops->read_inode(dir->entry->inode, &inode)) |
paul@202 | 109 | return DIRENT_ABORT; |
paul@202 | 110 | |
paul@202 | 111 | /* Align the size of the entry to avoid problems on architectures which |
paul@202 | 112 | require aligned accesses and where the compiler needs to assume an |
paul@202 | 113 | aligned structure. */ |
paul@202 | 114 | |
paul@202 | 115 | offset_t namelen = ext2fs_dirent_name_len(dir->entry); |
paul@202 | 116 | offset_t reclen = pad_align(DIRENT_CORE_SIZE + namelen); |
paul@202 | 117 | |
paul@202 | 118 | /* Construct a directory entry structure of the calculated size. */ |
paul@202 | 119 | |
paul@202 | 120 | char buffer[reclen]; |
paul@202 | 121 | struct dirent *dirent = (struct dirent *) buffer; |
paul@202 | 122 | |
paul@417 | 123 | memset(dirent, 0, reclen); |
paul@417 | 124 | |
paul@202 | 125 | dirent->d_ino = dir->entry->inode; |
paul@202 | 126 | dirent->d_off = dir->offset; |
paul@202 | 127 | dirent->d_reclen = reclen; |
paul@202 | 128 | dirent->d_type = convert_file_type(ext2fs_dirent_file_type(dir->entry)); |
paul@202 | 129 | |
paul@202 | 130 | /* Copy the name, padding the memory after it to the alignment boundary. */ |
paul@202 | 131 | |
paul@202 | 132 | memcpy(dirent->d_name, dir->entry->name, namelen); |
paul@202 | 133 | |
paul@202 | 134 | /* Write the structure to the pipe. */ |
paul@202 | 135 | |
paul@202 | 136 | offset_t nwritten = client_write(dir->writer, (const void *) dirent, reclen); |
paul@202 | 137 | |
paul@202 | 138 | /* Stop writing if the pipe is closed. */ |
paul@202 | 139 | |
paul@202 | 140 | if (nwritten < reclen) |
paul@202 | 141 | return DIRENT_ABORT; |
paul@202 | 142 | |
paul@202 | 143 | return 0; |
paul@202 | 144 | } |
paul@202 | 145 | |
paul@202 | 146 | // vim: tabstop=4 expandtab shiftwidth=4 |