1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libe2access/host/op_copy_in.c Sun Mar 06 01:30:32 2022 +0100
1.3 @@ -0,0 +1,270 @@
1.4 +/*
1.5 + * Access 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 +
1.27 +#include <sys/stat.h>
1.28 +
1.29 +#include <ext2fs/ext2fs.h>
1.30 +
1.31 +#include "file.h"
1.32 +#include "image.h"
1.33 +#include "path.h"
1.34 +#include "session.h"
1.35 +
1.36 +
1.37 +
1.38 +/* Copy buffer size. */
1.39 +
1.40 +extern int BUFSIZE;
1.41 +
1.42 +
1.43 +
1.44 +/* Alternative metadata set by options. */
1.45 +
1.46 +extern struct metadata md;
1.47 +
1.48 +
1.49 +
1.50 +/* Copy a file into the filesystem image. */
1.51 +
1.52 +static int _copy_file_in(const char *filename, ext2_filsys fs,
1.53 + ext2_ino_t ino_file, int flags)
1.54 +{
1.55 + int retval = 0;
1.56 + ext2_file_t file;
1.57 +
1.58 + /* Copying details. */
1.59 +
1.60 + FILE *fp;
1.61 + char buf[BUFSIZE];
1.62 + size_t got;
1.63 + unsigned int written;
1.64 +
1.65 + /* Open a file in the target directory. */
1.66 +
1.67 + if (ext2fs_file_open(fs, ino_file, flags, &file))
1.68 + return 1;
1.69 +
1.70 + /* Open the file in the source directory. */
1.71 +
1.72 + fp = fopen(filename, "r");
1.73 +
1.74 + /* Copy the file content. */
1.75 +
1.76 + if (fp != NULL)
1.77 + {
1.78 + while (got = fread(buf, sizeof(char), BUFSIZE, fp))
1.79 + {
1.80 + while (got)
1.81 + {
1.82 + if (ext2fs_file_write(file, buf, got, &written))
1.83 + {
1.84 + retval = 1;
1.85 + goto close_files;
1.86 + }
1.87 + got -= written;
1.88 + }
1.89 + }
1.90 + }
1.91 +
1.92 +close_files:
1.93 + fclose(fp);
1.94 + ext2fs_file_flush(file);
1.95 + ext2fs_file_close(file);
1.96 +
1.97 + return retval;
1.98 +}
1.99 +
1.100 +/* Make a new object using the given function. */
1.101 +
1.102 +static int _make_object(ext2_filsys fs, const char *filename,
1.103 + ext2_ino_t ino_parent, const char *basename,
1.104 + ext2_ino_t *ino_target,
1.105 + errcode_t (*fn)(ext2_filsys, ext2_ino_t, const char *,
1.106 + __u16, __u16, __u16, ext2_ino_t *))
1.107 +{
1.108 + errcode_t retval;
1.109 + struct stat st;
1.110 +
1.111 + /* Obtain the metadata. */
1.112 +
1.113 + if (lstat(filename, &st))
1.114 + {
1.115 + fprintf(stderr, "Failed to read object metadata: %s\n", filename);
1.116 + return 1;
1.117 + }
1.118 +
1.119 + retval = fn(fs, ino_parent, basename, st.st_mode & ~md.mask,
1.120 + md.have_uid ? md.uid : st.st_uid,
1.121 + md.have_gid ? md.gid : st.st_gid,
1.122 + ino_target);
1.123 +
1.124 + if (retval)
1.125 + {
1.126 + fprintf(stderr, "Failed to create object: %s\n", filename);
1.127 + return 1;
1.128 + }
1.129 +
1.130 + return 0;
1.131 +}
1.132 +
1.133 +/* Copy a source file from the external environment into the filesystem. */
1.134 +
1.135 +static int _copy_in(ext2_filsys fs, const char *filename, ext2_ino_t ino_target,
1.136 + const char *basename)
1.137 +{
1.138 + errcode_t retval;
1.139 + int flags;
1.140 +
1.141 + /* By default, treat the target as a new object. */
1.142 +
1.143 + ext2_ino_t ino_parent = ino_target;
1.144 + int target_is_new = 1;
1.145 +
1.146 + /* Source file details. */
1.147 +
1.148 + struct stat st;
1.149 +
1.150 + /* Without a basename, the target exists and is either a directory, into
1.151 + which the source file shall be copied, or it is a file that shall be
1.152 + overwritten. */
1.153 +
1.154 + if (basename == NULL)
1.155 + {
1.156 + basename = path_basename(filename);
1.157 + target_is_new = image_isdir_by_inode(fs, ino_target);
1.158 + }
1.159 +
1.160 + /* Directories are created with the same metadata. */
1.161 +
1.162 + if (isdir(filename))
1.163 + {
1.164 + if (!target_is_new)
1.165 + {
1.166 + fprintf(stderr, "Directory cannot be copied as it already exists: %s\n", filename);
1.167 + return 1;
1.168 + }
1.169 +
1.170 + if (_make_object(fs, filename, ino_parent, basename, &ino_target,
1.171 + image_make_dir))
1.172 + return 1;
1.173 + }
1.174 +
1.175 + /* Files are copied. */
1.176 +
1.177 + else if (isfile(filename))
1.178 + {
1.179 + flags = EXT2_FILE_WRITE;
1.180 +
1.181 + /* Obtain the inode for the target file. */
1.182 +
1.183 + if (target_is_new)
1.184 + {
1.185 + if (_make_object(fs, filename, ino_parent, basename, &ino_target,
1.186 + image_create_file))
1.187 + return 1;
1.188 +
1.189 + flags |= EXT2_FILE_CREATE;
1.190 + }
1.191 +
1.192 + /* NOTE: Overwrite/update metadata where appropriate. */
1.193 +
1.194 + if (_copy_file_in(filename, fs, ino_target, flags))
1.195 + {
1.196 + fprintf(stderr, "Failed to copy file: %s\n", filename);
1.197 + return 1;
1.198 + }
1.199 + }
1.200 +}
1.201 +
1.202 +/* Copy source files from the external environment into the filesystem image. */
1.203 +
1.204 +int copy_in(ext2_filsys fs, int argc, char *argv[])
1.205 +{
1.206 + errcode_t retval;
1.207 + int i;
1.208 +
1.209 + /* Target filename details. */
1.210 +
1.211 + const char *target = argv[argc - 1];
1.212 + const char *target_remaining = target;
1.213 + const char *basename;
1.214 +
1.215 + /* Target file and directory details. */
1.216 +
1.217 + int target_is_file;
1.218 + ext2_ino_t ino_target;
1.219 +
1.220 + /* Locate the target and test whether it is a file or a directory. */
1.221 +
1.222 + if (image_resolve_by_path(fs, &target_remaining, &ino_target))
1.223 + {
1.224 + /* Only a non-existent file in an existing directory is permitted. */
1.225 +
1.226 + if (!image_isdir_by_inode(fs, ino_target) || !path_is_leafname(target_remaining))
1.227 + {
1.228 + fprintf(stderr, "Target not found: %s\n", target);
1.229 + return 1;
1.230 + }
1.231 +
1.232 + /* Any absent target leaves the basename remaining.
1.233 + The resolved target is the parent directory. */
1.234 +
1.235 + target_is_file = 0;
1.236 + basename = target_remaining;
1.237 + }
1.238 + else
1.239 + {
1.240 + /* Any present target can be a file or directory.
1.241 + The resolved target is the actual target. */
1.242 +
1.243 + target_is_file = image_isfile_by_inode(fs, ino_target);
1.244 + basename = NULL;
1.245 + }
1.246 +
1.247 + /* Only permit a target file when one source file is given. */
1.248 +
1.249 + if (target_is_file)
1.250 + {
1.251 + if (argc > 2)
1.252 + {
1.253 + fprintf(stderr, "Target can only be a file when copying a single file: %s\n", target);
1.254 + return 1;
1.255 + }
1.256 + }
1.257 + else if (!image_isdir_by_inode(fs, ino_target))
1.258 + {
1.259 + fprintf(stderr, "Target is not a directory: %s\n", target);
1.260 + return 1;
1.261 + }
1.262 +
1.263 + /* Copy each source object to the target directory. */
1.264 +
1.265 + for (i = 0; i < argc - 1; i++)
1.266 + if (_copy_in(fs, argv[i], ino_target, basename))
1.267 + return 1;
1.268 +
1.269 + return 0;
1.270 +}
1.271 +
1.272 +/* vim: tabstop=4 expandtab shiftwidth=4
1.273 +*/