1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/fsaccess/op_copy_in.c Mon Mar 21 00:17:42 2022 +0100
1.3 @@ -0,0 +1,267 @@
1.4 +/*
1.5 + * Copy a file into a filesystem.
1.6 + *
1.7 + * Copyright (C) 2019, 2022 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <stdio.h>
1.26 +#include <string.h>
1.27 +
1.28 +#include <sys/stat.h>
1.29 +
1.30 +#include <e2access/path.h>
1.31 +#include <fsclient/client.h>
1.32 +#include <systypes/fcntl.h>
1.33 +
1.34 +#include "file.h"
1.35 +#include "ops.h"
1.36 +#include "session.h"
1.37 +
1.38 +
1.39 +
1.40 +/* Copy buffer size. */
1.41 +
1.42 +static int BUFSIZE = 4096;
1.43 +
1.44 +
1.45 +
1.46 +/* Alternative metadata set by options. */
1.47 +
1.48 +extern struct metadata md;
1.49 +
1.50 +
1.51 +
1.52 +/* Copy a file into the filesystem image. */
1.53 +
1.54 +static int _copy_file_in(const char *source, const char *target, flags_t flags)
1.55 +{
1.56 + int retval = 0;
1.57 +
1.58 + /* Copying details. */
1.59 +
1.60 + FILE *fp;
1.61 + file_t *target_fp;
1.62 + char buf[BUFSIZE];
1.63 + size_t got;
1.64 + offset_t written;
1.65 +
1.66 + /* Open a file in the target directory. */
1.67 +
1.68 + target_fp = client_open(target, flags);
1.69 +
1.70 + if (target_fp == NULL)
1.71 + return 1;
1.72 +
1.73 + /* Open the file in the source directory. */
1.74 +
1.75 + fp = fopen(source, "r");
1.76 +
1.77 + /* Copy the file content. */
1.78 +
1.79 + if (fp != NULL)
1.80 + {
1.81 + while ((got = fread(buf, sizeof(char), BUFSIZE, fp)))
1.82 + {
1.83 + while (got)
1.84 + {
1.85 + written = client_write(target_fp, buf, got);
1.86 +
1.87 + if (!written)
1.88 + {
1.89 + retval = 1;
1.90 + goto close_files;
1.91 + }
1.92 + got -= written;
1.93 + }
1.94 + }
1.95 + }
1.96 +
1.97 +close_files:
1.98 + fclose(fp);
1.99 + client_close(target_fp);
1.100 +
1.101 + return retval;
1.102 +}
1.103 +
1.104 +/* Copy a source file from the external environment into the filesystem. */
1.105 +
1.106 +static int _copy_in(const char *source, const char *target,
1.107 + const char *basename)
1.108 +{
1.109 + /* Obtain basename and eventual target path. */
1.110 +
1.111 + const char *source_basename = path_basename(source);
1.112 + char target_plus_basename[strlen(target) + 1 +
1.113 + (basename != NULL ? strlen(basename)
1.114 + : strlen(source_basename)) + 1];
1.115 + const char *target_path;
1.116 +
1.117 + /* Target file properties. */
1.118 +
1.119 + flags_t flags;
1.120 + struct stat st;
1.121 + long err;
1.122 +
1.123 + /* By default, treat the target as a new object. */
1.124 +
1.125 + int target_is_new = 1;
1.126 +
1.127 + /* Without a basename, the target exists and is either a directory, into
1.128 + which the source file shall be copied, or it is a file that shall be
1.129 + overwritten. */
1.130 +
1.131 + if (basename == NULL)
1.132 + {
1.133 + err = client_stat(target, &st);
1.134 +
1.135 + if (err)
1.136 + {
1.137 + fprintf(stderr, "Could not stat target: %s\n", target);
1.138 + return 1;
1.139 + }
1.140 +
1.141 + target_is_new = S_ISDIR(st.st_mode);
1.142 +
1.143 + if (target_is_new)
1.144 + basename = path_basename(source);
1.145 + }
1.146 +
1.147 + /* Obtain the target path. */
1.148 +
1.149 + if (basename != NULL)
1.150 + {
1.151 + sprintf(target_plus_basename, "%s/%s", target, basename);
1.152 + target_path = target_plus_basename;
1.153 + }
1.154 + else
1.155 + target_path = target;
1.156 +
1.157 + /* Directories are created with the same metadata. */
1.158 +
1.159 + if (isdir(source))
1.160 + {
1.161 + if (!target_is_new)
1.162 + {
1.163 + fprintf(stderr, "Directory cannot be copied as it already exists: %s\n", target_path);
1.164 + return 1;
1.165 + }
1.166 +
1.167 + err = client_stat(source, &st);
1.168 +
1.169 + if (err)
1.170 + {
1.171 + fprintf(stderr, "Could not stat source: %s\n", source);
1.172 + return 1;
1.173 + }
1.174 +
1.175 + if (client_mkdir(target_path, st.st_mode & ~md.mask))
1.176 + return 1;
1.177 + }
1.178 +
1.179 + /* Files are copied. */
1.180 +
1.181 + else if (isfile(source))
1.182 + {
1.183 + flags = O_WRONLY;
1.184 +
1.185 + /* Obtain the inode for the target file. */
1.186 +
1.187 + if (target_is_new)
1.188 + flags |= O_CREAT;
1.189 +
1.190 + /* NOTE: Overwrite/update metadata where appropriate. */
1.191 +
1.192 + if (_copy_file_in(source, target_path, flags))
1.193 + {
1.194 + fprintf(stderr, "Failed to copy file: %s\n", source);
1.195 + return 1;
1.196 + }
1.197 + }
1.198 +
1.199 + return 0;
1.200 +}
1.201 +
1.202 +/* Copy source files from the external environment into the filesystem image. */
1.203 +
1.204 +int copy_in(int argc, char *argv[])
1.205 +{
1.206 + int i;
1.207 +
1.208 + /* Target filename details. */
1.209 +
1.210 + char *target = argv[argc - 1];
1.211 + const char *basename;
1.212 + struct stat st;
1.213 +
1.214 + /* Locate the target and test whether it is a file or a directory. */
1.215 +
1.216 + long err = client_stat(target, &st);
1.217 +
1.218 + /* Only a non-existent file in an existing directory is permitted. */
1.219 +
1.220 + if (err == -L4_ENOENT)
1.221 + {
1.222 + /* Split the path, making target the parent directory. */
1.223 +
1.224 + basename = path_split(target);
1.225 + err = client_stat(target, &st);
1.226 +
1.227 + if (err)
1.228 + {
1.229 + fprintf(stderr, "Could not stat target parent: %s\n", target);
1.230 + return 1;
1.231 + }
1.232 +
1.233 + if (!S_ISDIR(st.st_mode))
1.234 + {
1.235 + fprintf(stderr, "Target parent is not directory: %s\n", target);
1.236 + return 1;
1.237 + }
1.238 + }
1.239 + else if (err)
1.240 + {
1.241 + fprintf(stderr, "Could not stat target: %s\n", target);
1.242 + return 1;
1.243 + }
1.244 +
1.245 + /* An existing object can be a file or directory. */
1.246 +
1.247 + else
1.248 + {
1.249 + basename = NULL;
1.250 +
1.251 + /* Only permit a target file when one source file is given. */
1.252 +
1.253 + if (!S_ISDIR(st.st_mode) && (argc > 2))
1.254 + {
1.255 + fprintf(stderr, "Target can only be a file when copying a single file: %s\n", target);
1.256 + return 1;
1.257 + }
1.258 + }
1.259 +
1.260 + /* Copy each source object to the target directory. */
1.261 +
1.262 + for (i = 0; i < argc - 1; i++)
1.263 + if (_copy_in(argv[i], target, basename))
1.264 + return 1;
1.265 +
1.266 + return 0;
1.267 +}
1.268 +
1.269 +/* vim: tabstop=4 expandtab shiftwidth=4
1.270 +*/