File 0001-test3.patch of Package systemd

From b3660c1407c86a679c4ee5efc17e34a3398c3bb5 Mon Sep 17 00:00:00 2001
From: Antonio Alvarez Feijoo <antonio.feijoo@suse.com>
Date: Fri, 12 Dec 2025 15:57:01 +0100
Subject: [PATCH] test3

---
 docs/PRESET.md               |   6 +-
 man/systemd.preset.xml       |  28 +++++-----
 man/systemd.unit.xml         |   2 +-
 src/shared/install.c         |  32 ++++++-----
 src/test/test-install-root.c | 104 +++++++++++++++++++++++++++++++++--
 5 files changed, 137 insertions(+), 35 deletions(-)

diff --git a/docs/PRESET.md b/docs/PRESET.md
index ba3c0a4770..76efdd1f99 100644
--- a/docs/PRESET.md
+++ b/docs/PRESET.md
@@ -25,16 +25,18 @@ Traditionally, policy about what services shall be enabled and what services sha
 This made it cumbersome to implement different policies per spin or per site, or to create software packages that do the right thing on more than one distribution.
 The enablement _mechanism_ was also encoding the enablement _policy_.
 
-systemd 32 and newer support package "preset" policies.
+systemd 32 and newer supports package "preset" policies.
 These encode which units shall be enabled by default when they are installed, and which units shall not be enabled.
 
+Since version 260, systemd gained a new preset operation: mask. This allows to specify in the policy which units shall be masked by default when they are installed, refusing their enablement unless they are explicitly unmasked.
+
 Preset files may be written for specific distributions, for specific spins or for specific sites, in order to enforce different policies as needed.
 Preset policies are stored in .preset files in /usr/lib/systemd/system-preset/ (or /usr/lib/systemd/initrd-preset/ for initrds).
 If no policy exists the default implied policy of "enable everything" is enforced, i.e. in Debian style.
 
 The policy encoded in preset files is applied to a unit by invoking "systemctl preset ".
 It is recommended to use this command in all package post installation scriptlets.
-"systemctl preset " is identical to "systemctl enable " resp. "systemctl disable " depending on the policy.
+"systemctl preset " is identical to "systemctl enable ", "systemctl disable ", or "systemctl mask " depending on the policy.
 
 Preset files allow clean separation of enablement mechanism (inside the package scriptlets, by invoking "systemctl preset"), and enablement policy (centralized in the preset files).
 
diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml
index a83492495e..f34f0ef619 100644
--- a/man/systemd.preset.xml
+++ b/man/systemd.preset.xml
@@ -40,15 +40,17 @@
   <refsect1>
     <title>Description</title>
 
-    <para>Preset files may be used to encode policy which units shall be enabled by default and which ones
-    shall be disabled. They are read by <command>systemctl preset</command> which uses this information to
-    enable or disable a unit. Depending on that policy, <command>systemctl preset</command> is identical to
-    <command>systemctl enable</command> or <command>systemctl disable</command>.
+    <para>Preset files may be used to encode policy which units shall be enabled by default, which units
+    shall be disabled by default, and which ones shall be masked. They are read by
+    <command>systemctl preset</command> which uses this information to enable, disable, or mask a unit.
+    Depending on that policy, <command>systemctl preset</command> is identical to
+    <command>systemctl enable</command>, <command>systemctl disable</command>, or
+    <command>systemctl mask</command>.
 
     <command>systemctl preset</command> is used by the post install scriptlets of rpm packages (or other OS
-    package formats), to enable/disable specific units by default on package installation, enforcing
+    package formats), to enable/disable/mask specific units by default on package installation, enforcing
     distribution, spin, or administrator preset policy. This allows choosing a certain set of units to be
-    enabled/disabled even before installing the actual package. For more information, see
+    enabled/disabled/masked even before installing the actual package. For more information, see
     <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
 
     <para>It is not recommended to ship preset files within the respective software packages implementing the
@@ -61,7 +63,7 @@
 
     <para>When the machine is booted for the first time,
     <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> will
-    enable/disable all units according to preset policy, similarly to <command>systemctl
+    enable/disable/mask all units according to preset policy, similarly to <command>systemctl
     preset-all</command>. Also see <varname>ConditionFirstBoot=</varname> in
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
     "First Boot Semantics" in
@@ -73,10 +75,10 @@
     <title>Preset File Format</title>
 
     <para>The preset files contain a list of directives, one per line. Empty lines and lines whose first
-    non-whitespace character is <literal>#</literal> or <literal>;</literal> are ignored.  Each directive
-    consists of one of the words <literal>enable</literal>, <literal>disable</literal>, or
-    <literal>ignore</literal>, followed by whitespace and a unit name. The unit name may contain shell-style
-    wildcards.</para>
+    non-whitespace character is <literal>#</literal> or <literal>;</literal> are ignored. Each directive
+    consists of one of the words <literal>enable</literal>, <literal>disable</literal>,
+    <literal>mask</literal>, or <literal>ignore</literal>, followed by whitespace and a unit name. The unit
+    name may contain shell-style wildcards.</para>
 
     <para>For the enable directive for template units, one or more instance names may be specified as a
     space-separated list after the unit name. In this case, those instances will be enabled instead of the
@@ -87,8 +89,8 @@
     for a description of unit aliasing.</para>
 
     <para>Three different directives are understood: <literal>enable</literal> may be used to enable units by
-    default, <literal>disable</literal> to disable units by default, and <literal>ignore</literal> to ignore
-    units and leave existing configuration intact.</para>
+    default, <literal>disable</literal> to disable units by default, <literal>mask</literal> to mask units by
+    default, and <literal>ignore</literal> to ignore units and leave existing configuration intact.</para>
 
     <para>If multiple lines apply to a unit name, the first matching
     one takes precedence over all others.</para>
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index c0d080a6e7..97cc114b98 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1694,7 +1694,7 @@
           <para>Note that the service manager itself will perform setup steps during First Boot: it will
           initialize
           <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
-          preset all units, enabling or disabling them according to the
+          preset all units, enabling, disabling, or masking them according to the
           <citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           settings. Additional setup may be performed via units with
           <varname>ConditionFirstBoot=yes</varname>.</para>
diff --git a/src/shared/install.c b/src/shared/install.c
index aa5d6e64a0..eee3d09206 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1503,6 +1503,11 @@ static int unit_file_load_or_readlink(
                 return r;
         bool outside_search_path = r > 0;
 
+        if (outside_search_path && endswith(info->symlink_target, "/run/systemd/preset-mask")) {
+                info->install_mode = INSTALL_MODE_MASKED;
+                return 0;
+        }
+
         r = null_or_empty_path_with_root(info->symlink_target, lp->root_dir);
         if (r < 0 && r != -ENOENT)
                 return log_debug_errno(r, "Failed to stat %s: %m", info->symlink_target);
@@ -2393,6 +2398,8 @@ int unit_file_unmask(
         bool dry_run = flags & UNIT_FILE_DRY_RUN;
 
         STRV_FOREACH(name, names) {
+                _cleanup_free_ char *path = NULL, *target = NULL;
+
                 if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
                         return -EINVAL;
 
@@ -2419,10 +2426,15 @@ int unit_file_unmask(
                         TAKE_PTR(info.name);  /* … and give it back here */
                 }
 
-                _cleanup_free_ char *path = path_make_absolute(*name, config_path);
+                path = path_make_absolute(*name, config_path);
                 if (!path)
                         return -ENOMEM;
 
+                /* Shortcut to detect if the unit is masked by preset,
+                 * as null_or_empty_path() would return -ENOENT. */
+                if (readlink_malloc(path, &target) == 0 && streq(target, "/run/systemd/preset-mask"))
+                        goto add_todo;
+
                 r = null_or_empty_path(path);
                 if (r == -ENOENT)
                         continue;
@@ -2430,7 +2442,7 @@ int unit_file_unmask(
                         return r;
                 if (r == 0)
                         continue;
-
+add_todo:
                 if (!GREEDY_REALLOC0(todo, n_todo + 2))
                         return -ENOMEM;
 
@@ -3622,8 +3634,6 @@ static int preset_mask_check_and_remove(
                 const char *config_path) {
 
         _cleanup_free_ char *path = NULL, *target = NULL;
-        struct stat st;
-        int r;
 
         assert(name);
         assert(config_path);
@@ -3632,17 +3642,11 @@ static int preset_mask_check_and_remove(
         if (!path)
                 return -ENOMEM;
 
-        if (stat(path, &st) == 0 && null_or_empty(&st)) {
-                r = readlink_malloc(path, &target);
-                if (r < 0)
-                        return r;
-
-                if (streq(target, "/run/systemd/preset-mask")) {
-                        if (unlink(path) < 0)
-                                return log_error_errno(errno, "Failed to remove %s: %m", path);
+        if (readlink_malloc(path, &target) == 0 && streq(target, "/run/systemd/preset-mask")) {
+                if (unlink(path) < 0)
+                        return log_error_errno(errno, "Failed to remove %s: %m", path);
 
-                        log_info("Removed '%s'.", path);
-                }
+                log_info("Removed '%s'.", path);
         }
 
         return 0;
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index 2545b1fd04..a447a1dbf2 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -608,9 +608,9 @@ TEST(indirect) {
 TEST(preset_and_list) {
         InstallChange *changes = NULL;
         size_t n_changes = 0, i;
-        const char *p, *q;
+        const char *p, *q, *s;
         UnitFileState state;
-        bool got_yes = false, got_no = false;
+        bool got_yes = false, got_no = false, got_mask = false;
         UnitFileList *fl;
         _cleanup_hashmap_free_ Hashmap *h = NULL;
 
@@ -618,6 +618,7 @@ TEST(preset_and_list) {
 
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) == -ENOENT);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) == -ENOENT);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) == -ENOENT);
 
         p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
@@ -630,6 +631,11 @@ TEST(preset_and_list) {
                                     "[Install]\n"
                                     "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
 
+        p = strjoina(root, "/usr/lib/systemd/system/preset-mask.service");
+        assert_se(write_string_file(p,
+                                    "[Install]\n"
+                                    "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
         p = strjoina(root, "/usr/lib/systemd/system/preset-ignore.service");
         assert_se(write_string_file(p,
                                     "[Install]\n"
@@ -638,11 +644,13 @@ TEST(preset_and_list) {
         p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
         assert_se(write_string_file(p,
                                     "enable *-yes.*\n"
+                                    "mask *-mask.*\n"
                                     "ignore *-ignore.*\n"
                                     "disable *\n", WRITE_STRING_FILE_CREATE) >= 0);
 
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
         assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
@@ -656,6 +664,7 @@ TEST(preset_and_list) {
 
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
         assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0);
@@ -668,6 +677,7 @@ TEST(preset_and_list) {
 
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
         assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
@@ -677,6 +687,33 @@ TEST(preset_and_list) {
 
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+        assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-mask.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+        assert_se(n_changes == 1);
+        assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
+        ASSERT_STREQ(changes[0].source, "/run/systemd/preset-mask");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/preset-mask.service");
+        ASSERT_STREQ(changes[0].path, p);
+        install_changes_free(changes, n_changes);
+        changes = NULL; n_changes = 0;
+
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+        assert_se(unit_file_unmask(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-mask.service"), &changes, &n_changes) >= 0);
+        assert_se(n_changes == 1);
+        assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
+        ASSERT_STREQ(changes[0].path, p);
+        install_changes_free(changes, n_changes);
+        changes = NULL; n_changes = 0;
+
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
         assert_se(unit_file_preset_all(RUNTIME_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
@@ -688,8 +725,10 @@ TEST(preset_and_list) {
         for (i = 0; i < n_changes; i++) {
 
                 if (changes[i].type == INSTALL_CHANGE_SYMLINK) {
-                        ASSERT_STREQ(changes[i].source, "/usr/lib/systemd/system/preset-yes.service");
-                        ASSERT_STREQ(changes[i].path, p);
+                        if (streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service"))
+                                ASSERT_STREQ(changes[i].path, p);
+                        else if (streq(changes[i].source, "/usr/lib/systemd/system/preset-mask.service"))
+                                ASSERT_STREQ(changes[i].path, "/run/systemd/preset-mask");
                 } else
                         assert_se(changes[i].type == INSTALL_CHANGE_UNLINK);
         }
@@ -699,12 +738,14 @@ TEST(preset_and_list) {
 
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_MASKED);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
         ASSERT_OK(unit_file_get_list(RUNTIME_SCOPE_SYSTEM, root, NULL, NULL, &h));
 
         p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
         q = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
+        s = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/preset-mask.service");
 
         HASHMAP_FOREACH(fl, h) {
                 _cleanup_free_ char *unit_filename = NULL;
@@ -719,15 +760,68 @@ TEST(preset_and_list) {
                 } else if (streq(fl->path, q)) {
                         got_no = true;
                         assert_se(fl->state == UNIT_FILE_DISABLED);
+                } else if (streq(fl->path, s)) {
+                        got_mask = true;
+                        assert_se(fl->state == UNIT_FILE_MASKED);
                 } else
                         assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT, UNIT_FILE_ALIAS));
         }
 
-        assert_se(got_yes && got_no);
+        assert_se(got_yes && got_no && got_mask);
 
         assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), &changes, &n_changes) >= 0);
         assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
         assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+        /* Enabling a masked unit should fail. */
+        assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-mask.service"), &changes, &n_changes) == -ERFKILL);
+        install_changes_free(changes, n_changes);
+        changes = NULL; n_changes = 0;
+
+        /* Disabling a masked unit must succeed but be a NOP. */
+        assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-mask.service"), &changes, &n_changes) >= 0);
+        assert_se(n_changes == 1);
+        assert_se(changes[0].type == INSTALL_CHANGE_IS_MASKED);
+        ASSERT_STREQ(changes[0].path, s);
+        install_changes_free(changes, n_changes);
+        changes = NULL; n_changes = 0;
+
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+
+        /* But, a preset can change from "mask" to "enable" or "disable", so it's allowed in this case. */
+        p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
+        assert_se(write_string_file(p, "enable *-mask.*\n", WRITE_STRING_FILE_TRUNCATE) >= 0);
+        assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-mask.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+        assert_se(n_changes == 1);
+        assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
+        ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/preset-mask.service");
+        q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-mask.service");
+        ASSERT_STREQ(changes[0].path, q);
+        install_changes_free(changes, n_changes);
+        changes = NULL; n_changes = 0;
+
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+        assert_se(write_string_file(p, "mask *-mask.*\n", WRITE_STRING_FILE_TRUNCATE) >= 0);
+        assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-mask.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+        assert_se(n_changes == 1);
+        assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
+        ASSERT_STREQ(changes[0].source, "/run/systemd/preset-mask");
+        q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/preset-mask.service");
+        ASSERT_STREQ(changes[0].path, q);
+        install_changes_free(changes, n_changes);
+        changes = NULL; n_changes = 0;
+
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+
+        assert_se(write_string_file(p, "disable *-mask.*\n", WRITE_STRING_FILE_TRUNCATE) >= 0);
+        assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-mask.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+        assert_se(n_changes == 1);
+        assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
+        install_changes_free(changes, n_changes);
+        changes = NULL; n_changes = 0;
+
+        assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-mask.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 }
 
 TEST(revert) {
-- 
2.51.0

openSUSE Build Service is sponsored by