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 /* Open the file associated with the indicated inode. */ 133 134 long Ext2FileOperations::open_file(ext2_ino_t ino, ext2_file_t *file) 135 { 136 std::lock_guard<std::mutex> guard(_lock); 137 138 errcode_t retval = ext2fs_file_open(_fs, ino, EXT2_FILE_WRITE, file); 139 140 // NOTE: Map error conditions. 141 142 if (retval) 143 return -L4_EIO; 144 145 return L4_EOK; 146 } 147 148 /* Remove an object from a directory. */ 149 150 long Ext2FileOperations::remove(ext2_ino_t ino) 151 { 152 std::lock_guard<std::mutex> guard(_lock); 153 154 errcode_t retval = image_remove_by_inode(_fs, ino); 155 156 // NOTE: Map error conditions. 157 158 if (retval) 159 return -L4_EIO; 160 161 return L4_EOK; 162 } 163 164 /* Rename an object. */ 165 166 long Ext2FileOperations::rename(ext2_ino_t source, 167 ext2_ino_t source_parent, const char *source_basename, 168 ext2_ino_t target_parent, const char *target_basename) 169 { 170 std::lock_guard<std::mutex> guard(_lock); 171 172 errcode_t retval = image_rename(_fs, source, source_parent, source_basename, 173 target_parent, target_basename); 174 175 // NOTE: Map error conditions. 176 177 if (retval) 178 return -L4_EIO; 179 180 return L4_EOK; 181 } 182 183 /* Unlink an object from a directory. */ 184 185 long Ext2FileOperations::unlink(ext2_ino_t ino_parent, ext2_ino_t ino) 186 { 187 std::lock_guard<std::mutex> guard(_lock); 188 189 errcode_t retval = image_unlink_by_inode(_fs, ino_parent, ino); 190 191 // NOTE: Map error conditions. 192 193 if (retval) 194 return -L4_EIO; 195 196 return L4_EOK; 197 } 198 199 200 201 /* Obtain the size of a file. */ 202 203 offset_t Ext2FileOperations::get_size(ext2_file_t file) 204 { 205 std::lock_guard<std::mutex> guard(_lock); 206 207 return ext2fs_file_get_size(file); 208 } 209 210 /* Update the size of a file. */ 211 212 void Ext2FileOperations::set_size(ext2_file_t file, offset_t size) 213 { 214 std::lock_guard<std::mutex> guard(_lock); 215 216 ext2fs_file_set_size(file, size); 217 } 218 219 /* Populate the given memory region with file content. */ 220 221 offset_t Ext2FileOperations::read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size) 222 { 223 std::lock_guard<std::mutex> guard(_lock); 224 225 unsigned int nread; 226 227 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 228 ext2fs_file_read(file, (void *) addr, size, &nread); 229 230 return (offset_t) nread; 231 } 232 233 /* Transfer content from the given memory region to a file. */ 234 235 void Ext2FileOperations::write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size) 236 { 237 std::lock_guard<std::mutex> guard(_lock); 238 239 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 240 ext2fs_file_write(file, addr, size, NULL); 241 } 242 243 /* Obtain information for the given inode. */ 244 245 long Ext2FileOperations::read_inode(ext2_ino_t ino, struct ext2_inode *inode) 246 { 247 std::lock_guard<std::mutex> guard(_lock); 248 249 if (ext2fs_read_inode(_fs, ino, inode)) 250 return -L4_EIO; 251 252 return L4_EOK; 253 } 254 255 256 257 /* Directory iteration support. */ 258 259 /* Callback function invoking the callback method. */ 260 261 static int _directory_iterate_fn(struct ext2_dir_entry *dir_entry, 262 int offset, int blocksize, char *buf, 263 void *priv_data) 264 { 265 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 266 267 return data->ops->directory_iterate_fn(dir_entry, offset, blocksize, buf, data); 268 } 269 270 /* Callback method invoking the actual callback, with control over locking. */ 271 272 int Ext2FileOperations::directory_iterate_fn(struct ext2_dir_entry *dir_entry, 273 int offset, int blocksize, 274 char *buf, struct Ext2DirectoryIteration *priv_data) 275 { 276 /* Release the lock to allow other operations to proceed. */ 277 278 _lock.unlock(); 279 280 /* Invoke the actual callback function. */ 281 282 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 283 int result = data->func(dir_entry, offset, blocksize, buf, data->priv_data); 284 285 /* Acquire the lock and continue iteration. */ 286 287 _lock.lock(); 288 return result; 289 } 290 291 /* Initiate iteration over a directory, with the given 'func' being called 292 with directory entry details for each entry. */ 293 294 long Ext2FileOperations::directory_iterate(ext2_ino_t dir, 295 int func(struct ext2_dir_entry *, int, int, 296 char *, void *), 297 void *priv_data) 298 { 299 /* NOTE: Iteration should probably be interrupted if it appears that the 300 directory has been modified and the structure of the data has 301 been changed. This condition might then cause iteration to begin 302 again, skipping ahead to the entry after the last visited entry. */ 303 304 /* Acquire the lock for iteration to proceed safely. */ 305 306 _lock.lock(); 307 308 /* Begin iteration using the given callback function to invoke a callback 309 method in this object that will wrap the actual callback with locking 310 operations. */ 311 312 struct Ext2DirectoryIteration data = {this, func, priv_data}; 313 int result = ext2fs_dir_iterate(_fs, dir, 0, 0, _directory_iterate_fn, &data); 314 315 /* Release the lock upon finishing iteration. */ 316 317 _lock.unlock(); 318 319 if (result) 320 return -L4_EIO; 321 else 322 return L4_EOK; 323 } 324 325 // vim: tabstop=4 expandtab shiftwidth=4