From: Amir Goldstein <amir73il@gmail.com>
To: Vivek Goyal <vgoyal@redhat.com>
Cc: Miklos Szeredi <miklos@szeredi.hu>,
Giuseppe Scrivano <gscrivan@redhat.com>,
overlayfs <linux-unionfs@vger.kernel.org>
Subject: Re: [PATCH] ovl: check for incomapt features in work dir
Date: Sun, 30 Aug 2020 23:25:14 +0300 [thread overview]
Message-ID: <CAOQ4uxgXQmSWnij=3YCSuR+ictF2uR=Y_23aTcDH0gqP06S73Q@mail.gmail.com> (raw)
In-Reply-To: <20200830200647.19047-1-amir73il@gmail.com>
On Sun, Aug 30, 2020 at 11:06 PM Amir Goldstein <amir73il@gmail.com> wrote:
>
> An incompatible feature is marked by a non-empty directory nested
> 2 levels deep under "work" dir, e.g.:
> workdir/work/incompat/volatile.
>
> This commit checks for marked incompat features, warns about them
> and fails to mount the overlay, for example:
> overlayfs: overlay with incompat feature 'volatile' cannot be mounted
>
> Very old kernels (i.e. v3.18) will fail to remove a non-empty "work"
> dir and fail the mount. Newer kernels will fail to remove a "work"
> dir with entries nested 3 levels and fall back to read-only mount.
>
> User mounting with old kernel will see a warning like these in dmesg:
> overlayfs: cleanup of 'incompat/...' failed (-39)
> overlayfs: cleanup of 'work/incompat' failed (-39)
> overlayfs: cleanup of 'ovl-work/work' failed (-39)
> overlayfs: failed to create directory /vdf/ovl-work/work (errno: 17);
> mounting read-only
>
> These warnings should give the hint to the user that:
> 1. mount failure is caused by backward incompatible features
> 2. mount failure can be resolved by manually removing the "work" directory
>
> There is nothing preventing users on old kernels from manually removing
> workdir entirely or mounting overlay with a new workdir, so this is in
> no way a full proof backward compatibility enforcement, but only a best
> effort.
>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> ---
>
> Vivek,
>
> I dusted off some code from my old ovl-features branch.
> This should explain what I was trying to say on review of volatile (v6).
>
> There are also some helpers on that branch to create incompat feature dir,
> but they seemed too generic for your needs, so I left them out.
>
> What I was trying to explain is that with this change, the documentation
> for "volatile" should intstruct user to remove the incompat/volatile dir
> entirely, not just the dirty file, in order to reuse the overlay and I
> think that makes sense.
>
> To be clear, an empty work/incompat dir does not prevent neither old nor
> new kernel from mounting the overlay and an empty incompat/volatile dir
> does not prevent old kernel from mounting the overlay, which is why we
> still need to create the dirty file.
>
> Thanks,
> Amir.
>
>
> fs/overlayfs/overlayfs.h | 4 +++-
> fs/overlayfs/readdir.c | 31 +++++++++++++++++++++++++------
> fs/overlayfs/super.c | 25 ++++++++++++++++++-------
> 3 files changed, 46 insertions(+), 14 deletions(-)
>
> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> index 29bc1ec699e7..dc8ad8dc884a 100644
> --- a/fs/overlayfs/overlayfs.h
> +++ b/fs/overlayfs/overlayfs.h
> @@ -31,6 +31,8 @@ enum ovl_path_type {
> #define OVL_XATTR_UPPER OVL_XATTR_PREFIX "upper"
> #define OVL_XATTR_METACOPY OVL_XATTR_PREFIX "metacopy"
>
> +#define OVL_INCOMPATDIR_NAME "incompat"
> +
> enum ovl_inode_flag {
> /* Pure upper dir that may contain non pure upper entries */
> OVL_IMPURE,
> @@ -54,6 +56,7 @@ enum {
> OVL_XINO_ON,
> };
>
> +
> /*
> * The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
> * where:
> @@ -349,7 +352,6 @@ static inline void ovl_inode_unlock(struct inode *inode)
> mutex_unlock(&OVL_I(inode)->lock);
> }
>
> -
> /* namei.c */
> int ovl_check_fb_len(struct ovl_fb *fb, int fb_len);
>
> diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
> index 6918b98faeb6..8e4938b51814 100644
> --- a/fs/overlayfs/readdir.c
> +++ b/fs/overlayfs/readdir.c
> @@ -1051,7 +1051,8 @@ int ovl_check_d_type_supported(struct path *realpath)
> return rdd.d_type_supported;
> }
>
> -static void ovl_workdir_cleanup_recurse(struct path *path, int level)
> +static int ovl_workdir_cleanup_recurse(struct path *path, int level,
> + bool incompat)
> {
> int err;
> struct inode *dir = path->dentry->d_inode;
> @@ -1079,25 +1080,42 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
> continue;
> if (p->len == 2 && p->name[1] == '.')
> continue;
> + } else if (incompat) {
> + pr_warn("overlay with incompat feature '%.*s' cannot be mounted\n",
> + p->len, p->name);
> + err = -EEXIST;
> + break;
> }
> dentry = lookup_one_len(p->name, path->dentry, p->len);
> if (IS_ERR(dentry))
> continue;
> if (dentry->d_inode)
> - ovl_workdir_cleanup(dir, path->mnt, dentry, level);
> + err = ovl_workdir_cleanup(dir, path->mnt, dentry, level);
> dput(dentry);
> + if (err)
> + break;
> }
> inode_unlock(dir);
> out:
> ovl_cache_free(&list);
> + return err;
> }
>
> int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
> - struct dentry *dentry, int level)
> + struct dentry *dentry, int level)
> {
> + bool incompat = false;
> int err;
>
> - if (!d_is_dir(dentry) || level > 1) {
> + /*
> + * The "work/incompat" directory is treated specially - if it is not
> + * empty, instead of printing a generic error and mounting read-only,
> + * we will error about incompat features and fail the mount.
> + */
> + if (d_is_dir(dentry) && level == 1 &&
> + !strcmp(dentry->d_name.name, OVL_INCOMPATDIR_NAME)) {
> + incompat = true;
Self nit - move this test into ovl_workdir_cleanup_recurse()
Noticed after posting, will post v2.
> + } else if (!d_is_dir(dentry) || level > 1) {
> return ovl_cleanup(dir, dentry);
> }
>
> @@ -1106,9 +1124,10 @@ int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
> struct path path = { .mnt = mnt, .dentry = dentry };
>
> inode_unlock(dir);
> - ovl_workdir_cleanup_recurse(&path, level + 1);
> + err = ovl_workdir_cleanup_recurse(&path, level + 1, incompat);
> inode_lock_nested(dir, I_MUTEX_PARENT);
> - err = ovl_cleanup(dir, dentry);
> + if (!err)
> + err = ovl_cleanup(dir, dentry);
> }
>
> return err;
> diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
> index 4b38141c2985..3cd47e4b2eae 100644
> --- a/fs/overlayfs/super.c
> +++ b/fs/overlayfs/super.c
> @@ -705,8 +705,12 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
> goto out_unlock;
>
> retried = true;
> - ovl_workdir_cleanup(dir, mnt, work, 0);
> + err = ovl_workdir_cleanup(dir, mnt, work, 0);
> dput(work);
> + if (err == -EEXIST) {
> + work = ERR_PTR(err);
> + goto out_unlock;
> + }
> goto retry;
> }
>
> @@ -1203,7 +1207,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
> struct path *workpath)
> {
> struct vfsmount *mnt = ovl_upper_mnt(ofs);
> - struct dentry *temp;
> + struct dentry *temp, *workdir;
> bool rename_whiteout;
> bool d_type;
> int fh_type;
> @@ -1213,10 +1217,13 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
> if (err)
> return err;
>
> - ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
> - if (!ofs->workdir)
> + workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
> + err = PTR_ERR(workdir);
> + if (IS_ERR_OR_NULL(workdir))
> goto out;
>
> + ofs->workdir = workdir;
> +
> err = ovl_setup_trap(sb, ofs->workdir, &ofs->workdir_trap, "workdir");
> if (err)
> goto out;
> @@ -1347,6 +1354,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
> struct ovl_entry *oe, struct path *upperpath)
> {
> struct vfsmount *mnt = ovl_upper_mnt(ofs);
> + struct dentry *indexdir;
> int err;
>
> err = mnt_want_write(mnt);
> @@ -1366,9 +1374,12 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
> ofs->workdir_trap = NULL;
> dput(ofs->workdir);
> ofs->workdir = NULL;
> - ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
> - if (ofs->indexdir) {
> - ofs->workdir = dget(ofs->indexdir);
> + indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
> + if (IS_ERR(indexdir)) {
> + err = PTR_ERR(indexdir);
> + } else if (indexdir) {
> + ofs->indexdir = indexdir;
> + ofs->workdir = dget(indexdir);
>
> err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
> "indexdir");
> --
> 2.17.1
>
prev parent reply other threads:[~2020-08-30 20:25 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-08-30 20:06 [PATCH] ovl: check for incomapt features in work dir Amir Goldstein
2020-08-30 20:25 ` Amir Goldstein [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='CAOQ4uxgXQmSWnij=3YCSuR+ictF2uR=Y_23aTcDH0gqP06S73Q@mail.gmail.com' \
--to=amir73il@gmail.com \
--cc=gscrivan@redhat.com \
--cc=linux-unionfs@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=vgoyal@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).