From: Anatol P. <ana...@gm...> - 2012-04-06 18:56:19
|
fallocate filesystem operation preallocates media space for the given file. If fallocate returns success then any subsequent write to the given range never fails with 'not enough space' error. Signed-off-by: Anatol Pomozov <ana...@gm...> --- example/fusexmp.c | 36 ++++++++++++++++++++++++++++++++++++ include/fuse.h | 14 ++++++++++++++ include/fuse_kernel.h | 16 +++++++++++++++- include/fuse_lowlevel.h | 22 ++++++++++++++++++++++ lib/fuse.c | 35 +++++++++++++++++++++++++++++++++++ lib/fuse_lowlevel.c | 15 +++++++++++++++ lib/fuse_versionscript | 1 + 7 files changed, 138 insertions(+), 1 deletion(-) diff --git a/example/fusexmp.c b/example/fusexmp.c index fa3fb4d..4996c74 100644 --- a/example/fusexmp.c +++ b/example/fusexmp.c @@ -310,6 +310,41 @@ static int xmp_fsync(const char *path, int isdatasync, return 0; } +static int xmp_fallocate(const char *path, struct fuse_file_info *fi, + off_t offset, off_t length) +{ + int fd; + int res; + + (void) fi; + fd = open(path, O_WRONLY); + if (fd == -1) + return -errno; + +#ifdef __APPLE__ + /* This code compiles but does not work as FUSE_FALLOCATED + not implemented in fuse4x yet. This part should probably be + sent as a separate patch after fuse4x gets FALLOCATE support. + */ + fstore_t prealloc_arg; + memset(&prealloc_arg, 0, sizeof(prealloc_arg)); + prealloc_arg.fst_flags = F_ALLOCATECONTIG; + prealloc_arg.fst_posmode = F_PEOFPOSMODE; + prealloc_arg.fst_offset = offset; + prealloc_arg.fst_length = length; + res = fcntl(fd, F_PREALLOCATE, &prealloc_arg); + + if (res == -1) + res = -errno; +#else + // TOASK errno is not set, res is positive error. Is it correct way? + res = -posix_fallocate(fd, offset, length); +#endif + + close(fd); + return res; +} + #ifdef HAVE_SETXATTR /* xattr operations are optional and can safely be left unimplemented */ static int xmp_setxattr(const char *path, const char *name, const char *value, @@ -371,6 +406,7 @@ static struct fuse_operations xmp_oper = { .statfs = xmp_statfs, .release = xmp_release, .fsync = xmp_fsync, + .fallocate = xmp_fallocate, #ifdef HAVE_SETXATTR .setxattr = xmp_setxattr, .getxattr = xmp_getxattr, diff --git a/include/fuse.h b/include/fuse.h index b05152d..7401ca1 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -569,6 +569,18 @@ struct fuse_operations { * Introduced in version 2.9 */ int (*flock) (const char *, struct fuse_file_info *, int op); + + /** + * Allocates space for an open file + * + * This function ensures that required space is allocated for specified + * file. If this function returns success then any subsequent write + * request to specified range is guaranteed not to fail because of lack + * of space on the file system media. + * + * Introduced in version 2.9 + */ + int (*fallocate) (const char *, struct fuse_file_info *, off_t, off_t); }; /** Extra context that may be needed by some filesystems @@ -864,6 +876,8 @@ int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, int fuse_fs_poll(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp); +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, off_t offset, off_t length); void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); void fuse_fs_destroy(struct fuse_fs *fs); diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 7b43cb0..909725d 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -80,6 +80,9 @@ * 7.18 * - add FUSE_IOCTL_DIR flag * - add FUSE_NOTIFY_DELETE + * + * 7.19 + * - add FUSE_FALLOCATE */ #ifndef _LINUX_FUSE_H @@ -116,7 +119,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 18 +#define FUSE_KERNEL_MINOR_VERSION 19 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -309,6 +312,7 @@ enum fuse_opcode { FUSE_POLL = 40, FUSE_NOTIFY_REPLY = 41, FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -602,6 +606,16 @@ struct fuse_notify_poll_wakeup_out { __u64 kh; }; +struct fuse_fallocate_in { + __u64 fh; + __u64 offset; + __u64 length; +// TOASK macosx expects that syscall returns "number of allocated bytes" +// do we need to add fuse_fallocate_out response because of it? +// Or maybe macosx syscall should return all-or-nothing - 0 on failure and "size" +// on success. +}; + struct fuse_in_header { __u32 len; __u32 opcode; diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index 3ecc46e..2d4813b 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -996,6 +996,28 @@ struct fuse_lowlevel_ops { */ void (*flock) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op); + + /** + * Allocate requested space. If this function returns success then + * subsequent writes to the specified range shall not fail due to the lack + * of free space on the file system storage media. + * + * Introduced in version 2.9 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param fi file information + * @param offset starting point for allocated region + * @param length size of allocated region + */ + // TOASK: do we need to pass fuse_file_info structure here? + // and in what order, some functions put fuse_file_info at the end of the + // args list, some other put it in the middle + void (*fallocate) (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, off_t offset, off_t length); }; /** diff --git a/lib/fuse.c b/lib/fuse.c index e01f450..3d3a84f 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -2280,6 +2280,22 @@ int fuse_fs_poll(struct fuse_fs *fs, const char *path, return -ENOSYS; } +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, + struct fuse_file_info *fi, off_t offset, off_t length) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fallocate) { + if (fs->debug) + fprintf(stderr, "fallocate %s offset: %llu, length: %llu\n", + path, + (unsigned long long) offset, + (unsigned long long) length); + + return fs->op.fallocate(path, fi, offset, length); + } else + return -ENOSYS; +} + static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) { struct node *node; @@ -3966,6 +3982,24 @@ static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } +static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, off_t offset, off_t length) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path; + int err; + + err = get_path_nullok(f, ino, &path); + if (!err) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fallocate(f->fs, path, fi, offset, length); + fuse_finish_interrupt(f, req, &d); + free_path(f, ino, path); + } + reply_err(req, err); +} + static int clean_delay(struct fuse *f) { /* @@ -4060,6 +4094,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { .bmap = fuse_lib_bmap, .ioctl = fuse_lib_ioctl, .poll = fuse_lib_poll, + .fallocate = fuse_lib_fallocate, }; int fuse_notify_poll(struct fuse_pollhandle *ph) diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index a0d4887..18d1dda 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -1717,6 +1717,20 @@ static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) } } +static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + + if (req->f->op.fallocate) + req->f->op.fallocate(req, nodeid, &fi, arg->offset, arg->length); + else + fuse_reply_err(req, ENOSYS); +} + static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_init_in *arg = (struct fuse_init_in *) inarg; @@ -2258,6 +2272,7 @@ static struct { [FUSE_BMAP] = { do_bmap, "BMAP" }, [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, [FUSE_POLL] = { do_poll, "POLL" }, + [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, [FUSE_DESTROY] = { do_destroy, "DESTROY" }, [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 8fbaa51..ab4b0bc 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -186,6 +186,7 @@ FUSE_2.9 { fuse_buf_size; fuse_fs_read_buf; fuse_fs_write_buf; + fuse_fs_fallocate; fuse_lowlevel_notify_retrieve; fuse_lowlevel_notify_store; fuse_reply_data; -- 1.7.10.rc3 |
From: Miklos S. <mi...@sz...> - 2012-04-18 10:16:16
|
On Fri, Apr 6, 2012 at 8:56 PM, Anatol Pomozov <ana...@gm...> wrote: > fallocate filesystem operation preallocates media space for the given file. > If fallocate returns success then any subsequent write to the given range > never fails with 'not enough space' error. > > Signed-off-by: Anatol Pomozov <ana...@gm...> > --- > example/fusexmp.c | 36 ++++++++++++++++++++++++++++++++++++ > include/fuse.h | 14 ++++++++++++++ > include/fuse_kernel.h | 16 +++++++++++++++- > include/fuse_lowlevel.h | 22 ++++++++++++++++++++++ > lib/fuse.c | 35 +++++++++++++++++++++++++++++++++++ > lib/fuse_lowlevel.c | 15 +++++++++++++++ > lib/fuse_versionscript | 1 + > 7 files changed, 138 insertions(+), 1 deletion(-) > > diff --git a/example/fusexmp.c b/example/fusexmp.c > index fa3fb4d..4996c74 100644 > --- a/example/fusexmp.c > +++ b/example/fusexmp.c > @@ -310,6 +310,41 @@ static int xmp_fsync(const char *path, int isdatasync, > return 0; > } > > +static int xmp_fallocate(const char *path, struct fuse_file_info *fi, > + off_t offset, off_t length) > +{ > + int fd; > + int res; > + > + (void) fi; > + fd = open(path, O_WRONLY); > + if (fd == -1) > + return -errno; > + > +#ifdef __APPLE__ > + /* This code compiles but does not work as FUSE_FALLOCATED > + not implemented in fuse4x yet. This part should probably be > + sent as a separate patch after fuse4x gets FALLOCATE support. > + */ > + fstore_t prealloc_arg; > + memset(&prealloc_arg, 0, sizeof(prealloc_arg)); > + prealloc_arg.fst_flags = F_ALLOCATECONTIG; > + prealloc_arg.fst_posmode = F_PEOFPOSMODE; > + prealloc_arg.fst_offset = offset; > + prealloc_arg.fst_length = length; > + res = fcntl(fd, F_PREALLOCATE, &prealloc_arg); > + > + if (res == -1) > + res = -errno; > +#else Please don't add non portable stuff to the examples. > + // TOASK errno is not set, res is positive error. Is it correct way? > + res = -posix_fallocate(fd, offset, length); Yes, this looks correct. > +#endif > + > + close(fd); > + return res; > +} > + > #ifdef HAVE_SETXATTR > /* xattr operations are optional and can safely be left unimplemented */ > static int xmp_setxattr(const char *path, const char *name, const char *value, > @@ -371,6 +406,7 @@ static struct fuse_operations xmp_oper = { > .statfs = xmp_statfs, > .release = xmp_release, > .fsync = xmp_fsync, > + .fallocate = xmp_fallocate, > #ifdef HAVE_SETXATTR > .setxattr = xmp_setxattr, > .getxattr = xmp_getxattr, > diff --git a/include/fuse.h b/include/fuse.h > index b05152d..7401ca1 100644 > --- a/include/fuse.h > +++ b/include/fuse.h > @@ -569,6 +569,18 @@ struct fuse_operations { > * Introduced in version 2.9 > */ > int (*flock) (const char *, struct fuse_file_info *, int op); > + > + /** > + * Allocates space for an open file > + * > + * This function ensures that required space is allocated for specified > + * file. If this function returns success then any subsequent write > + * request to specified range is guaranteed not to fail because of lack > + * of space on the file system media. > + * > + * Introduced in version 2.9 > + */ > + int (*fallocate) (const char *, struct fuse_file_info *, off_t, off_t); > }; > > /** Extra context that may be needed by some filesystems > @@ -864,6 +876,8 @@ int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, > int fuse_fs_poll(struct fuse_fs *fs, const char *path, > struct fuse_file_info *fi, struct fuse_pollhandle *ph, > unsigned *reventsp); > +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, > + struct fuse_file_info *fi, off_t offset, off_t length); > void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); > void fuse_fs_destroy(struct fuse_fs *fs); > > diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h > index 7b43cb0..909725d 100644 > --- a/include/fuse_kernel.h > +++ b/include/fuse_kernel.h > @@ -80,6 +80,9 @@ > * 7.18 > * - add FUSE_IOCTL_DIR flag > * - add FUSE_NOTIFY_DELETE > + * > + * 7.19 > + * - add FUSE_FALLOCATE > */ > > #ifndef _LINUX_FUSE_H > @@ -116,7 +119,7 @@ > #define FUSE_KERNEL_VERSION 7 > > /** Minor version number of this interface */ > -#define FUSE_KERNEL_MINOR_VERSION 18 > +#define FUSE_KERNEL_MINOR_VERSION 19 > > /** The node ID of the root inode */ > #define FUSE_ROOT_ID 1 > @@ -309,6 +312,7 @@ enum fuse_opcode { > FUSE_POLL = 40, > FUSE_NOTIFY_REPLY = 41, > FUSE_BATCH_FORGET = 42, > + FUSE_FALLOCATE = 43, > > /* CUSE specific operations */ > CUSE_INIT = 4096, > @@ -602,6 +606,16 @@ struct fuse_notify_poll_wakeup_out { > __u64 kh; > }; > > +struct fuse_fallocate_in { > + __u64 fh; > + __u64 offset; > + __u64 length; > +// TOASK macosx expects that syscall returns "number of allocated bytes" > +// do we need to add fuse_fallocate_out response because of it? > +// Or maybe macosx syscall should return all-or-nothing - 0 on failure and "size" > +// on success. Does OSX return other than size normally? > +}; > + > struct fuse_in_header { > __u32 len; > __u32 opcode; > diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h > index 3ecc46e..2d4813b 100644 > --- a/include/fuse_lowlevel.h > +++ b/include/fuse_lowlevel.h > @@ -996,6 +996,28 @@ struct fuse_lowlevel_ops { > */ > void (*flock) (fuse_req_t req, fuse_ino_t ino, > struct fuse_file_info *fi, int op); > + > + /** > + * Allocate requested space. If this function returns success then > + * subsequent writes to the specified range shall not fail due to the lack > + * of free space on the file system storage media. > + * > + * Introduced in version 2.9 > + * > + * Valid replies: > + * fuse_reply_err > + * > + * @param req request handle > + * @param ino the inode number > + * @param fi file information > + * @param offset starting point for allocated region > + * @param length size of allocated region > + */ > + // TOASK: do we need to pass fuse_file_info structure here? > + // and in what order, some functions put fuse_file_info at the end of the > + // args list, some other put it in the middle Put it at the end, please. > + void (*fallocate) (fuse_req_t req, fuse_ino_t ino, > + struct fuse_file_info *fi, off_t offset, off_t length); > }; > > /** > diff --git a/lib/fuse.c b/lib/fuse.c > index e01f450..3d3a84f 100644 > --- a/lib/fuse.c > +++ b/lib/fuse.c > @@ -2280,6 +2280,22 @@ int fuse_fs_poll(struct fuse_fs *fs, const char *path, > return -ENOSYS; > } > > +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, > + struct fuse_file_info *fi, off_t offset, off_t length) > +{ > + fuse_get_context()->private_data = fs->user_data; > + if (fs->op.fallocate) { > + if (fs->debug) > + fprintf(stderr, "fallocate %s offset: %llu, length: %llu\n", > + path, > + (unsigned long long) offset, > + (unsigned long long) length); > + > + return fs->op.fallocate(path, fi, offset, length); > + } else > + return -ENOSYS; > +} > + > static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) > { > struct node *node; > @@ -3966,6 +3982,24 @@ static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino, > reply_err(req, err); > } > > +static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, > + struct fuse_file_info *fi, off_t offset, off_t length) > +{ > + struct fuse *f = req_fuse_prepare(req); > + struct fuse_intr_data d; > + char *path; > + int err; > + > + err = get_path_nullok(f, ino, &path); > + if (!err) { > + fuse_prepare_interrupt(f, req, &d); > + err = fuse_fs_fallocate(f->fs, path, fi, offset, length); > + fuse_finish_interrupt(f, req, &d); > + free_path(f, ino, path); > + } > + reply_err(req, err); > +} > + > static int clean_delay(struct fuse *f) > { > /* > @@ -4060,6 +4094,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { > .bmap = fuse_lib_bmap, > .ioctl = fuse_lib_ioctl, > .poll = fuse_lib_poll, > + .fallocate = fuse_lib_fallocate, > }; > > int fuse_notify_poll(struct fuse_pollhandle *ph) > diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c > index a0d4887..18d1dda 100644 > --- a/lib/fuse_lowlevel.c > +++ b/lib/fuse_lowlevel.c > @@ -1717,6 +1717,20 @@ static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) > } > } > > +static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) > +{ > + struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; > + struct fuse_file_info fi; > + > + memset(&fi, 0, sizeof(fi)); > + fi.fh = arg->fh; > + > + if (req->f->op.fallocate) > + req->f->op.fallocate(req, nodeid, &fi, arg->offset, arg->length); > + else > + fuse_reply_err(req, ENOSYS); > +} > + > static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) > { > struct fuse_init_in *arg = (struct fuse_init_in *) inarg; > @@ -2258,6 +2272,7 @@ static struct { > [FUSE_BMAP] = { do_bmap, "BMAP" }, > [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, > [FUSE_POLL] = { do_poll, "POLL" }, > + [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, > [FUSE_DESTROY] = { do_destroy, "DESTROY" }, > [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, > [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, > diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript > index 8fbaa51..ab4b0bc 100644 > --- a/lib/fuse_versionscript > +++ b/lib/fuse_versionscript > @@ -186,6 +186,7 @@ FUSE_2.9 { > fuse_buf_size; > fuse_fs_read_buf; > fuse_fs_write_buf; > + fuse_fs_fallocate; > fuse_lowlevel_notify_retrieve; > fuse_lowlevel_notify_store; > fuse_reply_data; > -- > 1.7.10.rc3 > |
From: Anatol P. <ana...@gm...> - 2012-04-18 23:42:33
|
Hi, Miklos. Thanks for the review. I merged conversation from another thread into this one. > One issue: there's the mode argument which seems to be ignored. I > haven't looked in detail, but AFAICS this argument should be forwarded > to userspace. > mode parameter is used only with linux-specific fallocate() function. posix_fallocate() does not have it. Do we still want to pass "mode" to userspace? On Wed, Apr 18, 2012 at 3:16 AM, Miklos Szeredi <mi...@sz...> wrote: > On Fri, Apr 6, 2012 at 8:56 PM, Anatol Pomozov <ana...@gm...> > wrote: > > fallocate filesystem operation preallocates media space for the given > file. > > If fallocate returns success then any subsequent write to the given range > > never fails with 'not enough space' error. > > > > Signed-off-by: Anatol Pomozov <ana...@gm...> > > --- > > example/fusexmp.c | 36 ++++++++++++++++++++++++++++++++++++ > > include/fuse.h | 14 ++++++++++++++ > > include/fuse_kernel.h | 16 +++++++++++++++- > > include/fuse_lowlevel.h | 22 ++++++++++++++++++++++ > > lib/fuse.c | 35 +++++++++++++++++++++++++++++++++++ > > lib/fuse_lowlevel.c | 15 +++++++++++++++ > > lib/fuse_versionscript | 1 + > > 7 files changed, 138 insertions(+), 1 deletion(-) > > > > diff --git a/example/fusexmp.c b/example/fusexmp.c > > index fa3fb4d..4996c74 100644 > > --- a/example/fusexmp.c > > +++ b/example/fusexmp.c > > @@ -310,6 +310,41 @@ static int xmp_fsync(const char *path, int > isdatasync, > > return 0; > > } > > > > +static int xmp_fallocate(const char *path, struct fuse_file_info *fi, > > + off_t offset, off_t length) > > +{ > > + int fd; > > + int res; > > + > > + (void) fi; > > + fd = open(path, O_WRONLY); > > + if (fd == -1) > > + return -errno; > > + > > +#ifdef __APPLE__ > > + /* This code compiles but does not work as FUSE_FALLOCATED > > + not implemented in fuse4x yet. This part should probably be > > + sent as a separate patch after fuse4x gets FALLOCATE support. > > + */ > > + fstore_t prealloc_arg; > > + memset(&prealloc_arg, 0, sizeof(prealloc_arg)); > > + prealloc_arg.fst_flags = F_ALLOCATECONTIG; > > + prealloc_arg.fst_posmode = F_PEOFPOSMODE; > > + prealloc_arg.fst_offset = offset; > > + prealloc_arg.fst_length = length; > > + res = fcntl(fd, F_PREALLOCATE, &prealloc_arg); > > + > > + if (res == -1) > > + res = -errno; > > +#else > > Please don't add non portable stuff to the examples. > Ok. This code will stay in fuse4x' version of libfuse. > > > + // TOASK errno is not set, res is positive error. Is it correct > way? > > + res = -posix_fallocate(fd, offset, length); > > Yes, this looks correct. > > > +#endif > > + > > + close(fd); > > + return res; > > +} > > + > > #ifdef HAVE_SETXATTR > > /* xattr operations are optional and can safely be left unimplemented */ > > static int xmp_setxattr(const char *path, const char *name, const char > *value, > > @@ -371,6 +406,7 @@ static struct fuse_operations xmp_oper = { > > .statfs = xmp_statfs, > > .release = xmp_release, > > .fsync = xmp_fsync, > > + .fallocate = xmp_fallocate, > > #ifdef HAVE_SETXATTR > > .setxattr = xmp_setxattr, > > .getxattr = xmp_getxattr, > > diff --git a/include/fuse.h b/include/fuse.h > > index b05152d..7401ca1 100644 > > --- a/include/fuse.h > > +++ b/include/fuse.h > > @@ -569,6 +569,18 @@ struct fuse_operations { > > * Introduced in version 2.9 > > */ > > int (*flock) (const char *, struct fuse_file_info *, int op); > > + > > + /** > > + * Allocates space for an open file > > + * > > + * This function ensures that required space is allocated for > specified > > + * file. If this function returns success then any subsequent > write > > + * request to specified range is guaranteed not to fail because > of lack > > + * of space on the file system media. > > + * > > + * Introduced in version 2.9 > > + */ > > + int (*fallocate) (const char *, struct fuse_file_info *, off_t, > off_t); > > }; > > > > /** Extra context that may be needed by some filesystems > > @@ -864,6 +876,8 @@ int fuse_fs_ioctl(struct fuse_fs *fs, const char > *path, int cmd, void *arg, > > int fuse_fs_poll(struct fuse_fs *fs, const char *path, > > struct fuse_file_info *fi, struct fuse_pollhandle *ph, > > unsigned *reventsp); > > +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, > > + struct fuse_file_info *fi, off_t offset, off_t length); > > void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); > > void fuse_fs_destroy(struct fuse_fs *fs); > > > > diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h > > index 7b43cb0..909725d 100644 > > --- a/include/fuse_kernel.h > > +++ b/include/fuse_kernel.h > > @@ -80,6 +80,9 @@ > > * 7.18 > > * - add FUSE_IOCTL_DIR flag > > * - add FUSE_NOTIFY_DELETE > > + * > > + * 7.19 > > + * - add FUSE_FALLOCATE > > */ > > > > #ifndef _LINUX_FUSE_H > > @@ -116,7 +119,7 @@ > > #define FUSE_KERNEL_VERSION 7 > > > > /** Minor version number of this interface */ > > -#define FUSE_KERNEL_MINOR_VERSION 18 > > +#define FUSE_KERNEL_MINOR_VERSION 19 > > > > /** The node ID of the root inode */ > > #define FUSE_ROOT_ID 1 > > @@ -309,6 +312,7 @@ enum fuse_opcode { > > FUSE_POLL = 40, > > FUSE_NOTIFY_REPLY = 41, > > FUSE_BATCH_FORGET = 42, > > + FUSE_FALLOCATE = 43, > > > > /* CUSE specific operations */ > > CUSE_INIT = 4096, > > @@ -602,6 +606,16 @@ struct fuse_notify_poll_wakeup_out { > > __u64 kh; > > }; > > > > +struct fuse_fallocate_in { > > + __u64 fh; > > + __u64 offset; > > + __u64 length; > > +// TOASK macosx expects that syscall returns "number of allocated bytes" > > +// do we need to add fuse_fallocate_out response because of it? > > +// Or maybe macosx syscall should return all-or-nothing - 0 on failure > and "size" > > +// on success. > > Does OSX return other than size normally? > Currently only HFS filesystem implements vnop_allocate(). I briefly looked at its implementation and the answer is yes "allocate" can return some value in range [0,size] in case of errors (e.g. disk is full). It means that this syscall on HFS may return error *and* allocate part of the space at the same time. The question - how useful allocating only part of the space is? My gut feeling says that not very useful - if an user wants to preallocate space it means that he wants to preallocate *all* requested space. So I think it is better to stick with "allocate all or nothing" requirement and do not add return value to FUSE_FALLOCATE. > > +}; > > + > > struct fuse_in_header { > > __u32 len; > > __u32 opcode; > > diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h > > index 3ecc46e..2d4813b 100644 > > --- a/include/fuse_lowlevel.h > > +++ b/include/fuse_lowlevel.h > > @@ -996,6 +996,28 @@ struct fuse_lowlevel_ops { > > */ > > void (*flock) (fuse_req_t req, fuse_ino_t ino, > > struct fuse_file_info *fi, int op); > > + > > + /** > > + * Allocate requested space. If this function returns success > then > > + * subsequent writes to the specified range shall not fail due > to the lack > > + * of free space on the file system storage media. > > + * > > + * Introduced in version 2.9 > > + * > > + * Valid replies: > > + * fuse_reply_err > > + * > > + * @param req request handle > > + * @param ino the inode number > > + * @param fi file information > > + * @param offset starting point for allocated region > > + * @param length size of allocated region > > + */ > > + // TOASK: do we need to pass fuse_file_info structure here? > > + // and in what order, some functions put fuse_file_info at the > end of the > > + // args list, some other put it in the middle > > Put it at the end, please. > > > + void (*fallocate) (fuse_req_t req, fuse_ino_t ino, > > + struct fuse_file_info *fi, off_t offset, off_t > length); > > }; > > > > /** > > diff --git a/lib/fuse.c b/lib/fuse.c > > index e01f450..3d3a84f 100644 > > --- a/lib/fuse.c > > +++ b/lib/fuse.c > > @@ -2280,6 +2280,22 @@ int fuse_fs_poll(struct fuse_fs *fs, const char > *path, > > return -ENOSYS; > > } > > > > +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, > > + struct fuse_file_info *fi, off_t offset, off_t length) > > +{ > > + fuse_get_context()->private_data = fs->user_data; > > + if (fs->op.fallocate) { > > + if (fs->debug) > > + fprintf(stderr, "fallocate %s offset: %llu, > length: %llu\n", > > + path, > > + (unsigned long long) offset, > > + (unsigned long long) length); > > + > > + return fs->op.fallocate(path, fi, offset, length); > > + } else > > + return -ENOSYS; > > +} > > + > > static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) > > { > > struct node *node; > > @@ -3966,6 +3982,24 @@ static void fuse_lib_poll(fuse_req_t req, > fuse_ino_t ino, > > reply_err(req, err); > > } > > > > +static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, > > + struct fuse_file_info *fi, off_t offset, off_t length) > > +{ > > + struct fuse *f = req_fuse_prepare(req); > > + struct fuse_intr_data d; > > + char *path; > > + int err; > > + > > + err = get_path_nullok(f, ino, &path); > > + if (!err) { > > + fuse_prepare_interrupt(f, req, &d); > > + err = fuse_fs_fallocate(f->fs, path, fi, offset, length); > > + fuse_finish_interrupt(f, req, &d); > > + free_path(f, ino, path); > > + } > > + reply_err(req, err); > > +} > > + > > static int clean_delay(struct fuse *f) > > { > > /* > > @@ -4060,6 +4094,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { > > .bmap = fuse_lib_bmap, > > .ioctl = fuse_lib_ioctl, > > .poll = fuse_lib_poll, > > + .fallocate = fuse_lib_fallocate, > > }; > > > > int fuse_notify_poll(struct fuse_pollhandle *ph) > > diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c > > index a0d4887..18d1dda 100644 > > --- a/lib/fuse_lowlevel.c > > +++ b/lib/fuse_lowlevel.c > > @@ -1717,6 +1717,20 @@ static void do_poll(fuse_req_t req, fuse_ino_t > nodeid, const void *inarg) > > } > > } > > > > +static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void > *inarg) > > +{ > > + struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) > inarg; > > + struct fuse_file_info fi; > > + > > + memset(&fi, 0, sizeof(fi)); > > + fi.fh = arg->fh; > > + > > + if (req->f->op.fallocate) > > + req->f->op.fallocate(req, nodeid, &fi, arg->offset, > arg->length); > > + else > > + fuse_reply_err(req, ENOSYS); > > +} > > + > > static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void > *inarg) > > { > > struct fuse_init_in *arg = (struct fuse_init_in *) inarg; > > @@ -2258,6 +2272,7 @@ static struct { > > [FUSE_BMAP] = { do_bmap, "BMAP" }, > > [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, > > [FUSE_POLL] = { do_poll, "POLL" }, > > + [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, > > [FUSE_DESTROY] = { do_destroy, "DESTROY" }, > > [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, > > [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, > > diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript > > index 8fbaa51..ab4b0bc 100644 > > --- a/lib/fuse_versionscript > > +++ b/lib/fuse_versionscript > > @@ -186,6 +186,7 @@ FUSE_2.9 { > > fuse_buf_size; > > fuse_fs_read_buf; > > fuse_fs_write_buf; > > + fuse_fs_fallocate; > > fuse_lowlevel_notify_retrieve; > > fuse_lowlevel_notify_store; > > fuse_reply_data; > > -- > > 1.7.10.rc3 > > > |
From: Miklos S. <mi...@sz...> - 2012-04-19 11:31:17
|
Anatol Pomozov <ana...@gm...> writes: > Hi, Miklos. > > Thanks for the review. I merged conversation from another thread into this one. > > > One issue: there's the mode argument which seems to be ignored. I > haven't looked in detail, but AFAICS this argument should be forwarded > to userspace. > > > mode parameter is used only with linux-specific fallocate() function. > posix_fallocate() does not have it. Do we still want to pass "mode" to > userspace? There are two options: 1) check if the mode is zero which means posix_fallocate() or 2) pass the mode to userspace and filesystem can decide how to handle it. In no case should the mode argument be ignored. I'd vote for 2. Thanks, Miklos |
From: Anatol P. <ana...@gm...> - 2012-04-23 01:50:02
|
fallocate filesystem operation preallocates media space for the given file. If fallocate returns success then any subsequent write to the given range never fails with 'not enough space' error. --- example/fusexmp.c | 19 +++++++++++++++++++ include/fuse.h | 14 ++++++++++++++ include/fuse_kernel.h | 14 +++++++++++++- include/fuse_lowlevel.h | 20 ++++++++++++++++++++ lib/fuse.c | 36 ++++++++++++++++++++++++++++++++++++ lib/fuse_lowlevel.c | 15 +++++++++++++++ lib/fuse_versionscript | 1 + 7 files changed, 118 insertions(+), 1 deletion(-) diff --git a/example/fusexmp.c b/example/fusexmp.c index fa3fb4d..51fd5f7 100644 --- a/example/fusexmp.c +++ b/example/fusexmp.c @@ -310,6 +310,24 @@ static int xmp_fsync(const char *path, int isdatasync, return 0; } +static int xmp_fallocate(const char *path, int mode, + off_t offset, off_t length, struct fuse_file_info *fi) +{ + int fd; + int res; + + (void) fi; + (void) mode; + fd = open(path, O_WRONLY); + if (fd == -1) + return -errno; + + res = -posix_fallocate(fd, offset, length); + + close(fd); + return res; +} + #ifdef HAVE_SETXATTR /* xattr operations are optional and can safely be left unimplemented */ static int xmp_setxattr(const char *path, const char *name, const char *value, @@ -371,6 +389,7 @@ static struct fuse_operations xmp_oper = { .statfs = xmp_statfs, .release = xmp_release, .fsync = xmp_fsync, + .fallocate = xmp_fallocate, #ifdef HAVE_SETXATTR .setxattr = xmp_setxattr, .getxattr = xmp_getxattr, diff --git a/include/fuse.h b/include/fuse.h index 36b168c..f372ce8 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -575,6 +575,18 @@ struct fuse_operations { * Introduced in version 2.9 */ int (*flock) (const char *, struct fuse_file_info *, int op); + + /** + * Allocates space for an open file + * + * This function ensures that required space is allocated for specified + * file. If this function returns success then any subsequent write + * request to specified range is guaranteed not to fail because of lack + * of space on the file system media. + * + * Introduced in version 2.9 + */ + int (*fallocate) (const char *, int, off_t, off_t, struct fuse_file_info *); }; /** Extra context that may be needed by some filesystems @@ -870,6 +882,8 @@ int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, int fuse_fs_poll(struct fuse_fs *fs, const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp); +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, + off_t offset, off_t length, struct fuse_file_info *fi); void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); void fuse_fs_destroy(struct fuse_fs *fs); diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 1ce072c..c632b58 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -80,6 +80,9 @@ * 7.18 * - add FUSE_IOCTL_DIR flag * - add FUSE_NOTIFY_DELETE + * + * 7.19 + * - add FUSE_FALLOCATE */ #ifndef _LINUX_FUSE_H @@ -116,7 +119,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 18 +#define FUSE_KERNEL_MINOR_VERSION 19 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -309,6 +312,7 @@ enum fuse_opcode { FUSE_POLL = 40, FUSE_NOTIFY_REPLY = 41, FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -602,6 +606,14 @@ struct fuse_notify_poll_wakeup_out { __u64 kh; }; +struct fuse_fallocate_in { + __u64 fh; + __u64 offset; + __u64 length; + __u32 mode; + __u32 padding; +}; + struct fuse_in_header { __u32 len; __u32 opcode; diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index 3ecc46e..2036717 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -996,6 +996,26 @@ struct fuse_lowlevel_ops { */ void (*flock) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, int op); + + /** + * Allocate requested space. If this function returns success then + * subsequent writes to the specified range shall not fail due to the lack + * of free space on the file system storage media. + * + * Introduced in version 2.9 + * + * Valid replies: + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param offset starting point for allocated region + * @param length size of allocated region + * @param mode determines the operation to be performed on the given range, + * see fallocate(2) + */ + void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, + off_t offset, off_t length, struct fuse_file_info *fi); }; /** diff --git a/lib/fuse.c b/lib/fuse.c index 4922361..644878b 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -2281,6 +2281,23 @@ int fuse_fs_poll(struct fuse_fs *fs, const char *path, return -ENOSYS; } +int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, + off_t offset, off_t length, struct fuse_file_info *fi) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.fallocate) { + if (fs->debug) + fprintf(stderr, "fallocate %s mode %x, offset: %llu, length: %llu\n", + path, + mode, + (unsigned long long) offset, + (unsigned long long) length); + + return fs->op.fallocate(path, mode, offset, length, fi); + } else + return -ENOSYS; +} + static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) { struct node *node; @@ -3990,6 +4007,24 @@ static void fuse_lib_poll(fuse_req_t req, fuse_ino_t ino, reply_err(req, err); } +static void fuse_lib_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, + off_t offset, off_t length, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + char *path; + int err; + + err = get_path_nullok(f, ino, &path); + if (!err) { + fuse_prepare_interrupt(f, req, &d); + err = fuse_fs_fallocate(f->fs, path, mode, offset, length, fi); + fuse_finish_interrupt(f, req, &d); + free_path(f, ino, path); + } + reply_err(req, err); +} + static int clean_delay(struct fuse *f) { /* @@ -4084,6 +4119,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { .bmap = fuse_lib_bmap, .ioctl = fuse_lib_ioctl, .poll = fuse_lib_poll, + .fallocate = fuse_lib_fallocate, }; int fuse_notify_poll(struct fuse_pollhandle *ph) diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 2282ccf..305bbbf 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -1717,6 +1717,20 @@ static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) } } +static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + + if (req->f->op.fallocate) + req->f->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi); + else + fuse_reply_err(req, ENOSYS); +} + static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { struct fuse_init_in *arg = (struct fuse_init_in *) inarg; @@ -2267,6 +2281,7 @@ static struct { [FUSE_BMAP] = { do_bmap, "BMAP" }, [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, [FUSE_POLL] = { do_poll, "POLL" }, + [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, [FUSE_DESTROY] = { do_destroy, "DESTROY" }, [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 2d110fb..0e77630 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -186,6 +186,7 @@ FUSE_2.9 { fuse_buf_size; fuse_fs_read_buf; fuse_fs_write_buf; + fuse_fs_fallocate; fuse_lowlevel_notify_retrieve; fuse_lowlevel_notify_store; fuse_reply_data; -- 1.7.10.207.g0bb2e |
From: Anatol P. <ana...@gm...> - 2012-04-23 21:50:36
|
Hi On Thu, Apr 19, 2012 at 4:31 AM, Miklos Szeredi <mi...@sz...> wrote: > Anatol Pomozov <ana...@gm...> writes: > > > Hi, Miklos. > > > > Thanks for the review. I merged conversation from another thread into > this one. > > > > > > One issue: there's the mode argument which seems to be ignored. I > > haven't looked in detail, but AFAICS this argument should be > forwarded > > to userspace. > > > > > > mode parameter is used only with linux-specific fallocate() function. > > posix_fallocate() does not have it. Do we still want to pass "mode" to > > userspace? > > There are two options: > > 1) check if the mode is zero which means posix_fallocate() or > > 2) pass the mode to userspace and filesystem can decide how to handle it. > > In no case should the mode argument be ignored. > > I'd vote for 2. > option #2 is implemented. Please take another look at the patches. PS It looks like 2.9.0 has been released recently. It means comments in my patches should be updated. What is the next version, in what version FALLOCATE will be released? Feel free to update comments by yourself. |
From: Miklos S. <mi...@sz...> - 2012-04-24 14:22:11
|
Anatol Pomozov <ana...@gm...> writes: > Hi > > On Thu, Apr 19, 2012 at 4:31 AM, Miklos Szeredi <mi...@sz...> wrote: > > Anatol Pomozov <ana...@gm...> writes: > > > Hi, Miklos. > > > > Thanks for the review. I merged conversation from another thread into > this one. > > > > > > One issue: there's the mode argument which seems to be ignored. I > > haven't looked in detail, but AFAICS this argument should be > forwarded > > to userspace. > > > > > > mode parameter is used only with linux-specific fallocate() function. > > posix_fallocate() does not have it. Do we still want to pass "mode" to > > userspace? > > There are two options: > > 1) check if the mode is zero which means posix_fallocate() or > > 2) pass the mode to userspace and filesystem can decide how to handle it. > > In no case should the mode argument be ignored. > > I'd vote for 2. > > > option #2 is implemented. Please take another look at the patches. I don't see the updated libfuse patch. Did you send it? > PS It looks like 2.9.0 has been released recently. It means comments in my > patches should be updated. What is the next version, in what version FALLOCATE > will be released? Feel free to update comments by yourself. Ideally 2.9.1 Thanks, Miklos |
From: Anatol P. <ana...@gm...> - 2012-04-24 16:15:25
|
Hi On Tue, Apr 24, 2012 at 7:22 AM, Miklos Szeredi <mi...@sz...> wrote: > Anatol Pomozov <ana...@gm...> writes: > > > Hi > > > > On Thu, Apr 19, 2012 at 4:31 AM, Miklos Szeredi <mi...@sz...> > wrote: > > > > Anatol Pomozov <ana...@gm...> writes: > > > > > Hi, Miklos. > > > > > > Thanks for the review. I merged conversation from another thread > into > > this one. > > > > > > > > > One issue: there's the mode argument which seems to be > ignored. I > > > haven't looked in detail, but AFAICS this argument should be > > forwarded > > > to userspace. > > > > > > > > > mode parameter is used only with linux-specific fallocate() > function. > > > posix_fallocate() does not have it. Do we still want to pass > "mode" to > > > userspace? > > > > There are two options: > > > > 1) check if the mode is zero which means posix_fallocate() or > > > > 2) pass the mode to userspace and filesystem can decide how to > handle it. > > > > In no case should the mode argument be ignored. > > > > I'd vote for 2. > > > > > > option #2 is implemented. Please take another look at the patches. > > I don't see the updated libfuse patch. Did you send it? > Yep I did it Apr 22. I just resent it to your szeredi.hu mailbox. > > > PS It looks like 2.9.0 has been released recently. It means comments in > my > > patches should be updated. What is the next version, in what version > FALLOCATE > > will be released? Feel free to update comments by yourself. > > Ideally 2.9.1 > > Thanks, > Miklos > |