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