From patchwork Fri Jan 26 18:40:57 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 10186779 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 67178601D5 for ; Fri, 26 Jan 2018 18:42:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 58BD229EC8 for ; Fri, 26 Jan 2018 18:42:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4D8CF29ED0; Fri, 26 Jan 2018 18:42:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 872C129EC8 for ; Fri, 26 Jan 2018 18:42:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751905AbeAZSli (ORCPT ); Fri, 26 Jan 2018 13:41:38 -0500 Received: from mail-pf0-f196.google.com ([209.85.192.196]:37280 "EHLO mail-pf0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751678AbeAZSlc (ORCPT ); Fri, 26 Jan 2018 13:41:32 -0500 Received: by mail-pf0-f196.google.com with SMTP id p1so822647pfh.4 for ; Fri, 26 Jan 2018 10:41:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=osandov-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=M9c8v1mTXNjLeYUpwDfzOJFssSLZfc784oHBjHbshEU=; b=faef9xsdVmrj6iQak8SqvDgAEzRDpZq5oLEWcItVPekfcpJ4vADlu7pnYCk52LfA9y cBznVsEiwLZIn3aYFYL+QKUctVysy3ovmCYonoM1YnydMA3oJB3BvBSvsRvs0/pbOh1R nl3MXPtjz/aLelOp+dyQ4lXceGpOsyrtggHdMemRgBQaPEi+QdoDPr8HDc2/qCaBlKB3 KEWuz/mDZK370Nq06YdSLtsFmVyYsLJi3StFBnLmq9mcR4AQGw453cVij2R+fp5I9+oI /Ok7fc48RlqZ3FBApLaeASTB7mtSmldfrME/vzep5yOy6mv4obuZJOq/LnqvsDU/oGL0 ikCQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:in-reply-to:references; bh=M9c8v1mTXNjLeYUpwDfzOJFssSLZfc784oHBjHbshEU=; b=SSpRPj8bgCRXy8TNF3PDqCeAzw0c2tLlnI2dI/ymfGhD+pt1+5QY3biLTm0E6Rb+td OCAoXJdgpQP4LsGrNIcpz1Pp/8dwrk98bVxE6aiRTOOoN2hOy9qy9wmvpwT5gmao0JH/ pPT7eZ8Rycc/ZwhP9rJq7kGME2CmDcmne8RyaS4UCnjvM9vqP4JX4Yja/Sy/CDy/XwDZ zaIP/4kJZt9uJHYJx/T0Y04FG6GV/ZnJObZ7GB3t29mh25dH2XWTNsRsjqQz9QB2t5Dp WNSr+0o6mBks8XhKoGgA+E0xKZ4yS1j9vIUhJ1VpmristFAvPdgsP8gLAzPguksQdk65 jB1Q== X-Gm-Message-State: AKwxytcOP7+7bJJNePqKWyiKoxYsAPoLdQrwCe39Fa1Peej3TbcQHmgu uvHhkxH6a3jJx2qALXd9pk7FXCAAroA= X-Google-Smtp-Source: AH8x224Qa1uiXjAyEjaeL4thYfKEJv/f7NLolAmmHS2pk05er/CoUF9AK54r35iAiscZrvK5+UXxvA== X-Received: by 10.98.158.89 with SMTP id s86mr20105997pfd.203.1516992091301; Fri, 26 Jan 2018 10:41:31 -0800 (PST) Received: from vader.thefacebook.com ([2620:10d:c090:200::6:7f96]) by smtp.gmail.com with ESMTPSA id y29sm19627400pff.24.2018.01.26.10.41.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 26 Jan 2018 10:41:30 -0800 (PST) From: Omar Sandoval To: linux-btrfs@vger.kernel.org Cc: kernel-team@fb.com Subject: [PATCH 09/26] libbtrfsutil: add btrfs_util_[gs]et_default_subvolume() Date: Fri, 26 Jan 2018 10:40:57 -0800 Message-Id: <6d5d16c998ecbbd72ad0a350cef0b9d69776df10.1516991902.git.osandov@fb.com> X-Mailer: git-send-email 2.16.1 In-Reply-To: References: In-Reply-To: References: Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Omar Sandoval set_default_subvolume() is a trivial ioctl(), but there's no ioctl() for get_default_subvolume(), so we need to search the root tree. Signed-off-by: Omar Sandoval --- libbtrfsutil/btrfsutil.h | 37 +++++++++ libbtrfsutil/python/btrfsutilpy.h | 2 + libbtrfsutil/python/module.c | 14 ++++ libbtrfsutil/python/subvolume.c | 50 ++++++++++++ libbtrfsutil/python/tests/test_subvolume.py | 14 ++++ libbtrfsutil/subvolume.c | 116 ++++++++++++++++++++++++++++ 6 files changed, 233 insertions(+) diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h index d187d3a7..1643ea7b 100644 --- a/libbtrfsutil/btrfsutil.h +++ b/libbtrfsutil/btrfsutil.h @@ -260,6 +260,43 @@ enum btrfs_util_error btrfs_util_set_subvolume_read_only(const char *path, enum btrfs_util_error btrfs_util_f_set_subvolume_read_only(int fd, bool read_only); +/** + * btrfs_util_get_default_subvolume() - Get the default subvolume for a + * filesystem. + * @path: Path on a Btrfs filesystem. + * @id_ret: Returned subvolume ID. + * + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure. + */ +enum btrfs_util_error btrfs_util_get_default_subvolume(const char *path, + uint64_t *id_ret); + +/** + * btrfs_util_f_get_default_subvolume() - See + * btrfs_util_get_default_subvolume(). + */ +enum btrfs_util_error btrfs_util_f_get_default_subvolume(int fd, + uint64_t *id_ret); + +/** + * btrfs_util_set_default_subvolume() - Set the default subvolume for a + * filesystem. + * @path: Path in a Btrfs filesystem. This may be any path in the filesystem; it + * does not have to refer to a subvolume unless @id is zero. + * @id: ID of subvolume to set as the default. If zero is given, the subvolume + * ID of @path is used. + * + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure. + */ +enum btrfs_util_error btrfs_util_set_default_subvolume(const char *path, + uint64_t id); + +/** + * btrfs_util_f_set_default_subvolume() - See + * btrfs_util_set_default_subvolume(). + */ +enum btrfs_util_error btrfs_util_f_set_default_subvolume(int fd, uint64_t id); + struct btrfs_util_qgroup_inherit; /** diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h index 21253e51..41314d4a 100644 --- a/libbtrfsutil/python/btrfsutilpy.h +++ b/libbtrfsutil/python/btrfsutilpy.h @@ -66,6 +66,8 @@ PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds); PyObject *subvolume_info(PyObject *self, PyObject *args, PyObject *kwds); PyObject *get_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds); PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds); +PyObject *get_default_subvolume(PyObject *self, PyObject *args, PyObject *kwds); +PyObject *set_default_subvolume(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 3395fb14..0ac4d63a 100644 --- a/libbtrfsutil/python/module.c +++ b/libbtrfsutil/python/module.c @@ -173,6 +173,20 @@ static PyMethodDef btrfsutil_methods[] = { "Arguments:\n" "path -- string, bytes, path-like object, or open file descriptor\n" "read_only -- bool flag value"}, + {"get_default_subvolume", (PyCFunction)get_default_subvolume, + METH_VARARGS | METH_KEYWORDS, + "get_default_subvolume(path) -> int\n\n" + "Get the ID of the default subvolume of a filesystem.\n\n" + "Arguments:\n" + "path -- string, bytes, path-like object, or open file descriptor"}, + {"set_default_subvolume", (PyCFunction)set_default_subvolume, + METH_VARARGS | METH_KEYWORDS, + "set_default_subvolume(path, id=0)\n\n" + "Set the default subvolume of a filesystem.\n\n" + "Arguments:\n" + "path -- string, bytes, path-like object, or open file descriptor\n" + "id -- if not zero, set the default subvolume to the subvolume with\n" + "this ID instead of the given path"}, {"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 9ae7729f..7b30e03c 100644 --- a/libbtrfsutil/python/subvolume.c +++ b/libbtrfsutil/python/subvolume.c @@ -268,6 +268,56 @@ PyObject *set_subvolume_read_only(PyObject *self, PyObject *args, PyObject *kwds Py_RETURN_NONE; } +PyObject *get_default_subvolume(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&:get_default_subvolume", + keywords, &path_converter, &path)) + return NULL; + + if (path.path) + err = btrfs_util_get_default_subvolume(path.path, &id); + else + err = btrfs_util_f_get_default_subvolume(path.fd, &id); + if (err) { + SetFromBtrfsUtilErrorWithPath(err, &path); + path_cleanup(&path); + return NULL; + } + + path_cleanup(&path); + return PyLong_FromUnsignedLongLong(id); +} + +PyObject *set_default_subvolume(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; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:set_default_subvolume", + keywords, &path_converter, &path, &id)) + return NULL; + + if (path.path) + err = btrfs_util_set_default_subvolume(path.path, id); + else + err = btrfs_util_f_set_default_subvolume(path.fd, id); + if (err) { + SetFromBtrfsUtilErrorWithPath(err, &path); + path_cleanup(&path); + return NULL; + } + + path_cleanup(&path); + Py_RETURN_NONE; +} + 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 23871de9..937a4397 100644 --- a/libbtrfsutil/python/tests/test_subvolume.py +++ b/libbtrfsutil/python/tests/test_subvolume.py @@ -154,6 +154,20 @@ class TestSubvolume(BtrfsTestCase): btrfsutil.set_subvolume_read_only(arg, False) + def test_default_subvolume(self): + for arg in self.path_or_fd(self.mountpoint): + with self.subTest(type=type(arg)): + self.assertEqual(btrfsutil.get_default_subvolume(arg), 5) + + subvol = os.path.join(self.mountpoint, 'subvol') + btrfsutil.create_subvolume(subvol) + for arg in self.path_or_fd(subvol): + with self.subTest(type=type(arg)): + btrfsutil.set_default_subvolume(arg) + self.assertEqual(btrfsutil.get_default_subvolume(arg), 256) + btrfsutil.set_default_subvolume(arg, 5) + self.assertEqual(btrfsutil.get_default_subvolume(arg), 5) + def test_create_subvolume(self): subvol = os.path.join(self.mountpoint, 'subvol') diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c index 394c7cbd..eb57b4c4 100644 --- a/libbtrfsutil/subvolume.c +++ b/libbtrfsutil/subvolume.c @@ -483,6 +483,122 @@ enum btrfs_util_error btrfs_util_f_set_subvolume_read_only(int fd, return BTRFS_UTIL_OK; } +__attribute__((visibility("default"))) +enum btrfs_util_error btrfs_util_get_default_subvolume(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_f_get_default_subvolume(fd, id_ret); + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} + +__attribute__((visibility("default"))) +enum btrfs_util_error btrfs_util_f_get_default_subvolume(int fd, + uint64_t *id_ret) +{ + struct btrfs_ioctl_search_args search = { + .key = { + .tree_id = BTRFS_ROOT_TREE_OBJECTID, + .min_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID, + .max_objectid = BTRFS_ROOT_TREE_DIR_OBJECTID, + .min_type = BTRFS_DIR_ITEM_KEY, + .max_type = BTRFS_DIR_ITEM_KEY, + .min_offset = 0, + .max_offset = UINT64_MAX, + .min_transid = 0, + .max_transid = UINT64_MAX, + .nr_items = 0, + }, + }; + size_t items_pos = 0, buf_off = 0; + int ret; + + for (;;) { + const struct btrfs_ioctl_search_header *header; + + if (items_pos >= search.key.nr_items) { + search.key.nr_items = 4096; + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search); + if (ret == -1) + return BTRFS_UTIL_ERROR_SEARCH_FAILED; + items_pos = 0; + buf_off = 0; + + if (search.key.nr_items == 0) { + errno = ENOENT; + return BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND; + } + } + + header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off); + if (header->type == BTRFS_DIR_ITEM_KEY) { + const struct btrfs_dir_item *dir; + const char *name; + uint16_t name_len; + + dir = (struct btrfs_dir_item *)(header + 1); + name = (const char *)(dir + 1); + name_len = le16_to_cpu(dir->name_len); + if (strncmp(name, "default", name_len) == 0) { + *id_ret = le64_to_cpu(dir->location.objectid); + break; + } + } + + items_pos++; + buf_off += sizeof(*header) + header->len; + search.key.min_offset = header->offset + 1; + } + + return BTRFS_UTIL_OK; +} + +__attribute__((visibility("default"))) +enum btrfs_util_error btrfs_util_set_default_subvolume(const char *path, + uint64_t id) +{ + enum btrfs_util_error err; + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) + return BTRFS_UTIL_ERROR_OPEN_FAILED; + + err = btrfs_util_f_set_default_subvolume(fd, id); + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} + +__attribute__((visibility("default"))) +enum btrfs_util_error btrfs_util_f_set_default_subvolume(int fd, uint64_t id) +{ + enum btrfs_util_error err; + int ret; + + if (id == 0) { + err = btrfs_util_f_is_subvolume(fd); + if (err) + return err; + + err = btrfs_util_f_subvolume_id(fd, &id); + if (err) + return err; + } + + ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &id); + if (ret == -1) + return BTRFS_UTIL_ERROR_DEFAULT_SUBVOL_FAILED; + + return BTRFS_UTIL_OK; +} + static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path, char *name, size_t name_len, int *fd)