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.
This commit is contained in:
Lennart Poettering 2014-06-11 10:14:07 +02:00
parent 1ebab2103d
commit abef3f91ce
2 changed files with 46 additions and 7 deletions

View File

@ -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>

View File

@ -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;
}