chiark / gitweb /
tmpfiles: make sure "C" doesn't copy anything if the destination already exists
authorLennart Poettering <lennart@poettering.net>
Thu, 19 Jun 2014 17:36:08 +0000 (19:36 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 19 Jun 2014 17:36:08 +0000 (19:36 +0200)
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
src/shared/copy.c
src/shared/copy.h
src/tmpfiles/tmpfiles.c

index 6b27535..f3cc2ea 100644 (file)
@@ -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>
index 073b727..867e49b 100644 (file)
@@ -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))
index 8fb057f..1d5e0ad 100644 (file)
@@ -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);
index c6c8ce8..cbad78a 100644 (file)
@@ -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);