mirror of
https://github.com/kdave/btrfs-progs
synced 2025-01-18 03:30:45 +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_SYNC_FAILED,
|
||||||
BTRFS_UTIL_ERROR_START_SYNC_FAILED,
|
BTRFS_UTIL_ERROR_START_SYNC_FAILED,
|
||||||
BTRFS_UTIL_ERROR_WAIT_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
|
* to check whether the subvolume exists; %BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND
|
||||||
* will be returned if it does not.
|
* 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.
|
* 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_SYNC_FAILED] = "Could not sync filesystem",
|
||||||
[BTRFS_UTIL_ERROR_START_SYNC_FAILED] = "Could not start filesystem sync",
|
[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_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)
|
PUBLIC const char *btrfs_util_strerror(enum btrfs_util_error err)
|
||||||
|
@ -23,7 +23,12 @@ from pathlib import PurePath
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import btrfsutil
|
import btrfsutil
|
||||||
from tests import BtrfsTestCase, HAVE_PATH_LIKE
|
from tests import (
|
||||||
|
BtrfsTestCase,
|
||||||
|
drop_privs,
|
||||||
|
HAVE_PATH_LIKE,
|
||||||
|
skipUnlessHaveNobody,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestSubvolume(BtrfsTestCase):
|
class TestSubvolume(BtrfsTestCase):
|
||||||
@ -87,7 +92,7 @@ class TestSubvolume(BtrfsTestCase):
|
|||||||
finally:
|
finally:
|
||||||
os.chdir(pwd)
|
os.chdir(pwd)
|
||||||
|
|
||||||
def test_subvolume_info(self):
|
def _test_subvolume_info(self, subvol, snapshot):
|
||||||
for arg in self.path_or_fd(self.mountpoint):
|
for arg in self.path_or_fd(self.mountpoint):
|
||||||
with self.subTest(type=type(arg)):
|
with self.subTest(type=type(arg)):
|
||||||
info = btrfsutil.subvolume_info(arg)
|
info = btrfsutil.subvolume_info(arg)
|
||||||
@ -100,7 +105,7 @@ class TestSubvolume(BtrfsTestCase):
|
|||||||
self.assertEqual(info.parent_uuid, bytes(16))
|
self.assertEqual(info.parent_uuid, bytes(16))
|
||||||
self.assertEqual(info.received_uuid, bytes(16))
|
self.assertEqual(info.received_uuid, bytes(16))
|
||||||
self.assertNotEqual(info.generation, 0)
|
self.assertNotEqual(info.generation, 0)
|
||||||
self.assertEqual(info.ctransid, 0)
|
self.assertGreaterEqual(info.ctransid, 0)
|
||||||
self.assertEqual(info.otransid, 0)
|
self.assertEqual(info.otransid, 0)
|
||||||
self.assertEqual(info.stransid, 0)
|
self.assertEqual(info.stransid, 0)
|
||||||
self.assertEqual(info.rtransid, 0)
|
self.assertEqual(info.rtransid, 0)
|
||||||
@ -109,9 +114,6 @@ class TestSubvolume(BtrfsTestCase):
|
|||||||
self.assertEqual(info.stime, 0)
|
self.assertEqual(info.stime, 0)
|
||||||
self.assertEqual(info.rtime, 0)
|
self.assertEqual(info.rtime, 0)
|
||||||
|
|
||||||
subvol = os.path.join(self.mountpoint, 'subvol')
|
|
||||||
btrfsutil.create_subvolume(subvol)
|
|
||||||
|
|
||||||
info = btrfsutil.subvolume_info(subvol)
|
info = btrfsutil.subvolume_info(subvol)
|
||||||
self.assertEqual(info.id, 256)
|
self.assertEqual(info.id, 256)
|
||||||
self.assertEqual(info.parent_id, 5)
|
self.assertEqual(info.parent_id, 5)
|
||||||
@ -132,19 +134,43 @@ class TestSubvolume(BtrfsTestCase):
|
|||||||
self.assertEqual(info.rtime, 0)
|
self.assertEqual(info.rtime, 0)
|
||||||
|
|
||||||
subvol_uuid = info.uuid
|
subvol_uuid = info.uuid
|
||||||
snapshot = os.path.join(self.mountpoint, 'snapshot')
|
|
||||||
btrfsutil.create_snapshot(subvol, snapshot)
|
|
||||||
|
|
||||||
info = btrfsutil.subvolume_info(snapshot)
|
info = btrfsutil.subvolume_info(snapshot)
|
||||||
self.assertEqual(info.parent_uuid, subvol_uuid)
|
self.assertEqual(info.parent_uuid, subvol_uuid)
|
||||||
|
|
||||||
# TODO: test received_uuid, stransid, rtransid, stime, and rtime
|
# 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):
|
for arg in self.path_or_fd(self.mountpoint):
|
||||||
with self.subTest(type=type(arg)):
|
with self.subTest(type=type(arg)):
|
||||||
with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
|
with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
|
||||||
# BTRFS_EXTENT_TREE_OBJECTID
|
# BTRFS_EXTENT_TREE_OBJECTID
|
||||||
btrfsutil.subvolume_info(arg, 2)
|
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):
|
def test_read_only(self):
|
||||||
for arg in self.path_or_fd(self.mountpoint):
|
for arg in self.path_or_fd(self.mountpoint):
|
||||||
|
@ -31,6 +31,11 @@
|
|||||||
|
|
||||||
#include "btrfsutil_internal.h"
|
#include "btrfsutil_internal.h"
|
||||||
|
|
||||||
|
static bool is_root(void)
|
||||||
|
{
|
||||||
|
return geteuid() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
|
* This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
|
||||||
* a file descriptor and calling it, because fstat() and fstatfs() don't accept
|
* 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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum btrfs_util_error get_subvolume_info_root(int fd, uint64_t id,
|
static enum btrfs_util_error get_subvolume_info_privileged(int fd, uint64_t id,
|
||||||
struct btrfs_util_subvolume_info *subvol)
|
struct btrfs_util_subvolume_info *subvol)
|
||||||
{
|
{
|
||||||
struct btrfs_ioctl_search_args search = {
|
struct btrfs_ioctl_search_args search = {
|
||||||
.key = {
|
.key = {
|
||||||
@ -383,6 +388,45 @@ static enum btrfs_util_error get_subvolume_info_root(int fd, uint64_t id,
|
|||||||
return BTRFS_UTIL_OK;
|
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,
|
PUBLIC enum btrfs_util_error btrfs_util_subvolume_info_fd(int fd, uint64_t id,
|
||||||
struct btrfs_util_subvolume_info *subvol)
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (!is_root())
|
||||||
|
return get_subvolume_info_unprivileged(fd, subvol);
|
||||||
|
|
||||||
err = btrfs_util_subvolume_id_fd(fd, &id);
|
err = btrfs_util_subvolume_id_fd(fd, &id);
|
||||||
if (err)
|
if (err)
|
||||||
return 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 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,
|
PUBLIC enum btrfs_util_error btrfs_util_get_subvolume_read_only_fd(int fd,
|
||||||
|
Loading…
Reference in New Issue
Block a user