Skip to content

Commit 3fe6e52

Browse files
runcomMiklos Szeredi
authored andcommitted
ovl: override creds with the ones from the superblock mounter
In user namespace the whiteout creation fails with -EPERM because the current process isn't capable(CAP_SYS_ADMIN) when setting xattr. A simple reproducer: $ mkdir upper lower work merged lower/dir $ sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged $ unshare -m -p -f -U -r bash Now as root in the user namespace: \# touch merged/dir/{1,2,3} # this will force a copy up of lower/dir \# rm -fR merged/* This ends up failing with -EPERM after the files in dir has been correctly deleted: unlinkat(4, "2", 0) = 0 unlinkat(4, "1", 0) = 0 unlinkat(4, "3", 0) = 0 close(4) = 0 unlinkat(AT_FDCWD, "merged/dir", AT_REMOVEDIR) = -1 EPERM (Operation not permitted) Interestingly, if you don't place files in merged/dir you can remove it, meaning if upper/dir does not exist, creating the char device file works properly in that same location. This patch uses ovl_sb_creator_cred() to get the cred struct from the superblock mounter and override the old cred with these new ones so that the whiteout creation is possible because overlay is wrong in assuming that the creds it will get with prepare_creds will be in the initial user namespace. The old cap_raise game is removed in favor of just overriding the old cred struct. This patch also drops from ovl_copy_up_one() the following two lines: override_cred->fsuid = stat->uid; override_cred->fsgid = stat->gid; This is because the correct uid and gid are taken directly with the stat struct and correctly set with ovl_set_attr(). Signed-off-by: Antonio Murdaca <runcom@redhat.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 2dcd0af commit 3fe6e52

File tree

5 files changed

+27
-99
lines changed

5 files changed

+27
-99
lines changed

fs/overlayfs/copy_up.c

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
336336
struct dentry *upperdir;
337337
struct dentry *upperdentry;
338338
const struct cred *old_cred;
339-
struct cred *override_cred;
340339
char *link = NULL;
341340

342341
if (WARN_ON(!workdir))
@@ -357,28 +356,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
357356
return PTR_ERR(link);
358357
}
359358

360-
err = -ENOMEM;
361-
override_cred = prepare_creds();
362-
if (!override_cred)
363-
goto out_free_link;
364-
365-
override_cred->fsuid = stat->uid;
366-
override_cred->fsgid = stat->gid;
367-
/*
368-
* CAP_SYS_ADMIN for copying up extended attributes
369-
* CAP_DAC_OVERRIDE for create
370-
* CAP_FOWNER for chmod, timestamp update
371-
* CAP_FSETID for chmod
372-
* CAP_CHOWN for chown
373-
* CAP_MKNOD for mknod
374-
*/
375-
cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
376-
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
377-
cap_raise(override_cred->cap_effective, CAP_FOWNER);
378-
cap_raise(override_cred->cap_effective, CAP_FSETID);
379-
cap_raise(override_cred->cap_effective, CAP_CHOWN);
380-
cap_raise(override_cred->cap_effective, CAP_MKNOD);
381-
old_cred = override_creds(override_cred);
359+
old_cred = ovl_override_creds(dentry->d_sb);
382360

383361
err = -EIO;
384362
if (lock_rename(workdir, upperdir) != NULL) {
@@ -401,9 +379,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
401379
out_unlock:
402380
unlock_rename(workdir, upperdir);
403381
revert_creds(old_cred);
404-
put_cred(override_cred);
405382

406-
out_free_link:
407383
if (link)
408384
free_page((unsigned long) link);
409385

fs/overlayfs/dir.c

Lines changed: 5 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -405,28 +405,13 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
405405
err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
406406
} else {
407407
const struct cred *old_cred;
408-
struct cred *override_cred;
409408

410-
err = -ENOMEM;
411-
override_cred = prepare_creds();
412-
if (!override_cred)
413-
goto out_iput;
414-
415-
/*
416-
* CAP_SYS_ADMIN for setting opaque xattr
417-
* CAP_DAC_OVERRIDE for create in workdir, rename
418-
* CAP_FOWNER for removing whiteout from sticky dir
419-
*/
420-
cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
421-
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
422-
cap_raise(override_cred->cap_effective, CAP_FOWNER);
423-
old_cred = override_creds(override_cred);
409+
old_cred = ovl_override_creds(dentry->d_sb);
424410

425411
err = ovl_create_over_whiteout(dentry, inode, &stat, link,
426412
hardlink);
427413

428414
revert_creds(old_cred);
429-
put_cred(override_cred);
430415
}
431416

432417
if (!err)
@@ -662,32 +647,11 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
662647
if (OVL_TYPE_PURE_UPPER(type)) {
663648
err = ovl_remove_upper(dentry, is_dir);
664649
} else {
665-
const struct cred *old_cred;
666-
struct cred *override_cred;
667-
668-
err = -ENOMEM;
669-
override_cred = prepare_creds();
670-
if (!override_cred)
671-
goto out_drop_write;
672-
673-
/*
674-
* CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
675-
* CAP_DAC_OVERRIDE for create in workdir, rename
676-
* CAP_FOWNER for removing whiteout from sticky dir
677-
* CAP_FSETID for chmod of opaque dir
678-
* CAP_CHOWN for chown of opaque dir
679-
*/
680-
cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
681-
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
682-
cap_raise(override_cred->cap_effective, CAP_FOWNER);
683-
cap_raise(override_cred->cap_effective, CAP_FSETID);
684-
cap_raise(override_cred->cap_effective, CAP_CHOWN);
685-
old_cred = override_creds(override_cred);
650+
const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
686651

687652
err = ovl_remove_and_whiteout(dentry, is_dir);
688653

689654
revert_creds(old_cred);
690-
put_cred(override_cred);
691655
}
692656
out_drop_write:
693657
ovl_drop_write(dentry);
@@ -725,7 +689,6 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
725689
bool new_is_dir = false;
726690
struct dentry *opaquedir = NULL;
727691
const struct cred *old_cred = NULL;
728-
struct cred *override_cred = NULL;
729692

730693
err = -EINVAL;
731694
if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
@@ -794,26 +757,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
794757
old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
795758
new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
796759

797-
if (old_opaque || new_opaque) {
798-
err = -ENOMEM;
799-
override_cred = prepare_creds();
800-
if (!override_cred)
801-
goto out_drop_write;
802-
803-
/*
804-
* CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
805-
* CAP_DAC_OVERRIDE for create in workdir
806-
* CAP_FOWNER for removing whiteout from sticky dir
807-
* CAP_FSETID for chmod of opaque dir
808-
* CAP_CHOWN for chown of opaque dir
809-
*/
810-
cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
811-
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
812-
cap_raise(override_cred->cap_effective, CAP_FOWNER);
813-
cap_raise(override_cred->cap_effective, CAP_FSETID);
814-
cap_raise(override_cred->cap_effective, CAP_CHOWN);
815-
old_cred = override_creds(override_cred);
816-
}
760+
if (old_opaque || new_opaque)
761+
old_cred = ovl_override_creds(old->d_sb);
817762

818763
if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
819764
opaquedir = ovl_check_empty_and_clear(new);
@@ -943,10 +888,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
943888
out_unlock:
944889
unlock_rename(new_upperdir, old_upperdir);
945890
out_revert_creds:
946-
if (old_opaque || new_opaque) {
891+
if (old_opaque || new_opaque)
947892
revert_creds(old_cred);
948-
put_cred(override_cred);
949-
}
950893
out_drop_write:
951894
ovl_drop_write(old);
952895
out:

fs/overlayfs/overlayfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ void ovl_drop_write(struct dentry *dentry);
153153
bool ovl_dentry_is_opaque(struct dentry *dentry);
154154
void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
155155
bool ovl_is_whiteout(struct dentry *dentry);
156+
const struct cred *ovl_override_creds(struct super_block *sb);
156157
void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
157158
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
158159
unsigned int flags);

fs/overlayfs/readdir.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct ovl_dir_cache {
3636

3737
struct ovl_readdir_data {
3838
struct dir_context ctx;
39+
struct dentry *dentry;
3940
bool is_lowest;
4041
struct rb_root root;
4142
struct list_head *list;
@@ -206,17 +207,8 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
206207
struct ovl_cache_entry *p;
207208
struct dentry *dentry;
208209
const struct cred *old_cred;
209-
struct cred *override_cred;
210-
211-
override_cred = prepare_creds();
212-
if (!override_cred)
213-
return -ENOMEM;
214210

215-
/*
216-
* CAP_DAC_OVERRIDE for lookup
217-
*/
218-
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
219-
old_cred = override_creds(override_cred);
211+
old_cred = ovl_override_creds(rdd->dentry->d_sb);
220212

221213
err = mutex_lock_killable(&dir->d_inode->i_mutex);
222214
if (!err) {
@@ -232,7 +224,6 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
232224
inode_unlock(dir->d_inode);
233225
}
234226
revert_creds(old_cred);
235-
put_cred(override_cred);
236227

237228
return err;
238229
}
@@ -288,6 +279,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
288279
struct path realpath;
289280
struct ovl_readdir_data rdd = {
290281
.ctx.actor = ovl_fill_merge,
282+
.dentry = dentry,
291283
.list = list,
292284
.root = RB_ROOT,
293285
.is_lowest = false,

fs/overlayfs/super.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ struct ovl_fs {
4242
long lower_namelen;
4343
/* pathnames of lower and upper dirs, for show_options */
4444
struct ovl_config config;
45+
/* creds of process who forced instantiation of super block */
46+
const struct cred *creator_cred;
4547
};
4648

4749
struct ovl_dir_cache;
@@ -265,6 +267,13 @@ bool ovl_is_whiteout(struct dentry *dentry)
265267
return inode && IS_WHITEOUT(inode);
266268
}
267269

270+
const struct cred *ovl_override_creds(struct super_block *sb)
271+
{
272+
struct ovl_fs *ofs = sb->s_fs_info;
273+
274+
return override_creds(ofs->creator_cred);
275+
}
276+
268277
static bool ovl_is_opaquedir(struct dentry *dentry)
269278
{
270279
int res;
@@ -603,6 +612,7 @@ static void ovl_put_super(struct super_block *sb)
603612
kfree(ufs->config.lowerdir);
604613
kfree(ufs->config.upperdir);
605614
kfree(ufs->config.workdir);
615+
put_cred(ufs->creator_cred);
606616
kfree(ufs);
607617
}
608618

@@ -1108,10 +1118,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
11081118
else
11091119
sb->s_d_op = &ovl_dentry_operations;
11101120

1121+
ufs->creator_cred = prepare_creds();
1122+
if (!ufs->creator_cred)
1123+
goto out_put_lower_mnt;
1124+
11111125
err = -ENOMEM;
11121126
oe = ovl_alloc_entry(numlower);
11131127
if (!oe)
1114-
goto out_put_lower_mnt;
1128+
goto out_put_cred;
11151129

11161130
root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
11171131
if (!root_dentry)
@@ -1144,6 +1158,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
11441158

11451159
out_free_oe:
11461160
kfree(oe);
1161+
out_put_cred:
1162+
put_cred(ufs->creator_cred);
11471163
out_put_lower_mnt:
11481164
for (i = 0; i < ufs->numlower; i++)
11491165
mntput(ufs->lower_mnt[i]);

0 commit comments

Comments
 (0)