]>
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>
25 #include "btrfs-util.h"
27 #include "dirent-util.h"
31 #include "string-util.h"
35 #define COPY_BUFFER_SIZE (16*1024)
37 int copy_bytes(int fdf
, int fdt
, uint64_t max_bytes
, bool try_reflink
) {
38 bool try_sendfile
= true, try_splice
= true;
44 /* Try btrfs reflinks first. */
46 max_bytes
== (uint64_t) -1 &&
47 lseek(fdf
, 0, SEEK_CUR
) == 0 &&
48 lseek(fdt
, 0, SEEK_CUR
) == 0) {
50 r
= btrfs_reflink(fdf
, fdt
);
52 return 0; /* we copied the whole thing, hence hit EOF, return 0 */
56 size_t m
= COPY_BUFFER_SIZE
;
59 if (max_bytes
!= (uint64_t) -1) {
62 return 1; /* return > 0 if we hit the max_bytes limit */
64 if ((uint64_t) m
> max_bytes
)
65 m
= (size_t) max_bytes
;
68 /* First try sendfile(), unless we already tried */
71 n
= sendfile(fdt
, fdf
, NULL
, m
);
73 if (errno
!= EINVAL
&& errno
!= ENOSYS
)
77 /* use fallback below */
78 } else if (n
== 0) /* EOF */
85 /* The try splice, unless we already tried */
87 n
= splice(fdf
, NULL
, fdt
, NULL
, m
, 0);
89 if (errno
!= EINVAL
&& errno
!= ENOSYS
)
93 /* use fallback below */
94 } else if (n
== 0) /* EOF */
101 /* As a fallback just copy bits by hand */
105 n
= read(fdf
, buf
, m
);
108 if (n
== 0) /* EOF */
111 r
= loop_write(fdt
, buf
, (size_t) n
, false);
117 if (max_bytes
!= (uint64_t) -1) {
118 assert(max_bytes
>= (uint64_t) n
);
123 return 0; /* return 0 if we hit EOF earlier than the size limit */
126 static int fd_copy_symlink(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
127 _cleanup_free_
char *target
= NULL
;
134 r
= readlinkat_malloc(df
, from
, &target
);
138 if (symlinkat(target
, dt
, to
) < 0)
141 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
147 static int fd_copy_regular(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
148 _cleanup_close_
int fdf
= -1, fdt
= -1;
149 struct timespec ts
[2];
156 fdf
= openat(df
, from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
160 fdt
= openat(dt
, to
, O_WRONLY
|O_CREAT
|O_EXCL
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
, st
->st_mode
& 07777);
164 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, true);
170 if (fchown(fdt
, st
->st_uid
, st
->st_gid
) < 0)
173 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
178 (void) futimens(fdt
, ts
);
180 (void) copy_xattr(fdf
, fdt
);
193 static int fd_copy_fifo(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
200 r
= mkfifoat(dt
, to
, st
->st_mode
& 07777);
204 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
207 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
213 static int fd_copy_node(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
220 r
= mknodat(dt
, to
, st
->st_mode
, st
->st_rdev
);
224 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
227 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
233 static int fd_copy_directory(
236 const struct stat
*st
,
239 dev_t original_device
,
242 _cleanup_close_
int fdf
= -1, fdt
= -1;
243 _cleanup_closedir_
DIR *d
= NULL
;
252 fdf
= openat(df
, from
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
254 fdf
= fcntl(df
, F_DUPFD_CLOEXEC
, 3);
261 r
= mkdirat(dt
, to
, st
->st_mode
& 07777);
264 else if (errno
== EEXIST
&& merge
)
269 fdt
= openat(dt
, to
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
276 struct timespec ut
[2] = {
281 if (fchown(fdt
, st
->st_uid
, st
->st_gid
) < 0)
284 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
287 (void) futimens(fdt
, ut
);
288 (void) copy_xattr(dirfd(d
), fdt
);
291 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
295 if (STR_IN_SET(de
->d_name
, ".", ".."))
298 if (fstatat(dirfd(d
), de
->d_name
, &buf
, AT_SYMLINK_NOFOLLOW
) < 0) {
303 if (buf
.st_dev
!= original_device
)
306 if (S_ISREG(buf
.st_mode
))
307 q
= fd_copy_regular(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
308 else if (S_ISDIR(buf
.st_mode
))
309 q
= fd_copy_directory(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, original_device
, merge
);
310 else if (S_ISLNK(buf
.st_mode
))
311 q
= fd_copy_symlink(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
312 else if (S_ISFIFO(buf
.st_mode
))
313 q
= fd_copy_fifo(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
314 else if (S_ISBLK(buf
.st_mode
) || S_ISCHR(buf
.st_mode
))
315 q
= fd_copy_node(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
319 if (q
== -EEXIST
&& merge
)
329 int copy_tree_at(int fdf
, const char *from
, int fdt
, const char *to
, bool merge
) {
335 if (fstatat(fdf
, from
, &st
, AT_SYMLINK_NOFOLLOW
) < 0)
338 if (S_ISREG(st
.st_mode
))
339 return fd_copy_regular(fdf
, from
, &st
, fdt
, to
);
340 else if (S_ISDIR(st
.st_mode
))
341 return fd_copy_directory(fdf
, from
, &st
, fdt
, to
, st
.st_dev
, merge
);
342 else if (S_ISLNK(st
.st_mode
))
343 return fd_copy_symlink(fdf
, from
, &st
, fdt
, to
);
344 else if (S_ISFIFO(st
.st_mode
))
345 return fd_copy_fifo(fdf
, from
, &st
, fdt
, to
);
346 else if (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
))
347 return fd_copy_node(fdf
, from
, &st
, fdt
, to
);
352 int copy_tree(const char *from
, const char *to
, bool merge
) {
353 return copy_tree_at(AT_FDCWD
, from
, AT_FDCWD
, to
, merge
);
356 int copy_directory_fd(int dirfd
, const char *to
, bool merge
) {
363 if (fstat(dirfd
, &st
) < 0)
366 if (!S_ISDIR(st
.st_mode
))
369 return fd_copy_directory(dirfd
, NULL
, &st
, AT_FDCWD
, to
, st
.st_dev
, merge
);
372 int copy_file_fd(const char *from
, int fdt
, bool try_reflink
) {
373 _cleanup_close_
int fdf
= -1;
379 fdf
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
383 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, try_reflink
);
385 (void) copy_times(fdf
, fdt
);
386 (void) copy_xattr(fdf
, fdt
);
391 int copy_file(const char *from
, const char *to
, int flags
, mode_t mode
, unsigned chattr_flags
) {
397 RUN_WITH_UMASK(0000) {
398 fdt
= open(to
, flags
|O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_NOCTTY
, mode
);
403 if (chattr_flags
!= 0)
404 (void) chattr_fd(fdt
, chattr_flags
, (unsigned) -1);
406 r
= copy_file_fd(from
, fdt
, true);
413 if (close(fdt
) < 0) {
421 int copy_file_atomic(const char *from
, const char *to
, mode_t mode
, bool replace
, unsigned chattr_flags
) {
422 _cleanup_free_
char *t
= NULL
;
428 r
= tempfn_random(to
, NULL
, &t
);
432 r
= copy_file(from
, t
, O_NOFOLLOW
|O_EXCL
, mode
, chattr_flags
);
437 r
= renameat(AT_FDCWD
, t
, AT_FDCWD
, to
);
441 r
= rename_noreplace(AT_FDCWD
, t
, AT_FDCWD
, to
);
443 (void) unlink_noerrno(t
);
450 int copy_times(int fdf
, int fdt
) {
451 struct timespec ut
[2];
458 if (fstat(fdf
, &st
) < 0)
464 if (futimens(fdt
, ut
) < 0)
467 if (fd_getcrtime(fdf
, &crtime
) >= 0)
468 (void) fd_setcrtime(fdt
, crtime
);
473 int copy_xattr(int fdf
, int fdt
) {
474 _cleanup_free_
char *bufa
= NULL
, *bufb
= NULL
;
475 size_t sza
= 100, szb
= 100;
485 n
= flistxattr(fdf
, bufa
, sza
);
503 assert(l
< (size_t) n
);
505 if (startswith(p
, "user.")) {
514 m
= fgetxattr(fdf
, p
, bufb
, szb
);
516 if (errno
== ERANGE
) {
525 if (fsetxattr(fdt
, p
, bufb
, m
, 0) < 0)