14

Using a loop-mounted user created btrfs filesystem, with the permissions set correcly, a user is able to freely create btrfs subvolumes:

user@machine:~/btrfs/fs/snapshots$ /sbin/btrfs sub create newsubvol
Create subvolume './newsubvol'

However, trying to delete the newly created subvolume results in an error:

user@machine:~/btrfs/fs/snapshots$ /sbin/btrfs sub del newsubvol
Delete subvolume '/home/user/btrfs/fs/snapshots/newsubvol'
ERROR: cannot delete '/home/user/btrfs/fs/snapshots/newsubvol'

The root user, of course, is able to delete it:

root@machine:/home/user/btrfs/fs/snapshots# /sbin/btrfs sub del newsubvol
Delete subvolume '/home/user/btrfs/fs/snapshots/newsubvol'

This difference in behavior between the create and delete operations seems a bit strange. Can anyone shed some light on this?

Here is the exact sequence of commands:

user@machine:~$ dd if=/dev/zero of=btrfs_disk bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 1.2345 s, 84.9 MB/s
user@machine:~$ mkdir mountpoint
user@machine:~$ /sbin/mkfs.btrfs btrfs_disk

WARNING! - Btrfs Btrfs v0.19 IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using

SMALL VOLUME: forcing mixed metadata/data groups
Created a data/metadata chunk of size 8388608
fs created label (null) on btrfs_disk
    nodesize 4096 leafsize 4096 sectorsize 4096 size 100.00MB
Btrfs Btrfs v0.19
user@machine:~$ sudo mount btrfs_disk mountpoint/
user@machine:~$ cd mountpoint/
user@machine:~/mountpoint$ /sbin/btrfs sub create test
Create subvolume './test'
user@machine:~/mountpoint$ /sbin/btrfs sub delete test
Delete subvolume '/home/user/mountpoint/test'
ERROR: cannot delete '/home/user/mountpoint/test' - Operation not permitted

Here are the permissions:

user@machine:~/mountpoint$ ls -la
total 4
drwxr-xr-x 1 user user    8 Set  4 09:30 .
drwx------ 1 user user 4486 Set  4 09:29 ..
drwx------ 1 user user    0 Set  4 09:38 test

And the relevant line on df -T:

Filesystem              Type     1K-blocks      Used Available Use% Mounted on
/dev/loop0              btrfs       102400        32     98284   1% /home/user/mountpoint

The distro is a Debian Wheezy, 3.2.0-4-686-pae kernel, v0.19 btrfs-tools. The situation still occurs on Ubuntu Saucy, 3.11.0-4-generic kernel, v0.20-rc1 btrfs-tools.

6
  • From my understanding this filesystem type is still experimental and not production ready.
    – mdpc
    Aug 31, 2013 at 5:49
  • Can you add the output of df -T and btrfs version? When I tried the same I got the following error "ERROR: cannot create subvolume - Permission denied"
    – bsd
    Sep 3, 2013 at 19:42
  • @mdpc While that's true, btrfs has been around for some years, and it's expected to be somewhat stable at this stage. It may still be useful to understand if this is a bug or a 'feature', at this point. Sep 4, 2013 at 8:49
  • @bdowning I've added the exact sequence of commands, df, and the versions I used. Sep 4, 2013 at 8:50
  • The 3.2 kernel is now over a year and a half old. If you want to play with experimental filesystems, you might want to run a more up to date kernel.
    – psusi
    Sep 4, 2013 at 20:19

3 Answers 3

16
+50

Well this was a learning experience for me but I eventually figured it out. I'll explain my process here so that it's easier to know how to figure this stuff out on your own (BTRFS documentation, as I'm sure you found out, is relatively incomplete for the time being).

At first I thought that creating the subvolume was an ioctl with a handler that didn't do any capability check (which may or may not have been a security issue depending on whether there was some logic to it) whereas deleting it was modifying the metadata directly (and thus the user might require CAP_SYS_RAWIO to work properly).

To verify, I cracked open the btrfs-utils source code and this is what I found:

Create subvolume, cmds-receive.c Line 180:
         ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);

Delete subvolume, cmds-subvolume.c Line 259:
         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);

Well, that ain't helpful, they're both ioctl's (interesting side note: "snapshot" is often used interchangably in the source code with "subvolume" for some reason). So I went to the kernel source code and found both handlers in fs/btrfs/ioctl.c.

Eventually, I traced it back to btrfs_ioctl_snap_destroy() and on line 2116 :

     if (!capable(CAP_SYS_ADMIN)){

Specifically, this is a check if they don't have the capability but if they do have it, the logic skips straight to performing the operation. The body of the if statement checks to see if it's regular user who's the owner of the subvolume's inode and the USER_SUBVOL_RM_ALLOWED BTRFS option is enable it continues executing the handler. If they don't have either the ioctl handler exits with an error.

So it looks like destroying a "snapshot" (aka "subvolume") generally requires a user that has CAP_SYS_ADMIN (or for USER_SUBVOL_RM_ALLOWED to be enabled and the user "owns" the given subvolume). Great, what about creating a snapshot/volume?

The handler for the ioctl appears to be btrfs_ioctl_snap_create() this handler appears to contain no call to capable() directly or indirectly. Since that's the main way access is brokered I'm taking this to mean that subvolume creation always succeeds. This explains at a functional level why you're seeing the what you're seeing.

I can't speak to why this is considered desirable outside of BTRFS's main use case being with a server with restricted user access. That's not sufficient but I'm not seeing any code to actually stop the operation. If you can't find an answer to why that is (and you care to have it) you may have to ask on the kernel mailing list.

Conclusion

My research seems to indicate that anyone can create subvolumes but in order to delete a subvolume you either need to have CAP_SYS_ADMIN or it needs true both that the calling user is the owner of the subvolume inode and USER_SUBVOL_RM_ALLOWED enabled.

The subvolume creation doesn't make sense so I'm probably missing some indirect way that the operation is denied since that seems like an easy way to DoS a system.

Note: I'm not in a place where I can verify this functionality but once I get home I can set if setcap magic works how this predicts.

3
  • The way it makes sense (regarding your DoS concern) is if rmdir is allowed on empty subvolumes. Then rm -r will work transparently. Unfortunately the code simply wasn't developed (yet). It looks like someone made three tries in 2010 and then gave up :(. spinics.net/lists/linux-btrfs/msg06499.html
    – sourcejedi
    Mar 6, 2016 at 18:11
  • How to find out which user is the "owner of the subvolume"?
    – Abdull
    Feb 14 at 17:59
  • @Abdull - Presumably, one could use ls -l or stat to display the owner of a subvolume, just as one would to display the owner of a directory or file. And, presumably, chown could be used to change the owner.
    – mpb
    Sep 25 at 18:01
5

Deleting a subvolume allows someone to unlink files that they do not own. In my opinion, the files a privileged user writes in a location chosen by a less privileged user are fair game, but the person who contributed the non-root deletion functionality probably didn't feel confident enough about how safe those semantics were and was content to submit them as a new mount option (mount -o user_subvol_rm_allowed).

3
  • 1
    Well, surprisingly in UNIX® you can easily unlink a file you don't own — you just need to have write permission in its directory.
    – poige
    Oct 27, 2013 at 16:25
  • I have the same problem on fedora 20, i have /home in a subvolume, i'm using the user root, but anyaway, i cannot delete /home
    – c4f4t0r
    Jan 19, 2014 at 1:58
  • 1
    poige, if the file in question is in a folder that you do not own, you cannot unlink it and you cannot unlink the folder itself since it still contains stuff in it. You may only do non-destructive operations on this folder, like moving it or renaming it.
    – sleblanc
    Jan 18, 2015 at 21:38
-1

"Can't delete /home" (which is @home).

Why would you want to delete the subvolume on which your /home/ account resides, unless you have created a /home_snapshot_yymmdd snapshot to replace /home with?

I'm new to using btrfs but this is what I've found out: @/ and @home (/ and /home) are created by btrfs when it is installed on your HD as the file system. Unless you are doing a restore of /home from a previous snapshot, as I understand it, you'd be cutting yourself off at your knees.

However, you can mount the device that /home is on, AS ROOT, using mount /dev/sa /mnt/ (or what ever device your running btrfs system on) Then cd to /mnt/ and from there issue the delete command for @home. Then you can use the mv command to move @home_snapshot_yymmdd (or what ever you named it) to @home. The move may take hours, depending on the size of @home. Then CD back to your own account and issue sudo umount /mnt/ You've never really logout or shut down your system. That's a beauty of btrfs.

1
  • Seems that they already have done so. Btrfs does not make @ and @home unless you tell them to (or your distro like Ubuntu does that for you).
    – Anthon
    Jan 28, 2014 at 17:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .