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