1 /* 2 * File operations performed by an Ext2-compatible filesystem. 3 * 4 * Copyright (C) 2021 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 <e2access/access.h> 23 #include <e2access/image.h> 24 #include <e2access/path.h> 25 26 #include "ext2_file_operations.h" 27 28 /* Object type tests. */ 29 30 bool Ext2FileOperations::is_directory(ext2_ino_t ino_file) 31 { 32 std::lock_guard<std::mutex> guard(_lock); 33 34 return _image_isdir(_fs, ino_file); 35 } 36 37 bool Ext2FileOperations::is_file(ext2_ino_t ino_file) 38 { 39 std::lock_guard<std::mutex> guard(_lock); 40 41 return _image_isfile(_fs, ino_file); 42 } 43 44 /* Perform closing operations on a file. */ 45 46 void Ext2FileOperations::close_file(ext2_file_t file) 47 { 48 std::lock_guard<std::mutex> guard(_lock); 49 50 ext2fs_file_flush(file); 51 ext2fs_file_close(file); 52 } 53 54 /* Create a file in the directory indicated by the given inode number with the 55 given filename. The file is created with the given user permissions. */ 56 57 long Ext2FileOperations::create_file(ext2_ino_t ino_parent, const char *filename, 58 user_t user, ext2_ino_t *ino) 59 { 60 if (!path_is_leafname(filename)) 61 return -L4_EINVAL; 62 63 std::lock_guard<std::mutex> guard(_lock); 64 65 struct ext2_inode inode_parent; 66 errcode_t retval = ext2fs_read_inode(_fs, ino_parent, &inode_parent); 67 68 if (retval) 69 return -L4_EIO; 70 71 if (!access_can_write(user, &inode_parent)) 72 return -L4_EPERM; 73 74 if (image_create_file(_fs, ino_parent, filename, 0666 & ~user.umask, 75 user.uid, user.gid, ino)) 76 return -L4_EIO; 77 78 return L4_EOK; 79 } 80 81 /* For the given 'path', return an inode number or indicate the 'remaining' 82 part of the path that cannot be resolved. */ 83 84 long Ext2FileOperations::find_file(const char *path, ext2_ino_t *ino, 85 const char **remaining) 86 { 87 std::lock_guard<std::mutex> guard(_lock); 88 89 *remaining = path; 90 errcode_t retval = image_find_path(_fs, remaining, ino); 91 92 // NOTE: Map error conditions. 93 94 if (retval) 95 return -L4_EIO; 96 97 return L4_EOK; 98 } 99 100 /* Open the file associated with the indicated inode. */ 101 102 long Ext2FileOperations::open_file(ext2_ino_t ino, ext2_file_t *file) 103 { 104 std::lock_guard<std::mutex> guard(_lock); 105 106 errcode_t retval = ext2fs_file_open(_fs, ino, EXT2_FILE_WRITE, file); 107 108 // NOTE: Map error conditions. 109 110 if (retval) 111 return -L4_EIO; 112 113 return L4_EOK; 114 } 115 116 /* Obtain the size of a file. */ 117 118 offset_t Ext2FileOperations::get_size(ext2_file_t file) 119 { 120 std::lock_guard<std::mutex> guard(_lock); 121 122 return ext2fs_file_get_size(file); 123 } 124 125 /* Update the size of a file. */ 126 127 void Ext2FileOperations::set_size(ext2_file_t file, offset_t size) 128 { 129 std::lock_guard<std::mutex> guard(_lock); 130 131 ext2fs_file_set_size(file, size); 132 } 133 134 /* Populate the given memory region with file content. */ 135 136 offset_t Ext2FileOperations::read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size) 137 { 138 std::lock_guard<std::mutex> guard(_lock); 139 140 unsigned int nread; 141 142 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 143 ext2fs_file_read(file, (void *) addr, size, &nread); 144 145 return (offset_t) nread; 146 } 147 148 /* Transfer content from the given memory region to a file. */ 149 150 void Ext2FileOperations::write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size) 151 { 152 std::lock_guard<std::mutex> guard(_lock); 153 154 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 155 ext2fs_file_write(file, addr, size, NULL); 156 } 157 158 /* Obtain information for the given inode. */ 159 160 long Ext2FileOperations::read_inode(ext2_ino_t ino, struct ext2_inode *inode) 161 { 162 std::lock_guard<std::mutex> guard(_lock); 163 164 if (ext2fs_read_inode(_fs, ino, inode)) 165 return -L4_EIO; 166 167 return L4_EOK; 168 } 169 170 171 172 /* Directory iteration support. */ 173 174 /* Callback function invoking the callback method. */ 175 176 static int _directory_iterate_fn(struct ext2_dir_entry *dir_entry, 177 int offset, int blocksize, char *buf, 178 void *priv_data) 179 { 180 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 181 182 return data->ops->directory_iterate_fn(dir_entry, offset, blocksize, buf, data); 183 } 184 185 /* Callback method invoking the actual callback, with control over locking. */ 186 187 int Ext2FileOperations::directory_iterate_fn(struct ext2_dir_entry *dir_entry, 188 int offset, int blocksize, 189 char *buf, struct Ext2DirectoryIteration *priv_data) 190 { 191 /* Release the lock to allow other operations to proceed. */ 192 193 _lock.unlock(); 194 195 /* Invoke the actual callback function. */ 196 197 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 198 int result = data->func(dir_entry, offset, blocksize, buf, data->priv_data); 199 200 /* Acquire the lock and continue iteration. */ 201 202 _lock.lock(); 203 return result; 204 } 205 206 /* Initiate iteration over a directory, with the given 'func' being called 207 with directory entry details for each entry. */ 208 209 long Ext2FileOperations::directory_iterate(ext2_ino_t dir, 210 int func(struct ext2_dir_entry *, int, int, 211 char *, void *), 212 void *priv_data) 213 { 214 /* NOTE: Iteration should probably be interrupted if it appears that the 215 directory has been modified and the structure of the data has 216 been changed. This condition might then cause iteration to begin 217 again, skipping ahead to the entry after the last visited entry. */ 218 219 /* Acquire the lock for iteration to proceed safely. */ 220 221 _lock.lock(); 222 223 /* Begin iteration using the given callback function to invoke a callback 224 method in this object that will wrap the actual callback with locking 225 operations. */ 226 227 struct Ext2DirectoryIteration data = {this, func, priv_data}; 228 int result = ext2fs_dir_iterate(_fs, dir, 0, 0, _directory_iterate_fn, &data); 229 230 /* Release the lock upon finishing iteration. */ 231 232 _lock.unlock(); 233 234 if (result) 235 return -L4_EIO; 236 else 237 return L4_EOK; 238 } 239 240 // vim: tabstop=4 expandtab shiftwidth=4