LCOV - code coverage report
Current view: top level - tools - files.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 175 284 61.6 %
Date: 2015-10-19 Functions: 10 10 100.0 %

          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             : 

Generated by: LCOV version 1.10