1 /* 2 * Filesystem access functions. 3 * 4 * Copyright (C) 2019, 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 <string.h> 23 24 #include <ext2fs/ext2fs.h> 25 26 #include "image.h" 27 #include "path.h" 28 29 30 31 /* Create an inode for a file. */ 32 33 errcode_t image_create_file(ext2_filsys fs, ext2_ino_t ino_target, 34 const char *basename, __u16 mode, 35 __u16 uid, __u16 gid, ext2_ino_t *ino_file) 36 { 37 struct ext2_inode inode_file; 38 errcode_t retval; 39 int retry; 40 41 /* Without an inode, create a new one. */ 42 43 retval = ext2fs_new_inode(fs, ino_target, LINUX_S_IFREG | mode, 0, ino_file); 44 if (retval) 45 return retval; 46 47 /* Connect the inode to its parent. */ 48 49 for (retry = 0; retry <= 1; retry++) 50 { 51 retval = ext2fs_link(fs, ino_target, basename, *ino_file, 52 EXT2_FT_REG_FILE); 53 54 if (!retval) 55 break; 56 else if (retry) 57 return retval; 58 59 /* Expand the directory if necessary. */ 60 61 if (retval == EXT2_ET_DIR_NO_SPACE) 62 retval = ext2fs_expand_dir(fs, ino_target); 63 64 if (retval) 65 return retval; 66 } 67 68 /* Make sure that subsequent files employ different inodes. */ 69 70 ext2fs_inode_alloc_stats2(fs, *ino_file, 1, 0); 71 72 /* Populate the inode details. */ 73 74 image_set_metadata(&inode_file, 1, LINUX_S_IFREG | mode, uid, gid); 75 inode_file.i_links_count++; 76 77 return ext2fs_write_new_inode(fs, *ino_file, &inode_file); 78 } 79 80 /* Find an object in the given directory with the given name in the filesystem 81 image, updating the name reference to refer to the next component. */ 82 83 errcode_t image_find_next(ext2_filsys fs, ext2_ino_t ino_dir, 84 const char **basename, char *buf, ext2_ino_t *ino) 85 { 86 const char *end = path_component_end(*basename); 87 errcode_t retval; 88 89 /* Find the basename in the directory. */ 90 91 retval = ext2fs_lookup(fs, ino_dir, *basename, end - *basename, buf, ino); 92 93 /* Update the current component. */ 94 95 if (!retval) 96 *basename = path_component_next(end); 97 98 return retval; 99 } 100 101 /* Find an object with the given pathname in the filesystem image. */ 102 103 errcode_t image_find_path(ext2_filsys fs, const char **pathname, ext2_ino_t *ino) 104 { 105 char *buf; 106 ext2_ino_t ino_dir; 107 errcode_t retval; 108 109 retval = ext2fs_get_mem(fs->blocksize, &buf); 110 if (retval) 111 return retval; 112 113 /* Skip any leading root marker. */ 114 115 if (**pathname == '/') 116 (*pathname)++; 117 118 if (!**pathname) 119 *ino = EXT2_ROOT_INO; 120 121 /* Start at the root. */ 122 123 ino_dir = EXT2_ROOT_INO; 124 125 /* With any remaining path, find the next component. */ 126 127 while (**pathname) 128 { 129 retval = image_find_next(fs, ino_dir, pathname, buf, ino); 130 if (retval) 131 { 132 *ino = ino_dir; 133 break; 134 } 135 136 /* Move into the found object for searching the next component. */ 137 138 ino_dir = *ino; 139 } 140 141 ext2fs_free_mem(&buf); 142 143 return retval; 144 } 145 146 /* Find an object in the given directory with the given name in the filesystem 147 image. */ 148 149 errcode_t image_find_file(ext2_filsys fs, const char *dirname, 150 const char *basename, ext2_ino_t *ino) 151 { 152 char pathname[strlen(dirname) + strlen(basename) + 2]; 153 const char *s = pathname; 154 155 strcpy(pathname, dirname); 156 strcat(pathname, "/"); 157 strcat(pathname, basename); 158 159 return image_find_path(fs, &s, ino); 160 } 161 162 /* Obtain the inode for the object with the given pathname in the filesystem 163 image. */ 164 165 errcode_t image_inode(ext2_filsys fs, const char *pathname, 166 struct ext2_inode *inode) 167 { 168 ext2_ino_t ino; 169 errcode_t retval; 170 171 retval = image_find_path(fs, &pathname, &ino); 172 if (retval) 173 return retval; 174 175 return ext2fs_read_inode(fs, ino, inode); 176 } 177 178 /* List a directory in the filesystem image. */ 179 180 errcode_t image_list_dir(ext2_filsys fs, const char *path, 181 int (*proc)(struct ext2_dir_entry *, int, int, char *, 182 void *), 183 void *data) 184 { 185 char *buf; 186 ext2_ino_t ino; 187 errcode_t retval; 188 189 retval = ext2fs_get_mem(fs->blocksize, &buf); 190 if (retval) 191 return retval; 192 193 /* Locate the object and test whether it is a directory. */ 194 195 retval = image_find_path(fs, &path, &ino); 196 if (retval) 197 { 198 ext2fs_free_mem(&buf); 199 return retval; 200 } 201 202 if (!_image_isdir(fs, ino)) 203 return 1; 204 205 /* List the directory contents. */ 206 207 retval = ext2fs_dir_iterate(fs, ino, 0, buf, proc, data); 208 if (retval) 209 { 210 ext2fs_free_mem(&buf); 211 return retval; 212 } 213 214 ext2fs_free_mem(&buf); 215 return 0; 216 } 217 218 /* Make a directory in the given directory in the filesystem image having the 219 given name and metadata. */ 220 221 errcode_t image_make_dir(ext2_filsys fs, ext2_ino_t ino_dir, 222 const char *basename, __u16 mode, 223 __u16 uid, __u16 gid, ext2_ino_t *ino) 224 { 225 struct ext2_inode inode_dir; 226 errcode_t retval = 0; 227 228 /* Create an inode in the directory. */ 229 230 retval = ext2fs_new_inode(fs, ino_dir, LINUX_S_IFDIR | mode, 0, ino); 231 if (retval) 232 return retval; 233 234 /* Make the directory and update the metadata (due to ext2fs_mkdir 235 limitation). */ 236 237 retval = ext2fs_mkdir(fs, ino_dir, *ino, basename); 238 if (retval) 239 return retval; 240 241 retval = ext2fs_read_inode(fs, *ino, &inode_dir); 242 if (retval) 243 return retval; 244 245 image_set_metadata(&inode_dir, 0, LINUX_S_IFDIR | mode, uid, gid); 246 return ext2fs_write_inode(fs, *ino, &inode_dir); 247 } 248 249 /* Make a directory in the given directory in the filesystem image, updating 250 the name reference to refer to the next component. */ 251 252 errcode_t image_make_next_dir(ext2_filsys fs, ext2_ino_t ino_dir, 253 const char **basename, __u16 mode, __u16 uid, 254 __u16 gid, ext2_ino_t *ino) 255 { 256 char *end = (char *) path_component_end(*basename); 257 char endchar = *end; 258 errcode_t retval = 0; 259 260 /* Delimit the basename and make a directory using the inode. */ 261 262 if (endchar) 263 *end = '\0'; 264 265 /* Do not create directories for empty components. */ 266 267 if (**basename) 268 retval = image_make_dir(fs, ino_dir, *basename, mode, uid, gid, ino); 269 270 /* Restore the path separator and update the current component. */ 271 272 if (endchar) 273 *end = '/'; 274 275 if (!retval) 276 *basename = path_component_next(end); 277 278 return retval; 279 } 280 281 /* Make directories descending to the given path in the filesystem image. */ 282 283 errcode_t image_make_dirs(ext2_filsys fs, const char **pathname, 284 ext2_ino_t ino_dir, __u16 mode, __u16 uid, __u16 gid) 285 { 286 ext2_ino_t ino; 287 errcode_t retval; 288 289 while (**pathname) 290 { 291 retval = image_make_next_dir(fs, ino_dir, pathname, mode, uid, gid, &ino); 292 if (retval) 293 return retval; 294 295 /* Move into the created object for handling the next component. */ 296 297 ino_dir = ino; 298 } 299 300 return 0; 301 } 302 303 /* Remove an inode. */ 304 305 errcode_t image_remove_by_inode(ext2_filsys fs, ext2_ino_t ino) 306 { 307 struct ext2_inode_large inode; 308 errcode_t err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode, 309 sizeof(inode)); 310 311 /* Handle invalid inodes, ignore unreferenced inodes. */ 312 313 if (err) 314 return err; 315 316 if (!inode.i_links_count) 317 return 0; 318 319 /* Decrement the reference count. With no more references to the inode, 320 remove its resources. */ 321 322 inode.i_links_count--; 323 324 if (!inode.i_links_count) 325 { 326 err = ext2fs_free_ext_attr(fs, ino, &inode); 327 328 if (!err) 329 { 330 /* Deallocate blocks, if appropriate. ~0ULL as the end represents 331 truncation. */ 332 333 if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *) &inode)) 334 { 335 err = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode, NULL, 336 0, ~0ULL); 337 338 /* Update allocation statistics. */ 339 340 if (!err) 341 ext2fs_inode_alloc_stats2(fs, ino, -1, 342 LINUX_S_ISDIR(inode.i_mode)); 343 } 344 } 345 } 346 347 return ext2fs_write_inode_full(fs, ino, (struct ext2_inode *) &inode, 348 sizeof(inode)); 349 } 350 351 /* Set the mode, user and group metadata for a file. */ 352 353 void image_set_metadata(struct ext2_inode *inode, int clean, __u16 mode, 354 __u16 uid, __u16 gid) 355 { 356 if (clean) 357 memset(inode, 0, sizeof(*inode)); 358 359 inode->i_mode = mode; 360 inode->i_uid = uid; 361 inode->i_gid = gid; 362 } 363 364 /* Copy file metadata into a stat structure. */ 365 366 errcode_t image_stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *st) 367 { 368 struct ext2_inode inode; 369 errcode_t retval = ext2fs_read_inode(fs, ino, &inode); 370 371 if (retval) 372 return retval; 373 374 st->st_dev = 0; /* device identifier */ 375 st->st_ino = ino; 376 st->st_mode = inode.i_mode; 377 st->st_nlink = inode.i_links_count; 378 st->st_uid = inode_uid(inode); 379 st->st_gid = inode_gid(inode); 380 st->st_rdev = 0; /* special file device identifier */ 381 st->st_size = EXT2_I_SIZE(&inode); 382 st->st_blksize = fs->blocksize; 383 st->st_blocks = 0; /* number of 512 byte blocks allocated */ 384 st->st_atim.tv_sec = inode.i_atime; 385 st->st_atim.tv_nsec = 0; /* nanosecond resolution */ 386 st->st_mtim.tv_sec = inode.i_mtime; 387 st->st_mtim.tv_nsec = 0; 388 st->st_ctim.tv_sec = inode.i_ctime; 389 st->st_ctim.tv_nsec = 0; 390 391 return 0; 392 } 393 394 /* Unlink a directory entry by name. */ 395 396 errcode_t image_unlink_by_name(ext2_filsys fs, ext2_ino_t ino_parent, 397 const char *basename) 398 { 399 return ext2fs_unlink(fs, ino_parent, basename, 0, 0); 400 } 401 402 /* Unlink a directory entry by inode number. */ 403 404 errcode_t image_unlink_by_inode(ext2_filsys fs, ext2_ino_t ino_parent, 405 ext2_ino_t ino) 406 { 407 return ext2fs_unlink(fs, ino_parent, 0, ino, 0); 408 } 409 410 /* Test object types in the filesystem image. */ 411 412 int _image_isdir(ext2_filsys fs, ext2_ino_t ino) 413 { 414 struct ext2_inode inode; 415 416 if (ext2fs_read_inode(fs, ino, &inode)) 417 return 0; 418 419 return LINUX_S_ISDIR(inode.i_mode); 420 } 421 422 int image_isdir(ext2_filsys fs, const char *name) 423 { 424 ext2_ino_t ino; 425 426 if (image_find_path(fs, &name, &ino)) 427 return 0; 428 429 return _image_isdir(fs, ino); 430 } 431 432 int _image_isfile(ext2_filsys fs, ext2_ino_t ino) 433 { 434 struct ext2_inode inode; 435 436 if (ext2fs_read_inode(fs, ino, &inode)) 437 return 0; 438 439 return LINUX_S_ISREG(inode.i_mode); 440 } 441 442 int image_isfile(ext2_filsys fs, const char *name) 443 { 444 ext2_ino_t ino; 445 446 if (image_find_path(fs, &name, &ino)) 447 return 0; 448 449 return _image_isfile(fs, ino); 450 }