fl->fl_ops->fl_release_private(fl);
fl->fl_ops = NULL;
}
- fl->fl_lmops = NULL;
+ if (fl->fl_lmops) {
+ if (fl->fl_lmops->lm_put_owner)
+ fl->fl_lmops->lm_put_owner(fl);
+ fl->fl_lmops = NULL;
+ }
}
EXPORT_SYMBOL_GPL(locks_release_private);
EXPORT_SYMBOL(locks_init_lock);
-static void locks_copy_private(struct file_lock *new, struct file_lock *fl)
-{
- if (fl->fl_ops) {
- if (fl->fl_ops->fl_copy_lock)
- fl->fl_ops->fl_copy_lock(new, fl);
- new->fl_ops = fl->fl_ops;
- }
- if (fl->fl_lmops)
- new->fl_lmops = fl->fl_lmops;
-}
-
/*
* Initialize a new lock from an existing file_lock structure.
*/
-void __locks_copy_lock(struct file_lock *new, const struct file_lock *fl)
+void locks_copy_conflock(struct file_lock *new, struct file_lock *fl)
{
new->fl_owner = fl->fl_owner;
new->fl_pid = fl->fl_pid;
new->fl_type = fl->fl_type;
new->fl_start = fl->fl_start;
new->fl_end = fl->fl_end;
+ new->fl_lmops = fl->fl_lmops;
new->fl_ops = NULL;
- new->fl_lmops = NULL;
+
+ if (fl->fl_lmops) {
+ if (fl->fl_lmops->lm_get_owner)
+ fl->fl_lmops->lm_get_owner(new, fl);
+ }
}
-EXPORT_SYMBOL(__locks_copy_lock);
+EXPORT_SYMBOL(locks_copy_conflock);
void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
/* "new" must be a freshly-initialized lock */
WARN_ON_ONCE(new->fl_ops);
- __locks_copy_lock(new, fl);
+ locks_copy_conflock(new, fl);
+
new->fl_file = fl->fl_file;
new->fl_ops = fl->fl_ops;
- new->fl_lmops = fl->fl_lmops;
- locks_copy_private(new, fl);
+ if (fl->fl_ops) {
+ if (fl->fl_ops->fl_copy_lock)
+ fl->fl_ops->fl_copy_lock(new, fl);
+ }
}
EXPORT_SYMBOL(locks_copy_lock);
kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG);
}
+static void
+lease_setup(struct file_lock *fl, void **priv)
+{
+ struct file *filp = fl->fl_file;
+ struct fasync_struct *fa = *priv;
+
+ /*
+ * fasync_insert_entry() returns the old entry if any. If there was no
+ * old entry, then it used "priv" and inserted it into the fasync list.
+ * Clear the pointer to indicate that it shouldn't be freed.
+ */
+ if (!fasync_insert_entry(fa->fa_fd, filp, &fl->fl_fasync, fa))
+ *priv = NULL;
+
+ __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
+}
+
static const struct lock_manager_operations lease_manager_ops = {
.lm_break = lease_break_callback,
.lm_change = lease_modify,
+ .lm_setup = lease_setup,
};
/*
break;
}
if (cfl) {
- __locks_copy_lock(fl, cfl);
+ locks_copy_conflock(fl, cfl);
if (cfl->fl_nspid)
fl->fl_pid = pid_vnr(cfl->fl_nspid);
} else
if (!posix_locks_conflict(request, fl))
continue;
if (conflock)
- __locks_copy_lock(conflock, fl);
+ locks_copy_conflock(conflock, fl);
error = -EAGAIN;
if (!(request->fl_flags & FL_SLEEP))
goto out;
}
return 0;
}
-
EXPORT_SYMBOL(lease_modify);
static bool past_time(unsigned long then)
struct file_lock **before;
struct file_lock *fl;
+ lockdep_assert_held(&inode->i_lock);
+
before = &inode->i_flock;
while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) {
trace_time_out_leases(inode, fl);
*/
void lease_get_mtime(struct inode *inode, struct timespec *time)
{
- struct file_lock *flock = inode->i_flock;
- if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
+ bool has_lease = false;
+ struct file_lock *flock;
+
+ if (inode->i_flock) {
+ spin_lock(&inode->i_lock);
+ flock = inode->i_flock;
+ if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
+ has_lease = true;
+ spin_unlock(&inode->i_lock);
+ }
+
+ if (has_lease)
*time = current_fs_time(inode->i_sb);
else
*time = inode->i_mtime;
return ret;
}
-static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
+static int
+generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **priv)
{
struct file_lock *fl, **before, **my_before = NULL, *lease;
struct dentry *dentry = filp->f_path.dentry;
return -EINVAL;
}
+ spin_lock(&inode->i_lock);
+ time_out_leases(inode);
error = check_conflicting_open(dentry, arg);
if (error)
goto out;
}
if (my_before != NULL) {
+ lease = *my_before;
error = lease->fl_lmops->lm_change(my_before, arg);
- if (!error)
- *flp = *my_before;
- goto out;
+ if (error)
+ goto out;
+ goto out_setup;
}
error = -EINVAL;
smp_mb();
error = check_conflicting_open(dentry, arg);
if (error)
- locks_unlink_lock(flp);
+ goto out_unlink;
+
+out_setup:
+ if (lease->fl_lmops->lm_setup)
+ lease->fl_lmops->lm_setup(lease, priv);
out:
+ spin_unlock(&inode->i_lock);
if (is_deleg)
mutex_unlock(&inode->i_mutex);
+ if (!error && !my_before)
+ *flp = NULL;
return error;
+out_unlink:
+ locks_unlink_lock(before);
+ goto out;
}
-static int generic_delete_lease(struct file *filp, struct file_lock **flp)
+static int generic_delete_lease(struct file *filp)
{
+ int error = -EAGAIN;
struct file_lock *fl, **before;
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
- trace_generic_delete_lease(inode, *flp);
-
+ spin_lock(&inode->i_lock);
for (before = &inode->i_flock;
((fl = *before) != NULL) && IS_LEASE(fl);
before = &fl->fl_next) {
- if (fl->fl_file != filp)
- continue;
- return (*flp)->fl_lmops->lm_change(before, F_UNLCK);
+ if (fl->fl_file == filp)
+ break;
}
- return -EAGAIN;
+ trace_generic_delete_lease(inode, fl);
+ if (fl)
+ error = fl->fl_lmops->lm_change(before, F_UNLCK);
+ spin_unlock(&inode->i_lock);
+ return error;
}
/**
* generic_setlease - sets a lease on an open file
- * @filp: file pointer
- * @arg: type of lease to obtain
- * @flp: input - file_lock to use, output - file_lock inserted
+ * @filp: file pointer
+ * @arg: type of lease to obtain
+ * @flp: input - file_lock to use, output - file_lock inserted
+ * @priv: private data for lm_setup (may be NULL if lm_setup
+ * doesn't require it)
*
* The (input) flp->fl_lmops->lm_break function is required
* by break_lease().
- *
- * Called with inode->i_lock held.
*/
-int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
+int generic_setlease(struct file *filp, long arg, struct file_lock **flp,
+ void **priv)
{
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
if (error)
return error;
- time_out_leases(inode);
-
- BUG_ON(!(*flp)->fl_lmops->lm_break);
-
switch (arg) {
case F_UNLCK:
- return generic_delete_lease(filp, flp);
+ return generic_delete_lease(filp);
case F_RDLCK:
case F_WRLCK:
- return generic_add_lease(filp, arg, flp);
+ if (!(*flp)->fl_lmops->lm_break) {
+ WARN_ON_ONCE(1);
+ return -ENOLCK;
+ }
+ return generic_add_lease(filp, arg, flp, priv);
default:
return -EINVAL;
}
}
EXPORT_SYMBOL(generic_setlease);
-static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
-{
- if (filp->f_op->setlease)
- return filp->f_op->setlease(filp, arg, lease);
- else
- return generic_setlease(filp, arg, lease);
-}
-
/**
- * vfs_setlease - sets a lease on an open file
- * @filp: file pointer
- * @arg: type of lease to obtain
- * @lease: file_lock to use
- *
- * Call this to establish a lease on the file.
- * The (*lease)->fl_lmops->lm_break operation must be set; if not,
- * break_lease will oops!
- *
- * This will call the filesystem's setlease file method, if
- * defined. Note that there is no getlease method; instead, the
- * filesystem setlease method should call back to setlease() to
- * add a lease to the inode's lease list, where fcntl_getlease() can
- * find it. Since fcntl_getlease() only reports whether the current
- * task holds a lease, a cluster filesystem need only do this for
- * leases held by processes on this node.
- *
- * There is also no break_lease method; filesystems that
- * handle their own leases should break leases themselves from the
- * filesystem's open, create, and (on truncate) setattr methods.
- *
- * Warning: the only current setlease methods exist only to disable
- * leases in certain cases. More vfs changes may be required to
- * allow a full filesystem lease implementation.
+ * vfs_setlease - sets a lease on an open file
+ * @filp: file pointer
+ * @arg: type of lease to obtain
+ * @lease: file_lock to use when adding a lease
+ * @priv: private info for lm_setup when adding a lease (may be
+ * NULL if lm_setup doesn't require it)
+ *
+ * Call this to establish a lease on the file. The "lease" argument is not
+ * used for F_UNLCK requests and may be NULL. For commands that set or alter
+ * an existing lease, the (*lease)->fl_lmops->lm_break operation must be set;
+ * if not, this function will return -ENOLCK (and generate a scary-looking
+ * stack trace).
+ *
+ * The "priv" pointer is passed directly to the lm_setup function as-is. It
+ * may be NULL if the lm_setup operation doesn't require it.
*/
-
-int vfs_setlease(struct file *filp, long arg, struct file_lock **lease)
+int
+vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **priv)
{
- struct inode *inode = file_inode(filp);
- int error;
-
- spin_lock(&inode->i_lock);
- error = __vfs_setlease(filp, arg, lease);
- spin_unlock(&inode->i_lock);
-
- return error;
+ if (filp->f_op->setlease)
+ return filp->f_op->setlease(filp, arg, lease, priv);
+ else
+ return generic_setlease(filp, arg, lease, priv);
}
EXPORT_SYMBOL_GPL(vfs_setlease);
-static int do_fcntl_delete_lease(struct file *filp)
-{
- struct file_lock fl, *flp = &fl;
-
- lease_init(filp, F_UNLCK, flp);
-
- return vfs_setlease(filp, F_UNLCK, &flp);
-}
-
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
{
- struct file_lock *fl, *ret;
- struct inode *inode = file_inode(filp);
+ struct file_lock *fl;
struct fasync_struct *new;
int error;
locks_free_lock(fl);
return -ENOMEM;
}
- ret = fl;
- spin_lock(&inode->i_lock);
- error = __vfs_setlease(filp, arg, &ret);
- if (error)
- goto out_unlock;
- if (ret == fl)
- fl = NULL;
-
- /*
- * fasync_insert_entry() returns the old entry if any.
- * If there was no old entry, then it used 'new' and
- * inserted it into the fasync list. Clear new so that
- * we don't release it here.
- */
- if (!fasync_insert_entry(fd, filp, &ret->fl_fasync, new))
- new = NULL;
+ new->fa_fd = fd;
- error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
-out_unlock:
- spin_unlock(&inode->i_lock);
+ error = vfs_setlease(filp, arg, &fl, (void **)&new);
if (fl)
locks_free_lock(fl);
if (new)
int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
{
if (arg == F_UNLCK)
- return do_fcntl_delete_lease(filp);
+ return vfs_setlease(filp, F_UNLCK, NULL, NULL);
return do_fcntl_add_lease(fd, filp, arg);
}
if (file_lock.fl_type != F_UNLCK) {
error = posix_lock_to_flock(&flock, &file_lock);
if (error)
- goto out;
+ goto rel_priv;
}
error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock)))
error = 0;
+rel_priv:
+ locks_release_private(&file_lock);
out:
return error;
}
error = -EFAULT;
if (!copy_to_user(l, &flock, sizeof(flock)))
error = 0;
-
+
+ locks_release_private(&file_lock);
out:
return error;
}
module_init(proc_locks_init);
#endif
-/**
- * lock_may_read - checks that the region is free of locks
- * @inode: the inode that is being read
- * @start: the first byte to read
- * @len: the number of bytes to read
- *
- * Emulates Windows locking requirements. Whole-file
- * mandatory locks (share modes) can prohibit a read and
- * byte-range POSIX locks can prohibit a read if they overlap.
- *
- * N.B. this function is only ever called
- * from knfsd and ownership of locks is never checked.
- */
-int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
-{
- struct file_lock *fl;
- int result = 1;
-
- spin_lock(&inode->i_lock);
- for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (IS_POSIX(fl)) {
- if (fl->fl_type == F_RDLCK)
- continue;
- if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
- continue;
- } else if (IS_FLOCK(fl)) {
- if (!(fl->fl_type & LOCK_MAND))
- continue;
- if (fl->fl_type & LOCK_READ)
- continue;
- } else
- continue;
- result = 0;
- break;
- }
- spin_unlock(&inode->i_lock);
- return result;
-}
-
-EXPORT_SYMBOL(lock_may_read);
-
-/**
- * lock_may_write - checks that the region is free of locks
- * @inode: the inode that is being written
- * @start: the first byte to write
- * @len: the number of bytes to write
- *
- * Emulates Windows locking requirements. Whole-file
- * mandatory locks (share modes) can prohibit a write and
- * byte-range POSIX locks can prohibit a write if they overlap.
- *
- * N.B. this function is only ever called
- * from knfsd and ownership of locks is never checked.
- */
-int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
-{
- struct file_lock *fl;
- int result = 1;
-
- spin_lock(&inode->i_lock);
- for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (IS_POSIX(fl)) {
- if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
- continue;
- } else if (IS_FLOCK(fl)) {
- if (!(fl->fl_type & LOCK_MAND))
- continue;
- if (fl->fl_type & LOCK_WRITE)
- continue;
- } else
- continue;
- result = 0;
- break;
- }
- spin_unlock(&inode->i_lock);
- return result;
-}
-
-EXPORT_SYMBOL(lock_may_write);
-
static int __init filelock_init(void)
{
int i;