mirror of
https://github.com/systemd/systemd.git
synced 2025-02-25 21:57:32 +03:00
tmpfiles: add new "C" line for copying files or directories
This commit is contained in:
parent
cde684a293
commit
849958d1ba
@ -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 \
|
||||
|
@ -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>
|
||||
|
@ -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
292
src/shared/copy.c
Normal 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
25
src/shared/copy.h
Normal 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);
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user