copy.c revision c622fbdb8d37796c35ab1e08240a18b4222e5c9f
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer This file is part of systemd.
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer Copyright 2014 Lennart Poettering
0d6e798a784ef0ba6b95512e4453067b2f84a91aHarald Hoyer systemd is free software; you can redistribute it and/or modify it
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer under the terms of the GNU Lesser General Public License as published by
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier the Free Software Foundation; either version 2.1 of the License, or
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier (at your option) any later version.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier systemd is distributed in the hope that it will be useful, but
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier WITHOUT ANY WARRANTY; without even the implied warranty of
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier Lesser General Public License for more details.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier You should have received a copy of the GNU Lesser General Public License
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalierint copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier bool try_sendfile = true, try_splice = true;
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Try btrfs reflinks first. */
c6a77179a4097df355f0f04b8f3260c76b5e515cRonny Chevalier /* First try sendfile(), unless we already tried */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* use fallback below */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier } else if (n == 0) /* EOF */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Success! */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* The try splice, unless we already tried */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* use fallback below */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier } else if (n == 0) /* EOF */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier else if (n > 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* Success! */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier /* As a fallback just copy bits by hand */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (n == 0) /* EOF */
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = loop_write(fdt, buf, (size_t) n, false);
8a8332f77e61d41f3bb28b8f929ed41e0ffaf721Zbigniew Jędrzejewski-Szmek assert(max_bytes >= (uint64_t) n);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek r = readlinkat_malloc(df, from, &target);
739d81ddd005fae2bb82edce5b8a6173c7c48b34Zbigniew Jędrzejewski-Szmek if (symlinkat(target, dt, to) < 0)
b8667ee4162cd2510363602b417cecede9fd2ccaZbigniew Jędrzejewski-Szmek if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fchown(fdt, st->st_uid, st->st_gid) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalierstatic int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier r = mknodat(dt, to, st->st_mode, st->st_rdev);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fchown(fdt, st->st_uid, st->st_gid) < 0)
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
889a90422dd47284dffa32b9234a6e58991b000cRonny Chevalier q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fd_copy_regular(fdf, from, &st, fdt, to);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fd_copy_symlink(fdf, from, &st, fdt, to);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint copy_tree(const char *from, const char *to, bool merge) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint copy_directory_fd(int dirfd, const char *to, bool merge) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint copy_file_fd(const char *from, int fdt, bool try_reflink) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyerint copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (r < 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer _cleanup_free_ char *bufa = NULL, *bufb = NULL;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer const char *p;
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer while (n > 0) {
898720b7e9cf3bdf7a93e435cbed5dd6942ecf9bHarald Hoyer if (m < 0) {