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