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 const char *path_orig = path; 199 char *buf; 200 ext2_ino_t ino; 201 errcode_t retval; 202 203 retval = ext2fs_get_mem(fs->blocksize, &buf); 204 if (retval) 205 return retval; 206 207 /* Locate the object and test whether it is a directory. */ 208 209 retval = image_find_path(fs, &path, &ino); 210 if (retval) 211 { 212 ext2fs_free_mem(&buf); 213 return retval; 214 } 215 216 if (!_image_isdir(fs, ino)) 217 return 1; 218 219 /* List the directory contents. */ 220 221 retval = ext2fs_dir_iterate(fs, ino, 0, buf, proc, data); 222 if (retval) 223 { 224 ext2fs_free_mem(&buf); 225 return retval; 226 } 227 228 ext2fs_free_mem(&buf); 229 return 0; 230 } 231 232 /* Make a directory in the given directory in the filesystem image having the 233 given name and metadata. */ 234 235 errcode_t image_make_dir(ext2_filsys fs, ext2_ino_t ino_dir, 236 const char *basename, __u16 mode, 237 __u16 uid, __u16 gid, ext2_ino_t *ino) 238 { 239 struct ext2_inode inode_dir; 240 errcode_t retval = 0; 241 242 /* Create an inode in the directory. */ 243 244 retval = ext2fs_new_inode(fs, ino_dir, LINUX_S_IFDIR | mode, 0, ino); 245 if (retval) 246 return retval; 247 248 /* Make the directory and update the metadata (due to ext2fs_mkdir 249 limitation). */ 250 251 retval = ext2fs_mkdir(fs, ino_dir, *ino, basename); 252 if (retval) 253 return retval; 254 255 retval = ext2fs_read_inode(fs, *ino, &inode_dir); 256 if (retval) 257 return retval; 258 259 image_set_metadata(&inode_dir, 0, LINUX_S_IFDIR | mode, uid, gid); 260 return ext2fs_write_inode(fs, *ino, &inode_dir); 261 } 262 263 /* Make a directory in the given directory in the filesystem image, updating 264 the name reference to refer to the next component. */ 265 266 errcode_t image_make_next_dir(ext2_filsys fs, ext2_ino_t ino_dir, 267 const char **basename, __u16 mode, __u16 uid, 268 __u16 gid, ext2_ino_t *ino) 269 { 270 char *end = (char *) path_component_end(*basename); 271 char endchar = *end; 272 errcode_t retval = 0; 273 274 /* Delimit the basename and make a directory using the inode. */ 275 276 if (endchar) 277 *end = '\0'; 278 279 /* Do not create directories for empty components. */ 280 281 if (**basename) 282 retval = image_make_dir(fs, ino_dir, *basename, mode, uid, gid, ino); 283 284 /* Restore the path separator and update the current component. */ 285 286 if (endchar) 287 *end = '/'; 288 289 if (!retval) 290 *basename = path_component_next(end); 291 292 return retval; 293 } 294 295 /* Make directories descending to the given path in the filesystem image. */ 296 297 errcode_t image_make_dirs(ext2_filsys fs, const char **pathname, 298 ext2_ino_t ino_dir, __u16 mode, __u16 uid, __u16 gid) 299 { 300 ext2_ino_t ino; 301 errcode_t retval; 302 303 while (**pathname) 304 { 305 retval = image_make_next_dir(fs, ino_dir, pathname, mode, uid, gid, &ino); 306 if (retval) 307 return retval; 308 309 /* Move into the created object for handling the next component. */ 310 311 ino_dir = ino; 312 } 313 314 return 0; 315 } 316 317 /* Copy file metadata into a stat structure. */ 318 319 errcode_t image_stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *st) 320 { 321 struct ext2_inode inode; 322 errcode_t retval = ext2fs_read_inode(fs, ino, &inode); 323 324 if (retval) 325 return retval; 326 327 st->st_dev = 0; /* device identifier */ 328 st->st_ino = ino; 329 st->st_mode = inode.i_mode; 330 st->st_nlink = inode.i_links_count; 331 st->st_uid = inode_uid(inode); 332 st->st_gid = inode_gid(inode); 333 st->st_rdev = 0; /* special file device identifier */ 334 st->st_size = EXT2_I_SIZE(&inode); 335 st->st_blksize = fs->blocksize; 336 st->st_blocks = 0; /* number of 512 byte blocks allocated */ 337 st->st_atim.tv_sec = inode.i_atime; 338 st->st_atim.tv_nsec = 0; /* nanosecond resolution */ 339 st->st_mtim.tv_sec = inode.i_mtime; 340 st->st_mtim.tv_nsec = 0; 341 st->st_ctim.tv_sec = inode.i_ctime; 342 st->st_ctim.tv_nsec = 0; 343 344 return 0; 345 } 346 347 /* Test object types in the filesystem image. */ 348 349 int _image_isdir(ext2_filsys fs, ext2_ino_t ino) 350 { 351 struct ext2_inode inode; 352 353 if (ext2fs_read_inode(fs, ino, &inode)) 354 return 0; 355 356 return LINUX_S_ISDIR(inode.i_mode); 357 } 358 359 int image_isdir(ext2_filsys fs, const char *name) 360 { 361 ext2_ino_t ino; 362 363 if (image_find_path(fs, &name, &ino)) 364 return 0; 365 366 return _image_isdir(fs, ino); 367 } 368 369 int _image_isfile(ext2_filsys fs, ext2_ino_t ino) 370 { 371 struct ext2_inode inode; 372 373 if (ext2fs_read_inode(fs, ino, &inode)) 374 return 0; 375 376 return LINUX_S_ISREG(inode.i_mode); 377 } 378 379 int image_isfile(ext2_filsys fs, const char *name) 380 { 381 ext2_ino_t ino; 382 383 if (image_find_path(fs, &name, &ino)) 384 return 0; 385 386 return _image_isfile(fs, ino); 387 }