Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932681AbbBQMuL (ORCPT ); Tue, 17 Feb 2015 07:50:11 -0500 Received: from cdptpa-outbound-snat.email.rr.com ([107.14.166.230]:47618 "EHLO cdptpa-oedge-vip.email.rr.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1756272AbbBQMuG (ORCPT ); Tue, 17 Feb 2015 07:50:06 -0500 From: Jeff Layton To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Linus Torvalds , "Kirill A. Shutemov" , "J. Bruce Fields" , Christoph Hellwig , Dave Chinner , Sasha Levin Subject: [PATCH 3/4] locks: when upgrading, don't remove old flock lock until replacing with new one Date: Tue, 17 Feb 2015 07:46:29 -0500 Message-Id: <1424177190-14252-4-git-send-email-jeff.layton@primarydata.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1424177190-14252-1-git-send-email-jeff.layton@primarydata.com> References: <1424177190-14252-1-git-send-email-jeff.layton@primarydata.com> X-RR-Connecting-IP: 107.14.168.118:25 X-Cloudmark-Score: 0 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 2792 Lines: 91 There is a potential problem when upgrading a flock lock. Suppose we have a LOCK_SH lock on a file, and then want to upgrade it to a LOCK_EX lock. We go through the first loop in flock_lock_file, and remove the first lock. We then go through the second loop and try to insert a new LOCK_EX lock. If however, there is another LOCK_SH lock on the file, we're out of luck. We've removed our LOCK_SH lock already and can't insert a LOCK_EX lock. Fix this by ensuring that we don't remove any lock that we're replacing until we're sure that we can add its replacement. Signed-off-by: Jeff Layton --- fs/locks.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 00c105f499a2..59eadd416b8c 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -864,13 +864,16 @@ static int posix_locks_deadlock(struct file_lock *caller_fl, static int flock_lock_file(struct file *filp, struct file_lock *request) { struct file_lock *new_fl = NULL; + struct file_lock *old_fl = NULL; struct file_lock *fl; struct file_lock_context *ctx; struct inode *inode = file_inode(filp); int error = 0; - bool found = false; LIST_HEAD(dispose); + /* flock_locks_conflict relies on this */ + WARN_ON_ONCE(request->fl_file != filp); + ctx = locks_get_lock_context(inode); if (!ctx) return -ENOMEM; @@ -885,22 +888,29 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) if (request->fl_flags & FL_ACCESS) goto find_conflict; + /* + * Do we already hold a lock on this filp? It may be upgradeable, or it + * may be just what we need. + */ list_for_each_entry(fl, &ctx->flc_flock, fl_list) { if (filp != fl->fl_file) continue; if (request->fl_type == fl->fl_type) goto out; - found = true; - locks_delete_lock_ctx(fl, &dispose); + old_fl = fl; break; } if (request->fl_type == F_UNLCK) { - if ((request->fl_flags & FL_EXISTS) && !found) - error = -ENOENT; + if (old_fl) { + locks_delete_lock_ctx(old_fl, &dispose); + if (request->fl_flags & FL_EXISTS) + error = -ENOENT; + } goto out; } + /* SETLK(W) */ find_conflict: list_for_each_entry(fl, &ctx->flc_flock, fl_list) { if (!flock_locks_conflict(request, fl)) @@ -915,6 +925,8 @@ find_conflict: if (request->fl_flags & FL_ACCESS) goto out; locks_copy_lock(new_fl, request); + if (old_fl) + locks_delete_lock_ctx(old_fl, &dispose); locks_insert_lock_ctx(new_fl, &ctx->flc_flock); new_fl = NULL; error = 0; -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/