From e3b830965749c516756f4c13588a1eb29e648ec2 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 22 Jul 2005 17:24:30 +0000 Subject: [PATCH] added file locking --- ChangeLog | 2 + include/fuse_lowlevel.h | 85 ++++++++++++++++------------ kernel/file.c | 121 ++++++++++++++++++++++++++++++++++++++++ kernel/fuse_i.h | 2 + kernel/fuse_kernel.h | 19 ++++++- kernel/inode.c | 2 +- lib/fuse_lowlevel.c | 72 ++++++++++++++++++++++++ 7 files changed, 265 insertions(+), 38 deletions(-) diff --git a/ChangeLog b/ChangeLog index f0dfd3d..fbf82dc 100644 --- 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 * Don't change mtime/ctime/atime to local time on read/write. diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index deab718..ddead68 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -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 */ diff --git a/kernel/file.c b/kernel/file.c index f5e2c87..02dc9f0 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -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 */ }; diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index b9b29eb..6b7ba16 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -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; diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h index 104d31a..a470bed 100644 --- a/kernel/fuse_kernel.h +++ b/kernel/fuse_kernel.h @@ -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; diff --git a/kernel/inode.c b/kernel/inode.c index 7717ef1..e256cd7 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -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; } diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 4635e32..7f92d04 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -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); } -- 2.30.2