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