c1d36a8acb
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.
95 lines
1.7 KiB
C
95 lines
1.7 KiB
C
#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;
|
|
}
|
|
|