1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
22 #include <sys/sendfile.h>
25 #include "btrfs-util.h"
28 int copy_bytes(int fdf, int fdt, off_t max_bytes) {
29 bool try_sendfile = true;
35 /* Try btrfs reflinks first. */
36 if (max_bytes == (off_t) -1) {
37 r = btrfs_reflink(fdf, fdt);
46 if (max_bytes != (off_t) -1) {
51 if ((off_t) m > max_bytes)
52 m = (size_t) max_bytes;
55 /* First try sendfile(), unless we already tried */
58 n = sendfile(fdt, fdf, NULL, m);
60 if (errno != EINVAL && errno != ENOSYS)
64 /* use fallback below */
65 } else if (n == 0) /* EOF */
72 /* As a fallback just copy bits by hand */
76 n = read(fdf, buf, m);
82 r = loop_write(fdt, buf, (size_t) n, false);
89 if (max_bytes != (off_t) -1) {
90 assert(max_bytes >= n);
98 static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
99 _cleanup_free_ char *target = NULL;
106 r = readlinkat_malloc(df, from, &target);
110 if (symlinkat(target, dt, to) < 0)
113 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
119 static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
120 _cleanup_close_ int fdf = -1, fdt = -1;
127 fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
131 fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
135 r = copy_bytes(fdf, fdt, (off_t) -1);
141 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
144 if (fchmod(fdt, st->st_mode & 07777) < 0)
158 static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
165 r = mkfifoat(dt, to, st->st_mode & 07777);
169 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
172 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
178 static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
185 r = mknodat(dt, to, st->st_mode, st->st_rdev);
189 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
192 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
198 static int fd_copy_directory(
201 const struct stat *st,
204 dev_t original_device,
207 _cleanup_close_ int fdf = -1, fdt = -1;
208 _cleanup_closedir_ DIR *d = NULL;
217 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
219 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
226 r = mkdirat(dt, to, st->st_mode & 07777);
229 else if (errno == EEXIST && merge)
234 fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
241 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
244 if (fchmod(fdt, st->st_mode & 07777) < 0)
248 FOREACH_DIRENT(de, d, return -errno) {
252 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
257 if (buf.st_dev != original_device)
260 if (S_ISREG(buf.st_mode))
261 q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
262 else if (S_ISDIR(buf.st_mode))
263 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
264 else if (S_ISLNK(buf.st_mode))
265 q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
266 else if (S_ISFIFO(buf.st_mode))
267 q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
268 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
269 q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
273 if (q == -EEXIST && merge)
283 int copy_tree(const char *from, const char *to, bool merge) {
289 if (lstat(from, &st) < 0)
292 if (S_ISREG(st.st_mode))
293 return fd_copy_regular(AT_FDCWD, from, &st, AT_FDCWD, to);
294 else if (S_ISDIR(st.st_mode))
295 return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
296 else if (S_ISLNK(st.st_mode))
297 return fd_copy_symlink(AT_FDCWD, from, &st, AT_FDCWD, to);
298 else if (S_ISFIFO(st.st_mode))
299 return fd_copy_fifo(AT_FDCWD, from, &st, AT_FDCWD, to);
300 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
301 return fd_copy_node(AT_FDCWD, from, &st, AT_FDCWD, to);
306 int copy_tree_fd(int dirfd, const char *to, bool merge) {
313 if (fstat(dirfd, &st) < 0)
316 if (!S_ISDIR(st.st_mode))
319 return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
322 int copy_file_fd(const char *from, int fdt) {
323 _cleanup_close_ int fdf = -1;
328 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
332 return copy_bytes(fdf, fdt, (off_t) -1);
335 int copy_file(const char *from, const char *to, int flags, mode_t mode) {
341 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
345 r = copy_file_fd(from, fdt);
352 if (close(fdt) < 0) {