1 /* 2 * File operations performed 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 <e2access/access.h> 23 #include <e2access/image.h> 24 #include <e2access/path.h> 25 #include <systypes/fcntl.h> 26 27 #include "ext2_file_operations.h" 28 29 30 31 /* Access and object type tests. */ 32 33 bool Ext2FileOperations::can_access(user_t user, flags_t flags, ext2_ino_t ino) 34 { 35 struct ext2_inode inode; 36 errcode_t retval = ext2fs_read_inode(_fs, ino, &inode); 37 38 if (retval) 39 return false; 40 41 if ((flags & O_RDWR) || (flags & O_WRONLY)) 42 return access_can_write(user, &inode); 43 else 44 return access_can_read(user, &inode); 45 } 46 47 /* Assert that a directory is empty. */ 48 49 bool Ext2FileOperations::directory_is_empty(ext2_ino_t ino) 50 { 51 std::lock_guard<std::mutex> guard(_lock); 52 53 return !image_dir_empty_by_inode(_fs, ino); 54 } 55 56 /* Test for a directory. */ 57 58 bool Ext2FileOperations::is_directory(ext2_ino_t ino_file) 59 { 60 std::lock_guard<std::mutex> guard(_lock); 61 62 return image_isdir_by_inode(_fs, ino_file); 63 } 64 65 /* Test for a file. */ 66 67 bool Ext2FileOperations::is_file(ext2_ino_t ino_file) 68 { 69 std::lock_guard<std::mutex> guard(_lock); 70 71 return image_isfile_by_inode(_fs, ino_file); 72 } 73 74 75 76 /* Perform closing operations on a file. */ 77 78 void Ext2FileOperations::close_file(ext2_file_t file) 79 { 80 std::lock_guard<std::mutex> guard(_lock); 81 82 /* ext2fs_file_flush is called within the close function. */ 83 84 ext2fs_file_close(file); 85 } 86 87 /* Create a file in the directory indicated by the given inode number with the 88 given filename. The file is created with the given user permissions. */ 89 90 long Ext2FileOperations::create_file(ext2_ino_t ino_parent, const char *filename, 91 user_t user, ext2_ino_t *ino) 92 { 93 if (!path_is_leafname(filename)) 94 return -L4_EINVAL; 95 96 std::lock_guard<std::mutex> guard(_lock); 97 98 struct ext2_inode inode_parent; 99 errcode_t retval = ext2fs_read_inode(_fs, ino_parent, &inode_parent); 100 101 if (retval) 102 return -L4_EIO; 103 104 if (!access_can_write(user, &inode_parent)) 105 return -L4_EACCESS; 106 107 if (image_create_file(_fs, ino_parent, filename, 0666 & ~user.umask, 108 user.uid, user.gid, ino)) 109 return -L4_EIO; 110 111 return L4_EOK; 112 } 113 114 /* For the given 'path', return an inode number or indicate the 'remaining' 115 part of the path that cannot be resolved. */ 116 117 long Ext2FileOperations::find_file(const char *path, ext2_ino_t *ino, 118 const char **remaining) 119 { 120 std::lock_guard<std::mutex> guard(_lock); 121 122 *remaining = path; 123 errcode_t retval = image_resolve_by_path(_fs, remaining, ino); 124 125 // NOTE: Map error conditions. 126 127 if (retval) 128 return -L4_EIO; 129 130 return L4_EOK; 131 } 132 133 /* Make a new directory. */ 134 135 long Ext2FileOperations::mkdir(ext2_ino_t ino_parent, const char *basename, 136 sys_mode_t mode, user_t user) 137 { 138 std::lock_guard<std::mutex> guard(_lock); 139 140 ext2_ino_t ino; 141 errcode_t retval; 142 143 /* Test for permission to create the directory. */ 144 145 if (!can_access(user, O_WRONLY, ino_parent)) 146 return -L4_EACCESS; 147 148 /* Make the directory. */ 149 150 retval = image_make_dir(_fs, ino_parent, basename, mode, user.uid, user.gid, 151 &ino); 152 153 // NOTE: Map error conditions. 154 155 if (retval) 156 return -L4_EIO; 157 158 return L4_EOK; 159 } 160 161 /* Open the file associated with the indicated inode. */ 162 163 long Ext2FileOperations::open_file(ext2_ino_t ino, ext2_file_t *file) 164 { 165 std::lock_guard<std::mutex> guard(_lock); 166 167 errcode_t retval = ext2fs_file_open(_fs, ino, EXT2_FILE_WRITE, file); 168 169 // NOTE: Map error conditions. 170 171 if (retval) 172 return -L4_EIO; 173 174 return L4_EOK; 175 } 176 177 /* Remove an object from a directory. */ 178 179 long Ext2FileOperations::remove(ext2_ino_t ino) 180 { 181 std::lock_guard<std::mutex> guard(_lock); 182 183 errcode_t retval = image_remove_by_inode(_fs, ino); 184 185 // NOTE: Map error conditions. 186 187 if (retval) 188 return -L4_EIO; 189 190 return L4_EOK; 191 } 192 193 /* Rename an object. */ 194 195 long Ext2FileOperations::rename(ext2_ino_t source, 196 ext2_ino_t source_parent, const char *source_basename, 197 ext2_ino_t target_parent, const char *target_basename) 198 { 199 std::lock_guard<std::mutex> guard(_lock); 200 201 errcode_t retval = image_rename(_fs, source, source_parent, source_basename, 202 target_parent, target_basename); 203 204 // NOTE: Map error conditions. 205 206 if (retval) 207 return -L4_EIO; 208 209 return L4_EOK; 210 } 211 212 /* Unlink an object from a directory. */ 213 214 long Ext2FileOperations::unlink(ext2_ino_t ino_parent, ext2_ino_t ino) 215 { 216 std::lock_guard<std::mutex> guard(_lock); 217 218 errcode_t retval = image_unlink_by_inode(_fs, ino_parent, ino); 219 220 // NOTE: Map error conditions. 221 222 if (retval) 223 return -L4_EIO; 224 225 return L4_EOK; 226 } 227 228 229 230 /* Obtain the size of a file. */ 231 232 offset_t Ext2FileOperations::get_size(ext2_file_t file) 233 { 234 std::lock_guard<std::mutex> guard(_lock); 235 236 return ext2fs_file_get_size(file); 237 } 238 239 /* Update the size of a file. */ 240 241 void Ext2FileOperations::set_size(ext2_file_t file, offset_t size) 242 { 243 std::lock_guard<std::mutex> guard(_lock); 244 245 ext2fs_file_set_size(file, size); 246 } 247 248 /* Populate the given memory region with file content. */ 249 250 offset_t Ext2FileOperations::read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size) 251 { 252 std::lock_guard<std::mutex> guard(_lock); 253 254 unsigned int nread; 255 256 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 257 ext2fs_file_read(file, (void *) addr, size, &nread); 258 259 return (offset_t) nread; 260 } 261 262 /* Transfer content from the given memory region to a file. */ 263 264 void Ext2FileOperations::write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size) 265 { 266 std::lock_guard<std::mutex> guard(_lock); 267 268 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 269 ext2fs_file_write(file, addr, size, NULL); 270 } 271 272 /* Obtain information for the given inode. */ 273 274 long Ext2FileOperations::read_inode(ext2_ino_t ino, struct ext2_inode *inode) 275 { 276 std::lock_guard<std::mutex> guard(_lock); 277 278 if (ext2fs_read_inode(_fs, ino, inode)) 279 return -L4_EIO; 280 281 return L4_EOK; 282 } 283 284 /* Obtain stat-based information for the given inode. */ 285 286 long Ext2FileOperations::stat_inode(ext2_ino_t ino, struct stat *st) 287 { 288 std::lock_guard<std::mutex> guard(_lock); 289 290 if (image_stat_inode(_fs, ino, st)) 291 return -L4_EIO; 292 293 return L4_EOK; 294 } 295 296 297 298 /* Directory iteration support. */ 299 300 /* Callback function invoking the callback method. */ 301 302 static int _directory_iterate_fn(struct ext2_dir_entry *dir_entry, 303 int offset, int blocksize, char *buf, 304 void *priv_data) 305 { 306 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 307 308 return data->ops->directory_iterate_fn(dir_entry, offset, blocksize, buf, data); 309 } 310 311 /* Callback method invoking the actual callback, with control over locking. */ 312 313 int Ext2FileOperations::directory_iterate_fn(struct ext2_dir_entry *dir_entry, 314 int offset, int blocksize, 315 char *buf, struct Ext2DirectoryIteration *priv_data) 316 { 317 /* Release the lock to allow other operations to proceed. */ 318 319 _lock.unlock(); 320 321 /* Invoke the actual callback function. */ 322 323 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 324 int result = data->func(dir_entry, offset, blocksize, buf, data->priv_data); 325 326 /* Acquire the lock and continue iteration. */ 327 328 _lock.lock(); 329 return result; 330 } 331 332 /* Initiate iteration over a directory, with the given 'func' being called 333 with directory entry details for each entry. */ 334 335 long Ext2FileOperations::directory_iterate(ext2_ino_t dir, 336 int func(struct ext2_dir_entry *, int, int, 337 char *, void *), 338 void *priv_data) 339 { 340 /* NOTE: Iteration should probably be interrupted if it appears that the 341 directory has been modified and the structure of the data has 342 been changed. This condition might then cause iteration to begin 343 again, skipping ahead to the entry after the last visited entry. */ 344 345 /* Acquire the lock for iteration to proceed safely. */ 346 347 _lock.lock(); 348 349 /* Begin iteration using the given callback function to invoke a callback 350 method in this object that will wrap the actual callback with locking 351 operations. */ 352 353 struct Ext2DirectoryIteration data = {this, func, priv_data}; 354 int result = ext2fs_dir_iterate(_fs, dir, 0, 0, _directory_iterate_fn, &data); 355 356 /* Release the lock upon finishing iteration. */ 357 358 _lock.unlock(); 359 360 if (result) 361 return -L4_EIO; 362 else 363 return L4_EOK; 364 } 365 366 // vim: tabstop=4 expandtab shiftwidth=4