Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.1:Staging:A
shadow
btrfs-subvolumes.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File btrfs-subvolumes.patch of Package shadow
commit 52ea836ffbfa4d6797cf89d6ada58f76bee9cf6b Author: Adam Majer <amajer@suse.de> 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 <amajer@suse.de> 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 <linux/btrfs_tree.h> +#include <linux/magic.h> +#include <sys/statfs.h> + +#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,
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor