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 > |