]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/copy.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
15 #include <sys/sendfile.h>
17 #include <sys/xattr.h>
21 #include "alloc-util.h"
22 #include "btrfs-util.h"
23 #include "chattr-util.h"
25 #include "dirent-util.h"
32 #include "string-util.h"
34 #include "time-util.h"
35 #include "umask-util.h"
36 #include "user-util.h"
37 #include "xattr-util.h"
39 #define COPY_BUFFER_SIZE (16*1024u)
41 static ssize_t
try_copy_file_range(int fd_in
, loff_t
*off_in
,
42 int fd_out
, loff_t
*off_out
,
51 r
= copy_file_range(fd_in
, off_in
, fd_out
, off_out
, len
, flags
);
52 if (_unlikely_(have
< 0))
53 have
= r
>= 0 || errno
!= ENOSYS
;
60 int copy_bytes(int fdf
, int fdt
, uint64_t max_bytes
, CopyFlags copy_flags
) {
61 bool try_cfr
= true, try_sendfile
= true, try_splice
= true;
63 size_t m
= SSIZE_MAX
; /* that is the maximum that sendfile and c_f_r accept */
68 /* Try btrfs reflinks first. */
69 if ((copy_flags
& COPY_REFLINK
) &&
70 max_bytes
== (uint64_t) -1 &&
71 lseek(fdf
, 0, SEEK_CUR
) == 0 &&
72 lseek(fdt
, 0, SEEK_CUR
) == 0) {
74 r
= btrfs_reflink(fdf
, fdt
);
76 return 0; /* we copied the whole thing, hence hit EOF, return 0 */
82 if (max_bytes
!= (uint64_t) -1) {
84 return 1; /* return > 0 if we hit the max_bytes limit */
90 /* First try copy_file_range(), unless we already tried */
92 n
= try_copy_file_range(fdf
, NULL
, fdt
, NULL
, m
, 0u);
94 if (!IN_SET(n
, -EINVAL
, -ENOSYS
, -EXDEV
, -EBADF
))
98 /* use fallback below */
99 } else if (n
== 0) /* EOF */
106 /* First try sendfile(), unless we already tried */
108 n
= sendfile(fdt
, fdf
, NULL
, m
);
110 if (!IN_SET(errno
, EINVAL
, ENOSYS
))
113 try_sendfile
= false;
114 /* use fallback below */
115 } else if (n
== 0) /* EOF */
122 /* Then try splice, unless we already tried */
124 n
= splice(fdf
, NULL
, fdt
, NULL
, m
, 0);
126 if (!IN_SET(errno
, EINVAL
, ENOSYS
))
130 /* use fallback below */
131 } else if (n
== 0) /* EOF */
138 /* As a fallback just copy bits by hand */
140 uint8_t buf
[MIN(m
, COPY_BUFFER_SIZE
)];
142 n
= read(fdf
, buf
, sizeof buf
);
145 if (n
== 0) /* EOF */
148 r
= loop_write(fdt
, buf
, (size_t) n
, false);
154 if (max_bytes
!= (uint64_t) -1) {
155 assert(max_bytes
>= (uint64_t) n
);
158 /* sendfile accepts at most SSIZE_MAX-offset bytes to copy,
159 * so reduce our maximum by the amount we already copied,
160 * but don't go below our copy buffer size, unless we are
161 * close the limit of bytes we are allowed to copy. */
162 m
= MAX(MIN(COPY_BUFFER_SIZE
, max_bytes
), m
- n
);
165 return 0; /* return 0 if we hit EOF earlier than the size limit */
168 static int fd_copy_symlink(
171 const struct stat
*st
,
176 CopyFlags copy_flags
) {
178 _cleanup_free_
char *target
= NULL
;
185 r
= readlinkat_malloc(df
, from
, &target
);
189 if (symlinkat(target
, dt
, to
) < 0)
193 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
194 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
,
195 AT_SYMLINK_NOFOLLOW
) < 0)
201 static int fd_copy_regular(
204 const struct stat
*st
,
209 CopyFlags copy_flags
) {
211 _cleanup_close_
int fdf
= -1, fdt
= -1;
212 struct timespec ts
[2];
219 fdf
= openat(df
, from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
223 fdt
= openat(dt
, to
, O_WRONLY
|O_CREAT
|O_EXCL
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
, st
->st_mode
& 07777);
227 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, copy_flags
);
229 (void) unlinkat(dt
, to
, 0);
234 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
235 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
) < 0)
238 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
243 (void) futimens(fdt
, ts
);
244 (void) copy_xattr(fdf
, fdt
);
251 (void) unlinkat(dt
, to
, 0);
257 static int fd_copy_fifo(
260 const struct stat
*st
,
265 CopyFlags copy_flags
) {
272 r
= mkfifoat(dt
, to
, st
->st_mode
& 07777);
277 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
278 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
,
279 AT_SYMLINK_NOFOLLOW
) < 0)
282 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
288 static int fd_copy_node(
291 const struct stat
*st
,
296 CopyFlags copy_flags
) {
303 r
= mknodat(dt
, to
, st
->st_mode
, st
->st_rdev
);
308 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
309 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
,
310 AT_SYMLINK_NOFOLLOW
) < 0)
313 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
319 static int fd_copy_directory(
322 const struct stat
*st
,
325 dev_t original_device
,
328 CopyFlags copy_flags
) {
330 _cleanup_close_
int fdf
= -1, fdt
= -1;
331 _cleanup_closedir_
DIR *d
= NULL
;
340 fdf
= openat(df
, from
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
342 fdf
= fcntl(df
, F_DUPFD_CLOEXEC
, 3);
351 r
= mkdirat(dt
, to
, st
->st_mode
& 07777);
354 else if (errno
== EEXIST
&& (copy_flags
& COPY_MERGE
))
359 fdt
= openat(dt
, to
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
365 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
369 if (dot_or_dot_dot(de
->d_name
))
372 if (fstatat(dirfd(d
), de
->d_name
, &buf
, AT_SYMLINK_NOFOLLOW
) < 0) {
377 if (buf
.st_dev
!= original_device
)
380 if (S_ISREG(buf
.st_mode
))
381 q
= fd_copy_regular(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
382 else if (S_ISDIR(buf
.st_mode
))
383 q
= fd_copy_directory(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, original_device
, override_uid
, override_gid
, copy_flags
);
384 else if (S_ISLNK(buf
.st_mode
))
385 q
= fd_copy_symlink(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
386 else if (S_ISFIFO(buf
.st_mode
))
387 q
= fd_copy_fifo(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
388 else if (S_ISBLK(buf
.st_mode
) || S_ISCHR(buf
.st_mode
) || S_ISSOCK(buf
.st_mode
))
389 q
= fd_copy_node(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
393 if (q
== -EEXIST
&& (copy_flags
& COPY_MERGE
))
401 struct timespec ut
[2] = {
407 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
408 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
) < 0)
411 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
414 (void) copy_xattr(dirfd(d
), fdt
);
415 (void) futimens(fdt
, ut
);
421 int copy_tree_at(int fdf
, const char *from
, int fdt
, const char *to
, uid_t override_uid
, gid_t override_gid
, CopyFlags copy_flags
) {
427 if (fstatat(fdf
, from
, &st
, AT_SYMLINK_NOFOLLOW
) < 0)
430 if (S_ISREG(st
.st_mode
))
431 return fd_copy_regular(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
432 else if (S_ISDIR(st
.st_mode
))
433 return fd_copy_directory(fdf
, from
, &st
, fdt
, to
, st
.st_dev
, override_uid
, override_gid
, copy_flags
);
434 else if (S_ISLNK(st
.st_mode
))
435 return fd_copy_symlink(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
436 else if (S_ISFIFO(st
.st_mode
))
437 return fd_copy_fifo(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
438 else if (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
) || S_ISSOCK(st
.st_mode
))
439 return fd_copy_node(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
444 int copy_tree(const char *from
, const char *to
, uid_t override_uid
, gid_t override_gid
, CopyFlags copy_flags
) {
445 return copy_tree_at(AT_FDCWD
, from
, AT_FDCWD
, to
, override_uid
, override_gid
, copy_flags
);
448 int copy_directory_fd(int dirfd
, const char *to
, CopyFlags copy_flags
) {
454 if (fstat(dirfd
, &st
) < 0)
457 if (!S_ISDIR(st
.st_mode
))
460 return fd_copy_directory(dirfd
, NULL
, &st
, AT_FDCWD
, to
, st
.st_dev
, UID_INVALID
, GID_INVALID
, copy_flags
);
463 int copy_directory(const char *from
, const char *to
, CopyFlags copy_flags
) {
469 if (lstat(from
, &st
) < 0)
472 if (!S_ISDIR(st
.st_mode
))
475 return fd_copy_directory(AT_FDCWD
, from
, &st
, AT_FDCWD
, to
, st
.st_dev
, UID_INVALID
, GID_INVALID
, copy_flags
);
478 int copy_file_fd(const char *from
, int fdt
, CopyFlags copy_flags
) {
479 _cleanup_close_
int fdf
= -1;
485 fdf
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
489 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, copy_flags
);
491 (void) copy_times(fdf
, fdt
);
492 (void) copy_xattr(fdf
, fdt
);
497 int copy_file(const char *from
, const char *to
, int flags
, mode_t mode
, unsigned chattr_flags
, CopyFlags copy_flags
) {
503 RUN_WITH_UMASK(0000) {
504 fdt
= open(to
, flags
|O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_NOCTTY
, mode
);
509 if (chattr_flags
!= 0)
510 (void) chattr_fd(fdt
, chattr_flags
, (unsigned) -1);
512 r
= copy_file_fd(from
, fdt
, copy_flags
);
519 if (close(fdt
) < 0) {
527 int copy_file_atomic(const char *from
, const char *to
, mode_t mode
, unsigned chattr_flags
, CopyFlags copy_flags
) {
528 _cleanup_free_
char *t
= NULL
;
534 r
= tempfn_random(to
, NULL
, &t
);
538 r
= copy_file(from
, t
, O_NOFOLLOW
|O_EXCL
, mode
, chattr_flags
, copy_flags
);
542 if (copy_flags
& COPY_REPLACE
) {
543 r
= renameat(AT_FDCWD
, t
, AT_FDCWD
, to
);
547 r
= rename_noreplace(AT_FDCWD
, t
, AT_FDCWD
, to
);
556 int copy_times(int fdf
, int fdt
) {
557 struct timespec ut
[2];
564 if (fstat(fdf
, &st
) < 0)
570 if (futimens(fdt
, ut
) < 0)
573 if (fd_getcrtime(fdf
, &crtime
) >= 0)
574 (void) fd_setcrtime(fdt
, crtime
);
579 int copy_xattr(int fdf
, int fdt
) {
580 _cleanup_free_
char *bufa
= NULL
, *bufb
= NULL
;
581 size_t sza
= 100, szb
= 100;
591 n
= flistxattr(fdf
, bufa
, sza
);
609 assert(l
< (size_t) n
);
611 if (startswith(p
, "user.")) {
620 m
= fgetxattr(fdf
, p
, bufb
, szb
);
622 if (errno
== ERANGE
) {
631 if (fsetxattr(fdt
, p
, bufb
, m
, 0) < 0)