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.c536
1 files changed, 276 insertions, 260 deletions
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index b1e2813db..9f19b6dd8 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2014-2016 Firejail Authors 2 * Copyright (C) 2014-2017 Firejail Authors
3 * 3 *
4 * This file is part of firejail project 4 * This file is part of firejail project
5 * 5 *
@@ -21,6 +21,9 @@
21#include "../include/common.h" 21#include "../include/common.h"
22#include <fcntl.h> 22#include <fcntl.h>
23#include <ftw.h> 23#include <ftw.h>
24#include <errno.h>
25
26static int arg_follow_link = 0;
24 27
25 28
26#define COPY_LIMIT (500 * 1024 *1024) 29#define COPY_LIMIT (500 * 1024 *1024)
@@ -34,245 +37,244 @@ static char *inpath = NULL;
34 37
35// modified version of the function from util.c 38// modified version of the function from util.c
36static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) { 39static void copy_file(const char *srcname, const char *destname, mode_t mode, uid_t uid, gid_t gid) {
37 assert(srcname); 40 assert(srcname);
38 assert(destname); 41 assert(destname);
39 mode &= 07777; 42 mode &= 07777;
40 43
41 // open source 44 // open source
42 int src = open(srcname, O_RDONLY); 45 int src = open(srcname, O_RDONLY);
43 if (src < 0) { 46 if (src < 0) {
44 fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname); 47 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", srcname);
45 return; 48 return;
46 } 49 }
47 50
48 // open destination 51 // open destination
49 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755); 52 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, 0755);
50 if (dst < 0) { 53 if (dst < 0) {
51 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname); 54 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", destname);
52 close(src); 55 close(src);
53 return; 56 return;
54 } 57 }
55 58
56 // copy 59 // copy
57 ssize_t len; 60 ssize_t len;
58 static const int BUFLEN = 1024; 61 static const int BUFLEN = 1024;
59 unsigned char buf[BUFLEN]; 62 unsigned char buf[BUFLEN];
60 while ((len = read(src, buf, BUFLEN)) > 0) { 63 while ((len = read(src, buf, BUFLEN)) > 0) {
61 int done = 0; 64 int done = 0;
62 while (done != len) { 65 while (done != len) {
63 int rv = write(dst, buf + done, len - done); 66 int rv = write(dst, buf + done, len - done);
64 if (rv == -1) 67 if (rv == -1)
65 goto errexit; 68 goto errexit;
66 done += rv; 69 done += rv;
67 } 70 }
68 } 71 }
69 fflush(0); 72 fflush(0);
70 73
71 if (fchown(dst, uid, gid) == -1) 74 if (fchown(dst, uid, gid) == -1)
72 goto errexit; 75 goto errexit;
73 if (fchmod(dst, mode) == -1) 76 if (fchmod(dst, mode) == -1)
74 goto errexit; 77 goto errexit;
75 78
76 close(src); 79 close(src);
77 close(dst); 80 close(dst);
78 81
79 return; 82 return;
80 83
81errexit: 84errexit:
82 close(src); 85 close(src);
83 close(dst); 86 close(dst);
84 unlink(destname); 87 unlink(destname);
85 fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname); 88 fprintf(stderr, "Warning fcopy: cannot copy %s\n", destname);
86} 89}
87 90
88 91
89 92
90// modified version of the function in firejail/util.c 93// modified version of the function in firejail/util.c
91static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) { 94static void mkdir_attr(const char *fname, mode_t mode, uid_t uid, gid_t gid) {
92 assert(fname); 95 assert(fname);
93 mode &= 07777; 96 mode &= 07777;
94 97
95 if (mkdir(fname, mode) == -1 || 98 if (mkdir(fname, mode) == -1 ||
96 chmod(fname, mode) == -1) { 99 chmod(fname, mode) == -1) {
97 fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname); 100 fprintf(stderr, "Error fcopy: failed to create %s directory\n", fname);
98 errExit("mkdir/chmod"); 101 errExit("mkdir/chmod");
99 } 102 }
100 if (chown(fname, uid, gid)) 103 if (chown(fname, uid, gid))
101 fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname); 104 fprintf(stderr, "Warning fcopy: failed to change ownership of %s\n", fname);
102} 105}
103 106
104void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) { 107void copy_link(const char *target, const char *linkpath, mode_t mode, uid_t uid, gid_t gid) {
105 (void) mode; 108 (void) mode;
106 (void) uid; 109 (void) uid;
107 (void) gid; 110 (void) gid;
108 char *rp = realpath(target, NULL); 111 char *rp = realpath(target, NULL);
109 if (rp) { 112 if (rp) {
110 if (symlink(rp, linkpath) == -1) 113 if (symlink(rp, linkpath) == -1)
111 goto errout; 114 goto errout;
112 free(rp); 115 free(rp);
113 } 116 }
114 else 117 else
115 goto errout; 118 goto errout;
116 119
117 return; 120 return;
118errout: 121errout:
119 fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target); 122 fprintf(stderr, "Warning fcopy: cannot create symbolic link %s\n", target);
120} 123}
121 124
122static int first = 1; 125static int first = 1;
123static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) { 126static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) {
124 (void) st; 127 (void) st;
125 (void) sftw; 128 (void) sftw;
126 assert(infname); 129 assert(infname);
127 assert(*infname != '\0'); 130 assert(*infname != '\0');
128 assert(outpath); 131 assert(outpath);
129 assert(*outpath != '\0'); 132 assert(*outpath != '\0');
130 assert(inpath); 133 assert(inpath);
131 134
132 // check size limit 135 // check size limit
133 if (size_limit_reached) 136 if (size_limit_reached)
134 return 0; 137 return 0;
135 138
136 139
137 char *outfname; 140 char *outfname;
138 if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1) 141 if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1)
139 errExit("asprintf"); 142 errExit("asprintf");
140 143
141//printf("outpaht %s\n", outpath); 144 // don't copy it if we already have the file
142//printf("inpath %s\n", inpath); 145 struct stat s;
143//printf("infname %s\n", infname); 146 if (stat(outfname, &s) == 0) {
144//printf("outfname %s\n\n", outfname); 147 if (first)
145 148 first = 0;
146 // don't copy it if we already have the file 149 else
147 struct stat s; 150 fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname);
148 if (stat(outfname, &s) == 0) { 151 free(outfname);
149 if (first) 152 return 0;
150 first = 0; 153 }
151 else 154
152 fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname); 155 // extract mode and ownership
153 free(outfname); 156 if (stat(infname, &s) != 0) {
154 return 0; 157 fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname);
155 } 158 free(outfname);
156 159 return 0;
157 // extract mode and ownership 160 }
158 if (stat(infname, &s) != 0) { 161 uid_t uid = s.st_uid;
159 fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname); 162 gid_t gid = s.st_gid;
160 free(outfname); 163 mode_t mode = s.st_mode;
161 return 0; 164
162 } 165 // recalculate size
163 uid_t uid = s.st_uid; 166 if ((s.st_size + size_cnt) > COPY_LIMIT) {
164 gid_t gid = s.st_gid; 167 fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024);
165 mode_t mode = s.st_mode; 168 size_limit_reached = 1;
166 169 free(outfname);
167 // recalculate size 170 return 0;
168 if ((s.st_size + size_cnt) > COPY_LIMIT) { 171 }
169 fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024); 172
170 size_limit_reached = 1; 173 file_cnt++;
171 free(outfname); 174 size_cnt += s.st_size;
172 return 0; 175
173 } 176 if(ftype == FTW_F) {
174 177 copy_file(infname, outfname, mode, uid, gid);
175 file_cnt++; 178 }
176 size_cnt += s.st_size; 179 else if (ftype == FTW_D) {
177 180 mkdir_attr(outfname, mode, uid, gid);
178 if(ftype == FTW_F) { 181 }
179 copy_file(infname, outfname, mode, uid, gid); 182 else if (ftype == FTW_SL) {
180 } 183 copy_link(infname, outfname, mode, uid, gid);
181 else if (ftype == FTW_D) { 184 }
182 mkdir_attr(outfname, mode, uid, gid); 185
183 } 186 return(0);
184 else if (ftype == FTW_SL) {
185 copy_link(infname, outfname, mode, uid, gid);
186 }
187
188 return(0);
189} 187}
190 188
191static char *check(const char *src) { 189static char *check(const char *src) {
192 struct stat s; 190 struct stat s;
193 char *rsrc = realpath(src, NULL); 191 char *rsrc = realpath(src, NULL);
194 if (!rsrc || stat(rsrc, &s) == -1) 192 if (!rsrc || stat(rsrc, &s) == -1)
195 goto errexit; 193 goto errexit;
196 194
197 // check uid 195 // check uid
198 if (s.st_uid != getuid() || s.st_gid != getgid()) 196 if (s.st_uid != getuid() || s.st_gid != getgid())
199 goto errexit; 197 goto errexit;
200 198
201 // dir, link, regular file 199 // dir, link, regular file
202 if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode)) 200 if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode) || S_ISLNK(s.st_mode))
203 return rsrc; // normal exit from the function 201 return rsrc; // normal exit from the function
204 202
205errexit: 203errexit:
206 fprintf(stderr, "Error fcopy: invalid file %s\n", src); 204 fprintf(stderr, "Error fcopy: invalid file %s\n", src);
207 exit(1); 205 exit(1);
208} 206}
209 207
210static void duplicate_dir(const char *src, const char *dest, struct stat *s) { 208static void duplicate_dir(const char *src, const char *dest, struct stat *s) {
211 (void) s; 209 (void) s;
212 char *rsrc = check(src); 210 char *rsrc = check(src);
213 char *rdest = check(dest); 211 char *rdest = check(dest);
214 inpath = rsrc; 212 inpath = rsrc;
215 outpath = rdest; 213 outpath = rdest;
216 214
217 // walk 215 // walk
218 if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) { 216 if(nftw(rsrc, fs_copydir, 1, FTW_PHYS) != 0) {
219 fprintf(stderr, "Error: unable to copy file\n"); 217 fprintf(stderr, "Error: unable to copy file\n");
220 exit(1); 218 exit(1);
221 } 219 }
222 220
223 free(rsrc); 221 free(rsrc);
224 free(rdest); 222 free(rdest);
225} 223}
226 224
227static void duplicate_file(const char *src, const char *dest, struct stat *s) { 225static void duplicate_file(const char *src, const char *dest, struct stat *s) {
228 char *rsrc = check(src); 226 char *rsrc = check(src);
229 char *rdest = check(dest); 227 char *rdest = check(dest);
230 uid_t uid = s->st_uid; 228 uid_t uid = s->st_uid;
231 gid_t gid = s->st_gid; 229 gid_t gid = s->st_gid;
232 mode_t mode = s->st_mode; 230 mode_t mode = s->st_mode;
233 231
234 // build destination file name 232 // build destination file name
235 char *name; 233 char *name;
236 char *ptr = strrchr(rsrc, '/'); 234 char *ptr = (arg_follow_link)? strrchr(src, '/'): strrchr(rsrc, '/');
237 ptr++; 235 ptr++;
238 if (asprintf(&name, "%s/%s", rdest, ptr) == -1) 236 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
239 errExit("asprintf"); 237 errExit("asprintf");
240 238
241 // copy 239 // copy
242 copy_file(rsrc, name, mode, uid, gid); 240 copy_file(rsrc, name, mode, uid, gid);
243 241
244 free(name); 242 free(name);
245 free(rsrc); 243 free(rsrc);
246 free(rdest); 244 free(rdest);
247} 245}
248 246
249static void duplicate_link(const char *src, const char *dest, struct stat *s) { 247static void duplicate_link(const char *src, const char *dest, struct stat *s) {
250 char *rsrc = check(src); // we drop the result and use the original name 248 char *rsrc = check(src); // we drop the result and use the original name
251 char *rdest = check(dest); 249 char *rdest = check(dest);
252 uid_t uid = s->st_uid; 250 uid_t uid = s->st_uid;
253 gid_t gid = s->st_gid; 251 gid_t gid = s->st_gid;
254 mode_t mode = s->st_mode; 252 mode_t mode = s->st_mode;
255 253
256 // build destination file name 254 // build destination file name
257 char *name; 255 char *name;
258// char *ptr = strrchr(rsrc, '/'); 256// char *ptr = strrchr(rsrc, '/');
259 char *ptr = strrchr(src, '/'); 257 char *ptr = strrchr(src, '/');
260 ptr++; 258 ptr++;
261 if (asprintf(&name, "%s/%s", rdest, ptr) == -1) 259 if (asprintf(&name, "%s/%s", rdest, ptr) == -1)
262 errExit("asprintf"); 260 errExit("asprintf");
263 261
264 // copy 262 // copy
265 copy_link(rsrc, name, mode, uid, gid); 263 copy_link(rsrc, name, mode, uid, gid);
266 264
267 free(name); 265 free(name);
268 free(rsrc); 266 free(rsrc);
269 free(rdest); 267 free(rdest);
270} 268}
271 269
272static void usage(void) { 270static void usage(void) {
273 printf("Usage: fcopy src dest\n"); 271 fputs("Usage: fcopy [--follow-link] src dest\n"
274 printf("Copy src file in dest directory. If src is a directory, copy all the files in\n"); 272 "\n"
275 printf("src recoursively. If the destination directory does not exist, it will be created.\n"); 273 "Copy SRC to DEST/SRC. SRC may be a file, directory, or symbolic link.\n"
274 "If SRC is a directory it is copied recursively. If it is a symlink,\n"
275 "the link itself is duplicated, unless --follow-link is given,\n"
276 "in which case the destination of the link is copied.\n"
277 "DEST must already exist and must be a directory.\n", stderr);
276} 278}
277 279
278int main(int argc, char **argv) { 280int main(int argc, char **argv) {
@@ -285,56 +287,70 @@ for (i = 0; i < argc; i++)
285printf("\n"); 287printf("\n");
286} 288}
287#endif 289#endif
288 if (argc != 3) { 290 char *src;
289 fprintf(stderr, "Error fcopy: files missing\n"); 291 char *dest;
290 usage(); 292
291 exit(1); 293 if (argc == 3) {
292 } 294 src = argv[1];
293 295 dest = argv[2];
294 // check the two files; remove ending / 296 arg_follow_link = 0;
295 char *src = argv[1]; 297 }
296 int len = strlen(src); 298 else if (argc == 4 && !strcmp(argv[1], "--follow-link")) {
297 if (src[len - 1] == '/') 299 src = argv[2];
298 src[len - 1] = '\0'; 300 dest = argv[3];
299 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) { 301 arg_follow_link = 1;
300 fprintf(stderr, "Error fcopy: invalid file name %s\n", src); 302 }
301 exit(1); 303 else {
302 } 304 fprintf(stderr, "Error: arguments missing\n");
303 305 usage();
304 char *dest = argv[2]; 306 exit(1);
305 len = strlen(dest); 307 }
306 if (dest[len - 1] == '/') 308
307 dest[len - 1] = '\0'; 309 // check the two files; remove ending /
308 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) { 310 int len = strlen(src);
309 fprintf(stderr, "Error fcopy: invalid file name %s\n", dest); 311 if (src[len - 1] == '/')
310 exit(1); 312 src[len - 1] = '\0';
311 } 313 if (strcspn(src, "\\*&!?\"'<>%^(){}[];,") != (size_t)len) {
312 314 fprintf(stderr, "Error fcopy: invalid source file name %s\n", src);
313 315 exit(1);
314 // the destination should be a directory; 316 }
315 struct stat s; 317
316 if (stat(dest, &s) == -1 || 318 len = strlen(dest);
317 !S_ISDIR(s.st_mode)) { 319 if (dest[len - 1] == '/')
318 fprintf(stderr, "Error fcopy: invalid destination directory\n"); 320 dest[len - 1] = '\0';
319 exit(1); 321 if (strcspn(dest, "\\*&!?\"'<>%^(){}[];,~") != (size_t)len) {
320 } 322 fprintf(stderr, "Error fcopy: invalid dest file name %s\n", dest);
321 323 exit(1);
322 // copy files 324 }
323 if (lstat(src, &s) == -1) { 325
324 fprintf(stderr, "Error fcopy: cannot find source file\n"); 326
325 exit(1); 327 // the destination should be a directory;
326 } 328 struct stat s;
327 329 if (stat(dest, &s) == -1) {
328 if (S_ISDIR(s.st_mode)) 330 fprintf(stderr, "Error fcopy: dest dir %s: %s\n", dest, strerror(errno));
329 duplicate_dir(src, dest, &s); 331 exit(1);
330 else if (S_ISREG(s.st_mode)) 332 }
331 duplicate_file(src, dest, &s); 333 if (!S_ISDIR(s.st_mode)) {
332 else if (S_ISLNK(s.st_mode)) 334 fprintf(stderr, "Error fcopy: dest %s is not a directory\n", dest);
333 duplicate_link(src, dest, &s); 335 exit(1);
334 else { 336 }
335 fprintf(stderr, "Error fcopy: source file unsupported\n"); 337
336 exit(1); 338 // copy files
337 } 339 if ((arg_follow_link ? stat : lstat)(src, &s) == -1) {
338 340 fprintf(stderr, "Error fcopy: src %s: %s\n", src, strerror(errno));
339 return 0; 341 exit(1);
342 }
343
344 if (S_ISDIR(s.st_mode))
345 duplicate_dir(src, dest, &s);
346 else if (S_ISREG(s.st_mode))
347 duplicate_file(src, dest, &s);
348 else if (S_ISLNK(s.st_mode))
349 duplicate_link(src, dest, &s);
350 else {
351 fprintf(stderr, "Error fcopy: src %s is an unsupported type of file\n", src);
352 exit(1);
353 }
354
355 return 0;
340} 356}