summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/tmpfiles.d.xml12
-rw-r--r--src/shared/copy.c37
-rw-r--r--src/shared/copy.h2
-rw-r--r--src/tmpfiles/tmpfiles.c49
4 files changed, 59 insertions, 41 deletions
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index 6b2753549..f3cc2ea50 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -225,7 +225,17 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<varlistentry>
<term><varname>C</varname></term>
- <listitem><para>Recursively copy a file or directory, if the destination files or directories don't exist yet.</para></listitem>
+ <listitem><para>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.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 073b7279b..867e49bf8 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 8fb057fe8..1d5e0adc5 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 c6c8ce8fe..cbad78aea 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);