mirror of
https://github.com/kdave/btrfs-progs
synced 2025-01-03 04:02:04 +00:00
libbtrfsutil: relax the privileges of subvolume_info()
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>
This commit is contained in:
parent
39ac43a2a4
commit
bfe2dc3796
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -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.
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -23,7 +23,12 @@ from pathlib import PurePath
|
||||
import traceback
|
||||
|
||||
import btrfsutil
|
||||
from tests import BtrfsTestCase, HAVE_PATH_LIKE
|
||||
from tests import (
|
||||
BtrfsTestCase,
|
||||
drop_privs,
|
||||
HAVE_PATH_LIKE,
|
||||
skipUnlessHaveNobody,
|
||||
)
|
||||
|
||||
|
||||
class TestSubvolume(BtrfsTestCase):
|
||||
@ -87,7 +92,7 @@ class TestSubvolume(BtrfsTestCase):
|
||||
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)
|
||||
@ -100,7 +105,7 @@ class TestSubvolume(BtrfsTestCase):
|
||||
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)
|
||||
@ -109,9 +114,6 @@ class TestSubvolume(BtrfsTestCase):
|
||||
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)
|
||||
@ -132,19 +134,43 @@ class TestSubvolume(BtrfsTestCase):
|
||||
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):
|
||||
|
@ -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
|
||||
@ -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 = {
|
||||
@ -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)
|
||||
{
|
||||
@ -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;
|
||||
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user