2

I am trying to allow my backup program to take a snapshot of my filesystem. The goal is to create a snapshot for the duration of the backup to ensure a consistent view then the snapshot can be deleted.

However it appears that creating a backup as a user is not allowed.

% btrfs subvolume snapshot / test
Create a snapshot of '/' in './test'
ERROR: cannot snapshot '/': Operation not permitted

I have tried mounting with the user_subvol_rm_allowed which works but it would require updating the owner of / to one specific user. Not only does this not work with multiple users but it gives them write access to the drive. (My understanding is that after a snapshot file permissions are identical so it doesn't actually give any more access.) It doesn't appear to work with groups even if that group has write access to the directory.

Is there a solution to giving a particular user access to snapshot (and remove) this volume? The best option I have found so far is allowing a specific set of commands via sudo which should work but I don't love. I would rather if there was a way to grant this specific capability.

1
  • Destroying a "snapshot" ("subvolume") requires CAP_SYS_ADMIN, or USER_SUBVOL_RM_ALLOWED enabled and the user "owning" the given subvolume. Commented Jun 6, 2023 at 19:02

1 Answer 1

3

There does not appear to be any reasonable way to bypass this check.


I have located the permission check in the source code. It defers to the inode_owner_or_capable.

if (!inode_owner_or_capable(idmap, src_inode)) {
    /*
     * Subvolume creation is not restricted, but snapshots
     * are limited to own subvolumes only
     */
    ret = -EPERM;
}

__btrfs_ioctl_snap_create in fs/btrfs/ioctl.c

inode_owner_or_capable is very simple and just checks if the current user matches the file UID or if the user has the CAP_FOWNER permission. The CAP_FOWNER is very powerful so isn't a useful bypass in this case.

bool inode_owner_or_capable(struct mnt_idmap *idmap,
                const struct inode *inode)
{
    vfsuid_t vfsuid;
    struct user_namespace *ns;

    vfsuid = i_uid_into_vfsuid(idmap, inode);
    if (vfsuid_eq_kuid(vfsuid, current_fsuid()))
        return true;

    ns = current_user_ns();
    if (vfsuid_has_mapping(ns, vfsuid) && ns_capable(ns, CAP_FOWNER))
        return true;
    return false;
}

inode_owner_or_capable in fs/inode.c

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.