Add FALLOCATE operation
authorAnatol Pomozov <anatol.pomozov@gmail.com>
Mon, 23 Apr 2012 01:49:35 +0000 (18:49 -0700)
committerMiklos Szeredi <mszeredi@suse.cz>
Mon, 18 Jun 2012 11:32:43 +0000 (13:32 +0200)
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.

ChangeLog
configure.in
example/fusexmp.c
example/fusexmp_fh.c
include/fuse.h
include/fuse_kernel.h
include/fuse_lowlevel.h
lib/fuse.c
lib/fuse_lowlevel.c
lib/fuse_versionscript

index a60ac1237bade6867eedf54d1d87a02c3844169d..d465b3cbc946b0368b1e01a4787d22be3ebd1b40 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2012-04-24  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Add fallocate operation.  Patch by Anatol Pomozov
+
 2012-05-16  Miklos Szeredi <miklos@szeredi.hu>
 
        * Linking to a library that uses threads requires the application
index e9ce79b97ee981847f6db055de42c7417cb56703..a7448dfbd7acae3ac895d7f349969d8d217bb184 100644 (file)
@@ -57,6 +57,7 @@ if test "$enable_mtab" = "no"; then
 fi
 
 AC_CHECK_FUNCS([fork setxattr fdatasync splice vmsplice utimensat])
+AC_CHECK_FUNCS([posix_fallocate])
 AC_CHECK_MEMBERS([struct stat.st_atim])
 AC_CHECK_MEMBERS([struct stat.st_atimespec])
 
index 20a711678bc0c3f640d03879933e6781d95ed778..dca8a4672d07268041188eb7fb66efab68551a24 100644 (file)
@@ -310,6 +310,29 @@ static int xmp_fsync(const char *path, int isdatasync,
        return 0;
 }
 
+#ifdef HAVE_POSIX_FALLOCATE
+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;
+
+       if (mode)
+               return -EOPNOTSUPP;
+
+       fd = open(path, O_WRONLY);
+       if (fd == -1)
+               return -errno;
+
+       res = -posix_fallocate(fd, offset, length);
+
+       close(fd);
+       return res;
+}
+#endif
+
 #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 +394,9 @@ static struct fuse_operations xmp_oper = {
        .statfs         = xmp_statfs,
        .release        = xmp_release,
        .fsync          = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+       .fallocate      = xmp_fallocate,
+#endif
 #ifdef HAVE_SETXATTR
        .setxattr       = xmp_setxattr,
        .getxattr       = xmp_getxattr,
index d79ff37927882160896a1d33f81dcb27e7a6cd7e..1ba9dbc85e1cf796a0198e03e866c659590c78d4 100644 (file)
@@ -439,6 +439,19 @@ static int xmp_fsync(const char *path, int isdatasync,
        return 0;
 }
 
+#ifdef HAVE_POSIX_FALLOCATE
+static int xmp_fallocate(const char *path, int mode,
+                       off_t offset, off_t length, struct fuse_file_info *fi)
+{
+       (void) path;
+
+       if (mode)
+               return -EOPNOTSUPP;
+
+       return -posix_fallocate(fi->fh, offset, length);
+}
+#endif
+
 #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,
@@ -529,6 +542,9 @@ static struct fuse_operations xmp_oper = {
        .flush          = xmp_flush,
        .release        = xmp_release,
        .fsync          = xmp_fsync,
+#ifdef HAVE_POSIX_FALLOCATE
+       .fallocate      = xmp_fallocate,
+#endif
 #ifdef HAVE_SETXATTR
        .setxattr       = xmp_setxattr,
        .getxattr       = xmp_getxattr,
index 36b168cd9e5ddfb4604e182e4749b19401ab0f7c..c657e676438e4111d79650f170ee5873c4bbfb57 100644 (file)
@@ -575,6 +575,19 @@ 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.1
+        */
+       int (*fallocate) (const char *, int, off_t, off_t,
+                         struct fuse_file_info *);
 };
 
 /** Extra context that may be needed by some filesystems
@@ -870,6 +883,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);
 
index 1ce072c213099d4304b7b09ec5b1f9e32a020a75..c632b58fbcf43103609523cc90fa708caee6d14f 100644 (file)
@@ -80,6 +80,9 @@
  * 7.18
  *  - add FUSE_IOCTL_DIR flag
  *  - add FUSE_NOTIFY_DELETE
+ *
+ * 7.19
+ *  - add FUSE_FALLOCATE
  */
 
 #ifndef _LINUX_FUSE_H
 #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;
index 3ecc46e7746cacfe2c31da9ec994f4354df7baf0..2036717f36cb4aa375b6f960cf09aeea4ca26bff 100644 (file)
@@ -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);
 };
 
 /**
index 49223612fb3c59213858eb9a948530ce6b1a278b..644878b9425a85e57f1e9296256ecba1259d150a 100644 (file)
@@ -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)
index 2282ccfe7a9e56d10feb67bba2ab1eb9926fb099..305bbbf09eabe75abe1a61ee95875e24e14d7c56 100644 (file)
@@ -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" },
index 2d110fb4208888ab648b11e503aac1d9457fda2e..8d91887d32a2b7671295ecacb1306a73f7f552fc 100644 (file)
@@ -196,7 +196,12 @@ FUSE_2.9 {
                fuse_clean_cache;
                fuse_lowlevel_notify_delete;
                fuse_fs_flock;
+} FUSE_2.8;
+
+FUSE_2.9.1 {
+       global:
+               fuse_fs_fallocate;
 
        local:
                *;
-} FUSE_2.8;
+} FUSE_2.9;