From patchwork Fri Jan 26 18:41:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Omar Sandoval X-Patchwork-Id: 10186783 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 6D5D9601D5 for ; Fri, 26 Jan 2018 18:42:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5E18629EC8 for ; Fri, 26 Jan 2018 18:42:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 52CD829ED0; Fri, 26 Jan 2018 18:42:55 +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 1E9AC29EC8 for ; Fri, 26 Jan 2018 18:42:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752402AbeAZSmw (ORCPT ); Fri, 26 Jan 2018 13:42:52 -0500 Received: from mail-pf0-f195.google.com ([209.85.192.195]:33626 "EHLO mail-pf0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751632AbeAZSlh (ORCPT ); Fri, 26 Jan 2018 13:41:37 -0500 Received: by mail-pf0-f195.google.com with SMTP id t5so829758pfi.0 for ; Fri, 26 Jan 2018 10:41:36 -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=OK9Jmur5EjgjNgGzqEfTJMHN3BgVLkMLQaAHEZ6iNU4=; b=USG+sSgc35jT9u7QbtHHlxaWbG0PDfBdsy+UFLL7HDvLmNCcp5pzR5ApkDU6vDRxir gsbgsvB7L/g9vFJIJ94ZNC5+6Pp+XJgpD2Y/wfmlp7NrxvuX84e4DxGRWO8k20E29fLD AXc/ct0BwCXaMxKOYZm5gYUynSFNag7jZm1wlDQAZVi0YkrUfE1B9xfCtznZFeFbNfXw Tf+wU6ZqyovCGzhvnjAcNrkqq54lqNI7u1bfOJzKN5lmSPbJWY2goHvh3l10OtDkDdrD Mr/2WmDJxcdJ4cS0QsdoW4wNDZ8eWaX5e05sQv5VmedCdBjq4LQdqbBR/71krt3mH5Oy C9Dg== 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=OK9Jmur5EjgjNgGzqEfTJMHN3BgVLkMLQaAHEZ6iNU4=; b=eEW5sWXh2ot/7tR0lqKr4pIERbDHixeT/HnzKzytISddhtWp3FiMhNWVo3capDqGQB yFY66XgT2tXmMFlqrU/UjtydbqJgmt7OhBqUrwFZT8lmDcBgxmNnyiljLHeAm0MLT97B eZZhduUO5pWOegvpHQtqPt6vzVsYQUZ1fKUeL0aLVZ6jHY3eAg3SIdt4WR6w1YmkI1Gc rK30P5yCnLX9quFMZ1Xm9mXAm0nKm9nya5RjXX5p1ibfgofazz9ScdDTtcsr2ygGV4z3 H+XyOOcQ15NviHftIfs3aK+ETvFPkjaqEHcN4+p2DhJbj9/jdIft1aa44Vd+n5RVUnLx WgnQ== X-Gm-Message-State: AKwxytecifUziMRUvEOYajm+9ZLlHnrGboYupt2mZRRJ4/5SkmVCkEiK IKIn2bk/RCP3EgHZJe1UhdAJL5suVk8= X-Google-Smtp-Source: AH8x2263QHP6WJdEIPgJ8qy8MX95A3ViUTmX9J5Pu6vXB6BSIiEBFTreUZDv6AV7Xh25Jy2kniAz9g== X-Received: by 2002:a17:902:598e:: with SMTP id p14-v6mr14357011pli.289.1516992095787; Fri, 26 Jan 2018 10:41:35 -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.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 26 Jan 2018 10:41:35 -0800 (PST) From: Omar Sandoval To: linux-btrfs@vger.kernel.org Cc: kernel-team@fb.com Subject: [PATCH 14/26] libbtrfsutil: add filesystem sync helpers Date: Fri, 26 Jan 2018 10:41:02 -0800 Message-Id: <9adeb2d88afcc9d42ac584bd7a6b89053ee702bd.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 Namely, sync, start_sync, and wait_sync. Signed-off-by: Omar Sandoval --- Makefile | 4 +- libbtrfsutil/btrfsutil.h | 44 ++++++++++++ libbtrfsutil/filesystem.c | 103 +++++++++++++++++++++++++++ libbtrfsutil/internal.h | 7 ++ libbtrfsutil/python/btrfsutilpy.h | 3 + libbtrfsutil/python/filesystem.c | 94 ++++++++++++++++++++++++ libbtrfsutil/python/module.c | 21 ++++++ libbtrfsutil/python/setup.py | 1 + libbtrfsutil/python/tests/test_filesystem.py | 73 +++++++++++++++++++ libbtrfsutil/subvolume.c | 7 -- 10 files changed, 348 insertions(+), 9 deletions(-) create mode 100644 libbtrfsutil/filesystem.c create mode 100644 libbtrfsutil/python/filesystem.c create mode 100644 libbtrfsutil/python/tests/test_filesystem.py diff --git a/Makefile b/Makefile index 2f798f11..8bbe1c5e 100644 --- a/Makefile +++ b/Makefile @@ -123,8 +123,8 @@ libbtrfs_headers = send-stream.h send-utils.h send.h kernel-lib/rbtree.h btrfs-l kernel-lib/radix-tree.h kernel-lib/sizes.h kernel-lib/raid56.h \ extent-cache.h extent_io.h ioctl.h ctree.h btrfsck.h version.h libbtrfsutil_version := 0.1 -libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/qgroup.o \ - libbtrfsutil/subvolume.o +libbtrfsutil_objects = libbtrfsutil/errors.o libbtrfsutil/filesystem.o \ + libbtrfsutil/qgroup.o libbtrfsutil/subvolume.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 diff --git a/libbtrfsutil/btrfsutil.h b/libbtrfsutil/btrfsutil.h index 74b05e29..d968f4de 100644 --- a/libbtrfsutil/btrfsutil.h +++ b/libbtrfsutil/btrfsutil.h @@ -70,6 +70,50 @@ enum btrfs_util_error { */ const char *btrfs_util_strerror(enum btrfs_util_error err); +/** + * btrfs_util_sync() - Force a sync on a specific Btrfs filesystem. + * @path: Path on a Btrfs filesystem. + * + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure. + */ +enum btrfs_util_error btrfs_util_sync(const char *path); + +/** + * btrfs_util_f_sync() - See btrfs_util_sync(). + */ +enum btrfs_util_error btrfs_util_f_sync(int fd); + +/** + * btrfs_util_start_sync() - Start a sync on a specific Btrfs filesystem but + * don't wait for it. + * @path: Path on a Btrfs filesystem. + * @transid: Returned transaction ID which can be waited on with + * btrfs_util_wait_sync(). This can be %NULL. + * + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure. + */ +enum btrfs_util_error btrfs_util_start_sync(const char *path, + uint64_t *transid); + +/** + * btrfs_util_f_start_sync() - See btrfs_util_start_sync(). + */ +enum btrfs_util_error btrfs_util_f_start_sync(int fd, uint64_t *transid); + +/** + * btrfs_util_wait_sync() - Wait for a transaction with a given ID to sync. + * @path: Path on a Btrfs filesystem. + * @transid: Transaction ID to wait for, or zero for the current transaction. + * + * Return: %BTRFS_UTIL_OK on success, non-zero error code on failure. + */ +enum btrfs_util_error btrfs_util_wait_sync(const char *path, uint64_t transid); + +/** + * btrfs_util_f_wait_sync() - See btrfs_util_wait_sync(). + */ +enum btrfs_util_error btrfs_util_f_wait_sync(int fd, uint64_t transid); + /** * btrfs_util_is_subvolume() - Return whether a given path is a Btrfs subvolume. * @path: Path to check. diff --git a/libbtrfsutil/filesystem.c b/libbtrfsutil/filesystem.c new file mode 100644 index 00000000..7199f920 --- /dev/null +++ b/libbtrfsutil/filesystem.c @@ -0,0 +1,103 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include + +#include "btrfsutil.h" +#include "internal.h" + +enum btrfs_util_error btrfs_util_sync(const char *path) +{ + 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_sync(fd); + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} + +enum btrfs_util_error btrfs_util_f_sync(int fd) +{ + int ret; + + ret = ioctl(fd, BTRFS_IOC_SYNC, NULL); + if (ret == -1) + return BTRFS_UTIL_ERROR_SYNC_FAILED; + + return BTRFS_UTIL_OK; +} + +enum btrfs_util_error btrfs_util_start_sync(const char *path, + uint64_t *transid) +{ + 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_start_sync(fd, transid); + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} + +enum btrfs_util_error btrfs_util_f_start_sync(int fd, uint64_t *transid) +{ + int ret; + + ret = ioctl(fd, BTRFS_IOC_START_SYNC, transid); + if (ret == -1) + return BTRFS_UTIL_ERROR_START_SYNC_FAILED; + + return BTRFS_UTIL_OK; +} + +enum btrfs_util_error btrfs_util_wait_sync(const char *path, uint64_t transid) +{ + 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_wait_sync(fd, transid); + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} + +enum btrfs_util_error btrfs_util_f_wait_sync(int fd, uint64_t transid) +{ + int ret; + + ret = ioctl(fd, BTRFS_IOC_WAIT_SYNC, &transid); + if (ret == -1) + return BTRFS_UTIL_ERROR_WAIT_SYNC_FAILED; + + return BTRFS_UTIL_OK; +} diff --git a/libbtrfsutil/internal.h b/libbtrfsutil/internal.h index bc91ad88..9bc00d0e 100644 --- a/libbtrfsutil/internal.h +++ b/libbtrfsutil/internal.h @@ -26,4 +26,11 @@ #define le32_to_cpu __le32_to_cpu #define le64_to_cpu __le64_to_cpu +#define SAVE_ERRNO_AND_CLOSE(fd) { \ + int saved_errno = errno; \ + \ + close(fd); \ + errno = saved_errno; \ +} + #endif /* BTRFS_UTIL_INTERNAL_H */ diff --git a/libbtrfsutil/python/btrfsutilpy.h b/libbtrfsutil/python/btrfsutilpy.h index be5122e2..af3a6edf 100644 --- a/libbtrfsutil/python/btrfsutilpy.h +++ b/libbtrfsutil/python/btrfsutilpy.h @@ -63,6 +63,9 @@ void SetFromBtrfsUtilErrorWithPaths(enum btrfs_util_error err, struct path_arg *path1, struct path_arg *path2); +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); PyObject *subvolume_path(PyObject *self, PyObject *args, PyObject *kwds); diff --git a/libbtrfsutil/python/filesystem.c b/libbtrfsutil/python/filesystem.c new file mode 100644 index 00000000..05cc49a3 --- /dev/null +++ b/libbtrfsutil/python/filesystem.c @@ -0,0 +1,94 @@ +/* + * 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 . + */ + +#include "btrfsutilpy.h" + +PyObject *filesystem_sync(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&:sync", keywords, + &path_converter, &path)) + return NULL; + + if (path.path) + err = btrfs_util_sync(path.path); + else + err = btrfs_util_f_sync(path.fd); + if (err) { + SetFromBtrfsUtilErrorWithPath(err, &path); + path_cleanup(&path); + return NULL; + } + + path_cleanup(&path); + Py_RETURN_NONE; +} + +PyObject *start_sync(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *keywords[] = {"path", NULL}; + struct path_arg path = {.allow_fd = true}; + uint64_t transid; + enum btrfs_util_error err; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:start_sync", keywords, + &path_converter, &path)) + return NULL; + + if (path.path) + err = btrfs_util_start_sync(path.path, &transid); + else + err = btrfs_util_f_start_sync(path.fd, &transid); + if (err) { + SetFromBtrfsUtilErrorWithPath(err, &path); + path_cleanup(&path); + return NULL; + } + + path_cleanup(&path); + return PyLong_FromUnsignedLongLong(transid); +} + +PyObject *wait_sync(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *keywords[] = {"path", "transid", NULL}; + struct path_arg path = {.allow_fd = true}; + unsigned long long transid = 0; + enum btrfs_util_error err; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|K:wait_sync", keywords, + &path_converter, &path, &transid)) + return NULL; + + if (path.path) + err = btrfs_util_wait_sync(path.path, transid); + else + err = btrfs_util_f_wait_sync(path.fd, transid); + if (err) { + SetFromBtrfsUtilErrorWithPath(err, &path); + path_cleanup(&path); + return NULL; + } + + path_cleanup(&path); + Py_RETURN_NONE; +} diff --git a/libbtrfsutil/python/module.c b/libbtrfsutil/python/module.c index eaa062ac..2dbdc7be 100644 --- a/libbtrfsutil/python/module.c +++ b/libbtrfsutil/python/module.c @@ -155,6 +155,27 @@ void path_cleanup(struct path_arg *path) } static PyMethodDef btrfsutil_methods[] = { + {"sync", (PyCFunction)filesystem_sync, + METH_VARARGS | METH_KEYWORDS, + "sync(path)\n\n" + "Sync a specific Btrfs filesystem.\n\n" + "Arguments:\n" + "path -- string, bytes, path-like object, or open file descriptor"}, + {"start_sync", (PyCFunction)start_sync, + METH_VARARGS | METH_KEYWORDS, + "start_sync(path) -> int\n\n" + "Start a sync on a specific Btrfs filesystem and return the\n" + "transaction ID.\n\n" + "Arguments:\n" + "path -- string, bytes, path-like object, or open file descriptor"}, + {"wait_sync", (PyCFunction)wait_sync, + METH_VARARGS | METH_KEYWORDS, + "wait_sync(path, transid=0)\n\n" + "Wait for a transaction to sync.\n" + "Arguments:\n" + "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" diff --git a/libbtrfsutil/python/setup.py b/libbtrfsutil/python/setup.py index a2a43f80..6ef3d515 100755 --- a/libbtrfsutil/python/setup.py +++ b/libbtrfsutil/python/setup.py @@ -78,6 +78,7 @@ module = Extension( sources=[ 'constants.c', 'error.c', + 'filesystem.c', 'module.c', 'qgroup.c', 'subvolume.c', diff --git a/libbtrfsutil/python/tests/test_filesystem.py b/libbtrfsutil/python/tests/test_filesystem.py new file mode 100644 index 00000000..006a6b1e --- /dev/null +++ b/libbtrfsutil/python/tests/test_filesystem.py @@ -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 . + +import os +import time + +import btrfsutil +from tests import BtrfsTestCase, HAVE_PATH_LIKE + + +def touch(path): + now = time.time() + os.utime(path, (now, now)) + + +class TestSubvolume(BtrfsTestCase): + def super_generation(self): + with open(self.image, 'rb') as f: + # csum is 32 bytes, fsid is 16 bytes, bytenr is 8 bytes, flags is 8 + # bytes + f.seek(65536 + 32 + 16 + 8 + 8) + self.assertEqual(f.read(8), b'_BHRfS_M') + return int.from_bytes(f.read(8), 'little') + + def test_sync(self): + old_generation = self.super_generation() + for arg in self.path_or_fd(self.mountpoint): + with self.subTest(type=type(arg)): + touch(arg) + btrfsutil.sync(arg) + new_generation = self.super_generation() + self.assertGreater(new_generation, old_generation) + old_generation = new_generation + + def test_start_sync(self): + old_generation = self.super_generation() + for arg in self.path_or_fd(self.mountpoint): + with self.subTest(type=type(arg)): + touch(arg) + transid = btrfsutil.start_sync(arg) + self.assertGreater(transid, old_generation) + + def test_wait_sync(self): + old_generation = self.super_generation() + for arg in self.path_or_fd(self.mountpoint): + with self.subTest(type=type(arg)): + touch(arg) + transid = btrfsutil.start_sync(arg) + btrfsutil.wait_sync(arg, transid) + new_generation = self.super_generation() + self.assertGreater(new_generation, old_generation) + old_generation = new_generation + + touch(arg) + btrfsutil.start_sync(arg) + btrfsutil.wait_sync(arg) + new_generation = self.super_generation() + self.assertGreater(new_generation, old_generation) + old_generation = new_generation diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c index 903bcfb6..8b9a888e 100644 --- a/libbtrfsutil/subvolume.c +++ b/libbtrfsutil/subvolume.c @@ -33,13 +33,6 @@ #include "btrfsutil.h" #include "internal.h" -#define SAVE_ERRNO_AND_CLOSE(fd) { \ - int saved_errno = errno; \ - \ - close(fd); \ - errno = saved_errno; \ -} - /* * This intentionally duplicates btrfs_util_f_is_subvolume() instead of opening * a file descriptor and calling it, because fstat() and fstatfs() don't accept