diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-27 19:44:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-27 19:44:39 -0400 |
commit | 0121a32201dcc72933fb6019c41661e2f8a02fc5 (patch) | |
tree | 3e4944d4c1c7f1213413ac99f779629d67373c85 | |
parent | 559b6d90a0beb375c46dffe18133012bfa29f441 (diff) | |
parent | 21765194cecf2e4514ad75244df459f188140a0f (diff) |
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs update from Miklos Szeredi:
"The meat of this is a change to use the mounter's credentials for
operations that require elevated privileges (such as whiteout
creation). This fixes behavior under user namespaces as well as being
a nice cleanup"
* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
ovl: Do d_type check only if work dir creation was successful
ovl: update documentation
ovl: override creds with the ones from the superblock mounter
-rw-r--r-- | Documentation/filesystems/overlayfs.txt | 9 | ||||
-rw-r--r-- | fs/overlayfs/copy_up.c | 26 | ||||
-rw-r--r-- | fs/overlayfs/dir.c | 67 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 1 | ||||
-rw-r--r-- | fs/overlayfs/readdir.c | 14 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 37 |
6 files changed, 38 insertions, 116 deletions
diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.txt index 28091457b71a..d6259c786316 100644 --- a/Documentation/filesystems/overlayfs.txt +++ b/Documentation/filesystems/overlayfs.txt | |||
@@ -194,15 +194,6 @@ If a file with multiple hard links is copied up, then this will | |||
194 | "break" the link. Changes will not be propagated to other names | 194 | "break" the link. Changes will not be propagated to other names |
195 | referring to the same inode. | 195 | referring to the same inode. |
196 | 196 | ||
197 | Symlinks in /proc/PID/ and /proc/PID/fd which point to a non-directory | ||
198 | object in overlayfs will not contain valid absolute paths, only | ||
199 | relative paths leading up to the filesystem's root. This will be | ||
200 | fixed in the future. | ||
201 | |||
202 | Some operations are not atomic, for example a crash during copy_up or | ||
203 | rename will leave the filesystem in an inconsistent state. This will | ||
204 | be addressed in the future. | ||
205 | |||
206 | Changes to underlying filesystems | 197 | Changes to underlying filesystems |
207 | --------------------------------- | 198 | --------------------------------- |
208 | 199 | ||
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index cc514da6f3e7..80aa6f1eb336 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c | |||
@@ -336,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, | |||
336 | struct dentry *upperdir; | 336 | struct dentry *upperdir; |
337 | struct dentry *upperdentry; | 337 | struct dentry *upperdentry; |
338 | const struct cred *old_cred; | 338 | const struct cred *old_cred; |
339 | struct cred *override_cred; | ||
340 | char *link = NULL; | 339 | char *link = NULL; |
341 | 340 | ||
342 | if (WARN_ON(!workdir)) | 341 | if (WARN_ON(!workdir)) |
@@ -357,28 +356,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, | |||
357 | return PTR_ERR(link); | 356 | return PTR_ERR(link); |
358 | } | 357 | } |
359 | 358 | ||
360 | err = -ENOMEM; | 359 | old_cred = ovl_override_creds(dentry->d_sb); |
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); | ||
382 | 360 | ||
383 | err = -EIO; | 361 | err = -EIO; |
384 | if (lock_rename(workdir, upperdir) != NULL) { | 362 | if (lock_rename(workdir, upperdir) != NULL) { |
@@ -401,9 +379,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, | |||
401 | out_unlock: | 379 | out_unlock: |
402 | unlock_rename(workdir, upperdir); | 380 | unlock_rename(workdir, upperdir); |
403 | revert_creds(old_cred); | 381 | revert_creds(old_cred); |
404 | put_cred(override_cred); | ||
405 | 382 | ||
406 | out_free_link: | ||
407 | if (link) | 383 | if (link) |
408 | free_page((unsigned long) link); | 384 | free_page((unsigned long) link); |
409 | 385 | ||
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b3fc0a35bf62..22f0253a3567 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
@@ -405,28 +405,13 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, | |||
405 | err = ovl_create_upper(dentry, inode, &stat, link, hardlink); | 405 | err = ovl_create_upper(dentry, inode, &stat, link, hardlink); |
406 | } else { | 406 | } else { |
407 | const struct cred *old_cred; | 407 | const struct cred *old_cred; |
408 | struct cred *override_cred; | ||
409 | 408 | ||
410 | err = -ENOMEM; | 409 | old_cred = ovl_override_creds(dentry->d_sb); |
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); | ||
424 | 410 | ||
425 | err = ovl_create_over_whiteout(dentry, inode, &stat, link, | 411 | err = ovl_create_over_whiteout(dentry, inode, &stat, link, |
426 | hardlink); | 412 | hardlink); |
427 | 413 | ||
428 | revert_creds(old_cred); | 414 | revert_creds(old_cred); |
429 | put_cred(override_cred); | ||
430 | } | 415 | } |
431 | 416 | ||
432 | if (!err) | 417 | if (!err) |
@@ -662,32 +647,11 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) | |||
662 | if (OVL_TYPE_PURE_UPPER(type)) { | 647 | if (OVL_TYPE_PURE_UPPER(type)) { |
663 | err = ovl_remove_upper(dentry, is_dir); | 648 | err = ovl_remove_upper(dentry, is_dir); |
664 | } else { | 649 | } else { |
665 | const struct cred *old_cred; | 650 | const struct cred *old_cred = ovl_override_creds(dentry->d_sb); |
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); | ||
686 | 651 | ||
687 | err = ovl_remove_and_whiteout(dentry, is_dir); | 652 | err = ovl_remove_and_whiteout(dentry, is_dir); |
688 | 653 | ||
689 | revert_creds(old_cred); | 654 | revert_creds(old_cred); |
690 | put_cred(override_cred); | ||
691 | } | 655 | } |
692 | out_drop_write: | 656 | out_drop_write: |
693 | ovl_drop_write(dentry); | 657 | ovl_drop_write(dentry); |
@@ -725,7 +689,6 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, | |||
725 | bool new_is_dir = false; | 689 | bool new_is_dir = false; |
726 | struct dentry *opaquedir = NULL; | 690 | struct dentry *opaquedir = NULL; |
727 | const struct cred *old_cred = NULL; | 691 | const struct cred *old_cred = NULL; |
728 | struct cred *override_cred = NULL; | ||
729 | 692 | ||
730 | err = -EINVAL; | 693 | err = -EINVAL; |
731 | if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) | 694 | if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) |
@@ -794,26 +757,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, | |||
794 | old_opaque = !OVL_TYPE_PURE_UPPER(old_type); | 757 | old_opaque = !OVL_TYPE_PURE_UPPER(old_type); |
795 | new_opaque = !OVL_TYPE_PURE_UPPER(new_type); | 758 | new_opaque = !OVL_TYPE_PURE_UPPER(new_type); |
796 | 759 | ||
797 | if (old_opaque || new_opaque) { | 760 | if (old_opaque || new_opaque) |
798 | err = -ENOMEM; | 761 | old_cred = ovl_override_creds(old->d_sb); |
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 | } | ||
817 | 762 | ||
818 | if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) { | 763 | if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) { |
819 | opaquedir = ovl_check_empty_and_clear(new); | 764 | opaquedir = ovl_check_empty_and_clear(new); |
@@ -943,10 +888,8 @@ out_dput_old: | |||
943 | out_unlock: | 888 | out_unlock: |
944 | unlock_rename(new_upperdir, old_upperdir); | 889 | unlock_rename(new_upperdir, old_upperdir); |
945 | out_revert_creds: | 890 | out_revert_creds: |
946 | if (old_opaque || new_opaque) { | 891 | if (old_opaque || new_opaque) |
947 | revert_creds(old_cred); | 892 | revert_creds(old_cred); |
948 | put_cred(override_cred); | ||
949 | } | ||
950 | out_drop_write: | 893 | out_drop_write: |
951 | ovl_drop_write(old); | 894 | ovl_drop_write(old); |
952 | out: | 895 | out: |
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 99ec4b035237..724f5fcb4e24 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h | |||
@@ -153,6 +153,7 @@ void ovl_drop_write(struct dentry *dentry); | |||
153 | bool ovl_dentry_is_opaque(struct dentry *dentry); | 153 | bool ovl_dentry_is_opaque(struct dentry *dentry); |
154 | void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); | 154 | void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); |
155 | bool ovl_is_whiteout(struct dentry *dentry); | 155 | bool ovl_is_whiteout(struct dentry *dentry); |
156 | const struct cred *ovl_override_creds(struct super_block *sb); | ||
156 | void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); | 157 | void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); |
157 | struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | 158 | struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, |
158 | unsigned int flags); | 159 | unsigned int flags); |
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index da186ee4f846..d11ae826bcbc 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c | |||
@@ -36,6 +36,7 @@ struct ovl_dir_cache { | |||
36 | 36 | ||
37 | struct ovl_readdir_data { | 37 | struct ovl_readdir_data { |
38 | struct dir_context ctx; | 38 | struct dir_context ctx; |
39 | struct dentry *dentry; | ||
39 | bool is_lowest; | 40 | bool is_lowest; |
40 | struct rb_root root; | 41 | struct rb_root root; |
41 | struct list_head *list; | 42 | struct list_head *list; |
@@ -206,17 +207,8 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd) | |||
206 | struct ovl_cache_entry *p; | 207 | struct ovl_cache_entry *p; |
207 | struct dentry *dentry; | 208 | struct dentry *dentry; |
208 | const struct cred *old_cred; | 209 | const struct cred *old_cred; |
209 | struct cred *override_cred; | ||
210 | |||
211 | override_cred = prepare_creds(); | ||
212 | if (!override_cred) | ||
213 | return -ENOMEM; | ||
214 | 210 | ||
215 | /* | 211 | old_cred = ovl_override_creds(rdd->dentry->d_sb); |
216 | * CAP_DAC_OVERRIDE for lookup | ||
217 | */ | ||
218 | cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); | ||
219 | old_cred = override_creds(override_cred); | ||
220 | 212 | ||
221 | inode_lock(dir->d_inode); | 213 | inode_lock(dir->d_inode); |
222 | err = 0; | 214 | err = 0; |
@@ -234,7 +226,6 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd) | |||
234 | inode_unlock(dir->d_inode); | 226 | inode_unlock(dir->d_inode); |
235 | } | 227 | } |
236 | revert_creds(old_cred); | 228 | revert_creds(old_cred); |
237 | put_cred(override_cred); | ||
238 | 229 | ||
239 | return err; | 230 | return err; |
240 | } | 231 | } |
@@ -290,6 +281,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) | |||
290 | struct path realpath; | 281 | struct path realpath; |
291 | struct ovl_readdir_data rdd = { | 282 | struct ovl_readdir_data rdd = { |
292 | .ctx.actor = ovl_fill_merge, | 283 | .ctx.actor = ovl_fill_merge, |
284 | .dentry = dentry, | ||
293 | .list = list, | 285 | .list = list, |
294 | .root = RB_ROOT, | 286 | .root = RB_ROOT, |
295 | .is_lowest = false, | 287 | .is_lowest = false, |
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index ed53ae0fe868..ce02f46029da 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
@@ -42,6 +42,8 @@ struct ovl_fs { | |||
42 | long lower_namelen; | 42 | long lower_namelen; |
43 | /* pathnames of lower and upper dirs, for show_options */ | 43 | /* pathnames of lower and upper dirs, for show_options */ |
44 | struct ovl_config config; | 44 | struct ovl_config config; |
45 | /* creds of process who forced instantiation of super block */ | ||
46 | const struct cred *creator_cred; | ||
45 | }; | 47 | }; |
46 | 48 | ||
47 | struct ovl_dir_cache; | 49 | struct ovl_dir_cache; |
@@ -265,6 +267,13 @@ bool ovl_is_whiteout(struct dentry *dentry) | |||
265 | return inode && IS_WHITEOUT(inode); | 267 | return inode && IS_WHITEOUT(inode); |
266 | } | 268 | } |
267 | 269 | ||
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 | |||
268 | static bool ovl_is_opaquedir(struct dentry *dentry) | 277 | static bool ovl_is_opaquedir(struct dentry *dentry) |
269 | { | 278 | { |
270 | int res; | 279 | int res; |
@@ -603,6 +612,7 @@ static void ovl_put_super(struct super_block *sb) | |||
603 | kfree(ufs->config.lowerdir); | 612 | kfree(ufs->config.lowerdir); |
604 | kfree(ufs->config.upperdir); | 613 | kfree(ufs->config.upperdir); |
605 | kfree(ufs->config.workdir); | 614 | kfree(ufs->config.workdir); |
615 | put_cred(ufs->creator_cred); | ||
606 | kfree(ufs); | 616 | kfree(ufs); |
607 | } | 617 | } |
608 | 618 | ||
@@ -1064,16 +1074,19 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1064 | /* | 1074 | /* |
1065 | * Upper should support d_type, else whiteouts are visible. | 1075 | * Upper should support d_type, else whiteouts are visible. |
1066 | * Given workdir and upper are on same fs, we can do | 1076 | * Given workdir and upper are on same fs, we can do |
1067 | * iterate_dir() on workdir. | 1077 | * iterate_dir() on workdir. This check requires successful |
1078 | * creation of workdir in previous step. | ||
1068 | */ | 1079 | */ |
1069 | err = ovl_check_d_type_supported(&workpath); | 1080 | if (ufs->workdir) { |
1070 | if (err < 0) | 1081 | err = ovl_check_d_type_supported(&workpath); |
1071 | goto out_put_workdir; | 1082 | if (err < 0) |
1083 | goto out_put_workdir; | ||
1072 | 1084 | ||
1073 | if (!err) { | 1085 | if (!err) { |
1074 | pr_err("overlayfs: upper fs needs to support d_type.\n"); | 1086 | pr_err("overlayfs: upper fs needs to support d_type.\n"); |
1075 | err = -EINVAL; | 1087 | err = -EINVAL; |
1076 | goto out_put_workdir; | 1088 | goto out_put_workdir; |
1089 | } | ||
1077 | } | 1090 | } |
1078 | } | 1091 | } |
1079 | 1092 | ||
@@ -1108,10 +1121,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1108 | else | 1121 | else |
1109 | sb->s_d_op = &ovl_dentry_operations; | 1122 | sb->s_d_op = &ovl_dentry_operations; |
1110 | 1123 | ||
1124 | ufs->creator_cred = prepare_creds(); | ||
1125 | if (!ufs->creator_cred) | ||
1126 | goto out_put_lower_mnt; | ||
1127 | |||
1111 | err = -ENOMEM; | 1128 | err = -ENOMEM; |
1112 | oe = ovl_alloc_entry(numlower); | 1129 | oe = ovl_alloc_entry(numlower); |
1113 | if (!oe) | 1130 | if (!oe) |
1114 | goto out_put_lower_mnt; | 1131 | goto out_put_cred; |
1115 | 1132 | ||
1116 | root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); | 1133 | root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); |
1117 | if (!root_dentry) | 1134 | if (!root_dentry) |
@@ -1144,6 +1161,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1144 | 1161 | ||
1145 | out_free_oe: | 1162 | out_free_oe: |
1146 | kfree(oe); | 1163 | kfree(oe); |
1164 | out_put_cred: | ||
1165 | put_cred(ufs->creator_cred); | ||
1147 | out_put_lower_mnt: | 1166 | out_put_lower_mnt: |
1148 | for (i = 0; i < ufs->numlower; i++) | 1167 | for (i = 0; i < ufs->numlower; i++) |
1149 | mntput(ufs->lower_mnt[i]); | 1168 | mntput(ufs->lower_mnt[i]); |