Skip to content

Commit 235ce9e

Browse files
amir73ilMiklos Szeredi
authored andcommitted
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>
1 parent f75aef3 commit 235ce9e

2 files changed

Lines changed: 46 additions & 11 deletions

File tree

fs/overlayfs/readdir.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,9 @@ int ovl_check_d_type_supported(struct path *realpath)
10511051
return rdd.d_type_supported;
10521052
}
10531053

1054-
static void ovl_workdir_cleanup_recurse(struct path *path, int level)
1054+
#define OVL_INCOMPATDIR_NAME "incompat"
1055+
1056+
static int ovl_workdir_cleanup_recurse(struct path *path, int level)
10551057
{
10561058
int err;
10571059
struct inode *dir = path->dentry->d_inode;
@@ -1065,6 +1067,19 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
10651067
.root = &root,
10661068
.is_lowest = false,
10671069
};
1070+
bool incompat = false;
1071+
1072+
/*
1073+
* The "work/incompat" directory is treated specially - if it is not
1074+
* empty, instead of printing a generic error and mounting read-only,
1075+
* we will error about incompat features and fail the mount.
1076+
*
1077+
* When called from ovl_indexdir_cleanup(), path->dentry->d_name.name
1078+
* starts with '#'.
1079+
*/
1080+
if (level == 2 &&
1081+
!strcmp(path->dentry->d_name.name, OVL_INCOMPATDIR_NAME))
1082+
incompat = true;
10681083

10691084
err = ovl_dir_read(path, &rdd);
10701085
if (err)
@@ -1079,17 +1094,25 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level)
10791094
continue;
10801095
if (p->len == 2 && p->name[1] == '.')
10811096
continue;
1097+
} else if (incompat) {
1098+
pr_err("overlay with incompat feature '%s' cannot be mounted\n",
1099+
p->name);
1100+
err = -EINVAL;
1101+
break;
10821102
}
10831103
dentry = lookup_one_len(p->name, path->dentry, p->len);
10841104
if (IS_ERR(dentry))
10851105
continue;
10861106
if (dentry->d_inode)
1087-
ovl_workdir_cleanup(dir, path->mnt, dentry, level);
1107+
err = ovl_workdir_cleanup(dir, path->mnt, dentry, level);
10881108
dput(dentry);
1109+
if (err)
1110+
break;
10891111
}
10901112
inode_unlock(dir);
10911113
out:
10921114
ovl_cache_free(&list);
1115+
return err;
10931116
}
10941117

10951118
int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
@@ -1106,9 +1129,10 @@ int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt,
11061129
struct path path = { .mnt = mnt, .dentry = dentry };
11071130

11081131
inode_unlock(dir);
1109-
ovl_workdir_cleanup_recurse(&path, level + 1);
1132+
err = ovl_workdir_cleanup_recurse(&path, level + 1);
11101133
inode_lock_nested(dir, I_MUTEX_PARENT);
1111-
err = ovl_cleanup(dir, dentry);
1134+
if (!err)
1135+
err = ovl_cleanup(dir, dentry);
11121136
}
11131137

11141138
return err;

fs/overlayfs/super.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -705,8 +705,12 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
705705
goto out_unlock;
706706

707707
retried = true;
708-
ovl_workdir_cleanup(dir, mnt, work, 0);
708+
err = ovl_workdir_cleanup(dir, mnt, work, 0);
709709
dput(work);
710+
if (err == -EINVAL) {
711+
work = ERR_PTR(err);
712+
goto out_unlock;
713+
}
710714
goto retry;
711715
}
712716

@@ -1203,7 +1207,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
12031207
struct path *workpath)
12041208
{
12051209
struct vfsmount *mnt = ovl_upper_mnt(ofs);
1206-
struct dentry *temp;
1210+
struct dentry *temp, *workdir;
12071211
bool rename_whiteout;
12081212
bool d_type;
12091213
int fh_type;
@@ -1213,10 +1217,13 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
12131217
if (err)
12141218
return err;
12151219

1216-
ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
1217-
if (!ofs->workdir)
1220+
workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
1221+
err = PTR_ERR(workdir);
1222+
if (IS_ERR_OR_NULL(workdir))
12181223
goto out;
12191224

1225+
ofs->workdir = workdir;
1226+
12201227
err = ovl_setup_trap(sb, ofs->workdir, &ofs->workdir_trap, "workdir");
12211228
if (err)
12221229
goto out;
@@ -1347,6 +1354,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
13471354
struct ovl_entry *oe, struct path *upperpath)
13481355
{
13491356
struct vfsmount *mnt = ovl_upper_mnt(ofs);
1357+
struct dentry *indexdir;
13501358
int err;
13511359

13521360
err = mnt_want_write(mnt);
@@ -1366,9 +1374,12 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
13661374
ofs->workdir_trap = NULL;
13671375
dput(ofs->workdir);
13681376
ofs->workdir = NULL;
1369-
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
1370-
if (ofs->indexdir) {
1371-
ofs->workdir = dget(ofs->indexdir);
1377+
indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
1378+
if (IS_ERR(indexdir)) {
1379+
err = PTR_ERR(indexdir);
1380+
} else if (indexdir) {
1381+
ofs->indexdir = indexdir;
1382+
ofs->workdir = dget(indexdir);
13721383

13731384
err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
13741385
"indexdir");

0 commit comments

Comments
 (0)