added file locking

This commit is contained in:
Miklos Szeredi 2005-07-22 17:24:30 +00:00
parent 8d4d1b8241
commit e3b8309657
7 changed files with 265 additions and 38 deletions

View File

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

View 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 */

View 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 */
};

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

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

View 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;
}

View 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);
}