From: Darrick J. Wong <djwong@xxxxxxxxxx> Create a new ->getattr_iflags function so that iomap filesystems can set the appropriate in-kernel inode flags on instantiation. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- include/fuse.h | 7 ++ lib/fuse.c | 191 ++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 151 insertions(+), 47 deletions(-) diff --git a/include/fuse.h b/include/fuse.h index 1357f4319bcc21..7256f43fd5c39a 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -889,6 +889,13 @@ struct fuse_operations { uint64_t attr_ino, off_t pos_in, size_t written_in, uint32_t ioendflags_in, int error_in, uint64_t new_addr_in); + + /** + * Get file attributes and FUSE_IFLAG_* flags. Otherwise the same as + * getattr. + */ + int (*getattr_iflags) (const char *path, struct stat *buf, + unsigned int *iflags, struct fuse_file_info *fi); }; /** Extra context that may be needed by some filesystems diff --git a/lib/fuse.c b/lib/fuse.c index 725ab615d456e3..6b211084e2175a 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -123,6 +123,7 @@ struct fuse { struct list_head partial_slabs; struct list_head full_slabs; pthread_t prune_thread; + bool want_iflags; }; struct lock { @@ -144,6 +145,7 @@ struct node { char *name; uint64_t nlookup; int open_count; + unsigned int iflags; struct timespec stat_updated; struct timespec mtime; off_t size; @@ -1628,6 +1630,24 @@ int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, return fs->op.getattr(path, buf, fi); } +static int fuse_fs_getattr_iflags(struct fuse_fs *fs, const char *path, + struct stat *buf, unsigned int *iflags, + struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (!fs->op.getattr_iflags) + return -ENOSYS; + + if (fs->debug) { + char buf[10]; + + fuse_log(FUSE_LOG_DEBUG, "getattr_iflags[%s] %s\n", + file_info_string(fi, buf, sizeof(buf)), + path); + } + return fs->op.getattr_iflags(path, buf, iflags, fi); +} + int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, const char *newpath, unsigned int flags) { @@ -2473,7 +2493,7 @@ static void update_stat(struct node *node, const struct stat *stbuf) } static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name, - struct fuse_entry_param *e) + struct fuse_entry_param *e, unsigned int *iflags) { struct node *node; @@ -2491,25 +2511,64 @@ static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name, pthread_mutex_unlock(&f->lock); } set_stat(f, e->ino, &e->attr); + *iflags = node->iflags; return 0; } +static int lookup_and_update(struct fuse *f, fuse_ino_t nodeid, + const char *name, struct fuse_entry_param *e, + unsigned int iflags) +{ + struct node *node; + + node = find_node(f, nodeid, name); + if (node == NULL) + return -ENOMEM; + + e->ino = node->nodeid; + e->generation = node->generation; + e->entry_timeout = f->conf.entry_timeout; + e->attr_timeout = f->conf.attr_timeout; + if (f->conf.auto_cache) { + pthread_mutex_lock(&f->lock); + update_stat(node, &e->attr); + pthread_mutex_unlock(&f->lock); + } + set_stat(f, e->ino, &e->attr); + node->iflags = iflags; + return 0; +} + +static int getattr(struct fuse *f, const char *path, struct stat *buf, + unsigned int *iflags, struct fuse_file_info *fi) +{ + if (f->want_iflags) + return fuse_fs_getattr_iflags(f->fs, path, buf, iflags, fi); + return fuse_fs_getattr(f->fs, path, buf, fi); +} + static int lookup_path(struct fuse *f, fuse_ino_t nodeid, const char *name, const char *path, - struct fuse_entry_param *e, struct fuse_file_info *fi) + struct fuse_entry_param *e, unsigned int *iflags, + struct fuse_file_info *fi) { int res; memset(e, 0, sizeof(struct fuse_entry_param)); - res = fuse_fs_getattr(f->fs, path, &e->attr, fi); - if (res == 0) { - res = do_lookup(f, nodeid, name, e); - if (res == 0 && f->conf.debug) { - fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu\n", - (unsigned long long) e->ino); - } - } - return res; + *iflags = 0; + res = getattr(f, path, &e->attr, iflags, fi); + if (res) + return res; + + res = lookup_and_update(f, nodeid, name, e, *iflags); + if (res) + return res; + + if (f->conf.debug) + fuse_log(FUSE_LOG_DEBUG, " NODEID: %llu iflags 0x%x\n", + (unsigned long long) e->ino, *iflags); + + return 0; } static struct fuse_context_i *fuse_get_context_internal(void) @@ -2593,11 +2652,14 @@ static inline void reply_err(fuse_req_t req, int err) } static void reply_entry(fuse_req_t req, const struct fuse_entry_param *e, - int err) + unsigned int iflags, int err) { if (!err) { struct fuse *f = req_fuse(req); - if (fuse_reply_entry(req, e) == -ENOENT) { + int entry_res; + + entry_res = fuse_reply_entry_iflags(req, e, iflags); + if (entry_res == -ENOENT) { /* Skip forget for negative result */ if (e->ino != 0) forget_node(f, e->ino, 1); @@ -2638,6 +2700,9 @@ static void fuse_lib_init(void *data, struct fuse_conn_info *conn) /* Disable the receiving and processing of FUSE_INTERRUPT requests */ conn->no_interrupt = 1; } + + if (conn->want_ext & FUSE_CAP_IOMAP) + f->want_iflags = true; } void fuse_fs_destroy(struct fuse_fs *fs) @@ -2661,6 +2726,7 @@ static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *path; + unsigned int iflags = 0; int err; struct node *dot = NULL; @@ -2675,7 +2741,7 @@ static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, dot = get_node_nocheck(f, parent); if (dot == NULL) { pthread_mutex_unlock(&f->lock); - reply_entry(req, &e, -ESTALE); + reply_entry(req, &e, -ESTALE, 0); return; } dot->refctr++; @@ -2695,7 +2761,7 @@ static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, if (f->conf.debug) fuse_log(FUSE_LOG_DEBUG, "LOOKUP %s\n", path); fuse_prepare_interrupt(f, req, &d); - err = lookup_path(f, parent, name, path, &e, NULL); + err = lookup_path(f, parent, name, path, &e, &iflags, NULL); if (err == -ENOENT && f->conf.negative_timeout != 0.0) { e.ino = 0; e.entry_timeout = f->conf.negative_timeout; @@ -2709,7 +2775,7 @@ static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent, unref_node(f, dot); pthread_mutex_unlock(&f->lock); } - reply_entry(req, &e, err); + reply_entry(req, &e, iflags, err); } static void do_forget(struct fuse *f, fuse_ino_t ino, uint64_t nlookup) @@ -2745,6 +2811,7 @@ static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse *f = req_fuse_prepare(req); struct stat buf; char *path; + unsigned int iflags = 0; int err; memset(&buf, 0, sizeof(buf)); @@ -2756,7 +2823,7 @@ static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, if (!err) { struct fuse_intr_data d; fuse_prepare_interrupt(f, req, &d); - err = fuse_fs_getattr(f->fs, path, &buf, fi); + err = getattr(f, path, &buf, &iflags, fi); fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } @@ -2769,9 +2836,11 @@ static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino, buf.st_nlink--; if (f->conf.auto_cache) update_stat(node, &buf); + node->iflags = iflags; pthread_mutex_unlock(&f->lock); set_stat(f, ino, &buf); - fuse_reply_attr(req, &buf, f->conf.attr_timeout); + fuse_reply_attr_iflags(req, &buf, iflags, + f->conf.attr_timeout); } else reply_err(req, err); } @@ -2874,6 +2943,7 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, struct fuse *f = req_fuse_prepare(req); struct stat buf; char *path; + unsigned int iflags = 0; int err; memset(&buf, 0, sizeof(buf)); @@ -2932,19 +3002,23 @@ static void fuse_lib_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, err = fuse_fs_utimens(f->fs, path, tv, fi); } if (!err) { - err = fuse_fs_getattr(f->fs, path, &buf, fi); + err = getattr(f, path, &buf, &iflags, fi); } fuse_finish_interrupt(f, req, &d); free_path(f, ino, path); } if (!err) { - if (f->conf.auto_cache) { - pthread_mutex_lock(&f->lock); - update_stat(get_node(f, ino), &buf); - pthread_mutex_unlock(&f->lock); - } + struct node *node; + + pthread_mutex_lock(&f->lock); + node = get_node(f, ino); + if (f->conf.auto_cache) + update_stat(node, &buf); + node->iflags = iflags; + pthread_mutex_unlock(&f->lock); set_stat(f, ino, &buf); - fuse_reply_attr(req, &buf, f->conf.attr_timeout); + fuse_reply_attr_iflags(req, &buf, iflags, + f->conf.attr_timeout); } else reply_err(req, err); } @@ -2995,6 +3069,7 @@ static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *path; + unsigned int iflags = 0; int err; err = get_path_name(f, parent, name, &path); @@ -3011,7 +3086,7 @@ static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, err = fuse_fs_create(f->fs, path, mode, &fi); if (!err) { err = lookup_path(f, parent, name, path, &e, - &fi); + &iflags, &fi); fuse_fs_release(f->fs, path, &fi); } } @@ -3019,12 +3094,12 @@ static void fuse_lib_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, err = fuse_fs_mknod(f->fs, path, mode, rdev); if (!err) err = lookup_path(f, parent, name, path, &e, - NULL); + &iflags, NULL); } fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } - reply_entry(req, &e, err); + reply_entry(req, &e, iflags, err); } static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, @@ -3033,6 +3108,7 @@ static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *path; + unsigned int iflags = 0; int err; err = get_path_name(f, parent, name, &path); @@ -3042,11 +3118,12 @@ static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_prepare_interrupt(f, req, &d); err = fuse_fs_mkdir(f->fs, path, mode); if (!err) - err = lookup_path(f, parent, name, path, &e, NULL); + err = lookup_path(f, parent, name, path, &e, &iflags, + NULL); fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } - reply_entry(req, &e, err); + reply_entry(req, &e, iflags, err); } static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent, @@ -3116,6 +3193,7 @@ static void fuse_lib_symlink(fuse_req_t req, const char *linkname, struct fuse *f = req_fuse_prepare(req); struct fuse_entry_param e; char *path; + unsigned int iflags = 0; int err; err = get_path_name(f, parent, name, &path); @@ -3125,11 +3203,12 @@ static void fuse_lib_symlink(fuse_req_t req, const char *linkname, fuse_prepare_interrupt(f, req, &d); err = fuse_fs_symlink(f->fs, linkname, path); if (!err) - err = lookup_path(f, parent, name, path, &e, NULL); + err = lookup_path(f, parent, name, path, &e, &iflags, + NULL); fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } - reply_entry(req, &e, err); + reply_entry(req, &e, iflags, err); } static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir, @@ -3177,6 +3256,7 @@ static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, struct fuse_entry_param e; char *oldpath; char *newpath; + unsigned int iflags = 0; int err; err = get_path2(f, ino, NULL, newparent, newname, @@ -3188,11 +3268,11 @@ static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, err = fuse_fs_link(f->fs, oldpath, newpath); if (!err) err = lookup_path(f, newparent, newname, newpath, - &e, NULL); + &e, &iflags, NULL); fuse_finish_interrupt(f, req, &d); free_path2(f, ino, newparent, NULL, NULL, oldpath, newpath); } - reply_entry(req, &e, err); + reply_entry(req, &e, iflags, err); } static void fuse_do_release(struct fuse *f, fuse_ino_t ino, const char *path, @@ -3235,6 +3315,7 @@ static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, struct fuse_intr_data d; struct fuse_entry_param e; char *path; + unsigned int iflags; int err; err = get_path_name(f, parent, name, &path); @@ -3242,7 +3323,8 @@ static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, fuse_prepare_interrupt(f, req, &d); err = fuse_fs_create(f->fs, path, mode, fi); if (!err) { - err = lookup_path(f, parent, name, path, &e, fi); + err = lookup_path(f, parent, name, path, &e, + &iflags, fi); if (err) fuse_fs_release(f->fs, path, fi); else if (!S_ISREG(e.attr.st_mode)) { @@ -3262,10 +3344,14 @@ static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent, fuse_finish_interrupt(f, req, &d); } if (!err) { + int create_res; + pthread_mutex_lock(&f->lock); get_node(f, e.ino)->open_count++; pthread_mutex_unlock(&f->lock); - if (fuse_reply_create(req, &e, fi) == -ENOENT) { + + create_res = fuse_reply_create_iflags(req, &e, iflags, fi); + if (create_res == -ENOENT) { /* The open syscall was interrupted, so it must be cancelled */ fuse_do_release(f, e.ino, path, fi); @@ -3299,13 +3385,16 @@ static void open_auto_cache(struct fuse *f, fuse_ino_t ino, const char *path, if (diff_timespec(&now, &node->stat_updated) > f->conf.ac_attr_timeout) { struct stat stbuf; + unsigned int iflags = 0; int err; + pthread_mutex_unlock(&f->lock); - err = fuse_fs_getattr(f->fs, path, &stbuf, fi); + err = getattr(f, path, &stbuf, &iflags, fi); pthread_mutex_lock(&f->lock); - if (!err) + if (!err) { update_stat(node, &stbuf); - else + node->iflags = iflags; + } else node->cache_valid = 0; } } @@ -3634,6 +3723,7 @@ static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp, .ino = 0, }; struct fuse *f = dh->fuse; + unsigned int iflags = 0; int res; if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) { @@ -3658,6 +3748,7 @@ static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp, if (off) { size_t newlen; + size_t thislen; if (dh->filled) { dh->error = -EIO; @@ -3673,7 +3764,8 @@ static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp, if (statp && (flags & FUSE_FILL_DIR_PLUS)) { if (!is_dot_or_dotdot(name)) { - res = do_lookup(f, dh->nodeid, name, &e); + res = do_lookup(f, dh->nodeid, name, &e, + &iflags); if (res) { dh->error = res; return 1; @@ -3681,10 +3773,12 @@ static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp, } } - newlen = dh->len + - fuse_add_direntry_plus(dh->req, dh->contents + dh->len, - dh->needlen - dh->len, name, - &e, off); + thislen = fuse_add_direntry_plus_iflags(dh->req, + dh->contents + dh->len, + dh->needlen - dh->len, + name, iflags, &e, off); + newlen = dh->len + thislen; + if (newlen > dh->needlen) return 1; dh->len = newlen; @@ -3771,6 +3865,7 @@ static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh, unsigned rem = dh->needlen - dh->len; unsigned thislen; unsigned newlen; + unsigned int iflags = 0; pos++; if (flags & FUSE_READDIR_PLUS) { @@ -3782,15 +3877,17 @@ static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh, if (de->flags & FUSE_FILL_DIR_PLUS && !is_dot_or_dotdot(de->name)) { res = do_lookup(dh->fuse, dh->nodeid, - de->name, &e); + de->name, &e, &iflags); if (res) { dh->error = res; return 1; } } - thislen = fuse_add_direntry_plus(req, p, rem, - de->name, &e, pos); + thislen = fuse_add_direntry_plus_iflags(req, p, rem, + de->name, + iflags, &e, + pos); } else { thislen = fuse_add_direntry(req, p, rem, de->name, &de->stat, pos);