added file locking
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 22 Jul 2005 17:24:30 +0000 (17:24 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 22 Jul 2005 17:24:30 +0000 (17:24 +0000)
ChangeLog
include/fuse_lowlevel.h
kernel/file.c
kernel/fuse_i.h
kernel/fuse_kernel.h
kernel/inode.c
lib/fuse_lowlevel.c

index f0dfd3d84cd945fa76285f7ab822349f8f0fd994..fbf82dc39fd74a7a32f5d44a98556d09eb0213d9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,8 @@
        2,147,483,648 operations, so most people won't care.  Thanks to
        Franco Broi for the report and testing.
 
+       * Added file locking methods to kernel and low-level API.
+
 2005-07-21  Miklos Szeredi <miklos@szeredi.hu>
 
        * Don't change mtime/ctime/atime to local time on read/write.
index deab718c82f97874045c1ef48c89bda802f6c069..ddead682781773ed98c98fee80a25157f4f4e0a7 100644 (file)
@@ -39,8 +39,16 @@ struct fuse_entry_param {
     double entry_timeout;
 };
 
+struct fuse_lock_param {
+    int type;
+    off_t start;
+    off_t end;
+    unsigned long long owner;
+    pid_t pid;
+};
+
 struct fuse_ctx {
-        /** User ID of the calling process */
+    /** User ID of the calling process */
     uid_t uid;
 
     /** Group ID of the calling process */
@@ -62,50 +70,54 @@ struct fuse_ctx {
 /* ------------------------------------------ */
 
 struct fuse_ll_operations {
-    void* (*init)   (void *);
-    void (*destroy) (void *);
+    void* (*init)  (void *);
+    void (*destroy)(void *);
 
-    void (*lookup)  (fuse_req_t req, fuse_ino_t parent, const char *name);
-    void (*forget)  (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
-    void (*getattr) (fuse_req_t req, fuse_ino_t ino);
-    void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
+    void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
+    void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
+    void (*getattr)(fuse_req_t req, fuse_ino_t ino);
+    void (*setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
                      int to_set);
     void (*readlink)(fuse_req_t req, fuse_ino_t ino);
-    void (*mknod)   (fuse_req_t req, fuse_ino_t parent, const char *name,
-                     mode_t mode, dev_t rdev);
-    void (*mkdir)   (fuse_req_t req, fuse_ino_t parent, const char *name,
-                     mode_t mode);
-    void (*unlink)  (fuse_req_t req, fuse_ino_t parent, const char *name);
-    void (*rmdir)   (fuse_req_t req, fuse_ino_t parent, const char *name);
-    void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
-                     const char *name);
-    void (*rename)  (fuse_req_t req, fuse_ino_t parent, const char *name,
-                     fuse_ino_t newparent, const char *newname);
-    void (*link)    (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
-                     const char *newname);
-    void (*open)    (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
-    void (*read)    (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+    void (*mknod)  (fuse_req_t req, fuse_ino_t parent, const char *name,
+                    mode_t mode, dev_t rdev);
+    void (*mkdir)  (fuse_req_t req, fuse_ino_t parent, const char *name,
+                    mode_t mode);
+    void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
+    void (*rmdir)  (fuse_req_t req, fuse_ino_t parent, const char *name);
+    void (*symlink)(fuse_req_t req, const char *link, fuse_ino_t parent,
+                    const char *name);
+    void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
+                    fuse_ino_t newparent, const char *newname);
+    void (*link)   (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
+                    const char *newname);
+    void (*open)   (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+    void (*read)   (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                    struct fuse_file_info *fi);
+    void (*write)  (fuse_req_t req, fuse_ino_t ino, const char *buf,
+                    size_t size, off_t off, struct fuse_file_info *fi);
+    void (*flush)  (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+    void (*release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+    void (*fsync)  (fuse_req_t req, fuse_ino_t ino, int datasync,
+                    struct fuse_file_info *fi);
+    void (*opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
+    void (*readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
+                    struct fuse_file_info *fi);
+    void (*releasedir)(fuse_req_t req, fuse_ino_t ino,
+                       struct fuse_file_info *fi);
+    void (*fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync,
                      struct fuse_file_info *fi);
-    void (*write)   (fuse_req_t req, fuse_ino_t ino, const char *buf,
-                     size_t size, off_t off, struct fuse_file_info *fi);
-    void (*flush)   (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
-    void (*release) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
-    void (*fsync)   (fuse_req_t req, fuse_ino_t ino, int datasync,
-                     struct fuse_file_info *fi);
-    void (*opendir) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
-    void (*readdir)  (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
-                      struct fuse_file_info *fi);
-    void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
-                        struct fuse_file_info *fi);
-    void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
-                      struct fuse_file_info *fi);
-    void (*statfs)  (fuse_req_t req);
+    void (*statfs) (fuse_req_t req);
     void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name,
                      const char *value, size_t size, int flags);
     void (*getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name,
                      size_t size);
     void (*listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size);
     void (*removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name);
+    void (*getlk)  (fuse_req_t req, fuse_ino_t ino, 
+                    const struct fuse_lock_param *lk);
+    void (*setlk)  (fuse_req_t req, fuse_ino_t ino, int sleep,
+                    const struct fuse_lock_param *lk);
 };
 
 /* ------------------------------------------ */
@@ -141,6 +153,9 @@ int fuse_reply_statfs(fuse_req_t req, const struct statfs *statfs);
 /* getxattr, listxattr */
 int fuse_reply_xattr(fuse_req_t req, size_t count);
 
+/* getlk */
+int fuse_reply_getlk(fuse_req_t req, const struct fuse_lock_param *lk);
+
 /* ------------------------------------------ */
 
 /* return the size of a directory entry */
index f5e2c8745274cdb981736d9cc941d631a1508efe..02dc9f053b555cf52f056bf994d233c305121433 100644 (file)
@@ -606,6 +606,125 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
        return res;
 }
 
+static int default_getlk(struct file *file, struct file_lock *fl)
+{
+       struct file_lock *cfl = posix_test_lock(file, fl);
+       fl->fl_type = F_UNLCK;
+       if (cfl)
+               *fl = *cfl;
+       return 0;
+}
+
+static void convert_file_lock(const struct file_lock *fl,
+                             struct fuse_file_lock *ffl)
+{
+       ffl->start = fl->fl_start;
+       ffl->end   = fl->fl_end;
+       ffl->owner = (unsigned long) fl->fl_owner;
+       ffl->pid   = fl->fl_pid;
+       ffl->type  = fl->fl_type;
+}
+
+static int convert_fuse_file_lock(const struct fuse_file_lock *ffl,
+                                 struct file_lock *fl)
+{
+       if (ffl->start < 0 || ffl->end < 0 || ffl->end <= ffl->start)
+               return -EIO;
+
+       if (ffl->type != F_UNLCK && ffl->type != F_RDLCK &&
+           ffl->type != F_WRLCK)
+               return -EIO;
+
+       fl->fl_start = ffl->start;
+       fl->fl_end   = ffl->end;
+       fl->fl_owner = (fl_owner_t) (unsigned long) ffl->owner;
+       fl->fl_pid   = ffl->pid;
+       fl->fl_type  = ffl->type;
+
+       return 0;
+}
+
+static int fuse_getlk(struct file *file, struct file_lock *fl)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_req *req;
+       struct fuse_lk_in_out arg;
+       int err;
+
+       if (fc->no_lk)
+               return default_getlk(file, fl);
+
+       req = fuse_get_request(fc);
+       if (!req)
+               return -EINTR;
+
+       memset(&arg, 0, sizeof(arg));
+       convert_file_lock(fl, &arg.lk);
+       req->in.h.opcode = FUSE_GETLK;
+       req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(arg);
+       req->in.args[0].value = &arg;
+       req->out.numargs = 1;
+       req->out.args[0].size = sizeof(arg);
+       req->out.args[0].value = &arg;
+       request_send(fc, req);
+       err = req->out.h.error;
+       fuse_put_request(fc, req);
+       if (!err)
+               err = convert_fuse_file_lock(&arg.lk, fl);
+       else if (err == -ENOSYS) {
+               fc->no_lk = 1;
+               err = default_getlk(file, fl);
+       }
+
+       return err;
+}
+
+static int fuse_setlk(struct file *file, struct file_lock *fl)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+       struct fuse_req *req;
+       struct fuse_lk_in_out arg;
+       int err;
+
+       if (fc->no_lk)
+               return posix_lock_file_wait(file, fl);
+
+       req = fuse_get_request(fc);
+       if (!req)
+               return -EINTR;
+
+       memset(&arg, 0, sizeof(arg));
+       convert_file_lock(fl, &arg.lk);
+       req->in.h.opcode = (fl->fl_flags & FL_SLEEP) ? FUSE_SETLKW : FUSE_SETLK;
+       req->in.h.nodeid = get_node_id(inode);
+       req->inode = inode;
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(arg);
+       req->in.args[0].value = &arg;
+       request_send(fc, req);
+       err = req->out.h.error;
+       fuse_put_request(fc, req);
+       if (err == -ENOSYS) {
+               fc->no_lk = 1;
+               err = posix_lock_file_wait(file, fl);
+       }
+
+       return err;
+}
+
+static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+       if (cmd == F_GETLK)
+               return fuse_getlk(file, fl);
+       else
+               return fuse_setlk(file, fl);
+}
+
 #ifndef KERNEL_2_6
 static ssize_t fuse_file_read(struct file *file, char __user *buf,
                              size_t count, loff_t *ppos)
@@ -657,6 +776,7 @@ static struct file_operations fuse_file_operations = {
        .flush          = fuse_flush,
        .release        = fuse_release,
        .fsync          = fuse_fsync,
+       .lock           = fuse_file_lock,
 #ifdef KERNEL_2_6
        .sendfile       = generic_file_sendfile,
 #endif
@@ -670,6 +790,7 @@ static struct file_operations fuse_direct_io_file_operations = {
        .flush          = fuse_flush,
        .release        = fuse_release,
        .fsync          = fuse_fsync,
+       .lock           = fuse_file_lock,
        /* no mmap and sendfile */
 };
 
index b9b29eb3b68b8317a556df13f8ac4289aac476bf..6b7ba160f36e0ba14760b8d2c56d52da73e6475a 100644 (file)
@@ -336,6 +336,8 @@ struct fuse_conn {
        /** Is removexattr not implemented by fs? */
        unsigned no_removexattr : 1;
 
+       unsigned no_lk : 1;
+
 #ifdef KERNEL_2_6
        /** Backing dev info */
        struct backing_dev_info bdi;
index 104d31a24b4b8b010a01dddac9d27e9054314226..a470bedb683e15eda8911e266dadaf805afcf965 100644 (file)
@@ -14,7 +14,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 1
+#define FUSE_KERNEL_MINOR_VERSION 2
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -55,6 +55,14 @@ struct fuse_kstatfs {
        __u32   namelen;
 };
 
+struct fuse_file_lock {
+       __u64   start;
+       __u64   end;
+       __u64   owner;
+       __u32   pid;
+       __u32   type;
+};
+
 #define FATTR_MODE     (1 << 0)
 #define FATTR_UID      (1 << 1)
 #define FATTR_GID      (1 << 2)
@@ -91,7 +99,10 @@ enum fuse_opcode {
        FUSE_OPENDIR       = 27,
        FUSE_READDIR       = 28,
        FUSE_RELEASEDIR    = 29,
-       FUSE_FSYNCDIR      = 30
+       FUSE_FSYNCDIR      = 30,
+       FUSE_GETLK         = 31,
+       FUSE_SETLK         = 32,
+       FUSE_SETLKW        = 33
 };
 
 /* Conservative buffer size for the client */
@@ -214,6 +225,10 @@ struct fuse_getxattr_out {
        __u32   padding;
 };
 
+struct fuse_lk_in_out {
+       struct fuse_file_lock lk;
+};
+
 struct fuse_init_in_out {
        __u32   major;
        __u32   minor;
index 7717ef18c5bc0a974803295291001106cd4f19fd..e256cd79802ad2155d4c0fa3ec029be7918d0b01 100644 (file)
@@ -502,7 +502,7 @@ static struct fuse_conn *new_conn(void)
                fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
                fc->bdi.unplug_io_fn = default_unplug_io_fn;
 #endif
-               fc->reqctr = 1;
+               fc->reqctr = 0;
        }
        return fc;
 }
index 4635e326fac768911c2e6a4a2103d9e0abfadd5d..7f92d047ee1345259381d66af365976e40ef784b 100644 (file)
@@ -78,6 +78,9 @@ static const char *opname(enum fuse_opcode opcode)
     case FUSE_READDIR:         return "READDIR";
     case FUSE_RELEASEDIR:      return "RELEASEDIR";
     case FUSE_FSYNCDIR:                return "FSYNCDIR";
+    case FUSE_GETLK:           return "GETLK";
+    case FUSE_SETLK:           return "SETLK";
+    case FUSE_SETLKW:          return "SETLKW";
     default:                   return "???";
     }
 }
@@ -132,6 +135,26 @@ static void convert_attr(const struct fuse_attr *attr, struct stat *stbuf)
 #endif
 }
 
+static void convert_file_lock(const struct fuse_file_lock *ffl,
+                              struct fuse_lock_param *lk)
+{
+    lk->type  = ffl->type;
+    lk->start = ffl->start;
+    lk->end   = ffl->end;
+    lk->owner = ffl->owner;
+    lk->pid   = ffl->pid;
+}
+
+static void convert_lock_param(const struct fuse_lock_param *lk,
+                               struct fuse_file_lock *ffl)
+{
+    ffl->type  = lk->type;
+    ffl->start = lk->start;
+    ffl->end   = lk->end;
+    ffl->owner = lk->owner;
+    ffl->pid   = lk->pid;
+}
+
 static  size_t iov_length(const struct iovec *iov, size_t count)
 {
     size_t seg;
@@ -358,6 +381,16 @@ int fuse_reply_xattr(fuse_req_t req, size_t count)
     return send_reply_req(req, &arg, sizeof(arg));
 }
 
+int fuse_reply_getlk(fuse_req_t req, const struct fuse_lock_param *lk)
+{
+    struct fuse_lk_in_out arg;
+    
+    memset(&arg, 0, sizeof(arg));
+    convert_lock_param(lk, &arg.lk);
+    
+    return send_reply_req(req, &arg, sizeof(arg));
+}
+
 static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, char *name)
 {
     if (req->f->op.lookup)
@@ -386,6 +419,7 @@ static void do_setattr(fuse_req_t req, fuse_ino_t nodeid,
 {
     if (req->f->op.setattr) {
         struct stat stbuf;
+        memset(&stbuf, 0, sizeof(stbuf));
         convert_attr(&arg->attr, &stbuf);
         req->f->op.setattr(req, nodeid, &stbuf, arg->valid);
     } else
@@ -654,6 +688,32 @@ static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, char *name)
         fuse_reply_err(req, ENOSYS);
 }
 
+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid,
+                     struct fuse_lk_in_out *arg)
+{
+    if (req->f->op.getlk) {
+        struct fuse_lock_param lk;
+        
+        memset(&lk, 0, sizeof(lk));
+        convert_file_lock(&arg->lk, &lk);
+        req->f->op.getlk(req, nodeid, &lk);
+    } else
+        fuse_reply_err(req, ENOSYS);
+}
+
+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, int sleep, 
+                     struct fuse_lk_in_out *arg)
+{
+    if (req->f->op.setlk) {
+        struct fuse_lock_param lk;
+        
+        memset(&lk, 0, sizeof(lk));
+        convert_file_lock(&arg->lk, &lk);
+        req->f->op.setlk(req, nodeid, sleep, &lk);
+    } else
+        fuse_reply_err(req, ENOSYS);
+}
+
 static void do_init(struct fuse_ll *f, uint64_t unique,
                     struct fuse_init_in_out *arg)
 {
@@ -852,6 +912,18 @@ void fuse_ll_process_cmd(struct fuse_ll *f, struct fuse_cmd *cmd)
         do_fsyncdir(req, in->nodeid, (struct fuse_fsync_in *) inarg);
         break;
 
+    case FUSE_GETLK:
+        do_getlk(req, in->nodeid, (struct fuse_lk_in_out *) inarg);
+        break;
+
+    case FUSE_SETLK:
+        do_setlk(req, in->nodeid, 0, (struct fuse_lk_in_out *) inarg);
+        break;
+
+    case FUSE_SETLKW:
+        do_setlk(req, in->nodeid, 1, (struct fuse_lk_in_out *) inarg);
+        break;
+
     default:
         fuse_reply_err(req, ENOSYS);
     }