label: plumb label_context through LabelOps

SELinux labeling with --root/--image needs per-call context to carry an
alternate policy database and root prefix. The current LabelOps interface
has no way to pass this, forcing any solution to rely on global state.

Add a LabelContext *label_context to the label_ops callbacks and all
intermediate layers. Generic filesystem functions that most callers use
(xopenat_full, write_string_file_full, etc.) get a _label variant
carrying the extra parameter, with inline wrappers preserving the
original signatures so the vast majority of call sites remain untouched.

Signed-off-by: Simon de Vlieger <cmdr@supakeen.com>
This commit is contained in:
Simon de Vlieger
2026-07-31 09:49:41 +02:00
parent 63e35ca3f9
commit 6b0e3ea222
33 changed files with 192 additions and 169 deletions
+22 -21
View File
@@ -25,16 +25,17 @@ int mkdirat_safe_internal(
uid_t uid,
gid_t gid,
MkdirFlags flags,
mkdirat_func_t _mkdirat) {
mkdirat_func_t _mkdirat,
LabelContext *label_context) {
struct stat st;
int r;
assert(path);
assert(mode != MODE_INVALID);
assert(_mkdirat && _mkdirat != mkdirat);
assert(_mkdirat);
r = _mkdirat(dir_fd, path, mode);
r = _mkdirat(dir_fd, path, mode, label_context);
if (r >= 0)
return chmod_and_chown_at(dir_fd, path, mode, uid, gid);
if (r != -EEXIST)
@@ -52,7 +53,7 @@ int mkdirat_safe_internal(
if (r == 0)
return mkdirat_safe_internal(dir_fd, p, mode, uid, gid,
flags & ~MKDIR_FOLLOW_SYMLINK,
_mkdirat);
_mkdirat, label_context);
if (fstatat(dir_fd, p, &st, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
@@ -80,20 +81,20 @@ int mkdirat_safe_internal(
return 0;
}
int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode) {
int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode, LabelContext *label_context) {
return RET_NERRNO(mkdirat(dirfd, pathname, mode));
}
int mkdirat_safe(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
return mkdirat_safe_internal(dir_fd, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
return mkdirat_safe_internal(dir_fd, path, mode, uid, gid, flags, mkdirat_errno_wrapper, /* label_context= */ NULL);
}
int mkdirat_parents_internal(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat) {
int mkdirat_parents_internal(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat, LabelContext *label_context) {
const char *e = NULL;
int r;
assert(path);
assert(_mkdirat != mkdirat);
assert(_mkdirat);
if (isempty(path))
return 0;
@@ -134,7 +135,7 @@ int mkdirat_parents_internal(int dir_fd, const char *path, mode_t mode, uid_t ui
s[n] = '\0';
r = mkdirat_safe_internal(dir_fd, path, mode, uid, gid, flags | MKDIR_IGNORE_EXISTING, _mkdirat);
r = mkdirat_safe_internal(dir_fd, path, mode, uid, gid, flags | MKDIR_IGNORE_EXISTING, _mkdirat, label_context);
if (r < 0 && r != -EEXIST)
return r;
@@ -142,12 +143,12 @@ int mkdirat_parents_internal(int dir_fd, const char *path, mode_t mode, uid_t ui
}
}
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat) {
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat, LabelContext *label_context) {
_cleanup_close_ int fd = AT_FDCWD;
const char *p;
assert(path);
assert(_mkdirat != mkdirat);
assert(_mkdirat);
if (prefix) {
p = path_startswith_full(path, prefix, PATH_STARTSWITH_REFUSE_DOT_DOT);
@@ -160,34 +161,34 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, ui
} else
p = path;
return mkdirat_parents_internal(fd, p, mode, uid, gid, flags, _mkdirat);
return mkdirat_parents_internal(fd, p, mode, uid, gid, flags, _mkdirat, label_context);
}
int mkdirat_parents(int dir_fd, const char *path, mode_t mode) {
return mkdirat_parents_internal(dir_fd, path, mode, UID_INVALID, UID_INVALID, 0, mkdirat_errno_wrapper);
return mkdirat_parents_internal(dir_fd, path, mode, UID_INVALID, UID_INVALID, 0, mkdirat_errno_wrapper, /* label_context= */ NULL);
}
int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
return mkdir_parents_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
return mkdir_parents_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper, /* label_context= */ NULL);
}
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat) {
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdirat_func_t _mkdirat, LabelContext *label_context) {
int r;
/* Like mkdir -p */
assert(_mkdirat != mkdirat);
assert(_mkdirat);
r = mkdir_parents_internal(prefix, path, mode, uid, gid, flags | MKDIR_FOLLOW_SYMLINK, _mkdirat);
r = mkdir_parents_internal(prefix, path, mode, uid, gid, flags | MKDIR_FOLLOW_SYMLINK, _mkdirat, label_context);
if (r < 0)
return r;
if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) {
r = _mkdirat(AT_FDCWD, path, mode);
r = _mkdirat(AT_FDCWD, path, mode, label_context);
if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
return r;
} else {
r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdirat);
r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdirat, label_context);
if (r < 0 && r != -EEXIST)
return r;
}
@@ -196,11 +197,11 @@ int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t ui
}
int mkdir_p(const char *path, mode_t mode) {
return mkdir_p_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdirat_errno_wrapper);
return mkdir_p_internal(/* prefix= */ NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdirat_errno_wrapper, /* label_context= */ NULL);
}
int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper);
return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdirat_errno_wrapper, /* label_context= */ NULL);
}
int mkdir_p_root_full(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m, usec_t ts, Hashmap *subvolumes) {