LCOV - code coverage report
Current view: top level - basic - copy.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 263 459 57.3 %
Date: 2019-08-22 15:41:25 Functions: 13 17 76.5 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <dirent.h>
       4             : #include <errno.h>
       5             : #include <fcntl.h>
       6             : #include <stddef.h>
       7             : #include <stdio.h>
       8             : #include <stdlib.h>
       9             : #include <string.h>
      10             : #include <sys/sendfile.h>
      11             : #include <sys/stat.h>
      12             : #include <sys/xattr.h>
      13             : #include <time.h>
      14             : #include <unistd.h>
      15             : 
      16             : #include "alloc-util.h"
      17             : #include "btrfs-util.h"
      18             : #include "chattr-util.h"
      19             : #include "copy.h"
      20             : #include "dirent-util.h"
      21             : #include "fd-util.h"
      22             : #include "fs-util.h"
      23             : #include "io-util.h"
      24             : #include "macro.h"
      25             : #include "missing.h"
      26             : #include "mountpoint-util.h"
      27             : #include "stat-util.h"
      28             : #include "string-util.h"
      29             : #include "strv.h"
      30             : #include "time-util.h"
      31             : #include "tmpfile-util.h"
      32             : #include "umask-util.h"
      33             : #include "user-util.h"
      34             : #include "xattr-util.h"
      35             : 
      36             : #define COPY_BUFFER_SIZE (16U*1024U)
      37             : 
      38             : /* A safety net for descending recursively into file system trees to copy. On Linux PATH_MAX is 4096, which means the
      39             :  * deepest valid path one can build is around 2048, which we hence use as a safety net here, to not spin endlessly in
      40             :  * case of bind mount cycles and suchlike. */
      41             : #define COPY_DEPTH_MAX 2048U
      42             : 
      43          41 : static ssize_t try_copy_file_range(
      44             :                 int fd_in, loff_t *off_in,
      45             :                 int fd_out, loff_t *off_out,
      46             :                 size_t len,
      47             :                 unsigned flags) {
      48             : 
      49             :         static int have = -1;
      50             :         ssize_t r;
      51             : 
      52          41 :         if (have == 0)
      53           0 :                 return -ENOSYS;
      54             : 
      55          41 :         r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
      56          41 :         if (have < 0)
      57           2 :                 have = r >= 0 || errno != ENOSYS;
      58          41 :         if (r < 0)
      59          19 :                 return -errno;
      60             : 
      61          22 :         return r;
      62             : }
      63             : 
      64             : enum {
      65             :         FD_IS_NO_PIPE,
      66             :         FD_IS_BLOCKING_PIPE,
      67             :         FD_IS_NONBLOCKING_PIPE,
      68             : };
      69             : 
      70           6 : static int fd_is_nonblock_pipe(int fd) {
      71             :         struct stat st;
      72             :         int flags;
      73             : 
      74             :         /* Checks whether the specified file descriptor refers to a pipe, and if so if O_NONBLOCK is set. */
      75             : 
      76           6 :         if (fstat(fd, &st) < 0)
      77           0 :                 return -errno;
      78             : 
      79           6 :         if (!S_ISFIFO(st.st_mode))
      80           6 :                 return FD_IS_NO_PIPE;
      81             : 
      82           0 :         flags = fcntl(fd, F_GETFL);
      83           0 :         if (flags < 0)
      84           0 :                 return -errno;
      85             : 
      86           0 :         return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE;
      87             : }
      88             : 
      89           0 : static int sigint_pending(void) {
      90             :         sigset_t ss;
      91             : 
      92           0 :         assert_se(sigemptyset(&ss) >= 0);
      93           0 :         assert_se(sigaddset(&ss, SIGINT) >= 0);
      94             : 
      95           0 :         if (sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 }) < 0) {
      96           0 :                 if (errno == EAGAIN)
      97           0 :                         return false;
      98             : 
      99           0 :                 return -errno;
     100             :         }
     101             : 
     102           0 :         return true;
     103             : }
     104             : 
     105          32 : int copy_bytes_full(
     106             :                 int fdf, int fdt,
     107             :                 uint64_t max_bytes,
     108             :                 CopyFlags copy_flags,
     109             :                 void **ret_remains,
     110             :                 size_t *ret_remains_size,
     111             :                 copy_progress_bytes_t progress,
     112             :                 void *userdata) {
     113             : 
     114          32 :         bool try_cfr = true, try_sendfile = true, try_splice = true;
     115          32 :         int r, nonblock_pipe = -1;
     116          32 :         size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
     117             : 
     118          32 :         assert(fdf >= 0);
     119          32 :         assert(fdt >= 0);
     120             : 
     121             :         /* Tries to copy bytes from the file descriptor 'fdf' to 'fdt' in the smartest possible way. Copies a maximum
     122             :          * of 'max_bytes', which may be specified as UINT64_MAX, in which no maximum is applied. Returns negative on
     123             :          * error, zero if EOF is hit before the bytes limit is hit and positive otherwise. If the copy fails for some
     124             :          * reason but we read but didn't yet write some data an ret_remains/ret_remains_size is not NULL, then it will
     125             :          * be initialized with an allocated buffer containing this "remaining" data. Note that these two parameters are
     126             :          * initialized with a valid buffer only on failure and only if there's actually data already read. Otherwise
     127             :          * these parameters if non-NULL are set to NULL. */
     128             : 
     129          32 :         if (ret_remains)
     130           0 :                 *ret_remains = NULL;
     131          32 :         if (ret_remains_size)
     132           0 :                 *ret_remains_size = 0;
     133             : 
     134             :         /* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of
     135             :          * source and destination first. */
     136          32 :         if ((copy_flags & COPY_REFLINK)) {
     137             :                 off_t foffset;
     138             : 
     139          17 :                 foffset = lseek(fdf, 0, SEEK_CUR);
     140          17 :                 if (foffset >= 0) {
     141             :                         off_t toffset;
     142             : 
     143          15 :                         toffset = lseek(fdt, 0, SEEK_CUR);
     144          15 :                         if (toffset >= 0) {
     145             : 
     146          15 :                                 if (foffset == 0 && toffset == 0 && max_bytes == UINT64_MAX)
     147          11 :                                         r = btrfs_reflink(fdf, fdt); /* full file reflink */
     148             :                                 else
     149           4 :                                         r = btrfs_clone_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
     150          15 :                                 if (r >= 0) {
     151             :                                         off_t t;
     152             : 
     153             :                                         /* This worked, yay! Now — to be fully correct — let's adjust the file pointers */
     154           0 :                                         if (max_bytes == UINT64_MAX) {
     155             : 
     156             :                                                 /* We cloned to the end of the source file, let's position the read
     157             :                                                  * pointer there, and query it at the same time. */
     158           0 :                                                 t = lseek(fdf, 0, SEEK_END);
     159           0 :                                                 if (t < 0)
     160           0 :                                                         return -errno;
     161           0 :                                                 if (t < foffset)
     162           0 :                                                         return -ESPIPE;
     163             : 
     164             :                                                 /* Let's adjust the destination file write pointer by the same number
     165             :                                                  * of bytes. */
     166           0 :                                                 t = lseek(fdt, toffset + (t - foffset), SEEK_SET);
     167           0 :                                                 if (t < 0)
     168           0 :                                                         return -errno;
     169             : 
     170           0 :                                                 return 0; /* we copied the whole thing, hence hit EOF, return 0 */
     171             :                                         } else {
     172           0 :                                                 t = lseek(fdf, foffset + max_bytes, SEEK_SET);
     173           0 :                                                 if (t < 0)
     174           0 :                                                         return -errno;
     175             : 
     176           0 :                                                 t = lseek(fdt, toffset + max_bytes, SEEK_SET);
     177           0 :                                                 if (t < 0)
     178           0 :                                                         return -errno;
     179             : 
     180           0 :                                                 return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */
     181             :                                         }
     182             :                                 }
     183             :                         }
     184             :                 }
     185             :         }
     186             : 
     187       79987 :         for (;;) {
     188             :                 ssize_t n;
     189             : 
     190       80019 :                 if (max_bytes <= 0)
     191          10 :                         return 1; /* return > 0 if we hit the max_bytes limit */
     192             : 
     193       80009 :                 if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
     194           0 :                         r = sigint_pending();
     195           0 :                         if (r < 0)
     196           0 :                                 return r;
     197           0 :                         if (r > 0)
     198           0 :                                 return -EINTR;
     199             :                 }
     200             : 
     201       80009 :                 if (max_bytes != UINT64_MAX && m > max_bytes)
     202          15 :                         m = max_bytes;
     203             : 
     204             :                 /* First try copy_file_range(), unless we already tried */
     205       80009 :                 if (try_cfr) {
     206          41 :                         n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
     207          41 :                         if (n < 0) {
     208          19 :                                 if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF))
     209           0 :                                         return n;
     210             : 
     211          19 :                                 try_cfr = false;
     212             :                                 /* use fallback below */
     213          22 :                         } else if (n == 0) /* EOF */
     214           9 :                                 break;
     215             :                         else
     216             :                                 /* Success! */
     217          13 :                                 goto next;
     218             :                 }
     219             : 
     220             :                 /* First try sendfile(), unless we already tried */
     221       79987 :                 if (try_sendfile) {
     222          28 :                         n = sendfile(fdt, fdf, NULL, m);
     223          28 :                         if (n < 0) {
     224           6 :                                 if (!IN_SET(errno, EINVAL, ENOSYS))
     225           3 :                                         return -errno;
     226             : 
     227           3 :                                 try_sendfile = false;
     228             :                                 /* use fallback below */
     229          22 :                         } else if (n == 0) /* EOF */
     230           9 :                                 break;
     231             :                         else
     232             :                                 /* Success! */
     233          13 :                                 goto next;
     234             :                 }
     235             : 
     236             :                 /* Then try splice, unless we already tried. */
     237       79962 :                 if (try_splice) {
     238             : 
     239             :                         /* splice()'s asynchronous I/O support is a bit weird. When it encounters a pipe file
     240             :                          * descriptor, then it will ignore its O_NONBLOCK flag and instead only honour the
     241             :                          * SPLICE_F_NONBLOCK flag specified in its flag parameter. Let's hide this behaviour here, and
     242             :                          * check if either of the specified fds are a pipe, and if so, let's pass the flag
     243             :                          * automatically, depending on O_NONBLOCK being set.
     244             :                          *
     245             :                          * Here's a twist though: when we use it to move data between two pipes of which one has
     246             :                          * O_NONBLOCK set and the other has not, then we have no individual control over O_NONBLOCK
     247             :                          * behaviour. Hence in that case we can't use splice() and still guarantee systematic
     248             :                          * O_NONBLOCK behaviour, hence don't. */
     249             : 
     250           3 :                         if (nonblock_pipe < 0) {
     251             :                                 int a, b;
     252             : 
     253             :                                 /* Check if either of these fds is a pipe, and if so non-blocking or not */
     254           3 :                                 a = fd_is_nonblock_pipe(fdf);
     255           3 :                                 if (a < 0)
     256           0 :                                         return a;
     257             : 
     258           3 :                                 b = fd_is_nonblock_pipe(fdt);
     259           3 :                                 if (b < 0)
     260           0 :                                         return b;
     261             : 
     262           3 :                                 if ((a == FD_IS_NO_PIPE && b == FD_IS_NO_PIPE) ||
     263           0 :                                     (a == FD_IS_BLOCKING_PIPE && b == FD_IS_NONBLOCKING_PIPE) ||
     264           0 :                                     (a == FD_IS_NONBLOCKING_PIPE && b == FD_IS_BLOCKING_PIPE))
     265             : 
     266             :                                         /* splice() only works if one of the fds is a pipe. If neither is, let's skip
     267             :                                          * this step right-away. As mentioned above, if one of the two fds refers to a
     268             :                                          * blocking pipe and the other to a non-blocking pipe, we can't use splice()
     269             :                                          * either, hence don't try either. This hence means we can only use splice() if
     270             :                                          * either only one of the two fds is a pipe, or if both are pipes with the same
     271             :                                          * nonblocking flag setting. */
     272             : 
     273           3 :                                         try_splice = false;
     274             :                                 else
     275           0 :                                         nonblock_pipe = a == FD_IS_NONBLOCKING_PIPE || b == FD_IS_NONBLOCKING_PIPE;
     276             :                         }
     277             :                 }
     278             : 
     279       79962 :                 if (try_splice) {
     280           0 :                         n = splice(fdf, NULL, fdt, NULL, m, nonblock_pipe ? SPLICE_F_NONBLOCK : 0);
     281           0 :                         if (n < 0) {
     282           0 :                                 if (!IN_SET(errno, EINVAL, ENOSYS))
     283           0 :                                         return -errno;
     284             : 
     285           0 :                                 try_splice = false;
     286             :                                 /* use fallback below */
     287           0 :                         } else if (n == 0) /* EOF */
     288           0 :                                 break;
     289             :                         else
     290             :                                 /* Success! */
     291           0 :                                 goto next;
     292             :                 }
     293             : 
     294             :                 /* As a fallback just copy bits by hand */
     295       79962 :                 {
     296       79962 :                         uint8_t buf[MIN(m, COPY_BUFFER_SIZE)], *p = buf;
     297             :                         ssize_t z;
     298             : 
     299       79962 :                         n = read(fdf, buf, sizeof buf);
     300       79962 :                         if (n < 0)
     301           0 :                                 return -errno;
     302       79962 :                         if (n == 0) /* EOF */
     303           1 :                                 break;
     304             : 
     305       79961 :                         z = (size_t) n;
     306             :                         do {
     307             :                                 ssize_t k;
     308             : 
     309       79961 :                                 k = write(fdt, p, z);
     310       79961 :                                 if (k < 0) {
     311           0 :                                         r = -errno;
     312             : 
     313           0 :                                         if (ret_remains) {
     314             :                                                 void *copy;
     315             : 
     316           0 :                                                 copy = memdup(p, z);
     317           0 :                                                 if (!copy)
     318           0 :                                                         return -ENOMEM;
     319             : 
     320           0 :                                                 *ret_remains = copy;
     321             :                                         }
     322             : 
     323           0 :                                         if (ret_remains_size)
     324           0 :                                                 *ret_remains_size = z;
     325             : 
     326           0 :                                         return r;
     327             :                                 }
     328             : 
     329       79961 :                                 assert(k <= z);
     330       79961 :                                 z -= k;
     331       79961 :                                 p += k;
     332       79961 :                         } while (z > 0);
     333             :                 }
     334             : 
     335       79987 :         next:
     336       79987 :                 if (progress) {
     337           0 :                         r = progress(n, userdata);
     338           0 :                         if (r < 0)
     339           0 :                                 return r;
     340             :                 }
     341             : 
     342       79987 :                 if (max_bytes != (uint64_t) -1) {
     343       53768 :                         assert(max_bytes >= (uint64_t) n);
     344       53768 :                         max_bytes -= n;
     345             :                 }
     346             : 
     347             :                 /* sendfile accepts at most SSIZE_MAX-offset bytes to copy,
     348             :                  * so reduce our maximum by the amount we already copied,
     349             :                  * but don't go below our copy buffer size, unless we are
     350             :                  * close the limit of bytes we are allowed to copy. */
     351       79987 :                 m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n);
     352             :         }
     353             : 
     354          19 :         return 0; /* return 0 if we hit EOF earlier than the size limit */
     355             : }
     356             : 
     357           2 : static int fd_copy_symlink(
     358             :                 int df,
     359             :                 const char *from,
     360             :                 const struct stat *st,
     361             :                 int dt,
     362             :                 const char *to,
     363             :                 uid_t override_uid,
     364             :                 gid_t override_gid,
     365             :                 CopyFlags copy_flags) {
     366             : 
     367           2 :         _cleanup_free_ char *target = NULL;
     368             :         int r;
     369             : 
     370           2 :         assert(from);
     371           2 :         assert(st);
     372           2 :         assert(to);
     373             : 
     374           2 :         r = readlinkat_malloc(df, from, &target);
     375           2 :         if (r < 0)
     376           0 :                 return r;
     377             : 
     378           2 :         if (symlinkat(target, dt, to) < 0)
     379           0 :                 return -errno;
     380             : 
     381           4 :         if (fchownat(dt, to,
     382           2 :                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
     383           2 :                      gid_is_valid(override_gid) ? override_gid : st->st_gid,
     384             :                      AT_SYMLINK_NOFOLLOW) < 0)
     385           0 :                 return -errno;
     386             : 
     387           2 :         return 0;
     388             : }
     389             : 
     390           4 : static int fd_copy_regular(
     391             :                 int df,
     392             :                 const char *from,
     393             :                 const struct stat *st,
     394             :                 int dt,
     395             :                 const char *to,
     396             :                 uid_t override_uid,
     397             :                 gid_t override_gid,
     398             :                 CopyFlags copy_flags,
     399             :                 copy_progress_bytes_t progress,
     400             :                 void *userdata) {
     401             : 
     402           4 :         _cleanup_close_ int fdf = -1, fdt = -1;
     403             :         struct timespec ts[2];
     404             :         int r, q;
     405             : 
     406           4 :         assert(from);
     407           4 :         assert(st);
     408           4 :         assert(to);
     409             : 
     410           4 :         fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     411           4 :         if (fdf < 0)
     412           0 :                 return -errno;
     413             : 
     414           4 :         fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
     415           4 :         if (fdt < 0)
     416           0 :                 return -errno;
     417             : 
     418           4 :         r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress, userdata);
     419           4 :         if (r < 0) {
     420           0 :                 (void) unlinkat(dt, to, 0);
     421           0 :                 return r;
     422             :         }
     423             : 
     424           8 :         if (fchown(fdt,
     425           4 :                    uid_is_valid(override_uid) ? override_uid : st->st_uid,
     426           4 :                    gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
     427           0 :                 r = -errno;
     428             : 
     429           4 :         if (fchmod(fdt, st->st_mode & 07777) < 0)
     430           0 :                 r = -errno;
     431             : 
     432           4 :         ts[0] = st->st_atim;
     433           4 :         ts[1] = st->st_mtim;
     434           4 :         (void) futimens(fdt, ts);
     435           4 :         (void) copy_xattr(fdf, fdt);
     436             : 
     437           4 :         q = close(fdt);
     438           4 :         fdt = -1;
     439             : 
     440           4 :         if (q < 0) {
     441           0 :                 r = -errno;
     442           0 :                 (void) unlinkat(dt, to, 0);
     443             :         }
     444             : 
     445           4 :         return r;
     446             : }
     447             : 
     448           0 : static int fd_copy_fifo(
     449             :                 int df,
     450             :                 const char *from,
     451             :                 const struct stat *st,
     452             :                 int dt,
     453             :                 const char *to,
     454             :                 uid_t override_uid,
     455             :                 gid_t override_gid,
     456             :                 CopyFlags copy_flags) {
     457             :         int r;
     458             : 
     459           0 :         assert(from);
     460           0 :         assert(st);
     461           0 :         assert(to);
     462             : 
     463           0 :         r = mkfifoat(dt, to, st->st_mode & 07777);
     464           0 :         if (r < 0)
     465           0 :                 return -errno;
     466             : 
     467           0 :         if (fchownat(dt, to,
     468           0 :                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
     469           0 :                      gid_is_valid(override_gid) ? override_gid : st->st_gid,
     470             :                      AT_SYMLINK_NOFOLLOW) < 0)
     471           0 :                 r = -errno;
     472             : 
     473           0 :         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
     474           0 :                 r = -errno;
     475             : 
     476           0 :         return r;
     477             : }
     478             : 
     479           1 : static int fd_copy_node(
     480             :                 int df,
     481             :                 const char *from,
     482             :                 const struct stat *st,
     483             :                 int dt,
     484             :                 const char *to,
     485             :                 uid_t override_uid,
     486             :                 gid_t override_gid,
     487             :                 CopyFlags copy_flags) {
     488             :         int r;
     489             : 
     490           1 :         assert(from);
     491           1 :         assert(st);
     492           1 :         assert(to);
     493             : 
     494           1 :         r = mknodat(dt, to, st->st_mode, st->st_rdev);
     495           1 :         if (r < 0)
     496           0 :                 return -errno;
     497             : 
     498           2 :         if (fchownat(dt, to,
     499           1 :                      uid_is_valid(override_uid) ? override_uid : st->st_uid,
     500           1 :                      gid_is_valid(override_gid) ? override_gid : st->st_gid,
     501             :                      AT_SYMLINK_NOFOLLOW) < 0)
     502           0 :                 r = -errno;
     503             : 
     504           1 :         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
     505           0 :                 r = -errno;
     506             : 
     507           1 :         return r;
     508             : }
     509             : 
     510           7 : static int fd_copy_directory(
     511             :                 int df,
     512             :                 const char *from,
     513             :                 const struct stat *st,
     514             :                 int dt,
     515             :                 const char *to,
     516             :                 dev_t original_device,
     517             :                 unsigned depth_left,
     518             :                 uid_t override_uid,
     519             :                 gid_t override_gid,
     520             :                 CopyFlags copy_flags,
     521             :                 const char *display_path,
     522             :                 copy_progress_path_t progress_path,
     523             :                 copy_progress_bytes_t progress_bytes,
     524             :                 void *userdata) {
     525             : 
     526           7 :         _cleanup_close_ int fdf = -1, fdt = -1;
     527           7 :         _cleanup_closedir_ DIR *d = NULL;
     528             :         struct dirent *de;
     529             :         bool exists, created;
     530             :         int r;
     531             : 
     532           7 :         assert(st);
     533           7 :         assert(to);
     534             : 
     535           7 :         if (depth_left == 0)
     536           0 :                 return -ENAMETOOLONG;
     537             : 
     538           7 :         if (from)
     539           7 :                 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     540             :         else
     541           0 :                 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
     542           7 :         if (fdf < 0)
     543           0 :                 return -errno;
     544             : 
     545           7 :         d = fdopendir(fdf);
     546           7 :         if (!d)
     547           0 :                 return -errno;
     548           7 :         fdf = -1;
     549             : 
     550           7 :         exists = false;
     551           7 :         if (copy_flags & COPY_MERGE_EMPTY) {
     552           0 :                 r = dir_is_empty_at(dt, to);
     553           0 :                 if (r < 0 && r != -ENOENT)
     554           0 :                         return r;
     555           0 :                 else if (r == 1)
     556           0 :                         exists = true;
     557             :         }
     558             : 
     559           7 :         if (exists)
     560           0 :                 created = false;
     561             :         else {
     562           7 :                 r = mkdirat(dt, to, st->st_mode & 07777);
     563           7 :                 if (r >= 0)
     564           6 :                         created = true;
     565           1 :                 else if (errno == EEXIST && (copy_flags & COPY_MERGE))
     566           0 :                         created = false;
     567             :                 else
     568           1 :                         return -errno;
     569             :         }
     570             : 
     571           6 :         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     572           6 :         if (fdt < 0)
     573           0 :                 return -errno;
     574             : 
     575           6 :         r = 0;
     576             : 
     577          30 :         FOREACH_DIRENT_ALL(de, d, return -errno) {
     578          24 :                 const char *child_display_path = NULL;
     579          24 :                 _cleanup_free_ char *dp = NULL;
     580             :                 struct stat buf;
     581             :                 int q;
     582             : 
     583          24 :                 if (dot_or_dot_dot(de->d_name))
     584          12 :                         continue;
     585             : 
     586          12 :                 if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
     587           0 :                         r = sigint_pending();
     588           0 :                         if (r < 0)
     589           0 :                                 return r;
     590           0 :                         if (r > 0)
     591           0 :                                 return -EINTR;
     592             :                 }
     593             : 
     594          12 :                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
     595           0 :                         r = -errno;
     596           0 :                         continue;
     597             :                 }
     598             : 
     599          12 :                 if (progress_path) {
     600           0 :                         if (display_path)
     601           0 :                                 child_display_path = dp = path_join(display_path, de->d_name);
     602             :                         else
     603           0 :                                 child_display_path = de->d_name;
     604             : 
     605           0 :                         r = progress_path(child_display_path, &buf, userdata);
     606           0 :                         if (r < 0)
     607           0 :                                 return r;
     608             :                 }
     609             : 
     610          12 :                 if (S_ISDIR(buf.st_mode)) {
     611             :                         /*
     612             :                          * Don't descend into directories on other file systems, if this is requested. We do a simple
     613             :                          * .st_dev check here, which basically comes for free. Note that we do this check only on
     614             :                          * directories, not other kind of file system objects, for two reason:
     615             :                          *
     616             :                          * • The kernel's overlayfs pseudo file system that overlays multiple real file systems
     617             :                          *   propagates the .st_dev field of the file system a file originates from all the way up
     618             :                          *   through the stack to stat(). It doesn't do that for directories however. This means that
     619             :                          *   comparing .st_dev on non-directories suggests that they all are mount points. To avoid
     620             :                          *   confusion we hence avoid relying on this check for regular files.
     621             :                          *
     622             :                          * • The main reason we do this check at all is to protect ourselves from bind mount cycles,
     623             :                          *   where we really want to avoid descending down in all eternity. However the .st_dev check
     624             :                          *   is usually not sufficient for this protection anyway, as bind mount cycles from the same
     625             :                          *   file system onto itself can't be detected that way. (Note we also do a recursion depth
     626             :                          *   check, which is probably the better protection in this regard, which is why
     627             :                          *   COPY_SAME_MOUNT is optional).
     628             :                          */
     629             : 
     630           5 :                         if (FLAGS_SET(copy_flags, COPY_SAME_MOUNT)) {
     631           0 :                                 if (buf.st_dev != original_device)
     632           0 :                                         continue;
     633             : 
     634           0 :                                 r = fd_is_mount_point(dirfd(d), de->d_name, 0);
     635           0 :                                 if (r < 0)
     636           0 :                                         return r;
     637           0 :                                 if (r > 0)
     638           0 :                                         continue;
     639             :                         }
     640             : 
     641           5 :                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, child_display_path, progress_path, progress_bytes, userdata);
     642           7 :                 } else if (S_ISREG(buf.st_mode))
     643           4 :                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, progress_bytes, userdata);
     644           3 :                 else if (S_ISLNK(buf.st_mode))
     645           2 :                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
     646           1 :                 else if (S_ISFIFO(buf.st_mode))
     647           0 :                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
     648           1 :                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
     649           1 :                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
     650             :                 else
     651           0 :                         q = -EOPNOTSUPP;
     652             : 
     653          12 :                 if (q == -EINTR) /* Propagate SIGINT up instantly */
     654           0 :                         return q;
     655          12 :                 if (q == -EEXIST && (copy_flags & COPY_MERGE))
     656           0 :                         q = 0;
     657          12 :                 if (q < 0)
     658           0 :                         r = q;
     659             :         }
     660             : 
     661           6 :         if (created) {
     662           6 :                 struct timespec ut[2] = {
     663             :                         st->st_atim,
     664             :                         st->st_mtim
     665             :                 };
     666             : 
     667          12 :                 if (fchown(fdt,
     668           6 :                            uid_is_valid(override_uid) ? override_uid : st->st_uid,
     669           6 :                            gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
     670           0 :                         r = -errno;
     671             : 
     672           6 :                 if (fchmod(fdt, st->st_mode & 07777) < 0)
     673           0 :                         r = -errno;
     674             : 
     675           6 :                 (void) copy_xattr(dirfd(d), fdt);
     676           6 :                 (void) futimens(fdt, ut);
     677             :         }
     678             : 
     679           6 :         return r;
     680             : }
     681             : 
     682           3 : int copy_tree_at_full(
     683             :                 int fdf,
     684             :                 const char *from,
     685             :                 int fdt,
     686             :                 const char *to,
     687             :                 uid_t override_uid,
     688             :                 gid_t override_gid,
     689             :                 CopyFlags copy_flags,
     690             :                 copy_progress_path_t progress_path,
     691             :                 copy_progress_bytes_t progress_bytes,
     692             :                 void *userdata) {
     693             : 
     694             :         struct stat st;
     695             : 
     696           3 :         assert(from);
     697           3 :         assert(to);
     698             : 
     699           3 :         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
     700           1 :                 return -errno;
     701             : 
     702           2 :         if (S_ISREG(st.st_mode))
     703           0 :                 return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, progress_bytes, userdata);
     704           2 :         else if (S_ISDIR(st.st_mode))
     705           2 :                 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, progress_path, progress_bytes, userdata);
     706           0 :         else if (S_ISLNK(st.st_mode))
     707           0 :                 return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
     708           0 :         else if (S_ISFIFO(st.st_mode))
     709           0 :                 return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
     710           0 :         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
     711           0 :                 return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
     712             :         else
     713           0 :                 return -EOPNOTSUPP;
     714             : }
     715             : 
     716           0 : int copy_directory_fd_full(
     717             :                 int dirfd,
     718             :                 const char *to,
     719             :                 CopyFlags copy_flags,
     720             :                 copy_progress_path_t progress_path,
     721             :                 copy_progress_bytes_t progress_bytes,
     722             :                 void *userdata) {
     723             : 
     724             :         struct stat st;
     725             : 
     726           0 :         assert(dirfd >= 0);
     727           0 :         assert(to);
     728             : 
     729           0 :         if (fstat(dirfd, &st) < 0)
     730           0 :                 return -errno;
     731             : 
     732           0 :         if (!S_ISDIR(st.st_mode))
     733           0 :                 return -ENOTDIR;
     734             : 
     735           0 :         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, progress_path, progress_bytes, userdata);
     736             : }
     737             : 
     738           0 : int copy_directory_full(
     739             :                 const char *from,
     740             :                 const char *to,
     741             :                 CopyFlags copy_flags,
     742             :                 copy_progress_path_t progress_path,
     743             :                 copy_progress_bytes_t progress_bytes,
     744             :                 void *userdata) {
     745             : 
     746             :         struct stat st;
     747             : 
     748           0 :         assert(from);
     749           0 :         assert(to);
     750             : 
     751           0 :         if (lstat(from, &st) < 0)
     752           0 :                 return -errno;
     753             : 
     754           0 :         if (!S_ISDIR(st.st_mode))
     755           0 :                 return -ENOTDIR;
     756             : 
     757           0 :         return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, progress_path, progress_bytes, userdata);
     758             : }
     759             : 
     760           6 : int copy_file_fd_full(
     761             :                 const char *from,
     762             :                 int fdt,
     763             :                 CopyFlags copy_flags,
     764             :                 copy_progress_bytes_t progress_bytes,
     765             :                 void *userdata) {
     766             : 
     767           6 :         _cleanup_close_ int fdf = -1;
     768             :         int r;
     769             : 
     770           6 :         assert(from);
     771           6 :         assert(fdt >= 0);
     772             : 
     773           6 :         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
     774           6 :         if (fdf < 0)
     775           1 :                 return -errno;
     776             : 
     777           5 :         r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress_bytes, userdata);
     778             : 
     779           5 :         (void) copy_times(fdf, fdt, copy_flags);
     780           5 :         (void) copy_xattr(fdf, fdt);
     781             : 
     782           5 :         return r;
     783             : }
     784             : 
     785           1 : int copy_file_full(
     786             :                 const char *from,
     787             :                 const char *to,
     788             :                 int flags,
     789             :                 mode_t mode,
     790             :                 unsigned chattr_flags,
     791             :                 unsigned chattr_mask,
     792             :                 CopyFlags copy_flags,
     793             :                 copy_progress_bytes_t progress_bytes,
     794             :                 void *userdata) {
     795             : 
     796           1 :         int fdt = -1, r;
     797             : 
     798           1 :         assert(from);
     799           1 :         assert(to);
     800             : 
     801           2 :         RUN_WITH_UMASK(0000) {
     802           1 :                 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
     803           1 :                 if (fdt < 0)
     804           0 :                         return -errno;
     805             :         }
     806             : 
     807           1 :         if (chattr_mask != 0)
     808           0 :                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
     809             : 
     810           1 :         r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
     811           1 :         if (r < 0) {
     812           0 :                 close(fdt);
     813           0 :                 (void) unlink(to);
     814           0 :                 return r;
     815             :         }
     816             : 
     817           1 :         if (chattr_mask != 0)
     818           0 :                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
     819             : 
     820           1 :         if (close(fdt) < 0) {
     821           0 :                 unlink_noerrno(to);
     822           0 :                 return -errno;
     823             :         }
     824             : 
     825           1 :         return 0;
     826             : }
     827             : 
     828           3 : int copy_file_atomic_full(
     829             :                 const char *from,
     830             :                 const char *to,
     831             :                 mode_t mode,
     832             :                 unsigned chattr_flags,
     833             :                 unsigned chattr_mask,
     834             :                 CopyFlags copy_flags,
     835             :                 copy_progress_bytes_t progress_bytes,
     836             :                 void *userdata) {
     837             : 
     838           3 :         _cleanup_(unlink_and_freep) char *t = NULL;
     839           3 :         _cleanup_close_ int fdt = -1;
     840             :         int r;
     841             : 
     842           3 :         assert(from);
     843           3 :         assert(to);
     844             : 
     845             :         /* We try to use O_TMPFILE here to create the file if we can. Note that that only works if COPY_REPLACE is not
     846             :          * set though as we need to use linkat() for linking the O_TMPFILE file into the file system but that system
     847             :          * call can't replace existing files. Hence, if COPY_REPLACE is set we create a temporary name in the file
     848             :          * system right-away and unconditionally which we then can renameat() to the right name after we completed
     849             :          * writing it. */
     850             : 
     851           3 :         if (copy_flags & COPY_REPLACE) {
     852           1 :                 r = tempfn_random(to, NULL, &t);
     853           1 :                 if (r < 0)
     854           0 :                         return r;
     855             : 
     856           1 :                 fdt = open(t, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
     857           1 :                 if (fdt < 0) {
     858           0 :                         t = mfree(t);
     859           0 :                         return -errno;
     860             :                 }
     861             :         } else {
     862           2 :                 fdt = open_tmpfile_linkable(to, O_WRONLY|O_CLOEXEC, &t);
     863           2 :                 if (fdt < 0)
     864           0 :                         return fdt;
     865             :         }
     866             : 
     867           3 :         if (chattr_mask != 0)
     868           0 :                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
     869             : 
     870           3 :         r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata);
     871           3 :         if (r < 0)
     872           0 :                 return r;
     873             : 
     874           3 :         if (fchmod(fdt, mode) < 0)
     875           0 :                 return -errno;
     876             : 
     877           3 :         if (copy_flags & COPY_REPLACE) {
     878           1 :                 if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0)
     879           0 :                         return -errno;
     880             :         } else {
     881           2 :                 r = link_tmpfile(fdt, t, to);
     882           2 :                 if (r < 0)
     883           1 :                         return r;
     884             :         }
     885             : 
     886           2 :         if (chattr_mask != 0)
     887           0 :                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
     888             : 
     889           2 :         t = mfree(t);
     890           2 :         return 0;
     891             : }
     892             : 
     893           5 : int copy_times(int fdf, int fdt, CopyFlags flags) {
     894             :         struct timespec ut[2];
     895             :         struct stat st;
     896             : 
     897           5 :         assert(fdf >= 0);
     898           5 :         assert(fdt >= 0);
     899             : 
     900           5 :         if (fstat(fdf, &st) < 0)
     901           0 :                 return -errno;
     902             : 
     903           5 :         ut[0] = st.st_atim;
     904           5 :         ut[1] = st.st_mtim;
     905             : 
     906           5 :         if (futimens(fdt, ut) < 0)
     907           0 :                 return -errno;
     908             : 
     909           5 :         if (FLAGS_SET(flags, COPY_CRTIME)) {
     910             :                 usec_t crtime;
     911             : 
     912           0 :                 if (fd_getcrtime(fdf, &crtime) >= 0)
     913           0 :                         (void) fd_setcrtime(fdt, crtime);
     914             :         }
     915             : 
     916           5 :         return 0;
     917             : }
     918             : 
     919          15 : int copy_xattr(int fdf, int fdt) {
     920          15 :         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
     921          15 :         size_t sza = 100, szb = 100;
     922             :         ssize_t n;
     923          15 :         int ret = 0;
     924             :         const char *p;
     925             : 
     926             :         for (;;) {
     927          15 :                 bufa = malloc(sza);
     928          15 :                 if (!bufa)
     929           0 :                         return -ENOMEM;
     930             : 
     931          15 :                 n = flistxattr(fdf, bufa, sza);
     932          15 :                 if (n == 0)
     933           0 :                         return 0;
     934          15 :                 if (n > 0)
     935          15 :                         break;
     936           0 :                 if (errno != ERANGE)
     937           0 :                         return -errno;
     938             : 
     939           0 :                 sza *= 2;
     940             : 
     941           0 :                 bufa = mfree(bufa);
     942             :         }
     943             : 
     944          15 :         p = bufa;
     945          30 :         while (n > 0) {
     946             :                 size_t l;
     947             : 
     948          15 :                 l = strlen(p);
     949          15 :                 assert(l < (size_t) n);
     950             : 
     951          15 :                 if (startswith(p, "user.")) {
     952             :                         ssize_t m;
     953             : 
     954           0 :                         if (!bufb) {
     955           0 :                                 bufb = malloc(szb);
     956           0 :                                 if (!bufb)
     957           0 :                                         return -ENOMEM;
     958             :                         }
     959             : 
     960           0 :                         m = fgetxattr(fdf, p, bufb, szb);
     961           0 :                         if (m < 0) {
     962           0 :                                 if (errno == ERANGE) {
     963           0 :                                         szb *= 2;
     964           0 :                                         bufb = mfree(bufb);
     965           0 :                                         continue;
     966             :                                 }
     967             : 
     968           0 :                                 return -errno;
     969             :                         }
     970             : 
     971           0 :                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
     972           0 :                                 ret = -errno;
     973             :                 }
     974             : 
     975          15 :                 p += l + 1;
     976          15 :                 n -= l + 1;
     977             :         }
     978             : 
     979          15 :         return ret;
     980             : }

Generated by: LCOV version 1.14