summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2014-06-10 23:02:40 +0200
committerLennart Poettering <lennart@poettering.net>2014-06-10 23:02:40 +0200
commit849958d1ba3533c953fad46d4d41c0ec6e48316d (patch)
tree4002001e577cf348fe5df3fc4aa359bd21428a52
parentcde684a2932d3c8cbb9b3374aec27a1c20ba75fa (diff)
tmpfiles: add new "C" line for copying files or directories
-rw-r--r--Makefile.am4
-rw-r--r--man/tmpfiles.d.xml11
-rw-r--r--src/nspawn/nspawn.c3
-rw-r--r--src/shared/copy.c292
-rw-r--r--src/shared/copy.h25
-rw-r--r--src/shared/util.c62
-rw-r--r--src/shared/util.h3
-rw-r--r--src/tmpfiles/tmpfiles.c35
8 files changed, 372 insertions, 63 deletions
diff --git a/Makefile.am b/Makefile.am
index 4ff9f5a4fd..c67f0634b1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -808,7 +808,9 @@ libsystemd_shared_la_SOURCES = \
src/shared/async.c \
src/shared/async.h \
src/shared/eventfd-util.c \
- src/shared/eventfd-util.h
+ src/shared/eventfd-util.h \
+ src/shared/copy.c \
+ src/shared/copy.h
nodist_libsystemd_shared_la_SOURCES = \
src/shared/errno-from-name.h \
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index e54f1ba5c3..20ed803a52 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -184,6 +184,11 @@ L /tmp/foobar - - - - /dev/null</programlisting>
</varlistentry>
<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>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>m</varname></term>
<listitem><para>If the
specified file path exists,
@@ -446,8 +451,10 @@ r! /tmp/.X[0-9]*-lock</programlisting>
<varname>f</varname>, <varname>F</varname>,
and <varname>w</varname> may be used to
specify a short string that is written to the
- file, suffixed by a newline. Ignored for all
- other lines.</para>
+ file, suffixed by a newline. For
+ <varname>C</varname> specifies the source file
+ or directory. Ignored for all other
+ lines.</para>
</refsect2>
</refsect1>
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 26ac1bf7df..867cf19267 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -88,6 +88,7 @@
#include "blkid-util.h"
#include "gpt.h"
#include "siphash24.h"
+#include "copy.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
@@ -773,7 +774,7 @@ static int setup_resolv_conf(const char *dest) {
/* We don't really care for the results of this really. If it
* fails, it fails, but meh... */
- copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW);
+ copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644);
return 0;
}
diff --git a/src/shared/copy.c b/src/shared/copy.c
new file mode 100644
index 0000000000..4dfc2f3fca
--- /dev/null
+++ b/src/shared/copy.c
@@ -0,0 +1,292 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "copy.h"
+
+static int stream_bytes(int fdf, int fdt) {
+ assert(fdf >= 0);
+ assert(fdt >= 0);
+
+ for (;;) {
+ char buf[PIPE_BUF];
+ ssize_t n, k;
+
+ n = read(fdf, buf, sizeof(buf));
+ if (n < 0)
+ return -errno;
+ if (n == 0)
+ break;
+
+ errno = 0;
+ k = loop_write(fdt, buf, n, false);
+ if (k < 0)
+ return k;
+ if (k != n)
+ return errno ? -errno : -EIO;
+ }
+
+ return 0;
+}
+
+static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ _cleanup_free_ char *target = NULL;
+ int r;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ r = readlinkat_malloc(df, from, &target);
+ if (r < 0)
+ return r;
+
+ if (symlinkat(target, dt, to) < 0) {
+ if (errno == EEXIST)
+ return 0;
+
+ return -errno;
+ }
+
+ if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ _cleanup_close_ int fdf = -1, fdt = -1;
+ int r, q;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fdf < 0)
+ 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;
+
+ return -errno;
+ }
+
+ r = stream_bytes(fdf, fdt);
+ if (r < 0) {
+ unlinkat(dt, to, 0);
+ return r;
+ }
+
+ if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ r = -errno;
+
+ if (fchmod(fdt, st->st_mode & 07777) < 0)
+ r = -errno;
+
+ q = close(fdt);
+ fdt = -1;
+
+ if (q < 0) {
+ r = -errno;
+ unlinkat(dt, to, 0);
+ }
+
+ return r;
+}
+
+static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ int r;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ r = mkfifoat(dt, to, st->st_mode & 07777);
+ if (r < 0) {
+ if (errno == EEXIST)
+ return 0;
+
+ return -errno;
+ }
+
+ if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ r = -errno;
+
+ if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
+ r = -errno;
+
+ return r;
+}
+
+static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
+ int r;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ r = mknodat(dt, to, st->st_mode, st->st_rdev);
+ if (r < 0) {
+ if (errno == EEXIST)
+ return 0;
+
+ return -errno;
+ }
+
+ if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ r = -errno;
+
+ if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
+ r = -errno;
+
+ 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) {
+ _cleanup_close_ int fdf = -1, fdt = -1;
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ bool created;
+ int r;
+
+ assert(from);
+ assert(st);
+ assert(to);
+
+ fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fdf < 0)
+ return -errno;
+
+ d = fdopendir(fdf);
+ if (!d)
+ return -errno;
+ fdf = -1;
+
+ r = mkdirat(dt, to, st->st_mode & 07777);
+ if (r >= 0)
+ created = true;
+ else if (errno == EEXIST)
+ created = false;
+ else
+ return -errno;
+
+ fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fdt < 0)
+ return -errno;
+
+ if (created) {
+ if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ r = -errno;
+
+ if (fchmod(fdt, st->st_mode & 07777) < 0)
+ r = -errno;
+ }
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ struct stat buf;
+ int q;
+
+ if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
+ r = -errno;
+ continue;
+ }
+
+ if (buf.st_dev != original_device)
+ continue;
+
+ 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);
+ 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))
+ q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
+ q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ else
+ q = -ENOTSUP;
+
+ if (q < 0)
+ r = q;
+ }
+
+ return r;
+}
+
+int copy_tree(const char *from, const char *to) {
+ struct stat st;
+
+ assert(from);
+ assert(to);
+
+ if (lstat(from, &st) < 0)
+ return -errno;
+
+ 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);
+ 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))
+ return fd_copy_fifo(AT_FDCWD, from, &st, AT_FDCWD, to);
+ else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
+ return fd_copy_node(AT_FDCWD, from, &st, AT_FDCWD, to);
+ else
+ return -ENOTSUP;
+}
+
+int copy_file(const char *from, const char *to, int flags, mode_t mode) {
+ _cleanup_close_ int fdf = -1, fdt = -1;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fdf < 0)
+ return -errno;
+
+ fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
+ if (fdt < 0)
+ return -errno;
+
+ r = stream_bytes(fdf, fdt);
+ if (r < 0) {
+ unlink(to);
+ return r;
+ }
+
+ r = close(fdt);
+ fdt = -1;
+
+ if (r < 0) {
+ r = -errno;
+ unlink(to);
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/shared/copy.h b/src/shared/copy.h
new file mode 100644
index 0000000000..5b569543ee
--- /dev/null
+++ b/src/shared/copy.h
@@ -0,0 +1,25 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int copy_file(const char *from, const char *to, int flags, mode_t mode);
+int copy_tree(const char *from, const char *to);
diff --git a/src/shared/util.c b/src/shared/util.c
index 2e832ca384..91cbf20454 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -804,7 +804,7 @@ char *strappend(const char *s, const char *suffix) {
return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
}
-int readlink_malloc(const char *p, char **ret) {
+int readlinkat_malloc(int fd, const char *p, char **ret) {
size_t l = 100;
int r;
@@ -819,7 +819,7 @@ int readlink_malloc(const char *p, char **ret) {
if (!c)
return -ENOMEM;
- n = readlink(p, c, l-1);
+ n = readlinkat(fd, p, c, l-1);
if (n < 0) {
r = -errno;
free(c);
@@ -837,6 +837,10 @@ int readlink_malloc(const char *p, char **ret) {
}
}
+int readlink_malloc(const char *p, char **ret) {
+ return readlinkat_malloc(AT_FDCWD, p, ret);
+}
+
int readlink_and_make_absolute(const char *p, char **r) {
_cleanup_free_ char *target = NULL;
char *k;
@@ -4128,60 +4132,6 @@ int vt_disallocate(const char *name) {
return 0;
}
-int copy_file(const char *from, const char *to, int flags) {
- _cleanup_close_ int fdf = -1;
- int r, fdt;
-
- assert(from);
- assert(to);
-
- fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fdf < 0)
- return -errno;
-
- fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
- if (fdt < 0)
- return -errno;
-
- for (;;) {
- char buf[PIPE_BUF];
- ssize_t n, k;
-
- n = read(fdf, buf, sizeof(buf));
- if (n < 0) {
- r = -errno;
-
- close_nointr(fdt);
- unlink(to);
-
- return r;
- }
-
- if (n == 0)
- break;
-
- errno = 0;
- k = loop_write(fdt, buf, n, false);
- if (n != k) {
- r = k < 0 ? k : (errno ? -errno : -EIO);
-
- close_nointr(fdt);
- unlink(to);
-
- return r;
- }
- }
-
- r = close_nointr(fdt);
-
- if (r < 0) {
- unlink(to);
- return r;
- }
-
- return 0;
-}
-
int symlink_atomic(const char *from, const char *to) {
char *x;
_cleanup_free_ char *t;
diff --git a/src/shared/util.h b/src/shared/util.h
index 7618aefad5..0f8c393353 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -252,6 +252,7 @@ char *strnappend(const char *s, const char *suffix, size_t length);
char *replace_env(const char *format, char **env);
char **replace_env_argv(char **argv, char **env);
+int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
int readlink_and_make_absolute(const char *p, char **r);
int readlink_and_canonicalize(const char *p, char **r);
@@ -521,8 +522,6 @@ int terminal_vhangup(const char *name);
int vt_disallocate(const char *name);
-int copy_file(const char *from, const char *to, int flags);
-
int symlink_atomic(const char *from, const char *to);
int fchmod_umask(int fd, mode_t mode);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 48dc619d94..6745c23a15 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -53,6 +53,7 @@
#include "capability.h"
#include "specifier.h"
#include "build.h"
+#include "copy.h"
/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
* them in the file system. This is intended to be used to create
@@ -69,6 +70,7 @@ typedef enum ItemType {
CREATE_SYMLINK = 'L',
CREATE_CHAR_DEVICE = 'c',
CREATE_BLOCK_DEVICE = 'b',
+ COPY_FILES = 'C',
ADJUST_MODE = 'm',
/* These ones take globs */
@@ -671,6 +673,19 @@ static int create_item(Item *i) {
return r;
break;
+ case COPY_FILES:
+ r = copy_tree(i->argument, i->path);
+ if (r < 0) {
+ log_error("Failed to copy files: %s", strerror(-r));
+ return r;
+ }
+
+ r = item_set_perms(i, i->path);
+ if (r < 0)
+ return r;
+
+ break;
+
case WRITE_FILE:
r = glob_item(i, write_one_file);
if (r < 0)
@@ -849,6 +864,7 @@ static int remove_item_instance(Item *i, const char *instance) {
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
+ case COPY_FILES:
case ADJUST_MODE:
break;
@@ -895,6 +911,7 @@ static int remove_item(Item *i) {
case RELABEL_PATH:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
+ case COPY_FILES:
case ADJUST_MODE:
break;
@@ -967,6 +984,7 @@ static int clean_item(Item *i) {
case CREATE_DIRECTORY:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
+ case COPY_FILES:
clean_item_instance(i, i->path);
break;
case IGNORE_DIRECTORY_PATH:
@@ -1036,7 +1054,8 @@ static bool item_equal(Item *a, Item *b) {
if ((a->type == CREATE_FILE ||
a->type == TRUNCATE_FILE ||
a->type == WRITE_FILE ||
- a->type == CREATE_SYMLINK) &&
+ a->type == CREATE_SYMLINK ||
+ a->type == COPY_FILES) &&
!streq_ptr(a->argument, b->argument))
return false;
@@ -1159,6 +1178,20 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
break;
+ case COPY_FILES:
+ if (!i->argument) {
+ log_error("[%s:%u] Copy files requires argument.", fname, line);
+ return -EBADMSG;
+ }
+
+ if (!path_is_absolute(i->argument)) {
+ log_error("[%s:%u] Source path is not absolute.", fname, line);
+ return -EBADMSG;
+ }
+
+ path_kill_slashes(i->argument);
+ break;
+
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE: {
unsigned major, minor;