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