1 /* 2 * Access a filesystem. 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 <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 29 #include <ext2fs/ext2fs.h> 30 31 #include "file.h" 32 #include "format.h" 33 #include "image.h" 34 #include "path.h" 35 36 37 38 /* Copy buffer size. */ 39 40 const int BUFSIZE = 4096; 41 42 43 44 /* Show directory entries when iterating. */ 45 46 static int image_list_dir_proc(struct ext2_dir_entry *dirent, int offset, 47 int blocksize, char *buf, void *priv_data) 48 { 49 ext2_filsys fs = (ext2_filsys) priv_data; 50 struct ext2_inode inode; 51 52 /* Obtain the inode details for metadata. */ 53 54 if (ext2fs_read_inode(fs, dirent->inode, &inode)) 55 return DIRENT_ABORT; 56 57 /* Output details in the style of "ls -l" showing directory, permissions, 58 owner, group and size information. */ 59 60 printf("%s%s %5d %5d %6d ", 61 _image_isdir(fs, dirent->inode) ? "d" : "-", 62 get_permission_string(inode.i_mode), 63 inode.i_uid, 64 inode.i_gid, 65 EXT2_I_SIZE(&inode)); 66 67 /* Output the name which is presumably not necessarily null-terminated. */ 68 69 fwrite(dirent->name, sizeof(char), ext2fs_dirent_name_len(dirent), stdout); 70 fputc((int) '\n', stdout); 71 return 0; 72 } 73 74 75 76 /* Copy a file into the filesystem image. */ 77 78 int copy_file_in(const char *filename, ext2_filsys fs, ext2_ino_t ino_file, int flags) 79 { 80 int retval = 0; 81 ext2_file_t file; 82 83 /* Copying details. */ 84 85 FILE *fp; 86 char buf[BUFSIZE]; 87 size_t got; 88 unsigned int written; 89 90 /* Open a file in the target directory. */ 91 92 if (ext2fs_file_open(fs, ino_file, flags, &file)) 93 return 1; 94 95 /* Open the file in the source directory. */ 96 97 fp = fopen(filename, "r"); 98 99 /* Copy the file content. */ 100 101 if (fp != NULL) 102 { 103 while (got = fread(buf, sizeof(char), BUFSIZE, fp)) 104 { 105 while (got) 106 { 107 if (ext2fs_file_write(file, buf, got, &written)) 108 { 109 retval = 1; 110 goto close_files; 111 } 112 got -= written; 113 } 114 } 115 } 116 117 close_files: 118 fclose(fp); 119 ext2fs_file_flush(file); 120 ext2fs_file_close(file); 121 122 return retval; 123 } 124 125 /* Copy a file out of the filesystem image. */ 126 127 int copy_file_out(const char *target, const char *filename, int target_is_file, 128 ext2_filsys fs, ext2_ino_t ino_file) 129 { 130 int retval = 0; 131 ext2_file_t file; 132 133 /* Copying details. */ 134 135 FILE *fp; 136 char buf[BUFSIZE]; 137 unsigned int got; 138 size_t written; 139 140 /* Open the file in the source directory. */ 141 142 if (ext2fs_file_open(fs, ino_file, 0, &file)) 143 return 1; 144 145 /* Open a file in the target directory. */ 146 147 if (target_is_file) 148 fp = fopen(target, "w"); 149 else 150 fp = open_file_in_dir(target, path_basename(filename), "w"); 151 152 /* Copy the file content. */ 153 154 if (fp != NULL) 155 { 156 do 157 { 158 if (ext2fs_file_read(file, buf, BUFSIZE, &got)) 159 { 160 retval = 1; 161 goto close_files; 162 } 163 164 while (got) 165 { 166 written = fwrite(buf, sizeof(char), got, fp); 167 got -= written; 168 } 169 170 } while (got); 171 } 172 173 close_files: 174 fclose(fp); 175 ext2fs_file_close(file); 176 177 return retval; 178 } 179 180 181 182 /* Copy source files from the external environment into the filesystem image. */ 183 184 int copy_in(ext2_filsys fs, int argc, char *argv[]) 185 { 186 errcode_t retval; 187 188 /* Target filename details. */ 189 190 const char *target = argv[argc - 1]; 191 const char *target_remaining = argv[argc - 1]; 192 const char *basename; 193 int target_is_file; 194 195 /* Target file and directory details. */ 196 197 int target_is_new; 198 ext2_ino_t ino_file, ino_target; 199 int flags; 200 201 /* Source file details. */ 202 203 struct stat st; 204 int i; 205 206 /* Locate the target and test whether it is a file or a directory. */ 207 208 if (image_find_path(fs, &target_remaining, &ino_target)) 209 { 210 /* Only a non-existent file in an existing directory is permitted. */ 211 212 if (!_image_isdir(fs, ino_target) || !path_is_leafname(target_remaining)) 213 { 214 printf("Target %s not found.\n", target); 215 return 1; 216 } 217 218 target_is_file = 1; 219 target_is_new = 1; 220 } 221 else 222 { 223 target_is_file = _image_isfile(fs, ino_target); 224 target_is_new = 0; 225 } 226 227 /* Only permit a target file when one source file is given. */ 228 229 if (target_is_file) 230 { 231 if (argc > 2) 232 { 233 printf("Target %s can only be a file when copying a single file.\n", target); 234 return 1; 235 } 236 } 237 else if (!_image_isdir(fs, ino_target)) 238 { 239 printf("Target %s is not a directory.\n", target); 240 return 1; 241 } 242 243 /* Copy each source object to the target directory. */ 244 245 for (i = 0; i < argc - 1; i++) 246 { 247 if (target_is_file) 248 basename = target_remaining; 249 else 250 { 251 basename = path_basename(argv[i]); 252 target_is_new = image_find_file(fs, target, basename, &ino_file); 253 } 254 255 /* Directories are created with the same metadata. */ 256 257 if (isdir(argv[i])) 258 { 259 if (!target_is_new) 260 { 261 printf("Target %s cannot be created since it already exists.\n", target); 262 return 1; 263 } 264 265 /* Obtain the metadata. */ 266 267 if (lstat(argv[i], &st)) 268 return 1; 269 270 retval = image_make_dir(fs, ino_target, basename, st.st_mode, 271 st.st_uid, st.st_gid, &ino_file); 272 273 if (retval) 274 { 275 printf("Failed to create directory %s (%d).\n", argv[i], retval); 276 return 1; 277 } 278 } 279 280 /* Files are copied. */ 281 282 else if (isfile(argv[i])) 283 { 284 flags = EXT2_FILE_WRITE; 285 286 /* Obtain the inode for the target file. */ 287 288 if (target_is_new) 289 { 290 /* Populate the inode details. */ 291 292 if (lstat(argv[i], &st)) 293 return 1; 294 295 retval = image_create_file(fs, ino_target, basename, st.st_mode, 296 st.st_uid, st.st_gid, &ino_file); 297 if (retval) 298 { 299 printf("Failed to create file %s (%d).\n", argv[i], retval); 300 return 1; 301 } 302 303 flags |= EXT2_FILE_CREATE; 304 } 305 306 /* NOTE: Overwrite/update metadata where appropriate. */ 307 308 if (copy_file_in(argv[i], fs, ino_file, flags)) 309 { 310 printf("Failed to write to %s.\n", argv[i]); 311 return 1; 312 } 313 } 314 } 315 316 return 0; 317 } 318 319 /* Copy source files out of the filesystem image into the external environment. */ 320 321 int copy_out(ext2_filsys fs, int argc, char *argv[]) 322 { 323 /* Target filename details. */ 324 325 char *target = argv[argc - 1]; 326 int target_is_file; 327 328 /* Target file and directory details. */ 329 330 ext2_file_t file; 331 ext2_ino_t ino_file; 332 333 /* Source file details. */ 334 335 const char *path; 336 int i; 337 338 /* Locate the target and test whether it is a directory. */ 339 340 if (!isdir(target)) 341 { 342 /* Only a new or existing file in an existing directory is permitted. */ 343 344 if (isfile(target) || isdir_dirname(target)) 345 target_is_file = 1; 346 else 347 { 348 printf("Target %s is not a directory.\n", target); 349 return 1; 350 } 351 352 /* Only permit a target file when one source file is given. */ 353 354 if (argc > 2) 355 { 356 printf("Target %s can only be a file when copying a single file.\n", target); 357 return 1; 358 } 359 } 360 else 361 target_is_file = 0; 362 363 /* For each source filename, test whether it references a file. */ 364 365 for (i = 0; i < argc - 1; i++) 366 { 367 if (!image_isfile(fs, argv[i])) 368 { 369 printf("Source %s is not a file.\n", argv[i]); 370 return 1; 371 } 372 } 373 374 /* Copy each source file to the target directory. */ 375 376 for (i = 0; i < argc - 1; i++) 377 { 378 path = argv[i]; 379 380 if (image_find_path(fs, &path, &ino_file)) 381 return 1; 382 383 if (copy_file_out(target, argv[i], target_is_file, fs, ino_file)) 384 { 385 printf("Failed to read from %s.\n", argv[i]); 386 return 1; 387 } 388 389 /* NOTE: Overwrite/update metadata where appropriate. */ 390 } 391 392 return 0; 393 } 394 395 /* List directories in the filesystem image. */ 396 397 int list_dirs(ext2_filsys fs, int argc, char *argv[]) 398 { 399 int i; 400 const char *path; 401 402 for (i = 0; i < argc; i++) 403 { 404 path = argv[i]; 405 406 /* List the directory contents. */ 407 408 puts(path); 409 410 if (image_list_dir(fs, path, image_list_dir_proc, fs)) 411 return 1; 412 } 413 414 return 0; 415 } 416 417 /* Make directories in the filesystem image. */ 418 419 int make_dirs(ext2_filsys fs, int argc, char *argv[]) 420 { 421 int i; 422 char *path_end; 423 const char *path; 424 ext2_ino_t ino; 425 426 /* Make each directory component in the given pathname. */ 427 428 for (i = 0; i < argc; i++) 429 { 430 path = argv[i]; 431 if (!*path) 432 continue; 433 434 /* Search for the remaining components. */ 435 436 if (!image_find_path(fs, &path, &ino)) 437 continue; 438 439 /* From the first unrecognised component, make the remaining 440 directories. */ 441 442 if (image_make_dirs(fs, &path, ino, 0777, 0, 0)) 443 return 1; 444 } 445 446 return 0; 447 } 448 449 int main(int argc, char *argv[]) 450 { 451 int flags = EXT2_FLAG_RW; // | EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_64BITS; 452 char *fsname, *operation, *filename; 453 ext2_filsys fs = NULL; 454 errcode_t retval; 455 int exitcode = 0; 456 457 if (argc < 4) 458 { 459 printf("Usage: %s <device or image file> <operation> <filename>...\n", argv[0]); 460 return 1; 461 } 462 463 fsname = argv[1]; 464 465 /* Open the filesystem image using the POSIX file access mechanism. */ 466 467 retval = ext2fs_open(fsname, flags, 0, 0, unix_io_manager, &fs); 468 if (retval) 469 { 470 printf("Could not open filesystem using %s\n", fsname); 471 return 1; 472 } 473 474 //fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; 475 476 retval = ext2fs_read_bitmaps(fs); 477 if (retval) 478 { 479 printf("Could not read bitmaps from %s\n", fsname); 480 return 1; 481 } 482 483 /* Perform the requested operation. */ 484 485 operation = argv[2]; 486 487 if (!strcmp(operation, "--copy-out")) 488 { 489 exitcode = copy_out(fs, argc - 3, &argv[3]); 490 } 491 else if (!strcmp(operation, "--copy-in")) 492 { 493 exitcode = copy_in(fs, argc - 3, &argv[3]); 494 } 495 else if (!strcmp(operation, "--list-dirs")) 496 { 497 exitcode = list_dirs(fs, argc - 3, &argv[3]); 498 } 499 else if (!strcmp(operation, "--make-dirs")) 500 { 501 exitcode = make_dirs(fs, argc - 3, &argv[3]); 502 } 503 else 504 { 505 printf("Operation %s is not recognised.\n", operation); 506 exitcode = 1; 507 } 508 509 /* Close the filesystem image. */ 510 511 retval = ext2fs_flush(fs); 512 if (retval) 513 { 514 printf("Error flushing filesystem in %s\n", fsname); 515 exitcode = 1; 516 } 517 518 retval = ext2fs_close(fs); 519 if (retval) 520 { 521 printf("Error closing filesystem in %s\n", fsname); 522 exitcode = 1; 523 } 524 525 ext2fs_free(fs); 526 return exitcode; 527 }