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 : /* Copy bytes from input file descriptor ifd into file named
355 : * dst_named under directory with dest_dir_fd. Own the new file
356 : * by uid/gid
357 : */
358 : static int
359 3 : copy_file(int ifd,
360 : int dest_dir_fd,
361 : const char *file_name,
362 : const char *full_path,
363 : const struct stat *statp,
364 : uid_t uid, gid_t gid)
365 : {
366 3 : int ofd = -1;
367 : errno_t ret;
368 : char buf[1024];
369 : ssize_t cnt, written;
370 :
371 3 : ret = selinux_file_context(full_path);
372 3 : if (ret != 0) {
373 3 : DEBUG(SSSDBG_MINOR_FAILURE,
374 : "Failed to set SELinux context for [%s]\n", full_path);
375 : /* Not fatal */
376 : }
377 :
378 : /* Start with absolutely restrictive permissions */
379 3 : ofd = openat(dest_dir_fd, file_name,
380 : O_EXCL | O_CREAT | O_WRONLY | O_NOFOLLOW,
381 : 0);
382 3 : if (ofd < 0 && errno != EEXIST) {
383 0 : ret = errno;
384 0 : DEBUG(SSSDBG_OP_FAILURE,
385 : "Cannot open() destination file '%s': [%d][%s].\n",
386 : full_path, ret, strerror(ret));
387 0 : goto done;
388 : }
389 :
390 9 : while ((cnt = sss_atomic_read_s(ifd, buf, sizeof(buf))) != 0) {
391 3 : if (cnt == -1) {
392 0 : ret = errno;
393 0 : DEBUG(SSSDBG_CRIT_FAILURE,
394 : "Cannot read() from source file: [%d][%s].\n",
395 : ret, strerror(ret));
396 0 : goto done;
397 : }
398 :
399 3 : errno = 0;
400 3 : written = sss_atomic_write_s(ofd, buf, cnt);
401 3 : if (written == -1) {
402 0 : ret = errno;
403 0 : DEBUG(SSSDBG_CRIT_FAILURE,
404 : "Cannot write() to destination file: [%d][%s].\n",
405 : ret, strerror(ret));
406 0 : goto done;
407 : }
408 :
409 3 : if (written != cnt) {
410 0 : DEBUG(SSSDBG_CRIT_FAILURE,
411 : "Wrote %zd bytes, expected %zd\n", written, cnt);
412 0 : goto done;
413 : }
414 : }
415 :
416 : /* Set the ownership; permissions are still
417 : * restrictive. */
418 3 : ret = fchown(ofd, uid, gid);
419 3 : if (ret == -1 && errno != EPERM) {
420 0 : ret = errno;
421 0 : DEBUG(SSSDBG_OP_FAILURE,
422 : "Error changing owner of '%s': %s\n",
423 : full_path, strerror(ret));
424 0 : goto done;
425 : }
426 :
427 : /* Set the desired mode. */
428 3 : ret = fchmod(ofd, statp->st_mode);
429 3 : if (ret == -1) {
430 0 : ret = errno;
431 0 : DEBUG(SSSDBG_OP_FAILURE, "Error changing owner of '%s': %s\n",
432 : full_path, strerror(ret));
433 0 : goto done;
434 : }
435 :
436 3 : ret = sss_futime_set(ofd, statp);
437 3 : if (ret != EOK) {
438 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_futime_set failed [%d]: %s\n",
439 : ret, strerror(ret));
440 : /* Do not fail */
441 : }
442 :
443 3 : close(ofd);
444 3 : ofd = -1;
445 3 : ret = EOK;
446 :
447 : done:
448 3 : if (ofd != -1) close(ofd);
449 3 : return ret;
450 : }
451 :
452 : static errno_t
453 : copy_dir(struct copy_ctx *cctx,
454 : int src_dir_fd, const char *src_dir_path,
455 : int dest_parent_fd, const char *dest_dir_name,
456 : const char *dest_dir_path,
457 : mode_t mode,
458 : const struct stat *src_dir_stat);
459 :
460 : static errno_t
461 6 : copy_entry(struct copy_ctx *cctx,
462 : int src_dir_fd,
463 : const char *src_dir_path,
464 : int dest_dir_fd,
465 : const char *dest_dir_path,
466 : const char *ent_name)
467 : {
468 6 : char *src_ent_path = NULL;
469 6 : char *dest_ent_path = NULL;
470 6 : int ifd = -1;
471 : errno_t ret;
472 : struct stat st;
473 :
474 : /* Build the path of the source file or directory and its
475 : * corresponding member in the new tree. */
476 6 : src_ent_path = talloc_asprintf(cctx, "%s/%s", src_dir_path, ent_name);
477 6 : dest_ent_path = talloc_asprintf(cctx, "%s/%s", dest_dir_path, ent_name);
478 6 : if (!src_ent_path || !dest_ent_path) {
479 0 : ret = ENOMEM;
480 0 : goto done;
481 : }
482 :
483 : /* Open the input entry first, then we can fstat() it and be
484 : * certain that it is still the same file. O_NONBLOCK protects
485 : * us against FIFOs and perhaps side-effects of the open() of a
486 : * device file if there ever was one here, and doesn't matter
487 : * for regular files or directories. */
488 6 : ifd = sss_openat_cloexec(src_dir_fd, ent_name,
489 : O_RDONLY | O_NOFOLLOW | O_NONBLOCK, &ret);
490 6 : if (ifd == -1 && ret != ELOOP) {
491 : /* openat error */
492 0 : DEBUG(SSSDBG_CRIT_FAILURE, "openat failed on '%s': %s\n",
493 : src_ent_path, strerror(ret));
494 0 : goto done;
495 6 : } else if (ifd == -1 && ret == ELOOP) {
496 : /* Should be a symlink.. */
497 1 : ret = fstatat(src_dir_fd, ent_name, &st, AT_SYMLINK_NOFOLLOW);
498 1 : if (ret == -1) {
499 0 : ret = errno;
500 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fstatat failed on '%s': %s\n",
501 : src_ent_path, strerror(ret));
502 0 : goto done;
503 : }
504 :
505 : /* Handle symlinks */
506 1 : ret = copy_symlink(src_dir_fd, dest_dir_fd, ent_name,
507 : dest_ent_path, &st, cctx->uid, cctx->gid);
508 1 : if (ret != EOK) {
509 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot copy '%s' to '%s'\n",
510 : src_ent_path, dest_ent_path);
511 : }
512 1 : goto done;
513 : }
514 :
515 5 : ret = fstat(ifd, &st);
516 5 : if (ret != 0) {
517 0 : ret = errno;
518 0 : DEBUG(SSSDBG_CRIT_FAILURE,
519 : "couldn't stat '%s': %s\n", src_ent_path, strerror(ret));
520 0 : goto done;
521 : }
522 :
523 5 : if (S_ISDIR(st.st_mode)) {
524 : /* If it's a directory, descend into it. */
525 1 : ret = copy_dir(cctx, ifd, src_ent_path,
526 : dest_dir_fd, ent_name,
527 1 : dest_ent_path, st.st_mode & 07777,
528 : &st);
529 1 : if (ret != EOK) {
530 0 : DEBUG(SSSDBG_OP_FAILURE,
531 : "Couldn't recursively copy '%s' to '%s': %s\n",
532 : src_ent_path, dest_ent_path, strerror(ret));
533 0 : goto done;
534 : }
535 4 : } else if (S_ISREG(st.st_mode)) {
536 : /* Copy a regular file */
537 3 : ret = copy_file(ifd, dest_dir_fd, ent_name, dest_ent_path,
538 : &st, cctx->uid, cctx->gid);
539 3 : if (ret) {
540 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot copy '%s' to '%s'\n",
541 : src_ent_path, dest_ent_path);
542 0 : goto done;
543 : }
544 : } else {
545 : /* Is a special file */
546 1 : DEBUG(SSSDBG_FUNC_DATA, "'%s' is a special file, skipping.\n",
547 : src_ent_path);
548 : }
549 :
550 5 : ret = EOK;
551 : done:
552 6 : talloc_free(src_ent_path);
553 6 : talloc_free(dest_ent_path);
554 6 : if (ifd != -1) close(ifd);
555 6 : return ret;
556 : }
557 :
558 : static errno_t
559 4 : copy_dir(struct copy_ctx *cctx,
560 : int src_dir_fd, const char *src_dir_path,
561 : int dest_parent_fd, const char *dest_dir_name,
562 : const char *dest_dir_path,
563 : mode_t mode,
564 : const struct stat *src_dir_stat)
565 : {
566 : errno_t ret;
567 : errno_t dret;
568 4 : int dest_dir_fd = -1;
569 4 : DIR *dir = NULL;
570 : struct dirent *ent;
571 :
572 4 : if (!dest_dir_path) {
573 0 : return EINVAL;
574 : }
575 :
576 4 : dir = fdopendir(src_dir_fd);
577 4 : if (dir == NULL) {
578 0 : ret = errno;
579 0 : DEBUG(SSSDBG_CRIT_FAILURE,
580 : "Error reading '%s': %s\n", src_dir_path, strerror(ret));
581 0 : goto done;
582 : }
583 :
584 : /* Create the directory. It starts owned by us (presumbaly root), with
585 : * fairly restrictive permissions that still allow us to use the
586 : * directory.
587 : * */
588 4 : errno = 0;
589 4 : ret = mkdirat(dest_parent_fd, dest_dir_name, S_IRWXU);
590 4 : if (ret == -1 && errno != EEXIST) {
591 0 : ret = errno;
592 0 : DEBUG(SSSDBG_CRIT_FAILURE,
593 : "Error reading '%s': %s\n", dest_dir_path, strerror(ret));
594 0 : goto done;
595 : }
596 :
597 4 : dest_dir_fd = sss_openat_cloexec(dest_parent_fd, dest_dir_name,
598 : O_RDONLY | O_DIRECTORY | O_NOFOLLOW, &ret);
599 4 : if (dest_dir_fd == -1) {
600 0 : ret = errno;
601 0 : DEBUG(SSSDBG_CRIT_FAILURE,
602 : "Error opening '%s': %s\n", dest_dir_path, strerror(ret));
603 0 : goto done;
604 : }
605 :
606 22 : while ((ent = readdir(dir)) != NULL) {
607 : /* Iterate through each item in the directory. */
608 : /* Skip over self and parent hard links. */
609 24 : if (strcmp(ent->d_name, ".") == 0 ||
610 10 : strcmp(ent->d_name, "..") == 0) {
611 8 : continue;
612 : }
613 :
614 6 : ret = copy_entry(cctx,
615 : src_dir_fd, src_dir_path,
616 : dest_dir_fd, dest_dir_path,
617 6 : ent->d_name);
618 6 : if (ret != EOK) {
619 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not copy [%s] to [%s]\n",
620 : src_dir_path, dest_dir_path);
621 0 : goto done;
622 : }
623 : }
624 :
625 : /* Set the ownership on the directory. Permissions are still
626 : * fairly restrictive. */
627 4 : ret = fchown(dest_dir_fd, cctx->uid, cctx->gid);
628 4 : if (ret == -1 && errno != EPERM) {
629 0 : ret = errno;
630 0 : DEBUG(SSSDBG_OP_FAILURE,
631 : "Error changing owner of '%s': %s\n",
632 : dest_dir_path, strerror(ret));
633 0 : goto done;
634 : }
635 :
636 : /* Set the desired mode. Do this explicitly to preserve S_ISGID and
637 : * other bits. Do this after chown, because chown is permitted to
638 : * reset these bits. */
639 4 : ret = fchmod(dest_dir_fd, mode);
640 4 : if (ret == -1) {
641 0 : ret = errno;
642 0 : DEBUG(SSSDBG_OP_FAILURE,
643 : "Error setting mode of '%s': %s\n",
644 : dest_dir_path, strerror(ret));
645 0 : goto done;
646 : }
647 :
648 4 : sss_futime_set(dest_dir_fd, src_dir_stat);
649 4 : if (ret != EOK) {
650 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_futime_set failed [%d]: %s\n",
651 : ret, strerror(ret));
652 : /* Do not fail */
653 : }
654 :
655 4 : ret = EOK;
656 : done:
657 4 : if (dir) {
658 4 : dret = closedir(dir);
659 4 : if (dret != 0) {
660 0 : dret = errno;
661 0 : DEBUG(SSSDBG_MINOR_FAILURE,
662 : "Failed to close directory: %s.\n", strerror(dret));
663 : }
664 : }
665 :
666 4 : if (dest_dir_fd != -1) {
667 4 : close(dest_dir_fd);
668 : }
669 4 : return ret;
670 : }
671 :
672 : /* NOTE:
673 : * For several reasons, including the fact that we copy even special files
674 : * (pipes, etc) from the skeleton directory, the skeldir needs to be trusted
675 : */
676 3 : int copy_tree(const char *src_root, const char *dst_root,
677 : mode_t mode_root, uid_t uid, gid_t gid)
678 : {
679 3 : int ret = EOK;
680 3 : struct copy_ctx *cctx = NULL;
681 3 : int fd = -1;
682 : struct stat s_src;
683 :
684 3 : fd = sss_open_cloexec(src_root, O_RDONLY | O_DIRECTORY, &ret);
685 3 : if (fd == -1) {
686 0 : goto fail;
687 : }
688 :
689 3 : ret = fstat(fd, &s_src);
690 3 : if (ret == -1) {
691 0 : ret = errno;
692 0 : goto fail;
693 : }
694 :
695 3 : cctx = talloc_zero(NULL, struct copy_ctx);
696 3 : if (!cctx) {
697 0 : ret = ENOMEM;
698 0 : goto fail;
699 : }
700 :
701 3 : cctx->src_orig = src_root;
702 3 : cctx->dst_orig = dst_root;
703 3 : cctx->src_dev = s_src.st_dev;
704 3 : cctx->uid = uid;
705 3 : cctx->gid = gid;
706 :
707 3 : ret = copy_dir(cctx, fd, src_root, AT_FDCWD,
708 : dst_root, dst_root, mode_root, &s_src);
709 3 : if (ret != EOK) {
710 0 : DEBUG(SSSDBG_CRIT_FAILURE,
711 : "copy_dir failed: [%d][%s]\n", ret, strerror(ret));
712 0 : goto fail;
713 : }
714 :
715 : fail:
716 3 : if (fd != -1) close(fd);
717 3 : reset_selinux_file_context();
718 3 : talloc_free(cctx);
719 3 : return ret;
720 : }
721 :
|