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