aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuri Per <yuri@acronis.com>2019-11-03 11:44:31 +0200
committerNikolaus Rath <Nikolaus@rath.org>2019-11-03 09:44:31 +0000
commitd735af94fa54a5555ce725f1d4e6b97b812b6603 (patch)
treecb21ee1eceb66e1c85a5c78ae7aa4a5f9438274a
parentb9c584370aa489ac00b1e8a0454c61f30c0531af (diff)
downloadlibfuse-d735af94fa54a5555ce725f1d4e6b97b812b6603.tar.gz
Implement lseek operation (#457)
-rw-r--r--ChangeLog.rst6
-rw-r--r--example/passthrough.c23
-rw-r--r--example/passthrough_fh.c13
-rw-r--r--example/passthrough_ll.c14
-rw-r--r--include/fuse.h7
-rw-r--r--include/fuse_lowlevel.h33
-rwxr-xr-xlib/fuse.c43
-rw-r--r--lib/fuse_lowlevel.c25
-rw-r--r--lib/fuse_versionscript2
-rw-r--r--lib/modules/iconv.c14
-rw-r--r--lib/modules/subdir.c14
11 files changed, 194 insertions, 0 deletions
diff --git a/ChangeLog.rst b/ChangeLog.rst
index 4d568e1..a37b63f 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -1,3 +1,9 @@
+Unreleased Changes
+==================
+
+* Added support for FUSE_LSEEK operation which can be used to report holes
+ in sparse files.
+
libfuse 3.7.0 (2019-09-27)
==========================
diff --git a/example/passthrough.c b/example/passthrough.c
index 6de9fc1..012bd31 100644
--- a/example/passthrough.c
+++ b/example/passthrough.c
@@ -484,6 +484,28 @@ static ssize_t xmp_copy_file_range(const char *path_in,
}
#endif
+static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
+{
+ int fd;
+ off_t res;
+
+ if (fi == NULL)
+ fd = open(path, O_RDONLY);
+ else
+ fd = fi->fh;
+
+ if (fd == -1)
+ return -errno;
+
+ res = lseek(fd, off, whence);
+ if (res == -1)
+ res = -errno;
+
+ if (fi == NULL)
+ close(fd);
+ return res;
+}
+
static struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
@@ -522,6 +544,7 @@ static struct fuse_operations xmp_oper = {
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
+ .lseek = xmp_lseek,
};
int main(int argc, char *argv[])
diff --git a/example/passthrough_fh.c b/example/passthrough_fh.c
index 3fc80f8..13eb41e 100644
--- a/example/passthrough_fh.c
+++ b/example/passthrough_fh.c
@@ -596,6 +596,18 @@ static ssize_t xmp_copy_file_range(const char *path_in,
}
#endif
+static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
+{
+ off_t res;
+ (void) path;
+
+ res = lseek(fi->fh, off, whence);
+ if (res == -1)
+ return -errno;
+
+ return res;
+}
+
static struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
@@ -643,6 +655,7 @@ static struct fuse_operations xmp_oper = {
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = xmp_copy_file_range,
#endif
+ .lseek = xmp_lseek,
};
int main(int argc, char *argv[])
diff --git a/example/passthrough_ll.c b/example/passthrough_ll.c
index 0f1fda5..5372d02 100644
--- a/example/passthrough_ll.c
+++ b/example/passthrough_ll.c
@@ -1161,6 +1161,19 @@ static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in,
}
#endif
+static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
+ struct fuse_file_info *fi)
+{
+ off_t res;
+
+ (void)ino;
+ res = lseek(fi->fh, off, whence);
+ if (res != -1)
+ fuse_reply_lseek(req, res);
+ else
+ fuse_reply_err(req, errno);
+}
+
static struct fuse_lowlevel_ops lo_oper = {
.init = lo_init,
.lookup = lo_lookup,
@@ -1198,6 +1211,7 @@ static struct fuse_lowlevel_ops lo_oper = {
#ifdef HAVE_COPY_FILE_RANGE
.copy_file_range = lo_copy_file_range,
#endif
+ .lseek = lo_lseek,
};
int main(int argc, char *argv[])
diff --git a/include/fuse.h b/include/fuse.h
index 2d2291c..883f6e5 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -776,6 +776,11 @@ struct fuse_operations {
off_t offset_in, const char *path_out,
struct fuse_file_info *fi_out,
off_t offset_out, size_t size, int flags);
+
+ /**
+ * Find next data or hole after the specified offset
+ */
+ off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *);
};
/** Extra context that may be needed by some filesystems
@@ -1197,6 +1202,8 @@ ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
const char *path_out,
struct fuse_file_info *fi_out, off_t off_out,
size_t len, int flags);
+off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
+ struct fuse_file_info *fi);
void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn,
struct fuse_config *cfg);
void fuse_fs_destroy(struct fuse_fs *fs);
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index 2073460..18c6363 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1218,6 +1218,27 @@ struct fuse_lowlevel_ops {
fuse_ino_t ino_out, off_t off_out,
struct fuse_file_info *fi_out, size_t len,
int flags);
+
+ /**
+ * Find next data or hole after the specified offset
+ *
+ * If this request is answered with an error code of ENOSYS, this is
+ * treated as a permanent failure, i.e. all future lseek() requests will
+ * fail with the same error code without being send to the filesystem
+ * process.
+ *
+ * Valid replies:
+ * fuse_reply_lseek
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param off offset to start search from
+ * @param whence either SEEK_DATA or SEEK_HOLE
+ * @param fi file information
+ */
+ void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
+ struct fuse_file_info *fi);
};
/**
@@ -1569,6 +1590,18 @@ int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
*/
int fuse_reply_poll(fuse_req_t req, unsigned revents);
+/**
+ * Reply with offset
+ *
+ * Possible requests:
+ * lseek
+ *
+ * @param req request handle
+ * @param off offset of next data or hole
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_lseek(fuse_req_t req, off_t off);
+
/* ----------------------------------------------------------- *
* Notification *
* ----------------------------------------------------------- */
diff --git a/lib/fuse.c b/lib/fuse.c
index 229e139..b0f5b30 100755
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -2384,6 +2384,23 @@ ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in,
return -ENOSYS;
}
+off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence,
+ struct fuse_file_info *fi)
+{
+ fuse_get_context()->private_data = fs->user_data;
+ if (fs->op.lseek) {
+ if (fs->debug) {
+ char buf[10];
+ fuse_log(FUSE_LOG_DEBUG, "lseek[%s] %llu %d\n",
+ file_info_string(fi, buf, sizeof(buf)),
+ (unsigned long long) off, whence);
+ }
+ return fs->op.lseek(path, off, whence, fi);
+ } else {
+ return -ENOSYS;
+ }
+}
+
static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
{
struct node *node;
@@ -4354,6 +4371,31 @@ static void fuse_lib_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in,
free_path(f, nodeid_out, path_out);
}
+static void fuse_lib_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
+ struct fuse_file_info *fi)
+{
+ struct fuse *f = req_fuse_prepare(req);
+ struct fuse_intr_data d;
+ char *path;
+ int err;
+ off_t res;
+
+ err = get_path(f, ino, &path);
+ if (err) {
+ reply_err(req, err);
+ return;
+ }
+
+ fuse_prepare_interrupt(f, req, &d);
+ res = fuse_fs_lseek(f->fs, path, off, whence, fi);
+ fuse_finish_interrupt(f, req, &d);
+ free_path(f, ino, path);
+ if (res >= 0)
+ fuse_reply_lseek(req, res);
+ else
+ reply_err(req, res);
+}
+
static int clean_delay(struct fuse *f)
{
/*
@@ -4451,6 +4493,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = {
.poll = fuse_lib_poll,
.fallocate = fuse_lib_fallocate,
.copy_file_range = fuse_lib_copy_file_range,
+ .lseek = fuse_lib_lseek,
};
int fuse_notify_poll(struct fuse_pollhandle *ph)
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index f7fbc8f..f2d7038 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -1065,6 +1065,16 @@ int fuse_reply_poll(fuse_req_t req, unsigned revents)
return send_reply_ok(req, &arg, sizeof(arg));
}
+int fuse_reply_lseek(fuse_req_t req, off_t off)
+{
+ struct fuse_lseek_out arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.offset = off;
+
+ return send_reply_ok(req, &arg, sizeof(arg));
+}
+
static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
char *name = (char *) inarg;
@@ -1867,6 +1877,20 @@ static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void
fuse_reply_err(req, ENOSYS);
}
+static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+ struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg;
+ struct fuse_file_info fi;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
+
+ if (req->se->op.lseek)
+ req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi);
+ else
+ fuse_reply_err(req, ENOSYS);
+}
+
static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
@@ -2471,6 +2495,7 @@ static struct {
[FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
[FUSE_RENAME2] = { do_rename2, "RENAME2" },
[FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
+ [FUSE_LSEEK] = { do_lseek, "LSEEK" },
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
};
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 11ef1f7..160f11d 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -127,6 +127,8 @@ FUSE_3.0 {
fuse_cmdline_help;
fuse_apply_conn_info_opts;
fuse_parse_conn_info_opts;
+ fuse_fs_lseek;
+ fuse_reply_lseek;
local:
*;
diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c
index 431d02c..eb5edd8 100644
--- a/lib/modules/iconv.c
+++ b/lib/modules/iconv.c
@@ -554,6 +554,19 @@ static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
return err;
}
+static off_t iconv_lseek(const char *path, off_t off, int whence,
+ struct fuse_file_info *fi)
+{
+ struct iconv *ic = iconv_get();
+ char *newpath;
+ int res = iconv_convpath(ic, path, &newpath, 0);
+ if (!res) {
+ res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
+ free(newpath);
+ }
+ return res;
+}
+
static void *iconv_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
@@ -612,6 +625,7 @@ static const struct fuse_operations iconv_oper = {
.lock = iconv_lock,
.flock = iconv_flock,
.bmap = iconv_bmap,
+ .lseek = iconv_lseek,
};
static const struct fuse_opt iconv_opts[] = {
diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c
index 23f58f8..616c0ee 100644
--- a/lib/modules/subdir.c
+++ b/lib/modules/subdir.c
@@ -540,6 +540,19 @@ static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
return err;
}
+static off_t subdir_lseek(const char *path, off_t off, int whence,
+ struct fuse_file_info *fi)
+{
+ struct subdir *ic = subdir_get();
+ char *newpath;
+ int res = subdir_addpath(ic, path, &newpath);
+ if (!res) {
+ res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
+ free(newpath);
+ }
+ return res;
+}
+
static void *subdir_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
@@ -594,6 +607,7 @@ static const struct fuse_operations subdir_oper = {
.lock = subdir_lock,
.flock = subdir_flock,
.bmap = subdir_bmap,
+ .lseek = subdir_lseek,
};
static const struct fuse_opt subdir_opts[] = {