mirror of
https://github.com/libfuse/libfuse.git
synced 2024-11-24 20:54:16 +08:00
added file locking
This commit is contained in:
parent
8d4d1b8241
commit
e3b8309657
@ -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.
|
||||
|
@ -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 */
|
||||
|
121
kernel/file.c
121
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 */
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user