summaryrefslogtreecommitdiff
path: root/libbtrfsutil
diff options
context:
space:
mode:
authorOmar Sandoval <osandov@fb.com>2018-11-13 23:47:02 -0800
committerDavid Sterba <dsterba@suse.com>2018-11-26 16:45:30 +0100
commitbfe2dc379662bd2aba40db9fe9185a57dae0c4a9 (patch)
tree1e59cac3e50c9f14d503713bc84317091772e040 /libbtrfsutil
parent39ac43a2a47cf99cb4914696b51f8d9e28abd0d9 (diff)
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>
Diffstat (limited to 'libbtrfsutil')
-rw-r--r--libbtrfsutil/btrfsutil.h4
-rw-r--r--libbtrfsutil/errors.c2
-rw-r--r--libbtrfsutil/python/tests/test_subvolume.py42
-rw-r--r--libbtrfsutil/subvolume.c53
4 files changed, 89 insertions, 12 deletions
diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h
index 6d655f4..c192500 100644
--- a/libbtrfsutil/btrfsutil.h
+++ b/libbtrfsutil/btrfsutil.h
@@ -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.
*/
diff --git a/libbtrfsutil/errors.c b/libbtrfsutil/errors.c
index 634edc6..cf968b0 100644
--- a/libbtrfsutil/errors.c
+++ b/libbtrfsutil/errors.c
@@ -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)
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index 4049b08..55ebf34 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -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):
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c
index 0d7ef5b..69654db 100644
--- a/libbtrfsutil/subvolume.c
+++ b/libbtrfsutil/subvolume.c
@@ -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,