#include <sys/param.h>
#include <glob.h>
#include <fnmatch.h>
-#include <sys/capability.h>
+#include <sys/xattr.h>
#include "log.h"
#include "util.h"
#include "specifier.h"
#include "build.h"
#include "copy.h"
+#include "selinux-util.h"
+#include "btrfs-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
TRUNCATE_FILE = 'F',
CREATE_DIRECTORY = 'd',
TRUNCATE_DIRECTORY = 'D',
+ CREATE_SUBVOLUME = 'v',
CREATE_FIFO = 'p',
CREATE_SYMLINK = 'L',
CREATE_CHAR_DEVICE = 'c',
CREATE_BLOCK_DEVICE = 'b',
COPY_FILES = 'C',
+ SET_XATTR = 't',
/* These ones take globs */
WRITE_FILE = 'w',
char *path;
char *argument;
+ char **xattrs;
uid_t uid;
gid_t gid;
mode_t mode;
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 = FILE_HANDLE_INIT;
int mount_id_parent, mount_id;
int r_p, r;
return label_fix(path, false, false);
}
+static int get_xattrs_from_arg(Item *i) {
+ char *xattr;
+ const char *p;
+ int r;
+
+ assert(i);
+
+ if (!i->argument) {
+ log_error("%s: Argument can't be empty!", i->path);
+ return -EBADMSG;
+ }
+ p = i->argument;
+
+ while ((r = unquote_first_word(&p, &xattr, false)) > 0) {
+ _cleanup_free_ char *tmp = NULL, *name = NULL, *value = NULL;
+ r = split_pair(xattr, "=", &name, &value);
+ if (r < 0) {
+ log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr);
+ free(xattr);
+ continue;
+ }
+ free(xattr);
+ if (streq(name, "") || streq(value, "")) {
+ log_warning("Malformed xattr found: \"%s=%s\" - ignoring.", name, value);
+ continue;
+ }
+ tmp = unquote(value, "\"");
+ if (!tmp)
+ return log_oom();
+ free(value);
+ value = cunescape(tmp);
+ if (!value)
+ return log_oom();
+ if (strv_consume_pair(&i->xattrs, name, value) < 0)
+ return log_oom();
+ name = value = NULL;
+ }
+
+ return r;
+}
+
+static int item_set_xattrs(Item *i, const char *path) {
+ char **name, **value;
+
+ assert(i);
+ assert(path);
+
+ if (strv_isempty(i->xattrs))
+ return 0;
+
+ STRV_FOREACH_PAIR(name, value, i->xattrs) {
+ int n;
+ n = strlen(*value);
+ if (lsetxattr(path, *name, *value, n, 0) < 0) {
+ log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path);
+ return -errno;
+ }
+ }
+ return 0;
+}
+
static int write_one_file(Item *i, const char *path) {
_cleanup_close_ int fd = -1;
int flags, r = 0;
if (r < 0)
return r;
+ r = item_set_xattrs(i, i->path);
+ if (r < 0)
+ return r;
+
return 0;
}
break;
- case TRUNCATE_DIRECTORY:
case CREATE_DIRECTORY:
+ case TRUNCATE_DIRECTORY:
+ case CREATE_SUBVOLUME:
- RUN_WITH_UMASK(0000) {
+ RUN_WITH_UMASK(0000)
mkdir_parents_label(i->path, 0755);
- r = mkdir_label(i->path, i->mode);
+
+ if (i->type == CREATE_SUBVOLUME) {
+ RUN_WITH_UMASK((~i->mode) & 0777)
+ r = btrfs_subvol_make(i->path);
+ } else
+ r = 0;
+
+ if (i->type == CREATE_DIRECTORY || i->type == TRUNCATE_DIRECTORY || r == -ENOTTY) {
+ RUN_WITH_UMASK(0000)
+ r = mkdir_label(i->path, i->mode);
}
if (r < 0) {
if (r != -EEXIST)
- return log_error_errno(r, "Failed to create directory %s: %m", i->path);
+ return log_error_errno(r, "Failed to create directory or subvolume %s: %m", i->path);
if (stat(i->path, &st) < 0)
return log_error_errno(errno, "stat(%s) failed: %m", i->path);
if (r < 0)
return r;
+ r = item_set_xattrs(i, i->path);
+ if (r < 0)
+ return r;
+
break;
case CREATE_FIFO:
if (r < 0)
return r;
+ r = item_set_xattrs(i, i->path);
+ if (r < 0)
+ return r;
+
break;
case CREATE_SYMLINK:
}
}
+ r = item_set_xattrs(i, i->path);
+ if (r < 0)
+ return r;
+
break;
case CREATE_BLOCK_DEVICE:
if (r < 0)
return r;
+ r = item_set_xattrs(i, i->path);
+ if (r < 0)
+ return r;
+
break;
}
r = glob_item(i, item_set_perms_recursive);
if (r < 0)
return r;
+ break;
+ case SET_XATTR:
+ r = item_set_xattrs(i, i->path);
+ if (r < 0)
+ return r;
break;
}
case CREATE_FILE:
case TRUNCATE_FILE:
case CREATE_DIRECTORY:
+ case CREATE_SUBVOLUME:
case CREATE_FIFO:
case CREATE_SYMLINK:
case CREATE_BLOCK_DEVICE:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
case COPY_FILES:
+ case SET_XATTR:
break;
case REMOVE_PATH:
case CREATE_FILE:
case TRUNCATE_FILE:
case CREATE_DIRECTORY:
+ case CREATE_SUBVOLUME:
case CREATE_FIFO:
case CREATE_SYMLINK:
case CREATE_CHAR_DEVICE:
case RECURSIVE_RELABEL_PATH:
case WRITE_FILE:
case COPY_FILES:
+ case SET_XATTR:
break;
case REMOVE_PATH:
switch (i->type) {
case CREATE_DIRECTORY:
+ case CREATE_SUBVOLUME:
case TRUNCATE_DIRECTORY:
case IGNORE_PATH:
case COPY_FILES:
}
static int process_item(Item *i) {
- int r, q, p;
+ int r, q, p, t = 0;
_cleanup_free_ char *prefix = NULL;
assert(i);
Item *j;
j = hashmap_get(items, prefix);
- if (j)
- process_item(j);
+ if (j) {
+ int s;
+
+ s = process_item(j);
+ if (s < 0 && t == 0)
+ t = s;
+ }
}
r = arg_create ? create_item(i) : 0;
q = arg_remove ? remove_item(i) : 0;
p = arg_clean ? clean_item(i) : 0;
- if (r < 0)
- return r;
-
- if (q < 0)
- return q;
-
- return p;
+ return t < 0 ? t :
+ r < 0 ? r :
+ q < 0 ? q :
+ p;
}
static void item_free(Item *i) {
free(i->path);
free(i->argument);
+ strv_free(i->xattrs);
free(i);
}
case CREATE_FILE:
case TRUNCATE_FILE:
case CREATE_DIRECTORY:
+ case CREATE_SUBVOLUME:
case TRUNCATE_DIRECTORY:
case CREATE_FIFO:
case IGNORE_PATH:
break;
}
+ case SET_XATTR:
+ if (!i->argument) {
+ log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
+ return -EBADMSG;
+ }
+ r = get_xattrs_from_arg(i);
+ if (r < 0)
+ return r;
+ break;
+
default:
log_error("[%s:%u] Unknown command type '%c'.", fname, line, type);
return -EBADMSG;
} else
i->mode =
i->type == CREATE_DIRECTORY ||
+ i->type == CREATE_SUBVOLUME ||
i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
if (age && !streq(age, "-")) {
existing = hashmap_get(h, i->path);
if (existing) {
-
- /* Two identical items are fine */
- if (!item_equal(existing, i))
- log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
-
- return 0;
+ if (i->type == SET_XATTR) {
+ r = strv_extend_strv(&existing->xattrs, i->xattrs);
+ if (r < 0)
+ return log_oom();
+ return 0;
+ } else if (existing->type == SET_XATTR) {
+ r = strv_extend_strv(&i->xattrs, existing->xattrs);
+ if (r < 0)
+ return log_oom();
+ r = hashmap_replace(h, i->path, i);
+ if (r < 0) {
+ log_error("Failed to replace item for %s.", i->path);
+ return r;
+ }
+ item_free(existing);
+ } else {
+ /* Two identical items are fine */
+ if (!item_equal(existing, i))
+ log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
+ return 0;
+ }
+ } else {
+ r = hashmap_put(h, i->path, i);
+ if (r < 0) {
+ log_error("Failed to insert item %s: %s", i->path, strerror(-r));
+ return r;
+ }
}
- r = hashmap_put(h, i->path, i);
- if (r < 0)
- return log_error_errno(r, "Failed to insert item %s: %m", i->path);
-
i = NULL; /* avoid cleanup */
return 0;
continue;
HASHMAP_FOREACH(j, items, iter) {
- if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY)
+ if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
continue;
if (path_equal(j->path, i->path)) {
}
}
- HASHMAP_FOREACH(i, globs, iterator)
- process_item(i);
+ HASHMAP_FOREACH(i, globs, iterator) {
+ k = process_item(i);
+ if (k < 0 && r == 0)
+ r = k;
+ }
- HASHMAP_FOREACH(i, items, iterator)
- process_item(i);
+ HASHMAP_FOREACH(i, items, iterator) {
+ k = process_item(i);
+ if (k < 0 && r == 0)
+ r = k;
+ }
finish:
while ((i = hashmap_steal_first(items)))