From: Hadi Chokr Date: Tue, 21 Apr 2026 07:18:19 +0000 (+0200) Subject: useradd(8): fallback to regular dir for BTRFS home on non-BTRFS parent X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=7be9a6540f1cad477fd4f4e83d25904a5095bb86;p=thirdparty%2Fshadow.git useradd(8): fallback to regular dir for BTRFS home on non-BTRFS parent When the --btrfs-subvolume-home option is used but the parent directory is not on a BTRFS filesystem, useradd previously failed with an error. This is too strict; instead, fall back to creating a regular directory and issue a warning. The subvolume creation is attempted only when the parent is BTRFS. Otherwise, a regular directory is created and a syslog(3) warning is logged. Fixes: 3e8c105 (2026-01-02; "src/useradd: Support config for creating home dirs as Btrfs subvolumes") Co-authored-by: Hadi Chokr Co-authored-by: Alejandro Colomar Signed-off-by: Alejandro Colomar --- diff --git a/man/useradd.8.xml b/man/useradd.8.xml index 8a087e13a..0bde6683d 100644 --- a/man/useradd.8.xml +++ b/man/useradd.8.xml @@ -2,6 +2,7 @@ - Note: this feature works only if the underlying filesystem supports - Btrfs subvolumes. + If the parent directory of the user's home directory is + not on a Btrfs filesystem, + useradd will not create a + subvolume. + Instead, it creates a regular directory, + prints a warning to standard error, + and logs the event via syslog at level + LOG_WARN. + The user account is still created successfully. + + + If the filesystem type cannot be determined (e.g., because of + insufficient permissions, an I/O error, + or a + + statfs + 2 + + failure), + useradd treats this as a fatal error: + the home directory is not created, + the command exits with a non‑zero status + (E_HOMEDIR, 12), + and an error message is printed. diff --git a/src/useradd.c b/src/useradd.c index 9bd32b11a..9210379c8 100644 --- a/src/useradd.c +++ b/src/useradd.c @@ -1,11 +1,11 @@ -/* - * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh - * SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz - * SPDX-FileCopyrightText: 2000 - 2006, Tomasz Kłoczko - * SPDX-FileCopyrightText: 2007 - 2012, Nicolas François - * - * SPDX-License-Identifier: BSD-3-Clause - */ +// SPDX-FileCopyrightText: 1991-1994, Julianne Frances Haugh +// SPDX-FileCopyrightText: 1996-2000, Marek Michałkiewicz +// SPDX-FileCopyrightText: 2000-2006, Tomasz Kłoczko +// SPDX-FileCopyrightText: 2007-2012, Nicolas François +// SPDX-FileCopyrightText: 2025-2026, Hadi Chokr +// SPDX-FileCopyrightText: 2026, Alejandro Colomar +// SPDX-License-Identifier: BSD-3-Clause + #include "config.h" @@ -2251,6 +2251,8 @@ static void create_home(const struct option_flags *flags) owner root:root. */ for (cp = strtok(bhome, "/"); cp != NULL; cp = strtok(NULL, "/")) { + bool dir_created; + /* Avoid turning a relative path into an absolute path. */ if (strprefix(bhome, "/") || !streq(path, "")) strcat(path, "/"); @@ -2260,10 +2262,7 @@ static void create_home(const struct option_flags *flags) continue; } - /* Check if parent directory is BTRFS, fail if requesting - subvolume but no BTRFS. The paths could be different by the - trailing slash - */ + dir_created = false; #if WITH_BTRFS if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { char *btrfs_check = strdup(path); @@ -2284,25 +2283,26 @@ static void create_home(const struct option_flags *flags) free(btrfs_check); if (!is_btrfs(&sfs)) { fprintf(stderr, - _("%s: home directory \"%s\" must be mounted on BTRFS\n"), - Prog, path); - fail_exit(E_HOMEDIR, process_selinux); + _("%s: warning: \"%s\" is not on BTRFS; creating regular directory instead of subvolume\n"), + Prog, prefix_user_home); + } else { + if (btrfs_create_subvolume(path)) { + fprintf(stderr, + _("%s: failed to create BTRFS subvolume: %s\n"), + Prog, path); + fail_exit(E_HOMEDIR, process_selinux); + } + dir_created = true; } - // make subvolume to mount for user instead of directory - if (btrfs_create_subvolume(path)) { - fprintf(stderr, - _("%s: failed to create BTRFS subvolume: %s\n"), + } +#endif + if (!dir_created) { + if (mkdir(path, 0) != 0) { + fprintf(stderr, _("%s: cannot create directory %s\n"), Prog, path); fail_exit(E_HOMEDIR, process_selinux); } } - else -#endif - if (mkdir(path, 0) != 0) { - fprintf(stderr, _("%s: cannot create directory %s\n"), - Prog, path); - fail_exit(E_HOMEDIR, process_selinux); - } if (chown(path, 0, 0) < 0) { fprintf(stderr, _("%s: warning: chown on `%s' failed: %m\n"),