summaryrefslogtreecommitdiff
path: root/libbtrfsutil/python
diff options
context:
space:
mode:
authorOmar Sandoval <osandov@fb.com>2018-02-14 22:16:33 -0800
committerDavid Sterba <dsterba@suse.com>2018-03-06 11:28:36 +0100
commit8b87811f945bec2a0681334030ed51af1e4828f6 (patch)
tree217417d7eeddb7eb5704908ae8991c4b5158187d /libbtrfsutil/python
parentf676a8ad118ecba7fbf4edc77b91f788c6fa7e7c (diff)
libbtrfsutil: add btrfs_util_subvolume_path()
We can just walk up root backrefs with BTRFS_IOC_TREE_SEARCH and inode paths with BTRFS_IOC_INO_LOOKUP. Signed-off-by: Omar Sandoval <osandov@fb.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'libbtrfsutil/python')
-rw-r--r--libbtrfsutil/python/btrfsutilpy.h1
-rw-r--r--libbtrfsutil/python/module.c8
-rw-r--r--libbtrfsutil/python/subvolume.c30
-rw-r--r--libbtrfsutil/python/tests/test_subvolume.py31
4 files changed, 70 insertions, 0 deletions
diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h
index 19ba71f3..1fd475b4 100644
--- a/libbtrfsutil/python/btrfsutilpy.h
+++ b/libbtrfsutil/python/btrfsutilpy.h
@@ -63,6 +63,7 @@ PyObject *start_sync(PyObject *self, PyObject *args, PyObject *kwds);
PyObject *wait_sync(PyObject *self, PyObject *args, PyObject *kwds);
PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds);
+PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds);
PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds);
void add_module_constants(PyObject *m);
diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c
index deb26875..455e909a 100644
--- a/libbtrfsutil/python/module.c
+++ b/libbtrfsutil/python/module.c
@@ -165,6 +165,14 @@ static PyMethodDef btrfsutil_methods[] = {
"Get the ID of the subvolume containing a file.\n\n"
"Arguments:\n"
"path -- string, bytes, path-like object, or open file descriptor"},
+ {"subvolume_path", (PyCFunction)subvolume_path,
+ METH_VARARGS | METH_KEYWORDS,
+ "subvolume_path(path, id=0) -> int\n\n"
+ "Get the path of a subvolume relative to the filesystem root.\n\n"
+ "Arguments:\n"
+ "path -- string, bytes, path-like object, or open file descriptor\n"
+ "id -- if not zero, instead of returning the subvolume path of the\n"
+ "given path, return the path of the subvolume with this ID"},
{"create_subvolume", (PyCFunction)create_subvolume,
METH_VARARGS | METH_KEYWORDS,
"create_subvolume(path, async=False)\n\n"
diff --git a/libbtrfsutil/python/subvolume.c b/libbtrfsutil/python/subvolume.c
index 6f2080ee..6382d290 100644
--- a/libbtrfsutil/python/subvolume.c
+++ b/libbtrfsutil/python/subvolume.c
@@ -72,6 +72,36 @@ PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
return PyLong_FromUnsignedLongLong(id);
}
+PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *keywords[] = {"path", "id", NULL};
+ struct path_arg path = {.allow_fd = true};
+ enum btrfs_util_error err;
+ uint64_t id = 0;
+ char *subvol_path;
+ PyObject *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:subvolume_path",
+ keywords, &path_converter, &path, &id))
+ return NULL;
+
+ if (path.path)
+ err = btrfs_util_subvolume_path(path.path, id, &subvol_path);
+ else
+ err = btrfs_util_subvolume_path_fd(path.fd, id, &subvol_path);
+ if (err) {
+ SetFromBtrfsUtilErrorWithPath(err, &path);
+ path_cleanup(&path);
+ return NULL;
+ }
+
+ path_cleanup(&path);
+
+ ret = PyUnicode_DecodeFSDefault(subvol_path);
+ free(subvol_path);
+ return ret;
+}
+
PyObject *create_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *keywords[] = {"path", "async", "qgroup_inherit", NULL};
diff --git a/libbtrfsutil/python/tests/test_subvolume.py b/libbtrfsutil/python/tests/test_subvolume.py
index f6c5958d..57ba27bf 100644
--- a/libbtrfsutil/python/tests/test_subvolume.py
+++ b/libbtrfsutil/python/tests/test_subvolume.py
@@ -56,6 +56,37 @@ class TestSubvolume(BtrfsTestCase):
with self.subTest(type=type(arg)):
self.assertEqual(btrfsutil.subvolume_id(arg), 5)
+ def test_subvolume_path(self):
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'subvol1'))
+ os.mkdir(os.path.join(self.mountpoint, 'dir1'))
+ os.mkdir(os.path.join(self.mountpoint, 'dir1/dir2'))
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'dir1/dir2/subvol2'))
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'dir1/dir2/subvol2/subvol3'))
+ os.mkdir(os.path.join(self.mountpoint, 'subvol1/dir3'))
+ btrfsutil.create_subvolume(os.path.join(self.mountpoint, 'subvol1/dir3/subvol4'))
+
+ for arg in self.path_or_fd(self.mountpoint):
+ with self.subTest(type=type(arg)):
+ self.assertEqual(btrfsutil.subvolume_path(arg), '')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 5), '')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 256), 'subvol1')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 257), 'dir1/dir2/subvol2')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 258), 'dir1/dir2/subvol2/subvol3')
+ self.assertEqual(btrfsutil.subvolume_path(arg, 259), 'subvol1/dir3/subvol4')
+
+ pwd = os.getcwd()
+ try:
+ os.chdir(self.mountpoint)
+ path = ''
+ for i in range(26):
+ name = chr(ord('a') + i) * 255
+ path = os.path.join(path, name)
+ btrfsutil.create_subvolume(name)
+ os.chdir(name)
+ self.assertEqual(btrfsutil.subvolume_path('.'), path)
+ finally:
+ os.chdir(pwd)
+
def test_create_subvolume(self):
subvol = os.path.join(self.mountpoint, 'subvol')