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 bool Ext2FileOperations::is_directory(ext2_ino_t ino_file) 48 { 49 std::lock_guard<std::mutex> guard(_lock); 50 51 return _image_isdir(_fs, ino_file); 52 } 53 54 bool Ext2FileOperations::is_file(ext2_ino_t ino_file) 55 { 56 std::lock_guard<std::mutex> guard(_lock); 57 58 return _image_isfile(_fs, ino_file); 59 } 60 61 62 63 /* Perform closing operations on a file. */ 64 65 void Ext2FileOperations::close_file(ext2_file_t file) 66 { 67 std::lock_guard<std::mutex> guard(_lock); 68 69 ext2fs_file_flush(file); 70 ext2fs_file_close(file); 71 } 72 73 /* Create a file in the directory indicated by the given inode number with the 74 given filename. The file is created with the given user permissions. */ 75 76 long Ext2FileOperations::create_file(ext2_ino_t ino_parent, const char *filename, 77 user_t user, ext2_ino_t *ino) 78 { 79 if (!path_is_leafname(filename)) 80 return -L4_EINVAL; 81 82 std::lock_guard<std::mutex> guard(_lock); 83 84 struct ext2_inode inode_parent; 85 errcode_t retval = ext2fs_read_inode(_fs, ino_parent, &inode_parent); 86 87 if (retval) 88 return -L4_EIO; 89 90 if (!access_can_write(user, &inode_parent)) 91 return -L4_EPERM; 92 93 if (image_create_file(_fs, ino_parent, filename, 0666 & ~user.umask, 94 user.uid, user.gid, ino)) 95 return -L4_EIO; 96 97 return L4_EOK; 98 } 99 100 /* For the given 'path', return an inode number or indicate the 'remaining' 101 part of the path that cannot be resolved. */ 102 103 long Ext2FileOperations::find_file(const char *path, ext2_ino_t *ino, 104 const char **remaining) 105 { 106 std::lock_guard<std::mutex> guard(_lock); 107 108 *remaining = path; 109 errcode_t retval = image_find_path(_fs, remaining, ino); 110 111 // NOTE: Map error conditions. 112 113 if (retval) 114 return -L4_EIO; 115 116 return L4_EOK; 117 } 118 119 /* Open the file associated with the indicated inode. */ 120 121 long Ext2FileOperations::open_file(ext2_ino_t ino, ext2_file_t *file) 122 { 123 std::lock_guard<std::mutex> guard(_lock); 124 125 errcode_t retval = ext2fs_file_open(_fs, ino, EXT2_FILE_WRITE, file); 126 127 // NOTE: Map error conditions. 128 129 if (retval) 130 return -L4_EIO; 131 132 return L4_EOK; 133 } 134 135 /* Remove an object from a directory. */ 136 137 long Ext2FileOperations::remove(ext2_ino_t ino) 138 { 139 std::lock_guard<std::mutex> guard(_lock); 140 141 errcode_t retval = image_remove_by_inode(_fs, ino); 142 143 // NOTE: Map error conditions. 144 145 if (retval) 146 return -L4_EIO; 147 148 return L4_EOK; 149 } 150 151 /* Rename an object. */ 152 153 long Ext2FileOperations::rename(ext2_ino_t source, 154 ext2_ino_t source_parent, const char *source_basename, 155 ext2_ino_t target_parent, const char *target_basename) 156 { 157 std::lock_guard<std::mutex> guard(_lock); 158 159 errcode_t retval = image_rename(_fs, source, source_parent, source_basename, 160 target_parent, target_basename); 161 162 // NOTE: Map error conditions. 163 164 if (retval) 165 return -L4_EIO; 166 167 return L4_EOK; 168 } 169 170 /* Unlink an object from a directory. */ 171 172 long Ext2FileOperations::unlink(ext2_ino_t ino_parent, ext2_ino_t ino) 173 { 174 std::lock_guard<std::mutex> guard(_lock); 175 176 errcode_t retval = image_unlink_by_inode(_fs, ino_parent, ino); 177 178 // NOTE: Map error conditions. 179 180 if (retval) 181 return -L4_EIO; 182 183 return L4_EOK; 184 } 185 186 187 188 /* Obtain the size of a file. */ 189 190 offset_t Ext2FileOperations::get_size(ext2_file_t file) 191 { 192 std::lock_guard<std::mutex> guard(_lock); 193 194 return ext2fs_file_get_size(file); 195 } 196 197 /* Update the size of a file. */ 198 199 void Ext2FileOperations::set_size(ext2_file_t file, offset_t size) 200 { 201 std::lock_guard<std::mutex> guard(_lock); 202 203 ext2fs_file_set_size(file, size); 204 } 205 206 /* Populate the given memory region with file content. */ 207 208 offset_t Ext2FileOperations::read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size) 209 { 210 std::lock_guard<std::mutex> guard(_lock); 211 212 unsigned int nread; 213 214 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 215 ext2fs_file_read(file, (void *) addr, size, &nread); 216 217 return (offset_t) nread; 218 } 219 220 /* Transfer content from the given memory region to a file. */ 221 222 void Ext2FileOperations::write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size) 223 { 224 std::lock_guard<std::mutex> guard(_lock); 225 226 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 227 ext2fs_file_write(file, addr, size, NULL); 228 } 229 230 /* Obtain information for the given inode. */ 231 232 long Ext2FileOperations::read_inode(ext2_ino_t ino, struct ext2_inode *inode) 233 { 234 std::lock_guard<std::mutex> guard(_lock); 235 236 if (ext2fs_read_inode(_fs, ino, inode)) 237 return -L4_EIO; 238 239 return L4_EOK; 240 } 241 242 243 244 /* Directory iteration support. */ 245 246 /* Callback function invoking the callback method. */ 247 248 static int _directory_iterate_fn(struct ext2_dir_entry *dir_entry, 249 int offset, int blocksize, char *buf, 250 void *priv_data) 251 { 252 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 253 254 return data->ops->directory_iterate_fn(dir_entry, offset, blocksize, buf, data); 255 } 256 257 /* Callback method invoking the actual callback, with control over locking. */ 258 259 int Ext2FileOperations::directory_iterate_fn(struct ext2_dir_entry *dir_entry, 260 int offset, int blocksize, 261 char *buf, struct Ext2DirectoryIteration *priv_data) 262 { 263 /* Release the lock to allow other operations to proceed. */ 264 265 _lock.unlock(); 266 267 /* Invoke the actual callback function. */ 268 269 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 270 int result = data->func(dir_entry, offset, blocksize, buf, data->priv_data); 271 272 /* Acquire the lock and continue iteration. */ 273 274 _lock.lock(); 275 return result; 276 } 277 278 /* Initiate iteration over a directory, with the given 'func' being called 279 with directory entry details for each entry. */ 280 281 long Ext2FileOperations::directory_iterate(ext2_ino_t dir, 282 int func(struct ext2_dir_entry *, int, int, 283 char *, void *), 284 void *priv_data) 285 { 286 /* NOTE: Iteration should probably be interrupted if it appears that the 287 directory has been modified and the structure of the data has 288 been changed. This condition might then cause iteration to begin 289 again, skipping ahead to the entry after the last visited entry. */ 290 291 /* Acquire the lock for iteration to proceed safely. */ 292 293 _lock.lock(); 294 295 /* Begin iteration using the given callback function to invoke a callback 296 method in this object that will wrap the actual callback with locking 297 operations. */ 298 299 struct Ext2DirectoryIteration data = {this, func, priv_data}; 300 int result = ext2fs_dir_iterate(_fs, dir, 0, 0, _directory_iterate_fn, &data); 301 302 /* Release the lock upon finishing iteration. */ 303 304 _lock.unlock(); 305 306 if (result) 307 return -L4_EIO; 308 else 309 return L4_EOK; 310 } 311 312 // vim: tabstop=4 expandtab shiftwidth=4