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