LCOV - code coverage report
Current view: top level - basic - copy.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 146 288 50.7 %
Date: 2015-07-29 18:47:03 Functions: 10 14 71.4 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2014 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <sys/sendfile.h>
      23             : #include <sys/xattr.h>
      24             : 
      25             : #include "util.h"
      26             : #include "btrfs-util.h"
      27             : #include "strv.h"
      28             : #include "copy.h"
      29             : 
      30             : #define COPY_BUFFER_SIZE (16*1024)
      31             : 
      32          10 : int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
      33          10 :         bool try_sendfile = true;
      34             :         int r;
      35             : 
      36          10 :         assert(fdf >= 0);
      37          10 :         assert(fdt >= 0);
      38             : 
      39             :         /* Try btrfs reflinks first. */
      40          10 :         if (try_reflink && max_bytes == (off_t) -1) {
      41           6 :                 r = btrfs_reflink(fdf, fdt);
      42           6 :                 if (r >= 0)
      43           0 :                         return r;
      44             :         }
      45             : 
      46             :         for (;;) {
      47          17 :                 size_t m = COPY_BUFFER_SIZE;
      48             :                 ssize_t n;
      49             : 
      50          17 :                 if (max_bytes != (off_t) -1) {
      51             : 
      52           3 :                         if (max_bytes <= 0)
      53           0 :                                 return -EFBIG;
      54             : 
      55           3 :                         if ((off_t) m > max_bytes)
      56           3 :                                 m = (size_t) max_bytes;
      57             :                 }
      58             : 
      59             :                 /* First try sendfile(), unless we already tried */
      60          17 :                 if (try_sendfile) {
      61             : 
      62          17 :                         n = sendfile(fdt, fdf, NULL, m);
      63          17 :                         if (n < 0) {
      64           3 :                                 if (errno != EINVAL && errno != ENOSYS)
      65           3 :                                         return -errno;
      66             : 
      67           0 :                                 try_sendfile = false;
      68             :                                 /* use fallback below */
      69          14 :                         } else if (n == 0) /* EOF */
      70           7 :                                 break;
      71           7 :                         else if (n > 0)
      72             :                                 /* Succcess! */
      73           7 :                                 goto next;
      74             :                 }
      75             : 
      76             :                 /* As a fallback just copy bits by hand */
      77           0 :                 {
      78           0 :                         char buf[m];
      79             : 
      80           0 :                         n = read(fdf, buf, m);
      81           0 :                         if (n < 0)
      82           0 :                                 return -errno;
      83           0 :                         if (n == 0) /* EOF */
      84           0 :                                 break;
      85             : 
      86           0 :                         r = loop_write(fdt, buf, (size_t) n, false);
      87           0 :                         if (r < 0)
      88           0 :                                 return r;
      89             :                 }
      90             : 
      91             :         next:
      92           7 :                 if (max_bytes != (off_t) -1) {
      93           0 :                         assert(max_bytes >= n);
      94           0 :                         max_bytes -= n;
      95             :                 }
      96           7 :         }
      97             : 
      98           7 :         return 0;
      99             : }
     100             : 
     101           2 : static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
     102           4 :         _cleanup_free_ char *target = NULL;
     103             :         int r;
     104             : 
     105           2 :         assert(from);
     106           2 :         assert(st);
     107           2 :         assert(to);
     108             : 
     109           2 :         r = readlinkat_malloc(df, from, &target);
     110           2 :         if (r < 0)
     111           0 :                 return r;
     112             : 
     113           2 :         if (symlinkat(target, dt, to) < 0)
     114           0 :                 return -errno;
     115             : 
     116           2 :         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
     117           0 :                 return -errno;
     118             : 
     119           2 :         return 0;
     120             : }
     121             : 
     122           4 : static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
     123           8 :         _cleanup_close_ int fdf = -1, fdt = -1;
     124             :         struct timespec ts[2];
     125             :         int r, q;
     126             : 
     127           4 :         assert(from);
     128           4 :         assert(st);
     129           4 :         assert(to);
     130             : 
     131           4 :         fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     132           4 :         if (fdf < 0)
     133           0 :                 return -errno;
     134             : 
     135           4 :         fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
     136           4 :         if (fdt < 0)
     137           0 :                 return -errno;
     138             : 
     139           4 :         r = copy_bytes(fdf, fdt, (off_t) -1, true);
     140           4 :         if (r < 0) {
     141           0 :                 unlinkat(dt, to, 0);
     142           0 :                 return r;
     143             :         }
     144             : 
     145           4 :         if (fchown(fdt, st->st_uid, st->st_gid) < 0)
     146           0 :                 r = -errno;
     147             : 
     148           4 :         if (fchmod(fdt, st->st_mode & 07777) < 0)
     149           0 :                 r = -errno;
     150             : 
     151           4 :         ts[0] = st->st_atim;
     152           4 :         ts[1] = st->st_mtim;
     153           4 :         (void) futimens(fdt, ts);
     154             : 
     155           4 :         (void) copy_xattr(fdf, fdt);
     156             : 
     157           4 :         q = close(fdt);
     158           4 :         fdt = -1;
     159             : 
     160           4 :         if (q < 0) {
     161           0 :                 r = -errno;
     162           0 :                 unlinkat(dt, to, 0);
     163             :         }
     164             : 
     165           4 :         return r;
     166             : }
     167             : 
     168           0 : static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
     169             :         int r;
     170             : 
     171           0 :         assert(from);
     172           0 :         assert(st);
     173           0 :         assert(to);
     174             : 
     175           0 :         r = mkfifoat(dt, to, st->st_mode & 07777);
     176           0 :         if (r < 0)
     177           0 :                 return -errno;
     178             : 
     179           0 :         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
     180           0 :                 r = -errno;
     181             : 
     182           0 :         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
     183           0 :                 r = -errno;
     184             : 
     185           0 :         return r;
     186             : }
     187             : 
     188           0 : static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
     189             :         int r;
     190             : 
     191           0 :         assert(from);
     192           0 :         assert(st);
     193           0 :         assert(to);
     194             : 
     195           0 :         r = mknodat(dt, to, st->st_mode, st->st_rdev);
     196           0 :         if (r < 0)
     197           0 :                 return -errno;
     198             : 
     199           0 :         if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
     200           0 :                 r = -errno;
     201             : 
     202           0 :         if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
     203           0 :                 r = -errno;
     204             : 
     205           0 :         return r;
     206             : }
     207             : 
     208           7 : static int fd_copy_directory(
     209             :                 int df,
     210             :                 const char *from,
     211             :                 const struct stat *st,
     212             :                 int dt,
     213             :                 const char *to,
     214             :                 dev_t original_device,
     215             :                 bool merge) {
     216             : 
     217          14 :         _cleanup_close_ int fdf = -1, fdt = -1;
     218          14 :         _cleanup_closedir_ DIR *d = NULL;
     219             :         struct dirent *de;
     220             :         bool created;
     221             :         int r;
     222             : 
     223           7 :         assert(st);
     224           7 :         assert(to);
     225             : 
     226           7 :         if (from)
     227           7 :                 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     228             :         else
     229           0 :                 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
     230             : 
     231           7 :         d = fdopendir(fdf);
     232           7 :         if (!d)
     233           0 :                 return -errno;
     234           7 :         fdf = -1;
     235             : 
     236           7 :         r = mkdirat(dt, to, st->st_mode & 07777);
     237           7 :         if (r >= 0)
     238           6 :                 created = true;
     239           1 :         else if (errno == EEXIST && merge)
     240           0 :                 created = false;
     241             :         else
     242           1 :                 return -errno;
     243             : 
     244           6 :         fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
     245           6 :         if (fdt < 0)
     246           0 :                 return -errno;
     247             : 
     248           6 :         r = 0;
     249             : 
     250           6 :         if (created) {
     251           6 :                 struct timespec ut[2] = {
     252             :                         st->st_atim,
     253             :                         st->st_mtim
     254             :                 };
     255             : 
     256           6 :                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
     257           0 :                         r = -errno;
     258             : 
     259           6 :                 if (fchmod(fdt, st->st_mode & 07777) < 0)
     260           0 :                         r = -errno;
     261             : 
     262           6 :                 (void) futimens(fdt, ut);
     263           6 :                 (void) copy_xattr(dirfd(d), fdt);
     264             :         }
     265             : 
     266          29 :         FOREACH_DIRENT_ALL(de, d, return -errno) {
     267             :                 struct stat buf;
     268             :                 int q;
     269             : 
     270          23 :                 if (STR_IN_SET(de->d_name, ".", ".."))
     271          24 :                         continue;
     272             : 
     273          11 :                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
     274           0 :                         r = -errno;
     275           0 :                         continue;
     276             :                 }
     277             : 
     278          11 :                 if (buf.st_dev != original_device)
     279           0 :                         continue;
     280             : 
     281          11 :                 if (S_ISREG(buf.st_mode))
     282           4 :                         q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
     283           7 :                 else if (S_ISDIR(buf.st_mode))
     284           5 :                         q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
     285           2 :                 else if (S_ISLNK(buf.st_mode))
     286           2 :                         q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
     287           0 :                 else if (S_ISFIFO(buf.st_mode))
     288           0 :                         q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
     289           0 :                 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
     290           0 :                         q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
     291             :                 else
     292           0 :                         q = -EOPNOTSUPP;
     293             : 
     294          11 :                 if (q == -EEXIST && merge)
     295           0 :                         q = 0;
     296             : 
     297          11 :                 if (q < 0)
     298           0 :                         r = q;
     299          23 :         }
     300             : 
     301           6 :         return r;
     302             : }
     303             : 
     304           3 : int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
     305             :         struct stat st;
     306             : 
     307           3 :         assert(from);
     308           3 :         assert(to);
     309             : 
     310           3 :         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
     311           1 :                 return -errno;
     312             : 
     313           2 :         if (S_ISREG(st.st_mode))
     314           0 :                 return fd_copy_regular(fdf, from, &st, fdt, to);
     315           2 :         else if (S_ISDIR(st.st_mode))
     316           2 :                 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
     317           0 :         else if (S_ISLNK(st.st_mode))
     318           0 :                 return fd_copy_symlink(fdf, from, &st, fdt, to);
     319           0 :         else if (S_ISFIFO(st.st_mode))
     320           0 :                 return fd_copy_fifo(fdf, from, &st, fdt, to);
     321           0 :         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
     322           0 :                 return fd_copy_node(fdf, from, &st, fdt, to);
     323             :         else
     324           0 :                 return -EOPNOTSUPP;
     325             : }
     326             : 
     327           3 : int copy_tree(const char *from, const char *to, bool merge) {
     328           3 :         return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
     329             : }
     330             : 
     331           0 : int copy_directory_fd(int dirfd, const char *to, bool merge) {
     332             : 
     333             :         struct stat st;
     334             : 
     335           0 :         assert(dirfd >= 0);
     336           0 :         assert(to);
     337             : 
     338           0 :         if (fstat(dirfd, &st) < 0)
     339           0 :                 return -errno;
     340             : 
     341           0 :         if (!S_ISDIR(st.st_mode))
     342           0 :                 return -ENOTDIR;
     343             : 
     344           0 :         return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
     345             : }
     346             : 
     347           3 : int copy_file_fd(const char *from, int fdt, bool try_reflink) {
     348           6 :         _cleanup_close_ int fdf = -1;
     349             :         int r;
     350             : 
     351           3 :         assert(from);
     352           3 :         assert(fdt >= 0);
     353             : 
     354           3 :         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
     355           3 :         if (fdf < 0)
     356           1 :                 return -errno;
     357             : 
     358           2 :         r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
     359             : 
     360           2 :         (void) copy_times(fdf, fdt);
     361           2 :         (void) copy_xattr(fdf, fdt);
     362             : 
     363           2 :         return r;
     364             : }
     365             : 
     366           1 : int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
     367           1 :         int fdt = -1, r;
     368             : 
     369           1 :         assert(from);
     370           1 :         assert(to);
     371             : 
     372           2 :         RUN_WITH_UMASK(0000) {
     373           1 :                 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
     374           1 :                 if (fdt < 0)
     375           0 :                         return -errno;
     376             :         }
     377             : 
     378           1 :         if (chattr_flags != 0)
     379           0 :                 (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
     380             : 
     381           1 :         r = copy_file_fd(from, fdt, true);
     382           1 :         if (r < 0) {
     383           0 :                 close(fdt);
     384           0 :                 unlink(to);
     385           0 :                 return r;
     386             :         }
     387             : 
     388           1 :         if (close(fdt) < 0) {
     389           0 :                 unlink_noerrno(to);
     390           0 :                 return -errno;
     391             :         }
     392             : 
     393           1 :         return 0;
     394             : }
     395             : 
     396           0 : int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
     397           0 :         _cleanup_free_ char *t = NULL;
     398             :         int r;
     399             : 
     400           0 :         assert(from);
     401           0 :         assert(to);
     402             : 
     403           0 :         r = tempfn_random(to, NULL, &t);
     404           0 :         if (r < 0)
     405           0 :                 return r;
     406             : 
     407           0 :         r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
     408           0 :         if (r < 0)
     409           0 :                 return r;
     410             : 
     411           0 :         if (replace) {
     412           0 :                 r = renameat(AT_FDCWD, t, AT_FDCWD, to);
     413           0 :                 if (r < 0)
     414           0 :                         r = -errno;
     415             :         } else
     416           0 :                 r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
     417           0 :         if (r < 0) {
     418           0 :                 (void) unlink_noerrno(t);
     419           0 :                 return r;
     420             :         }
     421             : 
     422           0 :         return 0;
     423             : }
     424             : 
     425           2 : int copy_times(int fdf, int fdt) {
     426             :         struct timespec ut[2];
     427             :         struct stat st;
     428           2 :         usec_t crtime = 0;
     429             : 
     430           2 :         assert(fdf >= 0);
     431           2 :         assert(fdt >= 0);
     432             : 
     433           2 :         if (fstat(fdf, &st) < 0)
     434           0 :                 return -errno;
     435             : 
     436           2 :         ut[0] = st.st_atim;
     437           2 :         ut[1] = st.st_mtim;
     438             : 
     439           2 :         if (futimens(fdt, ut) < 0)
     440           0 :                 return -errno;
     441             : 
     442           2 :         if (fd_getcrtime(fdf, &crtime) >= 0)
     443           0 :                 (void) fd_setcrtime(fdt, crtime);
     444             : 
     445           2 :         return 0;
     446             : }
     447             : 
     448          12 : int copy_xattr(int fdf, int fdt) {
     449          24 :         _cleanup_free_ char *bufa = NULL, *bufb = NULL;
     450          12 :         size_t sza = 100, szb = 100;
     451             :         ssize_t n;
     452          12 :         int ret = 0;
     453             :         const char *p;
     454             : 
     455             :         for (;;) {
     456          12 :                 bufa = malloc(sza);
     457          12 :                 if (!bufa)
     458           0 :                         return -ENOMEM;
     459             : 
     460          12 :                 n = flistxattr(fdf, bufa, sza);
     461          12 :                 if (n == 0)
     462          12 :                         return 0;
     463           0 :                 if (n > 0)
     464           0 :                         break;
     465           0 :                 if (errno != ERANGE)
     466           0 :                         return -errno;
     467             : 
     468           0 :                 sza *= 2;
     469             : 
     470           0 :                 free(bufa);
     471           0 :                 bufa = NULL;
     472           0 :         }
     473             : 
     474           0 :         p = bufa;
     475           0 :         while (n > 0) {
     476             :                 size_t l;
     477             : 
     478           0 :                 l = strlen(p);
     479           0 :                 assert(l < (size_t) n);
     480             : 
     481           0 :                 if (startswith(p, "user.")) {
     482             :                         ssize_t m;
     483             : 
     484           0 :                         if (!bufb) {
     485           0 :                                 bufb = malloc(szb);
     486           0 :                                 if (!bufb)
     487           0 :                                         return -ENOMEM;
     488             :                         }
     489             : 
     490           0 :                         m = fgetxattr(fdf, p, bufb, szb);
     491           0 :                         if (m < 0) {
     492           0 :                                 if (errno == ERANGE) {
     493           0 :                                         szb *= 2;
     494           0 :                                         free(bufb);
     495           0 :                                         bufb = NULL;
     496           0 :                                         continue;
     497             :                                 }
     498             : 
     499           0 :                                 return -errno;
     500             :                         }
     501             : 
     502           0 :                         if (fsetxattr(fdt, p, bufb, m, 0) < 0)
     503           0 :                                 ret = -errno;
     504             :                 }
     505             : 
     506           0 :                 p += l + 1;
     507           0 :                 n -= l + 1;
     508             :         }
     509             : 
     510           0 :         return ret;
     511             : }

Generated by: LCOV version 1.11