]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/copy.c
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"
30 #define COPY_BUFFER_SIZE (16*1024)
32 int copy_bytes(int fdf
, int fdt
, uint64_t max_bytes
, bool try_reflink
) {
33 bool try_sendfile
= true, try_splice
= true;
39 /* Try btrfs reflinks first. */
41 max_bytes
== (uint64_t) -1 &&
42 lseek(fdf
, 0, SEEK_CUR
) == 0 &&
43 lseek(fdt
, 0, SEEK_CUR
) == 0) {
45 r
= btrfs_reflink(fdf
, fdt
);
51 size_t m
= COPY_BUFFER_SIZE
;
54 if (max_bytes
!= (uint64_t) -1) {
59 if ((uint64_t) m
> max_bytes
)
60 m
= (size_t) max_bytes
;
63 /* First try sendfile(), unless we already tried */
66 n
= sendfile(fdt
, fdf
, NULL
, m
);
68 if (errno
!= EINVAL
&& errno
!= ENOSYS
)
72 /* use fallback below */
73 } else if (n
== 0) /* EOF */
80 /* The try splice, unless we already tried */
82 n
= splice(fdf
, NULL
, fdt
, NULL
, m
, 0);
84 if (errno
!= EINVAL
&& errno
!= ENOSYS
)
88 /* use fallback below */
89 } else if (n
== 0) /* EOF */
96 /* As a fallback just copy bits by hand */
100 n
= read(fdf
, buf
, m
);
103 if (n
== 0) /* EOF */
106 r
= loop_write(fdt
, buf
, (size_t) n
, false);
112 if (max_bytes
!= (uint64_t) -1) {
113 assert(max_bytes
>= (uint64_t) n
);
121 static int fd_copy_symlink(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
122 _cleanup_free_
char *target
= NULL
;
129 r
= readlinkat_malloc(df
, from
, &target
);
133 if (symlinkat(target
, dt
, to
) < 0)
136 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
142 static int fd_copy_regular(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
143 _cleanup_close_
int fdf
= -1, fdt
= -1;
144 struct timespec ts
[2];
151 fdf
= openat(df
, from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
155 fdt
= openat(dt
, to
, O_WRONLY
|O_CREAT
|O_EXCL
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
, st
->st_mode
& 07777);
159 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, true);
165 if (fchown(fdt
, st
->st_uid
, st
->st_gid
) < 0)
168 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
173 (void) futimens(fdt
, ts
);
175 (void) copy_xattr(fdf
, fdt
);
188 static int fd_copy_fifo(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
195 r
= mkfifoat(dt
, to
, st
->st_mode
& 07777);
199 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
202 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
208 static int fd_copy_node(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
215 r
= mknodat(dt
, to
, st
->st_mode
, st
->st_rdev
);
219 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
222 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
228 static int fd_copy_directory(
231 const struct stat
*st
,
234 dev_t original_device
,
237 _cleanup_close_
int fdf
= -1, fdt
= -1;
238 _cleanup_closedir_
DIR *d
= NULL
;
247 fdf
= openat(df
, from
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
249 fdf
= fcntl(df
, F_DUPFD_CLOEXEC
, 3);
256 r
= mkdirat(dt
, to
, st
->st_mode
& 07777);
259 else if (errno
== EEXIST
&& merge
)
264 fdt
= openat(dt
, to
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
271 struct timespec ut
[2] = {
276 if (fchown(fdt
, st
->st_uid
, st
->st_gid
) < 0)
279 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
282 (void) futimens(fdt
, ut
);
283 (void) copy_xattr(dirfd(d
), fdt
);
286 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
290 if (STR_IN_SET(de
->d_name
, ".", ".."))
293 if (fstatat(dirfd(d
), de
->d_name
, &buf
, AT_SYMLINK_NOFOLLOW
) < 0) {
298 if (buf
.st_dev
!= original_device
)
301 if (S_ISREG(buf
.st_mode
))
302 q
= fd_copy_regular(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
303 else if (S_ISDIR(buf
.st_mode
))
304 q
= fd_copy_directory(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, original_device
, merge
);
305 else if (S_ISLNK(buf
.st_mode
))
306 q
= fd_copy_symlink(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
307 else if (S_ISFIFO(buf
.st_mode
))
308 q
= fd_copy_fifo(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
309 else if (S_ISBLK(buf
.st_mode
) || S_ISCHR(buf
.st_mode
))
310 q
= fd_copy_node(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
314 if (q
== -EEXIST
&& merge
)
324 int copy_tree_at(int fdf
, const char *from
, int fdt
, const char *to
, bool merge
) {
330 if (fstatat(fdf
, from
, &st
, AT_SYMLINK_NOFOLLOW
) < 0)
333 if (S_ISREG(st
.st_mode
))
334 return fd_copy_regular(fdf
, from
, &st
, fdt
, to
);
335 else if (S_ISDIR(st
.st_mode
))
336 return fd_copy_directory(fdf
, from
, &st
, fdt
, to
, st
.st_dev
, merge
);
337 else if (S_ISLNK(st
.st_mode
))
338 return fd_copy_symlink(fdf
, from
, &st
, fdt
, to
);
339 else if (S_ISFIFO(st
.st_mode
))
340 return fd_copy_fifo(fdf
, from
, &st
, fdt
, to
);
341 else if (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
))
342 return fd_copy_node(fdf
, from
, &st
, fdt
, to
);
347 int copy_tree(const char *from
, const char *to
, bool merge
) {
348 return copy_tree_at(AT_FDCWD
, from
, AT_FDCWD
, to
, merge
);
351 int copy_directory_fd(int dirfd
, const char *to
, bool merge
) {
358 if (fstat(dirfd
, &st
) < 0)
361 if (!S_ISDIR(st
.st_mode
))
364 return fd_copy_directory(dirfd
, NULL
, &st
, AT_FDCWD
, to
, st
.st_dev
, merge
);
367 int copy_file_fd(const char *from
, int fdt
, bool try_reflink
) {
368 _cleanup_close_
int fdf
= -1;
374 fdf
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
378 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, try_reflink
);
380 (void) copy_times(fdf
, fdt
);
381 (void) copy_xattr(fdf
, fdt
);
386 int copy_file(const char *from
, const char *to
, int flags
, mode_t mode
, unsigned chattr_flags
) {
392 RUN_WITH_UMASK(0000) {
393 fdt
= open(to
, flags
|O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_NOCTTY
, mode
);
398 if (chattr_flags
!= 0)
399 (void) chattr_fd(fdt
, chattr_flags
, (unsigned) -1);
401 r
= copy_file_fd(from
, fdt
, true);
408 if (close(fdt
) < 0) {
416 int copy_file_atomic(const char *from
, const char *to
, mode_t mode
, bool replace
, unsigned chattr_flags
) {
417 _cleanup_free_
char *t
= NULL
;
423 r
= tempfn_random(to
, NULL
, &t
);
427 r
= copy_file(from
, t
, O_NOFOLLOW
|O_EXCL
, mode
, chattr_flags
);
432 r
= renameat(AT_FDCWD
, t
, AT_FDCWD
, to
);
436 r
= rename_noreplace(AT_FDCWD
, t
, AT_FDCWD
, to
);
438 (void) unlink_noerrno(t
);
445 int copy_times(int fdf
, int fdt
) {
446 struct timespec ut
[2];
453 if (fstat(fdf
, &st
) < 0)
459 if (futimens(fdt
, ut
) < 0)
462 if (fd_getcrtime(fdf
, &crtime
) >= 0)
463 (void) fd_setcrtime(fdt
, crtime
);
468 int copy_xattr(int fdf
, int fdt
) {
469 _cleanup_free_
char *bufa
= NULL
, *bufb
= NULL
;
470 size_t sza
= 100, szb
= 100;
480 n
= flistxattr(fdf
, bufa
, sza
);
498 assert(l
< (size_t) n
);
500 if (startswith(p
, "user.")) {
509 m
= fgetxattr(fdf
, p
, bufb
, szb
);
511 if (errno
== ERANGE
) {
520 if (fsetxattr(fdt
, p
, bufb
, m
, 0) < 0)