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>,
|
||||
<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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue