]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
src/useradd: Support config for creating home dirs as Btrfs subvolumes
authorHadi Chokr <hadichokr@icloud.com>
Fri, 12 Dec 2025 10:18:06 +0000 (11:18 +0100)
committerAlejandro Colomar <foss+github@alejandro-colomar.es>
Fri, 2 Jan 2026 22:52:59 +0000 (23:52 +0100)
Closes: #1162
Co-authored-by: Neal Gompa <ngompa@velocitylimitless.com>
Signed-off-by: Hadi Chokr <hadichokr@icloud.com>
Signed-off-by: Neal Gompa <ngompa@velocitylimitless.com>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
man/useradd.8.xml
src/useradd.c

index e6db7f6d7fb7a9ca63a18550fef9dfaa4ae5a4f1..c745a62078454d23fa91c2605b6fd06c90b539a9 100644 (file)
          </para>
        </listitem>
       </varlistentry>
+      <varlistentry>
+       <term>
+         <option>--btrfs-subvolume-home</option>
+       </term>
+       <listitem>
+         <para>
+           Create the user's home directory as a Btrfs subvolume.
+         </para>
+         <para>
+           If this option is not specified,
+               <command>useradd</command> will follow the default behavior
+               defined by the <option>BTRFS_SUBVOLUME_HOME</option> variable
+               in <filename>/etc/default/useradd</filename>.
+           If this variable is not set, the default value is no.
+         </para>
+         <para>
+           When the <option>--btrfs-subvolume-home</option> command-line option
+           is specified,
+               a Btrfs subvolume is created
+               regardless of any configuration file settings.
+         </para>
+         <para>
+           Note: this feature works only if the underlying filesystem supports
+           Btrfs subvolumes.
+         </para>
+       </listitem>
+      </varlistentry>
       <varlistentry>
        <term>
          <option>-c</option>, <option>--comment</option>&nbsp;<replaceable>COMMENT</replaceable>
index 899efe3c84193019e0cefe0b8c9dccce761725f8..8433ed8a8cfcc03c1ed6d35951ebc94ca8bc6e85 100644 (file)
@@ -114,6 +114,7 @@ static const char *def_shell = "/bin/bash";
 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_log_init = "yes";
 
 static long def_inactive = -1;
@@ -222,6 +223,7 @@ static bool home_added = false;
 #define DSKEL                  "SKEL"
 #define DUSRSKEL               "USRSKEL"
 #define DCREATE_MAIL_SPOOL     "CREATE_MAIL_SPOOL"
+#define DBTRFS_SUBVOLUME_HOME  "BTRFS_SUBVOLUME_HOME"
 #define DLOG_INIT              "LOG_INIT"
 
 /* local function prototypes */
@@ -456,6 +458,7 @@ get_defaults(const struct option_flags *flags)
                                def_usrtemplate = xstrdup(ccp);
                        }
                }
+
                /*
                 * Create by default user mail spool or not ?
                 */
@@ -466,6 +469,15 @@ get_defaults(const struct option_flags *flags)
                        def_create_mail_spool = xstrdup(ccp);
                }
 
+               /*
+                * Create home directories as Btrfs subvolumes by default?
+                */
+               else if (streq(buf, DBTRFS_SUBVOLUME_HOME)) {
+                       if (streq(ccp, ""))
+                               ccp = "no";
+                       def_btrfs_subvolume_home = xstrdup(ccp);
+               }
+
                /*
                 * By default do we add the user to the lastlog and faillog databases ?
                 */
@@ -500,6 +512,7 @@ static void show_defaults (void)
        printf ("SKEL=%s\n", def_template);
        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 ("LOG_INIT=%s\n", def_log_init);
 }
 
@@ -523,6 +536,7 @@ set_defaults(void)
        bool  out_skel = false;
        bool  out_usrskel = false;
        bool  out_create_mail_spool = false;
+       bool  out_btrfs_subvolume_home = false;
        bool  out_log_init = false;
        char  buf[1024];
        char  *new_file = NULL;
@@ -639,6 +653,11 @@ set_defaults(void)
                                DCREATE_MAIL_SPOOL "=%s\n",
                                def_create_mail_spool);
                        out_create_mail_spool = true;
+               } else if (!out_btrfs_subvolume_home && streq(buf, DBTRFS_SUBVOLUME_HOME)) {
+                       fprintf(ofp,
+                               DBTRFS_SUBVOLUME_HOME "=%s\n",
+                               def_btrfs_subvolume_home);
+                       out_btrfs_subvolume_home = true;
                } else if (!out_log_init && streq(buf, DLOG_INIT)) {
                        fprintf(ofp, DLOG_INIT "=%s\n", def_log_init);
                        out_log_init = true;
@@ -673,6 +692,8 @@ set_defaults(void)
 
        if (!out_create_mail_spool)
                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_log_init)
                fprintf (ofp, DLOG_INIT "=%s\n", def_log_init);
        /*
@@ -1431,6 +1452,9 @@ static void process_flags (int argc, char **argv, struct option_flags *flags)
                }
        }
 
+       if (!subvolflg && strcaseeq(def_btrfs_subvolume_home, "yes"))
+               subvolflg = true;
+
        if (!gflg && !Nflg && !Uflg) {
                /* Get the settings from login.defs */
                Uflg = getdef_bool ("USERGROUPS_ENAB");