From patchwork Mon May 1 13:41:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amir Goldstein X-Patchwork-Id: 9706625 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 0D73960385 for ; Mon, 1 May 2017 13:42:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F35DB205A8 for ; Mon, 1 May 2017 13:42:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E837C2807E; Mon, 1 May 2017 13:42:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 48519205A8 for ; Mon, 1 May 2017 13:42:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1425611AbdEANmY (ORCPT ); Mon, 1 May 2017 09:42:24 -0400 Received: from mail-wr0-f195.google.com ([209.85.128.195]:33651 "EHLO mail-wr0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1424445AbdEANmN (ORCPT ); Mon, 1 May 2017 09:42:13 -0400 Received: by mail-wr0-f195.google.com with SMTP id w50so13817862wrc.0; Mon, 01 May 2017 06:42:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=e7E7IxOj+ST/+QkN9FoVXGuLU1oWPC05dOVEN8PYsCY=; b=JW/RtvMcSSwqkVbibC6VNrW6yrV/bGZLJ9euFWXalp8+cEprYtzjT4Ti/oc/IT8+0u p/UwrgmZ3NQPXC8fW682eJfHRDMYh8hVCv5BQQrLr00v3Du4UXjnK39qNHcCCvpnazgZ MsVuWXysveRl2w07XWOUtoej5ElZRA/OdPxN0ENeWWH7BHr8gLXYW4Nq+ad+WftNms32 z/bJLkPXTkdLOVEBTyuSDECFtq/b78EdqVAUYrzaM1JUhpGbNa4lc5zDjFX/b0iTVOXD 0enAb2bkjv3o1dFZ3UGQECfRlXkRzwgtCpGS/dbSWJXO3CVrXVs1qvcGi9Af5QTZ+kBJ iKRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=e7E7IxOj+ST/+QkN9FoVXGuLU1oWPC05dOVEN8PYsCY=; b=R4P7cdYwJjy+a5ep+nq1/92Z6hBY3a1gUh//7zOoBZJrWzjPPzG0q4PDonuXvxWL4H Rah6mQSHNT6q+SyZg65wmSJFzmZSrjN50qSS5wLrNu7PqVql6RE0YHhSupZ2Q1VINecG zufGL5B323jmL975mqa95dz1a4V/Lc8taKKWPZcB/d+/+puOJ38LvtL/WT8knopXu//j tXzCmySwZMJFcswOYbEg2aECh4dHlSFaCsGkKOzx4A8Jx1AaHLlkWbm4fs6MjuZhhN55 uui1pEvADoy3oEgau/w1RXeI7C1UY7IAEgrkxaVtuEmSXCucdcZaNYIjJ1ASQGVIMqiQ ARdw== X-Gm-Message-State: AN3rC/6MZ+ytwY6z0uirwJa/TCRra+pE2VXVfNcHu2Pq/EN1OZdHECRL y3JeEODP+wBtOQ== X-Received: by 10.223.154.240 with SMTP id a103mr15901042wrc.5.1493646131500; Mon, 01 May 2017 06:42:11 -0700 (PDT) Received: from amir-VirtualBox.lan (bzq-79-179-55-127.red.bezeqint.net. [79.179.55.127]) by smtp.gmail.com with ESMTPSA id l82sm12532249wmf.17.2017.05.01.06.42.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 01 May 2017 06:42:10 -0700 (PDT) From: Amir Goldstein To: Miklos Szeredi Cc: Vivek Goyal , Al Viro , linux-unionfs@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [PATCH v4 08/15] ovl: lookup non-dir copy up origin by file handle Date: Mon, 1 May 2017 16:41:59 +0300 Message-Id: <1493646126-10101-9-git-send-email-amir73il@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1493646126-10101-1-git-send-email-amir73il@gmail.com> References: <1493646126-10101-1-git-send-email-amir73il@gmail.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When redirect_fh is enabled, if overlay.origin xattr is found on a non-dir upper inode, instead of lookup of the copy up origin in lower layer by name, try to get it by calling exportfs_decode_fh(). On failure to lookup by file handle to lower layer or if redirect_fh is disabled, do not lookup the copy up origin by name, because the lower found by name could be another file in case the upper file was renamed. Signed-off-by: Amir Goldstein --- fs/overlayfs/namei.c | 176 +++++++++++++++++++++++++++++++++++++++++++++-- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 14 ++++ 3 files changed, 186 insertions(+), 5 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 66072b0..695a78e 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "overlayfs.h" #include "ovl_entry.h" @@ -22,7 +24,10 @@ struct ovl_lookup_data { bool stop; bool last; int idx; - char *redirect; + bool by_path; /* redirect by path: */ + char *redirect; /* - path to follow */ + bool by_fh; /* redirect by file handle: */ + struct ovl_fh *fh; /* - file handle to follow */ }; static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d, @@ -82,6 +87,51 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d, goto err_free; } +static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name) +{ + int res; + void *buf = NULL; + + res = vfs_getxattr(dentry, name, NULL, 0); + if (res <= 0) { + if (res == -ENODATA || res == -EOPNOTSUPP) + return 0; + goto fail; + } + buf = kzalloc(res, GFP_TEMPORARY); + if (!buf) { + res = -ENOMEM; + goto fail; + } + + res = vfs_getxattr(dentry, name, buf, res); + if (res < 0 || !ovl_redirect_fh_ok(buf, res)) + goto fail; + + return (struct ovl_fh *)buf; + +err_free: + kfree(buf); + return NULL; +fail: + pr_warn_ratelimited("overlayfs: failed to get %s (%i)\n", + name, res); + goto err_free; +} + +static void ovl_check_redirect_fh(struct dentry *dentry, + struct ovl_lookup_data *d) +{ + kfree(d->fh); + d->fh = ovl_get_fh(dentry, OVL_XATTR_ORIGIN); +} + +static void ovl_reset_redirect_fh(struct ovl_lookup_data *d) +{ + kfree(d->fh); + d->fh = NULL; +} + static bool ovl_is_opaquedir(struct dentry *dentry) { int res; @@ -149,9 +199,23 @@ static int ovl_lookup_data(struct dentry *this, struct ovl_lookup_data *d, * Check redirect dir even if d->last, because with redirect_dir, * a merge dir may have an opaque dir parent. */ - err = ovl_check_redirect(this, d, prelen, post); - if (err) - goto out_err; + if (d->by_path) { + err = ovl_check_redirect(this, d, prelen, post); + if (err) + goto out_err; + } + /* + * If non-dir has a valid origin file handle, it will be used to + * find the copy up origin in lower layers. + * + * Directory lookup by fh is not desired for all workloads, so it + * will be enabled by a future mount option. + */ + if (d->by_fh && !d_is_dir(this)) { + ovl_check_redirect_fh(this, d); + d->stop = !d->fh; + } + out: *ret = this; return 0; @@ -225,6 +289,76 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, return 0; } +static struct dentry *ovl_decode_fh(struct vfsmount *mnt, + const struct ovl_fh *fh, + int (*acceptable)(void *, struct dentry *)) +{ + int bytes = (fh->len - offsetof(struct ovl_fh, fid)); + + /* + * When redirect_fh is disabled, 'invalid' file handles are stored + * to indicate that this entry has been copied up. + */ + if (!bytes || (int)fh->type == FILEID_INVALID) + return ERR_PTR(-ESTALE); + + /* + * Several layers can be on the same fs and decoded dentry may be in + * either one of those layers. We are looking for a match of dentry + * and mnt to find out to which layer the decoded dentry belongs to. + */ + return exportfs_decode_fh(mnt, (struct fid *)fh->fid, + bytes >> 2, (int)fh->type, + acceptable, mnt); +} + +static int ovl_acceptable(void *ctx, struct dentry *dentry) +{ + return 1; +} + +/* Lookup by file handle in a lower layer mounted at @mnt */ +static int ovl_lookup_layer_fh(struct vfsmount *mnt, struct ovl_lookup_data *d, + struct dentry **ret) +{ + struct dentry *this = ovl_decode_fh(mnt, d->fh, ovl_acceptable); + int err; + + if (IS_ERR(this)) { + err = PTR_ERR(this); + *ret = NULL; + if (err == -ESTALE) + return 0; + return err; + } + + /* If found by file handle - don't follow that handle again */ + ovl_reset_redirect_fh(d); + return ovl_lookup_data(this, d, 0, "", ret); +} + +/* Find a lower layer where file handle should be decoded */ +static int ovl_find_layer_by_fh(struct dentry *dentry, int idx, + struct ovl_fh *fh) +{ + struct super_block *same_sb = ovl_same_sb(dentry->d_sb); + + /* We only support redirect_fh when all layers are on the same fs */ + if (!same_sb) + return -1; + + /* + * Since all layers are on the same fs, we use the first layer for + * decoding the file handle. We may get a disconnected dentry, + * which is fine, because we only need to hold the origin inode in + * cache and use its inode number. We may even get a connected dentry, + * that is not under the first layer's root. That is also fine for + * using it's inode number - it's the same as if we held a reference + * to a dentry in first layer that was moved under us. + */ + return 0; +} + /* * Returns next layer in stack starting from top. * Returns -1 if this is the last layer. @@ -270,7 +404,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .stop = false, .last = !poe->numlower, .idx = 0, + .by_path = true, .redirect = NULL, + .by_fh = ofs->redirect_fh, + .fh = NULL, }; if (dentry->d_name.len > ofs->namelen) @@ -299,7 +436,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, upperopaque = d.opaque; } - if (!d.stop && poe->numlower) { + if (!d.stop && (poe->numlower || d.fh)) { err = -ENOMEM; stack = kcalloc(ofs->numlower, sizeof(struct path), GFP_TEMPORARY); @@ -307,6 +444,33 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out_put_upper; } + /* Lookup non-dir copy up origin by file handle */ + if (!d.stop && d.fh && !S_ISDIR(d.mode)) { + /* Find layer where file handle should be decoded */ + i = ovl_find_layer_by_fh(dentry, 0, d.fh); + if (i < 0 || i > roe->numlower) + goto alloc_entry; + + d.last = true; + d.by_path = false; + err = ovl_lookup_layer_fh(roe->lowerstack[i].mnt, &d, &this); + if (err) + goto out_put; + + if (!this) + goto alloc_entry; + + stack[ctr].dentry = this; + stack[ctr].mnt = roe->lowerstack[i].mnt; + ctr++; + + /* Looked up by fh - do not lookup also by path */ + goto alloc_entry; + } + + /* Lookup lower layers by path */ + d.by_path = true; + d.by_fh = false; for (i = 0; !d.stop && i < poe->numlower; i++) { struct path lowerpath = poe->lowerstack[i]; @@ -338,6 +502,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } } +alloc_entry: oe = ovl_alloc_entry(ctr); err = -ENOMEM; if (!oe) @@ -386,6 +551,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, dput(upperdentry); kfree(upperredirect); out: + kfree(d.fh); kfree(d.redirect); revert_creds(old_cred); return ERR_PTR(err); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index da37aaf..90181e16 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -194,6 +194,7 @@ const char *ovl_dentry_get_redirect(struct dentry *dentry); void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect); bool ovl_redirect_fh(struct super_block *sb); void ovl_clear_redirect_fh(struct super_block *sb); +bool ovl_redirect_fh_ok(const char *redirect, size_t size); void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); void ovl_inode_init(struct inode *inode, struct inode *realinode, bool is_upper); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 9db0588..08c55e6 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -236,6 +236,20 @@ void ovl_clear_redirect_fh(struct super_block *sb) ofs->redirect_fh = false; } +bool ovl_redirect_fh_ok(const char *redirect, size_t size) +{ + struct ovl_fh *fh = (void *)redirect; + + if (size < sizeof(struct ovl_fh) || size < fh->len) + return false; + + if (fh->version > OVL_FH_VERSION || + fh->magic != OVL_FH_MAGIC) + return false; + + return true; +} + void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) { struct ovl_entry *oe = dentry->d_fsdata;