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

This commit is contained in:
Lennart Poettering 2014-06-10 23:02:40 +02:00
parent cde684a293
commit 849958d1ba
8 changed files with 372 additions and 63 deletions

View File

@ -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 \

View File

@ -183,6 +183,11 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<listitem><para>Create a block device node if it does not exist yet.</para></listitem>
</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
@ -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>

View File

@ -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;
}

292
src/shared/copy.c Normal file
View File

@ -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;
}

25
src/shared/copy.h Normal file
View File

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

View File

@ -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;

View File

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

View File

@ -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;