LCOV - code coverage report
Current view: top level - tools - files.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 196 310 63.2 %
Date: 2016-06-29 Functions: 12 12 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             : 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             : 

Generated by: LCOV version 1.10