diff mbox

[07/10] locks: define a lm_setup handler for leases

Message ID 1408804878-1331-8-git-send-email-jlayton@primarydata.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jeff Layton Aug. 23, 2014, 2:41 p.m. UTC
...and call it when setting up a new lease or modifying an existing one.
Add a lm_setup handler for fcntl leases and move the fasync setup into
it.

At the same time, change the semantics of how the file_lock double
pointer is handled. Up until now on a successful lease return, you get
back a pointer to the lock on the inode's i_flock list. This is bad,
since that pointer can't be relied on as valid once the inode->i_lock
has been released. Leases also don't carry a lot of information, so
tracking a pointer to it isn't terribly helpful anyway.

Change the code to instead just zero out the pointer if the lease we
passed in ended up being used. Then the callers can just check to see
if it's NULL after the call and free it if it isn't.

The new aux argument for lm_setup has the same semantics. The lm_setup
function can zero the pointer out to signal to the caller that it should
not be freed after the function returns.

Signed-off-by: Jeff Layton <jlayton@primarydata.com>
---
 fs/locks.c          | 96 +++++++++++++++++++++++++++++------------------------
 fs/nfsd/nfs4state.c |  9 ++---
 include/linux/fs.h  |  1 +
 3 files changed, 56 insertions(+), 50 deletions(-)

Comments

Christoph Hellwig Aug. 24, 2014, 3:58 p.m. UTC | #1
I like this change a lot!  But one caveat:

> +	/*
> +	 * Despite the fact that it's an int return function, __f_setown never
> +	 * returns an error. Just ignore any error return here, but spew a
> +	 * WARN_ON_ONCE in case this ever changes.
> +	 */
> +	WARN_ON_ONCE(__f_setown(filp, task_pid(current), PIDTYPE_PID, 0));

I don't think this is a good idea.  The errors in __f_setown come from
the security modules, and they could change easily.  If you can convince
the LSM people to change their file_set_fowner routine to return void
we could change __f_setown to return void as well and be done with it,
but without that this looks too dangerous.

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jeff Layton Aug. 25, 2014, 1:19 a.m. UTC | #2
On Sun, 24 Aug 2014 08:58:58 -0700
Christoph Hellwig <hch@infradead.org> wrote:

> I like this change a lot!  But one caveat:
> 
> > +	/*
> > +	 * Despite the fact that it's an int return function, __f_setown never
> > +	 * returns an error. Just ignore any error return here, but spew a
> > +	 * WARN_ON_ONCE in case this ever changes.
> > +	 */
> > +	WARN_ON_ONCE(__f_setown(filp, task_pid(current), PIDTYPE_PID, 0));
> 
> I don't think this is a good idea.  The errors in __f_setown come from
> the security modules, and they could change easily.  If you can convince
> the LSM people to change their file_set_fowner routine to return void
> we could change __f_setown to return void as well and be done with it,
> but without that this looks too dangerous.
> 

Understood. I figured that this might not be acceptable. I can make
this propagate the error back up, but cleaning up the mess may not be
easy. I'll see what I can do.

Note that the error handling in the existing code looks wrong to me
too. The lease gets added to the list (or updated), the fasync handler
gets set up. Then, if __f_setown returns an error, the code just
returns that error without unwinding anything.
Christoph Hellwig Aug. 26, 2014, 1:58 p.m. UTC | #3
On Sun, Aug 24, 2014 at 09:19:53PM -0400, Jeff Layton wrote:
> > I don't think this is a good idea.  The errors in __f_setown come from
> > the security modules, and they could change easily.  If you can convince
> > the LSM people to change their file_set_fowner routine to return void
> > we could change __f_setown to return void as well and be done with it,
> > but without that this looks too dangerous.
> > 
> 
> Understood. I figured that this might not be acceptable. I can make
> this propagate the error back up, but cleaning up the mess may not be
> easy. I'll see what I can do.

I'd say talk to the LSM people.  Right now they are only using it to
set up private data pointers, so they might agree on turning it into
a void return.  Or just be bold and do that work yourself, include it in
this series and just Cc them.

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/locks.c b/fs/locks.c
index 906e09da115a..b35b706c05fe 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -432,9 +432,32 @@  static void lease_break_callback(struct file_lock *fl)
 	kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG);
 }
 
+static void
+lease_setup(struct file_lock *fl, void **aux)
+{
+	struct file *filp = fl->fl_file;
+	struct fasync_struct *fa = *aux;
+
+	/*
+	 * fasync_insert_entry() returns the old entry if any. If there was no
+	 * old entry, then it used "aux" 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))
+		*aux = NULL;
+
+	/*
+	 * Despite the fact that it's an int return function, __f_setown never
+	 * returns an error. Just ignore any error return here, but spew a
+	 * WARN_ON_ONCE in case this ever changes.
+	 */
+	WARN_ON_ONCE(__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,
 };
 
 /*
@@ -1609,9 +1632,9 @@  generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **au
 	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;
@@ -1632,9 +1655,15 @@  generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **au
 	error = check_conflicting_open(dentry, arg);
 	if (error)
 		goto out_unlink;
+
+out_setup:
+	if (lease->fl_lmops->lm_setup)
+		lease->fl_lmops->lm_setup(lease, aux);
 out:
 	if (is_deleg)
 		mutex_unlock(&inode->i_mutex);
+	if (!error && !my_before)
+		*flp = NULL;
 	return error;
 out_unlink:
 	locks_unlink_lock(before);
@@ -1659,10 +1688,11 @@  static int generic_delete_lease(struct file *filp)
 
 /**
  *	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
- *	@aux: auxillary data for lm_setup
+ *	@filp:	file pointer
+ *	@arg:	type of lease to obtain
+ *	@flp:	input - file_lock to use, output - file_lock inserted
+ *	@aux:	auxillary 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().
@@ -1702,21 +1732,13 @@  int generic_setlease(struct file *filp, long arg, struct file_lock **flp,
 }
 EXPORT_SYMBOL(generic_setlease);
 
-static int
-__vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **aux)
-{
-	if (filp->f_op->setlease)
-		return filp->f_op->setlease(filp, arg, lease, aux);
-	else
-		return generic_setlease(filp, arg, lease, aux);
-}
-
 /**
  * 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
- * @aux: auxillary info for lm_setup when adding a lease
+ * @filp:	file pointer
+ * @arg:	type of lease to obtain
+ * @lease:	file_lock to use when adding a lease
+ * @aux:	auxillary 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
@@ -1730,8 +1752,10 @@  __vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **aux
  * 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.
+ *
+ * The "aux" 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, void **aux)
 {
@@ -1739,17 +1763,18 @@  vfs_setlease(struct file *filp, long arg, struct file_lock **lease, void **aux)
 	int error;
 
 	spin_lock(&inode->i_lock);
-	error = __vfs_setlease(filp, arg, lease, aux);
+	if (filp->f_op->setlease)
+		error = filp->f_op->setlease(filp, arg, lease, aux);
+	else
+		error = generic_setlease(filp, arg, lease, aux);
 	spin_unlock(&inode->i_lock);
-
 	return error;
 }
 EXPORT_SYMBOL_GPL(vfs_setlease);
 
 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;
 
@@ -1762,26 +1787,9 @@  static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
 		locks_free_lock(fl);
 		return -ENOMEM;
 	}
-	ret = fl;
-	spin_lock(&inode->i_lock);
-	error = __vfs_setlease(filp, arg, &ret, NULL);
-	if (error)
-		goto out_unlock;
-	if (ret == fl)
-		fl = NULL;
+	new->fa_fd = fd;
 
-	/*
-	 * 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;
-
-	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)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d964a41c9151..6af5d0890373 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3773,7 +3773,7 @@  static struct file_lock *nfs4_alloc_init_lease(struct nfs4_file *fp, int flag)
 static int nfs4_setlease(struct nfs4_delegation *dp)
 {
 	struct nfs4_file *fp = dp->dl_stid.sc_file;
-	struct file_lock *fl, *ret;
+	struct file_lock *fl;
 	struct file *filp;
 	int status = 0;
 
@@ -3787,14 +3787,11 @@  static int nfs4_setlease(struct nfs4_delegation *dp)
 		return -EBADF;
 	}
 	fl->fl_file = filp;
-	ret = fl;
 	status = vfs_setlease(filp, fl->fl_type, &fl, NULL);
-	if (status) {
+	if (fl)
 		locks_free_lock(fl);
+	if (status)
 		goto out_fput;
-	}
-	if (ret != fl)
-		locks_free_lock(fl);
 	spin_lock(&state_lock);
 	spin_lock(&fp->fi_lock);
 	/* Did the lease get broken before we took the lock? */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2668d054147f..70d22c12924f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -874,6 +874,7 @@  struct lock_manager_operations {
 	int (*lm_grant)(struct file_lock *, int);
 	void (*lm_break)(struct file_lock *);
 	int (*lm_change)(struct file_lock **, int);
+	void (*lm_setup)(struct file_lock *, void **);
 };
 
 struct lock_manager {