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