libguestfs/0013-daemon-Deprecate-guestfs_selinux_relabel-replace-wit.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

312 lines
12 KiB
Diff

From 1c0b56158aa63359d1e53f7a31b483194f235a34 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 13:27:32 +0100
Subject: [PATCH] daemon: Deprecate guestfs_selinux_relabel, replace with
guestfs_setfiles
The guestfs_selinux_relabel function was very hard to use. In
particular it didn't just do an SELinux relabel as you might expect.
Instead you have to write a whole bunch of code around it (example[1])
to make it useful.
Another problem is that it doesn't let you pass multiple paths to the
setfiles command, but the command itself does permit that (and, as it
turns out, will require it). There is no backwards compatible way to
extend the existing definition to allow a list parameter without
breaking API.
So deprecate guestfs_selinux_relabel. Reimplement it as
guestfs_setfiles. The new function is basically the same as the old
one, but allows you to pass a list of paths. The old function calls
the new function with a single path parameter.
[1] https://github.com/libguestfs/libguestfs-common/blob/master/mlcustomize/SELinux_relabel.ml
---
daemon/selinux.ml | 117 ++++++++++++++-------------
generator/actions_core.ml | 49 +++++------
generator/actions_core_deprecated.ml | 24 ++++++
generator/proc_nr.ml | 1 +
gobject/Makefile.inc | 2 +
lib/MAX_PROC_NR | 2 +-
tests/relabel/test-relabel.pl | 2 +-
7 files changed, 117 insertions(+), 80 deletions(-)
diff --git a/daemon/selinux.ml b/daemon/selinux.ml
index d954fdead..db0d71455 100644
--- a/daemon/selinux.ml
+++ b/daemon/selinux.ml
@@ -44,58 +44,65 @@ let 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
+let setfiles ?(force = false) specfile paths =
+ if paths = [] then ()
+ else (
+ (* Prefix /sysroot on all paths. *)
+ let ignored_paths =
+ [ "/dev"; "/proc"; "/selinux"; "/sys" ] |>
+ List.map sysroot_path in
+ let specfile = sysroot_path specfile in
+ let paths = List.map sysroot_path paths 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 args specfile;
+ List.push_back_list args paths;
+
+ 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
+ )
+
+(* This is the deprecated selinux_relabel function from libguestfs <= 1.56. *)
+let selinux_relabel ?force specfile path = setfiles ?force specfile [path]
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 128cbe0e9..60d3140ed 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -9356,29 +9356,6 @@ Show all the devices where the filesystems in C<device> is spanned over.
If not all the devices for the filesystems are present, then this function
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";
- longdesc = "\
-SELinux relabel parts of the filesystem.
-
-The C<specfile> parameter controls the policy spec file used.
-You have to parse C</etc/selinux/config> to find the correct
-SELinux policy and then pass the spec file, usually:
-C</etc/selinux/> + I<selinuxtype> + C</contexts/files/file_contexts>.
-
-The required C<path> parameter is the top level directory where
-relabelling starts. Normally you should pass C<path> as C</>
-to relabel the whole guest filesystem.
-
-The optional C<force> boolean controls whether the context
-is reset for customizable files, and also whether the
-user, role and range parts of the file context is changed." };
-
{ defaults with
name = "mksquashfs"; added = (1, 35, 25);
style = RErr, [String (Pathname, "path"); String (FileOut, "filename")], [OString "compress"; OStringList "excludes"];
@@ -9820,4 +9797,30 @@ them visible.
Use C<guestfs_list_dm_devices> to list all device mapper devices." };
+ { defaults with
+ name = "setfiles"; added = (1, 57, 1);
+ style = RErr, [String (PlainString, "specfile"); StringList (Pathname, "paths")], [OBool "force"];
+ impl = OCaml "Selinux.setfiles";
+ optional = Some "selinuxrelabel";
+ test_excuse = "tests are in the tests/relabel directory";
+ shortdesc = "low level relabel parts of the filesystem";
+ longdesc = "\
+This invokes the SELinux C<setfiles> command which is a low
+level tool used to relabel parts of the filesystem.
+
+The C<specfile> parameter controls the policy spec file used.
+You have to parse C</etc/selinux/config> to find the correct
+SELinux policy and then pass the spec file, usually:
+C</etc/selinux/> + I<selinuxtype> + C</contexts/files/file_contexts>.
+
+The required C<paths> parameter is the list of top level directories
+where relabelling starts. C<setfiles> will only relabel up to
+filesystem boundaries so, for example, passing just C<\"/\"> will
+relabel the whole root filesystem, but no other mounted filesystems.
+If the list is empty, setfiles is not called.
+
+The optional C<force> boolean controls whether the context
+is reset for customizable files, and also whether the
+user, role and range parts of the file context is changed." };
+
]
diff --git a/generator/actions_core_deprecated.ml b/generator/actions_core_deprecated.ml
index 9d4b29f9d..2b1f5cdb4 100644
--- a/generator/actions_core_deprecated.ml
+++ b/generator/actions_core_deprecated.ml
@@ -942,4 +942,28 @@ This call does nothing and returns an error." };
Used to check a btrfs filesystem, C<device> is the device file where the
filesystem is stored." };
+ { 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";
+ deprecated_by = Replaced_by "setfiles";
+ test_excuse = "tests are in the tests/relabel directory";
+ shortdesc = "relabel parts of the filesystem";
+ longdesc = "\
+SELinux relabel parts of the filesystem.
+
+The C<specfile> parameter controls the policy spec file used.
+You have to parse C</etc/selinux/config> to find the correct
+SELinux policy and then pass the spec file, usually:
+C</etc/selinux/> + I<selinuxtype> + C</contexts/files/file_contexts>.
+
+The required C<path> parameter is the top level directory where
+relabelling starts. Normally you should pass C<path> as C</>
+to relabel the whole guest filesystem.
+
+The optional C<force> boolean controls whether the context
+is reset for customizable files, and also whether the
+user, role and range parts of the file context is changed." };
+
]
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index 63cd72a3c..42624afef 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -521,6 +521,7 @@ let proc_nr = [
516, "command_out";
517, "sh_out";
518, "btrfs_scrub_full";
+519, "setfiles";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/gobject/Makefile.inc b/gobject/Makefile.inc
index b54245977..b828113c6 100644
--- a/gobject/Makefile.inc
+++ b/gobject/Makefile.inc
@@ -106,6 +106,7 @@ guestfs_gobject_headers= \
include/guestfs-gobject/optargs-rsync_out.h \
include/guestfs-gobject/optargs-selinux_relabel.h \
include/guestfs-gobject/optargs-set_e2attrs.h \
+ include/guestfs-gobject/optargs-setfiles.h \
include/guestfs-gobject/optargs-syslinux.h \
include/guestfs-gobject/optargs-tar_in.h \
include/guestfs-gobject/optargs-tar_out.h \
@@ -201,6 +202,7 @@ guestfs_gobject_sources= \
src/optargs-rsync_out.c \
src/optargs-selinux_relabel.c \
src/optargs-set_e2attrs.c \
+ src/optargs-setfiles.c \
src/optargs-syslinux.c \
src/optargs-tar_in.c \
src/optargs-tar_out.c \
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index 9a26b94d0..08f851b6e 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-518
+519
diff --git a/tests/relabel/test-relabel.pl b/tests/relabel/test-relabel.pl
index 06fb0840b..4d4f6c7ba 100755
--- a/tests/relabel/test-relabel.pl
+++ b/tests/relabel/test-relabel.pl
@@ -87,7 +87,7 @@ $g->write ("/etc/file_contexts", <<'EOF');
EOF
# Do the relabel.
-$g->selinux_relabel ("/etc/file_contexts", "/", force => 1);
+$g->setfiles ("/etc/file_contexts", ["/"], force => 1);
# Check the labels were set correctly.
my $errors = 0;
--
2.47.1