From e156347e0480d3eee4877463297149ae96e34f8c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 19 Jun 2014 19:36:08 +0200 Subject: tmpfiles: make sure "C" doesn't copy anything if the destination already exists Previously it would recursively copy the entire tree in, and descend into subdirectories even if the destination already exists. Let's do what the documentation says and not do that. If files down the tree shall be copied too, they should get their own "C" lines. --- man/tmpfiles.d.xml | 12 +++++++++++- src/shared/copy.c | 37 ++++++++++++------------------------- src/shared/copy.h | 2 +- src/tmpfiles/tmpfiles.c | 49 +++++++++++++++++++++++++++++++++++-------------- 4 files changed, 59 insertions(+), 41 deletions(-) diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 6b2753549c..f3cc2ea50c 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -225,7 +225,17 @@ L /tmp/foobar - - - - /dev/null C - Recursively copy a file or directory, if the destination files or directories don't exist yet. + Recursively + copy a file or directory, if + the destination files or + directories don't exist + yet. Note that this command + will not descend into + subdirectories if the + destination directory already + exists, instead the entire + copy operation is + skipped. diff --git a/src/shared/copy.c b/src/shared/copy.c index 073b7279b4..867e49bf89 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -59,12 +59,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,12 +81,8 @@ 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); if (r < 0) { @@ -123,12 +115,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 +135,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,7 +147,7 @@ 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; @@ -186,7 +170,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; @@ -219,7 +203,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 +213,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 +223,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 +235,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)) diff --git a/src/shared/copy.h b/src/shared/copy.h index 8fb057fe86..1d5e0adc53 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -22,5 +22,5 @@ ***/ int copy_file(const char *from, const char *to, int flags, mode_t mode); -int copy_tree(const char *from, const char *to); +int copy_tree(const char *from, const char *to, bool merge); int copy_bytes(int fdf, int fdt); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index c6c8ce8fee..cbad78aea5 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -667,10 +667,29 @@ static int create_item(Item *i) { break; case COPY_FILES: - r = copy_tree(i->argument, i->path); + r = copy_tree(i->argument, i->path, false); if (r < 0) { - log_error("Failed to copy files to %s: %s", i->path, strerror(-r)); - return r; + struct stat a, b; + + if (r != -EEXIST) { + log_error("Failed to copy files to %s: %s", i->path, strerror(-r)); + return -r; + } + + if (stat(i->argument, &a) < 0) { + log_error("stat(%s) failed: %m", i->argument); + return -errno; + } + + if (stat(i->path, &b) < 0) { + log_error("stat(%s) failed: %m", i->path); + return -errno; + } + + if ((a.st_mode ^ b.st_mode) & S_IFMT) { + log_debug("Can't copy to %s, file exists already and is of different type", i->path); + return 0; + } } r = item_set_perms(i, i->path); @@ -694,19 +713,21 @@ static int create_item(Item *i) { r = mkdir_label(i->path, i->mode); } - if (r < 0 && r != -EEXIST) { - log_error("Failed to create directory %s: %s", i->path, strerror(-r)); - return r; - } + if (r < 0) { + if (r != -EEXIST) { + log_error("Failed to create directory %s: %s", i->path, strerror(-r)); + return r; + } - if (stat(i->path, &st) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (stat(i->path, &st) < 0) { + log_error("stat(%s) failed: %m", i->path); + return -errno; + } - if (!S_ISDIR(st.st_mode)) { - log_error("%s is not a directory.", i->path); - return -EEXIST; + if (!S_ISDIR(st.st_mode)) { + log_debug("%s already exists and is not a directory.", i->path); + return 0; + } } r = item_set_perms(i, i->path); -- cgit v1.2.3-54-g00ecf