Branch data Line data Source code
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 : 1 : int remove_tree(const char *root)
85 : : {
86 : 1 : TALLOC_CTX *tmp_ctx = NULL;
87 : : int ret;
88 : :
89 : 1 : tmp_ctx = talloc_new(NULL);
90 [ + - ]: 1 : if (!tmp_ctx) {
91 : : return ENOMEM;
92 : : }
93 : :
94 : 1 : ret = remove_tree_with_ctx(tmp_ctx, 0, root);
95 : 1 : talloc_free(tmp_ctx);
96 : 1 : 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 : 3 : static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
105 : : dev_t parent_dev,
106 : : const char *root)
107 : : {
108 : 3 : char *fullpath = NULL;
109 : : struct dirent *result;
110 : : struct dirent direntp;
111 : : struct stat statres;
112 : 3 : DIR *rootdir = NULL;
113 : : int ret, err;
114 : :
115 : 3 : rootdir = opendir(root);
116 [ + - ]: 3 : if (rootdir == NULL) {
117 : 0 : ret = errno;
118 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot open directory %s [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
119 : : root, ret, strerror(ret)));
120 : : goto fail;
121 : : }
122 : :
123 [ + - ]: 13 : while (readdir_r(rootdir, &direntp, &result) == 0) {
124 [ + + ]: 13 : if (result == NULL) {
125 : : /* End of directory */
126 : : break;
127 : : }
128 : :
129 [ + + ][ + + ]: 17 : if (strcmp (direntp.d_name, ".") == 0 ||
[ + + ]
130 [ + + ][ + - ]: 7 : strcmp (direntp.d_name, "..") == 0) {
131 : 6 : continue;
132 : : }
133 : :
134 : 4 : fullpath = talloc_asprintf(mem_ctx, "%s/%s", root, direntp.d_name);
135 [ + - ]: 4 : if (fullpath == NULL) {
136 : : ret = ENOMEM;
137 : : goto fail;
138 : : }
139 : :
140 : 4 : ret = lstat(fullpath, &statres);
141 [ - + ]: 4 : if (ret != 0) {
142 : 0 : ret = errno;
143 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot stat %s: [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
144 : : fullpath, ret, strerror(ret)));
145 : : goto fail;
146 : : }
147 : :
148 [ + + ]: 4 : if (S_ISDIR(statres.st_mode)) {
149 : : /* if directory, recursively descend, but check if on the same FS */
150 [ + + ][ - + ]: 2 : if (parent_dev && parent_dev != statres.st_dev) {
151 [ # # ][ # # ]: 0 : DEBUG(1, ("Directory %s is on different filesystem, "
[ # # ][ # # ]
[ # # ]
152 : : "will not follow\n", fullpath));
153 : : ret = EFAULT;
154 : : goto fail;
155 : : }
156 : :
157 : 2 : ret = remove_tree_with_ctx(mem_ctx, statres.st_dev, fullpath);
158 [ - + ]: 2 : if (ret != EOK) {
159 [ # # ][ # # ]: 0 : DEBUG(1, ("Removing subdirectory %s failed: [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
160 : : fullpath, ret, strerror(ret)));
161 : : goto fail;
162 : : }
163 : : } else {
164 : 2 : ret = unlink(fullpath);
165 [ - + ]: 2 : if (ret != 0) {
166 : 0 : ret = errno;
167 [ # # ][ # # ]: 0 : DEBUG(1, ("Removing file %s failed: [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
168 : : fullpath, ret, strerror(ret)));
169 : : goto fail;
170 : : }
171 : : }
172 : :
173 : 10 : talloc_free(fullpath);
174 : : }
175 : :
176 : 3 : ret = closedir(rootdir);
177 : 3 : rootdir = NULL;
178 [ - + ]: 3 : if (ret != 0) {
179 : 0 : ret = errno;
180 : 0 : goto fail;
181 : : }
182 : :
183 : 3 : ret = rmdir(root);
184 [ - + ]: 3 : if (ret != 0) {
185 : 0 : ret = errno;
186 : 0 : goto fail;
187 : : }
188 : :
189 : : ret = EOK;
190 : :
191 : : fail:
192 [ - + ]: 3 : if (rootdir) { /* clean up on abnormal exit but retain return code */
193 : 0 : err = closedir(rootdir);
194 [ # # ]: 0 : if (err) {
195 [ # # ][ # # ]: 0 : DEBUG(1, ("closedir failed, bad dirp?\n"));
[ # # ][ # # ]
[ # # ]
196 : : }
197 : : }
198 : 3 : return ret;
199 : : }
200 : :
201 : 1 : static int copy_dir(const char *src, const char *dst,
202 : : const struct stat *statp, const struct timeval mt[2],
203 : : uid_t uid, gid_t gid)
204 : : {
205 : 1 : int ret = 0;
206 : :
207 : : /*
208 : : * Create a new target directory, make it owned by
209 : : * the user and then recursively copy that directory.
210 : : */
211 : 1 : selinux_file_context(dst);
212 : :
213 : 1 : ret = mkdir(dst, statp->st_mode);
214 [ - + ]: 1 : if (ret != 0) {
215 : 0 : ret = errno;
216 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot mkdir directory '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
217 : : dst, ret, strerror(ret)));
218 : : return ret;
219 : : }
220 : :
221 : 1 : ret = chown(dst, uid, gid);
222 [ - + ]: 1 : if (ret != 0) {
223 : 0 : ret = errno;
224 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot chown directory '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
225 : : dst, ret, strerror(ret)));
226 : : return ret;
227 : : }
228 : :
229 : 1 : ret = chmod(dst, statp->st_mode);
230 [ - + ]: 1 : if (ret != 0) {
231 : 0 : ret = errno;
232 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot chmod directory '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
233 : : dst, ret, strerror(ret)));
234 : : return ret;
235 : : }
236 : :
237 : 1 : ret = copy_tree(src, dst, uid, gid);
238 [ - + ]: 1 : if (ret != 0) {
239 : 0 : ret = errno;
240 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot copy directory from '%s' to '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
241 : : src, dst, ret, strerror(ret)));
242 : : return ret;
243 : : }
244 : :
245 : 1 : ret = utimes(dst, mt);
246 [ - + ]: 1 : if (ret != 0) {
247 : 0 : ret = errno;
248 [ # # ][ # # ]: 1 : DEBUG(1, ("Cannot set utimes on a directory '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
249 : : dst, ret, strerror(ret)));
250 : : return ret;
251 : : }
252 : :
253 : : return EOK;
254 : : }
255 : :
256 : 1 : static char *talloc_readlink(TALLOC_CTX *mem_ctx, const char *filename)
257 : : {
258 : 1 : size_t size = 1024;
259 : : ssize_t nchars;
260 : : char *buffer;
261 : :
262 : 1 : buffer = talloc_array(mem_ctx, char, size);
263 [ + - ]: 1 : if (!buffer) {
264 : : return NULL;
265 : : }
266 : :
267 : : while (1) {
268 : 1 : nchars = readlink(filename, buffer, size);
269 [ + - ]: 1 : if (nchars < 0) {
270 : : return NULL;
271 : : }
272 : :
273 [ - + ]: 1 : if ((size_t) nchars < size) {
274 : : /* The buffer was large enough */
275 : : break;
276 : : }
277 : :
278 : : /* Try again with a bigger buffer */
279 : 0 : size *= 2;
280 : 0 : buffer = talloc_realloc(mem_ctx, buffer, char, size);
281 [ # # ]: 0 : if (!buffer) {
282 : : return NULL;
283 : : }
284 : : }
285 : :
286 : : /* readlink does not nul-terminate */
287 : 1 : buffer[nchars] = '\0';
288 : 1 : return buffer;
289 : : }
290 : :
291 : 1 : static int copy_symlink(struct copy_ctx *cctx,
292 : : const char *src,
293 : : const char *dst,
294 : : const struct stat *statp,
295 : : const struct timeval mt[],
296 : : uid_t uid, gid_t gid)
297 : : {
298 : : int ret;
299 : : char *oldlink;
300 : : char *tmp;
301 : 1 : TALLOC_CTX *tmp_ctx = NULL;
302 : :
303 : 1 : tmp_ctx = talloc_new(cctx);
304 [ + - ]: 1 : if (!tmp_ctx) {
305 : : return ENOMEM;
306 : : }
307 : :
308 : : /*
309 : : * Get the name of the file which the link points
310 : : * to. If that name begins with the original
311 : : * source directory name, that part of the link
312 : : * name will be replaced with the original
313 : : * destination directory name.
314 : : */
315 : 1 : oldlink = talloc_readlink(tmp_ctx, src);
316 [ + - ]: 1 : if (oldlink == NULL) {
317 : : ret = ENOMEM;
318 : : goto done;
319 : : }
320 : :
321 : : /* If src was a link to an entry of the src_orig directory itself,
322 : : * create a link to the corresponding entry in the dst_orig
323 : : * directory.
324 : : * FIXME: This may change a relative link to an absolute link
325 : : */
326 [ - + ]: 1 : if (strncmp(oldlink, cctx->src_orig, strlen(cctx->src_orig)) == 0) {
327 : 0 : tmp = talloc_asprintf(tmp_ctx, "%s%s", cctx->dst_orig, oldlink + strlen(cctx->src_orig));
328 [ # # ]: 0 : if (tmp == NULL) {
329 : : ret = ENOMEM;
330 : : goto done;
331 : : }
332 : :
333 : 0 : talloc_free(oldlink);
334 : 0 : oldlink = tmp;
335 : : }
336 : :
337 : 1 : selinux_file_context(dst);
338 : :
339 : 1 : ret = symlink(oldlink, dst);
340 [ - + ]: 1 : if (ret != 0) {
341 : 0 : ret = errno;
342 [ # # ][ # # ]: 0 : DEBUG(1, ("symlink() failed on file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
343 : : dst, ret, strerror(ret)));
344 : : goto done;
345 : : }
346 : :
347 : 1 : ret = lchown(dst, uid, gid);
348 [ - + ]: 1 : if (ret != 0) {
349 : 0 : ret = errno;
350 [ # # ][ # # ]: 0 : DEBUG(1, ("lchown() failed on file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
351 : : dst, ret, strerror(ret)));
352 : : goto done;
353 : : }
354 : :
355 : : done:
356 : 1 : talloc_free(tmp_ctx);
357 : : return ret;
358 : : }
359 : :
360 : 1 : static int copy_special(const char *dst,
361 : : const struct stat *statp,
362 : : const struct timeval mt[],
363 : : uid_t uid, gid_t gid)
364 : : {
365 : 1 : int ret = 0;
366 : :
367 : 1 : selinux_file_context(dst);
368 : :
369 : 2 : ret = mknod(dst, statp->st_mode & ~07777, statp->st_rdev);
370 [ - + ]: 1 : if (ret != 0) {
371 : 0 : ret = errno;
372 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot mknod special file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
373 : : dst, ret, strerror(ret)));
374 : : return ret;
375 : : }
376 : :
377 : 1 : ret = chown(dst, uid, gid);
378 [ - + ]: 1 : if (ret != 0) {
379 : 0 : ret = errno;
380 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot chown special file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
381 : : dst, ret, strerror(ret)));
382 : : return ret;
383 : : }
384 : :
385 : 1 : ret = chmod(dst, statp->st_mode & 07777);
386 [ - + ]: 1 : if (ret != 0) {
387 : 0 : ret = errno;
388 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot chmod special file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
389 : : dst, ret, strerror(ret)));
390 : : return ret;
391 : : }
392 : :
393 : 1 : ret = utimes(dst, mt);
394 [ - + ]: 1 : if (ret != 0) {
395 : 0 : ret = errno;
396 [ # # ][ # # ]: 1 : DEBUG(1, ("Cannot call utimes on special file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
397 : : dst, ret, strerror(ret)));
398 : : return ret;
399 : : }
400 : :
401 : : return EOK;
402 : : }
403 : :
404 : 3 : static int copy_file(const char *src,
405 : : const char *dst,
406 : : const struct stat *statp,
407 : : const struct timeval mt[],
408 : : uid_t uid, gid_t gid)
409 : : {
410 : : int ret;
411 : 3 : int ifd = -1;
412 : 3 : int ofd = -1;
413 : : char buf[1024];
414 : : ssize_t cnt, written;
415 : : struct stat fstatbuf;
416 : :
417 : 3 : ifd = open(src, O_RDONLY);
418 [ - + ]: 3 : if (ifd < 0) {
419 : 0 : ret = errno;
420 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot open() source file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
421 : : src, ret, strerror(ret)));
422 : : goto fail;
423 : : }
424 : :
425 : 3 : ret = fstat(ifd, &fstatbuf);
426 [ - + ]: 3 : if (ret != 0) {
427 : 0 : ret = errno;
428 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot fstat() source file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
429 : : src, ret, strerror(ret)));
430 : : goto fail;
431 : : }
432 : :
433 [ + - ][ - + ]: 3 : if (statp->st_dev != fstatbuf.st_dev ||
434 : 3 : statp->st_ino != fstatbuf.st_ino) {
435 [ # # ][ # # ]: 0 : DEBUG(1, ("File %s was modified between lstat and open.\n", src));
[ # # ][ # # ]
[ # # ]
436 : : ret = EIO;
437 : : goto fail;
438 : : }
439 : :
440 : 3 : selinux_file_context(dst);
441 : :
442 : 3 : ofd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777);
443 [ - + ]: 3 : if (ofd < 0) {
444 : 0 : ret = errno;
445 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot open() destination file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
446 : : dst, ret, strerror(ret)));
447 : : goto fail;
448 : : }
449 : :
450 : 3 : ret = fchown(ofd, uid, gid);
451 [ - + ]: 3 : if (ret != 0) {
452 : 0 : ret = errno;
453 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot fchown() destination file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
454 : : dst, ret, strerror(ret)));
455 : : goto fail;
456 : : }
457 : :
458 : 3 : ret = fchmod(ofd, statp->st_mode & 07777);
459 [ - + ]: 3 : if (ret != 0) {
460 : 0 : ret = errno;
461 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot fchmod() destination file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
462 : : dst, ret, strerror(ret)));
463 : : goto fail;
464 : : }
465 : :
466 [ + + ]: 6 : while ((cnt = sss_atomic_read_s(ifd, buf, sizeof(buf))) != 0) {
467 [ - + ]: 3 : if (cnt == -1) {
468 : 0 : ret = errno;
469 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE,
[ # # ][ # # ]
[ # # ]
470 : : ("Cannot read() from source file '%s': [%d][%s].\n",
471 : : src, ret, strerror(ret)));
472 : : goto fail;
473 : : }
474 : :
475 : 3 : errno = 0;
476 : 3 : written = sss_atomic_write_s(ofd, buf, cnt);
477 [ - + ]: 3 : if (written == -1) {
478 : 0 : ret = errno;
479 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE,
[ # # ][ # # ]
[ # # ]
480 : : ("Cannot write() to destination file '%s': [%d][%s].\n",
481 : : dst, ret, strerror(ret)));
482 : : goto fail;
483 : : }
484 : :
485 [ - + ]: 3 : if (written != cnt) {
486 [ # # ][ # # ]: 3 : DEBUG(SSSDBG_CRIT_FAILURE,
[ # # ][ # # ]
[ # # ]
487 : : ("Wrote %d bytes, expected %d\n", written, cnt));
488 : : goto fail;
489 : : }
490 : : }
491 : :
492 : 3 : ret = close(ifd);
493 : 3 : ifd = -1;
494 [ - + ]: 3 : if (ret != 0) {
495 : 0 : ret = errno;
496 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot close() source file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
497 : : dst, ret, strerror(ret)));
498 : : goto fail;
499 : : }
500 : :
501 : 3 : ret = close(ofd);
502 : 3 : ifd = -1;
503 [ - + ]: 3 : if (ret != 0) {
504 : 0 : ret = errno;
505 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot close() destination file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
506 : : dst, ret, strerror(ret)));
507 : : goto fail;
508 : : }
509 : :
510 : 3 : ret = utimes(dst, mt);
511 [ - + ]: 3 : if (ret != 0) {
512 : 0 : ret = errno;
513 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot call utimes() on destination file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
514 : : dst, ret, strerror(ret)));
515 : : goto fail;
516 : : }
517 : :
518 : : return EOK;
519 : :
520 : : /* Reachable by jump only */
521 : : fail:
522 [ # # ]: 0 : if (ifd != -1) close(ifd);
523 [ # # ]: 3 : if (ofd != -1) close(ofd);
524 : : return ret;
525 : : }
526 : :
527 : : /*
528 : : * The context is not freed in case of error
529 : : * because this is a recursive function, will be freed when we
530 : : * reach the top level copy_tree() again
531 : : */
532 : 6 : static int copy_entry(struct copy_ctx *cctx,
533 : : const char *src,
534 : : const char *dst,
535 : : uid_t uid,
536 : : gid_t gid)
537 : : {
538 : 6 : int ret = EOK;
539 : : struct stat sb;
540 : : struct timeval mt[2];
541 : :
542 : 6 : ret = lstat(src, &sb);
543 [ - + ]: 6 : if (ret == -1) {
544 : 0 : ret = errno;
545 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot lstat() the source file '%s': [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
546 : : src, ret, strerror(ret)));
547 : : return ret;
548 : : }
549 : :
550 : 6 : mt[0].tv_sec = sb.st_atime;
551 : 6 : mt[0].tv_usec = 0;
552 : :
553 : 6 : mt[1].tv_sec = sb.st_mtime;
554 : 6 : mt[1].tv_usec = 0;
555 : :
556 [ + + ]: 6 : if (S_ISLNK (sb.st_mode)) {
557 : 1 : ret = copy_symlink(cctx, src, dst, &sb, mt, uid, gid);
558 [ - + ]: 1 : if (ret != EOK) {
559 [ # # ][ # # ]: 1 : DEBUG(1, ("Cannot copy symlink '%s' to '%s': [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
560 : : src, dst, ret, strerror(ret)));
561 : : }
562 : : return ret;
563 : : }
564 : :
565 [ + + ]: 5 : if (S_ISDIR(sb.st_mode)) {
566 : : /* Check if we're still on the same FS */
567 [ - + ]: 1 : if (sb.st_dev != cctx->src_dev) {
568 [ # # ][ # # ]: 0 : DEBUG(2, ("Will not descend to other FS\n"));
[ # # ][ # # ]
[ # # ]
569 : : /* Skip this without error */
570 : : return EOK;
571 : : }
572 : 1 : return copy_dir(src, dst, &sb, mt, uid, gid);
573 [ + + ]: 4 : } else if (!S_ISREG(sb.st_mode)) {
574 : : /*
575 : : * Deal with FIFOs and special files. The user really
576 : : * shouldn't have any of these, but it seems like it
577 : : * would be nice to copy everything ...
578 : : */
579 : 1 : return copy_special(dst, &sb, mt, uid, gid);
580 : : } else {
581 : : /*
582 : : * Create the new file and copy the contents. The new
583 : : * file will be owned by the provided UID and GID values.
584 : : */
585 : 6 : return copy_file(src, dst, &sb, mt, uid, gid);
586 : : }
587 : :
588 : : return ret;
589 : : }
590 : :
591 : : /*
592 : : * The context is not freed in case of error
593 : : * because this is a recursive function, will be freed when we
594 : : * reach the top level copy_tree() again
595 : : */
596 : 4 : static int copy_tree_ctx(struct copy_ctx *cctx,
597 : : const char *src_root,
598 : : const char *dst_root,
599 : : uid_t uid,
600 : : gid_t gid)
601 : : {
602 : 4 : DIR *src_dir = NULL;
603 : : int ret, err;
604 : : struct dirent *result;
605 : : struct dirent direntp;
606 : : char *src_name, *dst_name;
607 : : TALLOC_CTX *tmp_ctx;
608 : :
609 : 4 : tmp_ctx = talloc_new(cctx);
610 : :
611 : 4 : src_dir = opendir(src_root);
612 [ + - ]: 4 : if (src_dir == NULL) {
613 : 0 : ret = errno;
614 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot open the source directory %s: [%d][%s].\n",
[ # # ][ # # ]
[ # # ]
615 : : src_root, ret, strerror(ret)));
616 : : goto fail;
617 : : }
618 : :
619 [ + - ]: 18 : while (readdir_r(src_dir, &direntp, &result) == 0) {
620 [ + + ]: 18 : if (result == NULL) {
621 : : /* End of directory */
622 : : break;
623 : : }
624 : :
625 [ + + ][ + + ]: 24 : if (strcmp (direntp.d_name, ".") == 0 ||
[ + + ]
626 [ + + ][ + - ]: 10 : strcmp (direntp.d_name, "..") == 0) {
627 : 8 : continue;
628 : : }
629 : :
630 : : /* build src and dst paths */
631 : 6 : src_name = talloc_asprintf(tmp_ctx, "%s/%s", src_root, direntp.d_name);
632 : 6 : dst_name = talloc_asprintf(tmp_ctx, "%s/%s", dst_root, direntp.d_name);
633 [ + - ]: 6 : if (dst_name == NULL || src_name == NULL) {
634 : : ret = ENOMEM;
635 : : goto fail;
636 : : }
637 : :
638 : : /* copy */
639 : 6 : ret = copy_entry(cctx, src_name, dst_name, uid, gid);
640 [ - + ]: 6 : if (ret != EOK) {
641 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot copy '%s' to '%s', error %d\n",
[ # # ][ # # ]
[ # # ]
642 : : src_name, dst_name, ret));
643 : : goto fail;
644 : : }
645 : 6 : talloc_free(src_name);
646 : 14 : talloc_free(dst_name);
647 : : }
648 : :
649 : 4 : ret = closedir(src_dir);
650 : 4 : src_dir = NULL;
651 [ - + ]: 4 : if (ret != 0) {
652 : 0 : ret = errno;
653 : 0 : goto fail;
654 : : }
655 : :
656 : : ret = EOK;
657 : : fail:
658 [ - + ]: 4 : if (src_dir) { /* clean up on abnormal exit but retain return code */
659 : 0 : err = closedir(src_dir);
660 [ # # ]: 0 : if (err) {
661 [ # # ][ # # ]: 0 : DEBUG(1, ("closedir failed, bad dirp?\n"));
[ # # ][ # # ]
[ # # ]
662 : : }
663 : : }
664 : 4 : talloc_free(tmp_ctx);
665 : 4 : return ret;
666 : : }
667 : :
668 : 4 : int copy_tree(const char *src_root, const char *dst_root,
669 : : uid_t uid, gid_t gid)
670 : : {
671 : 4 : int ret = EOK;
672 : 4 : struct copy_ctx *cctx = NULL;
673 : : struct stat s_src;
674 : :
675 : 4 : cctx = talloc_zero(NULL, struct copy_ctx);
676 : :
677 : 4 : ret = lstat(src_root, &s_src);
678 [ - + ]: 4 : if (ret != 0) {
679 : 0 : ret = errno;
680 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot lstat the source directory '%s': [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
681 : : src_root, ret, strerror(ret)));
682 : : goto fail;
683 : : }
684 : :
685 : 4 : cctx->src_orig = src_root;
686 : 4 : cctx->dst_orig = dst_root;
687 : 4 : cctx->src_dev = s_src.st_dev;
688 : :
689 : 4 : ret = copy_tree_ctx(cctx, src_root, dst_root, uid, gid);
690 [ - + ]: 4 : if (ret != EOK) {
691 [ # # ][ # # ]: 0 : DEBUG(1, ("copy_tree_ctx failed: [%d][%s]\n", ret, strerror(ret)));
[ # # ][ # # ]
[ # # ]
692 : : goto fail;
693 : : }
694 : :
695 : : fail:
696 : 4 : reset_selinux_file_context();
697 : 4 : talloc_free(cctx);
698 : 4 : return ret;
699 : : }
700 : :
|