Skip to content

Commit

Permalink
libbtrfsutil: relax the privileges of subvolume_info()
Browse files Browse the repository at this point in the history
Attempt to use the BTRFS_IOC_GET_SUBVOL_INFO ioctl (added in kernel
4.18) for subvolume_info() if not root. Also, rename
get_subvolume_info_root() -> get_subvolume_info_privileged() for
consistency with further changes.

This is based on a patch from Misono Tomohiro.

Signed-off-by: Omar Sandoval <osandov@fb.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
osandov authored and kdave committed Nov 26, 2018
1 parent 39ac43a commit bfe2dc3
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 12 deletions.
4 changes: 3 additions & 1 deletion libbtrfsutil/btrfsutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ enum btrfs_util_error {
BTRFS_UTIL_ERROR_SYNC_FAILED,
BTRFS_UTIL_ERROR_START_SYNC_FAILED,
BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED,
BTRFS_UTIL_ERROR_GET_SUBVOL_INFO_FAILED,
};

/**
Expand Down Expand Up @@ -266,7 +267,8 @@ struct btrfs_util_subvolume_info {
* to check whether the subvolume exists; %BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
* will be returned if it does not.
*
* This requires appropriate privilege (CAP_SYS_ADMIN).
* This requires appropriate privilege (CAP_SYS_ADMIN) unless @id is zero and
* the kernel supports BTRFS_IOC_GET_SUBVOL_INFO (kernel >= 4.18).
*
* Return: %BTRFS_UTIL_OK on success, non-zero error code on failure.
*/
Expand Down
2 changes: 2 additions & 0 deletions libbtrfsutil/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ static const char * const error_messages[] = {
[BTRFS_UTIL_ERROR_SYNC_FAILED] = "Could not sync filesystem",
[BTRFS_UTIL_ERROR_START_SYNC_FAILED] = "Could not start filesystem sync",
[BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED] = "Could not wait for filesystem sync",
[BTRFS_UTIL_ERROR_GET_SUBVOL_INFO_FAILED] =
"Could not get subvolume information with BTRFS_IOC_GET_SUBVOL_INFO",
};

PUBLIC const char *btrfs_util_strerror(enum btrfs_util_error err)
Expand Down
42 changes: 34 additions & 8 deletions libbtrfsutil/python/tests/test_subvolume.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
import traceback

import btrfsutil
from tests import BtrfsTestCase, HAVE_PATH_LIKE
from tests import (
BtrfsTestCase,
drop_privs,
HAVE_PATH_LIKE,
skipUnlessHaveNobody,
)


class TestSubvolume(BtrfsTestCase):
Expand Down Expand Up @@ -87,7 +92,7 @@ def test_subvolume_path(self):
finally:
os.chdir(pwd)

def test_subvolume_info(self):
def _test_subvolume_info(self, subvol, snapshot):
for arg in self.path_or_fd(self.mountpoint):
with self.subTest(type=type(arg)):
info = btrfsutil.subvolume_info(arg)
Expand All @@ -100,7 +105,7 @@ def test_subvolume_info(self):
self.assertEqual(info.parent_uuid, bytes(16))
self.assertEqual(info.received_uuid, bytes(16))
self.assertNotEqual(info.generation, 0)
self.assertEqual(info.ctransid, 0)
self.assertGreaterEqual(info.ctransid, 0)
self.assertEqual(info.otransid, 0)
self.assertEqual(info.stransid, 0)
self.assertEqual(info.rtransid, 0)
Expand All @@ -109,9 +114,6 @@ def test_subvolume_info(self):
self.assertEqual(info.stime, 0)
self.assertEqual(info.rtime, 0)

subvol = os.path.join(self.mountpoint, 'subvol')
btrfsutil.create_subvolume(subvol)

info = btrfsutil.subvolume_info(subvol)
self.assertEqual(info.id, 256)
self.assertEqual(info.parent_id, 5)
Expand All @@ -132,19 +134,43 @@ def test_subvolume_info(self):
self.assertEqual(info.rtime, 0)

subvol_uuid = info.uuid
snapshot = os.path.join(self.mountpoint, 'snapshot')
btrfsutil.create_snapshot(subvol, snapshot)

info = btrfsutil.subvolume_info(snapshot)
self.assertEqual(info.parent_uuid, subvol_uuid)

# TODO: test received_uuid, stransid, rtransid, stime, and rtime

def test_subvolume_info(self):
subvol = os.path.join(self.mountpoint, 'subvol')
btrfsutil.create_subvolume(subvol)
snapshot = os.path.join(self.mountpoint, 'snapshot')
btrfsutil.create_snapshot(subvol, snapshot)

self._test_subvolume_info(subvol, snapshot)

for arg in self.path_or_fd(self.mountpoint):
with self.subTest(type=type(arg)):
with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
# BTRFS_EXTENT_TREE_OBJECTID
btrfsutil.subvolume_info(arg, 2)
self.assertEqual(e.exception.btrfsutilerror,
btrfsutil.ERROR_SUBVOLUME_NOT_FOUND)

@skipUnlessHaveNobody
def test_subvolume_info_unprivileged(self):
subvol = os.path.join(self.mountpoint, 'subvol')
btrfsutil.create_subvolume(subvol)
snapshot = os.path.join(self.mountpoint, 'snapshot')
btrfsutil.create_snapshot(subvol, snapshot)

with drop_privs():
try:
self._test_subvolume_info(subvol, snapshot)
except OSError as e:
if e.errno == errno.ENOTTY:
self.skipTest('BTRFS_IOC_GET_SUBVOL_INFO is not available')
else:
raise

def test_read_only(self):
for arg in self.path_or_fd(self.mountpoint):
Expand Down
53 changes: 50 additions & 3 deletions libbtrfsutil/subvolume.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@

#include "btrfsutil_internal.h"

static bool is_root(void)
{
return geteuid() == 0;
}

/*
* This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
* a file descriptor and calling it, because fstat() and fstatfs() don't accept
Expand Down Expand Up @@ -295,8 +300,8 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_info(const char *path,
return err;
}

static enum btrfs_util_error get_subvolume_info_root(int fd, uint64_t id,
struct btrfs_util_subvolume_info *subvol)
static enum btrfs_util_error get_subvolume_info_privileged(int fd, uint64_t id,
struct btrfs_util_subvolume_info *subvol)
{
struct btrfs_ioctl_search_args search = {
.key = {
Expand Down Expand Up @@ -383,6 +388,45 @@ static enum btrfs_util_error get_subvolume_info_root(int fd, uint64_t id,
return BTRFS_UTIL_OK;
}

static enum btrfs_util_error get_subvolume_info_unprivileged(int fd,
struct btrfs_util_subvolume_info *subvol)
{
struct btrfs_ioctl_get_subvol_info_args info;
int ret;

ret = ioctl(fd, BTRFS_IOC_GET_SUBVOL_INFO, &info);
if (ret == -1)
return BTRFS_UTIL_ERROR_GET_SUBVOL_INFO_FAILED;

subvol->id = info.treeid;
subvol->parent_id = info.parent_id;
subvol->dir_id = info.dirid;
subvol->flags = info.flags;
subvol->generation = info.generation;

memcpy(subvol->uuid, info.uuid, sizeof(subvol->uuid));
memcpy(subvol->parent_uuid, info.parent_uuid,
sizeof(subvol->parent_uuid));
memcpy(subvol->received_uuid, info.received_uuid,
sizeof(subvol->received_uuid));

subvol->ctransid = info.ctransid;
subvol->otransid = info.otransid;
subvol->stransid = info.stransid;
subvol->rtransid = info.rtransid;

subvol->ctime.tv_sec = info.ctime.sec;
subvol->ctime.tv_nsec = info.ctime.nsec;
subvol->otime.tv_sec = info.otime.sec;
subvol->otime.tv_nsec = info.otime.nsec;
subvol->stime.tv_sec = info.stime.sec;
subvol->stime.tv_nsec = info.stime.nsec;
subvol->rtime.tv_sec = info.rtime.sec;
subvol->rtime.tv_nsec = info.rtime.nsec;

return BTRFS_UTIL_OK;
}

PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
struct btrfs_util_subvolume_info *subvol)
{
Expand All @@ -393,6 +437,9 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
if (err)
return err;

if (!is_root())
return get_subvolume_info_unprivileged(fd, subvol);

err = btrfs_util_subvolume_id_fd(fd, &id);
if (err)
return err;
Expand All @@ -404,7 +451,7 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND;
}

return get_subvolume_info_root(fd, id, subvol);
return get_subvolume_info_privileged(fd, id, subvol);
}

PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd,
Expand Down

0 comments on commit bfe2dc3

Please sign in to comment.