1 /* 2 * Filesystem access functions. 3 * 4 * Copyright (C) 2019, 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 <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 /* Set the mode, user and group metadata for a file. */ 81 82 void image_set_metadata(struct ext2_inode *inode, int clean, __u16 mode, 83 __u16 uid, __u16 gid) 84 { 85 if (clean) 86 memset(inode, 0, sizeof(*inode)); 87 88 inode->i_mode = mode; 89 inode->i_uid = uid; 90 inode->i_gid = gid; 91 } 92 93 /* Find an object in the given directory with the given name in the filesystem 94 image, updating the name reference to refer to the next component. */ 95 96 errcode_t image_find_next(ext2_filsys fs, ext2_ino_t ino_dir, 97 const char **basename, char *buf, ext2_ino_t *ino) 98 { 99 const char *end = path_component_end(*basename); 100 errcode_t retval; 101 102 /* Find the basename in the directory. */ 103 104 retval = ext2fs_lookup(fs, ino_dir, *basename, end - *basename, buf, ino); 105 106 /* Update the current component. */ 107 108 if (!retval) 109 *basename = path_component_next(end); 110 111 return retval; 112 } 113 114 /* Find an object with the given pathname in the filesystem image. */ 115 116 errcode_t image_find_path(ext2_filsys fs, const char **pathname, ext2_ino_t *ino) 117 { 118 char *buf; 119 ext2_ino_t ino_dir; 120 errcode_t retval; 121 122 retval = ext2fs_get_mem(fs->blocksize, &buf); 123 if (retval) 124 return retval; 125 126 /* Skip any leading root marker. */ 127 128 if (**pathname == '/') 129 (*pathname)++; 130 131 if (!**pathname) 132 *ino = EXT2_ROOT_INO; 133 134 /* Start at the root. */ 135 136 ino_dir = EXT2_ROOT_INO; 137 138 /* With any remaining path, find the next component. */ 139 140 while (**pathname) 141 { 142 retval = image_find_next(fs, ino_dir, pathname, buf, ino); 143 if (retval) 144 { 145 *ino = ino_dir; 146 break; 147 } 148 149 /* Move into the found object for searching the next component. */ 150 151 ino_dir = *ino; 152 } 153 154 ext2fs_free_mem(&buf); 155 156 return retval; 157 } 158 159 /* Find an object in the given directory with the given name in the filesystem 160 image. */ 161 162 errcode_t image_find_file(ext2_filsys fs, const char *dirname, 163 const char *basename, ext2_ino_t *ino) 164 { 165 char pathname[strlen(dirname) + strlen(basename) + 2]; 166 const char *s = pathname; 167 168 strcpy(pathname, dirname); 169 strcat(pathname, "/"); 170 strcat(pathname, basename); 171 172 return image_find_path(fs, &s, ino); 173 } 174 175 /* Obtain the inode for the object with the given pathname in the filesystem 176 image. */ 177 178 errcode_t image_inode(ext2_filsys fs, const char *pathname, 179 struct ext2_inode *inode) 180 { 181 ext2_ino_t ino; 182 errcode_t retval; 183 184 retval = image_find_path(fs, &pathname, &ino); 185 if (retval) 186 return retval; 187 188 return ext2fs_read_inode(fs, ino, inode); 189 } 190 191 /* List a directory in the filesystem image. */ 192 193 errcode_t image_list_dir(ext2_filsys fs, const char *path, 194 int (*proc)(struct ext2_dir_entry *, int, int, char *, 195 void *), 196 void *data) 197 { 198 char *buf; 199 ext2_ino_t ino; 200 errcode_t retval; 201 202 retval = ext2fs_get_mem(fs->blocksize, &buf); 203 if (retval) 204 return retval; 205 206 /* Locate the object and test whether it is a directory. */ 207 208 retval = image_find_path(fs, &path, &ino); 209 if (retval) 210 { 211 ext2fs_free_mem(&buf); 212 return retval; 213 } 214 215 if (!_image_isdir(fs, ino)) 216 return 1; 217 218 /* List the directory contents. */ 219 220 retval = ext2fs_dir_iterate(fs, ino, 0, buf, proc, data); 221 if (retval) 222 { 223 ext2fs_free_mem(&buf); 224 return retval; 225 } 226 227 ext2fs_free_mem(&buf); 228 return 0; 229 } 230 231 /* Make a directory in the given directory in the filesystem image having the 232 given name and metadata. */ 233 234 errcode_t image_make_dir(ext2_filsys fs, ext2_ino_t ino_dir, 235 const char *basename, __u16 mode, 236 __u16 uid, __u16 gid, ext2_ino_t *ino) 237 { 238 struct ext2_inode inode_dir; 239 errcode_t retval = 0; 240 241 /* Create an inode in the directory. */ 242 243 retval = ext2fs_new_inode(fs, ino_dir, LINUX_S_IFDIR | mode, 0, ino); 244 if (retval) 245 return retval; 246 247 /* Make the directory and update the metadata (due to ext2fs_mkdir 248 limitation). */ 249 250 retval = ext2fs_mkdir(fs, ino_dir, *ino, basename); 251 if (retval) 252 return retval; 253 254 retval = ext2fs_read_inode(fs, *ino, &inode_dir); 255 if (retval) 256 return retval; 257 258 image_set_metadata(&inode_dir, 0, LINUX_S_IFDIR | mode, uid, gid); 259 return ext2fs_write_inode(fs, *ino, &inode_dir); 260 } 261 262 /* Make a directory in the given directory in the filesystem image, updating 263 the name reference to refer to the next component. */ 264 265 errcode_t image_make_next_dir(ext2_filsys fs, ext2_ino_t ino_dir, 266 const char **basename, __u16 mode, __u16 uid, 267 __u16 gid, ext2_ino_t *ino) 268 { 269 char *end = (char *) path_component_end(*basename); 270 char endchar = *end; 271 errcode_t retval = 0; 272 273 /* Delimit the basename and make a directory using the inode. */ 274 275 if (endchar) 276 *end = '\0'; 277 278 /* Do not create directories for empty components. */ 279 280 if (**basename) 281 retval = image_make_dir(fs, ino_dir, *basename, mode, uid, gid, ino); 282 283 /* Restore the path separator and update the current component. */ 284 285 if (endchar) 286 *end = '/'; 287 288 if (!retval) 289 *basename = path_component_next(end); 290 291 return retval; 292 } 293 294 /* Make directories descending to the given path in the filesystem image. */ 295 296 errcode_t image_make_dirs(ext2_filsys fs, const char **pathname, 297 ext2_ino_t ino_dir, __u16 mode, __u16 uid, __u16 gid) 298 { 299 ext2_ino_t ino; 300 errcode_t retval; 301 302 while (**pathname) 303 { 304 retval = image_make_next_dir(fs, ino_dir, pathname, mode, uid, gid, &ino); 305 if (retval) 306 return retval; 307 308 /* Move into the created object for handling the next component. */ 309 310 ino_dir = ino; 311 } 312 313 return 0; 314 } 315 316 /* Copy file metadata into a stat structure. */ 317 318 errcode_t image_stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *st) 319 { 320 struct ext2_inode inode; 321 errcode_t retval = ext2fs_read_inode(fs, ino, &inode); 322 323 if (retval) 324 return retval; 325 326 st->st_dev = 0; /* device identifier */ 327 st->st_ino = ino; 328 st->st_mode = inode.i_mode; 329 st->st_nlink = inode.i_links_count; 330 st->st_uid = inode_uid(inode); 331 st->st_gid = inode_gid(inode); 332 st->st_rdev = 0; /* special file device identifier */ 333 st->st_size = EXT2_I_SIZE(&inode); 334 st->st_blksize = fs->blocksize; 335 st->st_blocks = 0; /* number of 512 byte blocks allocated */ 336 st->st_atim.tv_sec = inode.i_atime; 337 st->st_atim.tv_nsec = 0; /* nanosecond resolution */ 338 st->st_mtim.tv_sec = inode.i_mtime; 339 st->st_mtim.tv_nsec = 0; 340 st->st_ctim.tv_sec = inode.i_ctime; 341 st->st_ctim.tv_nsec = 0; 342 343 return 0; 344 } 345 346 /* Remove an inode. */ 347 348 errcode_t image_remove_by_inode(ext2_filsys fs, ext2_ino_t ino) 349 { 350 struct ext2_inode_large inode; 351 errcode_t err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode, 352 sizeof(inode)); 353 354 /* Handle invalid inodes, ignore unreferenced inodes. */ 355 356 if (err) 357 return err; 358 359 if (!inode.i_links_count) 360 return 0; 361 362 /* Decrement the reference count. With no more references to the inode, 363 remove its resources. */ 364 365 inode.i_links_count--; 366 367 if (!inode.i_links_count) 368 { 369 err = ext2fs_free_ext_attr(fs, ino, &inode); 370 371 if (!err) 372 { 373 /* Deallocate blocks, if appropriate. ~0ULL as the end represents 374 truncation. */ 375 376 if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *) &inode)) 377 { 378 err = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode, NULL, 379 0, ~0ULL); 380 381 /* Update allocation statistics. */ 382 383 if (!err) 384 ext2fs_inode_alloc_stats2(fs, ino, -1, 385 LINUX_S_ISDIR(inode.i_mode)); 386 } 387 } 388 } 389 390 return ext2fs_write_inode_full(fs, ino, (struct ext2_inode *) &inode, 391 sizeof(inode)); 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 }