From patchwork Tue Aug 6 14:30:01 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miklos Szeredi X-Patchwork-Id: 2839433 Return-Path: X-Original-To: patchwork-linux-nfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id C4C019F479 for ; Tue, 6 Aug 2013 14:31:10 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 18DCF201E2 for ; Tue, 6 Aug 2013 14:31:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D17AD201C0 for ; Tue, 6 Aug 2013 14:31:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755078Ab3HFOa2 (ORCPT ); Tue, 6 Aug 2013 10:30:28 -0400 Received: from mail-bk0-f51.google.com ([209.85.214.51]:33121 "EHLO mail-bk0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754760Ab3HFOaZ (ORCPT ); Tue, 6 Aug 2013 10:30:25 -0400 Received: by mail-bk0-f51.google.com with SMTP id ji2so186431bkc.10 for ; Tue, 06 Aug 2013 07:30:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=szeredi.hu; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=lUbzN49yqLJFYReifOqoTSgA/87bt06AG2dUybzuqLs=; b=pi1jxTFDXId8pLwkYaD7D7wdx0ugXAJLmPyYso0LEFvU9zHmOBuc3Feix4uDWUwzq2 s8PEOzXUJNR64DIAneow9Hx3pV+dvKYh/UIdHt2vXYQxtcNsotNtBSmEJXzklcKXIc1M 8i+cIwBH0qLlf5sABW2b0N9OF0RXDAUIm6QAY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=lUbzN49yqLJFYReifOqoTSgA/87bt06AG2dUybzuqLs=; b=bYkngH5faDQEUzSUgzd877nTwdmhQwRRKoBVx7br5F7jzGa7i4kyhjvt9DuZFu9QmV QMFNd+82zCUiOMtj7uJeOO5ANe6znU2Edy4w6DtTEoYsb/oTS9Ac5pvpgMrhjB/nGrwc uSsavjqsv+Gx65Mz5v2qoVMhhLUNLm0SCFpEqWL8ckYTDWpNgFw5GuDujpjNXmMN15HE 3c+eZXWMWZcBcYiiELNgGsBN/EZvID0JD7J7Qzo78wzfsRNysmfMQARhJjIkY8qdIVRO HGOc49v8YAGEFjms7Z22acYs+d5qfekF1/bvHNkdBQnfYUqRmwCZPfUKvjhCqHnamT5W xWpw== X-Gm-Message-State: ALoCoQl9EX65rkPY6WOc14usLD+KV0ScJVbYV34U+RgO1a4RgTIVv7DgB0VVLZrX0hX8S9H4gQC9 X-Received: by 10.204.65.9 with SMTP id g9mr436024bki.33.1375799422118; Tue, 06 Aug 2013 07:30:22 -0700 (PDT) Received: from tucsk.pomaz.szeredi.hu (563BF5AA.catv.pool.telekom.hu. [86.59.245.170]) by mx.google.com with ESMTPSA id oe7sm726001bkb.5.2013.08.06.07.30.20 for (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 06 Aug 2013 07:30:21 -0700 (PDT) From: Miklos Szeredi To: rwheeler@redhat.com, avati@redhat.com, viro@ZenIV.linux.org.uk Cc: bfoster@redhat.com, dhowells@redhat.com, eparis@redhat.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-nfs@vger.kernel.org, Trond.Myklebust@netapp.com, swhiteho@redhat.com, mszeredi@suse.cz Subject: [PATCH 2/4] vfs: check unlinked ancestors before mount Date: Tue, 6 Aug 2013 16:30:01 +0200 Message-Id: <1375799403-28544-3-git-send-email-miklos@szeredi.hu> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1375799403-28544-1-git-send-email-miklos@szeredi.hu> References: <1375799403-28544-1-git-send-email-miklos@szeredi.hu> Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org X-Spam-Status: No, score=-5.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, KHOP_BIG_TO_CC,RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Miklos Szeredi We check submounts before doing d_drop() on a non-empty directory dentry in NFS (have_submounts()). But we do not exclude a racing mount. Nor do we prevent mounts to be added to the disconnected subtree using relative paths after the d_drop(). This patch fixes these issues by checking for unlinked (unhashed, non-root) ancestors before proceeding with the mount. This is done after setting DCACHE_MOUNTED on the soon-to-be mountpoint and with the rename seqlock taken for write. This ensures that the only one of have_submounts() or has_unlinked_ancestor() can succeed. Signed-off-by: Miklos Szeredi --- fs/dcache.c | 28 ++++++++++++++++++++++++++++ fs/internal.h | 1 + fs/namespace.c | 9 +++++++++ 3 files changed, 38 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index ba429d9..eae7cc1 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1103,6 +1103,34 @@ rename_retry: } EXPORT_SYMBOL(have_submounts); +static bool __has_unlinked_ancestor(struct dentry *dentry) +{ + struct dentry *this; + + for (this = dentry; !IS_ROOT(this); this = this->d_parent) { + if (d_unhashed(this)) + return true; + } + return false; +} + +/* + * Called by mount code to check if the mountpoint is reachable (e.g. NFS can + * unhash a directory dentry and then the complete subtree can become + * unreachable). + */ +bool has_unlinked_ancestor(struct dentry *dentry) +{ + bool found; + + /* Need exclusion wrt. check_submounts_and_drop() */ + write_seqlock(&rename_lock); + found = __has_unlinked_ancestor(dentry); + write_sequnlock(&rename_lock); + + return found; +} + /* * Search the dentry child list of the specified parent, * and move any unused dentries to the end of the unused diff --git a/fs/internal.h b/fs/internal.h index 7c5f01c..d232355 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -126,6 +126,7 @@ extern int invalidate_inodes(struct super_block *, bool); * dcache.c */ extern struct dentry *__d_alloc(struct super_block *, const struct qstr *); +extern bool has_unlinked_ancestor(struct dentry *dentry); /* * read_write.c diff --git a/fs/namespace.c b/fs/namespace.c index 7b1ca9b..bb92a9c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -634,6 +634,15 @@ static struct mountpoint *new_mountpoint(struct dentry *dentry) } dentry->d_flags |= DCACHE_MOUNTED; spin_unlock(&dentry->d_lock); + + if (has_unlinked_ancestor(dentry)) { + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_MOUNTED; + spin_unlock(&dentry->d_lock); + kfree(mp); + return ERR_PTR(-ENOENT); + } + mp->m_dentry = dentry; mp->m_count = 1; list_add(&mp->m_hash, chain);