[systemd-commits] 4 commits - Makefile.am man/tmpfiles.d.xml src/journal src/nspawn src/shared src/tmpfiles tmpfiles.d/systemd.conf

Lennart Poettering lennart at kemper.freedesktop.org
Tue Jun 10 14:45:01 PDT 2014


 Makefile.am                  |    4 
 man/tmpfiles.d.xml           |   48 +++----
 src/journal/journal-remote.c |    2 
 src/nspawn/nspawn.c          |    3 
 src/shared/copy.c            |  292 +++++++++++++++++++++++++++++++++++++++++++
 src/shared/copy.h            |   25 +++
 src/shared/label.c           |    4 
 src/shared/mkdir.c           |   15 +-
 src/shared/mkdir.h           |    2 
 src/shared/util.c            |   62 ---------
 src/shared/util.h            |    3 
 src/tmpfiles/tmpfiles.c      |  200 ++++++++++++++++-------------
 tmpfiles.d/systemd.conf      |    4 
 13 files changed, 481 insertions(+), 183 deletions(-)

New commits:
commit e73a03e059830a3df8fac811f923704311e93731
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 10 23:42:16 2014 +0200

    tmpfiles: get rid of "m" lines, make them redundant by "z"
    
    "m" so far has been a non-globbing version of "z". Since this makes it
    quite redundant, let's get rid of it. Remove "m" from the man pages,
    beef up "z" docs instead, and make "m" nothing more than a compatibility
    alias for "z".

diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index 20ed803..76cae39 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -189,17 +189,6 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
                                 </varlistentry>
 
                                 <varlistentry>
-                                        <term><varname>m</varname></term>
-                                        <listitem><para>If the
-                                        specified file path exists,
-                                        adjust its access mode, group
-                                        and user to the specified
-                                        values and reset the SELinux
-                                        security context. If it does not exist, do
-                                        nothing.</para></listitem>
-                                </varlistentry>
-
-                                <varlistentry>
                                         <term><varname>x</varname></term>
                                         <listitem><para>Ignore a path
                                         during cleaning. Use this type
@@ -262,27 +251,29 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
 
                                 <varlistentry>
                                         <term><varname>z</varname></term>
-                                        <listitem><para>Restore
-                                        SELinux security context
-                                        and set ownership and access
-                                        mode of a file or directory if
-                                        it exists.  Lines of this type
-                                        accept shell-style globs in
-                                        place of normal path names.
+                                        <listitem><para>Adjust the
+                                        access mode, group and user,
+                                        and restore the SELinux security
+                                        context of a file or directory,
+                                        if it exists. Lines of this
+                                        type accept shell-style globs
+                                        in place of normal path names.
                                         </para></listitem>
                                 </varlistentry>
 
                                 <varlistentry>
                                         <term><varname>Z</varname></term>
                                         <listitem><para>Recursively
-                                        restore SELinux security
-                                        context and set
-                                        ownership and access mode of a
-                                        path and all its
-                                        subdirectories (if it is a
-                                        directory). Lines of this type
-                                        accept shell-style globs in
-                                        place of normal path
+                                        set the access mode, group and
+                                        user, and restore the SELinux
+                                        security context of a file or
+                                        directory if it exists, as
+                                        well as of its subdirectories
+                                        and the files contained
+                                        therein (if applicable). Lines
+                                        of this type accept
+                                        shell-style globs in place of
+                                        normal path
                                         names.</para></listitem>
                                 </varlistentry>
                         </variablelist>
diff --git a/src/journal/journal-remote.c b/src/journal/journal-remote.c
index 9adad7a..915f234 100644
--- a/src/journal/journal-remote.c
+++ b/src/journal/journal-remote.c
@@ -185,7 +185,7 @@ static int open_output(Writer *s, const char* url) {
                 if (r < 0)
                         return log_oom();
         } else {
-                r = is_dir(arg_output);
+                r = is_dir(arg_output, true);
                 if (r > 0) {
                         r = asprintf(&output,
                                      "%s/remote-%s.journal", arg_output, name);
diff --git a/src/shared/mkdir.c b/src/shared/mkdir.c
index ba083d6..f941efb 100644
--- a/src/shared/mkdir.c
+++ b/src/shared/mkdir.c
@@ -58,11 +58,16 @@ int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         return mkdir_safe_internal(path, mode, uid, gid, mkdir);
 }
 
-int is_dir(const char* path) {
+int is_dir(const char* path, bool follow) {
         struct stat st;
 
-        if (stat(path, &st) < 0)
-                return -errno;
+        if (follow) {
+                if (stat(path, &st) < 0)
+                        return -errno;
+        } else {
+                if (lstat(path, &st) < 0)
+                        return -errno;
+        }
 
         return S_ISDIR(st.st_mode);
 }
@@ -85,7 +90,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
                 return 0;
 
         p = strndupa(path, e - path);
-        r = is_dir(p);
+        r = is_dir(p, true);
         if (r > 0)
                 return 0;
         if (r == 0)
@@ -130,7 +135,7 @@ int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_fu
                 return r;
 
         r = _mkdir(path, mode);
-        if (r < 0 && (errno != EEXIST || is_dir(path) <= 0))
+        if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0))
                 return -errno;
 
         return 0;
diff --git a/src/shared/mkdir.h b/src/shared/mkdir.h
index f1bf4c0..d15ede6 100644
--- a/src/shared/mkdir.h
+++ b/src/shared/mkdir.h
@@ -41,4 +41,4 @@ typedef int (*mkdir_func_t)(const char *pathname, mode_t mode);
 int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir);
 int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
 int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
-int is_dir(const char *path);
+int is_dir(const char *path, bool is_dir);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 6745c23..89f6c6b 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -71,7 +71,6 @@ typedef enum ItemType {
         CREATE_CHAR_DEVICE = 'c',
         CREATE_BLOCK_DEVICE = 'b',
         COPY_FILES = 'C',
-        ADJUST_MODE = 'm',
 
         /* These ones take globs */
         WRITE_FILE = 'w',
@@ -79,8 +78,9 @@ typedef enum ItemType {
         IGNORE_DIRECTORY_PATH = 'X',
         REMOVE_PATH = 'r',
         RECURSIVE_REMOVE_PATH = 'R',
+        ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
         RELABEL_PATH = 'z',
-        RECURSIVE_RELABEL_PATH = 'Z'
+        RECURSIVE_RELABEL_PATH = 'Z',
 } ItemType;
 
 typedef struct Item {
@@ -134,6 +134,7 @@ static bool needs_glob(ItemType t) {
                       IGNORE_DIRECTORY_PATH,
                       REMOVE_PATH,
                       RECURSIVE_REMOVE_PATH,
+                      ADJUST_MODE,
                       RELABEL_PATH,
                       RECURSIVE_RELABEL_PATH);
 }
@@ -543,109 +544,90 @@ static int write_one_file(Item *i, const char *path) {
         return 0;
 }
 
-static int recursive_relabel_children(Item *i, const char *path) {
+static int item_set_perms_children(Item *i, const char *path) {
         _cleanup_closedir_ DIR *d;
-        int ret = 0;
+        int r = 0;
+
+        assert(i);
+        assert(path);
 
         /* This returns the first error we run into, but nevertheless
          * tries to go on */
 
         d = opendir(path);
         if (!d)
-                return errno == ENOENT ? 0 : -errno;
+                return errno == ENOENT || errno == ENOTDIR ? 0 : -errno;
 
         for (;;) {
+                _cleanup_free_ char *p = NULL;
                 struct dirent *de;
-                bool dir;
-                int r;
-                _cleanup_free_ char *entry_path = NULL;
+                int q;
 
                 errno = 0;
                 de = readdir(d);
-                if (!de && errno != 0) {
-                        if (ret == 0)
-                                ret = -errno;
-                        break;
-                }
+                if (!de) {
+                        if (errno != 0 && r == 0)
+                                r = -errno;
 
-                if (!de)
                         break;
+                }
 
                 if (streq(de->d_name, ".") || streq(de->d_name, ".."))
                         continue;
 
-                if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
-                        if (ret == 0)
-                                ret = -ENOMEM;
-                        continue;
-                }
-
-                if (de->d_type == DT_UNKNOWN) {
-                        r = is_dir(entry_path);
-                        if (r < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
-                                continue;
-                        }
-
-                        dir = r;
-
-                } else
-                        dir = de->d_type == DT_DIR;
+                p = strjoin(path, "/", de->d_name, NULL);
+                if (!p)
+                        return -ENOMEM;
 
-                r = item_set_perms(i, entry_path);
-                if (r < 0) {
-                        if (ret == 0 && r != -ENOENT)
-                                ret = r;
-                        continue;
-                }
+                q = item_set_perms(i, p);
+                if (q < 0 && q != -ENOENT && r == 0)
+                        r = q;
 
-                if (dir) {
-                        r = recursive_relabel_children(i, entry_path);
-                        if (r < 0 && ret == 0)
-                                ret = r;
+                if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) {
+                        q = item_set_perms_children(i, p);
+                        if (q < 0 && r == 0)
+                                r = q;
                 }
         }
 
-        return ret;
+        return r;
 }
 
-static int recursive_relabel(Item *i, const char *path) {
-        int r;
-        struct stat st;
+static int item_set_perms_recursive(Item *i, const char *path) {
+        int r, q;
+
+        assert(i);
+        assert(path);
 
         r = item_set_perms(i, path);
         if (r < 0)
                 return r;
 
-        if (lstat(path, &st) < 0)
-                return -errno;
-
-        if (S_ISDIR(st.st_mode))
-                r = recursive_relabel_children(i, path);
+        q = item_set_perms_children(i, path);
+        if (q < 0 && r == 0)
+                r = q;
 
         return r;
 }
 
 static int glob_item(Item *i, int (*action)(Item *, const char *)) {
-        int r = 0, k;
         _cleanup_globfree_ glob_t g = {};
+        int r = 0, k;
         char **fn;
 
         errno = 0;
         k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
-        if (k != 0)
-                if (k != GLOB_NOMATCH) {
-                        if (errno > 0)
-                                errno = EIO;
+        if (k != 0 && k != GLOB_NOMATCH) {
+                if (errno == 0)
+                        errno = EIO;
 
-                        log_error("glob(%s) failed: %m", i->path);
-                        return -errno;
-                }
+                log_error("glob(%s) failed: %m", i->path);
+                return -errno;
+        }
 
         STRV_FOREACH(fn, g.gl_pathv) {
                 k = action(i, *fn);
-                if (k < 0)
+                if (k < 0 && r == 0)
                         r = k;
         }
 
@@ -693,13 +675,6 @@ static int create_item(Item *i) {
 
                 break;
 
-        case ADJUST_MODE:
-                r = item_set_perms_full(i, i->path, true);
-                if (r < 0)
-                        return r;
-
-                break;
-
         case TRUNCATE_DIRECTORY:
         case CREATE_DIRECTORY:
 
@@ -826,6 +801,7 @@ static int create_item(Item *i) {
                 break;
         }
 
+        case ADJUST_MODE:
         case RELABEL_PATH:
 
                 r = glob_item(i, item_set_perms);
@@ -835,9 +811,11 @@ static int create_item(Item *i) {
 
         case RECURSIVE_RELABEL_PATH:
 
-                r = glob_item(i, recursive_relabel);
+                r = glob_item(i, item_set_perms_recursive);
                 if (r < 0)
                         return r;
+
+                break;
         }
 
         log_debug("%s created successfully.", i->path);
@@ -861,11 +839,11 @@ static int remove_item_instance(Item *i, const char *instance) {
         case CREATE_CHAR_DEVICE:
         case IGNORE_PATH:
         case IGNORE_DIRECTORY_PATH:
+        case ADJUST_MODE:
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
         case WRITE_FILE:
         case COPY_FILES:
-        case ADJUST_MODE:
                 break;
 
         case REMOVE_PATH:
@@ -908,11 +886,11 @@ static int remove_item(Item *i) {
         case CREATE_BLOCK_DEVICE:
         case IGNORE_PATH:
         case IGNORE_DIRECTORY_PATH:
+        case ADJUST_MODE:
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
         case WRITE_FILE:
         case COPY_FILES:
-        case ADJUST_MODE:
                 break;
 
         case REMOVE_PATH:
@@ -1158,9 +1136,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         case IGNORE_DIRECTORY_PATH:
         case REMOVE_PATH:
         case RECURSIVE_REMOVE_PATH:
+        case ADJUST_MODE:
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
-        case ADJUST_MODE:
                 break;
 
         case CREATE_SYMLINK:
diff --git a/tmpfiles.d/systemd.conf b/tmpfiles.d/systemd.conf
index c5910f8..85dc356 100644
--- a/tmpfiles.d/systemd.conf
+++ b/tmpfiles.d/systemd.conf
@@ -25,7 +25,7 @@ d /run/systemd/netif 0755 systemd-network systemd-network -
 d /run/systemd/netif/links 0755 systemd-network systemd-network -
 d /run/systemd/netif/leases 0755 systemd-network systemd-network -
 
-m /var/log/journal 2755 root systemd-journal - -
+z /var/log/journal 2755 root systemd-journal - -
 Z /var/log/journal/%m 2755 root systemd-journal - -
-m /run/log/journal 2755 root systemd-journal - -
+z /run/log/journal 2755 root systemd-journal - -
 Z /run/log/journal/%m 2755 root systemd-journal - -

commit 849958d1ba3533c953fad46d4d41c0ec6e48316d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 10 23:02:40 2014 +0200

    tmpfiles: add new "C" line for copying files or directories

diff --git a/Makefile.am b/Makefile.am
index 4ff9f5a..c67f063 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 e54f1ba..20ed803 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 26ac1bf..867cf19 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 0000000..4dfc2f3
--- /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 0000000..5b56954
--- /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 2e832ca..91cbf20 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 7618aef..0f8c393 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 48dc619..6745c23 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;

commit cde684a2932d3c8cbb9b3374aec27a1c20ba75fa
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 10 22:50:46 2014 +0200

    tmpfiles: various modernizations

diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 68f0a5c..48dc619 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -63,7 +63,6 @@ typedef enum ItemType {
         /* These ones take file names */
         CREATE_FILE = 'f',
         TRUNCATE_FILE = 'F',
-        WRITE_FILE = 'w',
         CREATE_DIRECTORY = 'd',
         TRUNCATE_DIRECTORY = 'D',
         CREATE_FIFO = 'p',
@@ -73,6 +72,7 @@ typedef enum ItemType {
         ADJUST_MODE = 'm',
 
         /* These ones take globs */
+        WRITE_FILE = 'w',
         IGNORE_PATH = 'x',
         IGNORE_DIRECTORY_PATH = 'X',
         REMOVE_PATH = 'r',
@@ -126,7 +126,14 @@ static const char conf_file_dirs[] =
 #define MAX_DEPTH 256
 
 static bool needs_glob(ItemType t) {
-        return t == IGNORE_PATH || t == IGNORE_DIRECTORY_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
+        return IN_SET(t,
+                      WRITE_FILE,
+                      IGNORE_PATH,
+                      IGNORE_DIRECTORY_PATH,
+                      REMOVE_PATH,
+                      RECURSIVE_REMOVE_PATH,
+                      RELABEL_PATH,
+                      RECURSIVE_RELABEL_PATH);
 }
 
 static struct Item* find_glob(Hashmap *h, const char *match) {
@@ -217,7 +224,11 @@ static bool unix_socket_alive(const char *fn) {
 }
 
 static int dir_is_mount_point(DIR *d, const char *subdir) {
-        union file_handle_union h = { .handle.handle_bytes = MAX_HANDLE_SZ };
+
+        union file_handle_union h = {
+                .handle.handle_bytes = MAX_HANDLE_SZ
+        };
+
         int mount_id_parent, mount_id;
         int r_p, r;
 
@@ -301,7 +312,8 @@ static int dir_cleanup(
                 if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
                         continue;
 
-                if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
+                sub_path = strjoin(p, "/", dent->d_name, NULL);
+                if (!sub_path) {
                         r = log_oom();
                         goto finish;
                 }
@@ -327,7 +339,7 @@ static int dir_cleanup(
                                 int q;
 
                                 sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
-                                if (sub_dir == NULL) {
+                                if (!sub_dir) {
                                         if (errno != ENOENT) {
                                                 log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
                                                 r = -errno;
@@ -337,7 +349,6 @@ static int dir_cleanup(
                                 }
 
                                 q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false);
-
                                 if (q < 0)
                                         r = q;
                         }
@@ -434,6 +445,9 @@ finish:
 }
 
 static int item_set_perms_full(Item *i, const char *path, bool ignore_enoent) {
+        assert(i);
+        assert(path);
+
         /* not using i->path directly because it may be a glob */
         if (i->mode_set)
                 if (chmod(path, i->mode) < 0) {
@@ -488,9 +502,9 @@ static int write_one_file(Item *i, const char *path) {
         }
 
         if (i->argument) {
+                _cleanup_free_ char *unescaped;
                 ssize_t n;
                 size_t l;
-                _cleanup_free_ char *unescaped;
 
                 unescaped = cunescape(i->argument);
                 if (unescaped == NULL) {
@@ -992,7 +1006,6 @@ static void item_free(Item *i) {
 }
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
-#define _cleanup_item_free_ _cleanup_(item_freep)
 
 static bool item_equal(Item *a, Item *b) {
         assert(a);
@@ -1063,10 +1076,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 {}
         };
 
-        _cleanup_item_free_ Item *i = NULL;
+        _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
+        _cleanup_(item_freep) Item *i = NULL;
         Item *existing;
-        _cleanup_free_ char
-                *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
         char type;
         Hashmap *h;
         int r, n = -1;
@@ -1137,6 +1149,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                         log_error("[%s:%u] Symlink file requires argument.", fname, line);
                         return -EBADMSG;
                 }
+
                 break;
 
         case WRITE_FILE:
@@ -1182,7 +1195,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 return 0;
 
         if (arg_root) {
-                char *p = strappend(arg_root, i->path);
+                char *p;
+
+                p = strappend(arg_root, i->path);
                 if (!p)
                         return log_oom();
 

commit 874f1947e33922f08c578696af5b628a0f67fec2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 10 22:48:56 2014 +0200

    label: when clearing selinux context, don't mangle errno

diff --git a/src/shared/label.c b/src/shared/label.c
index e46d532..25a8b36 100644
--- a/src/shared/label.c
+++ b/src/shared/label.c
@@ -293,6 +293,8 @@ int label_socket_set(const char *label) {
 void label_context_clear(void) {
 
 #ifdef HAVE_SELINUX
+        PROTECT_ERRNO;
+
         if (!use_selinux())
                 return;
 
@@ -303,6 +305,8 @@ void label_context_clear(void) {
 void label_socket_clear(void) {
 
 #ifdef HAVE_SELINUX
+        PROTECT_ERRNO;
+
         if (!use_selinux())
                 return;
 
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 04b472d..68f0a5c 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -462,20 +462,21 @@ static int item_set_perms(Item *i, const char *path) {
 }
 
 static int write_one_file(Item *i, const char *path) {
-        int e, flags;
+        int flags;
         int fd = -1;
         struct stat st;
         int r = 0;
 
+        assert(i);
+        assert(path);
+
         flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
                 i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
 
         RUN_WITH_UMASK(0) {
                 label_context_set(path, S_IFREG);
                 fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
-                e = errno;
                 label_context_clear();
-                errno = e;
         }
 
         if (fd < 0) {
@@ -636,7 +637,6 @@ static int glob_item(Item *i, int (*action)(Item *, const char *)) {
 }
 
 static int create_item(Item *i) {
-        int e;
         struct stat st;
         int r = 0;
 
@@ -732,9 +732,7 @@ static int create_item(Item *i) {
 
                 label_context_set(i->path, S_IFLNK);
                 r = symlink(i->argument, i->path);
-                e = errno;
                 label_context_clear();
-                errno = e;
 
                 if (r < 0 && errno != EEXIST) {
                         log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
@@ -774,9 +772,7 @@ static int create_item(Item *i) {
                 RUN_WITH_UMASK(0000) {
                         label_context_set(i->path, file_type);
                         r = mknod(i->path, i->mode | file_type, i->major_minor);
-                        e = errno;
                         label_context_clear();
-                        errno = e;
                 }
 
                 if (r < 0 && errno != EEXIST) {



More information about the systemd-commits mailing list