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>
23 #include <sys/xattr.h>
26 #include "btrfs-util.h"
29 #define COPY_BUFFER_SIZE (16*1024)
31 int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
32 bool try_sendfile = true;
38 /* Try btrfs reflinks first. */
39 if (try_reflink && max_bytes == (off_t) -1) {
40 r = btrfs_reflink(fdf, fdt);
46 size_t m = COPY_BUFFER_SIZE;
49 if (max_bytes != (off_t) -1) {
54 if ((off_t) m > max_bytes)
55 m = (size_t) max_bytes;
58 /* First try sendfile(), unless we already tried */
61 n = sendfile(fdt, fdf, NULL, m);
63 if (errno != EINVAL && errno != ENOSYS)
67 /* use fallback below */
68 } else if (n == 0) /* EOF */
75 /* As a fallback just copy bits by hand */
79 n = read(fdf, buf, m);
85 r = loop_write(fdt, buf, (size_t) n, false);
91 if (max_bytes != (off_t) -1) {
92 assert(max_bytes >= n);
100 static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
101 _cleanup_free_ char *target = NULL;
108 r = readlinkat_malloc(df, from, &target);
112 if (symlinkat(target, dt, to) < 0)
115 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
121 static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
122 _cleanup_close_ int fdf = -1, fdt = -1;
129 fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
133 fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
137 r = copy_bytes(fdf, fdt, (off_t) -1, true);
143 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
146 if (fchmod(fdt, st->st_mode & 07777) < 0)
149 (void) copy_times(fdf, fdt);
150 (void) copy_xattr(fdf, fdt);
163 static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
170 r = mkfifoat(dt, to, st->st_mode & 07777);
174 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
177 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
183 static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
190 r = mknodat(dt, to, st->st_mode, st->st_rdev);
194 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
197 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
203 static int fd_copy_directory(
206 const struct stat *st,
209 dev_t original_device,
212 _cleanup_close_ int fdf = -1, fdt = -1;
213 _cleanup_closedir_ DIR *d = NULL;
222 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
224 fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
231 r = mkdirat(dt, to, st->st_mode & 07777);
234 else if (errno == EEXIST && merge)
239 fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
246 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
249 if (fchmod(fdt, st->st_mode & 07777) < 0)
252 (void) copy_times(fdf, fdt);
253 (void) copy_xattr(fdf, fdt);
256 FOREACH_DIRENT(de, d, return -errno) {
260 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
265 if (buf.st_dev != original_device)
268 if (S_ISREG(buf.st_mode))
269 q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
270 else if (S_ISDIR(buf.st_mode))
271 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
272 else if (S_ISLNK(buf.st_mode))
273 q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
274 else if (S_ISFIFO(buf.st_mode))
275 q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
276 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
277 q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
281 if (q == -EEXIST && merge)
291 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
297 if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
300 if (S_ISREG(st.st_mode))
301 return fd_copy_regular(fdf, from, &st, fdt, to);
302 else if (S_ISDIR(st.st_mode))
303 return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
304 else if (S_ISLNK(st.st_mode))
305 return fd_copy_symlink(fdf, from, &st, fdt, to);
306 else if (S_ISFIFO(st.st_mode))
307 return fd_copy_fifo(fdf, from, &st, fdt, to);
308 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
309 return fd_copy_node(fdf, from, &st, fdt, to);
314 int copy_tree(const char *from, const char *to, bool merge) {
315 return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
318 int copy_directory_fd(int dirfd, const char *to, bool merge) {
325 if (fstat(dirfd, &st) < 0)
328 if (!S_ISDIR(st.st_mode))
331 return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
334 int copy_file_fd(const char *from, int fdt, bool try_reflink) {
335 _cleanup_close_ int fdf = -1;
341 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
345 r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
347 (void) copy_times(fdf, fdt);
348 (void) copy_xattr(fdf, fdt);
353 int copy_file(const char *from, const char *to, int flags, mode_t mode) {
359 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
363 r = copy_file_fd(from, fdt, true);
370 if (close(fdt) < 0) {
378 int copy_times(int fdf, int fdt) {
379 struct timespec ut[2];
386 if (fstat(fdf, &st) < 0)
392 if (futimens(fdt, ut) < 0)
395 if (fd_getcrtime(fdf, &crtime) >= 0)
396 (void) fd_setcrtime(fdt, crtime);
401 int copy_xattr(int fdf, int fdt) {
402 _cleanup_free_ char *bufa = NULL, *bufb = NULL;
403 size_t sza = 100, szb = 100;
413 n = flistxattr(fdf, bufa, sza);
432 assert(l < (size_t) n);
434 if (startswith(p, "user.")) {
443 m = fgetxattr(fdf, p, bufb, szb);
445 if (errno == ERANGE) {
455 if (fsetxattr(fdt, p, bufb, m, 0) < 0)