From 11509ce3fc6a36b6e3c094bf8aa11820f17d0ede Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 26 Oct 2005 16:04:04 +0000 Subject: [PATCH] add ftruncate() method --- ChangeLog | 11 ++++++++--- example/fusexmp_fh.c | 15 +++++++++++++++ include/fuse.h | 20 +++++++++++++++++--- include/fuse_lowlevel.h | 12 +++++++++++- kernel/dir.c | 34 ++++++++++++++++++---------------- kernel/fuse_kernel.h | 18 ++++++++++++++++-- lib/fuse.c | 11 ++++++----- lib/fuse_lowlevel.c | 18 ++++++++++++------ 8 files changed, 103 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index a92cf6f..c6dfda4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,15 +1,20 @@ 2005-10-26 Miklos Szeredi + * Change kernel ABI version to 7.3 + * Add ACCESS operation. This is called from the access() system call if 'default_permissions' mount option is not given, and is not called on kernels 2.4.* - * Fix kernel module compile if kernel source and build directories - differ. Report and initial patch by John Eastman - * Add atomic CREATE+OPEN operation. This will only work with 2.6.15 (presumably) or later Linux kernels. + * Add ftruncate() method. This will only work with 2.6.15 + (presumably) or later Linux kernels. + + * Fix kernel module compile if kernel source and build directories + differ. Report and initial patch by John Eastman + 2005-10-18 Miklos Szeredi * lib: optimize buffer reallocation in fill_dir. diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c index 262a868..2f4b187 100644 --- a/example/fusexmp_fh.c +++ b/example/fusexmp_fh.c @@ -204,6 +204,20 @@ static int xmp_truncate(const char *path, off_t size) return 0; } +static int xmp_ftruncate(const char *path, off_t size, + struct fuse_file_info *fi) +{ + int res; + + (void) path; + + res = ftruncate(fi->fh, size); + if(res == -1) + return -errno; + + return 0; +} + static int xmp_utime(const char *path, struct utimbuf *buf) { int res; @@ -354,6 +368,7 @@ static struct fuse_operations xmp_oper = { .chmod = xmp_chmod, .chown = xmp_chown, .truncate = xmp_truncate, + .ftruncate = xmp_ftruncate, .utime = xmp_utime, .create = xmp_create, .open = xmp_open, diff --git a/include/fuse.h b/include/fuse.h index 290e9d3..6d7e54b 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -63,9 +63,9 @@ typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type, * * All methods are optional, but some are essential for a useful * filesystem (e.g. getattr). Open, flush, release, fsync, opendir, - * releasedir, fsyncdir, access, create, init and destroy are special - * purpose methods, without which a full featured filesystem can still - * be implemented. + * releasedir, fsyncdir, access, create, ftruncate, init and destroy + * are special purpose methods, without which a full featured + * filesystem can still be implemented. */ struct fuse_operations { /** Get file attributes. @@ -324,6 +324,20 @@ struct fuse_operations { * Introduced in version 2.5 */ int (*create) (const char *, mode_t, struct fuse_file_info *); + + /** + * Change the size of an open file + * + * This method is called instead of the truncate() method if the + * truncation was invoked from an ftruncate() system call. + * + * If this method is not implemented or under Linux kernel + * versions earlier than 2.6.15, the truncate() method will be + * called instead. + * + * Introduced in version 2.5 + */ + int (*ftruncate) (const char *, off_t, struct fuse_file_info *); }; /** Extra context that may be needed by some filesystems diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index e9c64ce..a027163 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -198,6 +198,13 @@ struct fuse_lowlevel_ops { * bitmask contain valid values. Other members contain undefined * values. * + * If the setattr was invoked from the ftruncate() system call + * under Linux kernel versions 2.6.15 or later, the fi->fh will + * contain the value set by the open method or will be undefined + * if the open method didn't set any value. Otherwise (not + * ftruncate call, or kernel version earlier than 2.6.15) the fi + * parameter will be NULL. + * * Valid replies: * fuse_reply_attr() * fuse_reply_err() @@ -206,7 +213,10 @@ struct fuse_lowlevel_ops { * @param ino the inode number * @param attr the attributes * @param to_set bit mask of attributes which should be set - * @param fi for future use, currently always NULL + * @param fi file information, or NULL + * + * Changed in version 2.5: + * file information filled in for ftruncate */ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi); diff --git a/kernel/dir.c b/kernel/dir.c index bc792cb..48df469 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -838,34 +838,36 @@ static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync) return file ? fuse_fsync_common(file, de, datasync, 1) : 0; } -static unsigned iattr_to_fattr(struct iattr *iattr, struct fuse_attr *fattr) +static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) { unsigned ivalid = iattr->ia_valid; - unsigned fvalid = 0; - - memset(fattr, 0, sizeof(*fattr)); if (ivalid & ATTR_MODE) - fvalid |= FATTR_MODE, fattr->mode = iattr->ia_mode; + arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode; if (ivalid & ATTR_UID) - fvalid |= FATTR_UID, fattr->uid = iattr->ia_uid; + arg->valid |= FATTR_UID, arg->uid = iattr->ia_uid; if (ivalid & ATTR_GID) - fvalid |= FATTR_GID, fattr->gid = iattr->ia_gid; + arg->valid |= FATTR_GID, arg->gid = iattr->ia_gid; if (ivalid & ATTR_SIZE) - fvalid |= FATTR_SIZE, fattr->size = iattr->ia_size; + arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; /* You can only _set_ these together (they may change by themselves) */ if ((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) { - fvalid |= FATTR_ATIME | FATTR_MTIME; + arg->valid |= FATTR_ATIME | FATTR_MTIME; #ifdef KERNEL_2_6 - fattr->atime = iattr->ia_atime.tv_sec; - fattr->mtime = iattr->ia_mtime.tv_sec; + arg->atime = iattr->ia_atime.tv_sec; + arg->mtime = iattr->ia_mtime.tv_sec; #else - fattr->atime = iattr->ia_atime; - fattr->mtime = iattr->ia_mtime; + arg->atime = iattr->ia_atime; + arg->mtime = iattr->ia_mtime; #endif } - - return fvalid; +#ifdef ATTR_FILE + if (ivalid & ATTR_FILE) { + struct fuse_file *ff = iattr->ia_file->private_data; + arg->valid |= FATTR_FH; + arg->fh = ff->fh; + } +#endif } static int fuse_setattr(struct dentry *entry, struct iattr *attr) @@ -904,7 +906,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) return -EINTR; memset(&inarg, 0, sizeof(inarg)); - inarg.valid = iattr_to_fattr(attr, &inarg.attr); + iattr_to_fattr(attr, &inarg); req->in.h.opcode = FUSE_SETATTR; req->in.h.nodeid = get_node_id(inode); req->inode = inode; diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h index 23f8315..48b60fc 100644 --- a/kernel/fuse_kernel.h +++ b/kernel/fuse_kernel.h @@ -42,7 +42,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 2 +#define FUSE_KERNEL_MINOR_VERSION 3 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -89,6 +89,7 @@ struct fuse_kstatfs { #define FATTR_SIZE (1 << 3) #define FATTR_ATIME (1 << 4) #define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) /** * Flags returned by the OPEN request @@ -182,7 +183,20 @@ struct fuse_link_in { struct fuse_setattr_in { __u32 valid; __u32 padding; - struct fuse_attr attr; + __u64 fh; + __u64 size; + __u64 unused1; + __u64 atime; + __u64 mtime; + __u64 unused2; + __u32 atimensec; + __u32 mtimensec; + __u32 unused3; + __u32 mode; + __u32 unused4; + __u32 uid; + __u32 gid; + __u32 unused5; }; struct fuse_open_in { diff --git a/lib/fuse.c b/lib/fuse.c index 5dad478..1399dfd 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -674,12 +674,15 @@ static int do_chown(struct fuse *f, const char *path, struct stat *attr, return err; } -static int do_truncate(struct fuse *f, const char *path, struct stat *attr) +static int do_truncate(struct fuse *f, const char *path, struct stat *attr, + struct fuse_file_info *fi) { int err; err = -ENOSYS; - if (f->op.truncate) + if (fi && f->op.ftruncate) + err = f->op.ftruncate(path, attr->st_size, fi); + else if (f->op.truncate) err = f->op.truncate(path, attr->st_size); return err; @@ -706,8 +709,6 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, char *path; int err; - (void) fi; - err = -ENOENT; pthread_rwlock_rdlock(&f->tree_lock); path = get_path(f, ino); @@ -720,7 +721,7 @@ static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID))) err = do_chown(f, path, attr, valid); if (!err && (valid & FUSE_SET_ATTR_SIZE)) - err = do_truncate(f, path, attr); + err = do_truncate(f, path, attr, fi); if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) err = do_utime(f, path, attr); if (!err) diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 9379968..b546312 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -95,7 +95,7 @@ static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) #endif } -static void convert_attr(const struct fuse_attr *attr, struct stat *stbuf) +static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) { stbuf->st_mode = attr->mode; stbuf->st_uid = attr->uid; @@ -103,11 +103,9 @@ static void convert_attr(const struct fuse_attr *attr, struct stat *stbuf) stbuf->st_size = attr->size; stbuf->st_atime = attr->atime; stbuf->st_mtime = attr->mtime; - stbuf->st_ctime = attr->ctime; #ifdef HAVE_STRUCT_STAT_ST_ATIM stbuf->st_atim.tv_nsec = attr->atimensec; stbuf->st_mtim.tv_nsec = attr->mtimensec; - stbuf->st_ctim.tv_nsec = attr->ctimensec; #endif } @@ -366,12 +364,20 @@ static void do_getattr(fuse_req_t req, fuse_ino_t nodeid) } static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, - struct fuse_setattr_in *arg, struct fuse_file_info *fi) + struct fuse_setattr_in *arg) { if (req->f->op.setattr) { + struct fuse_file_info *fi = NULL; + struct fuse_file_info fi_store; struct stat stbuf; memset(&stbuf, 0, sizeof(stbuf)); - convert_attr(&arg->attr, &stbuf); + convert_attr(arg, &stbuf); + if (arg->valid & FATTR_FH) { + arg->valid &= ~FATTR_FH; + memset(&fi_store, 0, sizeof(fi_store)); + fi = &fi_store; + fi->fh = arg->fh; + } req->f->op.setattr(req, nodeid, &stbuf, arg->valid, fi); } else fuse_reply_err(req, ENOSYS); @@ -754,7 +760,7 @@ static void fuse_ll_process(void *data, const char *buf, size_t len, break; case FUSE_SETATTR: - do_setattr(req, in->nodeid, (struct fuse_setattr_in *) inarg, NULL); + do_setattr(req, in->nodeid, (struct fuse_setattr_in *) inarg); break; case FUSE_READLINK: