summaryrefslogtreecommitdiff
path: root/src/tmpfiles/tmpfiles.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tmpfiles/tmpfiles.c')
-rw-r--r--src/tmpfiles/tmpfiles.c266
1 files changed, 151 insertions, 115 deletions
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 42f757c4b7..954f4aa985 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,43 +18,59 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <fcntl.h>
+#include <dirent.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <getopt.h>
+#include <glob.h>
#include <limits.h>
-#include <dirent.h>
+#include <linux/fs.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stddef.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <time.h>
-#include <glob.h>
-#include <fnmatch.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/xattr.h>
-#include <linux/fs.h>
+#include <time.h>
+#include <unistd.h>
+#include "acl-util.h"
+#include "alloc-util.h"
+#include "btrfs-util.h"
+#include "capability-util.h"
+#include "chattr-util.h"
+#include "conf-files.h"
+#include "copy.h"
+#include "def.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "formats-util.h"
+#include "fs-util.h"
+#include "glob-util.h"
+#include "io-util.h"
+#include "label.h"
#include "log.h"
-#include "util.h"
#include "macro.h"
#include "missing.h"
#include "mkdir.h"
+#include "mount-util.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "strv.h"
-#include "label.h"
-#include "set.h"
-#include "conf-files.h"
-#include "capability.h"
-#include "specifier.h"
-#include "build.h"
-#include "copy.h"
#include "rm-rf.h"
#include "selinux-util.h"
-#include "btrfs-util.h"
-#include "acl-util.h"
-#include "formats-util.h"
+#include "set.h"
+#include "specifier.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "umask-util.h"
+#include "user-util.h"
+#include "util.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
@@ -70,6 +84,8 @@ typedef enum ItemType {
CREATE_DIRECTORY = 'd',
TRUNCATE_DIRECTORY = 'D',
CREATE_SUBVOLUME = 'v',
+ CREATE_SUBVOLUME_INHERIT_QUOTA = 'q',
+ CREATE_SUBVOLUME_NEW_QUOTA = 'Q',
CREATE_FIFO = 'p',
CREATE_SYMLINK = 'L',
CREATE_CHAR_DEVICE = 'c',
@@ -78,6 +94,7 @@ typedef enum ItemType {
/* These ones take globs */
WRITE_FILE = 'w',
+ EMPTY_DIRECTORY = 'e',
SET_XATTR = 't',
RECURSIVE_SET_XATTR = 'T',
SET_ACL = 'a',
@@ -141,7 +158,7 @@ static char **arg_include_prefixes = NULL;
static char **arg_exclude_prefixes = NULL;
static char *arg_root = NULL;
-static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles");
+static const char conf_file_dirs[] = CONF_PATHS_NULSTR("tmpfiles.d");
#define MAX_DEPTH 256
@@ -163,6 +180,7 @@ static bool needs_glob(ItemType t) {
IGNORE_DIRECTORY_PATH,
REMOVE_PATH,
RECURSIVE_REMOVE_PATH,
+ EMPTY_DIRECTORY,
ADJUST_MODE,
RELABEL_PATH,
RECURSIVE_RELABEL_PATH,
@@ -179,8 +197,11 @@ static bool takes_ownership(ItemType t) {
CREATE_FILE,
TRUNCATE_FILE,
CREATE_DIRECTORY,
+ EMPTY_DIRECTORY,
TRUNCATE_DIRECTORY,
CREATE_SUBVOLUME,
+ CREATE_SUBVOLUME_INHERIT_QUOTA,
+ CREATE_SUBVOLUME_NEW_QUOTA,
CREATE_FIFO,
CREATE_SYMLINK,
CREATE_CHAR_DEVICE,
@@ -595,7 +616,7 @@ static int path_set_perms(Item *i, const char *path) {
* with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
* O_PATH. */
- fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
+ fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0)
return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path);
@@ -616,7 +637,7 @@ static int path_set_perms(Item *i, const char *path) {
if (!(st.st_mode & 0111))
m &= ~0111;
if (!(st.st_mode & 0222))
- m &= ~0222;
+ m &= ~0222;
if (!(st.st_mode & 0444))
m &= ~0444;
if (!S_ISDIR(st.st_mode))
@@ -641,7 +662,7 @@ static int path_set_perms(Item *i, const char *path) {
if (chown(fn,
i->uid_set ? i->uid : UID_INVALID,
i->gid_set ? i->gid : GID_INVALID) < 0)
- return log_error_errno(errno, "chown(%s) failed: %m", path);
+ return log_error_errno(errno, "chown(%s) failed: %m", path);
}
}
@@ -662,7 +683,7 @@ static int parse_xattrs_from_arg(Item *i) {
for (;;) {
_cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL;
- r = unquote_first_word(&p, &xattr, UNQUOTE_CUNESCAPE);
+ r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
if (r < 0)
log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
if (r <= 0)
@@ -786,7 +807,7 @@ static int path_set_acls(Item *item, const char *path) {
assert(item);
assert(path);
- fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
+ fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
if (fd < 0)
return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
@@ -845,7 +866,7 @@ static int parse_attribute_from_arg(Item *item) {
{ 'a', FS_APPEND_FL }, /* writes to file may only append */
{ 'c', FS_COMPR_FL }, /* Compress file */
{ 'd', FS_NODUMP_FL }, /* do not dump file */
- { 'e', FS_EXTENT_FL }, /* Top of directory hierarchies*/
+ { 'e', FS_EXTENT_FL }, /* Extents */
{ 'i', FS_IMMUTABLE_FL }, /* Immutable file */
{ 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */
{ 's', FS_SECRM_FL }, /* Secure deletion */
@@ -899,10 +920,7 @@ static int parse_attribute_from_arg(Item *item) {
v = attributes[i].value;
- if (mode == MODE_ADD || mode == MODE_SET)
- value |= v;
- else
- value &= ~v;
+ SET_FLAG(value, v, (mode == MODE_ADD || mode == MODE_SET));
mask |= v;
}
@@ -955,9 +973,10 @@ static int path_set_attribute(Item *item, const char *path) {
r = chattr_fd(fd, f, item->attribute_mask);
if (r < 0)
- return log_error_errno(r,
- "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
- path, item->attribute_value, item->attribute_mask);
+ log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING,
+ r,
+ "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m",
+ path, item->attribute_value, item->attribute_mask);
return 0;
}
@@ -1054,7 +1073,7 @@ static int item_do_children(Item *i, const char *path, action_t action) {
errno = 0;
de = readdir(d);
if (!de) {
- if (errno != 0 && r == 0)
+ if (errno > 0 && r == 0)
r = -errno;
break;
@@ -1132,6 +1151,7 @@ static int create_item(Item *i) {
_cleanup_free_ char *resolved = NULL;
struct stat st;
int r = 0;
+ int q = 0;
CreationMode creation;
assert(i);
@@ -1198,16 +1218,35 @@ static int create_item(Item *i) {
case CREATE_DIRECTORY:
case TRUNCATE_DIRECTORY:
case CREATE_SUBVOLUME:
-
+ case CREATE_SUBVOLUME_INHERIT_QUOTA:
+ case CREATE_SUBVOLUME_NEW_QUOTA:
RUN_WITH_UMASK(0000)
mkdir_parents_label(i->path, 0755);
- if (i->type == CREATE_SUBVOLUME)
- RUN_WITH_UMASK((~i->mode) & 0777) {
- r = btrfs_subvol_make(i->path);
- log_debug_errno(r, "Creating subvolume \"%s\": %m", i->path);
+ if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) {
+
+ if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0)
+
+ /* Don't create a subvolume unless the
+ * root directory is one, too. We do
+ * this under the assumption that if
+ * the root directory is just a plain
+ * directory (i.e. very light-weight),
+ * we shouldn't try to split it up
+ * into subvolumes (i.e. more
+ * heavy-weight). Thus, chroot()
+ * environments and suchlike will get
+ * a full brtfs subvolume set up below
+ * their tree only if they
+ * specifically set up a btrfs
+ * subvolume for the root dir too. */
+
+ r = -ENOTTY;
+ else {
+ RUN_WITH_UMASK((~i->mode) & 0777)
+ r = btrfs_subvol_make(i->path);
}
- else
+ } else
r = 0;
if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY)
@@ -1236,14 +1275,34 @@ static int create_item(Item *i) {
log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path);
+ if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
+ r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
+ if (r == -ENOTTY)
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
+ else if (r == -EROFS)
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
+ else if (r == -ENOPROTOOPT)
+ log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
+ else if (r < 0)
+ q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
+ else if (r > 0)
+ log_debug("Adjusted quota for subvolume \"%s\".", i->path);
+ else if (r == 0)
+ log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
+ }
+
+ /* fall through */
+
+ case EMPTY_DIRECTORY:
r = path_set_perms(i, i->path);
+ if (q < 0)
+ return q;
if (r < 0)
return r;
break;
case CREATE_FIFO:
-
RUN_WITH_UMASK(0000) {
mac_selinux_create_file_prepare(i->path, S_IFIFO);
r = mkfifo(i->path, i->mode);
@@ -1480,45 +1539,20 @@ static int remove_item_instance(Item *i, const char *instance) {
}
static int remove_item(Item *i) {
- int r = 0;
-
assert(i);
log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
switch (i->type) {
- case CREATE_FILE:
- case TRUNCATE_FILE:
- case CREATE_DIRECTORY:
- case CREATE_SUBVOLUME:
- case CREATE_FIFO:
- case CREATE_SYMLINK:
- case CREATE_CHAR_DEVICE:
- 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 SET_XATTR:
- case RECURSIVE_SET_XATTR:
- case SET_ACL:
- case RECURSIVE_SET_ACL:
- case SET_ATTRIBUTE:
- case RECURSIVE_SET_ATTRIBUTE:
- break;
-
case REMOVE_PATH:
case TRUNCATE_DIRECTORY:
case RECURSIVE_REMOVE_PATH:
- r = glob_item(i, remove_item_instance, false);
- break;
- }
+ return glob_item(i, remove_item_instance, false);
- return r;
+ default:
+ return 0;
+ }
}
static int clean_item_instance(Item *i, const char* instance) {
@@ -1541,13 +1575,12 @@ static int clean_item_instance(Item *i, const char* instance) {
d = opendir_nomod(instance);
if (!d) {
- if (errno == ENOENT || errno == ENOTDIR) {
+ if (IN_SET(errno, ENOENT, ENOTDIR)) {
log_debug_errno(errno, "Directory \"%s\": %m", instance);
return 0;
}
- log_error_errno(errno, "Failed to open directory %s: %m", instance);
- return -errno;
+ return log_error_errno(errno, "Failed to open directory %s: %m", instance);
}
if (fstat(dirfd(d), &s) < 0)
@@ -1561,8 +1594,7 @@ static int clean_item_instance(Item *i, const char* instance) {
if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0)
return log_error_errno(errno, "stat(%s/..) failed: %m", i->path);
- mountpoint = s.st_dev != ps.st_dev ||
- (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
+ mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino;
log_debug("Cleanup threshold for %s \"%s\" is %s",
mountpoint ? "mount point" : "directory",
@@ -1574,8 +1606,6 @@ static int clean_item_instance(Item *i, const char* instance) {
}
static int clean_item(Item *i) {
- int r = 0;
-
assert(i);
log_debug("Running clean action for entry %c %s", (char) i->type, i->path);
@@ -1583,19 +1613,19 @@ static int clean_item(Item *i) {
switch (i->type) {
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
+ case CREATE_SUBVOLUME_INHERIT_QUOTA:
+ case CREATE_SUBVOLUME_NEW_QUOTA:
+ case EMPTY_DIRECTORY:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
case COPY_FILES:
clean_item_instance(i, i->path);
- break;
+ return 0;
case IGNORE_DIRECTORY_PATH:
- r = glob_item(i, clean_item_instance, false);
- break;
+ return glob_item(i, clean_item_instance, false);
default:
- break;
+ return 0;
}
-
- return r;
}
static int process_item_array(ItemArray *array);
@@ -1759,9 +1789,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
assert(line >= 1);
assert(buffer);
- r = unquote_many_words(
+ r = extract_many_words(
&buffer,
- 0,
+ NULL,
+ EXTRACT_QUOTES,
&action,
&path,
&mode,
@@ -1818,6 +1849,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
case CREATE_DIRECTORY:
case CREATE_SUBVOLUME:
+ case CREATE_SUBVOLUME_INHERIT_QUOTA:
+ case CREATE_SUBVOLUME_NEW_QUOTA:
+ case EMPTY_DIRECTORY:
case TRUNCATE_DIRECTORY:
case CREATE_FIFO:
case IGNORE_PATH:
@@ -1982,8 +2016,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
i.mode = m;
i.mode_set = true;
} else
- i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
- ? 0755 : 0644;
+ i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
if (!isempty(age) && !streq(age, "-")) {
const char *a = age;
@@ -2074,7 +2107,7 @@ static int parse_argv(int argc, char *argv[]) {
{}
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
@@ -2088,9 +2121,7 @@ static int parse_argv(int argc, char *argv[]) {
return 0;
case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
- return 0;
+ return version();
case ARG_CREATE:
arg_create = true;
@@ -2119,12 +2150,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- free(arg_root);
- arg_root = path_make_absolute_cwd(optarg);
- if (!arg_root)
- return log_oom();
-
- path_kill_slashes(arg_root);
+ r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ if (r < 0)
+ return r;
break;
case '?':
@@ -2143,25 +2171,33 @@ static int parse_argv(int argc, char *argv[]) {
}
static int read_config_file(const char *fn, bool ignore_enoent) {
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_fclose_ FILE *_f = NULL;
+ FILE *f;
char line[LINE_MAX];
Iterator iterator;
unsigned v = 0;
Item *i;
- int r;
+ int r = 0;
assert(fn);
- r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
- if (r < 0) {
- if (ignore_enoent && r == -ENOENT) {
- log_debug_errno(r, "Failed to open \"%s\": %m", fn);
- return 0;
- }
+ if (streq(fn, "-")) {
+ log_debug("Reading config from stdin.");
+ fn = "<stdin>";
+ f = stdin;
+ } else {
+ r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT) {
+ log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
+ return 0;
+ }
- return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
+ return log_error_errno(r, "Failed to open '%s': %m", fn);
+ }
+ log_debug("Reading config file \"%s\".", fn);
+ f = _f;
}
- log_debug("Reading config file \"%s\".", fn);
FOREACH_LINE(line, f, break) {
char *l;
@@ -2187,7 +2223,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
continue;
ORDERED_HASHMAP_FOREACH(j, items, iter) {
- if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
+ if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
continue;
if (path_equal(j->path, i->path)) {
@@ -2230,7 +2266,7 @@ int main(int argc, char *argv[]) {
umask(0022);
- mac_selinux_init(NULL);
+ mac_selinux_init();
items = ordered_hashmap_new(&string_hash_ops);
globs = ordered_hashmap_new(&string_hash_ops);