]>
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"
26 #include "chattr-util.h"
28 #include "dirent-util.h"
33 #include "string-util.h"
35 #include "umask-util.h"
37 #include "xattr-util.h"
39 #define COPY_BUFFER_SIZE (16*1024)
41 int copy_bytes(int fdf
, int fdt
, uint64_t max_bytes
, bool try_reflink
) {
42 bool try_sendfile
= true, try_splice
= true;
48 /* Try btrfs reflinks first. */
50 max_bytes
== (uint64_t) -1 &&
51 lseek(fdf
, 0, SEEK_CUR
) == 0 &&
52 lseek(fdt
, 0, SEEK_CUR
) == 0) {
54 r
= btrfs_reflink(fdf
, fdt
);
56 return 0; /* we copied the whole thing, hence hit EOF, return 0 */
60 size_t m
= COPY_BUFFER_SIZE
;
63 if (max_bytes
!= (uint64_t) -1) {
66 return 1; /* return > 0 if we hit the max_bytes limit */
68 if ((uint64_t) m
> max_bytes
)
69 m
= (size_t) max_bytes
;
72 /* First try sendfile(), unless we already tried */
75 n
= sendfile(fdt
, fdf
, NULL
, m
);
77 if (errno
!= EINVAL
&& errno
!= ENOSYS
)
81 /* use fallback below */
82 } else if (n
== 0) /* EOF */
89 /* The try splice, unless we already tried */
91 n
= splice(fdf
, NULL
, fdt
, NULL
, m
, 0);
93 if (errno
!= EINVAL
&& errno
!= ENOSYS
)
97 /* use fallback below */
98 } else if (n
== 0) /* EOF */
105 /* As a fallback just copy bits by hand */
109 n
= read(fdf
, buf
, m
);
112 if (n
== 0) /* EOF */
115 r
= loop_write(fdt
, buf
, (size_t) n
, false);
121 if (max_bytes
!= (uint64_t) -1) {
122 assert(max_bytes
>= (uint64_t) n
);
127 return 0; /* return 0 if we hit EOF earlier than the size limit */
130 static int fd_copy_symlink(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
131 _cleanup_free_
char *target
= NULL
;
138 r
= readlinkat_malloc(df
, from
, &target
);
142 if (symlinkat(target
, dt
, to
) < 0)
145 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
151 static int fd_copy_regular(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
152 _cleanup_close_
int fdf
= -1, fdt
= -1;
153 struct timespec ts
[2];
160 fdf
= openat(df
, from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
164 fdt
= openat(dt
, to
, O_WRONLY
|O_CREAT
|O_EXCL
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
, st
->st_mode
& 07777);
168 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, true);
174 if (fchown(fdt
, st
->st_uid
, st
->st_gid
) < 0)
177 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
182 (void) futimens(fdt
, ts
);
184 (void) copy_xattr(fdf
, fdt
);
197 static int fd_copy_fifo(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
204 r
= mkfifoat(dt
, to
, st
->st_mode
& 07777);
208 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
211 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
217 static int fd_copy_node(int df
, const char *from
, const struct stat
*st
, int dt
, const char *to
) {
224 r
= mknodat(dt
, to
, st
->st_mode
, st
->st_rdev
);
228 if (fchownat(dt
, to
, st
->st_uid
, st
->st_gid
, AT_SYMLINK_NOFOLLOW
) < 0)
231 if (fchmodat(dt
, to
, st
->st_mode
& 07777, 0) < 0)
237 static int fd_copy_directory(
240 const struct stat
*st
,
243 dev_t original_device
,
246 _cleanup_close_
int fdf
= -1, fdt
= -1;
247 _cleanup_closedir_
DIR *d
= NULL
;
256 fdf
= openat(df
, from
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
258 fdf
= fcntl(df
, F_DUPFD_CLOEXEC
, 3);
265 r
= mkdirat(dt
, to
, st
->st_mode
& 07777);
268 else if (errno
== EEXIST
&& merge
)
273 fdt
= openat(dt
, to
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
280 struct timespec ut
[2] = {
285 if (fchown(fdt
, st
->st_uid
, st
->st_gid
) < 0)
288 if (fchmod(fdt
, st
->st_mode
& 07777) < 0)
291 (void) futimens(fdt
, ut
);
292 (void) copy_xattr(dirfd(d
), fdt
);
295 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
299 if (STR_IN_SET(de
->d_name
, ".", ".."))
302 if (fstatat(dirfd(d
), de
->d_name
, &buf
, AT_SYMLINK_NOFOLLOW
) < 0) {
307 if (buf
.st_dev
!= original_device
)
310 if (S_ISREG(buf
.st_mode
))
311 q
= fd_copy_regular(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
312 else if (S_ISDIR(buf
.st_mode
))
313 q
= fd_copy_directory(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
, original_device
, merge
);
314 else if (S_ISLNK(buf
.st_mode
))
315 q
= fd_copy_symlink(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
316 else if (S_ISFIFO(buf
.st_mode
))
317 q
= fd_copy_fifo(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
318 else if (S_ISBLK(buf
.st_mode
) || S_ISCHR(buf
.st_mode
))
319 q
= fd_copy_node(dirfd(d
), de
->d_name
, &buf
, fdt
, de
->d_name
);
323 if (q
== -EEXIST
&& merge
)
333 int copy_tree_at(int fdf
, const char *from
, int fdt
, const char *to
, bool merge
) {
339 if (fstatat(fdf
, from
, &st
, AT_SYMLINK_NOFOLLOW
) < 0)
342 if (S_ISREG(st
.st_mode
))
343 return fd_copy_regular(fdf
, from
, &st
, fdt
, to
);
344 else if (S_ISDIR(st
.st_mode
))
345 return fd_copy_directory(fdf
, from
, &st
, fdt
, to
, st
.st_dev
, merge
);
346 else if (S_ISLNK(st
.st_mode
))
347 return fd_copy_symlink(fdf
, from
, &st
, fdt
, to
);
348 else if (S_ISFIFO(st
.st_mode
))
349 return fd_copy_fifo(fdf
, from
, &st
, fdt
, to
);
350 else if (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
))
351 return fd_copy_node(fdf
, from
, &st
, fdt
, to
);
356 int copy_tree(const char *from
, const char *to
, bool merge
) {
357 return copy_tree_at(AT_FDCWD
, from
, AT_FDCWD
, to
, merge
);
360 int copy_directory_fd(int dirfd
, const char *to
, bool merge
) {
367 if (fstat(dirfd
, &st
) < 0)
370 if (!S_ISDIR(st
.st_mode
))
373 return fd_copy_directory(dirfd
, NULL
, &st
, AT_FDCWD
, to
, st
.st_dev
, merge
);
376 int copy_file_fd(const char *from
, int fdt
, bool try_reflink
) {
377 _cleanup_close_
int fdf
= -1;
383 fdf
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
387 r
= copy_bytes(fdf
, fdt
, (uint64_t) -1, try_reflink
);
389 (void) copy_times(fdf
, fdt
);
390 (void) copy_xattr(fdf
, fdt
);
395 int copy_file(const char *from
, const char *to
, int flags
, mode_t mode
, unsigned chattr_flags
) {
401 RUN_WITH_UMASK(0000) {
402 fdt
= open(to
, flags
|O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_NOCTTY
, mode
);
407 if (chattr_flags
!= 0)
408 (void) chattr_fd(fdt
, chattr_flags
, (unsigned) -1);
410 r
= copy_file_fd(from
, fdt
, true);
417 if (close(fdt
) < 0) {
425 int copy_file_atomic(const char *from
, const char *to
, mode_t mode
, bool replace
, unsigned chattr_flags
) {
426 _cleanup_free_
char *t
= NULL
;
432 r
= tempfn_random(to
, NULL
, &t
);
436 r
= copy_file(from
, t
, O_NOFOLLOW
|O_EXCL
, mode
, chattr_flags
);
441 r
= renameat(AT_FDCWD
, t
, AT_FDCWD
, to
);
445 r
= rename_noreplace(AT_FDCWD
, t
, AT_FDCWD
, to
);
447 (void) unlink_noerrno(t
);
454 int copy_times(int fdf
, int fdt
) {
455 struct timespec ut
[2];
462 if (fstat(fdf
, &st
) < 0)
468 if (futimens(fdt
, ut
) < 0)
471 if (fd_getcrtime(fdf
, &crtime
) >= 0)
472 (void) fd_setcrtime(fdt
, crtime
);
477 int copy_xattr(int fdf
, int fdt
) {
478 _cleanup_free_
char *bufa
= NULL
, *bufb
= NULL
;
479 size_t sza
= 100, szb
= 100;
489 n
= flistxattr(fdf
, bufa
, sza
);
507 assert(l
< (size_t) n
);
509 if (startswith(p
, "user.")) {
518 m
= fgetxattr(fdf
, p
, bufb
, szb
);
520 if (errno
== ERANGE
) {
529 if (fsetxattr(fdt
, p
, bufb
, m
, 0) < 0)