diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 5612b4803d..a6246844cb 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -148,6 +148,12 @@
+
+
+ Ignore configuration lines pertaining to unknown users or groups. This option is
+ intended to be used in early boot before all users or groups have been created.
+
+
Only apply rules with paths that start with
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 5232f17de7..518d8012da 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -200,6 +200,7 @@ static bool arg_cat_config = false;
static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
static OperationMask arg_operation = 0;
static bool arg_boot = false;
+static bool arg_graceful = false;
static PagerFlags arg_pager_flags = 0;
static char **arg_include_prefixes = NULL;
@@ -1131,7 +1132,8 @@ static int parse_acls_from_arg(Item *item) {
r = parse_acl(item->argument, &item->acl_access, &item->acl_access_exec,
&item->acl_default, !item->append_or_force);
if (r < 0)
- log_warning_errno(r, "Failed to parse ACL \"%s\", ignoring: %m", item->argument);
+ log_full_errno(arg_graceful && IN_SET(r, -ENOENT, -ESRCH) ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to parse ACL \"%s\", ignoring: %m", item->argument);
#else
log_warning("ACLs are not supported, ignoring.");
#endif
@@ -3322,7 +3324,8 @@ static int parse_line(
ItemArray *existing;
OrderedHashmap *h;
int r, pos;
- bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false, from_cred = false;
+ bool append_or_force = false, boot = false, allow_failure = false, try_replace = false,
+ unbase64 = false, from_cred = false, missing_user_or_group = false;
assert(fname);
assert(line >= 1);
@@ -3669,12 +3672,15 @@ static int parse_line(
u = user;
r = find_uid(u, &i.uid, uid_cache);
- if (r < 0) {
+ if (r == -ESRCH && arg_graceful) {
+ log_syntax(NULL, LOG_DEBUG, fname, line, r,
+ "%s: user '%s' not found, not adjusting ownership.", i.path, u);
+ missing_user_or_group = true;
+ } else if (r < 0) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve user '%s': %m", u);
- }
-
- i.uid_set = true;
+ } else
+ i.uid_set = true;
}
if (!empty_or_dash(group)) {
@@ -3687,12 +3693,15 @@ static int parse_line(
g = group;
r = find_gid(g, &i.gid, gid_cache);
- if (r < 0) {
+ if (r == -ESRCH && arg_graceful) {
+ log_syntax(NULL, LOG_DEBUG, fname, line, r,
+ "%s: group '%s' not found, not adjusting ownership.", i.path, g);
+ missing_user_or_group = true;
+ } else if (r < 0) {
*invalid_config = true;
- return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve group '%s'.", g);
- }
-
- i.gid_set = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve group '%s': %m", g);
+ } else
+ i.gid_set = true;
}
if (!empty_or_dash(mode)) {
@@ -3724,6 +3733,14 @@ static int parse_line(
CREATE_SUBVOLUME_INHERIT_QUOTA,
CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644;
+ if (missing_user_or_group && (i.mode & ~0777) != 0) {
+ /* Refuse any special bits for nodes where we couldn't resolve the ownership properly. */
+ mode_t adjusted = i.mode & 0777;
+ log_syntax(NULL, LOG_INFO, fname, line, 0,
+ "Changing mode 0%o to 0%o because of changed ownership.", i.mode, adjusted);
+ i.mode = adjusted;
+ }
+
if (!empty_or_dash(age)) {
const char *a = age;
_cleanup_free_ char *seconds = NULL, *age_by = NULL;
@@ -3844,6 +3861,7 @@ static int help(void) {
" --clean Clean up marked directories\n"
" --remove Remove marked files/directories\n"
" --boot Execute actions only safe at boot\n"
+ " --graceful Quitely ignore unknown users or groups\n"
" --prefix=PATH Only apply rules with the specified prefix\n"
" --exclude-prefix=PATH Ignore rules with the specified prefix\n"
" -E Ignore rules prefixed with /dev, /proc, /run, /sys\n"
@@ -3871,6 +3889,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_CLEAN,
ARG_REMOVE,
ARG_BOOT,
+ ARG_GRACEFUL,
ARG_PREFIX,
ARG_EXCLUDE_PREFIX,
ARG_ROOT,
@@ -3889,6 +3908,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "clean", no_argument, NULL, ARG_CLEAN },
{ "remove", no_argument, NULL, ARG_REMOVE },
{ "boot", no_argument, NULL, ARG_BOOT },
+ { "graceful", no_argument, NULL, ARG_GRACEFUL },
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
{ "root", required_argument, NULL, ARG_ROOT },
@@ -3938,6 +3958,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_boot = true;
break;
+ case ARG_GRACEFUL:
+ arg_graceful = true;
+ break;
+
case ARG_PREFIX:
if (strv_push(&arg_include_prefixes, optarg) < 0)
return log_oom();