lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <175573712879.20753.13907286537445374639.stgit@frogsfrogsfrogs>
Date: Wed, 20 Aug 2025 18:08:55 -0700
From: "Darrick J. Wong" <djwong@...nel.org>
To: tytso@....edu
Cc: amir73il@...il.com, John@...ves.net, bernd@...ernd.com,
 linux-fsdevel@...r.kernel.org, linux-ext4@...r.kernel.org, miklos@...redi.hu,
 amir73il@...il.com, joannelkoong@...il.com, neal@...pa.dev
Subject: [PATCH 04/20] fuse4fs: convert to low level API

From: Darrick J. Wong <djwong@...nel.org>

Convert fuse4fs to the lowlevel fuse API.  Amir supplied the auto
translation; I ported and cleaned it up by hand, and did the QA work to
make sure it still runs correctly.

Co-developed-by: Claude claude-4-sonnet
Signed-off-by: Amir Goldstein <amir73il@...il.com>
Signed-off-by: "Darrick J. Wong" <djwong@...nel.org>
---
 misc/fuse4fs.c | 2005 ++++++++++++++++++++++++++++++--------------------------
 1 file changed, 1073 insertions(+), 932 deletions(-)


diff --git a/misc/fuse4fs.c b/misc/fuse4fs.c
index 124a16eb0614a8..0dd47dcf18d77a 100644
--- a/misc/fuse4fs.c
+++ b/misc/fuse4fs.c
@@ -41,7 +41,7 @@
 # define __SET_FOB_FOR_FUSE
 # define _FILE_OFFSET_BITS 64
 #endif /* _FILE_OFFSET_BITS */
-#include <fuse.h>
+#include <fuse_lowlevel.h>
 #ifdef __SET_FOB_FOR_FUSE
 # undef _FILE_OFFSET_BITS
 #endif /* __SET_FOB_FOR_FUSE */
@@ -116,6 +116,8 @@
 #endif
 #endif /* !defined(ENODATA) */
 
+#define FUSE4FS_ATTR_TIMEOUT	(0.0)
+
 static inline uint64_t round_up(uint64_t b, unsigned int align)
 {
 	unsigned int m;
@@ -249,16 +251,18 @@ struct fuse4fs {
 	struct timespec op_start_time;
 	uint8_t timing;
 #endif
+	struct fuse_session *fuse;
 };
 
-#define FUSE4FS_CHECK_HANDLE(ff, fh) \
+#define FUSE4FS_CHECK_HANDLE(req, fh) \
 	do { \
 		if ((fh) == NULL || (fh)->magic != FUSE4FS_FILE_MAGIC) { \
 			fprintf(stderr, \
 				"FUSE4FS: Corrupt in-memory file handle at %s:%d!\n", \
 				__func__, __LINE__); \
 			fflush(stderr); \
-			return -EUCLEAN; \
+			fuse_reply_err(req, EUCLEAN); \
+			return; \
 		} \
 	} while (0)
 
@@ -270,19 +274,52 @@ struct fuse4fs {
 				__func__, __LINE__); \
 			fflush(stderr); \
 			retcode; \
+			return; \
 		} \
 		if ((ff)->opstate == F4OP_SHUTDOWN) { \
 			shutcode; \
+			return; \
 		} \
 	} while (0)
 
-#define FUSE4FS_CHECK_CONTEXT(ff) \
-	__FUSE4FS_CHECK_CONTEXT((ff), return -EUCLEAN, return -EIO)
+#define FUSE4FS_CHECK_CONTEXT(req) \
+	__FUSE4FS_CHECK_CONTEXT(fuse4fs_get(req), \
+				fuse_reply_err((req), EUCLEAN), \
+				fuse_reply_err((req), EIO))
 #define FUSE4FS_CHECK_CONTEXT_RETURN(ff) \
 	__FUSE4FS_CHECK_CONTEXT((ff), return, return)
 #define FUSE4FS_CHECK_CONTEXT_ABORT(ff) \
 	__FUSE4FS_CHECK_CONTEXT((ff), abort(), abort())
 
+static inline void fuse4fs_ino_from_fuse(ext2_ino_t *inop, fuse_ino_t fino)
+{
+	if (fino == FUSE_ROOT_ID)
+		*inop = EXT2_ROOT_INO;
+	else
+		*inop = fino;
+}
+
+static inline void fuse4fs_ino_to_fuse(fuse_ino_t *finop, ext2_ino_t ino)
+{
+	if (ino == EXT2_ROOT_INO)
+		*finop = FUSE_ROOT_ID;
+	else
+		*finop = ino;
+}
+
+#define FUSE4FS_CONVERT_FINO(req, ext2_inop, fuse_ino) \
+	do { \
+		if ((fuse_ino) > UINT32_MAX) { \
+			fprintf(stderr, \
+				"FUSE4FS: Bogus inode number 0x%llx at %s:%d!\n", \
+				(unsigned long long)(fuse_ino), __func__, __LINE__); \
+			fflush(stderr); \
+			fuse_reply_err((req), EIO); \
+			return; \
+		} \
+		fuse4fs_ino_from_fuse(ext2_inop, fuse_ino); \
+	} while (0)
+
 static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err,
 			     const char *func, int line);
 #define translate_error(fs, ino, err) __translate_error((fs), (ino), (err), \
@@ -449,11 +486,9 @@ static inline errcode_t fuse4fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
 				       sizeof(*inode));
 }
 
-static inline struct fuse4fs *fuse4fs_get(void)
+static inline struct fuse4fs *fuse4fs_get(fuse_req_t req)
 {
-	struct fuse_context *ctxt = fuse_get_context();
-
-	return ctxt->private_data;
+	return (struct fuse4fs *)fuse_req_userdata(req);
 }
 
 static inline struct fuse4fs_file_handle *
@@ -466,6 +501,7 @@ static inline void
 fuse4fs_set_handle(struct fuse_file_info *fp, struct fuse4fs_file_handle *fh)
 {
 	fp->fh = (uintptr_t)fh;
+	fp->keep_cache = 1;
 }
 
 #ifdef HAVE_CLOCK_MONOTONIC
@@ -726,7 +762,7 @@ static int fuse4fs_is_writeable(const struct fuse4fs *ff)
 }
 
 static inline int fuse4fs_is_superuser(struct fuse4fs *ff,
-				       const struct fuse_context *ctxt)
+				       const struct fuse_ctx *ctxt)
 {
 	if (ff->fakeroot)
 		return 1;
@@ -734,7 +770,7 @@ static inline int fuse4fs_is_superuser(struct fuse4fs *ff,
 }
 
 static inline int fuse4fs_want_check_owner(struct fuse4fs *ff,
-					   const struct fuse_context *ctxt)
+					   const struct fuse_ctx *ctxt)
 {
 	/*
 	 * The kernel is responsible for access control, so we allow anything
@@ -777,9 +813,9 @@ static int fuse4fs_iflags_access(struct fuse4fs *ff, ext2_ino_t ino,
 	return 0;
 }
 
-static int fuse4fs_inum_access(struct fuse4fs *ff, ext2_ino_t ino, int mask)
+static int fuse4fs_inum_access(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			       ext2_ino_t ino, int mask)
 {
-	struct fuse_context *ctxt = fuse_get_context();
 	ext2_filsys fs = ff->fs;
 	struct ext2_inode inode;
 	mode_t perms;
@@ -1114,9 +1150,9 @@ static errcode_t fuse4fs_mount(struct fuse4fs *ff)
 	return 0;
 }
 
-static void op_destroy(void *p EXT2FS_ATTR((unused)))
+static void op_destroy(void *userdata)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = userdata;
 	ext2_filsys fs;
 	errcode_t err;
 
@@ -1283,24 +1319,13 @@ static inline int fuse_set_feature_flag(struct fuse_conn_info *conn,
 }
 #endif
 
-static void *op_init(struct fuse_conn_info *conn,
-		     struct fuse_config *cfg EXT2FS_ATTR((unused)))
+static void op_init(void *userdata, struct fuse_conn_info *conn)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = userdata;
 	ext2_filsys fs;
 
 	FUSE4FS_CHECK_CONTEXT_ABORT(ff);
 
-	/*
-	 * Configure logging a second time, because libfuse might have
-	 * redirected std{out,err} as part of daemonization.  If this fails,
-	 * give up and move on.
-	 */
-	fuse4fs_setup_logging(ff);
-	if (ff->logfd >= 0)
-		close(ff->logfd);
-	ff->logfd = -1;
-
 	fs = ff->fs;
 	dbg_printf(ff, "%s: dev=%s\n", __func__, fs->device_name);
 #ifdef FUSE_CAP_IOCTL_DIR
@@ -1317,10 +1342,6 @@ static void *op_init(struct fuse_conn_info *conn,
 	fuse_set_feature_flag(conn, FUSE_CAP_NO_EXPORT_SUPPORT);
 #endif
 	conn->time_gran = 1;
-	cfg->use_ino = 1;
-	if (ff->debug)
-		cfg->debug = 1;
-	cfg->nullpath_ok = 1;
 
 	if (ff->kernel) {
 		char uuid[UUID_STR_SIZE];
@@ -1342,132 +1363,151 @@ static void *op_init(struct fuse_conn_info *conn,
 	 */
 	conn->want = conn->want_ext & 0xFFFFFFFF;
 #endif
-	return ff;
 }
 
-static int stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *statbuf)
+struct fuse4fs_stat {
+	struct fuse_entry_param	entry;
+};
+
+static int fuse4fs_stat_inode(struct fuse4fs *ff, ext2_ino_t ino,
+			      struct ext2_inode_large *inodep,
+			      struct fuse4fs_stat *fstat)
 {
 	struct ext2_inode_large inode;
+	ext2_filsys fs = ff->fs;
+	struct fuse_entry_param *entry = &fstat->entry;
+	struct stat *statbuf = &entry->attr;
 	dev_t fakedev = 0;
 	errcode_t err;
-	int ret = 0;
 	struct timespec tv;
 
-	err = fuse4fs_read_inode(fs, ino, &inode);
-	if (err)
-		return translate_error(fs, ino, err);
+	memset(fstat, 0, sizeof(*fstat));
+
+	if (!inodep) {
+		err = fuse4fs_read_inode(fs, ino, &inode);
+		if (err)
+			return translate_error(fs, ino, err);
+		inodep = &inode;
+	}
 
 	memcpy(&fakedev, fs->super->s_uuid, sizeof(fakedev));
 	statbuf->st_dev = fakedev;
 	statbuf->st_ino = ino;
-	statbuf->st_mode = inode.i_mode;
-	statbuf->st_nlink = inode.i_links_count;
-	statbuf->st_uid = inode_uid(inode);
-	statbuf->st_gid = inode_gid(inode);
-	statbuf->st_size = EXT2_I_SIZE(&inode);
+	statbuf->st_mode = inodep->i_mode;
+	statbuf->st_nlink = inodep->i_links_count;
+	statbuf->st_uid = inode_uid(*inodep);
+	statbuf->st_gid = inode_gid(*inodep);
+	statbuf->st_size = EXT2_I_SIZE(inodep);
 	statbuf->st_blksize = fs->blocksize;
 	statbuf->st_blocks = ext2fs_get_stat_i_blocks(fs,
-						EXT2_INODE(&inode));
-	EXT4_INODE_GET_XTIME(i_atime, &tv, &inode);
+						EXT2_INODE(inodep));
+	EXT4_INODE_GET_XTIME(i_atime, &tv, inodep);
 #if HAVE_STRUCT_STAT_ST_ATIM
 	statbuf->st_atim = tv;
 #else
 	statbuf->st_atime = tv.tv_sec;
 #endif
-	EXT4_INODE_GET_XTIME(i_mtime, &tv, &inode);
+	EXT4_INODE_GET_XTIME(i_mtime, &tv, inodep);
 #if HAVE_STRUCT_STAT_ST_ATIM
 	statbuf->st_mtim = tv;
 #else
 	statbuf->st_mtime = tv.tv_sec;
 #endif
-	EXT4_INODE_GET_XTIME(i_ctime, &tv, &inode);
+	EXT4_INODE_GET_XTIME(i_ctime, &tv, inodep);
 #if HAVE_STRUCT_STAT_ST_ATIM
 	statbuf->st_ctim = tv;
 #else
 	statbuf->st_ctime = tv.tv_sec;
 #endif
-	if (LINUX_S_ISCHR(inode.i_mode) ||
-	    LINUX_S_ISBLK(inode.i_mode)) {
-		if (inode.i_block[0])
-			statbuf->st_rdev = inode.i_block[0];
+	if (LINUX_S_ISCHR(inodep->i_mode) ||
+	    LINUX_S_ISBLK(inodep->i_mode)) {
+		if (inodep->i_block[0])
+			statbuf->st_rdev = inodep->i_block[0];
 		else
-			statbuf->st_rdev = inode.i_block[1];
+			statbuf->st_rdev = inodep->i_block[1];
 	}
 
-	return ret;
-}
-
-static int __fuse4fs_file_ino(struct fuse4fs *ff, const char *path,
-			      struct fuse_file_info *fp EXT2FS_ATTR((unused)),
-			      ext2_ino_t *inop,
-			      const char *func,
-			      int line)
-{
-	ext2_filsys fs = ff->fs;
-	errcode_t err;
-
-	if (fp) {
-		struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
-
-		if (fh->ino == 0)
-			return -ESTALE;
-
-		*inop = fh->ino;
-		dbg_printf(ff, "%s: get ino=%d\n", func, fh->ino);
-		return 0;
-	}
-
-	dbg_printf(ff, "%s: get path=%s\n", func, path);
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, inop);
-	if (err)
-		return __translate_error(fs, 0, err, func, line);
+	fuse4fs_ino_to_fuse(&entry->ino, ino);
+	entry->generation = inodep->i_generation;
+	entry->attr_timeout = FUSE4FS_ATTR_TIMEOUT;
+	entry->entry_timeout = FUSE4FS_ATTR_TIMEOUT;
 
 	return 0;
 }
 
-# define fuse4fs_file_ino(ff, path, fp, inop) \
-	__fuse4fs_file_ino((ff), (path), (fp), (inop), __func__, __LINE__)
-
-static int op_getattr(const char *path, struct stat *statbuf,
-		      struct fuse_file_info *fi)
+static void op_lookup(fuse_req_t req, fuse_ino_t fino, const char *name)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs_stat fstat;
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
-	ext2_ino_t ino;
+	ext2_ino_t parent, child;
+	errcode_t err;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, fino);
+	dbg_printf(ff, "%s: parent=%d name='%s'\n", __func__, parent, name);
 	fs = fuse4fs_start(ff);
-	ret = fuse4fs_file_ino(ff, path, fi, &ino);
+
+	err = ext2fs_namei(fs, EXT2_ROOT_INO, parent, name, &child);
+	if (err || child == 0) {
+		ret = translate_error(fs, 0, err);
+		goto out;
+	}
+
+	ret = fuse4fs_stat_inode(ff, child, NULL, &fstat);
 	if (ret)
 		goto out;
-	ret = stat_inode(fs, ino, statbuf);
+
 out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_entry(req, &fstat.entry);
 }
 
-static int op_readlink(const char *path, char *buf, size_t len)
+static void op_getattr(fuse_req_t req, fuse_ino_t fino,
+		       struct fuse_file_info *fi EXT2FS_ATTR((unused)))
 {
-	struct fuse4fs *ff = fuse4fs_get();
-	ext2_filsys fs;
-	errcode_t err;
+	struct fuse4fs_stat fstat;
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_ino_t ino;
+	int ret = 0;
+
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
+	fuse4fs_start(ff);
+	ret = fuse4fs_stat_inode(ff, ino, NULL, &fstat);
+	fuse4fs_finish(ff, ret);
+
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_attr(req, &fstat.entry.attr,
+				fstat.entry.attr_timeout);
+}
+
+static void op_readlink(fuse_req_t req, fuse_ino_t fino)
+{
 	struct ext2_inode inode;
+	char buf[PATH_MAX + 1];
+	struct fuse4fs *ff = fuse4fs_get(req);
+	ext2_filsys fs;
+	ext2_file_t file;
+	errcode_t err;
+	ext2_ino_t ino;
+	size_t len = PATH_MAX;
 	unsigned int got;
-	ext2_file_t file;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: path=%s\n", __func__, path);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
+	dbg_printf(ff, "%s: ino=%d\n", __func__, ino);
 	fs = fuse4fs_start(ff);
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
-	if (err || ino == 0) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
 
-	err = ext2fs_read_inode(fs, ino, &inode);
+	err = ext2fs_read_inode(fs, fino, &inode);
 	if (err) {
 		ret = translate_error(fs, ino, err);
 		goto out;
@@ -1478,7 +1518,6 @@ static int op_readlink(const char *path, char *buf, size_t len)
 		goto out;
 	}
 
-	len--;
 	if (inode.i_size < len)
 		len = inode.i_size;
 	if (ext2fs_is_fast_symlink(&inode))
@@ -1516,7 +1555,11 @@ static int op_readlink(const char *path, char *buf, size_t len)
 
 out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_readlink(req, buf);
 }
 
 static int fuse4fs_getxattr(struct fuse4fs *ff, ext2_ino_t ino,
@@ -1622,11 +1665,12 @@ static inline void fuse4fs_set_gid(struct ext2_inode_large *inode, gid_t gid)
 	ext2fs_set_i_gid_high(*inode, gid >> 16);
 }
 
-static int fuse4fs_new_child_gid(struct fuse4fs *ff, ext2_ino_t parent,
-				 gid_t *gid, int *parent_sgid)
+static int fuse4fs_new_child_gid(struct fuse4fs *ff,
+				 const struct fuse_ctx *ctxt,
+				 ext2_ino_t parent, gid_t *gid,
+				 int *parent_sgid)
 {
 	struct ext2_inode_large inode;
-	struct fuse_context *ctxt = fuse_get_context();
 	errcode_t err;
 
 	err = fuse4fs_read_inode(ff->fs, parent, &inode);
@@ -1702,36 +1746,44 @@ static void fuse4fs_set_extra_isize(struct fuse4fs *ff, ext2_ino_t ino,
 	inode->i_extra_isize = extra;
 }
 
-static int op_mknod(const char *path, mode_t mode, dev_t dev)
+static void fuse4fs_reply_entry(fuse_req_t req, ext2_ino_t ino,
+				struct ext2_inode_large *inode, int ret)
 {
-	struct fuse_context *ctxt = fuse_get_context();
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs_stat fstat;
+	struct fuse4fs *ff = fuse4fs_get(req);
+
+	if (ret) {
+		fuse_reply_err(req, -ret);
+		return;
+	}
+
+	/* Get stat info for the new entry */
+	ret = fuse4fs_stat_inode(ff, ino, inode, &fstat);
+	if (ret) {
+		fuse_reply_err(req, -ret);
+		return;
+	}
+
+	fuse_reply_entry(req, &fstat.entry);
+}
+
+static void op_mknod(fuse_req_t req, fuse_ino_t fino, const char *name,
+		     mode_t mode, dev_t dev)
+{
+	struct ext2_inode_large inode;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	ext2_ino_t parent, child;
-	char *temp_path;
 	errcode_t err;
-	char *node_name, a;
 	int filetype;
-	struct ext2_inode_large inode;
 	gid_t gid;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: path=%s mode=0%o dev=0x%x\n", __func__, path, mode,
-		   (unsigned int)dev);
-	temp_path = strdup(path);
-	if (!temp_path) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name = strrchr(temp_path, '/');
-	if (!node_name) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name++;
-	a = *node_name;
-	*node_name = 0;
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, fino);
+	dbg_printf(ff, "%s: parent=%d name='%s' mode=0%o dev=0x%x\n",
+		   __func__, parent, name, mode, (unsigned int)dev);
 
 	fs = fuse4fs_start(ff);
 	if (!fuse4fs_can_allocate(ff, 2)) {
@@ -1739,33 +1791,14 @@ static int op_mknod(const char *path, mode_t mode, dev_t dev)
 		goto out2;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
-			   &parent);
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out2;
-	}
-
-	ret = fuse4fs_inum_access(ff, parent, A_OK | W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, parent, A_OK | W_OK);
 	if (ret)
 		goto out2;
 
-	*node_name = a;
+	/* On a low level server, mknod handles all non-directory types */
+	filetype = ext2_file_type(mode);
 
-	if (LINUX_S_ISCHR(mode))
-		filetype = EXT2_FT_CHRDEV;
-	else if (LINUX_S_ISBLK(mode))
-		filetype = EXT2_FT_BLKDEV;
-	else if (LINUX_S_ISFIFO(mode))
-		filetype = EXT2_FT_FIFO;
-	else if (LINUX_S_ISSOCK(mode))
-		filetype = EXT2_FT_SOCK;
-	else {
-		ret = -EINVAL;
-		goto out2;
-	}
-
-	err = fuse4fs_new_child_gid(ff, parent, &gid, NULL);
+	err = fuse4fs_new_child_gid(ff, ctxt, parent, &gid, NULL);
 	if (err)
 		goto out2;
 
@@ -1775,9 +1808,9 @@ static int op_mknod(const char *path, mode_t mode, dev_t dev)
 		goto out2;
 	}
 
-	dbg_printf(ff, "%s: create ino=%d/name=%s in dir=%d\n", __func__, child,
-		   node_name, parent);
-	err = ext2fs_link(fs, parent, node_name, child,
+	dbg_printf(ff, "%s: create ino=%d name='%s' in dir=%d\n", __func__,
+		   child, name, parent);
+	err = ext2fs_link(fs, parent, name, child,
 			  filetype | EXT2FS_LINK_EXPAND);
 	if (err) {
 		ret = translate_error(fs, parent, err);
@@ -1826,42 +1859,28 @@ static int op_mknod(const char *path, mode_t mode, dev_t dev)
 
 out2:
 	fuse4fs_finish(ff, ret);
-out:
-	free(temp_path);
-	return ret;
+	fuse4fs_reply_entry(req, child, &inode, ret);
 }
 
-static int op_mkdir(const char *path, mode_t mode)
+static void op_mkdir(fuse_req_t req, fuse_ino_t fino, const char *name,
+		     mode_t mode)
 {
-	struct fuse_context *ctxt = fuse_get_context();
-	struct fuse4fs *ff = fuse4fs_get();
+	struct ext2_inode_large inode;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	ext2_ino_t parent, child;
-	char *temp_path;
 	errcode_t err;
-	char *node_name, a;
-	struct ext2_inode_large inode;
 	char *block;
 	blk64_t blk;
 	int ret = 0;
 	gid_t gid;
 	int parent_sgid;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: path=%s mode=0%o\n", __func__, path, mode);
-	temp_path = strdup(path);
-	if (!temp_path) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name = strrchr(temp_path, '/');
-	if (!node_name) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name++;
-	a = *node_name;
-	*node_name = 0;
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, fino);
+	dbg_printf(ff, "%s: parent=%d name='%s' mode=0%o\n",
+		   __func__, parent, name, mode);
 
 	fs = fuse4fs_start(ff);
 	if (!fuse4fs_can_allocate(ff, 1)) {
@@ -1869,25 +1888,15 @@ static int op_mkdir(const char *path, mode_t mode)
 		goto out2;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
-			   &parent);
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out2;
-	}
-
-	ret = fuse4fs_inum_access(ff, parent, A_OK | W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, parent, A_OK | W_OK);
 	if (ret)
 		goto out2;
 
-	err = fuse4fs_new_child_gid(ff, parent, &gid, &parent_sgid);
+	err = fuse4fs_new_child_gid(ff, ctxt, parent, &gid, &parent_sgid);
 	if (err)
 		goto out2;
 
-	*node_name = a;
-
-	err = ext2fs_mkdir2(fs, parent, 0, 0, EXT2FS_LINK_EXPAND,
-			    node_name, NULL);
+	err = ext2fs_mkdir2(fs, parent, 0, 0, EXT2FS_LINK_EXPAND, name, NULL);
 	if (err) {
 		ret = translate_error(fs, parent, err);
 		goto out2;
@@ -1898,14 +1907,13 @@ static int op_mkdir(const char *path, mode_t mode)
 		goto out2;
 
 	/* Still have to update the uid/gid of the dir */
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
-			   &child);
+	err = ext2fs_namei(fs, EXT2_ROOT_INO, parent, name, &child);
 	if (err) {
 		ret = translate_error(fs, 0, err);
 		goto out2;
 	}
-	dbg_printf(ff, "%s: created ino=%d/path=%s in dir=%d\n", __func__, child,
-		   node_name, parent);
+	dbg_printf(ff, "%s: created ino=%d name='%s' in dir=%d\n",
+		   __func__, child, name, parent);
 
 	err = fuse4fs_read_inode(fs, child, &inode);
 	if (err) {
@@ -1961,55 +1969,7 @@ static int op_mkdir(const char *path, mode_t mode)
 	ext2fs_free_mem(&block);
 out2:
 	fuse4fs_finish(ff, ret);
-out:
-	free(temp_path);
-	return ret;
-}
-
-static int fuse4fs_unlink(struct fuse4fs *ff, const char *path,
-			  ext2_ino_t *parent)
-{
-	ext2_filsys fs = ff->fs;
-	errcode_t err;
-	ext2_ino_t dir;
-	char *filename = strdup(path);
-	char *base_name;
-	int ret;
-
-	base_name = strrchr(filename, '/');
-	if (base_name) {
-		*base_name++ = '\0';
-		err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, filename,
-				   &dir);
-		if (err) {
-			free(filename);
-			return translate_error(fs, 0, err);
-		}
-	} else {
-		dir = EXT2_ROOT_INO;
-		base_name = filename;
-	}
-
-	ret = fuse4fs_inum_access(ff, dir, W_OK);
-	if (ret) {
-		free(filename);
-		return ret;
-	}
-
-	dbg_printf(ff, "%s: unlinking name=%s from dir=%d\n", __func__,
-		   base_name, dir);
-	err = ext2fs_unlink(fs, dir, base_name, 0, 0);
-	free(filename);
-	if (err)
-		return translate_error(fs, dir, err);
-
-	ret = update_mtime(fs, dir, NULL);
-	if (ret)
-		return ret;
-
-	if (parent)
-		*parent = dir;
-	return 0;
+	fuse4fs_reply_entry(req, child, &inode, ret);
 }
 
 static int fuse4fs_remove_ea_inodes(struct fuse4fs *ff, ext2_ino_t ino,
@@ -2118,49 +2078,78 @@ static int fuse4fs_remove_inode(struct fuse4fs *ff, ext2_ino_t ino)
 	return 0;
 }
 
-static int __op_unlink(struct fuse4fs *ff, const char *path)
+static int fuse4fs_unlink(struct fuse4fs *ff, ext2_ino_t parent,
+			  const char *name, ext2_ino_t child)
 {
 	ext2_filsys fs = ff->fs;
-	ext2_ino_t parent, ino;
 	errcode_t err;
 	int ret = 0;
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
+	err = ext2fs_unlink(fs, parent, name, child, 0);
+	if (err) {
+		ret = translate_error(fs, parent, err);
+		goto out;
+	}
+
+	ret = update_mtime(fs, parent, NULL);
+	if (ret)
+		goto out;
+out:
+	return ret;
+}
+
+static int fuse4fs_rmfile(struct fuse4fs *ff, ext2_ino_t parent,
+			  const char *name, ext2_ino_t child)
+{
+	int ret;
+
+	ret = fuse4fs_unlink(ff, parent, name, child);
+	if (ret)
+		return ret;
+
+	return fuse4fs_remove_inode(ff, child);
+}
+
+static void op_unlink(fuse_req_t req, fuse_ino_t fino, const char *name)
+{
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
+	ext2_filsys fs;
+	ext2_ino_t parent, child;
+	errcode_t err;
+	int ret;
+
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, fino);
+	fs = fuse4fs_start(ff);
+
+	/* Get the inode number for the file */
+	err = ext2fs_namei(fs, EXT2_ROOT_INO, parent, name, &child);
 	if (err) {
 		ret = translate_error(fs, 0, err);
 		goto out;
 	}
 
-	ret = fuse4fs_inum_access(ff, ino, W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, child, W_OK);
 	if (ret)
 		goto out;
 
-	ret = fuse4fs_unlink(ff, path, &parent);
+	ret = fuse4fs_inum_access(ff, ctxt, parent, W_OK);
 	if (ret)
 		goto out;
 
-	ret = fuse4fs_remove_inode(ff, ino);
+	dbg_printf(ff, "%s: unlink parent=%d name='%s' child=%d\n",
+		   __func__, parent, name, child);
+	ret = fuse4fs_rmfile(ff, parent, name, child);
 	if (ret)
 		goto out;
 
 	ret = fuse4fs_dirsync_flush(ff, parent, NULL);
 	if (ret)
 		goto out;
-
 out:
-	return ret;
-}
-
-static int op_unlink(const char *path)
-{
-	struct fuse4fs *ff = fuse4fs_get();
-	int ret;
-
-	FUSE4FS_CHECK_CONTEXT(ff);
-	fuse4fs_start(ff);
-	ret = __op_unlink(ff, path);
 	fuse4fs_finish(ff, ret);
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
 struct rd_struct {
@@ -2191,51 +2180,36 @@ static int rmdir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
 	return 0;
 }
 
-static int __op_rmdir(struct fuse4fs *ff, const char *path)
+static int fuse4fs_rmdir(struct fuse4fs *ff, ext2_ino_t parent,
+			 const char *name, ext2_ino_t child)
 {
 	ext2_filsys fs = ff->fs;
-	ext2_ino_t parent, child;
 	errcode_t err;
 	struct ext2_inode_large inode;
-	struct rd_struct rds;
+	struct rd_struct rds = {
+		.parent = 0,
+		.empty = 1,
+	};
 	int ret = 0;
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &child);
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
-	dbg_printf(ff, "%s: rmdir path=%s ino=%d\n", __func__, path, child);
-
-	ret = fuse4fs_inum_access(ff, child, W_OK);
-	if (ret)
-		goto out;
-
-	rds.parent = 0;
-	rds.empty = 1;
-
 	err = ext2fs_dir_iterate2(fs, child, 0, 0, rmdir_proc, &rds);
 	if (err) {
 		ret = translate_error(fs, child, err);
 		goto out;
 	}
 
-	/* the kernel checks parent permissions before emptiness */
+	/* Make sure we found a dotdot entry */
 	if (rds.parent == 0) {
 		ret = translate_error(fs, child, EXT2_ET_FILESYSTEM_CORRUPTED);
 		goto out;
 	}
 
-	ret = fuse4fs_inum_access(ff, rds.parent, W_OK);
-	if (ret)
-		goto out;
-
 	if (rds.empty == 0) {
 		ret = -ENOTEMPTY;
 		goto out;
 	}
 
-	ret = fuse4fs_unlink(ff, path, &parent);
+	ret = fuse4fs_unlink(ff, parent, name, child);
 	if (ret)
 		goto out;
 	/* Directories have to be "removed" twice. */
@@ -2266,74 +2240,81 @@ static int __op_rmdir(struct fuse4fs *ff, const char *path)
 		}
 	}
 
-	ret = fuse4fs_dirsync_flush(ff, parent, NULL);
-	if (ret)
-		goto out;
-
 out:
 	return ret;
 }
 
-static int op_rmdir(const char *path)
+static void op_rmdir(fuse_req_t req, fuse_ino_t fino, const char *name)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
+	ext2_filsys fs;
+	ext2_ino_t parent, child;
+	errcode_t err;
 	int ret;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	fuse4fs_start(ff);
-	ret = __op_rmdir(ff, path);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, fino);
+	fs = fuse4fs_start(ff);
+
+	err = ext2fs_namei(fs, EXT2_ROOT_INO, parent, name, &child);
+	if (err) {
+		ret = translate_error(fs, 0, err);
+		goto out;
+	}
+
+	ret = fuse4fs_inum_access(ff, ctxt, parent, W_OK);
+	if (ret)
+		goto out;
+
+	ret = fuse4fs_inum_access(ff, ctxt, child, W_OK);
+	if (ret)
+		goto out;
+
+	dbg_printf(ff, "%s: unlink parent=%d name='%s' child=%d\n",
+		   __func__, parent, name, child);
+	ret = fuse4fs_rmdir(ff, parent, name, child);
+	if (ret)
+		goto out;
+
+	ret = fuse4fs_dirsync_flush(ff, parent, NULL);
+	if (ret)
+		goto out;
+
+out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
-static int op_symlink(const char *src, const char *dest)
+static void op_symlink(fuse_req_t req, const char *target, fuse_ino_t fino,
+		       const char *name)
 {
-	struct fuse_context *ctxt = fuse_get_context();
-	struct fuse4fs *ff = fuse4fs_get();
-	ext2_filsys fs;
-	ext2_ino_t parent, child;
-	char *temp_path;
-	errcode_t err;
-	char *node_name, a;
 	struct ext2_inode_large inode;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
+	ext2_filsys fs;
+	ext2_ino_t parent, child;
+	errcode_t err;
 	gid_t gid;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: symlink %s to %s\n", __func__, src, dest);
-	temp_path = strdup(dest);
-	if (!temp_path) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name = strrchr(temp_path, '/');
-	if (!node_name) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name++;
-	a = *node_name;
-	*node_name = 0;
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, fino);
+	dbg_printf(ff, "%s: symlink dir=%d name='%s' target='%s'\n",
+		   __func__, parent, name, target);
 
 	fs = fuse4fs_start(ff);
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
-			   &parent);
-	*node_name = a;
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out2;
-	}
 
-	ret = fuse4fs_inum_access(ff, parent, A_OK | W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, parent, A_OK | W_OK);
 	if (ret)
 		goto out2;
 
-	err = fuse4fs_new_child_gid(ff, parent, &gid, NULL);
+	err = fuse4fs_new_child_gid(ff, ctxt, parent, &gid, NULL);
 	if (err)
 		goto out2;
 
 	/* Create symlink */
-	err = ext2fs_symlink(fs, parent, 0, node_name, src);
+	err = ext2fs_symlink(fs, parent, 0, name, target);
 	if (err == EXT2_ET_DIR_NO_SPACE) {
 		err = ext2fs_expand_dir(fs, parent);
 		if (err) {
@@ -2341,7 +2322,7 @@ static int op_symlink(const char *src, const char *dest)
 			goto out2;
 		}
 
-		err = ext2fs_symlink(fs, parent, 0, node_name, src);
+		err = ext2fs_symlink(fs, parent, 0, name, target);
 	}
 	if (err) {
 		ret = translate_error(fs, parent, err);
@@ -2354,14 +2335,13 @@ static int op_symlink(const char *src, const char *dest)
 		goto out2;
 
 	/* Still have to update the uid/gid of the symlink */
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
-			   &child);
+	err = ext2fs_namei(fs, EXT2_ROOT_INO, parent, name, &child);
 	if (err) {
 		ret = translate_error(fs, 0, err);
 		goto out2;
 	}
-	dbg_printf(ff, "%s: symlinking ino=%d/name=%s to dir=%d\n", __func__,
-		   child, node_name, parent);
+	dbg_printf(ff, "%s: symlinking dir=%d name='%s' child=%d\n",
+		   __func__, parent, name, child);
 
 	err = fuse4fs_read_inode(fs, child, &inode);
 	if (err) {
@@ -2387,9 +2367,7 @@ static int op_symlink(const char *src, const char *dest)
 
 out2:
 	fuse4fs_finish(ff, ret);
-out:
-	free(temp_path);
-	return ret;
+	fuse4fs_reply_entry(req, child, &inode, ret);
 }
 
 struct update_dotdot {
@@ -2415,39 +2393,43 @@ static int update_dotdot_helper(ext2_ino_t dir EXT2FS_ATTR((unused)),
 	return 0;
 }
 
-static int op_rename(const char *from, const char *to,
-		     unsigned int flags EXT2FS_ATTR((unused)))
+static void op_rename(fuse_req_t req, fuse_ino_t from_parent, const char *from,
+		      fuse_ino_t to_parent, const char *to, unsigned int flags)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	errcode_t err;
 	ext2_ino_t from_ino, to_ino, to_dir_ino, from_dir_ino;
-	char *temp_to = NULL, *temp_from = NULL;
-	char *cp, a;
 	struct ext2_inode inode;
 	struct update_dotdot ud;
 	int flushed = 0;
 	int ret = 0;
 
 	/* renameat2 is not supported */
-	if (flags)
-		return -ENOSYS;
+	if (flags) {
+		fuse_reply_err(req, ENOSYS);
+		return;
+	}
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: renaming %s to %s\n", __func__, from, to);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &from_dir_ino, from_parent);
+	FUSE4FS_CONVERT_FINO(req, &to_dir_ino, to_parent);
+	dbg_printf(ff, "%s: renaming dir=%d name='%s' to dir=%d name='%s'\n",
+		   __func__, from_dir_ino, from, to_dir_ino, to);
 	fs = fuse4fs_start(ff);
 	if (!fuse4fs_can_allocate(ff, 5)) {
 		ret = -ENOSPC;
 		goto out;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, from, &from_ino);
+	err = ext2fs_namei(fs, EXT2_ROOT_INO, from_dir_ino, from, &from_ino);
 	if (err || from_ino == 0) {
 		ret = translate_error(fs, 0, err);
 		goto out;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, to, &to_ino);
+	err = ext2fs_namei(fs, EXT2_ROOT_INO, to_dir_ino, to, &to_ino);
 	if (err && err != EXT2_ET_FILE_NOT_FOUND) {
 		ret = translate_error(fs, 0, err);
 		goto out;
@@ -2456,136 +2438,80 @@ static int op_rename(const char *from, const char *to,
 	if (err == EXT2_ET_FILE_NOT_FOUND)
 		to_ino = 0;
 
+	dbg_printf(ff,
+ "%s: renaming dir=%d name='%s' child=%d to dir=%d name='%s' child=%d\n",
+		   __func__, from_dir_ino, from, from_ino, to_dir_ino, to,
+		   to_ino);
+
 	/* Already the same file? */
 	if (to_ino != 0 && to_ino == from_ino) {
 		ret = 0;
 		goto out;
 	}
 
-	ret = fuse4fs_inum_access(ff, from_ino, W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, from_ino, W_OK);
 	if (ret)
 		goto out;
 
 	if (to_ino) {
-		ret = fuse4fs_inum_access(ff, to_ino, W_OK);
+		ret = fuse4fs_inum_access(ff, ctxt, to_ino, W_OK);
 		if (ret)
 			goto out;
 	}
 
-	temp_to = strdup(to);
-	if (!temp_to) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	temp_from = strdup(from);
-	if (!temp_from) {
-		ret = -ENOMEM;
-		goto out2;
-	}
-
-	/* Find parent dir of the source and check write access */
-	cp = strrchr(temp_from, '/');
-	if (!cp) {
-		ret = -EINVAL;
-		goto out2;
-	}
-
-	a = *(cp + 1);
-	*(cp + 1) = 0;
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_from,
-			   &from_dir_ino);
-	*(cp + 1) = a;
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out2;
-	}
-	if (from_dir_ino == 0) {
-		ret = -ENOENT;
-		goto out2;
-	}
-
-	ret = fuse4fs_inum_access(ff, from_dir_ino, W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, from_dir_ino, W_OK);
 	if (ret)
-		goto out2;
-
-	/* Find parent dir of the destination and check write access */
-	cp = strrchr(temp_to, '/');
-	if (!cp) {
-		ret = -EINVAL;
-		goto out2;
-	}
-
-	a = *(cp + 1);
-	*(cp + 1) = 0;
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_to,
-			   &to_dir_ino);
-	*(cp + 1) = a;
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out2;
-	}
-	if (to_dir_ino == 0) {
-		ret = -ENOENT;
-		goto out2;
-	}
+		goto out;
 
-	ret = fuse4fs_inum_access(ff, to_dir_ino, W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, to_dir_ino, W_OK);
 	if (ret)
-		goto out2;
+		goto out;
 
 	/* If the target exists, unlink it first */
 	if (to_ino != 0) {
 		err = ext2fs_read_inode(fs, to_ino, &inode);
 		if (err) {
 			ret = translate_error(fs, to_ino, err);
-			goto out2;
+			goto out;
 		}
 
-		dbg_printf(ff, "%s: unlinking %s ino=%d\n", __func__,
-			   LINUX_S_ISDIR(inode.i_mode) ? "dir" : "file",
-			   to_ino);
+		dbg_printf(ff, "%s: unlink dir=%d name='%s' child=%d\n",
+			   __func__, to_dir_ino, to, to_ino);
 		if (LINUX_S_ISDIR(inode.i_mode))
-			ret = __op_rmdir(ff, to);
+			ret = fuse4fs_rmdir(ff, to_dir_ino, to, to_ino);
 		else
-			ret = __op_unlink(ff, to);
+			ret = fuse4fs_rmfile(ff, to_dir_ino, to, to_ino);
 		if (ret)
-			goto out2;
+			goto out;
 	}
 
 	/* Get ready to do the move */
 	err = ext2fs_read_inode(fs, from_ino, &inode);
 	if (err) {
 		ret = translate_error(fs, from_ino, err);
-		goto out2;
+		goto out;
 	}
 
 	/* Link in the new file */
-	dbg_printf(ff, "%s: linking ino=%d/path=%s to dir=%d\n", __func__,
-		   from_ino, cp + 1, to_dir_ino);
-	err = ext2fs_link(fs, to_dir_ino, cp + 1, from_ino,
+	dbg_printf(ff, "%s: link dir=%d name='%s' child=%d\n",
+		   __func__, to_dir_ino, to, from_ino);
+	err = ext2fs_link(fs, to_dir_ino, to, from_ino,
 			  ext2_file_type(inode.i_mode) | EXT2FS_LINK_EXPAND);
 	if (err) {
 		ret = translate_error(fs, to_dir_ino, err);
-		goto out2;
+		goto out;
 	}
 
 	/* Update '..' pointer if dir */
-	err = ext2fs_read_inode(fs, from_ino, &inode);
-	if (err) {
-		ret = translate_error(fs, from_ino, err);
-		goto out2;
-	}
-
 	if (LINUX_S_ISDIR(inode.i_mode)) {
 		ud.new_dotdot = to_dir_ino;
-		dbg_printf(ff, "%s: updating .. entry for dir=%d\n", __func__,
-			   to_dir_ino);
+		dbg_printf(ff, "%s: updating .. entry for child=%d parent=%d\n",
+			   __func__, from_ino, to_dir_ino);
 		err = ext2fs_dir_iterate2(fs, from_ino, 0, NULL,
 					  update_dotdot_helper, &ud);
 		if (err) {
 			ret = translate_error(fs, from_ino, err);
-			goto out2;
+			goto out;
 		}
 
 		/* Decrease from_dir_ino's links_count */
@@ -2594,87 +2520,76 @@ static int op_rename(const char *from, const char *to,
 		err = ext2fs_read_inode(fs, from_dir_ino, &inode);
 		if (err) {
 			ret = translate_error(fs, from_dir_ino, err);
-			goto out2;
+			goto out;
 		}
 		inode.i_links_count--;
 		err = ext2fs_write_inode(fs, from_dir_ino, &inode);
 		if (err) {
 			ret = translate_error(fs, from_dir_ino, err);
-			goto out2;
+			goto out;
 		}
 
 		/* Increase to_dir_ino's links_count */
 		err = ext2fs_read_inode(fs, to_dir_ino, &inode);
 		if (err) {
 			ret = translate_error(fs, to_dir_ino, err);
-			goto out2;
+			goto out;
 		}
 		inode.i_links_count++;
 		err = ext2fs_write_inode(fs, to_dir_ino, &inode);
 		if (err) {
 			ret = translate_error(fs, to_dir_ino, err);
-			goto out2;
+			goto out;
 		}
 	}
 
 	/* Update timestamps */
 	ret = update_ctime(fs, from_ino, NULL);
 	if (ret)
-		goto out2;
+		goto out;
 
 	ret = update_mtime(fs, to_dir_ino, NULL);
 	if (ret)
-		goto out2;
+		goto out;
 
 	/* Remove the old file */
-	ret = fuse4fs_unlink(ff, from, NULL);
+	dbg_printf(ff, "%s: unlink dir=%d name='%s' child=%d\n",
+		   __func__, from_dir_ino, from, from_ino);
+	ret = fuse4fs_unlink(ff, from_dir_ino, from, from_ino);
 	if (ret)
-		goto out2;
+		goto out;
 
 	ret = fuse4fs_dirsync_flush(ff, from_dir_ino, &flushed);
 	if (ret)
-		goto out2;
+		goto out;
 
 	if (from_dir_ino != to_dir_ino && !flushed) {
 		ret = fuse4fs_dirsync_flush(ff, to_dir_ino, NULL);
 		if (ret)
-			goto out2;
+			goto out;
 	}
 
-out2:
-	free(temp_from);
-	free(temp_to);
 out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
-static int op_link(const char *src, const char *dest)
+static void op_link(fuse_req_t req, fuse_ino_t child_fino,
+		    fuse_ino_t parent_fino, const char *name)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct ext2_inode_large inode;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
-	char *temp_path;
 	errcode_t err;
-	char *node_name, a;
-	ext2_ino_t parent, ino;
-	struct ext2_inode_large inode;
+	ext2_ino_t parent, child;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: src=%s dest=%s\n", __func__, src, dest);
-	temp_path = strdup(dest);
-	if (!temp_path) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name = strrchr(temp_path, '/');
-	if (!node_name) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name++;
-	a = *node_name;
-	*node_name = 0;
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, parent_fino);
+	FUSE4FS_CONVERT_FINO(req, &child, child_fino);
+	dbg_printf(ff, "%s: link dir=%d name='%s' child=%d\n",
+		   __func__, parent, name, child);
 
 	fs = fuse4fs_start(ff);
 	if (!fuse4fs_can_allocate(ff, 2)) {
@@ -2682,48 +2597,32 @@ static int op_link(const char *src, const char *dest)
 		goto out2;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
-			   &parent);
-	*node_name = a;
-	if (err) {
-		err = -ENOENT;
-		goto out2;
-	}
-
-	ret = fuse4fs_inum_access(ff, parent, A_OK | W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, parent, A_OK | W_OK);
 	if (ret)
 		goto out2;
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, src, &ino);
-	if (err || ino == 0) {
-		ret = translate_error(fs, 0, err);
-		goto out2;
-	}
-
-	err = fuse4fs_read_inode(fs, ino, &inode);
+	err = fuse4fs_read_inode(fs, child, &inode);
 	if (err) {
-		ret = translate_error(fs, ino, err);
+		ret = translate_error(fs, child, err);
 		goto out2;
 	}
 
-	ret = fuse4fs_iflags_access(ff, ino, EXT2_INODE(&inode), W_OK);
+	ret = fuse4fs_iflags_access(ff, child, EXT2_INODE(&inode), W_OK);
 	if (ret)
 		goto out2;
 
 	inode.i_links_count++;
-	ret = update_ctime(fs, ino, &inode);
+	ret = update_ctime(fs, child, &inode);
 	if (ret)
 		goto out2;
 
-	err = fuse4fs_write_inode(fs, ino, &inode);
+	err = fuse4fs_write_inode(fs, child, &inode);
 	if (err) {
-		ret = translate_error(fs, ino, err);
+		ret = translate_error(fs, child, err);
 		goto out2;
 	}
 
-	dbg_printf(ff, "%s: linking ino=%d/name=%s to dir=%d\n", __func__, ino,
-		   node_name, parent);
-	err = ext2fs_link(fs, parent, node_name, ino,
+	err = ext2fs_link(fs, parent, name, child,
 			  ext2_file_type(inode.i_mode) | EXT2FS_LINK_EXPAND);
 	if (err) {
 		ret = translate_error(fs, parent, err);
@@ -2740,13 +2639,12 @@ static int op_link(const char *src, const char *dest)
 
 out2:
 	fuse4fs_finish(ff, ret);
-out:
-	free(temp_path);
-	return ret;
+	fuse4fs_reply_entry(req, child, &inode, ret);
 }
 
 /* Obtain group ids of the process that sent us a command(?) */
-static int fuse4fs_get_groups(struct fuse4fs *ff, gid_t **gids, size_t *nr_gids)
+static int fuse4fs_get_groups(struct fuse4fs *ff, fuse_req_t req, gid_t **gids,
+			      size_t *nr_gids)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
@@ -2759,7 +2657,7 @@ static int fuse4fs_get_groups(struct fuse4fs *ff, gid_t **gids, size_t *nr_gids)
 		if (err)
 			return translate_error(fs, 0, err);
 
-		ret = fuse_getgroups(nr, array);
+		ret = fuse_req_getgroups(req, nr, array);
 		if (ret < 0) {
 			/*
 			 * If there's an error, we failed to find the group
@@ -2791,17 +2689,18 @@ static int fuse4fs_get_groups(struct fuse4fs *ff, gid_t **gids, size_t *nr_gids)
  * that initiated the fuse request?  Returns 1 for yes, 0 for no, or a negative
  * errno.
  */
-static int fuse4fs_in_file_group(struct fuse_context *ctxt,
+static int fuse4fs_in_file_group(struct fuse4fs *ff, fuse_req_t req,
 				 const struct ext2_inode_large *inode)
 {
-	struct fuse4fs *ff = fuse4fs_get();
 	gid_t *gids = NULL;
 	size_t i, nr_gids = 0;
 	gid_t gid = inode_gid(*inode);
 	int ret;
 
-	ret = fuse4fs_get_groups(ff, &gids, &nr_gids);
+	ret = fuse4fs_get_groups(ff, req, &gids, &nr_gids);
 	if (ret == -ENOENT) {
+		const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+
 		/* magic return code for "could not get caller group info" */
 		return ctxt->gid == inode_gid(*inode);
 	}
@@ -2820,37 +2719,21 @@ static int fuse4fs_in_file_group(struct fuse_context *ctxt,
 	return ret;
 }
 
-static int op_chmod(const char *path, mode_t mode, struct fuse_file_info *fi)
+static int fuse4fs_chmod(struct fuse4fs *ff, fuse_req_t req, ext2_ino_t ino,
+			 mode_t mode, struct ext2_inode_large *inode)
 {
-	struct fuse_context *ctxt = fuse_get_context();
-	struct fuse4fs *ff = fuse4fs_get();
-	ext2_filsys fs;
-	errcode_t err;
-	ext2_ino_t ino;
-	struct ext2_inode_large inode;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	fs = fuse4fs_start(ff);
-	ret = fuse4fs_file_ino(ff, path, fi, &ino);
-	if (ret)
-		goto out;
-	dbg_printf(ff, "%s: path=%s mode=0%o ino=%d\n", __func__, path, mode, ino);
-
-	err = fuse4fs_read_inode(fs, ino, &inode);
-	if (err) {
-		ret = translate_error(fs, ino, err);
-		goto out;
-	}
+	dbg_printf(ff, "%s: ino=%d mode=0%o\n", __func__, ino, mode);
 
-	ret = fuse4fs_iflags_access(ff, ino, EXT2_INODE(&inode), W_OK);
+	ret = fuse4fs_iflags_access(ff, ino, EXT2_INODE(inode), W_OK);
 	if (ret)
-		goto out;
+		return ret;
 
-	if (fuse4fs_want_check_owner(ff, ctxt) && ctxt->uid != inode_uid(inode)) {
-		ret = -EPERM;
-		goto out;
-	}
+	if (fuse4fs_want_check_owner(ff, ctxt) &&
+	    ctxt->uid != inode_uid(*inode))
+		return -EPERM;
 
 	/*
 	 * XXX: We should really check that the inode gid is not in /any/
@@ -2858,100 +2741,60 @@ static int op_chmod(const char *path, mode_t mode, struct fuse_file_info *fi)
 	 * group.
 	 */
 	if (!fuse4fs_is_superuser(ff, ctxt)) {
-		ret = fuse4fs_in_file_group(ctxt, &inode);
+		ret = fuse4fs_in_file_group(ff, req, inode);
 		if (ret < 0)
-			goto out;
+			return ret;
 
 		if (!ret)
 			mode &= ~S_ISGID;
 	}
 
-	inode.i_mode &= ~0xFFF;
-	inode.i_mode |= mode & 0xFFF;
+	inode->i_mode &= ~0xFFF;
+	inode->i_mode |= mode & 0xFFF;
 
-	dbg_printf(ff, "%s: path=%s new_mode=0%o ino=%d\n", __func__,
-		   path, inode.i_mode, ino);
+	dbg_printf(ff, "%s: ino=%d new_mode=0%o\n",
+		   __func__, ino, inode->i_mode);
 
-	ret = update_ctime(fs, ino, &inode);
-	if (ret)
-		goto out;
-
-	err = fuse4fs_write_inode(fs, ino, &inode);
-	if (err) {
-		ret = translate_error(fs, ino, err);
-		goto out;
-	}
-
-out:
-	fuse4fs_finish(ff, ret);
-	return ret;
+	return 0;
 }
 
-static int op_chown(const char *path, uid_t owner, gid_t group,
-		    struct fuse_file_info *fi)
+static int fuse4fs_chown(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			 ext2_ino_t ino, const int to_set,
+			 const struct stat *attr,
+			 struct ext2_inode_large *inode)
 {
-	struct fuse_context *ctxt = fuse_get_context();
-	struct fuse4fs *ff = fuse4fs_get();
-	ext2_filsys fs;
-	errcode_t err;
-	ext2_ino_t ino;
-	struct ext2_inode_large inode;
+	uid_t owner = (to_set & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)~0;
+	gid_t group = (to_set & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)~0;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	fs = fuse4fs_start(ff);
-	ret = fuse4fs_file_ino(ff, path, fi, &ino);
-	if (ret)
-		goto out;
-	dbg_printf(ff, "%s: path=%s owner=%d group=%d ino=%d\n", __func__,
-		   path, owner, group, ino);
-
-	err = fuse4fs_read_inode(fs, ino, &inode);
-	if (err) {
-		ret = translate_error(fs, ino, err);
-		goto out;
-	}
+	dbg_printf(ff, "%s: ino=%d owner=%d group=%d\n",
+		   __func__, ino, owner, group);
 
-	ret = fuse4fs_iflags_access(ff, ino, EXT2_INODE(&inode), W_OK);
+	ret = fuse4fs_iflags_access(ff, ino, EXT2_INODE(inode), W_OK);
 	if (ret)
-		goto out;
+		return ret;
 
 	/* FUSE seems to feed us ~0 to mean "don't change" */
 	if (owner != (uid_t) ~0) {
 		/* Only root gets to change UID. */
 		if (fuse4fs_want_check_owner(ff, ctxt) &&
-		    !(inode_uid(inode) == ctxt->uid && owner == ctxt->uid)) {
-			ret = -EPERM;
-			goto out;
-		}
-		fuse4fs_set_uid(&inode, owner);
+		    !(inode_uid(*inode) == ctxt->uid && owner == ctxt->uid))
+			return -EPERM;
+
+		fuse4fs_set_uid(inode, owner);
 	}
 
 	if (group != (gid_t) ~0) {
 		/* Only root or the owner get to change GID. */
 		if (fuse4fs_want_check_owner(ff, ctxt) &&
-		    inode_uid(inode) != ctxt->uid) {
-			ret = -EPERM;
-			goto out;
-		}
+		    inode_uid(*inode) != ctxt->uid)
+			return -EPERM;
 
 		/* XXX: We /should/ check group membership but FUSE */
-		fuse4fs_set_gid(&inode, group);
+		fuse4fs_set_gid(inode, group);
 	}
 
-	ret = update_ctime(fs, ino, &inode);
-	if (ret)
-		goto out;
-
-	err = fuse4fs_write_inode(fs, ino, &inode);
-	if (err) {
-		ret = translate_error(fs, ino, err);
-		goto out;
-	}
-
-out:
-	fuse4fs_finish(ff, ret);
-	return ret;
+	return 0;
 }
 
 static int fuse4fs_punch_posteof(struct fuse4fs *ff, ext2_ino_t ino,
@@ -3026,32 +2869,6 @@ static int fuse4fs_truncate(struct fuse4fs *ff, ext2_ino_t ino, off_t new_size)
 	return 0;
 }
 
-static int op_truncate(const char *path, off_t len, struct fuse_file_info *fi)
-{
-	struct fuse4fs *ff = fuse4fs_get();
-	ext2_ino_t ino;
-	int ret = 0;
-
-	FUSE4FS_CHECK_CONTEXT(ff);
-	fuse4fs_start(ff);
-	ret = fuse4fs_file_ino(ff, path, fi, &ino);
-	if (ret)
-		goto out;
-	dbg_printf(ff, "%s: ino=%d len=%jd\n", __func__, ino, (intmax_t) len);
-
-	ret = fuse4fs_inum_access(ff, ino, W_OK);
-	if (ret)
-		goto out;
-
-	ret = fuse4fs_truncate(ff, ino, len);
-	if (ret)
-		goto out;
-
-out:
-	fuse4fs_finish(ff, ret);
-	return ret;
-}
-
 #ifdef __linux__
 static void detect_linux_executable_open(int kernel_flags, int *access_check,
 				  int *e2fs_open_flags)
@@ -3073,19 +2890,20 @@ static void detect_linux_executable_open(int kernel_flags, int *access_check,
 }
 #endif /* __linux__ */
 
-static int __op_open(struct fuse4fs *ff, const char *path,
-		     struct fuse_file_info *fp)
+static int fuse4fs_open_file(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			     ext2_ino_t ino, struct fuse_file_info *fp)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
 	struct fuse4fs_file_handle *file;
 	int check = 0, ret = 0;
 
-	dbg_printf(ff, "%s: path=%s oflags=0o%o\n", __func__, path, fp->flags);
+	dbg_printf(ff, "%s: ino=%d oflags=0o%o\n", __func__, ino, fp->flags);
 	err = ext2fs_get_mem(sizeof(*file), &file);
 	if (err)
 		return translate_error(fs, 0, err);
 	file->magic = FUSE4FS_FILE_MAGIC;
+	file->ino = ino;
 
 	file->open_flags = 0;
 	switch (fp->flags & O_ACCMODE) {
@@ -3114,14 +2932,7 @@ static int __op_open(struct fuse4fs *ff, const char *path,
 	if (fp->flags & O_CREAT)
 		file->open_flags |= EXT2_FILE_CREATE;
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &file->ino);
-	if (err || file->ino == 0) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
-	dbg_printf(ff, "%s: ino=%d\n", __func__, file->ino);
-
-	ret = fuse4fs_inum_access(ff, file->ino, check);
+	ret = fuse4fs_inum_access(ff, ctxt, file->ino, check);
 	if (ret) {
 		/*
 		 * In a regular (Linux) fs driver, the kernel will open
@@ -3133,7 +2944,7 @@ static int __op_open(struct fuse4fs *ff, const char *path,
 		 * also employ undocumented hacks (see above).
 		 */
 		if (check == R_OK) {
-			ret = fuse4fs_inum_access(ff, file->ino, X_OK);
+			ret = fuse4fs_inum_access(ff, ctxt, file->ino, X_OK);
 			if (ret)
 				goto out;
 		} else
@@ -3154,34 +2965,48 @@ static int __op_open(struct fuse4fs *ff, const char *path,
 	return ret;
 }
 
-static int op_open(const char *path, struct fuse_file_info *fp)
+static void op_open(fuse_req_t req, fuse_ino_t fino, struct fuse_file_info *fp)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
+	ext2_ino_t ino;
 	int ret;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
 	fuse4fs_start(ff);
-	ret = __op_open(ff, path, fp);
+	ret = fuse4fs_open_file(ff, ctxt, ino, fp);
 	fuse4fs_finish(ff, ret);
-	return ret;
+
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_open(req, fp);
 }
 
-static int op_read(const char *path EXT2FS_ATTR((unused)), char *buf,
-		   size_t len, off_t offset,
-		   struct fuse_file_info *fp)
+static void op_read(fuse_req_t req, fuse_ino_t fino EXT2FS_ATTR((unused)),
+		    size_t len, off_t offset, struct fuse_file_info *fp)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = fuse4fs_get(req);
 	struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
+	char *buf;
 	ext2_filsys fs;
 	ext2_file_t efp;
 	errcode_t err;
 	unsigned int got = 0;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	FUSE4FS_CHECK_HANDLE(ff, fh);
+	buf = calloc(len, sizeof(char));
+	if (!buf) {
+		fuse_reply_err(req, errno);
+		return;
+	}
+
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CHECK_HANDLE(req, fh);
 	dbg_printf(ff, "%s: ino=%d off=0x%llx len=0x%zx\n", __func__, fh->ino,
 		   (unsigned long long)offset, len);
+
 	fs = fuse4fs_start(ff);
 	err = ext2fs_file_open(fs, fh->ino, fh->open_flags, &efp);
 	if (err) {
@@ -3217,14 +3042,18 @@ static int op_read(const char *path EXT2FS_ATTR((unused)), char *buf,
 	}
 out:
 	fuse4fs_finish(ff, ret);
-	return got ? (int) got : ret;
+	if (got)
+		fuse_reply_buf(req, buf, got);
+	else
+		fuse_reply_err(req, -ret);
+	ext2fs_free_mem(&buf);
 }
 
-static int op_write(const char *path EXT2FS_ATTR((unused)),
-		    const char *buf, size_t len, off_t offset,
-		    struct fuse_file_info *fp)
+static void op_write(fuse_req_t req, fuse_ino_t fino EXT2FS_ATTR((unused)),
+		     const char *buf, size_t len, off_t offset,
+		     struct fuse_file_info *fp)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = fuse4fs_get(req);
 	struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
 	ext2_filsys fs;
 	ext2_file_t efp;
@@ -3232,8 +3061,8 @@ static int op_write(const char *path EXT2FS_ATTR((unused)),
 	unsigned int got = 0;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	FUSE4FS_CHECK_HANDLE(ff, fh);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CHECK_HANDLE(req, fh);
 	dbg_printf(ff, "%s: ino=%d off=0x%llx len=0x%zx\n", __func__, fh->ino,
 		   (unsigned long long) offset, len);
 	fs = fuse4fs_start(ff);
@@ -3287,20 +3116,23 @@ static int op_write(const char *path EXT2FS_ATTR((unused)),
 
 out:
 	fuse4fs_finish(ff, ret);
-	return got ? (int) got : ret;
+	if (got)
+		fuse_reply_write(req, got);
+	else
+		fuse_reply_err(req, -ret);
 }
 
-static int op_release(const char *path EXT2FS_ATTR((unused)),
-		      struct fuse_file_info *fp)
+static void op_release(fuse_req_t req, fuse_ino_t fino EXT2FS_ATTR((unused)),
+		       struct fuse_file_info *fp)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = fuse4fs_get(req);
 	struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
 	ext2_filsys fs;
 	errcode_t err;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	FUSE4FS_CHECK_HANDLE(ff, fh);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CHECK_HANDLE(req, fh);
 	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
 	fs = fuse4fs_start(ff);
 
@@ -3317,21 +3149,21 @@ static int op_release(const char *path EXT2FS_ATTR((unused)),
 
 	ext2fs_free_mem(&fh);
 
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
-static int op_fsync(const char *path EXT2FS_ATTR((unused)),
-		    int datasync EXT2FS_ATTR((unused)),
-		    struct fuse_file_info *fp)
+static void op_fsync(fuse_req_t req, fuse_ino_t fino EXT2FS_ATTR((unused)),
+		     int datasync EXT2FS_ATTR((unused)),
+		     struct fuse_file_info *fp)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = fuse4fs_get(req);
 	struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
 	ext2_filsys fs;
 	errcode_t err;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	FUSE4FS_CHECK_HANDLE(ff, fh);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CHECK_HANDLE(req, fh);
 	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
 	fs = fuse4fs_start(ff);
 	/* For now, flush everything, even if it's slow */
@@ -3342,22 +3174,24 @@ static int op_fsync(const char *path EXT2FS_ATTR((unused)),
 	}
 	fuse4fs_finish(ff, ret);
 
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
-static int op_statfs(const char *path EXT2FS_ATTR((unused)),
-		     struct statvfs *buf)
+static void op_statfs(fuse_req_t req, fuse_ino_t fino)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct statvfs buf;
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	uint64_t fsid, *f;
+	ext2_ino_t ino;
 	blk64_t overhead, reserved, free;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: path=%s\n", __func__, path);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
+	dbg_printf(ff, "%s: ino=%d\n", __func__, ino);
 	fs = fuse4fs_start(ff);
-	buf->f_bsize = fs->blocksize;
-	buf->f_frsize = 0;
+	buf.f_bsize = fs->blocksize;
+	buf.f_frsize = 0;
 
 	if (ff->minixdf)
 		overhead = 0;
@@ -3370,27 +3204,27 @@ static int op_statfs(const char *path EXT2FS_ATTR((unused)),
 		reserved = ext2fs_blocks_count(fs->super) / 10;
 	free = ext2fs_free_blocks_count(fs->super);
 
-	buf->f_blocks = ext2fs_blocks_count(fs->super) - overhead;
-	buf->f_bfree = free;
+	buf.f_blocks = ext2fs_blocks_count(fs->super) - overhead;
+	buf.f_bfree = free;
 	if (free < reserved)
-		buf->f_bavail = 0;
+		buf.f_bavail = 0;
 	else
-		buf->f_bavail = free - reserved;
-	buf->f_files = fs->super->s_inodes_count;
-	buf->f_ffree = fs->super->s_free_inodes_count;
-	buf->f_favail = fs->super->s_free_inodes_count;
+		buf.f_bavail = free - reserved;
+	buf.f_files = fs->super->s_inodes_count;
+	buf.f_ffree = fs->super->s_free_inodes_count;
+	buf.f_favail = fs->super->s_free_inodes_count;
 	f = (uint64_t *)fs->super->s_uuid;
 	fsid = *f;
 	f++;
 	fsid ^= *f;
-	buf->f_fsid = fsid;
-	buf->f_flag = 0;
+	buf.f_fsid = fsid;
+	buf.f_flag = 0;
 	if (ff->opstate != F4OP_WRITABLE)
-		buf->f_flag |= ST_RDONLY;
-	buf->f_namemax = EXT2_NAME_LEN;
+		buf.f_flag |= ST_RDONLY;
+	buf.f_namemax = EXT2_NAME_LEN;
 	fuse4fs_finish(ff, 0);
 
-	return 0;
+	fuse_reply_statfs(req, &buf);
 }
 
 static const char *valid_xattr_prefixes[] = {
@@ -3414,35 +3248,33 @@ static int validate_xattr_name(const char *name)
 	return 0;
 }
 
-static int op_getxattr(const char *path, const char *key, char *value,
-		       size_t len)
+static void op_getxattr(fuse_req_t req, fuse_ino_t fino, const char *key,
+			size_t len)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
-	void *ptr;
+	void *ptr = NULL;
 	size_t plen;
 	ext2_ino_t ino;
-	errcode_t err;
 	int ret = 0;
 
-	if (!validate_xattr_name(key))
-		return -ENODATA;
+	if (!validate_xattr_name(key)) {
+		fuse_reply_err(req, ENODATA);
+		return;
+	}
 
-	FUSE4FS_CHECK_CONTEXT(ff);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
 	fs = fuse4fs_start(ff);
 	if (!ext2fs_has_feature_xattr(fs->super)) {
 		ret = -ENOTSUP;
 		goto out;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
-	if (err || ino == 0) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
-	dbg_printf(ff, "%s: ino=%d name=%s\n", __func__, ino, key);
+	dbg_printf(ff, "%s: ino=%d name='%s'\n", __func__, ino, key);
 
-	ret = fuse4fs_inum_access(ff, ino, R_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, ino, R_OK);
 	if (ret)
 		goto out;
 
@@ -3451,19 +3283,26 @@ static int op_getxattr(const char *path, const char *key, char *value,
 		goto out;
 
 	if (!len) {
+		/* Just tell us the length */
 		ret = plen;
 	} else if (len < plen) {
+		/* Caller's buffer wasn't big enough */
 		ret = -ERANGE;
 	} else {
-		memcpy(value, ptr, plen);
+		/* We have data */
 		ret = plen;
 	}
 
+out:
+	fuse4fs_finish(ff, ret);
+
+	if (ret < 0)
+		fuse_reply_err(req, -ret);
+	else if (!len)
+		fuse_reply_xattr(req, ret);
+	else
+		fuse_reply_buf(req, ptr, ret);
 	ext2fs_free_mem(&ptr);
-out:
-	fuse4fs_finish(ff, ret);
-
-	return ret;
 }
 
 static int count_buffer_space(char *name, char *value EXT2FS_ATTR((unused)),
@@ -3488,31 +3327,30 @@ static int copy_names(char *name, char *value EXT2FS_ATTR((unused)),
 	return 0;
 }
 
-static int op_listxattr(const char *path, char *names, size_t len)
+static void op_listxattr(fuse_req_t req, fuse_ino_t fino, size_t len)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	struct ext2_xattr_handle *h;
+	char *names = NULL;
+	char *next_name;
 	unsigned int bufsz;
 	ext2_ino_t ino;
 	errcode_t err;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
 	fs = fuse4fs_start(ff);
 	if (!ext2fs_has_feature_xattr(fs->super)) {
 		ret = -ENOTSUP;
 		goto out;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
-	if (err || ino == 0) {
-		ret = translate_error(fs, ino, err);
-		goto out;
-	}
 	dbg_printf(ff, "%s: ino=%d\n", __func__, ino);
 
-	ret = fuse4fs_inum_access(ff, ino, R_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, ino, R_OK);
 	if (ret)
 		goto out;
 
@@ -3537,21 +3375,28 @@ static int op_listxattr(const char *path, char *names, size_t len)
 	}
 
 	if (len == 0) {
-		ret = bufsz;
+		/* Just tell us the length */
 		goto out2;
 	} else if (len < bufsz) {
+		/* Caller's buffer wasn't big enough */
 		ret = -ERANGE;
 		goto out2;
 	}
 
 	/* Copy names out */
-	memset(names, 0, len);
-	err = ext2fs_xattrs_iterate(h, copy_names, &names);
+	names = calloc(len, sizeof(char));
+	if (!names) {
+		ret = translate_error(fs, ino, errno);
+		goto out2;
+	}
+	next_name = names;
+
+	err = ext2fs_xattrs_iterate(h, copy_names, &next_name);
 	if (err) {
 		ret = translate_error(fs, ino, err);
 		goto out2;
 	}
-	ret = bufsz;
+
 out2:
 	err = ext2fs_xattrs_close(&h);
 	if (err && !ret)
@@ -3559,41 +3404,47 @@ static int op_listxattr(const char *path, char *names, size_t len)
 out:
 	fuse4fs_finish(ff, ret);
 
-	return ret;
+	if (ret < 0)
+		fuse_reply_err(req, -ret);
+	else if (names)
+		fuse_reply_buf(req, names, bufsz);
+	else
+		fuse_reply_xattr(req, bufsz);
+	free(names);
 }
 
-static int op_setxattr(const char *path EXT2FS_ATTR((unused)),
-		       const char *key, const char *value,
-		       size_t len, int flags)
+static void op_setxattr(fuse_req_t req, fuse_ino_t fino, const char *key,
+			const char *value, size_t len, int flags)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	struct ext2_xattr_handle *h;
 	ext2_ino_t ino;
 	errcode_t err;
 	int ret = 0;
 
-	if (flags & ~(XATTR_CREATE | XATTR_REPLACE))
-		return -EOPNOTSUPP;
+	if (flags & ~(XATTR_CREATE | XATTR_REPLACE)) {
+		fuse_reply_err(req, EOPNOTSUPP);
+		return;
+	}
 
-	if (!validate_xattr_name(key))
-		return -EINVAL;
+	if (!validate_xattr_name(key)) {
+		fuse_reply_err(req, EINVAL);
+		return;
+	}
 
-	FUSE4FS_CHECK_CONTEXT(ff);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
 	fs = fuse4fs_start(ff);
 	if (!ext2fs_has_feature_xattr(fs->super)) {
 		ret = -ENOTSUP;
 		goto out;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
-	if (err || ino == 0) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
-	dbg_printf(ff, "%s: ino=%d name=%s\n", __func__, ino, key);
+	dbg_printf(ff, "%s: ino=%d name='%s'\n", __func__, ino, key);
 
-	ret = fuse4fs_inum_access(ff, ino, W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, ino, W_OK);
 	if (ret == -EACCES) {
 		ret = -EPERM;
 		goto out;
@@ -3650,13 +3501,13 @@ static int op_setxattr(const char *path EXT2FS_ATTR((unused)),
 		ret = translate_error(fs, ino, err);
 out:
 	fuse4fs_finish(ff, ret);
-
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
-static int op_removexattr(const char *path, const char *key)
+static void op_removexattr(fuse_req_t req, fuse_ino_t fino, const char *key)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	struct ext2_xattr_handle *h;
 	void *buf;
@@ -3669,13 +3520,18 @@ static int op_removexattr(const char *path, const char *key)
 	 * Once in a while libfuse gives us a no-name xattr to delete as part
 	 * of clearing ACLs.  Just pretend we cleared them.
 	 */
-	if (key[0] == 0)
-		return 0;
+	if (key[0] == 0) {
+		fuse_reply_err(req, 0);
+		return;
+	}
 
-	if (!validate_xattr_name(key))
-		return -ENODATA;
+	if (!validate_xattr_name(key)) {
+		fuse_reply_err(req, ENODATA);
+		return;
+	}
 
-	FUSE4FS_CHECK_CONTEXT(ff);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
 	fs = fuse4fs_start(ff);
 	if (!ext2fs_has_feature_xattr(fs->super)) {
 		ret = -ENOTSUP;
@@ -3687,14 +3543,9 @@ static int op_removexattr(const char *path, const char *key)
 		goto out;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
-	if (err || ino == 0) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
 	dbg_printf(ff, "%s: ino=%d name=%s\n", __func__, ino, key);
 
-	ret = fuse4fs_inum_access(ff, ino, W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, ino, W_OK);
 	if (ret)
 		goto out;
 
@@ -3744,24 +3595,26 @@ static int op_removexattr(const char *path, const char *key)
 		ret = translate_error(fs, ino, err);
 out:
 	fuse4fs_finish(ff, ret);
-
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
 struct readdir_iter {
 	void *buf;
-	ext2_filsys fs;
-	fuse_fill_dir_t func;
+	size_t bufsz;
+	size_t bufused;
 
+	ext2_filsys fs;
 	struct fuse4fs *ff;
-	enum fuse_readdir_flags flags;
+	fuse_req_t req;
+
+	bool readdirplus;
 	unsigned int nr;
 	off_t startpos;
 	off_t dirpos;
 };
 
 static inline mode_t dirent_fmode(ext2_filsys fs,
-				   const struct ext2_dir_entry *dirent)
+				  const struct ext2_dir_entry *dirent)
 {
 	if (!ext2fs_has_feature_filetype(fs->super))
 		return 0;
@@ -3795,10 +3648,15 @@ static int op_readdir_iter(ext2_ino_t dir EXT2FS_ATTR((unused)),
 {
 	struct readdir_iter *i = data;
 	char namebuf[EXT2_NAME_LEN + 1];
-	struct stat stat = {
-		.st_ino = dirent->inode,
-		.st_mode = dirent_fmode(i->fs, dirent),
+	struct fuse4fs_stat fstat = {
+		.entry = {
+			.attr = {
+				.st_ino = dirent->inode,
+				.st_mode = dirent_fmode(i->fs, dirent),
+			},
+		},
 	};
+	size_t entrysize;
 	int ret;
 
 	i->dirpos++;
@@ -3806,48 +3664,67 @@ static int op_readdir_iter(ext2_ino_t dir EXT2FS_ATTR((unused)),
 		return 0;
 
 	dbg_printf(i->ff, "READDIR%s ino=%d %u offset=0x%llx\n",
-			i->flags == FUSE_READDIR_PLUS ? "PLUS" : "",
+			i->readdirplus ? "PLUS" : "",
 			dir,
 			i->nr++,
 			(unsigned long long)i->dirpos);
 
-	if (i->flags == FUSE_READDIR_PLUS) {
-		ret = stat_inode(i->fs, dirent->inode, &stat);
+	if (i->readdirplus) {
+		ret = fuse4fs_stat_inode(i->ff, dirent->inode, NULL, &fstat);
 		if (ret)
 			return DIRENT_ABORT;
 	}
 
 	memcpy(namebuf, dirent->name, dirent->name_len & 0xFF);
 	namebuf[dirent->name_len & 0xFF] = 0;
-	ret = i->func(i->buf, namebuf, &stat, i->dirpos , 0);
-	if (ret)
+
+	if (i->readdirplus) {
+		entrysize = fuse_add_direntry_plus(i->req, i->buf + i->bufused,
+						   i->bufsz - i->bufused,
+						   namebuf, &fstat.entry,
+						   i->dirpos);
+	} else {
+		entrysize = fuse_add_direntry(i->req, i->buf + i->bufused,
+					      i->bufsz - i->bufused, namebuf,
+					      &fstat.entry.attr, i->dirpos);
+	}
+	if (entrysize > i->bufsz - i->bufused) {
+		/* Buffer is full */
 		return DIRENT_ABORT;
+	}
 
+	i->bufused += entrysize;
 	return 0;
 }
 
-static int op_readdir(const char *path EXT2FS_ATTR((unused)), void *buf,
-		      fuse_fill_dir_t fill_func, off_t offset,
-		      struct fuse_file_info *fp, enum fuse_readdir_flags flags)
+static void __op_readdir(fuse_req_t req, fuse_ino_t fino, size_t size,
+			 off_t offset, bool plus, struct fuse_file_info *fp)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = fuse4fs_get(req);
 	struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
 	errcode_t err;
 	struct readdir_iter i = {
 		.ff = ff,
+		.req = req,
 		.dirpos = 0,
 		.startpos = offset,
-		.flags = flags,
+		.readdirplus = plus,
+		.bufsz = size,
 	};
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	FUSE4FS_CHECK_HANDLE(ff, fh);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CHECK_HANDLE(req, fh);
 	dbg_printf(ff, "%s: ino=%d offset=0x%llx\n", __func__, fh->ino,
 			(unsigned long long)offset);
+
+	err = ext2fs_get_mem(size, &i.buf);
+	if (err) {
+		ret = translate_error(i.fs, fh->ino, err);
+		goto out;
+	}
+
 	i.fs = fuse4fs_start(ff);
-	i.buf = buf;
-	i.func = fill_func;
 	err = ext2fs_dir_iterate2(i.fs, fh->ino, 0, NULL, op_readdir_iter, &i);
 	if (err) {
 		ret = translate_error(i.fs, fh->ino, err);
@@ -3861,64 +3738,66 @@ static int op_readdir(const char *path EXT2FS_ATTR((unused)), void *buf,
 	}
 out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_buf(req, i.buf, i.bufused);
+
+	ext2fs_free_mem(&i.buf);
+}
+
+static void op_readdir(fuse_req_t req, fuse_ino_t fino, size_t size,
+		       off_t offset, struct fuse_file_info *fp)
+{
+	__op_readdir(req, fino, size, offset, false, fp);
+}
+
+static void op_readdirplus(fuse_req_t req, fuse_ino_t fino, size_t size,
+			   off_t offset, struct fuse_file_info *fp)
+{
+	__op_readdir(req, fino, size, offset, true, fp);
 }
 
-static int op_access(const char *path, int mask)
+static void op_access(fuse_req_t req, fuse_ino_t fino, int mask)
 {
-	struct fuse4fs *ff = fuse4fs_get();
-	ext2_filsys fs;
-	errcode_t err;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_ino_t ino;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: path=%s mask=0x%x\n", __func__, path, mask);
-	fs = fuse4fs_start(ff);
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
-	if (err || ino == 0) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
+	dbg_printf(ff, "%s: ino=%d mask=0x%x\n",
+		   __func__, ino, mask);
+	fuse4fs_start(ff);
 
-	ret = fuse4fs_inum_access(ff, ino, mask);
+	ret = fuse4fs_inum_access(ff, ctxt, ino, mask);
 	if (ret)
 		goto out;
 
 out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 
-static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
+static void op_create(fuse_req_t req, fuse_ino_t fino, const char *name,
+		      mode_t mode, struct fuse_file_info *fp)
 {
-	struct fuse_context *ctxt = fuse_get_context();
-	struct fuse4fs *ff = fuse4fs_get();
+	struct ext2_inode_large inode;
+	struct fuse4fs_stat fstat;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	ext2_ino_t parent, child;
-	char *temp_path;
 	errcode_t err;
-	char *node_name, a;
 	int filetype;
-	struct ext2_inode_large inode;
 	gid_t gid;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	dbg_printf(ff, "%s: path=%s mode=0%o\n", __func__, path, mode);
-	temp_path = strdup(path);
-	if (!temp_path) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name = strrchr(temp_path, '/');
-	if (!node_name) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	node_name++;
-	a = *node_name;
-	*node_name = 0;
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &parent, fino);
+	dbg_printf(ff, "%s: parent=%d name='%s' mode=0%o\n",
+		   __func__, parent, name, mode);
 
 	fs = fuse4fs_start(ff);
 	if (!fuse4fs_can_allocate(ff, 1)) {
@@ -3926,23 +3805,14 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
 		goto out2;
 	}
 
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, temp_path,
-			   &parent);
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out2;
-	}
-
-	ret = fuse4fs_inum_access(ff, parent, A_OK | W_OK);
+	ret = fuse4fs_inum_access(ff, ctxt, parent, A_OK | W_OK);
 	if (ret)
 		goto out2;
 
-	err = fuse4fs_new_child_gid(ff, parent, &gid, NULL);
+	err = fuse4fs_new_child_gid(ff, ctxt, parent, &gid, NULL);
 	if (err)
 		goto out2;
 
-	*node_name = a;
-
 	filetype = ext2_file_type(mode);
 
 	err = ext2fs_new_inode(fs, parent, mode, 0, &child);
@@ -3951,9 +3821,9 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
 		goto out2;
 	}
 
-	dbg_printf(ff, "%s: creating ino=%d/name=%s in dir=%d\n", __func__, child,
-		   node_name, parent);
-	err = ext2fs_link(fs, parent, node_name, child,
+	dbg_printf(ff, "%s: creating dir=%d name='%s' child=%d\n",
+		   __func__, parent, name, child);
+	err = ext2fs_link(fs, parent, name, child,
 			  filetype | EXT2FS_LINK_EXPAND);
 	if (err) {
 		ret = translate_error(fs, parent, err);
@@ -4005,7 +3875,7 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
 		goto out2;
 
 	fp->flags &= ~O_TRUNC;
-	ret = __op_open(ff, path, fp);
+	ret = fuse4fs_open_file(ff, ctxt, child, fp);
 	if (ret)
 		goto out2;
 
@@ -4013,44 +3883,152 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
 	if (ret)
 		goto out2;
 
+	ret = fuse4fs_stat_inode(ff, child, NULL, &fstat);
+	if (ret)
+		goto out2;
+
 out2:
 	fuse4fs_finish(ff, ret);
-out:
-	free(temp_path);
-	return ret;
+
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_create(req, &fstat.entry, fp);
+}
+
+enum fuse4fs_time_action {
+	TA_NOW,		/* set to current time */
+	TA_OMIT,	/* do not set timestamp */
+	TA_THIS,	/* set to specific timestamp */
+};
+
+static inline const char *
+fuse4fs_time_action_string(enum fuse4fs_time_action act)
+{
+	switch (act) {
+	case TA_NOW:
+		return "now";
+	case TA_OMIT:
+		return "omit";
+	case TA_THIS:
+		return "specific";
+	}
+	return NULL; /* shut up gcc */
 }
 
-static int op_utimens(const char *path, const struct timespec ctv[2],
-		      struct fuse_file_info *fi)
+static int fuse4fs_utimens(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			   ext2_ino_t ino, const int to_set,
+			   const struct stat *attr,
+			   struct ext2_inode_large *inode)
 {
-	struct fuse4fs *ff = fuse4fs_get();
-	struct timespec tv[2];
-	ext2_filsys fs;
-	errcode_t err;
-	ext2_ino_t ino;
-	struct ext2_inode_large inode;
+	enum fuse4fs_time_action aact = TA_OMIT;
+	enum fuse4fs_time_action mact = TA_OMIT;
+	struct timespec atime = { };
+	struct timespec mtime = { };
+	struct timespec now = { };
 	int access = W_OK;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	fs = fuse4fs_start(ff);
-	ret = fuse4fs_file_ino(ff, path, fi, &ino);
-	if (ret)
-		goto out;
-	dbg_printf(ff, "%s: ino=%d atime=%lld.%ld mtime=%lld.%ld\n", __func__,
-			ino,
-			(long long int)ctv[0].tv_sec, ctv[0].tv_nsec,
-			(long long int)ctv[1].tv_sec, ctv[1].tv_nsec);
+	if (to_set & (FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW))
+		get_now(&now);
+
+	if (to_set & FUSE_SET_ATTR_ATIME_NOW) {
+		atime = now;
+		aact = TA_NOW;
+	} else if (to_set & FUSE_SET_ATTR_ATIME) {
+#if HAVE_STRUCT_STAT_ST_ATIM
+		atime = attr->st_atim;
+#else
+		atime.tv_sec = attr->st_atime;
+#endif
+		aact = TA_THIS;
+	}
+
+	if (to_set & FUSE_SET_ATTR_MTIME_NOW) {
+		mtime = now;
+		mact = TA_NOW;
+	} else if (to_set & FUSE_SET_ATTR_MTIME) {
+#if HAVE_STRUCT_STAT_ST_ATIM
+		mtime = attr->st_mtim;
+#else
+		mtime.tv_sec = attr->st_mtime;
+#endif
+		mact = TA_THIS;
+	}
+
+	dbg_printf(ff, "%s: ino=%d atime=%s:%lld.%ld mtime=%s:%lld.%ld\n",
+		   __func__, ino, fuse4fs_time_action_string(aact),
+		   (long long int)atime.tv_sec, atime.tv_nsec,
+		   fuse4fs_time_action_string(mact),
+		   (long long int)mtime.tv_sec, mtime.tv_nsec);
 
 	/*
 	 * ext4 allows timestamp updates of append-only files but only if we're
 	 * setting to current time
 	 */
-	if (ctv[0].tv_nsec == UTIME_NOW && ctv[1].tv_nsec == UTIME_NOW)
+	if (aact == TA_NOW && mact == TA_NOW)
 		access |= A_OK;
-	ret = fuse4fs_inum_access(ff, ino, access);
+	ret = fuse4fs_inum_access(ff, ctxt, ino, access);
 	if (ret)
+		return ret;
+
+	if (aact != TA_OMIT)
+		EXT4_INODE_SET_XTIME(i_atime, &atime, inode);
+	if (mact != TA_OMIT)
+		EXT4_INODE_SET_XTIME(i_mtime, &mtime, inode);
+
+	return 0;
+}
+
+static int fuse4fs_setsize(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			   ext2_ino_t ino, off_t new_size,
+			   struct ext2_inode_large *inode)
+{
+	errcode_t err;
+	int ret;
+
+	/* Write inode because truncate makes its own copy */
+	err = fuse4fs_write_inode(ff->fs, ino, inode);
+	if (err)
+		return translate_error(ff->fs, ino, err);
+
+	ret = fuse4fs_inum_access(ff, ctxt, ino, W_OK);
+	if (ret)
+		return ret;
+
+	ret = fuse4fs_truncate(ff, ino, new_size);
+	if (ret)
+		return ret;
+
+	/* Re-read inode after truncate */
+	err = fuse4fs_read_inode(ff->fs, ino, inode);
+	if (err)
+		return translate_error(ff->fs, ino, err);
+
+	return 0;
+}
+
+static void op_setattr(fuse_req_t req, fuse_ino_t fino, struct stat *attr,
+		       int to_set, struct fuse_file_info *fi EXT2FS_ATTR((unused)))
+{
+	struct ext2_inode_large inode;
+	struct fuse4fs_stat fstat;
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
+	ext2_filsys fs;
+	ext2_ino_t ino;
+	errcode_t err;
+	int ret = 0;
+
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
+	dbg_printf(ff, "%s: ino=%d to_set=0x%x\n", __func__, ino, to_set);
+	fs = fuse4fs_start(ff);
+
+	if (!fuse4fs_is_writeable(ff)) {
+		ret = -EROFS;
 		goto out;
+	}
 
 	err = fuse4fs_read_inode(fs, ino, &inode);
 	if (err) {
@@ -4058,20 +4036,35 @@ static int op_utimens(const char *path, const struct timespec ctv[2],
 		goto out;
 	}
 
-	tv[0] = ctv[0];
-	tv[1] = ctv[1];
-#ifdef UTIME_NOW
-	if (tv[0].tv_nsec == UTIME_NOW)
-		get_now(tv);
-	if (tv[1].tv_nsec == UTIME_NOW)
-		get_now(tv + 1);
-#endif /* UTIME_NOW */
-#ifdef UTIME_OMIT
-	if (tv[0].tv_nsec != UTIME_OMIT)
-		EXT4_INODE_SET_XTIME(i_atime, &tv[0], &inode);
-	if (tv[1].tv_nsec != UTIME_OMIT)
-		EXT4_INODE_SET_XTIME(i_mtime, &tv[1], &inode);
-#endif /* UTIME_OMIT */
+	/* Handle mode change using helper */
+	if (to_set & FUSE_SET_ATTR_MODE) {
+		ret = fuse4fs_chmod(ff, req, ino, attr->st_mode, &inode);
+		if (ret)
+			goto out;
+	}
+
+	/* Handle owner/group change using helper */
+	if (to_set & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
+		ret = fuse4fs_chown(ff, ctxt, ino, to_set, attr, &inode);
+		if (ret)
+			goto out;
+	}
+
+	/* Handle size change using helper */
+	if (to_set & FUSE_SET_ATTR_SIZE) {
+		ret = fuse4fs_setsize(ff, ctxt, ino, attr->st_size, &inode);
+		if (ret)
+			goto out;
+	}
+
+	/* Handle time changes using helper */
+	if (to_set & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
+		ret = fuse4fs_utimens(ff, ctxt, ino, to_set, attr, &inode);
+		if (ret)
+			goto out;
+	}
+
+	/* Update ctime for any attribute change */
 	ret = update_ctime(fs, ino, &inode);
 	if (ret)
 		goto out;
@@ -4082,9 +4075,17 @@ static int op_utimens(const char *path, const struct timespec ctv[2],
 		goto out;
 	}
 
+	/* Get updated stat info to return */
+	ret = fuse4fs_stat_inode(ff, ino, &inode, &fstat);
+
 out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_attr(req, &fstat.entry.attr,
+				fstat.entry.attr_timeout);
 }
 
 #define FUSE4FS_MODIFIABLE_IFLAGS \
@@ -4103,32 +4104,38 @@ static inline int set_iflags(struct ext2_inode_large *inode, __u32 iflags)
 
 #ifdef SUPPORT_I_FLAGS
 static int ioctl_getflags(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			  void *data)
+			  __u32 *outdata, size_t *outsize)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
 	struct ext2_inode_large inode;
 
+	if (*outsize < sizeof(__u32))
+		return -EFAULT;
+
 	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
 	err = fuse4fs_read_inode(fs, fh->ino, &inode);
 	if (err)
 		return translate_error(fs, fh->ino, err);
 
-	*(__u32 *)data = inode.i_flags & EXT2_FL_USER_VISIBLE;
+	*outdata = inode.i_flags & EXT2_FL_USER_VISIBLE;
+	*outsize = sizeof(__u32);
 	return 0;
 }
 
-static int ioctl_setflags(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			  void *data)
+static int ioctl_setflags(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			  struct fuse4fs_file_handle *fh, const __u32 *indata,
+			  size_t insize)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
 	struct ext2_inode_large inode;
 	int ret;
-	__u32 flags = *(__u32 *)data;
-	struct fuse_context *ctxt = fuse_get_context();
 
-	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
+	if (insize < sizeof(__u32))
+		return -EFAULT;
+
+	dbg_printf(ff, "%s: ino=%d iflags=0x%x\n", __func__, fh->ino, *indata);
 	err = fuse4fs_read_inode(fs, fh->ino, &inode);
 	if (err)
 		return translate_error(fs, fh->ino, err);
@@ -4136,7 +4143,7 @@ static int ioctl_setflags(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 	if (fuse4fs_want_check_owner(ff, ctxt) && inode_uid(inode) != ctxt->uid)
 		return -EPERM;
 
-	ret = set_iflags(&inode, flags);
+	ret = set_iflags(&inode, *indata);
 	if (ret)
 		return ret;
 
@@ -4152,32 +4159,38 @@ static int ioctl_setflags(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 }
 
 static int ioctl_getversion(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			    void *data)
+			    __u32 *outdata, size_t *outsize)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
 	struct ext2_inode_large inode;
 
+	if (*outsize < sizeof(__u32))
+		return -EFAULT;
+
 	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
 	err = fuse4fs_read_inode(fs, fh->ino, &inode);
 	if (err)
 		return translate_error(fs, fh->ino, err);
 
-	*(__u32 *)data = inode.i_generation;
+	*outdata = inode.i_generation;
+	*outsize = sizeof(__u32);
 	return 0;
 }
 
-static int ioctl_setversion(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			    void *data)
+static int ioctl_setversion(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			    struct fuse4fs_file_handle *fh, const __u32 *indata,
+			    size_t insize)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
 	struct ext2_inode_large inode;
 	int ret;
-	__u32 generation = *(__u32 *)data;
-	struct fuse_context *ctxt = fuse_get_context();
 
-	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
+	if (insize < sizeof(__u32))
+		return -EFAULT;
+
+	dbg_printf(ff, "%s: ino=%d generation=%d\n", __func__, fh->ino, *indata);
 	err = fuse4fs_read_inode(fs, fh->ino, &inode);
 	if (err)
 		return translate_error(fs, fh->ino, err);
@@ -4185,7 +4198,7 @@ static int ioctl_setversion(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 	if (fuse4fs_want_check_owner(ff, ctxt) && inode_uid(inode) != ctxt->uid)
 		return -EPERM;
 
-	inode.i_generation = generation;
+	inode.i_generation = *indata;
 
 	ret = update_ctime(fs, fh->ino, &inode);
 	if (ret)
@@ -4222,14 +4235,16 @@ static __u32 iflags_to_fsxflags(__u32 iflags)
 }
 
 static int ioctl_fsgetxattr(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			    void *data)
+			    struct fsxattr *fsx, size_t *outsize)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
 	struct ext2_inode_large inode;
-	struct fsxattr *fsx = data;
 	unsigned int inode_size;
 
+	if (*outsize < sizeof(struct fsxattr))
+		return -EFAULT;
+
 	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
 	err = fuse4fs_read_inode(fs, fh->ino, &inode);
 	if (err)
@@ -4240,6 +4255,7 @@ static int ioctl_fsgetxattr(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 	if (ext2fs_inode_includes(inode_size, i_projid))
 		fsx->fsx_projid = inode_projid(inode);
 	fsx->fsx_xflags = iflags_to_fsxflags(inode.i_flags);
+	*outsize = sizeof(struct fsxattr);
 	return 0;
 }
 
@@ -4264,18 +4280,21 @@ static __u32 fsxflags_to_iflags(__u32 xflags)
 	return iflags;
 }
 
-static int ioctl_fssetxattr(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			    void *data)
+static int ioctl_fssetxattr(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			    struct fuse4fs_file_handle *fh,
+			    const struct fsxattr *fsx, size_t insize)
 {
 	ext2_filsys fs = ff->fs;
 	errcode_t err;
 	struct ext2_inode_large inode;
 	int ret;
-	struct fuse_context *ctxt = fuse_get_context();
-	struct fsxattr *fsx = data;
-	__u32 flags = fsxflags_to_iflags(fsx->fsx_xflags);
+	__u32 flags;
 	unsigned int inode_size;
 
+	if (insize < sizeof(struct fsxattr))
+		return -EFAULT;
+
+	flags = fsxflags_to_iflags(fsx->fsx_xflags);
 	dbg_printf(ff, "%s: ino=%d\n", __func__, fh->ino);
 	err = fuse4fs_read_inode(fs, fh->ino, &inode);
 	if (err)
@@ -4306,17 +4325,24 @@ static int ioctl_fssetxattr(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 
 #ifdef FITRIM
 static int ioctl_fitrim(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			void *data)
+			const struct fstrim_range *fr_in, size_t insize,
+			struct fstrim_range *fr, size_t *outsize)
 {
 	ext2_filsys fs = ff->fs;
-	struct fstrim_range *fr = data;
 	blk64_t start, end, max_blocks, b, cleared, minlen;
 	blk64_t max_blks = ext2fs_blocks_count(fs->super);
 	errcode_t err = 0;
 
+	if (insize < sizeof(struct fstrim_range))
+		return -EFAULT;
+
+	if (*outsize < sizeof(struct fstrim_range))
+		return -EFAULT;
+
 	if (!fuse4fs_is_writeable(ff))
 		return -EROFS;
 
+	memcpy(fr, fr_in, sizeof(*fr));
 	start = FUSE4FS_B_TO_FSBT(ff, fr->start);
 	if (fr->len == -1ULL)
 		end = -1ULL;
@@ -4387,6 +4413,7 @@ static int ioctl_fitrim(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 
 out:
 	fr->len = FUSE4FS_FSB_TO_B(ff, cleared);
+	*outsize = sizeof(struct fstrim_range);
 	dbg_printf(ff, "%s: len=%llu err=%ld\n", __func__, fr->len, err);
 	return err;
 }
@@ -4396,10 +4423,10 @@ static int ioctl_fitrim(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 # define EXT4_IOC_SHUTDOWN	_IOR('X', 125, __u32)
 #endif
 
-static int ioctl_shutdown(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
-			  void *data)
+static int ioctl_shutdown(struct fuse4fs *ff, const struct fuse_ctx *ctxt,
+			  struct fuse4fs_file_handle *fh, const void *indata,
+			  size_t insize)
 {
-	struct fuse_context *ctxt = fuse_get_context();
 	ext2_filsys fs = ff->fs;
 
 	if (!fuse4fs_is_superuser(ff, ctxt))
@@ -4419,49 +4446,61 @@ static int ioctl_shutdown(struct fuse4fs *ff, struct fuse4fs_file_handle *fh,
 	return 0;
 }
 
-static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
-		    unsigned int cmd,
-		    void *arg EXT2FS_ATTR((unused)),
-		    struct fuse_file_info *fp,
-		    unsigned int flags EXT2FS_ATTR((unused)), void *data)
+static void op_ioctl(fuse_req_t req, fuse_ino_t fino EXT2FS_ATTR((unused)),
+		     unsigned int cmd,
+		     void *arg EXT2FS_ATTR((unused)),
+		     struct fuse_file_info *fp,
+		     unsigned int flags EXT2FS_ATTR((unused)),
+		     const void *indata, size_t insize,
+		     size_t outsize)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	const struct fuse_ctx *ctxt = fuse_req_ctx(req);
+	struct fuse4fs *ff = fuse4fs_get(req);
 	struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
+	void *outdata = NULL;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	FUSE4FS_CHECK_HANDLE(ff, fh);
+	if (outsize > 0) {
+		outdata = calloc(outsize, sizeof(char));
+		if (!outdata) {
+			fuse_reply_err(req, errno);
+			return;
+		}
+	}
+
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CHECK_HANDLE(req, fh);
 	fuse4fs_start(ff);
 	switch ((unsigned long) cmd) {
 #ifdef SUPPORT_I_FLAGS
 	case EXT2_IOC_GETFLAGS:
-		ret = ioctl_getflags(ff, fh, data);
+		ret = ioctl_getflags(ff, fh, outdata, &outsize);
 		break;
 	case EXT2_IOC_SETFLAGS:
-		ret = ioctl_setflags(ff, fh, data);
+		ret = ioctl_setflags(ff, ctxt, fh, indata, insize);
 		break;
 	case EXT2_IOC_GETVERSION:
-		ret = ioctl_getversion(ff, fh, data);
+		ret = ioctl_getversion(ff, fh, outdata, &outsize);
 		break;
 	case EXT2_IOC_SETVERSION:
-		ret = ioctl_setversion(ff, fh, data);
+		ret = ioctl_setversion(ff, ctxt, fh, indata, insize);
 		break;
 #endif
 #ifdef FS_IOC_FSGETXATTR
 	case FS_IOC_FSGETXATTR:
-		ret = ioctl_fsgetxattr(ff, fh, data);
+		ret = ioctl_fsgetxattr(ff, fh, outdata, &outsize);
 		break;
 	case FS_IOC_FSSETXATTR:
-		ret = ioctl_fssetxattr(ff, fh, data);
+		ret = ioctl_fssetxattr(ff, ctxt, fh, indata, insize);
 		break;
 #endif
 #ifdef FITRIM
 	case FITRIM:
-		ret = ioctl_fitrim(ff, fh, data);
+		ret = ioctl_fitrim(ff, fh, indata, insize, outdata, &outsize);
 		break;
 #endif
 	case EXT4_IOC_SHUTDOWN:
-		ret = ioctl_shutdown(ff, fh, data);
+		ret = ioctl_shutdown(ff, ctxt, fh, indata, insize);
 		break;
 	default:
 		dbg_printf(ff, "%s: Unknown ioctl %d\n", __func__, cmd);
@@ -4469,28 +4508,29 @@ static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
 	}
 	fuse4fs_finish(ff, ret);
 
-	return ret;
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_ioctl(req, 0, outdata, outsize);
+	free(outdata);
 }
 
-static int op_bmap(const char *path, size_t blocksize EXT2FS_ATTR((unused)),
-		   uint64_t *idx)
+static void op_bmap(fuse_req_t req, fuse_ino_t fino,
+		    size_t blocksize EXT2FS_ATTR((unused)), uint64_t idx)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = fuse4fs_get(req);
 	ext2_filsys fs;
 	ext2_ino_t ino;
+	blk64_t blkno;
 	errcode_t err;
 	int ret = 0;
 
-	FUSE4FS_CHECK_CONTEXT(ff);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CONVERT_FINO(req, &ino, fino);
 	fs = fuse4fs_start(ff);
-	err = ext2fs_namei(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, path, &ino);
-	if (err) {
-		ret = translate_error(fs, 0, err);
-		goto out;
-	}
-	dbg_printf(ff, "%s: ino=%d blk=%"PRIu64"\n", __func__, ino, *idx);
+	dbg_printf(ff, "%s: ino=%d blk=%"PRIu64"\n", __func__, ino, idx);
 
-	err = ext2fs_bmap2(fs, ino, NULL, NULL, 0, *idx, 0, (blk64_t *)idx);
+	err = ext2fs_bmap2(fs, ino, NULL, NULL, 0, idx, 0, &blkno);
 	if (err) {
 		ret = translate_error(fs, ino, err);
 		goto out;
@@ -4498,7 +4538,10 @@ static int op_bmap(const char *path, size_t blocksize EXT2FS_ATTR((unused)),
 
 out:
 	fuse4fs_finish(ff, ret);
-	return ret;
+	if (ret)
+		fuse_reply_err(req, -ret);
+	else
+		fuse_reply_bmap(req, blkno);
 }
 
 #ifdef SUPPORT_FALLOCATE
@@ -4741,20 +4784,22 @@ static int fuse4fs_zero_range(struct fuse4fs *ff,
 	return ret;
 }
 
-static int op_fallocate(const char *path EXT2FS_ATTR((unused)), int mode,
-			off_t offset, off_t len,
-			struct fuse_file_info *fp)
+static void op_fallocate(fuse_req_t req, fuse_ino_t fino EXT2FS_ATTR((unused)),
+			 int mode, off_t offset, off_t len,
+			 struct fuse_file_info *fp)
 {
-	struct fuse4fs *ff = fuse4fs_get();
+	struct fuse4fs *ff = fuse4fs_get(req);
 	struct fuse4fs_file_handle *fh = fuse4fs_get_handle(fp);
 	int ret;
 
 	/* Catch unknown flags */
-	if (mode & ~(FL_ZERO_RANGE_FLAG | FL_PUNCH_HOLE_FLAG | FL_KEEP_SIZE_FLAG))
-		return -EOPNOTSUPP;
+	if (mode & ~(FL_ZERO_RANGE_FLAG | FL_PUNCH_HOLE_FLAG | FL_KEEP_SIZE_FLAG)) {
+		fuse_reply_err(req, EOPNOTSUPP);
+		return;
+	}
 
-	FUSE4FS_CHECK_CONTEXT(ff);
-	FUSE4FS_CHECK_HANDLE(ff, fh);
+	FUSE4FS_CHECK_CONTEXT(req);
+	FUSE4FS_CHECK_HANDLE(req, fh);
 	fuse4fs_start(ff);
 	if (!fuse4fs_is_writeable(ff)) {
 		ret = -EROFS;
@@ -4774,12 +4819,13 @@ static int op_fallocate(const char *path EXT2FS_ATTR((unused)), int mode,
 		ret = fuse4fs_allocate_range(ff, fh, mode, offset, len);
 out:
 	fuse4fs_finish(ff, ret);
-
-	return ret;
+	fuse_reply_err(req, -ret);
 }
 #endif /* SUPPORT_FALLOCATE */
 
-static struct fuse_operations fs_ops = {
+static struct fuse_lowlevel_ops fs_ops = {
+	.lookup = op_lookup,
+	.setattr = op_setattr,
 	.init = op_init,
 	.destroy = op_destroy,
 	.getattr = op_getattr,
@@ -4791,9 +4837,6 @@ static struct fuse_operations fs_ops = {
 	.symlink = op_symlink,
 	.rename = op_rename,
 	.link = op_link,
-	.chmod = op_chmod,
-	.chown = op_chown,
-	.truncate = op_truncate,
 	.open = op_open,
 	.read = op_read,
 	.write = op_write,
@@ -4806,11 +4849,11 @@ static struct fuse_operations fs_ops = {
 	.removexattr = op_removexattr,
 	.opendir = op_open,
 	.readdir = op_readdir,
+	.readdirplus = op_readdirplus,
 	.releasedir = op_release,
 	.fsyncdir = op_fsync,
 	.access = op_access,
 	.create = op_create,
-	.utimens = op_utimens,
 	.bmap = op_bmap,
 #ifdef SUPERFLUOUS
 	.lock = op_lock,
@@ -4959,8 +5002,8 @@ static int fuse4fs_opt_proc(void *data, const char *arg,
 	"\n",
 			outargs->argv[0]);
 		if (key == FUSE4FS_HELPFULL) {
-			fuse_opt_add_arg(outargs, "-h");
-			fuse_main(outargs->argc, outargs->argv, &fs_ops, NULL);
+			printf("FUSE options:\n");
+			fuse_cmdline_help();
 		} else {
 			fprintf(stderr, "Try --helpfull to get a list of "
 				"all flags, including the FUSE options.\n");
@@ -4970,8 +5013,7 @@ static int fuse4fs_opt_proc(void *data, const char *arg,
 	case FUSE4FS_VERSION:
 		fprintf(stderr, "fuse4fs %s (%s)\n", E2FSPROGS_VERSION,
 			E2FSPROGS_DATE);
-		fuse_opt_add_arg(outargs, "--version");
-		fuse_main(outargs->argc, outargs->argv, &fs_ops, NULL);
+		fprintf(stderr, "FUSE library version %s\n", fuse_pkgversion());
 		exit(0);
 	}
 	return 1;
@@ -5040,6 +5082,106 @@ static void fuse4fs_com_err_proc(const char *whoami, errcode_t code,
 	fflush(stderr);
 }
 
+static int fuse4fs_main(struct fuse_args *args, struct fuse4fs *ff)
+{
+	struct fuse_cmdline_opts opts;
+	struct fuse_session *se;
+	struct fuse_loop_config *loop_config = NULL;
+	int ret;
+
+	if (fuse_parse_cmdline(args, &opts) != 0) {
+		ret = 1;
+		goto out;
+	}
+
+	if (ff->debug)
+		opts.debug = true;
+
+	if (opts.show_help) {
+		fuse_cmdline_help();
+		ret = 0;
+		goto out_free_opts;
+	}
+
+	if (opts.show_version) {
+		printf("FUSE library version %s\n", fuse_pkgversion());
+		ret = 0;
+		goto out_free_opts;
+	}
+
+	if (!opts.mountpoint) {
+		fprintf(stderr, "error: no mountpoint specified\n");
+		ret = 2;
+		goto out_free_opts;
+	}
+
+	se = fuse_session_new(args, &fs_ops, sizeof(fs_ops), ff);
+	if (se == NULL) {
+		ret = 3;
+		goto out_free_opts;
+	}
+	ff->fuse = se;
+
+	if (fuse_session_mount(se, opts.mountpoint) != 0) {
+		ret = 4;
+		goto out_destroy_session;
+	}
+
+	if (fuse_daemonize(opts.foreground) != 0) {
+		ret = 5;
+		goto out_unmount;
+	}
+
+	/*
+	 * Configure logging a second time, because libfuse might have
+	 * redirected std{out,err} as part of daemonization.  If this fails,
+	 * give up and move on.
+	 */
+	fuse4fs_setup_logging(ff);
+	if (ff->logfd >= 0)
+		close(ff->logfd);
+	ff->logfd = -1;
+
+	if (fuse_set_signal_handlers(se) != 0) {
+		ret = 6;
+		goto out_unmount;
+	}
+
+	loop_config = fuse_loop_cfg_create();
+	if (loop_config == NULL) {
+		ret = 7;
+		goto out_remove_signal_handlers;
+	}
+
+	/*
+	 * Since there's a Big Kernel Lock around all the libext2fs code, we
+	 * only need to start three threads -- one to decode a request, another
+	 * to do the filesystem work, and a third to transmit the reply.
+	 */
+	fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
+	fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
+	fuse_loop_cfg_set_max_threads(loop_config, 3);
+
+	if (fuse_session_loop_mt(se, loop_config) != 0) {
+		ret = 8;
+		goto out_loopcfg;
+	}
+
+out_loopcfg:
+	fuse_loop_cfg_destroy(loop_config);
+out_remove_signal_handlers:
+	fuse_remove_signal_handlers(se);
+out_unmount:
+	fuse_session_unmount(se);
+out_destroy_session:
+	ff->fuse = NULL;
+	fuse_session_destroy(se);
+out_free_opts:
+	free(opts.mountpoint);
+out:
+	return ret;
+}
+
 int main(int argc, char *argv[])
 {
 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
@@ -5178,8 +5320,7 @@ int main(int argc, char *argv[])
 	get_random_bytes(&fctx.next_generation, sizeof(unsigned int));
 
 	/* Set up default fuse parameters */
-	snprintf(extra_args, BUFSIZ, "-okernel_cache,subtype=%s,"
-		 "fsname=%s,attr_timeout=0",
+	snprintf(extra_args, BUFSIZ, "-osubtype=%s,fsname=%s",
 		 get_subtype(argv[0]),
 		 fctx.device);
 	if (fctx.no_default_opts == 0)
@@ -5218,7 +5359,7 @@ int main(int argc, char *argv[])
 	}
 
 	pthread_mutex_init(&fctx.bfl, NULL);
-	ret = fuse_main(args.argc, args.argv, &fs_ops, &fctx);
+	ret = fuse4fs_main(&args, &fctx);
 	pthread_mutex_destroy(&fctx.bfl);
 
 	switch(ret) {


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ