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