1 /* 2 * Copy a file into 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 24 #include <sys/stat.h> 25 26 #include <ext2fs/ext2fs.h> 27 28 #include "file.h" 29 #include "image.h" 30 #include "path.h" 31 #include "session.h" 32 33 34 35 /* Copy buffer size. */ 36 37 static int BUFSIZE = 4096; 38 39 40 41 /* Alternative metadata set by options. */ 42 43 extern struct metadata md; 44 45 46 47 /* Copy a file into the filesystem image. */ 48 49 static int _copy_file_in(const char *filename, ext2_filsys fs, 50 ext2_ino_t ino_file, int flags) 51 { 52 int retval = 0; 53 ext2_file_t file; 54 55 /* Copying details. */ 56 57 FILE *fp; 58 char buf[BUFSIZE]; 59 size_t got; 60 unsigned int written; 61 62 /* Open a file in the target directory. */ 63 64 if (ext2fs_file_open(fs, ino_file, flags, &file)) 65 return 1; 66 67 /* Open the file in the source directory. */ 68 69 fp = fopen(filename, "r"); 70 71 /* Copy the file content. */ 72 73 if (fp != NULL) 74 { 75 while (got = fread(buf, sizeof(char), BUFSIZE, fp)) 76 { 77 while (got) 78 { 79 if (ext2fs_file_write(file, buf, got, &written)) 80 { 81 retval = 1; 82 goto close_files; 83 } 84 got -= written; 85 } 86 } 87 } 88 89 close_files: 90 fclose(fp); 91 ext2fs_file_flush(file); 92 ext2fs_file_close(file); 93 94 return retval; 95 } 96 97 /* Make a new object using the given function. */ 98 99 static int _make_object(ext2_filsys fs, const char *filename, 100 ext2_ino_t ino_parent, const char *basename, 101 ext2_ino_t *ino_target, 102 errcode_t (*fn)(ext2_filsys, ext2_ino_t, const char *, 103 __u16, __u16, __u16, ext2_ino_t *)) 104 { 105 errcode_t retval; 106 struct stat st; 107 108 /* Obtain the metadata. */ 109 110 if (lstat(filename, &st)) 111 { 112 fprintf(stderr, "Failed to read object metadata: %s\n", filename); 113 return 1; 114 } 115 116 retval = fn(fs, ino_parent, basename, st.st_mode & ~md.mask, 117 md.have_uid ? md.uid : st.st_uid, 118 md.have_gid ? md.gid : st.st_gid, 119 ino_target); 120 121 if (retval) 122 { 123 fprintf(stderr, "Failed to create object: %s\n", filename); 124 return 1; 125 } 126 127 return 0; 128 } 129 130 /* Copy a source file from the external environment into the filesystem. */ 131 132 static int _copy_in(ext2_filsys fs, const char *filename, ext2_ino_t ino_target, 133 const char *basename) 134 { 135 errcode_t retval; 136 int flags; 137 138 /* By default, treat the target as a new object. */ 139 140 ext2_ino_t ino_parent = ino_target; 141 int target_is_new = 1; 142 143 /* Source file details. */ 144 145 struct stat st; 146 147 /* Without a basename, the target exists and is either a directory, into 148 which the source file shall be copied, or it is a file that shall be 149 overwritten. */ 150 151 if (basename == NULL) 152 { 153 basename = path_basename(filename); 154 target_is_new = image_isdir_by_inode(fs, ino_target); 155 } 156 157 /* Directories are created with the same metadata. */ 158 159 if (isdir(filename)) 160 { 161 if (!target_is_new) 162 { 163 fprintf(stderr, "Directory cannot be copied as it already exists: %s\n", filename); 164 return 1; 165 } 166 167 if (_make_object(fs, filename, ino_parent, basename, &ino_target, 168 image_make_dir)) 169 return 1; 170 } 171 172 /* Files are copied. */ 173 174 else if (isfile(filename)) 175 { 176 flags = EXT2_FILE_WRITE; 177 178 /* Obtain the inode for the target file. */ 179 180 if (target_is_new) 181 { 182 if (_make_object(fs, filename, ino_parent, basename, &ino_target, 183 image_create_file)) 184 return 1; 185 186 flags |= EXT2_FILE_CREATE; 187 } 188 189 /* NOTE: Overwrite/update metadata where appropriate. */ 190 191 if (_copy_file_in(filename, fs, ino_target, flags)) 192 { 193 fprintf(stderr, "Failed to copy file: %s\n", filename); 194 return 1; 195 } 196 } 197 } 198 199 /* Copy source files from the external environment into the filesystem image. */ 200 201 int copy_in(ext2_filsys fs, int argc, char *argv[]) 202 { 203 errcode_t retval; 204 int i; 205 206 /* Target filename details. */ 207 208 const char *target = argv[argc - 1]; 209 const char *target_remaining = target; 210 const char *basename; 211 212 /* Target file and directory details. */ 213 214 int target_is_file; 215 ext2_ino_t ino_target; 216 217 /* Locate the target and test whether it is a file or a directory. */ 218 219 if (image_resolve_by_path(fs, &target_remaining, &ino_target)) 220 { 221 /* Only a non-existent file in an existing directory is permitted. */ 222 223 if (!image_isdir_by_inode(fs, ino_target) || !path_is_leafname(target_remaining)) 224 { 225 fprintf(stderr, "Target not found: %s\n", target); 226 return 1; 227 } 228 229 /* Any absent target leaves the basename remaining. 230 The resolved target is the parent directory. */ 231 232 target_is_file = 0; 233 basename = target_remaining; 234 } 235 else 236 { 237 /* Any present target can be a file or directory. 238 The resolved target is the actual target. */ 239 240 target_is_file = image_isfile_by_inode(fs, ino_target); 241 basename = NULL; 242 } 243 244 /* Only permit a target file when one source file is given. */ 245 246 if (target_is_file) 247 { 248 if (argc > 2) 249 { 250 fprintf(stderr, "Target can only be a file when copying a single file: %s\n", target); 251 return 1; 252 } 253 } 254 else if (!image_isdir_by_inode(fs, ino_target)) 255 { 256 fprintf(stderr, "Target is not a directory: %s\n", target); 257 return 1; 258 } 259 260 /* Copy each source object to the target directory. */ 261 262 for (i = 0; i < argc - 1; i++) 263 if (_copy_in(fs, argv[i], ino_target, basename)) 264 return 1; 265 266 return 0; 267 } 268 269 /* vim: tabstop=4 expandtab shiftwidth=4 270 */