Merge tag 'fuse-3.8.0' into HEAD am: 898c253bc0
am: 54ee84ad31

Change-Id: I791e883487b0ffb5348c232a5bf83b3a51feed61
diff --git a/AUTHORS b/AUTHORS
index 7e97c8f..2bee369 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -30,6 +30,7 @@
 Antonio SJ Musumeci <trapexit@spawn.link>
 Arunav Sanyal <Khalian@users.noreply.github.com>
 Ashley Pittman <ashleypittman@users.noreply.github.com>
+AsumFace <asumface@gmail.com>
 Banglang <banglang.huang@foxmail.com>
 Baptiste Daroussin <bapt@FreeBSD.org>
 Benjamin Barenblat <bbaren@google.com>
@@ -52,6 +53,8 @@
 David Sheets <sheets@alum.mit.edu>
 divinity76 <divinity76@gmail.com>
 DrDaveD <2129743+DrDaveD@users.noreply.github.com>
+Dr. David Alan Gilbert <dgilbert@redhat.com>
+Emily Herbert <emilyherbert@cs.umass.edu>
 Emmanuel Dreyfus <manu@netbsd.org>
 Enke Chen <enkechen@yahoo.com>
 Eric Engestrom <eric@engestrom.ch>
@@ -60,6 +63,7 @@
 Fedor Korotkov <fedor.korotkov@gmail.com>
 Feng Shuo <steve.shuo.feng@gmail.com>
 Forty-Bot <Forty-Bot@users.noreply.github.com>
+Giuseppe Scrivano <giuseppe@scrivano.org>
 guraga <rulumasi@dodsi.com>
 HazelFZ <xfzfygz@gmail.com>
 Heiko Becker <heirecka@exherbo.org>
@@ -77,6 +81,7 @@
 Joseph Dodge <joseph.dodge@veritas.com>
 Josh Soref <jsoref@users.noreply.github.com>
 Kevin Vigor <kvigor@fb.com>
+Kirill Smelkov <kirr@nexedi.com>
 Laszlo Boszormenyi (GCS) <gcs@debian.org>
 Laszlo Papp <ext-laszlo.papp@nokia.com>
 Liu Bo <liub.liubo@gmail.com>
@@ -121,7 +126,10 @@
 Scott Worley <scottworley@scottworley.com>
 Sebastian Pipping <sebastian@pipping.org>
 SÅ‚awek Rudnicki <slawek.rudnicki@editshare.com>
+Stefan Hajnoczi <stefanha@gmail.com>
+Stefan Hajnoczi <stefanha@redhat.com>
 Tej Chajed <tchajed@mit.edu>
+tenzap <46226844+tenzap@users.noreply.github.com>
 therealneworld@gmail.com <therealneworld@gmail.com>
 Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
 userwithuid <userwithuid@gmail.com>
@@ -129,3 +137,4 @@
 Vivek Goyal <vgoyal@redhat.com>
 William Woodruff <william@yossarian.net>
 Winfried Koehler <w_scan@gmx-topmail.de>
+Yuri Per <yuri@acronis.com>
diff --git a/Android.bp b/Android.bp
index 9fb4a16..e07f2b8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,6 +35,7 @@
         "lib/buffer.c",
         "lib/cuse_lowlevel.c",
         "lib/fuse.c",
+        "lib/fuse_log.c",
         "lib/fuse_loop.c",
         "lib/fuse_loop_mt.c",
         "lib/fuse_lowlevel.c",
diff --git a/METADATA b/METADATA
index 9a45a95..78a08d4 100644
--- a/METADATA
+++ b/METADATA
@@ -12,7 +12,7 @@
     type: GIT
     value: "https://github.com/libfuse/libfuse.git"
   }
-  version: "fuse-3.6.2"
-  last_upgrade_date { year: 2019 month: 7 day: 19 }
+  version: "fuse-3.8.0"
+  last_upgrade_date { year: 2019 month: 11 day: 6 }
   license_type: RESTRICTED_IF_STATICALLY_LINKED
 }
diff --git a/README.md b/README.md
index 8fd40f5..f0f48c4 100644
--- a/README.md
+++ b/README.md
@@ -89,7 +89,7 @@
 To limit the harm that malicious users can do this way, *fusermount3*
 enforces the following limitations:
 
-  - The user can only mount on a mountpoint for which he has write
+  - The user can only mount on a mountpoint for which they have write
     permission
 
   - The mountpoint must not be a sticky directory which isn't owned by
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 @@
 				    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 @@
 				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_common.h b/include/fuse_common.h
index 0481aac..2d686b2 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -15,6 +15,7 @@
 #define FUSE_COMMON_H_
 
 #include "fuse_opt.h"
+#include "fuse_log.h"
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -83,7 +84,8 @@
 	unsigned int cache_readdir : 1;
 
 	/** Padding.  Reserved for future use*/
-	unsigned int padding : 26;
+	unsigned int padding : 25;
+	unsigned int padding2 : 32;
 
 	/** File handle id.  May be filled in by filesystem in create,
 	 * open, and opendir().  Available in most other file operations on the
@@ -246,7 +248,7 @@
 #define FUSE_CAP_READDIRPLUS		(1 << 13)
 
 /**
- * Indicates that the filesystem supports adaptive readdirplus. 
+ * Indicates that the filesystem supports adaptive readdirplus.
  *
  * If FUSE_CAP_READDIRPLUS is not set, this flag has no effect.
  *
@@ -258,6 +260,15 @@
  * will issue both readdir() and readdirplus() requests, depending on
  * how much information is expected to be required.
  *
+ * As of Linux 4.20, the algorithm is as follows: when userspace
+ * starts to read directory entries, issue a READDIRPLUS request to
+ * the filesystem. If any entry attributes have been looked up by the
+ * time userspace requests the next batch of entries continue with
+ * READDIRPLUS, otherwise switch to plain READDIR.  This will reasult
+ * in eg plain "ls" triggering READDIRPLUS first then READDIR after
+ * that because it doesn't do lookups.  "ls -l" should result in all
+ * READDIRPLUS, except if dentries are already cached.
+ *
  * This feature is enabled by default when supported by the kernel and
  * if the filesystem implements both a readdirplus() and a readdir()
  * handler.
diff --git a/include/fuse_log.h b/include/fuse_log.h
new file mode 100644
index 0000000..5e112e0
--- /dev/null
+++ b/include/fuse_log.h
@@ -0,0 +1,82 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2019  Red Hat, Inc.
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB.
+*/
+
+#ifndef FUSE_LOG_H_
+#define FUSE_LOG_H_
+
+/** @file
+ *
+ * This file defines the logging interface of FUSE
+ */
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Log severity level
+ *
+ * These levels correspond to syslog(2) log levels since they are widely used.
+ */
+enum fuse_log_level {
+	FUSE_LOG_EMERG,
+	FUSE_LOG_ALERT,
+	FUSE_LOG_CRIT,
+	FUSE_LOG_ERR,
+	FUSE_LOG_WARNING,
+	FUSE_LOG_NOTICE,
+	FUSE_LOG_INFO,
+	FUSE_LOG_DEBUG
+};
+
+/**
+ * Log message handler function.
+ *
+ * This function must be thread-safe.  It may be called from any libfuse
+ * function, including fuse_parse_cmdline() and other functions invoked before
+ * a FUSE filesystem is created.
+ *
+ * Install a custom log message handler function using fuse_set_log_func().
+ *
+ * @param level log severity level
+ * @param fmt sprintf-style format string including newline
+ * @param ap format string arguments
+ */
+typedef void (*fuse_log_func_t)(enum fuse_log_level level,
+				const char *fmt, va_list ap);
+
+/**
+ * Install a custom log handler function.
+ *
+ * Log messages are emitted by libfuse functions to report errors and debug
+ * information.  Messages are printed to stderr by default but this can be
+ * overridden by installing a custom log message handler function.
+ *
+ * The log message handler function is global and affects all FUSE filesystems
+ * created within this process.
+ *
+ * @param func a custom log message handler function or NULL to revert to
+ *             the default
+ */
+void fuse_set_log_func(fuse_log_func_t func);
+
+/**
+ * Emit a log message
+ *
+ * @param level severity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc)
+ * @param fmt sprintf-style format string including newline
+ */
+void fuse_log(enum fuse_log_level level, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FUSE_LOG_H_ */
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 @@
 				 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_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/include/meson.build b/include/meson.build
index 55cb6f7..bf67197 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -1,4 +1,4 @@
 libfuse_headers = [ 'fuse.h', 'fuse_common.h', 'fuse_lowlevel.h',
-	            'fuse_opt.h', 'cuse_lowlevel.h' ]
+	            'fuse_opt.h', 'cuse_lowlevel.h', 'fuse_log.h' ]
 
 install_headers(libfuse_headers, subdir: 'fuse3')
diff --git a/lib/cuse_lowlevel.c b/lib/cuse_lowlevel.c
index 019b20f..9917b64 100644
--- a/lib/cuse_lowlevel.c
+++ b/lib/cuse_lowlevel.c
@@ -122,14 +122,14 @@
 				      NULL);
 
 	if (dev_info_len > CUSE_INIT_INFO_MAX) {
-		fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
+		fuse_log(FUSE_LOG_ERR, "cuse: dev_info (%zu) too large, limit=%u\n",
 			dev_info_len, CUSE_INIT_INFO_MAX);
 		return NULL;
 	}
 
 	cd = calloc(1, sizeof(*cd) + dev_info_len);
 	if (!cd) {
-		fprintf(stderr, "cuse: failed to allocate cuse_data\n");
+		fuse_log(FUSE_LOG_ERR, "cuse: failed to allocate cuse_data\n");
 		return NULL;
 	}
 
@@ -203,8 +203,8 @@
 
 	(void) nodeid;
 	if (se->debug) {
-		fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
-		fprintf(stderr, "flags=0x%08x\n", arg->flags);
+		fuse_log(FUSE_LOG_DEBUG, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
+		fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
 	}
 	se->conn.proto_major = arg->major;
 	se->conn.proto_minor = arg->minor;
@@ -212,14 +212,14 @@
 	se->conn.want = 0;
 
 	if (arg->major < 7) {
-		fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
+		fuse_log(FUSE_LOG_ERR, "cuse: unsupported protocol version: %u.%u\n",
 			arg->major, arg->minor);
 		fuse_reply_err(req, EPROTO);
 		return;
 	}
 
 	if (bufsize < FUSE_MIN_READ_BUFFER) {
-		fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
+		fuse_log(FUSE_LOG_ERR, "cuse: warning: buffer size too small: %zu\n",
 			bufsize);
 		bufsize = FUSE_MIN_READ_BUFFER;
 	}
@@ -242,14 +242,14 @@
 	outarg.dev_minor = cd->dev_minor;
 
 	if (se->debug) {
-		fprintf(stderr, "   CUSE_INIT: %u.%u\n",
+		fuse_log(FUSE_LOG_DEBUG, "   CUSE_INIT: %u.%u\n",
 			outarg.major, outarg.minor);
-		fprintf(stderr, "   flags=0x%08x\n", outarg.flags);
-		fprintf(stderr, "   max_read=0x%08x\n", outarg.max_read);
-		fprintf(stderr, "   max_write=0x%08x\n", outarg.max_write);
-		fprintf(stderr, "   dev_major=%u\n", outarg.dev_major);
-		fprintf(stderr, "   dev_minor=%u\n", outarg.dev_minor);
-		fprintf(stderr, "   dev_info: %.*s\n", cd->dev_info_len,
+		fuse_log(FUSE_LOG_DEBUG, "   flags=0x%08x\n", outarg.flags);
+		fuse_log(FUSE_LOG_DEBUG, "   max_read=0x%08x\n", outarg.max_read);
+		fuse_log(FUSE_LOG_DEBUG, "   max_write=0x%08x\n", outarg.max_write);
+		fuse_log(FUSE_LOG_DEBUG, "   dev_major=%u\n", outarg.dev_major);
+		fuse_log(FUSE_LOG_DEBUG, "   dev_minor=%u\n", outarg.dev_minor);
+		fuse_log(FUSE_LOG_DEBUG, "   dev_info: %.*s\n", cd->dev_info_len,
 			cd->dev_info);
 	}
 
@@ -303,9 +303,9 @@
 	fd = open(devname, O_RDWR);
 	if (fd == -1) {
 		if (errno == ENODEV || errno == ENOENT)
-			fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
+			fuse_log(FUSE_LOG_ERR, "cuse: device not found, try 'modprobe cuse' first\n");
 		else
-			fprintf(stderr, "cuse: failed to open %s: %s\n",
+			fuse_log(FUSE_LOG_ERR, "cuse: failed to open %s: %s\n",
 				devname, strerror(errno));
 		goto err_se;
 	}
diff --git a/lib/fuse.c b/lib/fuse.c
index ec6cc59..b0f5b30 100755
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -214,12 +214,12 @@
 
 	mod = calloc(1, sizeof(struct fuse_module));
 	if (!mod) {
-		fprintf(stderr, "fuse: failed to allocate module\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module\n");
 		return -1;
 	}
 	mod->name = strdup(name);
 	if (!mod->name) {
-		fprintf(stderr, "fuse: failed to allocate module name\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module name\n");
 		free(mod);
 		return -1;
 	}
@@ -256,19 +256,19 @@
 
 	tmp = malloc(strlen(module) + 64);
 	if (!tmp) {
-		fprintf(stderr, "fuse: memory allocation failed\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
 		return -1;
 	}
 	sprintf(tmp, "libfusemod_%s.so", module);
 	so = calloc(1, sizeof(struct fusemod_so));
 	if (!so) {
-		fprintf(stderr, "fuse: failed to allocate module so\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate module so\n");
 		goto out;
 	}
 
 	so->handle = dlopen(tmp, RTLD_NOW);
 	if (so->handle == NULL) {
-		fprintf(stderr, "fuse: dlopen(%s) failed: %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: dlopen(%s) failed: %s\n",
 			tmp, dlerror());
 		goto out_free_so;
 	}
@@ -276,7 +276,7 @@
 	sprintf(tmp, "fuse_module_%s_factory", module);
 	*(void**)(&factory) = dlsym(so->handle, tmp);
 	if (factory == NULL) {
-		fprintf(stderr, "fuse: symbol <%s> not found in module: %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
 			tmp, dlerror());
 		goto out_dlclose;
 	}
@@ -481,7 +481,8 @@
 	list_del(&slab->list);
 	res = munmap(slab, f->pagesize);
 	if (res == -1)
-		fprintf(stderr, "fuse warning: munmap(%p) failed\n", slab);
+		fuse_log(FUSE_LOG_WARNING, "fuse warning: munmap(%p) failed\n",
+			 slab);
 }
 
 static void free_node_mem(struct fuse *f, struct node *node)
@@ -540,7 +541,7 @@
 {
 	struct node *node = get_node_nocheck(f, nodeid);
 	if (!node) {
-		fprintf(stderr, "fuse internal error: node %llu not found\n",
+		fuse_log(FUSE_LOG_ERR, "fuse internal error: node %llu not found\n",
 			(unsigned long long) nodeid);
 		abort();
 	}
@@ -754,7 +755,7 @@
 					remerge_name(f);
 				return;
 			}
-		fprintf(stderr,
+		fuse_log(FUSE_LOG_ERR,
 			"fuse internal error: unable to unhash node: %llu\n",
 			(unsigned long long) node->nodeid);
 		abort();
@@ -819,7 +820,7 @@
 static void delete_node(struct fuse *f, struct node *node)
 {
 	if (f->conf.debug)
-		fprintf(stderr, "DELETE: %llu\n",
+		fuse_log(FUSE_LOG_DEBUG, "DELETE: %llu\n",
 			(unsigned long long) node->nodeid);
 
 	assert(node->treelock == 0);
@@ -965,7 +966,7 @@
 		*bufsize = newbufsize;
 	}
 	s -= len;
-	strncpy(s, name, len);
+	memcpy(s, name, len);
 	s--;
 	*s = '/';
 
@@ -1165,10 +1166,10 @@
 			wnode = lookup_node(f, nodeid, name);
 
 		if (wnode) {
-			fprintf(stderr, "%s %llu (w)\n",
+			fuse_log(FUSE_LOG_DEBUG, "%s %llu (w)\n",
 				msg, (unsigned long long) wnode->nodeid);
 		} else {
-			fprintf(stderr, "%s %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "%s %llu\n",
 				msg, (unsigned long long) nodeid);
 		}
 	}
@@ -1479,7 +1480,7 @@
 
 	if (newnode != NULL) {
 		if (hide) {
-			fprintf(stderr, "fuse: hidden file got created during hiding\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: hidden file got created during hiding\n");
 			err = -EBUSY;
 			goto out;
 		}
@@ -1632,7 +1633,7 @@
 	if (fs->op.getattr) {
 		if (fs->debug) {
 			char buf[10];
-			fprintf(stderr, "getattr[%s] %s\n",
+			fuse_log(FUSE_LOG_DEBUG, "getattr[%s] %s\n",
 				file_info_string(fi, buf, sizeof(buf)),
 				path);
 		}
@@ -1648,7 +1649,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.rename) {
 		if (fs->debug)
-			fprintf(stderr, "rename %s %s 0x%x\n", oldpath, newpath,
+			fuse_log(FUSE_LOG_DEBUG, "rename %s %s 0x%x\n", oldpath, newpath,
 				flags);
 
 		return fs->op.rename(oldpath, newpath, flags);
@@ -1662,7 +1663,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.unlink) {
 		if (fs->debug)
-			fprintf(stderr, "unlink %s\n", path);
+			fuse_log(FUSE_LOG_DEBUG, "unlink %s\n", path);
 
 		return fs->op.unlink(path);
 	} else {
@@ -1675,7 +1676,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.rmdir) {
 		if (fs->debug)
-			fprintf(stderr, "rmdir %s\n", path);
+			fuse_log(FUSE_LOG_DEBUG, "rmdir %s\n", path);
 
 		return fs->op.rmdir(path);
 	} else {
@@ -1688,7 +1689,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.symlink) {
 		if (fs->debug)
-			fprintf(stderr, "symlink %s %s\n", linkname, path);
+			fuse_log(FUSE_LOG_DEBUG, "symlink %s %s\n", linkname, path);
 
 		return fs->op.symlink(linkname, path);
 	} else {
@@ -1701,7 +1702,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.link) {
 		if (fs->debug)
-			fprintf(stderr, "link %s %s\n", oldpath, newpath);
+			fuse_log(FUSE_LOG_DEBUG, "link %s %s\n", oldpath, newpath);
 
 		return fs->op.link(oldpath, newpath);
 	} else {
@@ -1715,7 +1716,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.release) {
 		if (fs->debug)
-			fprintf(stderr, "release%s[%llu] flags: 0x%x\n",
+			fuse_log(FUSE_LOG_DEBUG, "release%s[%llu] flags: 0x%x\n",
 				fi->flush ? "+flush" : "",
 				(unsigned long long) fi->fh, fi->flags);
 
@@ -1733,13 +1734,13 @@
 		int err;
 
 		if (fs->debug)
-			fprintf(stderr, "opendir flags: 0x%x %s\n", fi->flags,
+			fuse_log(FUSE_LOG_DEBUG, "opendir flags: 0x%x %s\n", fi->flags,
 				path);
 
 		err = fs->op.opendir(path, fi);
 
 		if (fs->debug && !err)
-			fprintf(stderr, "   opendir[%llu] flags: 0x%x %s\n",
+			fuse_log(FUSE_LOG_DEBUG, "   opendir[%llu] flags: 0x%x %s\n",
 				(unsigned long long) fi->fh, fi->flags, path);
 
 		return err;
@@ -1756,13 +1757,13 @@
 		int err;
 
 		if (fs->debug)
-			fprintf(stderr, "open flags: 0x%x %s\n", fi->flags,
+			fuse_log(FUSE_LOG_DEBUG, "open flags: 0x%x %s\n", fi->flags,
 				path);
 
 		err = fs->op.open(path, fi);
 
 		if (fs->debug && !err)
-			fprintf(stderr, "   open[%llu] flags: 0x%x %s\n",
+			fuse_log(FUSE_LOG_DEBUG, "   open[%llu] flags: 0x%x %s\n",
 				(unsigned long long) fi->fh, fi->flags, path);
 
 		return err;
@@ -1792,7 +1793,7 @@
 		int res;
 
 		if (fs->debug)
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_DEBUG,
 				"read[%llu] %zu bytes from %llu flags: 0x%x\n",
 				(unsigned long long) fi->fh,
 				size, (unsigned long long) off, fi->flags);
@@ -1822,12 +1823,12 @@
 		}
 
 		if (fs->debug && res >= 0)
-			fprintf(stderr, "   read[%llu] %zu bytes from %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "   read[%llu] %zu bytes from %llu\n",
 				(unsigned long long) fi->fh,
 				fuse_buf_size(*bufp),
 				(unsigned long long) off);
 		if (res >= 0 && fuse_buf_size(*bufp) > size)
-			fprintf(stderr, "fuse: read too many bytes\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
 
 		if (res < 0)
 			return res;
@@ -1846,7 +1847,7 @@
 		int res;
 
 		if (fs->debug)
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_DEBUG,
 				"read[%llu] %zu bytes from %llu flags: 0x%x\n",
 				(unsigned long long) fi->fh,
 				size, (unsigned long long) off, fi->flags);
@@ -1867,12 +1868,12 @@
 		}
 
 		if (fs->debug && res >= 0)
-			fprintf(stderr, "   read[%llu] %u bytes from %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "   read[%llu] %u bytes from %llu\n",
 				(unsigned long long) fi->fh,
 				res,
 				(unsigned long long) off);
 		if (res >= 0 && res > (int) size)
-			fprintf(stderr, "fuse: read too many bytes\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: read too many bytes\n");
 
 		return res;
 	} else {
@@ -1891,7 +1892,7 @@
 
 		assert(buf->idx == 0 && buf->off == 0);
 		if (fs->debug)
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_DEBUG,
 				"write%s[%llu] %zu bytes to %llu flags: 0x%x\n",
 				fi->writepage ? "page" : "",
 				(unsigned long long) fi->fh,
@@ -1931,12 +1932,12 @@
 		}
 out:
 		if (fs->debug && res >= 0)
-			fprintf(stderr, "   write%s[%llu] %u bytes to %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "   write%s[%llu] %u bytes to %llu\n",
 				fi->writepage ? "page" : "",
 				(unsigned long long) fi->fh, res,
 				(unsigned long long) off);
 		if (res > (int) size)
-			fprintf(stderr, "fuse: wrote too many bytes\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: wrote too many bytes\n");
 
 		return res;
 	} else {
@@ -1960,7 +1961,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.fsync) {
 		if (fs->debug)
-			fprintf(stderr, "fsync[%llu] datasync: %i\n",
+			fuse_log(FUSE_LOG_DEBUG, "fsync[%llu] datasync: %i\n",
 				(unsigned long long) fi->fh, datasync);
 
 		return fs->op.fsync(path, datasync, fi);
@@ -1975,7 +1976,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.fsyncdir) {
 		if (fs->debug)
-			fprintf(stderr, "fsyncdir[%llu] datasync: %i\n",
+			fuse_log(FUSE_LOG_DEBUG, "fsyncdir[%llu] datasync: %i\n",
 				(unsigned long long) fi->fh, datasync);
 
 		return fs->op.fsyncdir(path, datasync, fi);
@@ -1990,7 +1991,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.flush) {
 		if (fs->debug)
-			fprintf(stderr, "flush[%llu]\n",
+			fuse_log(FUSE_LOG_DEBUG, "flush[%llu]\n",
 				(unsigned long long) fi->fh);
 
 		return fs->op.flush(path, fi);
@@ -2004,7 +2005,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.statfs) {
 		if (fs->debug)
-			fprintf(stderr, "statfs %s\n", path);
+			fuse_log(FUSE_LOG_DEBUG, "statfs %s\n", path);
 
 		return fs->op.statfs(path, buf);
 	} else {
@@ -2020,7 +2021,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.releasedir) {
 		if (fs->debug)
-			fprintf(stderr, "releasedir[%llu] flags: 0x%x\n",
+			fuse_log(FUSE_LOG_DEBUG, "releasedir[%llu] flags: 0x%x\n",
 				(unsigned long long) fi->fh, fi->flags);
 
 		return fs->op.releasedir(path, fi);
@@ -2037,7 +2038,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.readdir) {
 		if (fs->debug) {
-			fprintf(stderr, "readdir%s[%llu] from %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "readdir%s[%llu] from %llu\n",
 				(flags & FUSE_READDIR_PLUS) ? "plus" : "",
 				(unsigned long long) fi->fh,
 				(unsigned long long) off);
@@ -2057,7 +2058,7 @@
 		int err;
 
 		if (fs->debug)
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_DEBUG,
 				"create flags: 0x%x %s 0%o umask=0%03o\n",
 				fi->flags, path, mode,
 				fuse_get_context()->umask);
@@ -2065,7 +2066,7 @@
 		err = fs->op.create(path, mode, fi);
 
 		if (fs->debug && !err)
-			fprintf(stderr, "   create[%llu] flags: 0x%x %s\n",
+			fuse_log(FUSE_LOG_DEBUG, "   create[%llu] flags: 0x%x %s\n",
 				(unsigned long long) fi->fh, fi->flags, path);
 
 		return err;
@@ -2080,7 +2081,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.lock) {
 		if (fs->debug)
-			fprintf(stderr, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s %s start: %llu len: %llu pid: %llu\n",
 				(unsigned long long) fi->fh,
 				(cmd == F_GETLK ? "F_GETLK" :
 				 (cmd == F_SETLK ? "F_SETLK" :
@@ -2107,7 +2108,7 @@
 		if (fs->debug) {
 			int xop = op & ~LOCK_NB;
 
-			fprintf(stderr, "lock[%llu] %s%s\n",
+			fuse_log(FUSE_LOG_DEBUG, "lock[%llu] %s%s\n",
 				(unsigned long long) fi->fh,
 				xop == LOCK_SH ? "LOCK_SH" :
 				(xop == LOCK_EX ? "LOCK_EX" :
@@ -2127,7 +2128,7 @@
 	if (fs->op.chown) {
 		if (fs->debug) {
 			char buf[10];
-			fprintf(stderr, "chown[%s] %s %lu %lu\n",
+			fuse_log(FUSE_LOG_DEBUG, "chown[%s] %s %lu %lu\n",
 				file_info_string(fi, buf, sizeof(buf)),
 				path, (unsigned long) uid, (unsigned long) gid);
 		}
@@ -2144,7 +2145,7 @@
 	if (fs->op.truncate) {
 		if (fs->debug) {
 			char buf[10];
-			fprintf(stderr, "truncate[%s] %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "truncate[%s] %llu\n",
 				file_info_string(fi, buf, sizeof(buf)),
 				(unsigned long long) size);
 		}
@@ -2161,7 +2162,7 @@
 	if (fs->op.utimens) {
 		if (fs->debug) {
 			char buf[10];
-			fprintf(stderr, "utimens[%s] %s %li.%09lu %li.%09lu\n",
+			fuse_log(FUSE_LOG_DEBUG, "utimens[%s] %s %li.%09lu %li.%09lu\n",
 				file_info_string(fi, buf, sizeof(buf)),
 				path, tv[0].tv_sec, tv[0].tv_nsec,
 				tv[1].tv_sec, tv[1].tv_nsec);
@@ -2177,7 +2178,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.access) {
 		if (fs->debug)
-			fprintf(stderr, "access %s 0%o\n", path, mask);
+			fuse_log(FUSE_LOG_DEBUG, "access %s 0%o\n", path, mask);
 
 		return fs->op.access(path, mask);
 	} else {
@@ -2191,7 +2192,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.readlink) {
 		if (fs->debug)
-			fprintf(stderr, "readlink %s %lu\n", path,
+			fuse_log(FUSE_LOG_DEBUG, "readlink %s %lu\n", path,
 				(unsigned long) len);
 
 		return fs->op.readlink(path, buf, len);
@@ -2206,7 +2207,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.mknod) {
 		if (fs->debug)
-			fprintf(stderr, "mknod %s 0%o 0x%llx umask=0%03o\n",
+			fuse_log(FUSE_LOG_DEBUG, "mknod %s 0%o 0x%llx umask=0%03o\n",
 				path, mode, (unsigned long long) rdev,
 				fuse_get_context()->umask);
 
@@ -2221,7 +2222,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.mkdir) {
 		if (fs->debug)
-			fprintf(stderr, "mkdir %s 0%o umask=0%03o\n",
+			fuse_log(FUSE_LOG_DEBUG, "mkdir %s 0%o umask=0%03o\n",
 				path, mode, fuse_get_context()->umask);
 
 		return fs->op.mkdir(path, mode);
@@ -2236,7 +2237,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.setxattr) {
 		if (fs->debug)
-			fprintf(stderr, "setxattr %s %s %lu 0x%x\n",
+			fuse_log(FUSE_LOG_DEBUG, "setxattr %s %s %lu 0x%x\n",
 				path, name, (unsigned long) size, flags);
 
 		return fs->op.setxattr(path, name, value, size, flags);
@@ -2251,7 +2252,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.getxattr) {
 		if (fs->debug)
-			fprintf(stderr, "getxattr %s %s %lu\n",
+			fuse_log(FUSE_LOG_DEBUG, "getxattr %s %s %lu\n",
 				path, name, (unsigned long) size);
 
 		return fs->op.getxattr(path, name, value, size);
@@ -2266,7 +2267,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.listxattr) {
 		if (fs->debug)
-			fprintf(stderr, "listxattr %s %lu\n",
+			fuse_log(FUSE_LOG_DEBUG, "listxattr %s %lu\n",
 				path, (unsigned long) size);
 
 		return fs->op.listxattr(path, list, size);
@@ -2281,7 +2282,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.bmap) {
 		if (fs->debug)
-			fprintf(stderr, "bmap %s blocksize: %lu index: %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "bmap %s blocksize: %lu index: %llu\n",
 				path, (unsigned long) blocksize,
 				(unsigned long long) *idx);
 
@@ -2296,7 +2297,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.removexattr) {
 		if (fs->debug)
-			fprintf(stderr, "removexattr %s %s\n", path, name);
+			fuse_log(FUSE_LOG_DEBUG, "removexattr %s %s\n", path, name);
 
 		return fs->op.removexattr(path, name);
 	} else {
@@ -2311,7 +2312,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.ioctl) {
 		if (fs->debug)
-			fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n",
+			fuse_log(FUSE_LOG_DEBUG, "ioctl[%llu] 0x%x flags: 0x%x\n",
 				(unsigned long long) fi->fh, cmd, flags);
 
 		return fs->op.ioctl(path, cmd, arg, fi, flags, data);
@@ -2328,14 +2329,14 @@
 		int res;
 
 		if (fs->debug)
-			fprintf(stderr, "poll[%llu] ph: %p, events 0x%x\n",
+			fuse_log(FUSE_LOG_DEBUG, "poll[%llu] ph: %p, events 0x%x\n",
 				(unsigned long long) fi->fh, ph,
 				fi->poll_events);
 
 		res = fs->op.poll(path, fi, ph, reventsp);
 
 		if (fs->debug && !res)
-			fprintf(stderr, "   poll[%llu] revents: 0x%x\n",
+			fuse_log(FUSE_LOG_DEBUG, "   poll[%llu] revents: 0x%x\n",
 				(unsigned long long) fi->fh, *reventsp);
 
 		return res;
@@ -2349,7 +2350,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.fallocate) {
 		if (fs->debug)
-			fprintf(stderr, "fallocate %s mode %x, offset: %llu, length: %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "fallocate %s mode %x, offset: %llu, length: %llu\n",
 				path,
 				mode,
 				(unsigned long long) offset,
@@ -2369,7 +2370,7 @@
 	fuse_get_context()->private_data = fs->user_data;
 	if (fs->op.copy_file_range) {
 		if (fs->debug)
-			fprintf(stderr, "copy_file_range from %s:%llu to "
+			fuse_log(FUSE_LOG_DEBUG, "copy_file_range from %s:%llu to "
 			                "%s:%llu, length: %llu\n",
 				path_in,
 				(unsigned long long) off_in,
@@ -2383,6 +2384,23 @@
 		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;
@@ -2520,7 +2538,7 @@
 	if (res == 0) {
 		res = do_lookup(f, nodeid, name, e);
 		if (res == 0 && f->conf.debug) {
-			fprintf(stderr, "   NODEID: %llu\n",
+			fuse_log(FUSE_LOG_DEBUG, "   NODEID: %llu\n",
 				(unsigned long long) e->ino);
 		}
 	}
@@ -2543,7 +2561,7 @@
 			   abort.  If memory is so low that the
 			   context cannot be allocated, there's not
 			   much hope for the filesystem anyway */
-			fprintf(stderr, "fuse: failed to allocate thread specific data\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate thread specific data\n");
 			abort();
 		}
 		pthread_setspecific(fuse_context_key, c);
@@ -2567,7 +2585,7 @@
 	if (!fuse_context_ref) {
 		err = pthread_key_create(&fuse_context_key, fuse_freecontext);
 		if (err) {
-			fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
+			fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
 				strerror(err));
 			pthread_mutex_unlock(&fuse_context_lock);
 			return -1;
@@ -2680,7 +2698,7 @@
 			pthread_mutex_lock(&f->lock);
 			if (len == 1) {
 				if (f->conf.debug)
-					fprintf(stderr, "LOOKUP-DOT\n");
+					fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOT\n");
 				dot = get_node_nocheck(f, parent);
 				if (dot == NULL) {
 					pthread_mutex_unlock(&f->lock);
@@ -2690,7 +2708,7 @@
 				dot->refctr++;
 			} else {
 				if (f->conf.debug)
-					fprintf(stderr, "LOOKUP-DOTDOT\n");
+					fuse_log(FUSE_LOG_DEBUG, "LOOKUP-DOTDOT\n");
 				parent = get_node(f, parent)->parent->nodeid;
 			}
 			pthread_mutex_unlock(&f->lock);
@@ -2702,7 +2720,7 @@
 	if (!err) {
 		struct fuse_intr_data d;
 		if (f->conf.debug)
-			fprintf(stderr, "LOOKUP %s\n", path);
+			fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path);
 		fuse_prepare_interrupt(f, req, &d);
 		err = lookup_path(f, parent, name, path, &e, NULL);
 		if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
@@ -2724,7 +2742,7 @@
 static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup)
 {
 	if (f->conf.debug)
-		fprintf(stderr, "FORGET %llu/%llu\n", (unsigned long long)ino,
+		fuse_log(FUSE_LOG_DEBUG, "FORGET %llu/%llu\n", (unsigned long long)ino,
 			(unsigned long long) nlookup);
 	forget_node(f, ino, nlookup);
 }
@@ -2792,7 +2810,7 @@
 	if (fs->op.chmod) {
 		if (fs->debug) {
 			char buf[10];
-			fprintf(stderr, "chmod[%s] %s %llo\n",
+			fuse_log(FUSE_LOG_DEBUG, "chmod[%s] %s %llo\n",
 				file_info_string(fi, buf, sizeof(buf)),
 				path, (unsigned long long) mode);
 		}
@@ -4353,6 +4371,31 @@
 	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)
 {
 	/*
@@ -4450,6 +4493,7 @@
 	.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)
@@ -4765,13 +4809,13 @@
 	struct fuse_fs *fs;
 
 	if (sizeof(struct fuse_operations) < op_size) {
-		fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not not work\n");
 		op_size = sizeof(struct fuse_operations);
 	}
 
 	fs = (struct fuse_fs *) calloc(1, sizeof(struct fuse_fs));
 	if (!fs) {
-		fprintf(stderr, "fuse: failed to allocate fuse_fs object\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse_fs object\n");
 		return NULL;
 	}
 
@@ -4786,7 +4830,7 @@
 	t->size = NODE_TABLE_MIN_SIZE;
 	t->array = (struct node **) calloc(1, sizeof(struct node *) * t->size);
 	if (t->array == NULL) {
-		fprintf(stderr, "fuse: memory allocation failed\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
 		return -1;
 	}
 	t->use = 0;
@@ -4838,7 +4882,7 @@
 
 	f = (struct fuse *) calloc(1, sizeof(struct fuse));
 	if (f == NULL) {
-		fprintf(stderr, "fuse: failed to allocate fuse object\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
 		goto out;
 	}
 
@@ -4916,7 +4960,7 @@
 		goto out_free_fs;
 
 	if (f->conf.debug) {
-		fprintf(stderr, "nullpath_ok: %i\n", f->conf.nullpath_ok);
+		fuse_log(FUSE_LOG_DEBUG, "nullpath_ok: %i\n", f->conf.nullpath_ok);
 	}
 
 	/* Trace topmost layer by default */
@@ -4933,7 +4977,7 @@
 
 	root = alloc_node(f);
 	if (root == NULL) {
-		fprintf(stderr, "fuse: memory allocation failed\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
 		goto out_free_id_table;
 	}
 	if (lru_enabled(f)) {
diff --git a/lib/fuse_log.c b/lib/fuse_log.c
new file mode 100644
index 0000000..0d268ab
--- /dev/null
+++ b/lib/fuse_log.c
@@ -0,0 +1,40 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2019  Red Hat, Inc.
+
+  Logging API.
+
+  This program can be distributed under the terms of the GNU LGPLv2.
+  See the file COPYING.LIB
+*/
+
+#include "fuse_log.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+static void default_log_func(
+		__attribute__(( unused )) enum fuse_log_level level,
+		const char *fmt, va_list ap)
+{
+	vfprintf(stderr, fmt, ap);
+}
+
+static fuse_log_func_t log_func = default_log_func;
+
+void fuse_set_log_func(fuse_log_func_t func)
+{
+	if (!func)
+		func = default_log_func;
+
+	log_func = func;
+}
+
+void fuse_log(enum fuse_log_level level, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	log_func(level, fmt, ap);
+	va_end(ap);
+}
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
index 83f1641..445e9a0 100644
--- a/lib/fuse_loop_mt.c
+++ b/lib/fuse_loop_mt.c
@@ -58,7 +58,7 @@
 {
 	struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
 	if (ch == NULL) {
-		fprintf(stderr, "fuse: failed to allocate channel\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate channel\n");
 		return NULL;
 	}
 
@@ -201,7 +201,7 @@
 	pthread_attr_init(&attr);
 	stack_size = getenv(ENVNAME_THREAD_STACK);
 	if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
-		fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
+		fuse_log(FUSE_LOG_ERR, "fuse: invalid stack size: %s\n", stack_size);
 
 	/* Disallow signal reception in worker threads */
 	sigemptyset(&newset);
@@ -214,7 +214,7 @@
 	pthread_sigmask(SIG_SETMASK, &oldset, NULL);
 	pthread_attr_destroy(&attr);
 	if (res != 0) {
-		fprintf(stderr, "fuse: error creating thread: %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: error creating thread: %s\n",
 			strerror(res));
 		return -1;
 	}
@@ -235,7 +235,7 @@
 #endif
 	clonefd = open(devname, O_RDWR | O_CLOEXEC);
 	if (clonefd == -1) {
-		fprintf(stderr, "fuse: failed to open %s: %s\n", devname,
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n", devname,
 			strerror(errno));
 		return NULL;
 	}
@@ -244,7 +244,7 @@
 	masterfd = mt->se->fd;
 	res = ioctl(clonefd, FUSE_DEV_IOC_CLONE, &masterfd);
 	if (res == -1) {
-		fprintf(stderr, "fuse: failed to clone device fd: %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to clone device fd: %s\n",
 			strerror(errno));
 		close(clonefd);
 		return NULL;
@@ -262,7 +262,7 @@
 
 	struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
 	if (!w) {
-		fprintf(stderr, "fuse: failed to allocate worker structure\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate worker structure\n");
 		return -1;
 	}
 	memset(w, 0, sizeof(struct fuse_worker));
@@ -274,7 +274,7 @@
 		w->ch = fuse_clone_chan(mt);
 		if(!w->ch) {
 			/* Don't attempt this again */
-			fprintf(stderr, "fuse: trying to continue "
+			fuse_log(FUSE_LOG_ERR, "fuse: trying to continue "
 				"without -o clone_fd.\n");
 			mt->clone_fd = 0;
 		}
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index b9f128f..f2d7038 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -150,7 +150,7 @@
 
 	req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req));
 	if (req == NULL) {
-		fprintf(stderr, "fuse: failed to allocate request\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n");
 	} else {
 		req->se = se;
 		req->ctr = 1;
@@ -170,15 +170,15 @@
 	out->len = iov_length(iov, count);
 	if (se->debug) {
 		if (out->unique == 0) {
-			fprintf(stderr, "NOTIFY: code=%d length=%u\n",
+			fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n",
 				out->error, out->len);
 		} else if (out->error) {
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_DEBUG,
 				"   unique: %llu, error: %i (%s), outsize: %i\n",
 				(unsigned long long) out->unique, out->error,
 				strerror(-out->error), out->len);
 		} else {
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_DEBUG,
 				"   unique: %llu, success, outsize: %i\n",
 				(unsigned long long) out->unique, out->len);
 		}
@@ -207,7 +207,7 @@
 	struct fuse_out_header out;
 
 	if (error <= -1000 || error > 0) {
-		fprintf(stderr, "fuse: bad error value: %i\n",	error);
+		fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n",	error);
 		error = -ERANGE;
 	}
 
@@ -285,7 +285,7 @@
 	dirent->off = off;
 	dirent->namelen = namelen;
 	dirent->type = (stbuf->st_mode & S_IFMT) >> 12;
-	strncpy(dirent->name, name, namelen);
+	memcpy(dirent->name, name, namelen);
 	memset(dirent->name + namelen, 0, entlen_padded - entlen);
 
 	return entlen_padded;
@@ -378,7 +378,7 @@
 	dirent->off = off;
 	dirent->namelen = namelen;
 	dirent->type = (e->attr.st_mode & S_IFMT) >> 12;
-	strncpy(dirent->name, name, namelen);
+	memcpy(dirent->name, name, namelen);
 	memset(dirent->name + namelen, 0, entlen_padded - entlen);
 
 	return entlen_padded;
@@ -601,11 +601,11 @@
 
 	res = read(fd, buf, len);
 	if (res == -1) {
-		fprintf(stderr, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
+		fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
 		return -EIO;
 	}
 	if (res != len) {
-		fprintf(stderr, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
+		fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
 		return -EIO;
 	}
 	return 0;
@@ -714,7 +714,7 @@
 
 	if (res != headerlen) {
 		res = -EIO;
-		fprintf(stderr, "fuse: short vmsplice to pipe: %u/%zu\n", res,
+		fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res,
 			headerlen);
 		goto clear_pipe;
 	}
@@ -807,7 +807,7 @@
 	out->len = headerlen + len;
 
 	if (se->debug) {
-		fprintf(stderr,
+		fuse_log(FUSE_LOG_DEBUG,
 			"   unique: %llu, success, outsize: %i (splice)\n",
 			(unsigned long long) out->unique, out->len);
 	}
@@ -826,7 +826,7 @@
 	}
 	if (res != out->len) {
 		res = -EIO;
-		fprintf(stderr, "fuse: short splice from pipe: %u/%u\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n",
 			res, out->len);
 		goto clear_pipe;
 	}
@@ -1065,6 +1065,16 @@
 	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;
@@ -1396,7 +1406,7 @@
 			sizeof(struct fuse_write_in);
 	}
 	if (bufv.buf[0].size < arg->size) {
-		fprintf(stderr, "fuse: do_write_buf: buffer size too small\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n");
 		fuse_reply_err(req, EIO);
 		goto out;
 	}
@@ -1724,7 +1734,7 @@
 
 	(void) nodeid;
 	if (se->debug)
-		fprintf(stderr, "INTERRUPT: %llu\n",
+		fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n",
 			(unsigned long long) arg->unique);
 
 	req->u.i.unique = arg->unique;
@@ -1867,6 +1877,20 @@
 		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;
@@ -1877,10 +1901,10 @@
 
 	(void) nodeid;
 	if (se->debug) {
-		fprintf(stderr, "INIT: %u.%u\n", arg->major, arg->minor);
+		fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
 		if (arg->major == 7 && arg->minor >= 6) {
-			fprintf(stderr, "flags=0x%08x\n", arg->flags);
-			fprintf(stderr, "max_readahead=0x%08x\n",
+			fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags);
+			fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n",
 				arg->max_readahead);
 		}
 	}
@@ -1894,7 +1918,7 @@
 	outarg.minor = FUSE_KERNEL_MINOR_VERSION;
 
 	if (arg->major < 7) {
-		fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n",
 			arg->major, arg->minor);
 		fuse_reply_err(req, EPROTO);
 		return;
@@ -1991,7 +2015,7 @@
 	se->conn.time_gran = 1;
 	
 	if (bufsize < FUSE_MIN_READ_BUFFER) {
-		fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n",
 			bufsize);
 		bufsize = FUSE_MIN_READ_BUFFER;
 	}
@@ -2005,7 +2029,7 @@
 		se->op.init(se->userdata, &se->conn);
 
 	if (se->conn.want & (~se->conn.capable)) {
-		fprintf(stderr, "fuse: error: filesystem requested capabilities "
+		fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabilities "
 			"0x%x that are not supported by kernel, aborting.\n",
 			se->conn.want & (~se->conn.capable));
 		fuse_reply_err(req, EPROTO);
@@ -2016,7 +2040,7 @@
 
 	unsigned max_read_mo = get_max_read(se->mo);
 	if (se->conn.max_read != max_read_mo) {
-		fprintf(stderr, "fuse: error: init() and fuse_session_new() "
+		fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() "
 			"requested different maximum read size (%u vs %u)\n",
 			se->conn.max_read, max_read_mo);
 		fuse_reply_err(req, EPROTO);
@@ -2080,16 +2104,16 @@
 		outarg.time_gran = se->conn.time_gran;
 
 	if (se->debug) {
-		fprintf(stderr, "   INIT: %u.%u\n", outarg.major, outarg.minor);
-		fprintf(stderr, "   flags=0x%08x\n", outarg.flags);
-		fprintf(stderr, "   max_readahead=0x%08x\n",
+		fuse_log(FUSE_LOG_DEBUG, "   INIT: %u.%u\n", outarg.major, outarg.minor);
+		fuse_log(FUSE_LOG_DEBUG, "   flags=0x%08x\n", outarg.flags);
+		fuse_log(FUSE_LOG_DEBUG, "   max_readahead=0x%08x\n",
 			outarg.max_readahead);
-		fprintf(stderr, "   max_write=0x%08x\n", outarg.max_write);
-		fprintf(stderr, "   max_background=%i\n",
+		fuse_log(FUSE_LOG_DEBUG, "   max_write=0x%08x\n", outarg.max_write);
+		fuse_log(FUSE_LOG_DEBUG, "   max_background=%i\n",
 			outarg.max_background);
-		fprintf(stderr, "   congestion_threshold=%i\n",
+		fuse_log(FUSE_LOG_DEBUG, "   congestion_threshold=%i\n",
 			outarg.congestion_threshold);
-		fprintf(stderr, "   time_gran=%u\n",
+		fuse_log(FUSE_LOG_DEBUG, "   time_gran=%u\n",
 			outarg.time_gran);
 	}
 	if (arg->minor < 5)
@@ -2326,7 +2350,7 @@
 		sizeof(struct fuse_notify_retrieve_in);
 
 	if (bufv.buf[0].size < arg->size) {
-		fprintf(stderr, "fuse: retrieve reply: buffer size too small\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n");
 		fuse_reply_none(req);
 		goto out;
 	}
@@ -2471,6 +2495,7 @@
 	[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"   },
 };
 
@@ -2489,11 +2514,11 @@
 {
 	ssize_t res = fuse_buf_copy(dst, src, 0);
 	if (res < 0) {
-		fprintf(stderr, "fuse: copy from pipe: %s\n", strerror(-res));
+		fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res));
 		return res;
 	}
 	if ((size_t)res < fuse_buf_size(dst)) {
-		fprintf(stderr, "fuse: copy from pipe: short read\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
 		return -1;
 	}
 	return 0;
@@ -2525,7 +2550,7 @@
 
 		mbuf = malloc(tmpbuf.buf[0].size);
 		if (mbuf == NULL) {
-			fprintf(stderr, "fuse: failed to allocate header\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n");
 			goto clear_pipe;
 		}
 		tmpbuf.buf[0].mem = mbuf;
@@ -2540,7 +2565,7 @@
 	}
 
 	if (se->debug) {
-		fprintf(stderr,
+		fuse_log(FUSE_LOG_DEBUG,
 			"unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n",
 			(unsigned long long) in->unique,
 			opname((enum fuse_opcode) in->opcode), in->opcode,
@@ -2757,7 +2782,7 @@
 	}
 
 	if (res < sizeof(struct fuse_in_header)) {
-		fprintf(stderr, "short splice from fuse device\n");
+		fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n");
 		return -EIO;
 	}
 
@@ -2780,7 +2805,7 @@
 		if (!buf->mem) {
 			buf->mem = malloc(se->bufsize);
 			if (!buf->mem) {
-				fprintf(stderr,
+				fuse_log(FUSE_LOG_ERR,
 					"fuse: failed to allocate read buffer\n");
 				return -ENOMEM;
 			}
@@ -2791,13 +2816,13 @@
 
 		res = fuse_buf_copy(&dst, &src, 0);
 		if (res < 0) {
-			fprintf(stderr, "fuse: copy from pipe: %s\n",
+			fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n",
 				strerror(-res));
 			fuse_ll_clear_pipe(se);
 			return res;
 		}
 		if (res < tmpbuf.size) {
-			fprintf(stderr, "fuse: copy from pipe: short read\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n");
 			fuse_ll_clear_pipe(se);
 			return -EIO;
 		}
@@ -2817,7 +2842,7 @@
 	if (!buf->mem) {
 		buf->mem = malloc(se->bufsize);
 		if (!buf->mem) {
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_ERR,
 				"fuse: failed to allocate read buffer\n");
 			return -ENOMEM;
 		}
@@ -2849,7 +2874,7 @@
 		return -err;
 	}
 	if ((size_t) res < sizeof(struct fuse_in_header)) {
-		fprintf(stderr, "short read on fuse device\n");
+		fuse_log(FUSE_LOG_ERR, "short read on fuse device\n");
 		return -EIO;
 	}
 
@@ -2867,18 +2892,18 @@
 	struct mount_opts *mo;
 
 	if (sizeof(struct fuse_lowlevel_ops) < op_size) {
-		fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n");
 		op_size = sizeof(struct fuse_lowlevel_ops);
 	}
 
 	if (args->argc == 0) {
-		fprintf(stderr, "fuse: empty argv passed to fuse_session_new().\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n");
 		return NULL;
 	}
 
 	se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session));
 	if (se == NULL) {
-		fprintf(stderr, "fuse: failed to allocate fuse object\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n");
 		goto out1;
 	}
 	se->fd = -1;
@@ -2904,19 +2929,19 @@
 
 	if(args->argc == 1 &&
 	   args->argv[0][0] == '-') {
-		fprintf(stderr, "fuse: warning: argv[0] looks like an option, but "
+		fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but "
 			"will be ignored\n");
 	} else if (args->argc != 1) {
 		int i;
-		fprintf(stderr, "fuse: unknown option(s): `");
+		fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `");
 		for(i = 1; i < args->argc-1; i++)
-			fprintf(stderr, "%s ", args->argv[i]);
-		fprintf(stderr, "%s'\n", args->argv[i]);
+			fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]);
+		fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]);
 		goto out4;
 	}
 
 	if (se->debug)
-		fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
+		fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION);
 
 	se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() +
 		FUSE_BUFFER_HEADER_SIZE;
@@ -2929,7 +2954,7 @@
 
 	err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor);
 	if (err) {
-		fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n",
 			strerror(err));
 		goto out5;
 	}
@@ -2976,7 +3001,7 @@
 	fd = fuse_mnt_parse_fuse_fd(mountpoint);
 	if (fd != -1) {
 		if (fcntl(fd, F_GETFD) == -1) {
-			fprintf(stderr,
+			fuse_log(FUSE_LOG_ERR,
 				"fuse: Invalid file descriptor /dev/fd/%u\n",
 				fd);
 			return -1;
diff --git a/lib/fuse_opt.c b/lib/fuse_opt.c
index f6cae4f..93066b9 100644
--- a/lib/fuse_opt.c
+++ b/lib/fuse_opt.c
@@ -10,6 +10,7 @@
 */
 
 #include "config.h"
+#include "fuse_i.h"
 #include "fuse_opt.h"
 #include "fuse_misc.h"
 
@@ -47,7 +48,7 @@
 
 static int alloc_failed(void)
 {
-	fprintf(stderr, "fuse: memory allocation failed\n");
+	fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
 	return -1;
 }
 
@@ -99,7 +100,7 @@
 static int next_arg(struct fuse_opt_context *ctx, const char *opt)
 {
 	if (ctx->argctr + 1 >= ctx->argc) {
-		fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
+		fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
 		return -1;
 	}
 	ctx->argctr++;
@@ -217,7 +218,7 @@
 		*s = copy;
 	} else {
 		if (sscanf(param, format, var) != 1) {
-			fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
+			fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg);
 			return -1;
 		}
 	}
@@ -336,7 +337,7 @@
 	char *copy = strdup(opts);
 
 	if (!copy) {
-		fprintf(stderr, "fuse: memory allocation failed\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
 		return -1;
 	}
 	res = process_real_option_group(ctx, copy);
diff --git a/lib/fuse_signals.c b/lib/fuse_signals.c
index e6f7112..4271947 100644
--- a/lib/fuse_signals.c
+++ b/lib/fuse_signals.c
@@ -24,7 +24,7 @@
 	if (fuse_instance) {
 		fuse_session_exit(fuse_instance);
 		if(sig <= 0) {
-			fprintf(stderr, "assertion error: signal value <= 0\n");
+			fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n");
 			abort();
 		}
 		fuse_instance->error = sig;
@@ -79,7 +79,7 @@
 void fuse_remove_signal_handlers(struct fuse_session *se)
 {
 	if (fuse_instance != se)
-		fprintf(stderr,
+		fuse_log(FUSE_LOG_ERR,
 			"fuse: fuse_remove_signal_handlers: unknown session\n");
 	else
 		fuse_instance = NULL;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 141deca..d18ba29 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -126,6 +126,8 @@
 		fuse_cmdline_help;
 		fuse_apply_conn_info_opts;
 		fuse_parse_conn_info_opts;
+		fuse_fs_lseek;
+		fuse_reply_lseek;
 
 	local:
 		*;
@@ -157,6 +159,12 @@
 		fuse_fs_copy_file_range;
 } FUSE_3.3;
 
+FUSE_3.7 {
+	global:
+		fuse_set_log_func;
+		fuse_log;
+} FUSE_3.3;
+
 # Local Variables:
 # indent-tabs-mode: t
 # End:
diff --git a/lib/helper.c b/lib/helper.c
index 5b80c6e..64ff7ad 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -154,14 +154,14 @@
 
 			char mountpoint[PATH_MAX] = "";
 			if (realpath(arg, mountpoint) == NULL) {
-				fprintf(stderr,
+				fuse_log(FUSE_LOG_ERR,
 					"fuse: bad mount point `%s': %s\n",
 					arg, strerror(errno));
 				return -1;
 			}
 			return fuse_opt_add_opt(&opts->mountpoint, mountpoint);
 		} else {
-			fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
+			fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg);
 			return -1;
 		}
 
@@ -186,7 +186,7 @@
 
 	subtype_opt = (char *) malloc(strlen(basename) + 64);
 	if (subtype_opt == NULL) {
-		fprintf(stderr, "fuse: memory allocation failed\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
 		return -1;
 	}
 #ifdef __FreeBSD__
@@ -307,7 +307,7 @@
 
 	if (!opts.show_help &&
 	    !opts.mountpoint) {
-		fprintf(stderr, "error: no mountpoint specified\n");
+		fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n");
 		res = 2;
 		goto out1;
 	}
@@ -411,7 +411,7 @@
 
 	opts = calloc(1, sizeof(struct fuse_conn_info_opts));
 	if(opts == NULL) {
-		fprintf(stderr, "calloc failed\n");
+		fuse_log(FUSE_LOG_ERR, "calloc failed\n");
 		return NULL;
 	}
 	if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) {
diff --git a/lib/meson.build b/lib/meson.build
index e27feab..28f0aee 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -1,7 +1,8 @@
 libfuse_sources = ['fuse.c', 'fuse_i.h', 'fuse_loop.c', 'fuse_loop_mt.c',
                    'fuse_lowlevel.c', 'fuse_misc.h', 'fuse_opt.c',
                    'fuse_signals.c', 'buffer.c', 'cuse_lowlevel.c',
-                   'helper.c', 'modules/subdir.c', 'mount_util.c' ]
+                   'helper.c', 'modules/subdir.c', 'mount_util.c',
+                   'fuse_log.c' ]
 
 if host_machine.system().startswith('linux')
    libfuse_sources += [ 'mount.c' ]
diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c
index 84f4236..eb5edd8 100644
--- a/lib/modules/iconv.c
+++ b/lib/modules/iconv.c
@@ -554,6 +554,19 @@
 	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 @@
 	.lock		= iconv_lock,
 	.flock		= iconv_flock,
 	.bmap		= iconv_bmap,
+	.lseek		= iconv_lseek,
 };
 
 static const struct fuse_opt iconv_opts[] = {
@@ -659,7 +673,7 @@
 
 	ic = calloc(1, sizeof(struct iconv));
 	if (ic == NULL) {
-		fprintf(stderr, "fuse-iconv: memory allocation failed\n");
+		fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n");
 		return NULL;
 	}
 
@@ -667,7 +681,7 @@
 		goto out_free;
 
 	if (!next[0] || next[1]) {
-		fprintf(stderr, "fuse-iconv: exactly one next filesystem required\n");
+		fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n");
 		goto out_free;
 	}
 
@@ -678,13 +692,13 @@
 		old = strdup(setlocale(LC_CTYPE, ""));
 	ic->tofs = iconv_open(from, to);
 	if (ic->tofs == (iconv_t) -1) {
-		fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
 			to, from);
 		goto out_free;
 	}
 	ic->fromfs = iconv_open(to, from);
 	if (ic->tofs == (iconv_t) -1) {
-		fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
 			from, to);
 		goto out_iconv_close_to;
 	}
diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c
index f84d8c1..616c0ee 100644
--- a/lib/modules/subdir.c
+++ b/lib/modules/subdir.c
@@ -540,6 +540,19 @@
 	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 @@
 	.lock		= subdir_lock,
 	.flock		= subdir_flock,
 	.bmap		= subdir_bmap,
+	.lseek		= subdir_lseek,
 };
 
 static const struct fuse_opt subdir_opts[] = {
@@ -633,7 +647,7 @@
 
 	d = calloc(1, sizeof(struct subdir));
 	if (d == NULL) {
-		fprintf(stderr, "fuse-subdir: memory allocation failed\n");
+		fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
 		return NULL;
 	}
 
@@ -641,19 +655,19 @@
 		goto out_free;
 
 	if (!next[0] || next[1]) {
-		fprintf(stderr, "fuse-subdir: exactly one next filesystem required\n");
+		fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
 		goto out_free;
 	}
 
 	if (!d->base) {
-		fprintf(stderr, "fuse-subdir: missing 'subdir' option\n");
+		fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
 		goto out_free;
 	}
 
 	if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
 		char *tmp = realloc(d->base, strlen(d->base) + 2);
 		if (!tmp) {
-			fprintf(stderr, "fuse-subdir: memory allocation failed\n");
+			fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
 			goto out_free;
 		}
 		d->base = tmp;
diff --git a/lib/mount.c b/lib/mount.c
index 7a18c11..979f8d9 100644
--- a/lib/mount.c
+++ b/lib/mount.c
@@ -175,7 +175,7 @@
 			return;
 		}
 	}
-	fprintf(stderr, "fuse: internal error, can't find mount flag\n");
+	fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n");
 	abort();
 }
 
@@ -248,7 +248,7 @@
 
 	cmsg = CMSG_FIRSTHDR(&msg);
 	if (cmsg->cmsg_type != SCM_RIGHTS) {
-		fprintf(stderr, "got control message of unknown type %d\n",
+		fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n",
 			cmsg->cmsg_type);
 		return -1;
 	}
@@ -312,7 +312,7 @@
 	int rv;
 
 	if (!mountpoint) {
-		fprintf(stderr, "fuse: missing mountpoint parameter\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
 		return -1;
 	}
 
@@ -393,13 +393,13 @@
 	int res;
 
 	if (!mnt) {
-		fprintf(stderr, "fuse: missing mountpoint parameter\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
 		return -1;
 	}
 
 	res = stat(mnt, &stbuf);
 	if (res == -1) {
-		fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
 			mnt, strerror(errno));
 		return -1;
 	}
@@ -413,9 +413,9 @@
 	fd = open(devname, O_RDWR | O_CLOEXEC);
 	if (fd == -1) {
 		if (errno == ENODEV || errno == ENOENT)
-			fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
+			fuse_log(FUSE_LOG_ERR, "fuse: device not found, try 'modprobe fuse' first\n");
 		else
-			fprintf(stderr, "fuse: failed to open %s: %s\n",
+			fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
 				devname, strerror(errno));
 		return -1;
 	}
@@ -435,7 +435,7 @@
 
 	type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
 	if (!type || !source) {
-		fprintf(stderr, "fuse: failed to allocate memory\n");
+		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
 		goto out_close;
 	}
 
@@ -471,10 +471,10 @@
 			int errno_save = errno;
 			if (mo->blkdev && errno == ENODEV &&
 			    !fuse_mnt_check_fuseblk())
-				fprintf(stderr,
+				fuse_log(FUSE_LOG_ERR,
 					"fuse: 'fuseblk' support missing\n");
 			else
-				fprintf(stderr, "fuse: mount failed: %s\n",
+				fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
 					strerror(errno_save));
 		}
 
diff --git a/lib/mount_bsd.c b/lib/mount_bsd.c
index cbd3ced..35f3634 100644
--- a/lib/mount_bsd.c
+++ b/lib/mount_bsd.c
@@ -86,6 +86,9 @@
 	FUSE_DUAL_OPT_KEY("neglect_shares",	KEY_KERN),
 	FUSE_DUAL_OPT_KEY("push_symlinks_in",	KEY_KERN),
 	FUSE_OPT_KEY("nosync_unmount",		KEY_KERN),
+#if __FreeBSD_version >= 1200519
+	FUSE_DUAL_OPT_KEY("intr",		KEY_KERN),
+#endif
 	/* stock FBSD mountopt parsing routine lets anything be negated... */
 	/*
 	 * Linux specific mount options, but let just the mount util
@@ -161,7 +164,7 @@
 		fd = strtol(fdnam, &ep, 10);
 
 		if (*ep != '\0') {
-			fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
+			fuse_log(FUSE_LOG_ERR, "invalid value given in FUSE_DEV_FD\n");
 			return -1;
 		}
 
diff --git a/meson.build b/meson.build
index f101d82..0f1a77f 100644
--- a/meson.build
+++ b/meson.build
@@ -1,4 +1,4 @@
-project('libfuse3', ['cpp', 'c'], version: '3.6.2',
+project('libfuse3', ['cpp', 'c'], version: '3.8.0',
         meson_version: '>= 0.42',
         default_options: [ 'buildtype=debugoptimized' ])