libbtrfsutil: add btrfs_util_is_subvolume() and btrfs_util_subvolume_id()
These are the most trivial helpers in the library and will be used to implement several of the more involved functions. Signed-off-by: Omar Sandoval <osandov@fb.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
1b2775bdb0
commit
92d4035074
2
Makefile
2
Makefile
|
@ -136,7 +136,7 @@ libbtrfsutil_minor := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_MINOR ([0-
|
|||
libbtrfsutil_patch := $(shell sed -rn 's/^\#define BTRFS_UTIL_VERSION_PATCH ([0-9])+$$/\1/p' libbtrfsutil/btrfsutil.h)
|
||||
libbtrfsutil_version := $(libbtrfsutil_major).$(libbtrfsutil_minor).$(libbtrfsutil_patch)
|
||||
libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/filesystem.o \
|
||||
libbtrfsutil/qgroup.o
|
||||
libbtrfsutil/subvolume.o libbtrfsutil/qgroup.o
|
||||
convert_objects = convert/main.o convert/common.o convert/source-fs.o \
|
||||
convert/source-ext2.o convert/source-reiserfs.o
|
||||
mkfs_objects = mkfs/main.o mkfs/common.o mkfs/rootdir.o
|
||||
|
|
|
@ -61,6 +61,8 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err,
|
|||
PyObject *filesystem_sync(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
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);
|
||||
|
||||
void add_module_constants(PyObject *m);
|
||||
|
||||
|
|
|
@ -153,6 +153,18 @@ static PyMethodDef btrfsutil_methods[] = {
|
|||
"path -- string, bytes, path-like object, or open file descriptor\n"
|
||||
"transid -- int transaction ID to wait for, or zero for the current\n"
|
||||
"transaction"},
|
||||
{"is_subvolume", (PyCFunction)is_subvolume,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"is_subvolume(path) -> bool\n\n"
|
||||
"Get whether a file is a subvolume.\n\n"
|
||||
"Arguments:\n"
|
||||
"path -- string, bytes, path-like object, or open file descriptor"},
|
||||
{"subvolume_id", (PyCFunction)subvolume_id,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"subvolume_id(path) -> int\n\n"
|
||||
"Get the ID of the subvolume containing a file.\n\n"
|
||||
"Arguments:\n"
|
||||
"path -- string, bytes, path-like object, or open file descriptor"},
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ module = Extension(
|
|||
'filesystem.c',
|
||||
'module.c',
|
||||
'qgroup.c',
|
||||
'subvolume.c',
|
||||
],
|
||||
include_dirs=['..'],
|
||||
library_dirs=['../..'],
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Facebook
|
||||
*
|
||||
* This file is part of libbtrfsutil.
|
||||
*
|
||||
* libbtrfsutil is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libbtrfsutil is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "btrfsutilpy.h"
|
||||
|
||||
PyObject *is_subvolume(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *keywords[] = {"path", NULL};
|
||||
struct path_arg path = {.allow_fd = true};
|
||||
enum btrfs_util_error err;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:is_subvolume",
|
||||
keywords, &path_converter, &path))
|
||||
return NULL;
|
||||
|
||||
if (path.path)
|
||||
err = btrfs_util_is_subvolume(path.path);
|
||||
else
|
||||
err = btrfs_util_is_subvolume_fd(path.fd);
|
||||
if (err == BTRFS_UTIL_OK) {
|
||||
path_cleanup(&path);
|
||||
Py_RETURN_TRUE;
|
||||
} else if (err == BTRFS_UTIL_ERROR_NOT_BTRFS ||
|
||||
err == BTRFS_UTIL_ERROR_NOT_SUBVOLUME) {
|
||||
path_cleanup(&path);
|
||||
Py_RETURN_FALSE;
|
||||
} else {
|
||||
SetFromBtrfsUtilErrorWithPath(err, &path);
|
||||
path_cleanup(&path);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *subvolume_id(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *keywords[] = {"path", NULL};
|
||||
struct path_arg path = {.allow_fd = true};
|
||||
enum btrfs_util_error err;
|
||||
uint64_t id;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:subvolume_id",
|
||||
keywords, &path_converter, &path))
|
||||
return NULL;
|
||||
|
||||
if (path.path)
|
||||
err = btrfs_util_subvolume_id(path.path, &id);
|
||||
else
|
||||
err = btrfs_util_subvolume_id_fd(path.fd, &id);
|
||||
if (err) {
|
||||
SetFromBtrfsUtilErrorWithPath(err, &path);
|
||||
path_cleanup(&path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path_cleanup(&path);
|
||||
return PyLong_FromUnsignedLongLong(id);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
# Copyright (C) 2018 Facebook
|
||||
#
|
||||
# This file is part of libbtrfsutil.
|
||||
#
|
||||
# libbtrfsutil is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# libbtrfsutil is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import fcntl
|
||||
import errno
|
||||
import os
|
||||
import os.path
|
||||
from pathlib import PurePath
|
||||
import traceback
|
||||
|
||||
import btrfsutil
|
||||
from tests import BtrfsTestCase
|
||||
|
||||
|
||||
class TestSubvolume(BtrfsTestCase):
|
||||
def test_is_subvolume(self):
|
||||
dir = os.path.join(self.mountpoint, 'foo')
|
||||
os.mkdir(dir)
|
||||
|
||||
for arg in self.path_or_fd(self.mountpoint):
|
||||
with self.subTest(type=type(arg)):
|
||||
self.assertTrue(btrfsutil.is_subvolume(arg))
|
||||
for arg in self.path_or_fd(dir):
|
||||
with self.subTest(type=type(arg)):
|
||||
self.assertFalse(btrfsutil.is_subvolume(arg))
|
||||
|
||||
with self.assertRaises(btrfsutil.BtrfsUtilError) as e:
|
||||
btrfsutil.is_subvolume(os.path.join(self.mountpoint, 'bar'))
|
||||
# This is a bit of an implementation detail, but really this is testing
|
||||
# that the exception is initialized correctly.
|
||||
self.assertEqual(e.exception.btrfsutilerror, btrfsutil.ERROR_STATFS_FAILED)
|
||||
self.assertEqual(e.exception.errno, errno.ENOENT)
|
||||
|
||||
def test_subvolume_id(self):
|
||||
dir = os.path.join(self.mountpoint, 'foo')
|
||||
os.mkdir(dir)
|
||||
|
||||
for arg in self.path_or_fd(self.mountpoint):
|
||||
with self.subTest(type=type(arg)):
|
||||
self.assertEqual(btrfsutil.subvolume_id(arg), 5)
|
||||
for arg in self.path_or_fd(dir):
|
||||
with self.subTest(type=type(arg)):
|
||||
self.assertEqual(btrfsutil.subvolume_id(arg), 5)
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Facebook
|
||||
*
|
||||
* This file is part of libbtrfsutil.
|
||||
*
|
||||
* libbtrfsutil is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libbtrfsutil is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with libbtrfsutil. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <linux/magic.h>
|
||||
|
||||
#include "btrfsutil_internal.h"
|
||||
|
||||
/*
|
||||
* This intentionally duplicates btrfs_util_is_subvolume_fd() instead of opening
|
||||
* a file descriptor and calling it, because fstat() and fstatfs() don't accept
|
||||
* file descriptors opened with O_PATH on old kernels (before v3.6 and before
|
||||
* v3.12, respectively), but stat() and statfs() can be called on a path that
|
||||
* the user doesn't have read or write permissions to.
|
||||
*/
|
||||
PUBLIC enum btrfs_util_error btrfs_util_is_subvolume(const char *path)
|
||||
{
|
||||
struct statfs sfs;
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
ret = statfs(path, &sfs);
|
||||
if (ret == -1)
|
||||
return BTRFS_UTIL_ERROR_STATFS_FAILED;
|
||||
|
||||
if (sfs.f_type != BTRFS_SUPER_MAGIC) {
|
||||
errno = EINVAL;
|
||||
return BTRFS_UTIL_ERROR_NOT_BTRFS;
|
||||
}
|
||||
|
||||
ret = stat(path, &st);
|
||||
if (ret == -1)
|
||||
return BTRFS_UTIL_ERROR_STAT_FAILED;
|
||||
|
||||
if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
|
||||
errno = EINVAL;
|
||||
return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
|
||||
}
|
||||
|
||||
return BTRFS_UTIL_OK;
|
||||
}
|
||||
|
||||
PUBLIC enum btrfs_util_error btrfs_util_is_subvolume_fd(int fd)
|
||||
{
|
||||
struct statfs sfs;
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
ret = fstatfs(fd, &sfs);
|
||||
if (ret == -1)
|
||||
return BTRFS_UTIL_ERROR_STATFS_FAILED;
|
||||
|
||||
if (sfs.f_type != BTRFS_SUPER_MAGIC) {
|
||||
errno = EINVAL;
|
||||
return BTRFS_UTIL_ERROR_NOT_BTRFS;
|
||||
}
|
||||
|
||||
ret = fstat(fd, &st);
|
||||
if (ret == -1)
|
||||
return BTRFS_UTIL_ERROR_STAT_FAILED;
|
||||
|
||||
if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) {
|
||||
errno = EINVAL;
|
||||
return BTRFS_UTIL_ERROR_NOT_SUBVOLUME;
|
||||
}
|
||||
|
||||
return BTRFS_UTIL_OK;
|
||||
}
|
||||
|
||||
PUBLIC enum btrfs_util_error btrfs_util_subvolume_id(const char *path,
|
||||
uint64_t *id_ret)
|
||||
{
|
||||
enum btrfs_util_error err;
|
||||
int fd;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return BTRFS_UTIL_ERROR_OPEN_FAILED;
|
||||
|
||||
err = btrfs_util_subvolume_id_fd(fd, id_ret);
|
||||
SAVE_ERRNO_AND_CLOSE(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd,
|
||||
uint64_t *id_ret)
|
||||
{
|
||||
struct btrfs_ioctl_ino_lookup_args args = {
|
||||
.treeid = 0,
|
||||
.objectid = BTRFS_FIRST_FREE_OBJECTID,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
|
||||
if (ret == -1) {
|
||||
close(fd);
|
||||
return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
|
||||
}
|
||||
|
||||
*id_ret = args.treeid;
|
||||
|
||||
return BTRFS_UTIL_OK;
|
||||
}
|
Loading…
Reference in New Issue