]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
useradd: skip btrfs subvolume creation for system users
authorHadi Chokr <hadichokr@icloud.com>
Mon, 20 Jul 2026 11:58:29 +0000 (11:58 +0000)
committerAlejandro Colomar <foss+github@alejandro-colomar.es>
Tue, 28 Jul 2026 15:11:47 +0000 (17:11 +0200)
Gate subvolume creation on UID_MIN <= UID <= UID_MAX to exclude system
users regardless of their home path.

System users hold no snapshot-worthy data, so subvolumes for them just
waste metadata and add filesystem objects nobody uses. The BTRFS
backend was always aimed at interactive users anyway.

However you can set BTRFS_SUBVOLUME_SYSTEM=yes in /etc/default/useradd
to get the old behavior back. Has no effect unless subvolume creation
is enabled in the first place.

Document the new variable in useradd(8).

Fixes: c1d36a8acb1d (2019-05-04; "Add support for btrfs subvolumes for user homes")
Fixes: 3e8c105f0703 (2026-01-02; "src/useradd: Support config for creating home dirs as Btrfs subvolumes")
Signed-off-by: Hadi Chokr <hadichokr@icloud.com>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
man/useradd.8.xml
src/useradd.c

index 65af1517fa7961447c285f772412808b9404ab57..9e15763fad03eda6d6477da2945d476bfaf8c262 100644 (file)
                a Btrfs subvolume is created
                regardless of any configuration file settings.
          </para>
+         <para>
+           Subvolumes are only created for regular users,
+           whose UID is within the
+           <option>UID_MIN</option>-<option>UID_MAX</option>
+           range defined in <filename>/etc/login.defs</filename>.
+           System users get a regular home directory instead.
+           Set the <option>BTRFS_SUBVOLUME_SYSTEM</option> variable
+           in <filename>/etc/default/useradd</filename>
+           to <replaceable>yes</replaceable>
+           to create subvolumes for system users as well.
+           If this variable is not set,
+           the default value is no.
+         </para>
          <para>
            If the parent directory of the user's home directory is
            <emphasis>not</emphasis> on a Btrfs filesystem,
index 9588911c2949aa95ed2d999f3cba17266bb834b0..5404b46c877aabf10d33b4c998b286dd23862950 100644 (file)
@@ -115,6 +115,7 @@ static const char *def_template = SKEL_DIR;
 static const char *def_usrtemplate = USRSKELDIR;
 static const char *def_create_mail_spool = "yes";
 static const char *def_btrfs_subvolume_home = "no";
+static const char *def_btrfs_subvolume_system = "no";
 static const char *def_log_init = "yes";
 
 static long def_inactive = -1;
@@ -223,6 +224,7 @@ static bool home_added = false;
 #define DUSRSKEL               "USRSKEL"
 #define DCREATE_MAIL_SPOOL     "CREATE_MAIL_SPOOL"
 #define DBTRFS_SUBVOLUME_HOME  "BTRFS_SUBVOLUME_HOME"
+#define DBTRFS_SUBVOLUME_SYSTEM        "BTRFS_SUBVOLUME_SYSTEM"
 #define DLOG_INIT              "LOG_INIT"
 
 /* local function prototypes */
@@ -471,6 +473,15 @@ get_defaults(const struct option_flags *flags)
                        def_btrfs_subvolume_home = xstrdup(ccp);
                }
 
+               /*
+                * Create subvolume homes also for system users?
+                */
+               else if (streq(buf, DBTRFS_SUBVOLUME_SYSTEM)) {
+                       if (streq(ccp, ""))
+                               ccp = "no";
+                       def_btrfs_subvolume_system = xstrdup(ccp);
+               }
+
                /*
                 * By default do we add the user to the lastlog and faillog databases ?
                 */
@@ -506,6 +517,7 @@ static void show_defaults (void)
        printf ("USRSKEL=%s\n", def_usrtemplate);
        printf ("CREATE_MAIL_SPOOL=%s\n", def_create_mail_spool);
        printf ("BTRFS_SUBVOLUME_HOME=%s\n", def_btrfs_subvolume_home);
+       printf ("BTRFS_SUBVOLUME_SYSTEM=%s\n", def_btrfs_subvolume_system);
        printf ("LOG_INIT=%s\n", def_log_init);
 }
 
@@ -530,6 +542,7 @@ set_defaults(void)
        bool  out_usrskel = false;
        bool  out_create_mail_spool = false;
        bool  out_btrfs_subvolume_home = false;
+       bool  out_btrfs_subvolume_system = false;
        bool  out_log_init = false;
        char  buf[1024];
        char  *new_file = NULL;
@@ -644,6 +657,11 @@ set_defaults(void)
                                DBTRFS_SUBVOLUME_HOME "=%s\n",
                                def_btrfs_subvolume_home);
                        out_btrfs_subvolume_home = true;
+               } else if (!out_btrfs_subvolume_system && streq(buf, DBTRFS_SUBVOLUME_SYSTEM)) {
+                       fprintf(ofp,
+                               DBTRFS_SUBVOLUME_SYSTEM "=%s\n",
+                               def_btrfs_subvolume_system);
+                       out_btrfs_subvolume_system = true;
                } else if (!out_log_init && streq(buf, DLOG_INIT)) {
                        fprintf(ofp, DLOG_INIT "=%s\n", def_log_init);
                        out_log_init = true;
@@ -680,6 +698,8 @@ set_defaults(void)
                fprintf (ofp, DCREATE_MAIL_SPOOL "=%s\n", def_create_mail_spool);
        if (!out_btrfs_subvolume_home)
                fprintf (ofp, DBTRFS_SUBVOLUME_HOME "=%s\n", def_btrfs_subvolume_home);
+       if (!out_btrfs_subvolume_system)
+               fprintf (ofp, DBTRFS_SUBVOLUME_SYSTEM "=%s\n", def_btrfs_subvolume_system);
        if (!out_log_init)
                fprintf (ofp, DLOG_INIT "=%s\n", def_log_init);
        /*
@@ -2135,10 +2155,20 @@ usr_update (unsigned long subuid_count, unsigned long subgid_count,
 static bool
 want_btrfs_subvolume(const char *path)
 {
+       uid_t  min, max;
+
        if (!subvolflg)
                return false;
+       if (strlen(prefix_user_home) - strlen(path) > 1)
+               return false;
+
+       if (strcaseeq(def_btrfs_subvolume_system, "yes"))
+               return true;
+
+       min = getdef_ulong("UID_MIN", 1000UL);
+       max = getdef_ulong("UID_MAX", 60000UL);
 
-       return strlen(prefix_user_home) - strlen(path) <= 1;
+       return user_id >= min && user_id <= max;
 }
 #endif