From: Hadi Chokr Date: Mon, 20 Jul 2026 11:58:29 +0000 (+0000) Subject: useradd: skip btrfs subvolume creation for system users X-Git-Tag: 4.20.0~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b3b133ef9eb59967b74aea029838e24f16447ee4;p=thirdparty%2Fshadow.git useradd: skip btrfs subvolume creation for system users 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 Reviewed-by: Alejandro Colomar --- diff --git a/man/useradd.8.xml b/man/useradd.8.xml index 65af1517f..9e15763fa 100644 --- a/man/useradd.8.xml +++ b/man/useradd.8.xml @@ -155,6 +155,19 @@ a Btrfs subvolume is created regardless of any configuration file settings. + + Subvolumes are only created for regular users, + whose UID is within the + - + range defined in /etc/login.defs. + System users get a regular home directory instead. + Set the variable + in /etc/default/useradd + to yes + to create subvolumes for system users as well. + If this variable is not set, + the default value is no. + If the parent directory of the user's home directory is not on a Btrfs filesystem, diff --git a/src/useradd.c b/src/useradd.c index 9588911c2..5404b46c8 100644 --- a/src/useradd.c +++ b/src/useradd.c @@ -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