diff --git a/btrfs-subvolumes.patch b/btrfs-subvolumes.patch new file mode 100644 index 0000000..e2fa4ad --- /dev/null +++ b/btrfs-subvolumes.patch @@ -0,0 +1,318 @@ +commit 52ea836ffbfa4d6797cf89d6ada58f76bee9cf6b +Author: Adam Majer +Date: Wed Jan 23 16:17:05 2019 +0100 + + Add autotools support for BtrFS option + + Feature is enabled by default, if headers are available. It can be + turned off explictly. + +commit 81ead2042afcdb8d423da855cf1528618a4e0c01 +Author: Adam Majer +Date: Mon Jan 21 09:32:36 2019 +0100 + + Add support for btrfs subvolumes for user homes + + new switch added to useradd command, --btrfs-subvolume-home. When + specified *and* the filesystem is detected as btrfs, it will create a + subvolume for user's home instead of a plain directory. This is done via + `btrfs subvolume` command. Specifying the new switch while trying to + create home on non-btrfs will result in an error. + + userdel -r will handle and remove this subvolume transparently via + `btrfs subvolume` command. Previosuly this failed as you can't rmdir a + subvolume. + + usermod, when moving user's home across devices, will detect if the home + is a subvolume and issue an error messages instead of copying it. Moving + user's home (as subvolume) on same btrfs works transparently. + + +--- a/configure.ac ++++ b/configure.ac +@@ -256,6 +256,9 @@ AC_ARG_WITH(audit, + AC_ARG_WITH(libpam, + [AC_HELP_STRING([--with-libpam], [use libpam for PAM support @<:@default=yes if found@:>@])], + [with_libpam=$withval], [with_libpam=maybe]) ++AC_ARG_WITH(btrfs, ++ [AC_HELP_STRING([--with-btrfs], [add BtrFS support @<:@default=yes if found@:>@])], ++ [with_selinux=$withval], [with_selinux=maybe]) + AC_ARG_WITH(selinux, + [AC_HELP_STRING([--with-selinux], [use SELinux support @<:@default=yes if found@:>@])], + [with_selinux=$withval], [with_selinux=maybe]) +@@ -453,6 +456,20 @@ if test "$with_libcrack" = "yes"; then + AC_DEFINE(HAVE_LIBCRACK_PW, 1, [Defined if it includes *Pw functions.])) + fi + ++if test "$with_btrfs" != "no"; then ++ AC_CHECK_HEADERS([sys/statfs.h linux/magic.h linux/btrfs_tree.h], \ ++ [btrfs_headers="yes"], [btrfs_headers="no"]) ++ if test "$btrfs_headers$with_btrfs" = "noyes" ; then ++ AC_MSG_ERROR([One of sys/statfs.h linux/magic.h linux/btrfs_tree.h is missing]) ++ fi ++ ++ if test "$btrfs_headers" = "yes" ; then ++ AC_DEFINE(WITH_BTRFS, 1, [Build shadow with BtrFS support]) ++ with_btrfs="yes" ++ fi ++fi ++AM_CONDITIONAL(WITH_BTRFS, test x$with_btrfs = xyes) ++ + AC_SUBST(LIBSELINUX) + AC_SUBST(LIBSEMANAGE) + if test "$with_selinux" != "no"; then +@@ -672,6 +689,7 @@ if test "$with_libpam" = "yes"; then + echo " suid account management tools: $enable_acct_tools_setuid" + fi + echo " SELinux support: $with_selinux" ++echo " BtrFS support: $with_btrfs" + echo " ACL support: $with_acl" + echo " Extended Attributes support: $with_attr" + echo " tcb support (incomplete): $with_tcb" +--- a/lib/prototypes.h ++++ b/lib/prototypes.h +@@ -72,6 +72,14 @@ extern int expire (const struct passwd * + /* isexpired.c */ + extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); + ++/* btrfs.c */ ++#ifdef WITH_BTRFS ++extern int btrfs_create_subvolume(const char *path); ++extern int btrfs_remove_subvolume(const char *path); ++extern int btrfs_is_subvolume(const char *path); ++extern int is_btrfs(const char *path); ++#endif ++ + /* basename() renamed to Basename() to avoid libc name space confusion */ + /* basename.c */ + extern /*@observer@*/const char *Basename (const char *str); +--- a/libmisc/Makefile.am ++++ b/libmisc/Makefile.am +@@ -72,3 +72,8 @@ libmisc_a_SOURCES = \ + xgetspnam.c \ + xmalloc.c \ + yesno.c ++ ++if WITH_BTRFS ++libmisc_a_SOURCES += btrfs.c ++endif ++ +--- /dev/null ++++ b/libmisc/btrfs.c +@@ -0,0 +1,94 @@ ++#include ++#include ++#include ++ ++#include "prototypes.h" ++ ++ ++static int run_btrfs_subvolume_cmd(const char *subcmd, const char *arg1, const char *arg2) ++{ ++ int status = 0; ++ const char *cmd = "/sbin/btrfs"; ++ const char *argv[] = { ++ strrchr(cmd, '/'), ++ "subvolume", ++ subcmd, ++ arg1, ++ arg2, ++ NULL ++ }; ++ ++ if (argv[0] == NULL) ++ argv[0] = cmd; ++ else ++ argv[0] = argv[0] + 1; ++ ++ if (access(cmd, X_OK)) { ++ return 1; ++ } ++ ++ if (run_command(cmd, argv, NULL, &status)) ++ return -1; ++ return status; ++} ++ ++ ++int btrfs_create_subvolume(const char *path) ++{ ++ return run_btrfs_subvolume_cmd("create", path, NULL); ++} ++ ++ ++int btrfs_remove_subvolume(const char *path) ++{ ++ return run_btrfs_subvolume_cmd("delete", "-C", path); ++} ++ ++ ++/* Adapted from btrfsprogs */ ++/* ++ * This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening ++ * a file descriptor and calling it, because fstat() and fstatfs() don't accept ++ * file descriptors opened with O_PATH on old kernels (before v3.6 and before ++ * v3.12, respectively), but stat() and statfs() can be called on a path that ++ * the user doesn't have read or write permissions to. ++ * ++ * returns: ++ * 1 - btrfs subvolume ++ * 0 - not btrfs subvolume ++ * -1 - error ++ */ ++int btrfs_is_subvolume(const char *path) ++{ ++ struct stat st; ++ int ret; ++ ++ ret = is_btrfs(path); ++ if (ret <= 0) ++ return ret; ++ ++ ret = stat(path, &st); ++ if (ret == -1) ++ return -1; ++ ++ if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++/* Adapted from btrfsprogs */ ++int is_btrfs(const char *path) ++{ ++ struct statfs sfs; ++ int ret; ++ ++ ret = statfs(path, &sfs); ++ if (ret == -1) ++ return -1; ++ ++ return sfs.f_type == BTRFS_SUPER_MAGIC; ++} ++ +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -164,6 +164,7 @@ static bool + oflg = false, /* permit non-unique user ID to be specified with -u */ + rflg = false, /* create a system account */ + sflg = false, /* shell program for new account */ ++ subvolflg = false, /* create subvolume home on BTRFS */ + uflg = false, /* specify user ID for new account */ + Uflg = false; /* create a group having the same name as the user */ + +@@ -805,6 +806,9 @@ static void usage (int status) + Prog, Prog, Prog); + (void) fputs (_(" -b, --base-dir BASE_DIR base directory for the home directory of the\n" + " new account\n"), usageout); ++#ifdef WITH_BTRFS ++ (void) fputs (_(" --btrfs-subvolume-home use BTRFS subvolume for home directory\n"), usageout); ++#endif + (void) fputs (_(" -c, --comment COMMENT GECOS field of the new account\n"), usageout); + (void) fputs (_(" -d, --home-dir HOME_DIR home directory of the new account\n"), usageout); + (void) fputs (_(" -D, --defaults print or change default useradd configuration\n"), usageout); +@@ -1085,6 +1089,9 @@ static void process_flags (int argc, cha + int c; + static struct option long_options[] = { + {"base-dir", required_argument, NULL, 'b'}, ++#ifdef WITH_BTRFS ++ {"btrfs-subvolume-home", no_argument, NULL, 200}, ++#endif + {"comment", required_argument, NULL, 'c'}, + {"home-dir", required_argument, NULL, 'd'}, + {"defaults", no_argument, NULL, 'D'}, +@@ -1131,6 +1138,9 @@ static void process_flags (int argc, cha + def_home = optarg; + bflg = true; + break; ++ case 200: ++ subvolflg = true; ++ break; + case 'c': + if (!VALID (optarg)) { + fprintf (stderr, +@@ -2049,6 +2059,37 @@ static void create_home (void) + strcat (path, "/"); + strcat (path, cp); + if (access (path, F_OK) != 0) { ++ /* Check if parent directory is BTRFS, fail if requesting ++ subvolume but no BTRFS. The paths cound be different by the ++ trailing slash ++ */ ++#if WITH_BTRFS ++ if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { ++ char *btrfs_check = strdup(path); ++ ++ if (!btrfs_check) { ++ fprintf (stderr, ++ _("%s: error while duplicating string in BTRFS check %s\n"), ++ Prog, path); ++ fail_exit (E_HOMEDIR); ++ } ++ btrfs_check[strlen(path) - strlen(cp) - 1] = '\0'; ++ if (is_btrfs(btrfs_check) <= 0) { ++ fprintf (stderr, ++ _("%s: home directory \"%s\" must be mounted on BTRFS\n"), ++ Prog, path); ++ fail_exit (E_HOMEDIR); ++ } ++ // make subvolume to mount for user instead of directory ++ if (btrfs_create_subvolume(path)) { ++ fprintf (stderr, ++ _("%s: failed to create BTRFS subvolume: %s\n"), ++ Prog, path); ++ fail_exit (E_HOMEDIR); ++ } ++ } ++ else ++#endif + if (mkdir (path, 0) != 0) { + fprintf (stderr, + _("%s: cannot create directory %s\n"), +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -1273,6 +1273,23 @@ int main (int argc, char **argv) + #endif /* EXTRA_CHECK_HOME_DIR */ + + if (rflg) { ++#ifdef WITH_BTRFS ++ int is_subvolume = btrfs_is_subvolume (user_home); ++ if (is_subvolume < 0) { ++ errors++; ++ /* continue */ ++ } ++ else if (is_subvolume > 0) { ++ if (btrfs_remove_subvolume (user_home)) { ++ fprintf (stderr, ++ _("%s: error removing subvolume %s\n"), ++ Prog, user_home); ++ errors++; ++ /* continue */ ++ } ++ } ++ else ++#endif + if (remove_tree (user_home, true) != 0) { + fprintf (stderr, + _("%s: error removing directory %s\n"), +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1818,6 +1818,15 @@ static void move_home (void) + return; + } else { + if (EXDEV == errno) { ++#ifdef WITH_BTRFS ++ if (btrfs_is_subvolume (prefix_user_home) > 0) { ++ fprintf (stderr, ++ _("%s: error: cannot move subvolume from %s to %s - different device\n"), ++ Prog, prefix_user_home, prefix_user_newhome); ++ fail_exit (E_HOMEDIR); ++ } ++#endif ++ + if (copy_tree (prefix_user_home, prefix_user_newhome, true, + true, + user_id, diff --git a/shadow.changes b/shadow.changes index ee0beed..2e95800 100644 --- a/shadow.changes +++ b/shadow.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Jan 23 09:35:01 UTC 2019 - adam.majer@suse.de + +- btrfs-subvolumes.patch: implement support for creating user home + directories on btrfs subvolumes (fate#316134) + ------------------------------------------------------------------- Wed Oct 31 14:17:29 UTC 2018 - Valentin Rothberg @@ -25,7 +31,7 @@ Mon May 14 12:45:42 UTC 2018 - mvetter@suse.com - Remove shadow-4.1.5.1-pam_group.patch: upstreamed - Update userdel-script.patch: change due to prefix - Update useradd-mkdirs.patch: change due to prefix - Additionally changed in that patch: + Additionally changed in that patch (bsc#1106914): * Test for strdup() failure * Directory to 0755 instead 0777 - Add shadow-4.6.0-fix-usermod-prefix-crash.patch: diff --git a/shadow.spec b/shadow.spec index b6b0027..d7363bd 100644 --- a/shadow.spec +++ b/shadow.spec @@ -1,7 +1,7 @@ # # spec file for package shadow # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,7 +12,7 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# Please submit bugfixes or comments via http://bugs.opensuse.org/ # @@ -45,11 +45,15 @@ Patch10: encryption_method_nis.patch Patch11: useradd-mkdirs.patch Patch12: shadow-4.6.0-fix-usermod-prefix-crash.patch Patch20: disable_new_audit_function.patch +Patch21: btrfs-subvolumes.patch BuildRequires: audit-devel > 2.3 +BuildRequires: autoconf +BuildRequires: automake BuildRequires: libacl-devel BuildRequires: libattr-devel BuildRequires: libselinux-devel BuildRequires: libsemanage-devel +BuildRequires: libtool BuildRequires: pam-devel BuildRequires: xz Requires(pre): group(root) @@ -80,6 +84,7 @@ group accounts. %if 0%{?suse_version} < 1330 %patch20 -p1 %endif +%patch21 -p1 iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 mv -v doc/HOWTO.utf8 doc/HOWTO @@ -88,6 +93,7 @@ mv -v doc/HOWTO.utf8 doc/HOWTO export CFLAGS="%{optflags} -fpie" export LDFLAGS="-pie" +autoreconf -fvi %configure \ --disable-shadowgrp \ --enable-account-tools-setuid \