Skip to content

Commit

Permalink
btrfs: use automount to bind-mount all subvol roots.
Browse files Browse the repository at this point in the history
All subvol roots are now marked as automounts.  If the d_automount()
function determines that the dentry is not the root of the vfsmount, it
creates a simple loop-back mount of the dentry onto itself.  If it
determines that it IS the root of the vfsmount, it returns -EISDIR so
that no further automounting is attempted.

btrfs_getattr pays special attention to these automount dentries.
If it is NOT the root of the vfsmount:
 - the ->dev is reported as that for the rest of the vfsmount
 - the ->ino is reported as the subvol objectid, suitable transformed
   to avoid collision.

This way the same inode appear to be different depending on which mount
it is in.

automounted vfsmounts are kept on a list and timeout after 500 to 1000
seconds of last use.  This is configurable via a module parameter.
The tracking and timeout of automounts is copied from NFS.

Signed-off-by: NeilBrown <neilb@suse.de>
  • Loading branch information
neilbrown authored and intel-lab-lkp committed Jul 27, 2021
1 parent a763d20 commit 5874902
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
2 changes: 2 additions & 0 deletions fs/btrfs/btrfs_inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,6 @@ static inline void btrfs_print_data_csum_error(struct btrfs_inode *inode,
mirror_num);
}

void btrfs_release_automount_timer(void);

#endif
108 changes: 108 additions & 0 deletions fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <linux/migrate.h>
#include <linux/sched/mm.h>
#include <linux/iomap.h>
#include <linux/fs_context.h>
#include <asm/unaligned.h>
#include "misc.h"
#include "ctree.h"
Expand Down Expand Up @@ -5656,6 +5657,8 @@ static int btrfs_init_locked_inode(struct inode *inode, void *p)
struct btrfs_iget_args *args = p;

inode->i_ino = args->ino;
if (args->ino == BTRFS_FIRST_FREE_OBJECTID)
inode->i_flags |= S_AUTOMOUNT;
BTRFS_I(inode)->location.objectid = args->ino;
BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
BTRFS_I(inode)->location.offset = 0;
Expand Down Expand Up @@ -5859,6 +5862,101 @@ static int btrfs_dentry_delete(const struct dentry *dentry)
return 0;
}

static void btrfs_expire_automounts(struct work_struct *work);
static LIST_HEAD(btrfs_automount_list);
static DECLARE_DELAYED_WORK(btrfs_automount_task, btrfs_expire_automounts);
int btrfs_mountpoint_expiry_timeout = 500 * HZ;
static void btrfs_expire_automounts(struct work_struct *work)
{
struct list_head *list = &btrfs_automount_list;
int timeout = READ_ONCE(btrfs_mountpoint_expiry_timeout);

mark_mounts_for_expiry(list);
if (!list_empty(list) && timeout > 0)
schedule_delayed_work(&btrfs_automount_task, timeout);
}

void btrfs_release_automount_timer(void)
{
if (list_empty(&btrfs_automount_list))
cancel_delayed_work(&btrfs_automount_task);
}

static struct vfsmount *btrfs_automount(struct path *path)
{
struct fs_context fc;
struct vfsmount *mnt;
int timeout = READ_ONCE(btrfs_mountpoint_expiry_timeout);

if (path->dentry == path->mnt->mnt_root)
/* dentry is root of the vfsmount,
* so skip automount processing
*/
return ERR_PTR(-EISDIR);
/* Create a bind-mount to expose the subvol in the mount table */
fc.root = path->dentry;
fc.sb_flags = 0;
fc.source = "btrfs-automount";
mnt = vfs_create_mount(&fc);
if (IS_ERR(mnt))
return mnt;
mntget(mnt);
mnt_set_expiry(mnt, &btrfs_automount_list);
if (timeout > 0)
schedule_delayed_work(&btrfs_automount_task, timeout);
return mnt;
}

static int param_set_btrfs_timeout(const char *val, const struct kernel_param *kp)
{
long num;
int ret;

if (!val)
return -EINVAL;
ret = kstrtol(val, 0, &num);
if (ret)
return -EINVAL;
if (num > 0) {
if (num >= INT_MAX / HZ)
num = INT_MAX;
else
num *= HZ;
*((int *)kp->arg) = num;
if (!list_empty(&btrfs_automount_list))
mod_delayed_work(system_wq, &btrfs_automount_task, num);
} else {
*((int *)kp->arg) = -1*HZ;
cancel_delayed_work(&btrfs_automount_task);
}
return 0;
}

static int param_get_btrfs_timeout(char *buffer, const struct kernel_param *kp)
{
long num = *((int *)kp->arg);

if (num > 0) {
if (num >= INT_MAX - (HZ - 1))
num = INT_MAX / HZ;
else
num = (num + (HZ - 1)) / HZ;
} else
num = -1;
return scnprintf(buffer, PAGE_SIZE, "%li\n", num);
}

static const struct kernel_param_ops param_ops_btrfs_timeout = {
.set = param_set_btrfs_timeout,
.get = param_get_btrfs_timeout,
};
#define param_check_btrfs_timeout(name, p) __param_check(name, p, int)

module_param(btrfs_mountpoint_expiry_timeout, btrfs_timeout, 0644);
MODULE_PARM_DESC(btrfs_mountpoint_expiry_timeout,
"Set the btrfs automounted mountpoint timeout value (seconds). "
"Values <= 0 turn expiration off.");

static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
Expand Down Expand Up @@ -9046,6 +9144,15 @@ static int btrfs_getattr(struct user_namespace *mnt_userns,

generic_fillattr(&init_user_ns, inode, stat);
stat->dev = BTRFS_I(inode)->root->anon_dev;
if ((inode->i_flags & S_AUTOMOUNT) &&
path->dentry != path->mnt->mnt_root) {
/* This is the mounted-on side of the automount,
* so we show the inode number from the ROOT_ITEM key
* and the dev of the mountpoint.
*/
stat->ino = btrfs_location_to_ino(&BTRFS_I(inode)->root->root_key);
stat->dev = BTRFS_I(d_inode(path->mnt->mnt_root))->root->anon_dev;
}

spin_lock(&BTRFS_I(inode)->lock);
delalloc_bytes = BTRFS_I(inode)->new_delalloc_bytes;
Expand Down Expand Up @@ -10686,4 +10793,5 @@ static const struct inode_operations btrfs_symlink_inode_operations = {

const struct dentry_operations btrfs_dentry_operations = {
.d_delete = btrfs_dentry_delete,
.d_automount = btrfs_automount,
};
1 change: 1 addition & 0 deletions fs/btrfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
static void btrfs_put_super(struct super_block *sb)
{
close_ctree(btrfs_sb(sb));
btrfs_release_automount_timer();
}

enum {
Expand Down

0 comments on commit 5874902

Please sign in to comment.