1 /*
2 Authors:
3 Jakub Hrozek <jhrozek@redhat.com>
4
5 Copyright (C) 2009 Red Hat
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22 * This file incorporates work covered by the following copyright and
23 * permission notice:
24 *
25 * Copyright (c) 1991 - 1994, Julianne Frances Haugh
26 * Copyright (c) 1996 - 2001, Marek Michałkiewicz
27 * Copyright (c) 2003 - 2006, Tomasz Kłoczko
28 * Copyright (c) 2007 - 2008, Nicolas François
29 *
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. The name of the copyright holders or contributors may not be used to
41 * endorse or promote products derived from this software without
42 * specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
45 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
46 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
47 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
48 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
50 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
54 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55 */
56
57 #include <sys/stat.h>
58 #include <sys/types.h>
59 #include <sys/time.h>
60 #include <dirent.h>
61 #include <fcntl.h>
62 #include <errno.h>
63 #include <talloc.h>
64
65 #include "config.h"
66 #include "util/util.h"
67 #include "tools/tools_util.h"
68
69 int copy_tree(const char *src_root, const char *dst_root,
70 uid_t uid, gid_t gid);
71
72 struct copy_ctx {
73 const char *src_orig;
74 const char *dst_orig;
75 dev_t src_dev;
76 };
77
78 /* wrapper in order not to create a temporary context in
79 * every iteration */
80 static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
81 dev_t parent_dev,
82 const char *root);
83
84 int remove_tree(const char *root)
85 {
86 TALLOC_CTX *tmp_ctx = NULL;
87 int ret;
88
89 tmp_ctx = talloc_new(NULL);
90 if (!tmp_ctx) {
91 return ENOMEM;
92 }
93
94 ret = remove_tree_with_ctx(tmp_ctx, 0, root);
95 talloc_free(tmp_ctx);
96 return ret;
97 }
98
99 /*
100 * The context is not freed in case of error
101 * because this is a recursive function, will be freed when we
102 * reach the top level remove_tree() again
103 */
104 static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
105 dev_t parent_dev,
106 const char *root)
107 {
108 char *fullpath = NULL;
109 struct dirent *result;
110 struct dirent direntp;
111 struct stat statres;
112 DIR *rootdir = NULL;
113 int ret;
114
115 rootdir = opendir(root);
116 if (rootdir == NULL) {
117 ret = errno;
118 DEBUG(1, ("Cannot open directory %s [%d][%s]\n",
119 root, ret, strerror(ret)));
120 goto fail;
121 }
122
123 while (readdir_r(rootdir, &direntp, &result) == 0) {
124 if (result == NULL) {
125 /* End of directory */
126 break;
127 }
128
129 if (strcmp (direntp.d_name, ".") == 0 ||
130 strcmp (direntp.d_name, "..") == 0) {
131 continue;
132 }
133
134 fullpath = talloc_asprintf(mem_ctx, "%s/%s", root, direntp.d_name);
135 if (fullpath == NULL) {
136 ret = ENOMEM;
137 goto fail;
138 }
139
140 ret = lstat(fullpath, &statres);
141 if (ret != 0) {
142 ret = errno;
143 DEBUG(1, ("Cannot stat %s: [%d][%s]\n",
144 fullpath, ret, strerror(ret)));
145 goto fail;
146 }
147
148 if (S_ISDIR(statres.st_mode)) {
149 /* if directory, recursively descend, but check if on the same FS */
150 if (parent_dev && parent_dev != statres.st_dev) {
151 DEBUG(1, ("Directory %s is on different filesystem, "
152 "will not follow\n", fullpath));
153 ret = EFAULT;
154 goto fail;
155 }
156
157 ret = remove_tree_with_ctx(mem_ctx, statres.st_dev, fullpath);
158 if (ret != EOK) {
159 DEBUG(1, ("Removing subdirectory %s failed: [%d][%s]\n",
160 fullpath, ret, strerror(ret)));
161 goto fail;
162 }
163 } else {
164 ret = unlink(fullpath);
165 if (ret != 0) {
166 ret = errno;
167 DEBUG(1, ("Removing file %s failed: [%d][%s]\n",
168 fullpath, ret, strerror(ret)));
169 goto fail;
170 }
171 }
172
173 talloc_free(fullpath);
174 }
175
176 ret = closedir(rootdir);
177 if (ret != 0) {
178 ret = errno;
179 goto fail;
180 }
181
182 ret = rmdir(root);
183 if (ret != 0) {
184 ret = errno;
185 goto fail;
186 }
187
188 fail:
189 return ret;
190 }
191
192 static int copy_dir(const char *src, const char *dst,
193 const struct stat *statp, const struct timeval mt[2],
194 uid_t uid, gid_t gid)
195 {
196 int ret = 0;
197
198 /*
199 * Create a new target directory, make it owned by
200 * the user and then recursively copy that directory.
201 */
202 selinux_file_context(dst);
203
204 ret = mkdir(dst, statp->st_mode);
205 if (ret != 0) {
206 ret = errno;
207 DEBUG(1, ("Cannot mkdir directory '%s': [%d][%s].\n",
208 dst, ret, strerror(ret)));
209 return ret;
210 }
211
212 ret = chown(dst, uid, gid);
213 if (ret != 0) {
214 ret = errno;
215 DEBUG(1, ("Cannot chown directory '%s': [%d][%s].\n",
216 dst, ret, strerror(ret)));
217 return ret;
218 }
219
220 ret = chmod(dst, statp->st_mode);
221 if (ret != 0) {
222 ret = errno;
223 DEBUG(1, ("Cannot chmod directory '%s': [%d][%s].\n",
224 dst, ret, strerror(ret)));
225 return ret;
226 }
227
228 ret = copy_tree(src, dst, uid, gid);
229 if (ret != 0) {
230 ret = errno;
231 DEBUG(1, ("Cannot copy directory from '%s' to '%s': [%d][%s].\n",
232 src, dst, ret, strerror(ret)));
233 return ret;
234 }
235
236 ret = utimes(dst, mt);
237 if (ret != 0) {
238 ret = errno;
239 DEBUG(1, ("Cannot set utimes on a directory '%s': [%d][%s].\n",
240 dst, ret, strerror(ret)));
241 return ret;
242 }
243
244 return EOK;
245 }
246
247 static char *talloc_readlink(TALLOC_CTX *mem_ctx, const char *filename)
248 {
249 size_t size = 1024;
250 ssize_t nchars;
251 char *buffer;
252
253 buffer = talloc_array(mem_ctx, char, size);
254 if (!buffer) {
255 return NULL;
256 }
257
258 while (1) {
259 nchars = readlink(filename, buffer, size);
260 if (nchars < 0) {
261 return NULL;
262 }
263
264 if ((size_t) nchars < size) {
265 /* The buffer was large enough */
266 break;
267 }
268
269 /* Try again with a bigger buffer */
270 size *= 2;
271 buffer = talloc_realloc(mem_ctx, buffer, char, size);
272 if (!buffer) {
273 return NULL;
274 }
275 }
276
277 /* readlink does not nul-terminate */
278 buffer[nchars] = '\0';
279 return buffer;
280 }
281
282 static int copy_symlink(struct copy_ctx *cctx,
283 const char *src,
284 const char *dst,
285 const struct stat *statp,
286 const struct timeval mt[],
287 uid_t uid, gid_t gid)
288 {
289 int ret;
290 char *oldlink;
291 char *tmp;
292 TALLOC_CTX *tmp_ctx = NULL;
293
294 tmp_ctx = talloc_new(cctx);
295 if (!tmp_ctx) {
296 return ENOMEM;
297 }
298
299 /*
300 * Get the name of the file which the link points
301 * to. If that name begins with the original
302 * source directory name, that part of the link
303 * name will be replaced with the original
304 * destination directory name.
305 */
306 oldlink = talloc_readlink(tmp_ctx, src);
307 if (oldlink == NULL) {
308 ret = ENOMEM;
309 goto done;
310 }
311
312 /* If src was a link to an entry of the src_orig directory itself,
313 * create a link to the corresponding entry in the dst_orig
314 * directory.
315 * FIXME: This may change a relative link to an absolute link
316 */
317 if (strncmp(oldlink, cctx->src_orig, strlen(cctx->src_orig)) == 0) {
318 tmp = talloc_asprintf(tmp_ctx, "%s%s", cctx->dst_orig, oldlink + strlen(cctx->src_orig));
319 if (tmp == NULL) {
320 ret = ENOMEM;
321 goto done;
322 }
323
324 talloc_free(oldlink);
325 oldlink = tmp;
326 }
327
328 selinux_file_context(dst);
329
330 ret = symlink(oldlink, dst);
331 if (ret != 0) {
332 ret = errno;
333 DEBUG(1, ("symlink() failed on file '%s': [%d][%s].\n",
334 dst, ret, strerror(ret)));
335 goto done;
336 }
337
338 ret = lchown(dst, uid, gid);
339 if (ret != 0) {
340 ret = errno;
341 DEBUG(1, ("lchown() failed on file '%s': [%d][%s].\n",
342 dst, ret, strerror(ret)));
343 goto done;
344 }
345
346 done:
347 talloc_free(tmp_ctx);
348 return ret;
349 }
350
351 static int copy_special(const char *dst,
352 const struct stat *statp,
353 const struct timeval mt[],
354 uid_t uid, gid_t gid)
355 {
356 int ret = 0;
357
358 selinux_file_context(dst);
359
360 ret = mknod(dst, statp->st_mode & ~07777, statp->st_rdev);
361 if (ret != 0) {
362 ret = errno;
363 DEBUG(1, ("Cannot mknod special file '%s': [%d][%s].\n",
364 dst, ret, strerror(ret)));
365 return ret;
366 }
367
368 ret = chown(dst, uid, gid);
369 if (ret != 0) {
370 ret = errno;
371 DEBUG(1, ("Cannot chown special file '%s': [%d][%s].\n",
372 dst, ret, strerror(ret)));
373 return ret;
374 }
375
376 ret = chmod(dst, statp->st_mode & 07777);
377 if (ret != 0) {
378 ret = errno;
379 DEBUG(1, ("Cannot chmod special file '%s': [%d][%s].\n",
380 dst, ret, strerror(ret)));
381 return ret;
382 }
383
384 ret = utimes(dst, mt);
385 if (ret != 0) {
386 ret = errno;
387 DEBUG(1, ("Cannot call utimes on special file '%s': [%d][%s].\n",
388 dst, ret, strerror(ret)));
389 return ret;
390 }
391
392 return EOK;
393 }
394
395 static int copy_file(const char *src,
396 const char *dst,
397 const struct stat *statp,
398 const struct timeval mt[],
399 uid_t uid, gid_t gid)
400 {
401 int ret;
402 int ifd = -1;
403 int ofd = -1;
404 char buf[1024];
405 ssize_t cnt, written, offset;
406 struct stat fstatbuf;
407
408 ifd = open(src, O_RDONLY);
409 if (ifd < 0) {
410 ret = errno;
411 DEBUG(1, ("Cannot open() source file '%s': [%d][%s].\n",
412 src, ret, strerror(ret)));
413 goto fail;
414 }
415
416 ret = fstat(ifd, &fstatbuf);
417 if (ret != 0) {
418 ret = errno;
419 DEBUG(1, ("Cannot fstat() source file '%s': [%d][%s].\n",
420 src, ret, strerror(ret)));
421 goto fail;
422 }
423
424 if (statp->st_dev != fstatbuf.st_dev ||
425 statp->st_ino != fstatbuf.st_ino) {
426 DEBUG(1, ("File %s was modified between lstat and open.\n", src));
427 ret = EIO;
428 goto fail;
429 }
430
431 selinux_file_context(dst);
432
433 ofd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777);
434 if (ofd < 0) {
435 ret = errno;
436 DEBUG(1, ("Cannot open() destination file '%s': [%d][%s].\n",
437 dst, ret, strerror(ret)));
438 goto fail;
439 }
440
441 ret = fchown(ofd, uid, gid);
442 if (ret != 0) {
443 ret = errno;
444 DEBUG(1, ("Cannot fchown() destination file '%s': [%d][%s].\n",
445 dst, ret, strerror(ret)));
446 goto fail;
447 }
448
449 ret = fchmod(ofd, statp->st_mode & 07777);
450 if (ret != 0) {
451 ret = errno;
452 DEBUG(1, ("Cannot fchmod() destination file '%s': [%d][%s].\n",
453 dst, ret, strerror(ret)));
454 goto fail;
455 }
456
457 while ((cnt = read(ifd, buf, sizeof(buf))) > 0) {
458 offset = 0;
459 while (cnt > 0) {
460 written = write(ofd, buf+offset, (size_t)cnt);
461 if (written == -1) {
462 ret = errno;
463 DEBUG(1, ("Cannot write() to source file '%s': [%d][%s].\n",
464 dst, ret, strerror(ret)));
465 goto fail;
466 }
467 offset += written;
468 cnt -= written;
469 }
470 }
471 if (cnt == -1) {
472 ret = errno;
473 DEBUG(1, ("Cannot read() from source file '%s': [%d][%s].\n",
474 dst, ret, strerror(ret)));
475 goto fail;
476 }
477
478
479 ret = close(ifd);
480 ifd = -1;
481 if (ret != 0) {
482 ret = errno;
483 DEBUG(1, ("Cannot close() source file '%s': [%d][%s].\n",
484 dst, ret, strerror(ret)));
485 goto fail;
486 }
487
488 ret = close(ofd);
489 ifd = -1;
490 if (ret != 0) {
491 ret = errno;
492 DEBUG(1, ("Cannot close() destination file '%s': [%d][%s].\n",
493 dst, ret, strerror(ret)));
494 goto fail;
495 }
496
497 ret = utimes(dst, mt);
498 if (ret != 0) {
499 ret = errno;
500 DEBUG(1, ("Cannot call utimes() on destination file '%s': [%d][%s].\n",
501 dst, ret, strerror(ret)));
502 goto fail;
503 }
504
505 return EOK;
506
507 /* Reachable by jump only */
508 fail:
509 if (ifd != -1) close(ifd);
510 if (ofd != -1) close(ofd);
511 return ret;
512 }
513
514 /*
515 * The context is not freed in case of error
516 * because this is a recursive function, will be freed when we
517 * reach the top level copy_tree() again
518 */
519 static int copy_entry(struct copy_ctx *cctx,
520 const char *src,
521 const char *dst,
522 uid_t uid,
523 gid_t gid)
524 {
525 int ret = EOK;
526 struct stat sb;
527 struct timeval mt[2];
528
529 ret = lstat(src, &sb);
530 if (ret == -1) {
531 ret = errno;
532 DEBUG(1, ("Cannot lstat() the source file '%s': [%d][%s].\n",
533 src, ret, strerror(ret)));
534 return ret;
535 }
536
537 mt[0].tv_sec = sb.st_atime;
538 mt[0].tv_usec = 0;
539
540 mt[1].tv_sec = sb.st_mtime;
541 mt[1].tv_usec = 0;
542
543 if (S_ISLNK (sb.st_mode)) {
544 ret = copy_symlink(cctx, src, dst, &sb, mt, uid, gid);
545 if (ret != EOK) {
546 DEBUG(1, ("Cannot copy symlink '%s' to '%s': [%d][%s]\n",
547 src, dst, ret, strerror(ret)));
548 }
549 return ret;
550 }
551
552 if (S_ISDIR(sb.st_mode)) {
553 /* Check if we're still on the same FS */
554 if (sb.st_dev != cctx->src_dev) {
555 DEBUG(2, ("Will not descend to other FS\n"));
556 /* Skip this without error */
557 return EOK;
558 }
559 return copy_dir(src, dst, &sb, mt, uid, gid);
560 } else if (!S_ISREG(sb.st_mode)) {
561 /*
562 * Deal with FIFOs and special files. The user really
563 * shouldn't have any of these, but it seems like it
564 * would be nice to copy everything ...
565 */
566 return copy_special(dst, &sb, mt, uid, gid);
567 } else {
568 /*
569 * Create the new file and copy the contents. The new
570 * file will be owned by the provided UID and GID values.
571 */
572 return copy_file(src, dst, &sb, mt, uid, gid);
573 }
574
575 return ret;
576 }
577
578 /*
579 * The context is not freed in case of error
580 * because this is a recursive function, will be freed when we
581 * reach the top level copy_tree() again
582 */
583 static int copy_tree_ctx(struct copy_ctx *cctx,
584 const char *src_root,
585 const char *dst_root,
586 uid_t uid,
587 gid_t gid)
588 {
589 DIR *src_dir;
590 int ret;
591 struct dirent *result;
592 struct dirent direntp;
593 char *src_name, *dst_name;
594 TALLOC_CTX *tmp_ctx;
595
596 tmp_ctx = talloc_new(cctx);
597
Event alloc_fn: Calling allocation function "opendir". Event var_assign: Assigning: "src_dir" = storage returned from "opendir(src_root)".
Also see events: [leaked_storage][noescape] | |
598 src_dir = opendir(src_root);
At conditional (1): "src_dir == NULL": Taking false branch.
|
599 if (src_dir == NULL) {
600 ret = errno;
601 DEBUG(1, ("Cannot open the source directory %s: [%d][%s].\n",
602 src_root, ret, strerror(ret)));
603 goto fail;
604 }
605
Event noescape: Variable "src_dir" is not freed or pointed-to in function "readdir_r".
Also see events: [alloc_fn][var_assign][leaked_storage]At conditional (2): "readdir_r(src_dir, &direntp, &result) == 0": Taking true branch.
| | |
606 while (readdir_r(src_dir, &direntp, &result) == 0) {
At conditional (3): "result == NULL": Taking false branch.
|
607 if (result == NULL) {
608 /* End of directory */
609 break;
610 }
611
At conditional (4): "strcmp(direntp.d_name, &".") == 0": Taking false branch.
At conditional (5): "strcmp(direntp.d_name, &"..") == 0": Taking false branch.
| |
612 if (strcmp (direntp.d_name, ".") == 0 ||
613 strcmp (direntp.d_name, "..") == 0) {
614 continue;
615 }
616
617 /* build src and dst paths */
618 src_name = talloc_asprintf(tmp_ctx, "%s/%s", src_root, direntp.d_name);
619 dst_name = talloc_asprintf(tmp_ctx, "%s/%s", dst_root, direntp.d_name);
At conditional (6): "dst_name == NULL": Taking true branch.
|
620 if (dst_name == NULL || src_name == NULL) {
621 ret = ENOMEM;
622 goto fail;
623 }
624
625 /* copy */
626 ret = copy_entry(cctx, src_name, dst_name, uid, gid);
627 if (ret != EOK) {
628 DEBUG(1, ("Cannot copy '%s' to '%s', error %d\n",
629 src_name, dst_name, ret));
630 goto fail;
631 }
632 talloc_free(src_name);
633 talloc_free(dst_name);
634 }
635
636 ret = closedir(src_dir);
637 if (ret != 0) {
638 ret = errno;
639 goto fail;
640 }
641
642 fail:
643 talloc_free(tmp_ctx);
Event leaked_storage: Variable "src_dir" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][noescape] | |
644 return ret;
645 }
646
647 int copy_tree(const char *src_root, const char *dst_root,
648 uid_t uid, gid_t gid)
649 {
650 int ret = EOK;
651 struct copy_ctx *cctx = NULL;
652 struct stat s_src;
653
654 cctx = talloc_zero(NULL, struct copy_ctx);
655
656 ret = lstat(src_root, &s_src);
657 if (ret != 0) {
658 ret = errno;
659 DEBUG(1, ("Cannot lstat the source directory '%s': [%d][%s]\n",
660 src_root, ret, strerror(ret)));
661 goto fail;
662 }
663
664 cctx->src_orig = src_root;
665 cctx->dst_orig = dst_root;
666 cctx->src_dev = s_src.st_dev;
667
668 ret = copy_tree_ctx(cctx, src_root, dst_root, uid, gid);
669 if (ret != EOK) {
670 DEBUG(1, ("copy_tree_ctx failed: [%d][%s]\n", ret, strerror(ret)));
671 goto fail;
672 }
673
674 fail:
675 reset_selinux_file_context();
676 talloc_free(cctx);
677 return ret;
678 }
679