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 "config.h"
58 :
59 : #include <sys/stat.h>
60 : #include <sys/types.h>
61 : #include <sys/time.h>
62 : #include <dirent.h>
63 : #include <fcntl.h>
64 : #include <errno.h>
65 : #include <talloc.h>
66 :
67 : #include "util/util.h"
68 : #include "tools/tools_util.h"
69 :
70 : struct copy_ctx {
71 : const char *src_orig;
72 : const char *dst_orig;
73 : dev_t src_dev;
74 : uid_t uid;
75 : gid_t gid;
76 : };
77 :
78 1 : static int sss_timeat_set(int dir_fd, const char *path,
79 : const struct stat *statp,
80 : int flags)
81 : {
82 : int ret;
83 :
84 : #ifdef HAVE_UTIMENSAT
85 : struct timespec timebuf[2];
86 :
87 1 : timebuf[0] = statp->st_atim;
88 1 : timebuf[1] = statp->st_mtim;
89 :
90 1 : ret = utimensat(dir_fd, path, timebuf, flags);
91 : #else
92 : struct timeval tv[2];
93 :
94 : tv[0].tv_sec = statp->st_atime;
95 : tv[0].tv_usec = 0;
96 : tv[1].tv_sec = statp->st_mtime;
97 : tv[1].tv_usec = 0;
98 :
99 : ret = futimesat(dir_fd, path, tv);
100 : #endif
101 1 : if (ret == -1) {
102 0 : return errno;
103 : }
104 :
105 1 : return EOK;
106 : }
107 :
108 7 : static int sss_futime_set(int fd, const struct stat *statp)
109 : {
110 : int ret;
111 :
112 : #ifdef HAVE_FUTIMENS
113 : struct timespec timebuf[2];
114 :
115 7 : timebuf[0] = statp->st_atim;
116 7 : timebuf[1] = statp->st_mtim;
117 7 : ret = futimens(fd, timebuf);
118 : #else
119 : struct timeval tv[2];
120 :
121 : tv[0].tv_sec = statp->st_atime;
122 : tv[0].tv_usec = 0;
123 : tv[1].tv_sec = statp->st_mtime;
124 : tv[1].tv_usec = 0;
125 :
126 : ret = futimes(fd, tv);
127 : #endif
128 7 : if (ret == -1) {
129 0 : return errno;
130 : }
131 :
132 7 : return EOK;
133 : }
134 :
135 : /* wrapper in order not to create a temporary context in
136 : * every iteration */
137 : static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
138 : int parent_fd,
139 : const char *dir_name,
140 : dev_t parent_dev);
141 :
142 1 : int remove_tree(const char *root)
143 : {
144 1 : TALLOC_CTX *tmp_ctx = NULL;
145 : int ret;
146 :
147 1 : tmp_ctx = talloc_new(NULL);
148 1 : if (!tmp_ctx) {
149 0 : return ENOMEM;
150 : }
151 :
152 1 : ret = remove_tree_with_ctx(tmp_ctx, AT_FDCWD, root, 0);
153 1 : talloc_free(tmp_ctx);
154 1 : return ret;
155 : }
156 :
157 : /*
158 : * The context is not freed in case of error
159 : * because this is a recursive function, will be freed when we
160 : * reach the top level remove_tree() again
161 : */
162 3 : static int remove_tree_with_ctx(TALLOC_CTX *mem_ctx,
163 : int parent_fd,
164 : const char *dir_name,
165 : dev_t parent_dev)
166 : {
167 : struct dirent *result;
168 : struct stat statres;
169 3 : DIR *rootdir = NULL;
170 : int ret, err;
171 : int dir_fd;
172 :
173 3 : dir_fd = sss_openat_cloexec(parent_fd, dir_name,
174 : O_RDONLY | O_DIRECTORY | O_NOFOLLOW, &ret);
175 3 : if (dir_fd == -1) {
176 0 : ret = errno;
177 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot open %s: [%d]: %s\n",
178 : dir_name, ret, strerror(ret));
179 0 : return ret;
180 : }
181 :
182 3 : rootdir = fdopendir(dir_fd);
183 3 : if (rootdir == NULL) {
184 0 : ret = errno;
185 0 : DEBUG(SSSDBG_CRIT_FAILURE,
186 : "Cannot open directory: [%d][%s]\n", ret, strerror(ret));
187 0 : close(dir_fd);
188 0 : goto fail;
189 : }
190 :
191 16 : while ((result = readdir(rootdir)) != NULL) {
192 17 : if (strcmp(result->d_name, ".") == 0 ||
193 7 : strcmp(result->d_name, "..") == 0) {
194 6 : continue;
195 : }
196 :
197 4 : ret = fstatat(dir_fd, result->d_name,
198 : &statres, AT_SYMLINK_NOFOLLOW);
199 4 : if (ret != 0) {
200 0 : ret = errno;
201 0 : DEBUG(SSSDBG_CRIT_FAILURE,
202 : "stat failed: [%d][%s]\n", ret, strerror(ret));
203 0 : goto fail;
204 : }
205 :
206 4 : if (S_ISDIR(statres.st_mode)) {
207 : /* if directory, recursively descend, but check if on the same FS */
208 2 : if (parent_dev && parent_dev != statres.st_dev) {
209 0 : DEBUG(SSSDBG_CRIT_FAILURE,
210 : "Directory %s is on different filesystem, "
211 : "will not follow\n", result->d_name);
212 0 : ret = EFAULT;
213 0 : goto fail;
214 : }
215 :
216 2 : ret = remove_tree_with_ctx(mem_ctx, dir_fd, result->d_name, statres.st_dev);
217 2 : if (ret != EOK) {
218 0 : DEBUG(SSSDBG_CRIT_FAILURE,
219 : "Removing subdirectory failed: [%d][%s]\n",
220 : ret, strerror(ret));
221 0 : goto fail;
222 : }
223 : } else {
224 2 : ret = unlinkat(dir_fd, result->d_name, 0);
225 2 : if (ret != 0) {
226 0 : ret = errno;
227 0 : DEBUG(SSSDBG_CRIT_FAILURE,
228 : "Removing file failed: [%d][%s]\n", ret, strerror(ret));
229 0 : goto fail;
230 : }
231 : }
232 : }
233 :
234 3 : ret = closedir(rootdir);
235 3 : rootdir = NULL;
236 3 : if (ret != 0) {
237 0 : ret = errno;
238 0 : goto fail;
239 : }
240 :
241 3 : ret = unlinkat(parent_fd, dir_name, AT_REMOVEDIR);
242 3 : if (ret == -1) {
243 0 : ret = errno;
244 : }
245 :
246 3 : ret = EOK;
247 : fail:
248 3 : if (rootdir) { /* clean up on abnormal exit but retain return code */
249 0 : err = closedir(rootdir);
250 0 : if (err) {
251 0 : DEBUG(SSSDBG_CRIT_FAILURE, "closedir failed, bad dirp?\n");
252 : }
253 : }
254 3 : return ret;
255 : }
256 :
257 1 : static char *talloc_readlinkat(TALLOC_CTX *mem_ctx, int dir_fd,
258 : const char *filename)
259 : {
260 1 : size_t size = 1024;
261 : ssize_t nchars;
262 : char *buffer;
263 : char *new_buffer;
264 :
265 1 : buffer = talloc_array(mem_ctx, char, size);
266 1 : if (!buffer) {
267 0 : return NULL;
268 : }
269 :
270 : while (1) {
271 1 : nchars = readlinkat(dir_fd, filename, buffer, size);
272 1 : if (nchars < 0) {
273 0 : talloc_free(buffer);
274 0 : return NULL;
275 : }
276 :
277 1 : if ((size_t) nchars < size) {
278 : /* The buffer was large enough */
279 1 : break;
280 : }
281 :
282 : /* Try again with a bigger buffer */
283 0 : size *= 2;
284 0 : new_buffer = talloc_realloc(mem_ctx, buffer, char, size);
285 0 : if (!new_buffer) {
286 0 : talloc_free(buffer);
287 0 : return NULL;
288 : }
289 0 : buffer = new_buffer;
290 0 : }
291 :
292 : /* readlink does not nul-terminate */
293 1 : buffer[nchars] = '\0';
294 1 : return buffer;
295 : }
296 :
297 : static int
298 1 : copy_symlink(int src_dir_fd,
299 : int dst_dir_fd,
300 : const char *file_name,
301 : const char *full_path,
302 : const struct stat *statp,
303 : uid_t uid, gid_t gid)
304 : {
305 : char *buf;
306 : errno_t ret;
307 :
308 1 : buf = talloc_readlinkat(NULL, src_dir_fd, file_name);
309 1 : if (!buf) {
310 0 : return ENOMEM;
311 : }
312 :
313 1 : ret = selinux_file_context(full_path);
314 1 : if (ret != 0) {
315 1 : DEBUG(SSSDBG_MINOR_FAILURE,
316 : "Failed to set SELinux context for [%s]\n", full_path);
317 : /* Not fatal */
318 : }
319 :
320 1 : ret = symlinkat(buf, dst_dir_fd, file_name);
321 1 : talloc_free(buf);
322 1 : if (ret == -1) {
323 0 : ret = errno;
324 0 : if (ret == EEXIST) {
325 0 : DEBUG(SSSDBG_MINOR_FAILURE,
326 : "symlink pointing to already exists at '%s'\n", full_path);
327 0 : return EOK;
328 : }
329 :
330 0 : DEBUG(SSSDBG_CRIT_FAILURE, "symlinkat failed: %s\n", strerror(ret));
331 0 : return ret;
332 : }
333 :
334 1 : ret = fchownat(dst_dir_fd, file_name,
335 : uid, gid, AT_SYMLINK_NOFOLLOW);
336 1 : if (ret == -1) {
337 0 : ret = errno;
338 0 : DEBUG(SSSDBG_CRIT_FAILURE,
339 : "fchownat failed: %s\n", strerror(ret));
340 0 : return ret;
341 : }
342 :
343 1 : ret = sss_timeat_set(dst_dir_fd, file_name, statp,
344 : AT_SYMLINK_NOFOLLOW);
345 1 : if (ret != EOK) {
346 0 : DEBUG(SSSDBG_MINOR_FAILURE, "utimensat failed [%d]: %s\n",
347 : ret, strerror(ret));
348 : /* Do not fail */
349 : }
350 :
351 1 : return EOK;
352 : }
353 :
354 : static int
355 4 : copy_file_contents(int ifd,
356 : int ofd,
357 : mode_t mode,
358 : uid_t uid, gid_t gid)
359 : {
360 : errno_t ret;
361 : char buf[1024];
362 : ssize_t cnt, written;
363 :
364 12 : while ((cnt = sss_atomic_read_s(ifd, buf, sizeof(buf))) != 0) {
365 4 : if (cnt == -1) {
366 0 : ret = errno;
367 0 : DEBUG(SSSDBG_CRIT_FAILURE,
368 : "Cannot read() from source file: [%d][%s].\n",
369 : ret, strerror(ret));
370 0 : goto done;
371 : }
372 :
373 4 : errno = 0;
374 4 : written = sss_atomic_write_s(ofd, buf, cnt);
375 4 : if (written == -1) {
376 0 : ret = errno;
377 0 : DEBUG(SSSDBG_CRIT_FAILURE,
378 : "Cannot write() to destination file: [%d][%s].\n",
379 : ret, strerror(ret));
380 0 : goto done;
381 : }
382 :
383 4 : if (written != cnt) {
384 0 : ret = EINVAL;
385 0 : DEBUG(SSSDBG_CRIT_FAILURE,
386 : "Wrote %zd bytes, expected %zd\n", written, cnt);
387 0 : goto done;
388 : }
389 : }
390 :
391 : /* Set the ownership; permissions are still
392 : * restrictive. */
393 4 : ret = fchown(ofd, uid, gid);
394 4 : if (ret == -1 && errno != EPERM) {
395 0 : ret = errno;
396 0 : DEBUG(SSSDBG_OP_FAILURE,
397 : "Error changing owner: %s\n",
398 : strerror(ret));
399 0 : goto done;
400 : }
401 :
402 : /* Set the desired mode. */
403 4 : ret = fchmod(ofd, mode);
404 4 : if (ret == -1) {
405 0 : ret = errno;
406 0 : DEBUG(SSSDBG_OP_FAILURE, "Error changing mode: %s\n",
407 : strerror(ret));
408 0 : goto done;
409 : }
410 :
411 4 : ret = EOK;
412 :
413 : done:
414 4 : return ret;
415 : }
416 :
417 :
418 : /* Copy bytes from input file descriptor ifd into file named
419 : * dst_named under directory with dest_dir_fd. Own the new file
420 : * by uid/gid
421 : */
422 : static int
423 3 : copy_file(int ifd,
424 : int dest_dir_fd,
425 : const char *file_name,
426 : const char *full_path,
427 : const struct stat *statp,
428 : uid_t uid, gid_t gid)
429 : {
430 3 : int ofd = -1;
431 : errno_t ret;
432 :
433 3 : ret = selinux_file_context(full_path);
434 3 : if (ret != 0) {
435 3 : DEBUG(SSSDBG_MINOR_FAILURE,
436 : "Failed to set SELinux context for [%s]\n", full_path);
437 : /* Not fatal */
438 : }
439 :
440 : /* Start with absolutely restrictive permissions */
441 3 : ofd = openat(dest_dir_fd, file_name,
442 : O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW,
443 : 0);
444 3 : if (ofd < 0 && errno != EEXIST) {
445 0 : ret = errno;
446 0 : DEBUG(SSSDBG_OP_FAILURE,
447 : "Cannot open() destination file '%s': [%d][%s].\n",
448 : full_path, ret, strerror(ret));
449 0 : goto done;
450 : }
451 :
452 3 : ret = copy_file_contents(ifd, ofd, statp->st_mode, uid, gid);
453 3 : if (ret != EOK) goto done;
454 :
455 :
456 3 : ret = sss_futime_set(ofd, statp);
457 3 : if (ret != EOK) {
458 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_futime_set failed [%d]: %s\n",
459 : ret, strerror(ret));
460 : /* Do not fail */
461 : }
462 3 : ret = EOK;
463 :
464 : done:
465 3 : if (ofd != -1) close(ofd);
466 3 : return ret;
467 : }
468 :
469 : int
470 1 : copy_file_secure(const char *src,
471 : const char *dest,
472 : mode_t mode,
473 : uid_t uid, gid_t gid,
474 : bool force)
475 : {
476 1 : int ifd = -1;
477 1 : int ofd = -1;
478 1 : int dest_flags = 0;
479 : errno_t ret;
480 :
481 1 : ret = selinux_file_context(dest);
482 1 : if (ret != 0) {
483 1 : DEBUG(SSSDBG_MINOR_FAILURE,
484 : "Failed to set SELinux context for [%s]\n", dest);
485 : /* Not fatal */
486 : }
487 :
488 : /* Start with absolutely restrictive permissions */
489 1 : dest_flags = O_CREAT | O_WRONLY | O_NOFOLLOW;
490 1 : if (!force) {
491 1 : dest_flags |= O_EXCL;
492 : }
493 :
494 1 : ofd = open(dest, dest_flags, mode);
495 1 : if (ofd < 0) {
496 0 : DEBUG(SSSDBG_OP_FAILURE,
497 : "Cannot open() destination file '%s': [%d][%s].\n",
498 : dest, errno, strerror(errno));
499 0 : goto done;
500 : }
501 :
502 1 : ifd = sss_open_cloexec(src, O_RDONLY | O_NOFOLLOW, &ret);
503 1 : if (ifd < 0) {
504 0 : DEBUG(SSSDBG_OP_FAILURE,
505 : "Cannot open() source file '%s': [%d][%s].\n",
506 : src, ret, strerror(ret));
507 0 : goto done;
508 : }
509 :
510 1 : ret = copy_file_contents(ifd, ofd, mode, uid, gid);
511 :
512 : done:
513 1 : if (ifd != -1) close(ifd);
514 1 : if (ofd != -1) close(ofd);
515 1 : return ret;
516 : }
517 :
518 : static errno_t
519 : copy_dir(struct copy_ctx *cctx,
520 : int src_dir_fd, const char *src_dir_path,
521 : int dest_parent_fd, const char *dest_dir_name,
522 : const char *dest_dir_path,
523 : mode_t mode,
524 : const struct stat *src_dir_stat);
525 :
526 : static errno_t
527 6 : copy_entry(struct copy_ctx *cctx,
528 : int src_dir_fd,
529 : const char *src_dir_path,
530 : int dest_dir_fd,
531 : const char *dest_dir_path,
532 : const char *ent_name)
533 : {
534 6 : char *src_ent_path = NULL;
535 6 : char *dest_ent_path = NULL;
536 6 : int ifd = -1;
537 : errno_t ret;
538 : struct stat st;
539 :
540 : /* Build the path of the source file or directory and its
541 : * corresponding member in the new tree. */
542 6 : src_ent_path = talloc_asprintf(cctx, "%s/%s", src_dir_path, ent_name);
543 6 : dest_ent_path = talloc_asprintf(cctx, "%s/%s", dest_dir_path, ent_name);
544 6 : if (!src_ent_path || !dest_ent_path) {
545 0 : ret = ENOMEM;
546 0 : goto done;
547 : }
548 :
549 : /* Open the input entry first, then we can fstat() it and be
550 : * certain that it is still the same file. O_NONBLOCK protects
551 : * us against FIFOs and perhaps side-effects of the open() of a
552 : * device file if there ever was one here, and doesn't matter
553 : * for regular files or directories. */
554 6 : ifd = sss_openat_cloexec(src_dir_fd, ent_name,
555 : O_RDONLY | O_NOFOLLOW | O_NONBLOCK, &ret);
556 6 : if (ifd == -1 && ret != ELOOP) {
557 : /* openat error */
558 0 : DEBUG(SSSDBG_CRIT_FAILURE, "openat failed on '%s': %s\n",
559 : src_ent_path, strerror(ret));
560 0 : goto done;
561 6 : } else if (ifd == -1 && ret == ELOOP) {
562 : /* Should be a symlink.. */
563 1 : ret = fstatat(src_dir_fd, ent_name, &st, AT_SYMLINK_NOFOLLOW);
564 1 : if (ret == -1) {
565 0 : ret = errno;
566 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fstatat failed on '%s': %s\n",
567 : src_ent_path, strerror(ret));
568 0 : goto done;
569 : }
570 :
571 : /* Handle symlinks */
572 1 : ret = copy_symlink(src_dir_fd, dest_dir_fd, ent_name,
573 : dest_ent_path, &st, cctx->uid, cctx->gid);
574 1 : if (ret != EOK) {
575 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot copy '%s' to '%s'\n",
576 : src_ent_path, dest_ent_path);
577 : }
578 1 : goto done;
579 : }
580 :
581 5 : ret = fstat(ifd, &st);
582 5 : if (ret != 0) {
583 0 : ret = errno;
584 0 : DEBUG(SSSDBG_CRIT_FAILURE,
585 : "couldn't stat '%s': %s\n", src_ent_path, strerror(ret));
586 0 : goto done;
587 : }
588 :
589 5 : if (S_ISDIR(st.st_mode)) {
590 : /* If it's a directory, descend into it. */
591 1 : ret = copy_dir(cctx, ifd, src_ent_path,
592 : dest_dir_fd, ent_name,
593 1 : dest_ent_path, st.st_mode & 07777,
594 : &st);
595 1 : if (ret != EOK) {
596 0 : DEBUG(SSSDBG_OP_FAILURE,
597 : "Couldn't recursively copy '%s' to '%s': %s\n",
598 : src_ent_path, dest_ent_path, strerror(ret));
599 0 : goto done;
600 : }
601 4 : } else if (S_ISREG(st.st_mode)) {
602 : /* Copy a regular file */
603 3 : ret = copy_file(ifd, dest_dir_fd, ent_name, dest_ent_path,
604 : &st, cctx->uid, cctx->gid);
605 3 : if (ret) {
606 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot copy '%s' to '%s'\n",
607 : src_ent_path, dest_ent_path);
608 0 : goto done;
609 : }
610 : } else {
611 : /* Is a special file */
612 1 : DEBUG(SSSDBG_FUNC_DATA, "'%s' is a special file, skipping.\n",
613 : src_ent_path);
614 : }
615 :
616 5 : ret = EOK;
617 : done:
618 6 : talloc_free(src_ent_path);
619 6 : talloc_free(dest_ent_path);
620 6 : if (ifd != -1) close(ifd);
621 6 : return ret;
622 : }
623 :
624 : static errno_t
625 4 : copy_dir(struct copy_ctx *cctx,
626 : int src_dir_fd, const char *src_dir_path,
627 : int dest_parent_fd, const char *dest_dir_name,
628 : const char *dest_dir_path,
629 : mode_t mode,
630 : const struct stat *src_dir_stat)
631 : {
632 : errno_t ret;
633 : errno_t dret;
634 4 : int dest_dir_fd = -1;
635 4 : DIR *dir = NULL;
636 : struct dirent *ent;
637 :
638 4 : if (!dest_dir_path) {
639 0 : return EINVAL;
640 : }
641 :
642 4 : dir = fdopendir(src_dir_fd);
643 4 : if (dir == NULL) {
644 0 : ret = errno;
645 0 : DEBUG(SSSDBG_CRIT_FAILURE,
646 : "Error reading '%s': %s\n", src_dir_path, strerror(ret));
647 0 : goto done;
648 : }
649 :
650 : /* Create the directory. It starts owned by us (presumbaly root), with
651 : * fairly restrictive permissions that still allow us to use the
652 : * directory.
653 : * */
654 4 : errno = 0;
655 4 : ret = mkdirat(dest_parent_fd, dest_dir_name, S_IRWXU);
656 4 : if (ret == -1 && errno != EEXIST) {
657 0 : ret = errno;
658 0 : DEBUG(SSSDBG_CRIT_FAILURE,
659 : "Error reading '%s': %s\n", dest_dir_path, strerror(ret));
660 0 : goto done;
661 : }
662 :
663 4 : dest_dir_fd = sss_openat_cloexec(dest_parent_fd, dest_dir_name,
664 : O_RDONLY | O_DIRECTORY | O_NOFOLLOW, &ret);
665 4 : if (dest_dir_fd == -1) {
666 0 : ret = errno;
667 0 : DEBUG(SSSDBG_CRIT_FAILURE,
668 : "Error opening '%s': %s\n", dest_dir_path, strerror(ret));
669 0 : goto done;
670 : }
671 :
672 22 : while ((ent = readdir(dir)) != NULL) {
673 : /* Iterate through each item in the directory. */
674 : /* Skip over self and parent hard links. */
675 24 : if (strcmp(ent->d_name, ".") == 0 ||
676 10 : strcmp(ent->d_name, "..") == 0) {
677 8 : continue;
678 : }
679 :
680 6 : ret = copy_entry(cctx,
681 : src_dir_fd, src_dir_path,
682 : dest_dir_fd, dest_dir_path,
683 6 : ent->d_name);
684 6 : if (ret != EOK) {
685 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not copy [%s] to [%s]\n",
686 : src_dir_path, dest_dir_path);
687 0 : goto done;
688 : }
689 : }
690 :
691 : /* Set the ownership on the directory. Permissions are still
692 : * fairly restrictive. */
693 4 : ret = fchown(dest_dir_fd, cctx->uid, cctx->gid);
694 4 : if (ret == -1 && errno != EPERM) {
695 0 : ret = errno;
696 0 : DEBUG(SSSDBG_OP_FAILURE,
697 : "Error changing owner of '%s': %s\n",
698 : dest_dir_path, strerror(ret));
699 0 : goto done;
700 : }
701 :
702 : /* Set the desired mode. Do this explicitly to preserve S_ISGID and
703 : * other bits. Do this after chown, because chown is permitted to
704 : * reset these bits. */
705 4 : ret = fchmod(dest_dir_fd, mode);
706 4 : if (ret == -1) {
707 0 : ret = errno;
708 0 : DEBUG(SSSDBG_OP_FAILURE,
709 : "Error setting mode of '%s': %s\n",
710 : dest_dir_path, strerror(ret));
711 0 : goto done;
712 : }
713 :
714 4 : sss_futime_set(dest_dir_fd, src_dir_stat);
715 4 : if (ret != EOK) {
716 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_futime_set failed [%d]: %s\n",
717 : ret, strerror(ret));
718 : /* Do not fail */
719 : }
720 :
721 4 : ret = EOK;
722 : done:
723 4 : if (dir) {
724 4 : dret = closedir(dir);
725 4 : if (dret != 0) {
726 0 : dret = errno;
727 0 : DEBUG(SSSDBG_MINOR_FAILURE,
728 : "Failed to close directory: %s.\n", strerror(dret));
729 : }
730 : }
731 :
732 4 : if (dest_dir_fd != -1) {
733 4 : close(dest_dir_fd);
734 : }
735 4 : return ret;
736 : }
737 :
738 : /* NOTE:
739 : * For several reasons, including the fact that we copy even special files
740 : * (pipes, etc) from the skeleton directory, the skeldir needs to be trusted
741 : */
742 3 : int copy_tree(const char *src_root, const char *dst_root,
743 : mode_t mode_root, uid_t uid, gid_t gid)
744 : {
745 3 : int ret = EOK;
746 3 : struct copy_ctx *cctx = NULL;
747 3 : int fd = -1;
748 : struct stat s_src;
749 :
750 3 : fd = sss_open_cloexec(src_root, O_RDONLY | O_DIRECTORY, &ret);
751 3 : if (fd == -1) {
752 0 : goto fail;
753 : }
754 :
755 3 : ret = fstat(fd, &s_src);
756 3 : if (ret == -1) {
757 0 : ret = errno;
758 0 : goto fail;
759 : }
760 :
761 3 : cctx = talloc_zero(NULL, struct copy_ctx);
762 3 : if (!cctx) {
763 0 : ret = ENOMEM;
764 0 : goto fail;
765 : }
766 :
767 3 : cctx->src_orig = src_root;
768 3 : cctx->dst_orig = dst_root;
769 3 : cctx->src_dev = s_src.st_dev;
770 3 : cctx->uid = uid;
771 3 : cctx->gid = gid;
772 :
773 3 : ret = copy_dir(cctx, fd, src_root, AT_FDCWD,
774 : dst_root, dst_root, mode_root, &s_src);
775 3 : if (ret != EOK) {
776 0 : DEBUG(SSSDBG_CRIT_FAILURE,
777 : "copy_dir failed: [%d][%s]\n", ret, strerror(ret));
778 0 : goto fail;
779 : }
780 :
781 : fail:
782 3 : if (fd != -1) close(fd);
783 3 : reset_selinux_file_context();
784 3 : talloc_free(cctx);
785 3 : return ret;
786 : }
787 :
|