libguestfs/0010-daemon-Reimplement-guestfs_selinux_relabel-in-OCaml.patch
Richard W.M. Jones 583fc558f3 Fix conversion of SLES15 with encrypted btrfs filesystem
resolves: RHEL-93584
Add guestfs_setfiles API
resolves: RHEL-108832
2025-08-13 18:09:25 +01:00

407 lines
12 KiB
Diff

From ed40333a23ae8f20ac0360df444d10db369fa6d9 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 12:22:42 +0100
Subject: [PATCH] daemon: Reimplement guestfs_selinux_relabel in OCaml
No change, just reimplement the existing C implementation in OCaml.
---
.gitignore | 1 +
daemon/Makefile.am | 4 +-
daemon/selinux-relabel.c | 169 --------------------------------------
daemon/selinux.c | 7 ++
daemon/selinux.ml | 101 +++++++++++++++++++++++
docs/C_SOURCE_FILES | 1 -
generator/actions_core.ml | 1 +
po/POTFILES | 1 -
8 files changed, 113 insertions(+), 172 deletions(-)
delete mode 100644 daemon/selinux-relabel.c
create mode 100644 daemon/selinux.ml
diff --git a/.gitignore b/.gitignore
index 81cd278cc..02160caff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,6 +108,7 @@ Makefile.in
/daemon/parted.mli
/daemon/realpath.mli
/daemon/rpm.mli
+/daemon/selinux.mli
/daemon/sfdisk.mli
/daemon/stamp-guestfsd.pod
/daemon/statvfs.mli
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 6d7492013..c644d9881 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -59,6 +59,7 @@ generator_built = \
parted.mli \
realpath.mli \
rpm.mli \
+ selinux.mli \
sfdisk.mli \
statvfs.mli \
structs.ml \
@@ -173,7 +174,6 @@ guestfsd_SOURCES = \
rsync.c \
scrub.c \
selinux.c \
- selinux-relabel.c \
sfdisk.c \
sh.c \
sleep.c \
@@ -307,6 +307,7 @@ SOURCES_MLI = \
parted.mli \
realpath.mli \
rpm.mli \
+ selinux.mli \
sfdisk.mli \
statvfs.mli \
structs.mli \
@@ -345,6 +346,7 @@ SOURCES_ML = \
listfs.ml \
realpath.ml \
statvfs.ml \
+ selinux.ml \
inspect_types.ml \
inspect_utils.ml \
inspect_fs_unix_fstab.ml \
diff --git a/daemon/selinux-relabel.c b/daemon/selinux-relabel.c
deleted file mode 100644
index cfc5a31d9..000000000
--- a/daemon/selinux-relabel.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/* libguestfs - the guestfsd daemon
- * Copyright (C) 2016 Red Hat Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "guestfs_protocol.h"
-#include "daemon.h"
-#include "actions.h"
-#include "optgroups.h"
-
-#include "ignore-value.h"
-
-#define MAX_ARGS 64
-
-int
-optgroup_selinuxrelabel_available (void)
-{
- return prog_exists ("setfiles");
-}
-
-static int
-dir_exists (const char *dir)
-{
- struct stat statbuf;
-
- if (stat (dir, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
- return 1;
- else
- return 0;
-}
-
-static int
-setfiles_has_option (int *flag, char opt_char)
-{
- CLEANUP_FREE char *err = NULL;
-
- if (*flag == -1) {
- char option[] = { '-', opt_char, '\0' }; /* "-X" */
- char err_opt[32]; /* "invalid option -- 'X'" */
-
- snprintf(err_opt, sizeof(err_opt), "invalid option -- '%c'", opt_char);
- ignore_value (command (NULL, &err, "setfiles", option, NULL));
- *flag = err && strstr (err, /* "invalid option -- " */ err_opt) == NULL;
- }
-
- return *flag;
-}
-
-/* Takes optional arguments, consult optargs_bitmask. */
-int
-do_selinux_relabel (const char *specfile, const char *path,
- int force)
-{
- static int flag_m = -1;
- static int flag_C = -1;
- static int flag_T = -1;
- const char *argv[MAX_ARGS];
- CLEANUP_FREE char *s_dev = NULL, *s_proc = NULL, *s_selinux = NULL,
- *s_sys = NULL, *s_specfile = NULL, *s_path = NULL;
- CLEANUP_FREE char *err = NULL;
- size_t i = 0;
- int setfiles_status;
-
- s_dev = sysroot_path ("/dev");
- if (!s_dev) {
- malloc_error:
- reply_with_perror ("malloc");
- return -1;
- }
- s_proc = sysroot_path ("/proc"); if (!s_proc) goto malloc_error;
- s_selinux = sysroot_path ("/selinux"); if (!s_selinux) goto malloc_error;
- s_sys = sysroot_path ("/sys"); if (!s_sys) goto malloc_error;
- s_specfile = sysroot_path (specfile); if (!s_specfile) goto malloc_error;
- s_path = sysroot_path (path); if (!s_path) goto malloc_error;
-
- /* Default settings if not selected. */
- if (!(optargs_bitmask & GUESTFS_SELINUX_RELABEL_FORCE_BITMASK))
- force = 0;
-
- /* If setfiles takes an excessively long time to run (but still
- * completes) then removing .../contexts/files/file_contexts.bin
- * appears to help. If you find any such cases, please add
- * observations to the bug report:
- * https://bugzilla.redhat.com/show_bug.cgi?id=1396297
- */
- ADD_ARG (argv, i, "setfiles");
- if (force)
- ADD_ARG (argv, i, "-F");
-
- /* Exclude some directories that should never be relabelled in
- * ordinary Linux guests. These won't be mounted anyway. We have
- * to prefix all these with the sysroot path.
- */
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_dev);
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_proc);
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_sys);
- if (dir_exists (s_selinux)) {
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_selinux);
- }
-
- /* You have to use the -m option (where available) otherwise
- * setfiles puts all the mountpoints on the excludes list for no
- * useful reason (RHBZ#1433577).
- */
- if (setfiles_has_option (&flag_m, 'm'))
- ADD_ARG (argv, i, "-m");
-
- /* Not only do we want setfiles to trudge through individual relabeling
- * errors, we also want the setfiles exit status to differentiate a fatal
- * error from "relabeling errors only". See RHBZ#1794518.
- */
- if (setfiles_has_option (&flag_C, 'C'))
- ADD_ARG (argv, i, "-C");
-
- /* If the appliance is being run with multiple vCPUs, running setfiles
- * in multithreading mode might speeds up the process. Option "-T" was
- * introduced in SELinux userspace v3.4, and we need to check whether it's
- * supported. Passing "-T 0" creates as many threads as there're available
- * vCPU cores.
- * https://github.com/SELinuxProject/selinux/releases/tag/3.4
- */
- if (setfiles_has_option (&flag_T, 'T')) {
- ADD_ARG (argv, i, "-T"); ADD_ARG (argv, i, "0");
- }
-
- /* Relabelling in a chroot. */
- if (STRNEQ (sysroot, "/")) {
- ADD_ARG (argv, i, "-r");
- ADD_ARG (argv, i, sysroot);
- }
-
- if (verbose)
- ADD_ARG (argv, i, "-v");
- else
- /* Suppress non-error output. */
- ADD_ARG (argv, i, "-q");
-
- /* Add parameters. */
- ADD_ARG (argv, i, s_specfile);
- ADD_ARG (argv, i, s_path);
- ADD_ARG (argv, i, NULL);
-
- setfiles_status = commandrv (NULL, &err, argv);
- if ((setfiles_status == 0) || (setfiles_status == 1 && flag_C))
- return 0;
-
- reply_with_error ("%s", err);
- return -1;
-}
diff --git a/daemon/selinux.c b/daemon/selinux.c
index f4d839c19..4500d0096 100644
--- a/daemon/selinux.c
+++ b/daemon/selinux.c
@@ -39,6 +39,13 @@ optgroup_selinux_available (void)
return 1;
}
+/* For historical reasons, this is really "is setfiles available" */
+int
+optgroup_selinuxrelabel_available (void)
+{
+ return prog_exists ("setfiles");
+}
+
/* setcon is only valid under the following circumstances:
* - single threaded
* - enforcing=0
diff --git a/daemon/selinux.ml b/daemon/selinux.ml
new file mode 100644
index 000000000..d954fdead
--- /dev/null
+++ b/daemon/selinux.ml
@@ -0,0 +1,101 @@
+(* SELinux functions.
+ * Copyright (C) 2009-2025 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+open Sysroot
+open Utils
+
+(* Test if setfiles has various options.
+ *
+ * The only way to do this is to run setfiles with the option alone, and
+ * test for the stderr message [invalid option -- 'X'].
+ *)
+let setfiles_has_option_m,
+ setfiles_has_option_C,
+ setfiles_has_option_T =
+ let setfiles_has_option flag =
+ let err_msg = sprintf "invalid option -- '%c'" flag in
+ let opt = sprintf "-%c" flag in
+ let _, _, err = commandr "setfiles" [opt] in
+ String.find err err_msg = -1
+ in
+ let setfiles_has_option_m = lazy (setfiles_has_option 'm')
+ and setfiles_has_option_C = lazy (setfiles_has_option 'C')
+ and setfiles_has_option_T = lazy (setfiles_has_option 'T') in
+ (fun () -> Lazy.force setfiles_has_option_m),
+ (fun () -> Lazy.force setfiles_has_option_C),
+ (fun () -> Lazy.force setfiles_has_option_T)
+
+let selinux_relabel ?(force = false) specfile path =
+ (* Prefix /sysroot on all paths. *)
+ let ignored_paths =
+ [ "/dev"; "/proc"; "/selinux"; "/sys" ] |>
+ List.map sysroot_path in
+ let specfile = sysroot_path specfile in
+ let path = sysroot_path path in
+
+ let args = ref [] in
+ if force then List.push_back args "-F";
+ List.iter (
+ fun ignored_path ->
+ List.push_back_list args [ "-e"; ignored_path ]
+ ) ignored_paths;
+
+ (* You have to use the -m option (where available) otherwise
+ * setfiles puts all the mountpoints on the excludes list for no
+ * useful reason (RHBZ#1433577).
+ *)
+ if setfiles_has_option_m () then List.push_back args "-m";
+
+ (* Not only do we want setfiles to trudge through individual relabeling
+ * errors, we also want the setfiles exit status to differentiate a fatal
+ * error from "relabeling errors only". See RHBZ#1794518.
+ *)
+ if setfiles_has_option_C () then List.push_back args "-C";
+
+ (* If the appliance is being run with multiple vCPUs, running setfiles
+ * in multithreading mode might speeds up the process. Option "-T" was
+ * introduced in SELinux userspace v3.4, and we need to check whether it's
+ * supported. Passing "-T 0" creates as many threads as there're available
+ * vCPU cores.
+ * https://github.com/SELinuxProject/selinux/releases/tag/3.4
+ *)
+ if setfiles_has_option_T () then
+ List.push_back_list args [ "-T"; "0" ];
+
+ (* Relabelling in a chroot. *)
+ if sysroot () <> "/" then
+ List.push_back_list args [ "-r"; sysroot () ];
+
+ if verbose () then
+ List.push_back args "-v"
+ else
+ (* Suppress non-error output. *)
+ List.push_back args "-q";
+
+ (* Add parameters. *)
+ List.push_back_list args [ specfile; path ];
+
+ let args = !args in
+ let r, _, err = commandr "setfiles" args in
+
+ let ok = r = 0 || r = 1 && setfiles_has_option_C () in
+ if not ok then failwithf "setfiles: %s" err
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index cdfb1d615..5270667bf 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -132,7 +132,6 @@ daemon/rename.c
daemon/rpm-c.c
daemon/rsync.c
daemon/scrub.c
-daemon/selinux-relabel.c
daemon/selinux.c
daemon/sfdisk.c
daemon/sh.c
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 108494ece..128cbe0e9 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -9359,6 +9359,7 @@ fails and the C<errno> is set to C<ENODEV>." };
{ defaults with
name = "selinux_relabel"; added = (1, 33, 43);
style = RErr, [String (PlainString, "specfile"); String (Pathname, "path")], [OBool "force"];
+ impl = OCaml "Selinux.selinux_relabel";
optional = Some "selinuxrelabel";
test_excuse = "tests are in the tests/relabel directory";
shortdesc = "relabel parts of the filesystem";
diff --git a/po/POTFILES b/po/POTFILES
index acf3a68d7..fbe0a7fe2 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -110,7 +110,6 @@ daemon/rename.c
daemon/rpm-c.c
daemon/rsync.c
daemon/scrub.c
-daemon/selinux-relabel.c
daemon/selinux.c
daemon/sfdisk.c
daemon/sh.c
--
2.47.1