From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxx> Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- 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) {