1 /* 2 * Copy a file out of a filesystem. 3 * 4 * Copyright (C) 2019, 2022 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 <string.h> 24 25 #include <sys/stat.h> 26 27 #include <ext2fs/ext2fs.h> 28 29 #include "file.h" 30 #include "image.h" 31 #include "path.h" 32 #include "session.h" 33 34 35 36 /* Copy buffer size. */ 37 38 static int BUFSIZE = 4096; 39 40 41 42 /* Alternative metadata set by options. */ 43 44 extern struct metadata md; 45 46 47 48 /* Copy a file out of the filesystem image. */ 49 50 static int _copy_file_out(const char *target, const char *filename, 51 int target_is_file, ext2_filsys fs, 52 ext2_ino_t ino_file) 53 { 54 int retval = 0; 55 ext2_file_t file; 56 57 /* Copying details. */ 58 59 FILE *fp; 60 char buf[BUFSIZE]; 61 unsigned int got; 62 size_t written; 63 64 /* Open the file in the source directory. */ 65 66 if (ext2fs_file_open(fs, ino_file, 0, &file)) 67 return 1; 68 69 /* Open a file in the target directory. */ 70 71 if (target_is_file) 72 fp = fopen(target, "w"); 73 else 74 fp = open_file_in_dir(target, path_basename(filename), "w"); 75 76 /* Copy the file content. */ 77 78 if (fp != NULL) 79 { 80 do 81 { 82 if (ext2fs_file_read(file, buf, BUFSIZE, &got)) 83 { 84 retval = 1; 85 goto close_files; 86 } 87 88 while (got) 89 { 90 written = fwrite(buf, sizeof(char), got, fp); 91 got -= written; 92 } 93 94 } while (got); 95 } 96 97 close_files: 98 fclose(fp); 99 ext2fs_file_close(file); 100 101 return retval; 102 } 103 104 105 106 /* Make a new directory in the external environment. */ 107 108 static int _make_directory(const char *target, const char *basename, 109 ext2_filsys fs, ext2_ino_t ino) 110 { 111 errcode_t retval; 112 struct ext2_inode inode; 113 char target_path[strlen(target) + strlen(basename) + 2]; 114 115 sprintf(target_path, "%s/%s", target, basename); 116 117 retval = ext2fs_read_inode(fs, ino, &inode); 118 119 if (retval) 120 return retval; 121 122 return mkdir(target_path, inode.i_mode); 123 } 124 125 /* Copy a file from the filesystem into the external environment. */ 126 127 static int _copy_out(ext2_filsys fs, const char *source, const char *target, 128 int target_is_file) 129 { 130 ext2_ino_t ino_source; 131 132 if (image_find_by_path(fs, source, &ino_source)) 133 { 134 fprintf(stderr, "Failed to find file: %s\n", source); 135 return 1; 136 } 137 138 /* Test whether the filename references a file. */ 139 140 if (image_isdir_by_path(fs, source)) 141 { 142 if (_make_directory(target, path_basename(source), fs, ino_source)) 143 { 144 fprintf(stderr, "Failed to make directory: %s/%s\n", target, 145 path_basename(source)); 146 return 1; 147 } 148 } 149 else if (image_isfile_by_path(fs, source)) 150 { 151 if (_copy_file_out(target, source, target_is_file, fs, ino_source)) 152 { 153 fprintf(stderr, "Failed to read from file: %s\n", source); 154 return 1; 155 } 156 } 157 else 158 { 159 fprintf(stderr, "Object type not supported: %s\n", source); 160 return 1; 161 } 162 163 /* NOTE: Overwrite/update metadata where appropriate. */ 164 165 return 0; 166 } 167 168 /* Copy source files out of the filesystem image into the external environment. */ 169 170 int copy_out(ext2_filsys fs, int argc, char *argv[]) 171 { 172 int i; 173 174 /* Target filename details. */ 175 176 char *target = argv[argc - 1]; 177 int target_is_file; 178 179 /* Locate the target and test whether it is a directory. */ 180 181 if (!isdir(target)) 182 { 183 /* Only a new or existing file in an existing directory is permitted. */ 184 185 if (isfile(target) || isdir_dirname(target)) 186 target_is_file = 1; 187 else 188 { 189 fprintf(stderr, "Target is not a directory: %s\n", target); 190 return 1; 191 } 192 193 /* Only permit a target file when one source file is given. */ 194 195 if (argc > 2) 196 { 197 fprintf(stderr, "Target can only be a file when copying a single file: %s\n", target); 198 return 1; 199 } 200 } 201 else 202 target_is_file = 0; 203 204 /* Copy each source file to the target directory. */ 205 206 for (i = 0; i < argc - 1; i++) 207 if (_copy_out(fs, argv[i], target, target_is_file)) 208 return 1; 209 210 return 0; 211 } 212 213 /* vim: tabstop=4 expandtab shiftwidth=4 214 */