diff options
author | Lennart Poettering <lennart@poettering.net> | 2014-06-11 10:14:07 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2014-06-11 10:14:07 +0200 |
commit | abef3f91ce5fa9eeffceead885d2d2cabd9f1c96 (patch) | |
tree | a16b1bc5ec3b7d14ed5adceca99be6198f8e6b3a | |
parent | 1ebab2103d8f82822318e708363c0cc2b930289e (diff) |
tmpfiles: add ability to mask access mode by pre-existing access mode on files/directories
This way it makes a lot more sense to specify an access mode for "Z"
lines.
-rw-r--r-- | man/tmpfiles.d.xml | 16 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 37 |
2 files changed, 46 insertions, 7 deletions
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 5d8c2b5b32..0081a6762a 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -368,6 +368,22 @@ r! /tmp/.X[0-9]*-lock</programlisting> ignored for <varname>x</varname>, <varname>r</varname>, <varname>R</varname>, <varname>L</varname> lines.</para> + + <para>Optionally, if prefixed with + <literal>~</literal> the access mode is masked + based on the already set access bits for + existing file or directories: if the existing + file has all executable bits unset then all + executable bits are removed from the new + access mode, too. Similar, if all read bits + are removed from the old access mode they will + be removed from the new access mode too, and + if all write bits are removed, they will be + removed from the new access mode too. In + addition the sticky/suid/gid bit is removed unless + applied to a directory. This + functionality is particularly useful in + conjunction with <varname>Z</varname>.</para> </refsect2> <refsect2> diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index b80731d470..bb12dd0b5a 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -99,6 +99,7 @@ typedef struct Item { bool gid_set:1; bool mode_set:1; bool age_set:1; + bool mask_perms:1; bool keep_first_level:1; @@ -454,11 +455,29 @@ static int item_set_perms(Item *i, const char *path) { assert(path); /* not using i->path directly because it may be a glob */ - if (i->mode_set) - if (chmod(path, i->mode) < 0) { + if (i->mode_set) { + mode_t m = i->mode; + + if (i->mask_perms) { + struct stat st; + + if (stat(path, &st) >= 0) { + if (!(st.st_mode & 0111)) + m &= ~0111; + if (!(st.st_mode & 0222)) + m &= ~0222; + if (!(st.st_mode & 0444)) + m &= ~0444; + if (!S_ISDIR(st.st_mode)) + m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ + } + } + + if (chmod(path, m) < 0) { log_error("chmod(%s) failed: %m", path); return -errno; } + } if (i->uid_set || i->gid_set) if (chown(path, @@ -1056,15 +1075,13 @@ static bool item_equal(Item *a, Item *b) { static bool should_include_path(const char *path) { char **prefix; - STRV_FOREACH(prefix, arg_exclude_prefixes) { + STRV_FOREACH(prefix, arg_exclude_prefixes) if (path_startswith(path, *prefix)) return false; - } - STRV_FOREACH(prefix, arg_include_prefixes) { + STRV_FOREACH(prefix, arg_include_prefixes) if (path_startswith(path, *prefix)) return true; - } /* no matches, so we should include this path only if we * have no whitelist at all */ @@ -1249,9 +1266,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { } if (mode && !streq(mode, "-")) { + const char *mm = mode; unsigned m; - if (sscanf(mode, "%o", &m) != 1) { + if (*mm == '~') { + i->mask_perms = true; + mm++; + } + + if (sscanf(mm, "%o", &m) != 1) { log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); return -ENOENT; } |