[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 07/18] fuse: Implement hole detection through lseek
From: |
Max Reitz |
Subject: |
[PATCH 07/18] fuse: Implement hole detection through lseek |
Date: |
Thu, 19 Dec 2019 15:38:07 +0100 |
This is a relatively new feature in libfuse (available since 3.8.0,
which was released in November 2019), so we have to let configure check
whether it is available before making use of it.
Signed-off-by: Max Reitz <address@hidden>
---
block/fuse.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 33 ++++++++++++++++++++++
2 files changed, 110 insertions(+)
diff --git a/block/fuse.c b/block/fuse.c
index 018afee6cd..6b693b05b7 100644
--- a/block/fuse.c
+++ b/block/fuse.c
@@ -579,6 +579,80 @@ static void fuse_flush(fuse_req_t req, fuse_ino_t inode,
fuse_reply_err(req, ret < 0 ? -ret : 0);
}
+#ifdef CONFIG_FUSE_LSEEK
+/**
+ * Let clients inquire allocation status.
+ */
+static void fuse_lseek(fuse_req_t req, fuse_ino_t inode, off_t offset,
+ int whence, struct fuse_file_info *fi)
+{
+ BdrvFuseSession *session = fuse_req_userdata(req);
+
+ if (whence != SEEK_HOLE && whence != SEEK_DATA) {
+ fuse_reply_err(req, EINVAL);
+ return;
+ }
+
+ while (true) {
+ int64_t pnum;
+ int ret;
+
+ ret = bdrv_block_status_above(blk_bs(session->blk), NULL,
+ offset, INT64_MAX, &pnum, NULL, NULL);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ return;
+ }
+
+ if (!pnum && (ret & BDRV_BLOCK_EOF)) {
+ int64_t blk_len;
+
+ /*
+ * If blk_getlength() rounds (e.g. by sectors), then the
+ * export length will be rounded, too. However,
+ * bdrv_block_status_above() may return EOF at unaligned
+ * offsets. We must not let this become visible and thus
+ * always simulate a hole between @offset (the real EOF)
+ * and @blk_len (the client-visible EOF).
+ */
+
+ blk_len = blk_getlength(session->blk);
+ if (blk_len < 0) {
+ fuse_reply_err(req, -blk_len);
+ return;
+ }
+
+ if (offset > blk_len || whence == SEEK_DATA) {
+ fuse_reply_err(req, ENXIO);
+ } else {
+ fuse_reply_lseek(req, offset);
+ }
+ return;
+ }
+
+ if (ret & BDRV_BLOCK_DATA) {
+ if (whence == SEEK_DATA) {
+ fuse_reply_lseek(req, offset);
+ return;
+ }
+ } else {
+ if (whence == SEEK_HOLE) {
+ fuse_reply_lseek(req, offset);
+ return;
+ }
+ }
+
+ /* Safety check against infinite loops */
+ if (!pnum) {
+ fuse_reply_err(req, ENXIO);
+ return;
+ }
+
+ offset += pnum;
+ }
+}
+#endif
+
static const struct fuse_lowlevel_ops fuse_ops = {
.lookup = fuse_lookup,
.getattr = fuse_getattr,
@@ -588,4 +662,7 @@ static const struct fuse_lowlevel_ops fuse_ops = {
.write = fuse_write,
.fallocate = fuse_fallocate,
.flush = fuse_flush,
+#ifdef CONFIG_FUSE_LSEEK
+ .lseek = fuse_lseek,
+#endif
};
diff --git a/configure b/configure
index ff7d760a0a..18c38f111b 100755
--- a/configure
+++ b/configure
@@ -6062,11 +6062,39 @@ EOF
fuse_libs=$(pkg-config --libs fuse3)
if compile_prog "$fuse_cflags" "$fuse_libs"; then
fuse=yes
+
+ cat > $TMPC <<EOF
+#define FUSE_USE_VERSION 31
+#include <fuse.h>
+#include <fuse_lowlevel.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+static void fuse_lseek(fuse_req_t req, fuse_ino_t inode, off_t offset,
+ int whence, struct fuse_file_info *fi)
+{
+ if (whence == SEEK_DATA || whence == SEEK_HOLE) {
+ fuse_reply_lseek(req, offset);
+ } else {
+ fuse_reply_err(req, EINVAL);
+ }
+}
+const struct fuse_lowlevel_ops fuse_ops = {
+ .lseek = fuse_lseek,
+};
+int main(void) { return 0; }
+EOF
+ if compile_prog "$fuse_cflags" "$fuse_libs"; then
+ fuse_lseek=yes
+ else
+ fuse_lseek=no
+ fi
else
if test "$fuse" = "yes"; then
feature_not_found "fuse"
fi
fuse=no
+ fuse_lseek=no
fi
fi
@@ -6585,6 +6613,7 @@ echo "libudev $libudev"
echo "default devices $default_devices"
echo "plugin support $plugins"
echo "fuse exports $fuse"
+echo "fuse lseek $fuse_lseek"
if test "$supported_cpu" = "no"; then
echo
@@ -7443,6 +7472,10 @@ if test "$fuse" = "yes"; then
echo "CONFIG_FUSE=y" >> $config_host_mak
echo "FUSE_CFLAGS=$fuse_cflags" >> $config_host_mak
echo "FUSE_LIBS=$fuse_libs" >> $config_host_mak
+
+ if test "$fuse_lseek" = "yes"; then
+ echo "CONFIG_FUSE_LSEEK=y" >> $config_host_mak
+ fi
fi
if test "$tcg_interpreter" = "yes"; then
--
2.23.0
Re: [PATCH 02/18] fuse: Allow exporting BDSs via FUSE, Eric Blake, 2019/12/20
[PATCH 03/18] fuse: Implement standard FUSE operations, Max Reitz, 2019/12/19
[PATCH 04/18] fuse: Add fuse-export-remove, Max Reitz, 2019/12/19
[PATCH 05/18] fuse: Allow growable exports, Max Reitz, 2019/12/19
[PATCH 06/18] fuse: (Partially) implement fallocate(), Max Reitz, 2019/12/19
[PATCH 07/18] fuse: Implement hole detection through lseek,
Max Reitz <=
[PATCH 08/18] iotests: Do not needlessly filter _make_test_img, Max Reitz, 2019/12/19
[PATCH 09/18] iotests: Do not pipe _make_test_img, Max Reitz, 2019/12/19
[PATCH 10/18] iotests: Use convert -n in some cases, Max Reitz, 2019/12/19
[PATCH 11/18] iotests: Avoid renaming images, Max Reitz, 2019/12/19
[PATCH 12/18] iotests: Derive image names from $TEST_IMG, Max Reitz, 2019/12/19
[PATCH 13/18] iotests/091: Use _cleanup_qemu instad of "wait", Max Reitz, 2019/12/19
[PATCH 14/18] iotests: Restrict some Python tests to file, Max Reitz, 2019/12/19
[PATCH 15/18] iotests: Let _make_test_img guess $TEST_IMG_FILE, Max Reitz, 2019/12/19
[PATCH 16/18] iotests: Allow testing FUSE exports, Max Reitz, 2019/12/19
[PATCH 17/18] iotests: Enable fuse for many tests, Max Reitz, 2019/12/19