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 /* Remove an object from a directory. */ 117 118 long Ext2FileOperations::remove(ext2_ino_t ino) 119 { 120 std::lock_guard<std::mutex> guard(_lock); 121 122 errcode_t retval = image_remove_by_inode(_fs, ino); 123 124 // NOTE: Map error conditions. 125 126 if (retval) 127 return -L4_EIO; 128 129 return L4_EOK; 130 } 131 132 /* Unlink an object from a directory. */ 133 134 long Ext2FileOperations::unlink(ext2_ino_t ino_parent, ext2_ino_t ino) 135 { 136 std::lock_guard<std::mutex> guard(_lock); 137 138 errcode_t retval = image_unlink_by_inode(_fs, ino_parent, ino); 139 140 // NOTE: Map error conditions. 141 142 if (retval) 143 return -L4_EIO; 144 145 return L4_EOK; 146 } 147 148 /* Obtain the size of a file. */ 149 150 offset_t Ext2FileOperations::get_size(ext2_file_t file) 151 { 152 std::lock_guard<std::mutex> guard(_lock); 153 154 return ext2fs_file_get_size(file); 155 } 156 157 /* Update the size of a file. */ 158 159 void Ext2FileOperations::set_size(ext2_file_t file, offset_t size) 160 { 161 std::lock_guard<std::mutex> guard(_lock); 162 163 ext2fs_file_set_size(file, size); 164 } 165 166 /* Populate the given memory region with file content. */ 167 168 offset_t Ext2FileOperations::read_file(ext2_file_t file, offset_t filepos, void *addr, offset_t size) 169 { 170 std::lock_guard<std::mutex> guard(_lock); 171 172 unsigned int nread; 173 174 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 175 ext2fs_file_read(file, (void *) addr, size, &nread); 176 177 return (offset_t) nread; 178 } 179 180 /* Transfer content from the given memory region to a file. */ 181 182 void Ext2FileOperations::write_file(ext2_file_t file, offset_t filepos, const void *addr, offset_t size) 183 { 184 std::lock_guard<std::mutex> guard(_lock); 185 186 ext2fs_file_llseek(file, filepos, SEEK_SET, NULL); 187 ext2fs_file_write(file, addr, size, NULL); 188 } 189 190 /* Obtain information for the given inode. */ 191 192 long Ext2FileOperations::read_inode(ext2_ino_t ino, struct ext2_inode *inode) 193 { 194 std::lock_guard<std::mutex> guard(_lock); 195 196 if (ext2fs_read_inode(_fs, ino, inode)) 197 return -L4_EIO; 198 199 return L4_EOK; 200 } 201 202 203 204 /* Directory iteration support. */ 205 206 /* Callback function invoking the callback method. */ 207 208 static int _directory_iterate_fn(struct ext2_dir_entry *dir_entry, 209 int offset, int blocksize, char *buf, 210 void *priv_data) 211 { 212 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 213 214 return data->ops->directory_iterate_fn(dir_entry, offset, blocksize, buf, data); 215 } 216 217 /* Callback method invoking the actual callback, with control over locking. */ 218 219 int Ext2FileOperations::directory_iterate_fn(struct ext2_dir_entry *dir_entry, 220 int offset, int blocksize, 221 char *buf, struct Ext2DirectoryIteration *priv_data) 222 { 223 /* Release the lock to allow other operations to proceed. */ 224 225 _lock.unlock(); 226 227 /* Invoke the actual callback function. */ 228 229 struct Ext2DirectoryIteration *data = reinterpret_cast<struct Ext2DirectoryIteration *>(priv_data); 230 int result = data->func(dir_entry, offset, blocksize, buf, data->priv_data); 231 232 /* Acquire the lock and continue iteration. */ 233 234 _lock.lock(); 235 return result; 236 } 237 238 /* Initiate iteration over a directory, with the given 'func' being called 239 with directory entry details for each entry. */ 240 241 long Ext2FileOperations::directory_iterate(ext2_ino_t dir, 242 int func(struct ext2_dir_entry *, int, int, 243 char *, void *), 244 void *priv_data) 245 { 246 /* NOTE: Iteration should probably be interrupted if it appears that the 247 directory has been modified and the structure of the data has 248 been changed. This condition might then cause iteration to begin 249 again, skipping ahead to the entry after the last visited entry. */ 250 251 /* Acquire the lock for iteration to proceed safely. */ 252 253 _lock.lock(); 254 255 /* Begin iteration using the given callback function to invoke a callback 256 method in this object that will wrap the actual callback with locking 257 operations. */ 258 259 struct Ext2DirectoryIteration data = {this, func, priv_data}; 260 int result = ext2fs_dir_iterate(_fs, dir, 0, 0, _directory_iterate_fn, &data); 261 262 /* Release the lock upon finishing iteration. */ 263 264 _lock.unlock(); 265 266 if (result) 267 return -L4_EIO; 268 else 269 return L4_EOK; 270 } 271 272 // vim: tabstop=4 expandtab shiftwidth=4