aboutsummaryrefslogtreecommitdiffstats
path: root/src/fcopy/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fcopy/main.c')
-rw-r--r--src/fcopy/main.c405
1 files changed, 0 insertions, 405 deletions
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
deleted file mode 100644
index e93cd1cb8..000000000
--- a/src/fcopy/main.c
+++ /dev/null
@@ -1,405 +0,0 @@
1/*
2 * Copyright (C) 2014-2018 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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 along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "../include/common.h"
22#include <fcntl.h>
23#include <ftw.h>
24#include <errno.h>
25#include <pwd.h>
26
27int arg_quiet = 0;
28static int arg_follow_link = 0;
29
30#define COPY_LIMIT (500 * 1024 *1024)
31static int size_limit_reached = 0;
32static unsigned file_cnt = 0;
33static unsigned size_cnt = 0;
34
35static char *outpath = NULL;
36static char *inpath = NULL;
37
38// modified version of the function from util.c
39static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) {
40 assert(srcname);
41 assert(destname);
42 mode &= 07777;
43
44 // don't copy the file if it is already there
45 struct stat s;
46 if (stat(destname, &s) == 0)
47 return;
48
49 // open source
50 int src = open(srcname, O_RDONLY);
51 if (src < 0) {
52 if (!arg_quiet)
53 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", srcname);
54 return;
55 }
56
57 // open destination
58 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755);
59 if (dst < 0) {
60 if (!arg_quiet)
61 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname);
62 close(src);
63 return;
64 }
65
66 // copy
67 ssize_t len;
68 static const int BUFLEN = 1024;
69 unsigned char buf[BUFLEN];
70 while ((len = read(src, buf, BUFLEN)) > 0) {
71 int done = 0;
72 while (done != len) {
73 int rv = write(dst, buf + done, len - done);
74 if (rv == -1)
75 goto errexit;
76 done += rv;
77 }
78 }
79 fflush(0);
80
81 if (fchown(dst, uid, gid) == -1)
82 goto errexit;
83 if (fchmod(dst, mode) == -1)
84 goto errexit;
85
86 close(src);
87 close(dst);
88
89 return;
90
91errexit:
92 close(src);
93 close(dst);
94 unlink(destname);
95 if (!arg_quiet)
96 fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname);
97}
98
99
100// modified version of the function in firejail/util.c
101static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) {
102 assert(fname);
103 mode &= 07777;
104
105 if (mkdir(fname, mode) == -1 ||
106 chmod(fname, mode) == -1) {
107 fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname);
108 errExit("mkdir/chmod");
109 }
110 if (chown(fname, uid, gid)) {
111 if (!arg_quiet)
112 fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname);
113 }
114}
115
116
117void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) {
118 (void) mode;
119 (void) uid;
120 (void) gid;
121
122 // if the link is already there, don't create it
123 struct stat s;
124 if (stat(linkpath, &s) == 0)
125 return;
126
127 char *rp = realpath(target, NULL);
128 if (rp) {
129 if (symlink(rp, linkpath) == -1) {
130 free(rp);
131 goto errout;
132 }
133 free(rp);
134 }
135 else
136 goto errout;
137
138 return;
139errout:
140 if (!arg_quiet)
141 fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target);
142}
143
144
145
146static int first = 1;
147static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) {
148 (void) st;
149 (void) sftw;
150 assert(infname);
151 assert(*infname != '\0');
152 assert(outpath);
153 assert(*outpath != '\0');
154 assert(inpath);
155
156 // check size limit
157 if (size_limit_reached)
158 return 0;
159
160 char *outfname;
161 if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1)
162 errExit("asprintf");
163
164 // don't copy it if we already have the file
165 struct stat s;
166 if (stat(outfname, &s) == 0) {
167 if (first)
168 first = 0;
169 else if (!arg_quiet)
170 fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname);
171 free(outfname);
172 return 0;
173 }
174
175 // extract mode and ownership
176 if (stat(infname, &s) != 0) {
177 if (!arg_quiet)
178 fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname);
179 free(outfname);
180 return 0;
181 }
182 uid_t uid = s.st_uid;
183 gid_t gid = s.st_gid;
184 mode_t mode = s.st_mode;
185
186 // recalculate size
187 if ((s.st_size + size_cnt) > COPY_LIMIT) {
188 fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024);
189 size_limit_reached = 1;
190 free(outfname);
191 return 0;
192 }
193
194 file_cnt++;
195 size_cnt += s.st_size;
196
197 if(ftype == FTW_F) {
198 copy_file(infname, outfname, mode, uid, gid);
199 }
200 else if (ftype == FTW_D) {
201 mkdir_attr(outfname, mode, uid, gid);
202 }
203 else if (ftype == FTW_SL) {
204 copy_link(infname, outfname, mode, uid, gid);
205 }
206
207 return(0);
208}
209
210
211static char *check(const char *src) {
212 struct stat s;
213 char *rsrc = realpath(src, NULL);
214 if (!rsrc || stat(rsrc, &s) == -1)
215 goto errexit;
216
217 // on systems with systemd-resolved installed /etc/resolve.conf is a symlink to
218 // /run/systemd/resolve/resolv.conf; this file is owned by systemd-resolve user
219 // checking gid will fail for files with a larger group such as /usr/bin/mutt_dotlock
220 uid_t user = getuid();
221 if (user == 0 && strncmp(rsrc, "/run/systemd/resolve/", 21) == 0) {
222 // check user systemd-resolve
223 struct passwd *p = getpwnam("systemd-resolve");
224 if (!p)
225 goto errexit;
226 if (s.st_uid != user && s.st_uid != p->pw_uid)
227 goto errexit;
228 }
229 else {
230 if (s.st_uid != user)
231 goto errexit;
232 }
233
234 // dir, link, regular file
235 if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode))
236 return rsrc; // normal exit from the function
237
238errexit:
239 fprintf(stderr, "Error fcopy: invalid file %s\n", src);
240 exit(1);
241}
242
243
244static void duplicate_dir(const char *src, const char *dest, struct stat *s) {
245 (void) s;
246 char *rsrc = check(src);
247 char *rdest = check(dest);
248 inpath = rsrc;
249 outpath = rdest;
250
251 // walk
252 if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) {
253 fprintf(stderr, "Error: unable to copy file\n");
254 exit(1);
255 }
256
257 free(rsrc);
258 free(rdest);
259}
260
261
262static void duplicate_file(const char *src, const char *dest, struct stat *s) {
263 char *rsrc = check(src);
264 char *rdest = check(dest);
265 uid_t uid = s->st_uid;
266 gid_t gid = s->st_gid;
267 mode_t mode = s->st_mode;
268
269 // build destination file name
270 char *name;
271 char *ptr = (arg_follow_link)? strrchr(src, '/'): strrchr(rsrc, '/');
272 ptr++;
273 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
274 errExit("asprintf");
275
276 // copy
277 copy_file(rsrc, name, mode, uid, gid);
278
279 free(name);
280 free(rsrc);
281 free(rdest);
282}
283
284
285static void duplicate_link(const char *src, const char *dest, struct stat *s) {
286 char *rsrc = check(src); // we drop the result and use the original name
287 char *rdest = check(dest);
288 uid_t uid = s->st_uid;
289 gid_t gid = s->st_gid;
290 mode_t mode = s->st_mode;
291
292 // build destination file name
293 char *name;
294 // char *ptr = strrchr(rsrc, '/');
295 char *ptr = strrchr(src, '/');
296 ptr++;
297 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
298 errExit("asprintf");
299
300 // copy
301 copy_link(rsrc, name, mode, uid, gid);
302
303 free(name);
304 free(rsrc);
305 free(rdest);
306}
307
308
309static void usage(void) {
310 fputs("Usage: fcopy [--follow-link] src dest\n"
311 "\n"
312 "Copy SRC to DEST/SRC. SRC may be a file, directory, or symbolic link.\n"
313 "If SRC is a directory it is copied recursively. If it is a symlink,\n"
314 "the link itself is duplicated, unless --follow-link is given,\n"
315 "in which case the destination of the link is copied.\n"
316 "DEST must already exist and must be a directory.\n", stderr);
317}
318
319
320int main(int argc, char **argv) {
321#if 0
322 {
323 //system("cat /proc/self/status");
324 int i;
325 for (i = 0; i < argc; i++)
326 printf("*%s* ", argv[i]);
327 printf("\n");
328 }
329#endif
330 char *quiet = getenv("FIREJAIL_QUIET");
331 if (quiet && strcmp(quiet, "yes") == 0)
332 arg_quiet = 1;
333
334 char *src;
335 char *dest;
336
337 if (argc == 3) {
338 src = argv[1];
339 dest = argv[2];
340 arg_follow_link = 0;
341 }
342 else if (argc == 4 && !strcmp(argv[1], "--follow-link")) {
343 src = argv[2];
344 dest = argv[3];
345 arg_follow_link = 1;
346 }
347 else {
348 fprintf(stderr, "Error: arguments missing\n");
349 usage();
350 exit(1);
351 }
352
353 // trim trailing chars
354 if (src[strlen(src) - 1] == '/')
355 src[strlen(src) - 1] = '\0';
356 if (dest[strlen(dest) - 1] == '/')
357 dest[strlen(dest) - 1] = '\0';
358
359 // check the two files; remove ending /
360 int len = strlen(src);
361 if (src[len - 1] == '/')
362 src[len - 1] = '\0';
363 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) {
364 fprintf(stderr, "Error fcopy: invalid source file name %s\n", src);
365 exit(1);
366 }
367
368 len = strlen(dest);
369 if (dest[len - 1] == '/')
370 dest[len - 1] = '\0';
371 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) {
372 fprintf(stderr, "Error fcopy: invalid dest file name %s\n", dest);
373 exit(1);
374 }
375
376 // the destination should be a directory;
377 struct stat s;
378 if (stat(dest, &s) == -1) {
379 fprintf(stderr, "Error fcopy: dest dir %s: %s\n", dest, strerror(errno));
380 exit(1);
381 }
382 if (!S_ISDIR(s.st_mode)) {
383 fprintf(stderr, "Error fcopy: dest %s is not a directory\n", dest);
384 exit(1);
385 }
386
387 // copy files
388 if ((arg_follow_link ? stat : lstat)(src, &s) == -1) {
389 fprintf(stderr, "Error fcopy: src %s: %s\n", src, strerror(errno));
390 exit(1);
391 }
392
393 if (S_ISDIR(s.st_mode))
394 duplicate_dir(src, dest, &s);
395 else if (S_ISREG(s.st_mode))
396 duplicate_file(src, dest, &s);
397 else if (S_ISLNK(s.st_mode))
398 duplicate_link(src, dest, &s);
399 else {
400 fprintf(stderr, "Error fcopy: src %s is an unsupported type of file\n", src);
401 exit(1);
402 }
403
404 return 0;
405}