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.
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 */
/* ------------------------------------------ */
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);
};
/* ------------------------------------------ */
/* 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 */
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)
.flush = fuse_flush,
.release = fuse_release,
.fsync = fuse_fsync,
+ .lock = fuse_file_lock,
#ifdef KERNEL_2_6
.sendfile = generic_file_sendfile,
#endif
.flush = fuse_flush,
.release = fuse_release,
.fsync = fuse_fsync,
+ .lock = fuse_file_lock,
/* no mmap and sendfile */
};
/** 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;
#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
__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)
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 */
__u32 padding;
};
+struct fuse_lk_in_out {
+ struct fuse_file_lock lk;
+};
+
struct fuse_init_in_out {
__u32 major;
__u32 minor;
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;
}
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 "???";
}
}
#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;
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)
{
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
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)
{
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);
}