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:
parent
1ebab2103d
commit
abef3f91ce
|
@ -368,6 +368,22 @@ r! /tmp/.X[0-9]*-lock</programlisting>
|
||||||
ignored for <varname>x</varname>,
|
ignored for <varname>x</varname>,
|
||||||
<varname>r</varname>, <varname>R</varname>,
|
<varname>r</varname>, <varname>R</varname>,
|
||||||
<varname>L</varname> lines.</para>
|
<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>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
|
|
|
@ -99,6 +99,7 @@ typedef struct Item {
|
||||||
bool gid_set:1;
|
bool gid_set:1;
|
||||||
bool mode_set:1;
|
bool mode_set:1;
|
||||||
bool age_set:1;
|
bool age_set:1;
|
||||||
|
bool mask_perms:1;
|
||||||
|
|
||||||
bool keep_first_level:1;
|
bool keep_first_level:1;
|
||||||
|
|
||||||
|
@ -454,11 +455,29 @@ static int item_set_perms(Item *i, const char *path) {
|
||||||
assert(path);
|
assert(path);
|
||||||
|
|
||||||
/* not using i->path directly because it may be a glob */
|
/* not using i->path directly because it may be a glob */
|
||||||
if (i->mode_set)
|
if (i->mode_set) {
|
||||||
if (chmod(path, i->mode) < 0) {
|
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);
|
log_error("chmod(%s) failed: %m", path);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (i->uid_set || i->gid_set)
|
if (i->uid_set || i->gid_set)
|
||||||
if (chown(path,
|
if (chown(path,
|
||||||
|
@ -1056,15 +1075,13 @@ static bool item_equal(Item *a, Item *b) {
|
||||||
static bool should_include_path(const char *path) {
|
static bool should_include_path(const char *path) {
|
||||||
char **prefix;
|
char **prefix;
|
||||||
|
|
||||||
STRV_FOREACH(prefix, arg_exclude_prefixes) {
|
STRV_FOREACH(prefix, arg_exclude_prefixes)
|
||||||
if (path_startswith(path, *prefix))
|
if (path_startswith(path, *prefix))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
STRV_FOREACH(prefix, arg_include_prefixes) {
|
STRV_FOREACH(prefix, arg_include_prefixes)
|
||||||
if (path_startswith(path, *prefix))
|
if (path_startswith(path, *prefix))
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
/* no matches, so we should include this path only if we
|
/* no matches, so we should include this path only if we
|
||||||
* have no whitelist at all */
|
* 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, "-")) {
|
if (mode && !streq(mode, "-")) {
|
||||||
|
const char *mm = mode;
|
||||||
unsigned m;
|
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);
|
log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue