X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Fcopy.c;h=f22a9409918a84c0eb3fb28f94db26077b844615;hp=073b7279b43ac3b96e95a2d71b3fdd2eb2216b54;hb=d7c7c334f56edab8cfc102b657366277a65738cf;hpb=19f3934057d20c63f4c95791312038a41b4666d0 diff --git a/src/shared/copy.c b/src/shared/copy.c index 073b7279b..f22a94099 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -19,29 +19,70 @@ along with systemd; If not, see . ***/ +#include + #include "util.h" +#include "btrfs-util.h" #include "copy.h" -int copy_bytes(int fdf, int fdt) { +int copy_bytes(int fdf, int fdt, off_t max_bytes) { + bool try_sendfile = true; + assert(fdf >= 0); assert(fdt >= 0); for (;;) { - char buf[PIPE_BUF]; - ssize_t n, k; - - n = read(fdf, buf, sizeof(buf)); - if (n < 0) - return -errno; - if (n == 0) - break; - - errno = 0; - k = loop_write(fdt, buf, n, false); - if (k < 0) - return k; - if (k != n) - return errno ? -errno : -EIO; + size_t m = PIPE_BUF; + ssize_t n; + + if (max_bytes != (off_t) -1) { + + if (max_bytes <= 0) + return -EFBIG; + + if ((off_t) m > max_bytes) + m = (size_t) max_bytes; + } + + /* First try sendfile(), unless we already tried */ + if (try_sendfile) { + + n = sendfile(fdt, fdf, NULL, m); + if (n < 0) { + if (errno != EINVAL && errno != ENOSYS) + return -errno; + + try_sendfile = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Succcess! */ + goto next; + } + + /* As a fallback just copy bits by hand */ + { + char buf[m]; + int r; + + n = read(fdf, buf, m); + if (n < 0) + return -errno; + if (n == 0) /* EOF */ + break; + + r = loop_write(fdt, buf, n, false); + if (r < 0) + return r; + + } + + next: + if (max_bytes != (off_t) -1) { + assert(max_bytes >= n); + max_bytes -= n; + } } return 0; @@ -59,12 +100,8 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int if (r < 0) return r; - if (symlinkat(target, dt, to) < 0) { - if (errno == EEXIST) - return 0; - + if (symlinkat(target, dt, to) < 0) return -errno; - } if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) return -errno; @@ -85,14 +122,10 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int return -errno; fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777); - if (fdt < 0) { - if (errno == EEXIST) - return 0; - + if (fdt < 0) return -errno; - } - r = copy_bytes(fdf, fdt); + r = copy_bytes(fdf, fdt, (off_t) -1); if (r < 0) { unlinkat(dt, to, 0); return r; @@ -123,12 +156,8 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, assert(to); r = mkfifoat(dt, to, st->st_mode & 07777); - if (r < 0) { - if (errno == EEXIST) - return 0; - + if (r < 0) return -errno; - } if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) r = -errno; @@ -147,12 +176,8 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, assert(to); r = mknodat(dt, to, st->st_mode, st->st_rdev); - if (r < 0) { - if (errno == EEXIST) - return 0; - + if (r < 0) return -errno; - } if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) r = -errno; @@ -163,20 +188,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) { +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) @@ -186,7 +219,7 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in r = mkdirat(dt, to, st->st_mode & 07777); if (r >= 0) created = true; - else if (errno == EEXIST) + else if (errno == EEXIST && merge) created = false; else return -errno; @@ -195,6 +228,8 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in if (fdt < 0) return -errno; + r = 0; + if (created) { if (fchown(fdt, st->st_uid, st->st_gid) < 0) r = -errno; @@ -203,7 +238,6 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in r = -errno; } - r = 0; FOREACH_DIRENT(de, d, return -errno) { struct stat buf; int q; @@ -219,7 +253,7 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in if (S_ISREG(buf.st_mode)) q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name); else if (S_ISDIR(buf.st_mode)) - q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device); + q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge); else if (S_ISLNK(buf.st_mode)) q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); else if (S_ISFIFO(buf.st_mode)) @@ -229,6 +263,9 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in else q = -ENOTSUP; + if (q == -EEXIST && merge) + q = 0; + if (q < 0) r = q; } @@ -236,7 +273,7 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in return r; } -int copy_tree(const char *from, const char *to) { +int copy_tree(const char *from, const char *to, bool merge) { struct stat st; assert(from); @@ -248,7 +285,7 @@ int copy_tree(const char *from, const char *to) { if (S_ISREG(st.st_mode)) return fd_copy_regular(AT_FDCWD, from, &st, AT_FDCWD, to); else if (S_ISDIR(st.st_mode)) - return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev); + return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); else if (S_ISLNK(st.st_mode)) return fd_copy_symlink(AT_FDCWD, from, &st, AT_FDCWD, to); else if (S_ISFIFO(st.st_mode)) @@ -259,34 +296,55 @@ int copy_tree(const char *from, const char *to) { return -ENOTSUP; } -int copy_file(const char *from, const char *to, int flags, mode_t mode) { - _cleanup_close_ int fdf = -1, fdt = -1; - int r; +int copy_tree_fd(int dirfd, const char *to, bool merge) { - assert(from); + 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) { + _cleanup_close_ int fdf = -1; + + assert(from); + assert(fdt >= 0); + fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fdf < 0) return -errno; + return copy_bytes(fdf, fdt, (off_t) -1); +} + +int copy_file(const char *from, const char *to, int flags, mode_t mode) { + int fdt, r; + + assert(from); + assert(to); + fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode); if (fdt < 0) return -errno; - r = copy_bytes(fdf, fdt); + r = copy_file_fd(from, fdt); if (r < 0) { + close(fdt); unlink(to); return r; } - r = close(fdt); - fdt = -1; - - if (r < 0) { - r = -errno; - unlink(to); - return r; + if (close(fdt) < 0) { + unlink_noerrno(to); + return -errno; } return 0;