X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Fcopy.c;h=b4a85c7bff0a12f76835a7cce3f867be37951b30;hp=a863246b2bf47c5ed821f2603f8ddb9f0ea3c38a;hb=bc854dc7cd051e1e5a6ebcca8084b07168051c6c;hpb=cda134ab1eac84f874aacf8e885a07112a7fd5ce diff --git a/src/shared/copy.c b/src/shared/copy.c index a863246b2..b4a85c7bf 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -22,14 +22,23 @@ #include #include "util.h" +#include "btrfs-util.h" #include "copy.h" -int copy_bytes(int fdf, int fdt, off_t max_bytes) { +int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { bool try_sendfile = true; + int r; assert(fdf >= 0); assert(fdt >= 0); + /* Try btrfs reflinks first. */ + if (try_reflink && max_bytes == (off_t) -1) { + r = btrfs_reflink(fdf, fdt); + if (r >= 0) + return r; + } + for (;;) { size_t m = PIPE_BUF; ssize_t n; @@ -37,7 +46,7 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes) { if (max_bytes != (off_t) -1) { if (max_bytes <= 0) - return -E2BIG; + return -EFBIG; if ((off_t) m > max_bytes) m = (size_t) max_bytes; @@ -63,7 +72,6 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes) { /* As a fallback just copy bits by hand */ { char buf[m]; - ssize_t k; n = read(fdf, buf, m); if (n < 0) @@ -71,13 +79,9 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes) { if (n == 0) /* EOF */ break; - errno = 0; - k = loop_write(fdt, buf, n, false); - if (k < 0) - return k; - if (k != n) - return errno ? -errno : -EIO; - + r = loop_write(fdt, buf, (size_t) n, false); + if (r < 0) + return r; } next: @@ -127,7 +131,7 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int if (fdt < 0) return -errno; - r = copy_bytes(fdf, fdt, (off_t) -1); + r = copy_bytes(fdf, fdt, (off_t) -1, true); if (r < 0) { unlinkat(dt, to, 0); return r; @@ -190,20 +194,28 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, return r; } -static int fd_copy_directory(int df, const char *from, const struct stat *st, int dt, const char *to, dev_t original_device, bool merge) { +static int fd_copy_directory( + int df, + const char *from, + const struct stat *st, + int dt, + const char *to, + dev_t original_device, + bool merge) { + _cleanup_close_ int fdf = -1, fdt = -1; _cleanup_closedir_ DIR *d = NULL; struct dirent *de; bool created; int r; - assert(from); assert(st); assert(to); - fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fdf < 0) - return -errno; + if (from) + fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + else + fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); d = fdopendir(fdf); if (!d) @@ -290,7 +302,23 @@ int copy_tree(const char *from, const char *to, bool merge) { return -ENOTSUP; } -int copy_file_fd(const char *from, int fdt) { +int copy_tree_fd(int dirfd, const char *to, bool merge) { + + struct stat st; + + assert(dirfd >= 0); + assert(to); + + if (fstat(dirfd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); +} + +int copy_file_fd(const char *from, int fdt, bool try_reflink) { _cleanup_close_ int fdf = -1; assert(from); @@ -300,7 +328,7 @@ int copy_file_fd(const char *from, int fdt) { if (fdf < 0) return -errno; - return copy_bytes(fdf, fdt, (off_t) -1); + return copy_bytes(fdf, fdt, (off_t) -1, try_reflink); } int copy_file(const char *from, const char *to, int flags, mode_t mode) { @@ -313,7 +341,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode) { if (fdt < 0) return -errno; - r = copy_file_fd(from, fdt); + r = copy_file_fd(from, fdt, true); if (r < 0) { close(fdt); unlink(to);