]>
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
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/sendfile.h>
30 #include <sys/xattr.h>
34 #include "alloc-util.h"
35 #include "btrfs-util.h"
36 #include "chattr-util.h"
38 #include "dirent-util.h"
45 #include "string-util.h"
47 #include "time-util.h"
48 #include "umask-util.h"
49 #include "user-util.h"
50 #include "xattr-util.h"
52 #define COPY_BUFFER_SIZE (16*1024u)
54 static ssize_t
try_copy_file_range(int fd_in
, loff_t
*off_in
,
55 int fd_out
, loff_t
*off_out
,
64 r
= copy_file_range(fd_in
, off_in
, fd_out
, off_out
, len
, flags
);
65 if (_unlikely_(have
< 0))
66 have
= r
>= 0 || errno
!= ENOSYS
;
73 int copy_bytes(int fdf
, int fdt
, uint64_t max_bytes
, CopyFlags copy_flags
) {
74 bool try_cfr
= true, try_sendfile
= true, try_splice
= true;
76 size_t m
= SSIZE_MAX
; /* that is the maximum that sendfile and c_f_r accept */
81 /* Try btrfs reflinks first. */
82 if ((copy_flags
& COPY_REFLINK
) &&
83 max_bytes
== (uint64_t) -1 &&
84 lseek(fdf
, 0, SEEK_CUR
) == 0 &&
85 lseek(fdt
, 0, SEEK_CUR
) == 0) {
87 r
= btrfs_reflink(fdf
, fdt
);
89 return 0; /* we copied the whole thing, hence hit EOF, return 0 */
95 if (max_bytes
!= (uint64_t) -1) {
97 return 1; /* return > 0 if we hit the max_bytes limit */
103 /* First try copy_file_range(), unless we already tried */
105 n
= try_copy_file_range(fdf
, NULL
, fdt
, NULL
, m
, 0u);
107 if (!IN_SET(n
, -EINVAL
, -ENOSYS
, -EXDEV
, -EBADF
))
111 /* use fallback below */
112 } else if (n
== 0) /* EOF */
119 /* First try sendfile(), unless we already tried */
121 n
= sendfile(fdt
, fdf
, NULL
, m
);
123 if (!IN_SET(errno
, EINVAL
, ENOSYS
))
126 try_sendfile
= false;
127 /* use fallback below */
128 } else if (n
== 0) /* EOF */
135 /* Then try splice, unless we already tried */
137 n
= splice(fdf
, NULL
, fdt
, NULL
, m
, 0);
139 if (!IN_SET(errno
, EINVAL
, ENOSYS
))
143 /* use fallback below */
144 } else if (n
== 0) /* EOF */
151 /* As a fallback just copy bits by hand */
153 uint8_t buf
[MIN(m
, COPY_BUFFER_SIZE
)];
155 n
= read(fdf
, buf
, sizeof buf
);
158 if (n
== 0) /* EOF */
161 r
= loop_write(fdt
, buf
, (size_t) n
, false);
167 if (max_bytes
!= (uint64_t) -1) {
168 assert(max_bytes
>= (uint64_t) n
);
171 /* sendfile accepts at most SSIZE_MAX-offset bytes to copy,
172 * so reduce our maximum by the amount we already copied,
173 * but don't go below our copy buffer size, unless we are
174 * close the limit of bytes we are allowed to copy. */
175 m
= MAX(MIN(COPY_BUFFER_SIZE
, max_bytes
), m
- n
);
178 return 0; /* return 0 if we hit EOF earlier than the size limit */
181 static int fd_copy_symlink(
184 const struct stat
*st
,
189 CopyFlags copy_flags
) {
191 _cleanup_free_
char *target
= NULL
;
198 r
= readlinkat_malloc(df
, from
, &target
);
202 if (symlinkat(target
, dt
, to
) < 0)
206 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
207 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
,
208 AT_SYMLINK_NOFOLLOW
) < 0)
214 static int fd_copy_regular(
217 const struct stat
*st
,
222 CopyFlags copy_flags
) {
224 _cleanup_close_
int fdf
= -1, fdt
= -1;
225 struct timespec ts
[2];
232 fdf
= openat(df
, from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
236 fdt
= openat(dt
, to
, O_WRONLY
|O_CREAT
|O_EXCL
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
, st
->st_mode
& 07777);
240 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, copy_flags
);
242 (void) unlinkat(dt
, to
, 0);
247 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
248 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
) < 0)
251 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
256 (void) futimens(fdt
, ts
);
257 (void) copy_xattr(fdf
, fdt
);
264 (void) unlinkat(dt
, to
, 0);
270 static int fd_copy_fifo(
273 const struct stat
*st
,
278 CopyFlags copy_flags
) {
285 r
= mkfifoat(dt
, to
, st
->st_mode
& 07777);
290 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
291 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
,
292 AT_SYMLINK_NOFOLLOW
) < 0)
295 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
301 static int fd_copy_node(
304 const struct stat
*st
,
309 CopyFlags copy_flags
) {
316 r
= mknodat(dt
, to
, st
->st_mode
, st
->st_rdev
);
321 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
322 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
,
323 AT_SYMLINK_NOFOLLOW
) < 0)
326 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
332 static int fd_copy_directory(
335 const struct stat
*st
,
338 dev_t original_device
,
341 CopyFlags copy_flags
) {
343 _cleanup_close_
int fdf
= -1, fdt
= -1;
344 _cleanup_closedir_
DIR *d
= NULL
;
353 fdf
= openat(df
, from
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
355 fdf
= fcntl(df
, F_DUPFD_CLOEXEC
, 3);
364 r
= mkdirat(dt
, to
, st
->st_mode
& 07777);
367 else if (errno
== EEXIST
&& (copy_flags
& COPY_MERGE
))
372 fdt
= openat(dt
, to
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
378 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
382 if (dot_or_dot_dot(de
->d_name
))
385 if (fstatat(dirfd(d
), de
->d_name
, &buf
, AT_SYMLINK_NOFOLLOW
) < 0) {
390 if (buf
.st_dev
!= original_device
)
393 if (S_ISREG(buf
.st_mode
))
394 q
= fd_copy_regular(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
395 else if (S_ISDIR(buf
.st_mode
))
396 q
= fd_copy_directory(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, original_device
, override_uid
, override_gid
, copy_flags
);
397 else if (S_ISLNK(buf
.st_mode
))
398 q
= fd_copy_symlink(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
399 else if (S_ISFIFO(buf
.st_mode
))
400 q
= fd_copy_fifo(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
401 else if (S_ISBLK(buf
.st_mode
) || S_ISCHR(buf
.st_mode
) || S_ISSOCK(buf
.st_mode
))
402 q
= fd_copy_node(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, override_uid
, override_gid
, copy_flags
);
406 if (q
== -EEXIST
&& (copy_flags
& COPY_MERGE
))
414 struct timespec ut
[2] = {
420 uid_is_valid(override_uid
) ? override_uid
: st
->st_uid
,
421 gid_is_valid(override_gid
) ? override_gid
: st
->st_gid
) < 0)
424 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
427 (void) copy_xattr(dirfd(d
), fdt
);
428 (void) futimens(fdt
, ut
);
434 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
) {
440 if (fstatat(fdf
, from
, &st
, AT_SYMLINK_NOFOLLOW
) < 0)
443 if (S_ISREG(st
.st_mode
))
444 return fd_copy_regular(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
445 else if (S_ISDIR(st
.st_mode
))
446 return fd_copy_directory(fdf
, from
, &st
, fdt
, to
, st
.st_dev
, override_uid
, override_gid
, copy_flags
);
447 else if (S_ISLNK(st
.st_mode
))
448 return fd_copy_symlink(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
449 else if (S_ISFIFO(st
.st_mode
))
450 return fd_copy_fifo(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
451 else if (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
) || S_ISSOCK(st
.st_mode
))
452 return fd_copy_node(fdf
, from
, &st
, fdt
, to
, override_uid
, override_gid
, copy_flags
);
457 int copy_tree(const char *from
, const char *to
, uid_t override_uid
, gid_t override_gid
, CopyFlags copy_flags
) {
458 return copy_tree_at(AT_FDCWD
, from
, AT_FDCWD
, to
, override_uid
, override_gid
, copy_flags
);
461 int copy_directory_fd(int dirfd
, const char *to
, CopyFlags copy_flags
) {
467 if (fstat(dirfd
, &st
) < 0)
470 if (!S_ISDIR(st
.st_mode
))
473 return fd_copy_directory(dirfd
, NULL
, &st
, AT_FDCWD
, to
, st
.st_dev
, UID_INVALID
, GID_INVALID
, copy_flags
);
476 int copy_directory(const char *from
, const char *to
, CopyFlags copy_flags
) {
482 if (lstat(from
, &st
) < 0)
485 if (!S_ISDIR(st
.st_mode
))
488 return fd_copy_directory(AT_FDCWD
, from
, &st
, AT_FDCWD
, to
, st
.st_dev
, UID_INVALID
, GID_INVALID
, copy_flags
);
491 int copy_file_fd(const char *from
, int fdt
, CopyFlags copy_flags
) {
492 _cleanup_close_
int fdf
= -1;
498 fdf
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
502 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, copy_flags
);
504 (void) copy_times(fdf
, fdt
);
505 (void) copy_xattr(fdf
, fdt
);
510 int copy_file(const char *from
, const char *to
, int flags
, mode_t mode
, unsigned chattr_flags
, CopyFlags copy_flags
) {
516 RUN_WITH_UMASK(0000) {
517 fdt
= open(to
, flags
|O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_NOCTTY
, mode
);
522 if (chattr_flags
!= 0)
523 (void) chattr_fd(fdt
, chattr_flags
, (unsigned) -1);
525 r
= copy_file_fd(from
, fdt
, copy_flags
);
532 if (close(fdt
) < 0) {
540 int copy_file_atomic(const char *from
, const char *to
, mode_t mode
, unsigned chattr_flags
, CopyFlags copy_flags
) {
541 _cleanup_free_
char *t
= NULL
;
547 r
= tempfn_random(to
, NULL
, &t
);
551 r
= copy_file(from
, t
, O_NOFOLLOW
|O_EXCL
, mode
, chattr_flags
, copy_flags
);
555 if (copy_flags
& COPY_REPLACE
) {
556 r
= renameat(AT_FDCWD
, t
, AT_FDCWD
, to
);
560 r
= rename_noreplace(AT_FDCWD
, t
, AT_FDCWD
, to
);
569 int copy_times(int fdf
, int fdt
) {
570 struct timespec ut
[2];
577 if (fstat(fdf
, &st
) < 0)
583 if (futimens(fdt
, ut
) < 0)
586 if (fd_getcrtime(fdf
, &crtime
) >= 0)
587 (void) fd_setcrtime(fdt
, crtime
);
592 int copy_xattr(int fdf
, int fdt
) {
593 _cleanup_free_
char *bufa
= NULL
, *bufb
= NULL
;
594 size_t sza
= 100, szb
= 100;
604 n
= flistxattr(fdf
, bufa
, sza
);
622 assert(l
< (size_t) n
);
624 if (startswith(p
, "user.")) {
633 m
= fgetxattr(fdf
, p
, bufb
, szb
);
635 if (errno
== ERANGE
) {
644 if (fsetxattr(fdt
, p
, bufb
, m
, 0) < 0)