From: Lennart Poettering Date: Fri, 26 Dec 2014 16:00:39 +0000 (+0100) Subject: copy: try top copy atime/time/xattrs when copying files X-Git-Tag: v219~809 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=e6bd041c973c1f4074c3268b23c380ede0d64f19 copy: try top copy atime/time/xattrs when copying files --- diff --git a/src/shared/copy.c b/src/shared/copy.c index 0c2cdc8d9..92f6e1e11 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -20,6 +20,7 @@ ***/ #include +#include #include "util.h" #include "btrfs-util.h" @@ -145,6 +146,9 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int if (fchmod(fdt, st->st_mode & 07777) < 0) r = -errno; + (void) copy_times(fdf, fdt); + (void) copy_xattr(fdf, fdt); + q = close(fdt); fdt = -1; @@ -244,6 +248,9 @@ static int fd_copy_directory( if (fchmod(fdt, st->st_mode & 07777) < 0) r = -errno; + + (void) copy_times(fdf, fdt); + (void) copy_xattr(fdf, fdt); } FOREACH_DIRENT(de, d, return -errno) { @@ -326,6 +333,7 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) { int copy_file_fd(const char *from, int fdt, bool try_reflink) { _cleanup_close_ int fdf = -1; + int r; assert(from); assert(fdt >= 0); @@ -334,7 +342,12 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) { if (fdf < 0) return -errno; - return copy_bytes(fdf, fdt, (off_t) -1, try_reflink); + r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink); + + (void) copy_times(fdf, fdt); + (void) copy_xattr(fdf, fdt); + + return r; } int copy_file(const char *from, const char *to, int flags, mode_t mode) { @@ -361,3 +374,91 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode) { return 0; } + +int copy_times(int fdf, int fdt) { + struct timespec ut[2]; + struct stat st; + usec_t crtime; + + assert(fdf >= 0); + assert(fdt >= 0); + + if (fstat(fdf, &st) < 0) + return -errno; + + ut[0] = st.st_atim; + ut[1] = st.st_mtim; + + if (futimens(fdt, ut) < 0) + return -errno; + + if (fd_getcrtime(fdf, &crtime) >= 0) + (void) fd_setcrtime(fdt, crtime); + + return 0; +} + +int copy_xattr(int fdf, int fdt) { + _cleanup_free_ char *bufa = NULL, *bufb = NULL; + size_t sza = 100, szb = 100; + ssize_t n; + int ret = 0; + const char *p; + + for (;;) { + bufa = malloc(sza); + if (!bufa) + return -ENOMEM; + + n = flistxattr(fdf, bufa, sza); + if (n == 0) + return 0; + if (n > 0) + break; + if (errno != ERANGE) + return -errno; + + sza *= 2; + + free(bufa); + bufa = NULL; + } + + p = bufa; + while (n > 0) { + size_t l; + + l = strlen(p); + assert(l < (size_t) n); + + if (startswith(p, "user.")) { + ssize_t m; + + if (!bufb) { + bufb = malloc(szb); + if (!bufb) + return -ENOMEM; + } + + m = fgetxattr(fdf, p, bufb, szb); + if (m < 0) { + if (errno == ERANGE) { + szb *= 2; + free(bufb); + bufb = NULL; + continue; + } + + return -errno; + } + + if (fsetxattr(fdt, p, bufb, m, 0) < 0) + ret = -errno; + } + + p += l + 1; + n -= l + 1; + } + + return ret; +} diff --git a/src/shared/copy.h b/src/shared/copy.h index 714addf4c..6d725ef26 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -30,3 +30,5 @@ int copy_tree(const char *from, const char *to, bool merge); int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); int copy_directory_fd(int dirfd, const char *to, bool merge); int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink); +int copy_times(int fdf, int fdt); +int copy_xattr(int fdf, int fdt);