diff options
author | Amir Goldstein <amir73il@gmail.com> | 2020-08-30 23:28:03 +0300 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2020-09-02 10:58:48 +0200 |
commit | 235ce9ed96bc624fe929f724883cc04b7a714ac2 (patch) | |
tree | 036151f9e6fdc153871ef0e2c70c47e5ef526207 /fs/overlayfs/super.c | |
parent | f75aef392f869018f78cfedf3c320a6b3fcfda6b (diff) |
ovl: check for incompatible features in work dir
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>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/overlayfs/super.c')
-rw-r--r-- | fs/overlayfs/super.c | 25 |
1 files changed, 18 insertions, 7 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 4b38141c2985..f82defec02c1 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -705,8 +705,12 @@ retry: goto out_unlock; retried = true; - ovl_workdir_cleanup(dir, mnt, work, 0); + err = ovl_workdir_cleanup(dir, mnt, work, 0); dput(work); + if (err == -EINVAL) { + 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"); |