@@ -6226,11 +6226,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
@@ -7425,6 +7453,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
@@ -548,6 +548,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)
+{
+ FuseExport *exp = 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(exp->common.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(exp->common.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,
@@ -557,6 +631,9 @@ 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
};
const BlockExportDriver blk_exp_fuse = {
@@ -1537,6 +1537,7 @@ summary_info += {'thread sanitizer': config_host.has_key('CONFIG_TSAN')}
summary_info += {'rng-none': config_host.has_key('CONFIG_RNG_NONE')}
summary_info += {'Linux keyring': config_host.has_key('CONFIG_SECRET_KEYRING')}
summary_info += {'fuse exports': config_host.has_key('CONFIG_FUSE')}
+summary_info += {'fuse lseek': config_host.has_key('CONFIG_FUSE_LSEEK')}
summary(summary_info, bool_yn: true)
if not supported_cpus.contains(cpu)
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 <mreitz@redhat.com> --- configure | 32 +++++++++++++++++++ block/export/fuse.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ meson.build | 1 + 3 files changed, 110 insertions(+)