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.
This commit is contained in:
Lennart Poettering 2014-06-19 19:36:08 +02:00
parent 19f3934057
commit e156347e04
4 changed files with 59 additions and 41 deletions

View 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>

View 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))

View 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);

View 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);