The data structure used by tmpfiles is changed: instead of hashmaps
mapping {path → Item*} we now have hashmaps containing
{path -> ItemArray}, where ItemArray contains a pointer
to an array of Items.
For current code it doesn't matter much, but when we add new types it
is easier to simply add a new Item for a given path, then to coalesce
multiple lines into one Item.
In the future, this change will also make it possible to remember the
file and line where each Item originates, and use that in reporting
errors. Currently this is not possible, since each Item can be created
from multiple lines.
<varlistentry>
<term><varname>t</varname></term>
<varlistentry>
<term><varname>t</varname></term>
- <listitem><para>Set extended attributes on item. It may be
- used in conjunction with other types (only
- <varname>d</varname>, <varname>D</varname>,
- <varname>f</varname>, <varname>F</varname>,
- <varname>L</varname>, <varname>p</varname>,
- <varname>c</varname>, <varname>b</varname>, makes sense).
- If used as a standalone line, then
- <command>systemd-tmpfiles</command> will try to set extended
- attributes on specified path. This can be especially used
- to set SMACK labels. </para></listitem>
+ <listitem><para>Set extended attributes on the specified
+ path. This can be useful for setting SMACK labels.
+ </para></listitem>
</varlistentry>
</variablelist>
</varlistentry>
</variablelist>
This file is part of systemd.
Copyright 2010 Lennart Poettering, Kay Sievers
This file is part of systemd.
Copyright 2010 Lennart Poettering, Kay Sievers
+ Copyright 2015 Zbigniew Jędrzejewski-Szmek
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
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
+typedef struct ItemArray {
+ Item *items;
+ size_t count;
+ size_t size;
+} ItemArray;
+
static bool arg_create = false;
static bool arg_clean = false;
static bool arg_remove = false;
static bool arg_create = false;
static bool arg_clean = false;
static bool arg_remove = false;
RECURSIVE_RELABEL_PATH);
}
RECURSIVE_RELABEL_PATH);
}
+static bool takes_ownership(ItemType t) {
+ return IN_SET(t,
+ CREATE_FILE,
+ TRUNCATE_FILE,
+ CREATE_DIRECTORY,
+ TRUNCATE_DIRECTORY,
+ CREATE_SUBVOLUME,
+ CREATE_FIFO,
+ CREATE_SYMLINK,
+ CREATE_CHAR_DEVICE,
+ CREATE_BLOCK_DEVICE,
+ COPY_FILES,
+
+ WRITE_FILE,
+ IGNORE_PATH,
+ IGNORE_DIRECTORY_PATH,
+ REMOVE_PATH,
+ RECURSIVE_REMOVE_PATH);
+}
+
static struct Item* find_glob(Hashmap *h, const char *match) {
static struct Item* find_glob(Hashmap *h, const char *match) {
- HASHMAP_FOREACH(j, h, i)
- if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
- return j;
+ HASHMAP_FOREACH(j, h, i) {
+ unsigned n;
+
+ for (n = 0; n < j->count; n++) {
+ Item *item = j->items + n;
+
+ if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
+ return item;
+ }
+ }
- r = item_set_xattrs(i, i->path);
- if (r < 0)
- return r;
-
- r = item_set_xattrs(i, i->path);
- if (r < 0)
- return r;
-
- r = item_set_xattrs(i, i->path);
- if (r < 0)
- return r;
-
break;
case CREATE_SYMLINK:
break;
case CREATE_SYMLINK:
- r = item_set_xattrs(i, i->path);
- if (r < 0)
- return r;
-
break;
case CREATE_BLOCK_DEVICE:
break;
case CREATE_BLOCK_DEVICE:
- r = item_set_xattrs(i, i->path);
- if (r < 0)
- return r;
-
case REMOVE_PATH:
if (remove(instance) < 0 && errno != ENOENT)
case REMOVE_PATH:
if (remove(instance) < 0 && errno != ENOENT)
- return log_error_errno(errno, "remove(%s): %m", instance);
+ return log_error_errno(errno, "rm(%s): %m", instance);
+static int process_item_array(ItemArray *array);
+
static int process_item(Item *i) {
int r, q, p, t = 0;
_cleanup_free_ char *prefix = NULL;
static int process_item(Item *i) {
int r, q, p, t = 0;
_cleanup_free_ char *prefix = NULL;
return log_oom();
PATH_FOREACH_PREFIX(prefix, i->path) {
return log_oom();
PATH_FOREACH_PREFIX(prefix, i->path) {
j = hashmap_get(items, prefix);
if (j) {
int s;
j = hashmap_get(items, prefix);
if (j) {
int s;
+ s = process_item_array(j);
if (s < 0 && t == 0)
t = s;
}
if (s < 0 && t == 0)
t = s;
}
-static void item_free(Item *i) {
+static int process_item_array(ItemArray *array) {
+ unsigned n;
+ int r = 0, k;
+ assert(array);
+
+ for (n = 0; n < array->count; n++) {
+ k = process_item(array->items + n);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+
+ return r;
+}
+static void item_free_contents(Item *i) {
+ assert(i);
free(i->path);
free(i->argument);
strv_free(i->xattrs);
free(i->path);
free(i->argument);
strv_free(i->xattrs);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
+static void item_array_free(ItemArray *a) {
+ unsigned n;
+
+ if (!a)
+ return;
+
+ for (n = 0; n < a->count; n++)
+ item_free_contents(a->items + n);
+ free(a->items);
+ free(a);
+}
-static bool item_equal(Item *a, Item *b) {
+static bool item_compatible(Item *a, Item *b) {
+ assert(streq(a->path, b->path));
- if (!streq_ptr(a->path, b->path))
- return false;
+ if (takes_ownership(a->type) && takes_ownership(b->type))
+ /* check if the items are the same */
+ return streq_ptr(a->argument, b->argument) &&
- if (a->type != b->type)
- return false;
+ a->uid_set == b->uid_set &&
+ a->uid == b->uid &&
- if (a->uid_set != b->uid_set ||
- (a->uid_set && a->uid != b->uid))
- return false;
+ a->gid_set == b->gid_set &&
+ a->gid == b->gid &&
- if (a->gid_set != b->gid_set ||
- (a->gid_set && a->gid != b->gid))
- return false;
+ a->mode_set == b->mode_set &&
+ a->mode == b->mode &&
- if (a->mode_set != b->mode_set ||
- (a->mode_set && a->mode != b->mode))
- return false;
+ a->age_set == b->age_set &&
+ a->age == b->age &&
- if (a->age_set != b->age_set ||
- (a->age_set && a->age != b->age))
- return false;
+ a->mask_perms == b->mask_perms &&
- if ((a->type == CREATE_FILE ||
- a->type == TRUNCATE_FILE ||
- a->type == WRITE_FILE ||
- a->type == CREATE_SYMLINK ||
- a->type == COPY_FILES) &&
- !streq_ptr(a->argument, b->argument))
- return false;
+ a->keep_first_level == b->keep_first_level &&
- if ((a->type == CREATE_CHAR_DEVICE ||
- a->type == CREATE_BLOCK_DEVICE) &&
- a->major_minor != b->major_minor)
- return false;
+ a->major_minor == b->major_minor;
};
_cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = 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_(item_free_contents) Item i = {};
+ ItemArray *existing;
bool force = false, boot = false;
assert(fname);
bool force = false, boot = false;
assert(fname);
if (r < 2) {
log_error("[%s:%u] Syntax error.", fname, line);
return -EIO;
if (r < 2) {
log_error("[%s:%u] Syntax error.", fname, line);
return -EIO;
if (boot && !arg_boot)
return 0;
if (boot && !arg_boot)
return 0;
- i = new0(Item, 1);
- if (!i)
- return log_oom();
-
- i->type = action[0];
- i->force = force;
+ i.type = action[0];
+ i.force = force;
- r = specifier_printf(path, specifier_table, NULL, &i->path);
+ r = specifier_printf(path, specifier_table, NULL, &i.path);
if (r < 0) {
log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
return r;
}
if (r < 0) {
log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path);
return r;
}
- if (n >= 0) {
- n += strspn(buffer+n, WHITESPACE);
- if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
- i->argument = unquote(buffer+n, "\"");
- if (!i->argument)
+ if (c >= 0) {
+ c += strspn(buffer+c, WHITESPACE);
+ if (buffer[c] != 0 && (buffer[c] != '-' || buffer[c+1] != 0)) {
+ i.argument = unquote(buffer+c, "\"");
+ if (!i.argument)
case CREATE_FILE:
case TRUNCATE_FILE:
case CREATE_FILE:
case TRUNCATE_FILE:
break;
case CREATE_SYMLINK:
break;
case CREATE_SYMLINK:
- if (!i->argument) {
- i->argument = strappend("/usr/share/factory/", i->path);
- if (!i->argument)
+ if (!i.argument) {
+ i.argument = strappend("/usr/share/factory/", i.path);
+ if (!i.argument)
return log_oom();
}
break;
case WRITE_FILE:
return log_oom();
}
break;
case WRITE_FILE:
log_error("[%s:%u] Write file requires argument.", fname, line);
return -EBADMSG;
}
break;
case COPY_FILES:
log_error("[%s:%u] Write file requires argument.", fname, line);
return -EBADMSG;
}
break;
case COPY_FILES:
- if (!i->argument) {
- i->argument = strappend("/usr/share/factory/", i->path);
- if (!i->argument)
+ if (!i.argument) {
+ i.argument = strappend("/usr/share/factory/", i.path);
+ if (!i.argument)
- } else if (!path_is_absolute(i->argument)) {
+ } else if (!path_is_absolute(i.argument)) {
log_error("[%s:%u] Source path is not absolute.", fname, line);
return -EBADMSG;
}
log_error("[%s:%u] Source path is not absolute.", fname, line);
return -EBADMSG;
}
- path_kill_slashes(i->argument);
+ path_kill_slashes(i.argument);
break;
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE: {
unsigned major, minor;
break;
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE: {
unsigned major, minor;
log_error("[%s:%u] Device file requires argument.", fname, line);
return -EBADMSG;
}
log_error("[%s:%u] Device file requires argument.", fname, line);
return -EBADMSG;
}
- if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
- log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
+ if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) {
+ log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
- i->major_minor = makedev(major, minor);
+ i.major_minor = makedev(major, minor);
log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
return -EBADMSG;
}
log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
return -EBADMSG;
}
- r = get_xattrs_from_arg(i);
+ r = get_xattrs_from_arg(&i);
if (r < 0)
return r;
break;
default:
if (r < 0)
return r;
break;
default:
- log_error("[%s:%u] Unknown command type '%c'.", fname, line, i->type);
+ log_error("[%s:%u] Unknown command type '%c'.", fname, line, i.type);
- if (!path_is_absolute(i->path)) {
- log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
+ if (!path_is_absolute(i.path)) {
+ log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
- path_kill_slashes(i->path);
+ path_kill_slashes(i.path);
- if (!should_include_path(i->path))
+ if (!should_include_path(i.path))
return 0;
if (arg_root) {
char *p;
return 0;
if (arg_root) {
char *p;
- p = strappend(arg_root, i->path);
+ p = strappend(arg_root, i.path);
if (!p)
return log_oom();
if (!p)
return log_oom();
- free(i->path);
- i->path = p;
+ free(i.path);
+ i.path = p;
}
if (user && !streq(user, "-")) {
const char *u = user;
}
if (user && !streq(user, "-")) {
const char *u = user;
- r = get_user_creds(&u, &i->uid, NULL, NULL, NULL);
+ r = get_user_creds(&u, &i.uid, NULL, NULL, NULL);
if (r < 0) {
log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
return r;
}
if (r < 0) {
log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
return r;
}
}
if (group && !streq(group, "-")) {
const char *g = group;
}
if (group && !streq(group, "-")) {
const char *g = group;
- r = get_group_creds(&g, &i->gid);
+ r = get_group_creds(&g, &i.gid);
if (r < 0) {
log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
return r;
}
if (r < 0) {
log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
return r;
}
}
if (mode && !streq(mode, "-")) {
}
if (mode && !streq(mode, "-")) {
unsigned m;
if (*mm == '~') {
unsigned m;
if (*mm == '~') {
- i->mode = m;
- i->mode_set = true;
+ i.mode = m;
+ i.mode_set = true;
- i->mode =
- i->type == CREATE_DIRECTORY ||
- i->type == CREATE_SUBVOLUME ||
- i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
+ i.mode = IN_SET(i.type, CREATE_DIRECTORY, CREATE_SUBVOLUME, TRUNCATE_DIRECTORY)
+ ? 0755 : 0644;
if (age && !streq(age, "-")) {
const char *a = age;
if (*a == '~') {
if (age && !streq(age, "-")) {
const char *a = age;
if (*a == '~') {
- i->keep_first_level = true;
+ i.keep_first_level = true;
- if (parse_sec(a, &i->age) < 0) {
+ if (parse_sec(a, &i.age) < 0) {
log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
return -EBADMSG;
}
log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
return -EBADMSG;
}
- h = needs_glob(i->type) ? globs : items;
+ h = needs_glob(i.type) ? globs : items;
- existing = hashmap_get(h, i->path);
+ existing = hashmap_get(h, i.path);
- 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))
+ unsigned n;
+
+ for (n = 0; n < existing->count; n++) {
+ if (!item_compatible(existing->items + n, &i))
log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.",
- fname, line, i->path);
- return 0;
- r = hashmap_put(h, i->path, i);
- if (r < 0) {
- log_error("Failed to insert item %s: %s", i->path, strerror(-r));
- return r;
- }
+ existing = new0(ItemArray, 1);
+ r = hashmap_put(h, i.path, existing);
+ if (r < 0)
+ return log_oom();
- i = NULL; /* avoid cleanup */
+ if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
+ return log_oom();
+ memcpy(existing->items + existing->count++, &i, sizeof(i));
+ zero(i);
int main(int argc, char *argv[]) {
int r, k;
int main(int argc, char *argv[]) {
int r, k;
Iterator iterator;
r = parse_argv(argc, argv);
Iterator iterator;
r = parse_argv(argc, argv);
- HASHMAP_FOREACH(i, globs, iterator) {
- k = process_item(i);
+ HASHMAP_FOREACH(a, globs, iterator) {
+ k = process_item_array(a);
if (k < 0 && r == 0)
r = k;
}
if (k < 0 && r == 0)
r = k;
}
- HASHMAP_FOREACH(i, items, iterator) {
- k = process_item(i);
+ HASHMAP_FOREACH(a, items, iterator) {
+ k = process_item_array(a);
if (k < 0 && r == 0)
r = k;
}
finish:
if (k < 0 && r == 0)
r = k;
}
finish:
- while ((i = hashmap_steal_first(items)))
- item_free(i);
+ while ((a = hashmap_steal_first(items)))
+ item_array_free(a);
- while ((i = hashmap_steal_first(globs)))
- item_free(i);
+ while ((a = hashmap_steal_first(globs)))
+ item_array_free(a);
hashmap_free(items);
hashmap_free(globs);
hashmap_free(items);
hashmap_free(globs);