diff options
| author | Miklos Szeredi <mszeredi@suse.cz> | 2014-12-12 18:59:43 -0500 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-12-12 18:59:43 -0500 |
| commit | dd662667e6d3e55b42798a6e6e7f37dddc639460 (patch) | |
| tree | 3e82fc75b4d29e4bf09362c8c55b90e4ddcf23f7 /fs/overlayfs | |
| parent | 263b4a0fee43f1239c4d6f3c3a62fb5a20d84f2e (diff) | |
ovl: add mutli-layer infrastructure
Add multiple lower layers to 'struct ovl_fs' and 'struct ovl_entry'.
ovl_entry will have an array of paths, instead of just the dentry. This
allows a compact array containing just the layers which exist at current
point in the tree (which is expected to be a small number for the majority
of dentries).
The number of layers is not limited by this infrastructure.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/overlayfs')
| -rw-r--r-- | fs/overlayfs/super.c | 98 |
1 files changed, 64 insertions, 34 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 821719cc8537..460d866b97a2 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
| @@ -35,7 +35,8 @@ struct ovl_config { | |||
| 35 | /* private information held for overlayfs's superblock */ | 35 | /* private information held for overlayfs's superblock */ |
| 36 | struct ovl_fs { | 36 | struct ovl_fs { |
| 37 | struct vfsmount *upper_mnt; | 37 | struct vfsmount *upper_mnt; |
| 38 | struct vfsmount *lower_mnt; | 38 | unsigned numlower; |
| 39 | struct vfsmount **lower_mnt; | ||
| 39 | struct dentry *workdir; | 40 | struct dentry *workdir; |
| 40 | long lower_namelen; | 41 | long lower_namelen; |
| 41 | /* pathnames of lower and upper dirs, for show_options */ | 42 | /* pathnames of lower and upper dirs, for show_options */ |
| @@ -47,7 +48,6 @@ struct ovl_dir_cache; | |||
| 47 | /* private information held for every overlayfs dentry */ | 48 | /* private information held for every overlayfs dentry */ |
| 48 | struct ovl_entry { | 49 | struct ovl_entry { |
| 49 | struct dentry *__upperdentry; | 50 | struct dentry *__upperdentry; |
| 50 | struct dentry *lowerdentry; | ||
| 51 | struct ovl_dir_cache *cache; | 51 | struct ovl_dir_cache *cache; |
| 52 | union { | 52 | union { |
| 53 | struct { | 53 | struct { |
| @@ -56,10 +56,16 @@ struct ovl_entry { | |||
| 56 | }; | 56 | }; |
| 57 | struct rcu_head rcu; | 57 | struct rcu_head rcu; |
| 58 | }; | 58 | }; |
| 59 | unsigned numlower; | ||
| 60 | struct path lowerstack[]; | ||
| 59 | }; | 61 | }; |
| 60 | 62 | ||
| 61 | const char *ovl_opaque_xattr = "trusted.overlay.opaque"; | 63 | const char *ovl_opaque_xattr = "trusted.overlay.opaque"; |
| 62 | 64 | ||
| 65 | static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe) | ||
| 66 | { | ||
| 67 | return oe->numlower ? oe->lowerstack[0].dentry : NULL; | ||
| 68 | } | ||
| 63 | 69 | ||
| 64 | enum ovl_path_type ovl_path_type(struct dentry *dentry) | 70 | enum ovl_path_type ovl_path_type(struct dentry *dentry) |
| 65 | { | 71 | { |
| @@ -69,7 +75,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry) | |||
| 69 | if (oe->__upperdentry) { | 75 | if (oe->__upperdentry) { |
| 70 | type = __OVL_PATH_UPPER; | 76 | type = __OVL_PATH_UPPER; |
| 71 | 77 | ||
| 72 | if (oe->lowerdentry) { | 78 | if (oe->numlower) { |
| 73 | if (S_ISDIR(dentry->d_inode->i_mode)) | 79 | if (S_ISDIR(dentry->d_inode->i_mode)) |
| 74 | type |= __OVL_PATH_MERGE; | 80 | type |= __OVL_PATH_MERGE; |
| 75 | } else if (!oe->opaque) { | 81 | } else if (!oe->opaque) { |
| @@ -117,7 +123,7 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry) | |||
| 117 | { | 123 | { |
| 118 | struct ovl_entry *oe = dentry->d_fsdata; | 124 | struct ovl_entry *oe = dentry->d_fsdata; |
| 119 | 125 | ||
| 120 | return oe->lowerdentry; | 126 | return __ovl_dentry_lower(oe); |
| 121 | } | 127 | } |
| 122 | 128 | ||
| 123 | struct dentry *ovl_dentry_real(struct dentry *dentry) | 129 | struct dentry *ovl_dentry_real(struct dentry *dentry) |
| @@ -127,7 +133,7 @@ struct dentry *ovl_dentry_real(struct dentry *dentry) | |||
| 127 | 133 | ||
| 128 | realdentry = ovl_upperdentry_dereference(oe); | 134 | realdentry = ovl_upperdentry_dereference(oe); |
| 129 | if (!realdentry) | 135 | if (!realdentry) |
| 130 | realdentry = oe->lowerdentry; | 136 | realdentry = __ovl_dentry_lower(oe); |
| 131 | 137 | ||
| 132 | return realdentry; | 138 | return realdentry; |
| 133 | } | 139 | } |
| @@ -140,7 +146,7 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper) | |||
| 140 | if (realdentry) { | 146 | if (realdentry) { |
| 141 | *is_upper = true; | 147 | *is_upper = true; |
| 142 | } else { | 148 | } else { |
| 143 | realdentry = oe->lowerdentry; | 149 | realdentry = __ovl_dentry_lower(oe); |
| 144 | *is_upper = false; | 150 | *is_upper = false; |
| 145 | } | 151 | } |
| 146 | return realdentry; | 152 | return realdentry; |
| @@ -162,11 +168,9 @@ void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache) | |||
| 162 | 168 | ||
| 163 | void ovl_path_lower(struct dentry *dentry, struct path *path) | 169 | void ovl_path_lower(struct dentry *dentry, struct path *path) |
| 164 | { | 170 | { |
| 165 | struct ovl_fs *ofs = dentry->d_sb->s_fs_info; | ||
| 166 | struct ovl_entry *oe = dentry->d_fsdata; | 171 | struct ovl_entry *oe = dentry->d_fsdata; |
| 167 | 172 | ||
| 168 | path->mnt = ofs->lower_mnt; | 173 | *path = oe->numlower ? oe->lowerstack[0] : (struct path) { NULL, NULL }; |
| 169 | path->dentry = oe->lowerdentry; | ||
| 170 | } | 174 | } |
| 171 | 175 | ||
| 172 | int ovl_want_write(struct dentry *dentry) | 176 | int ovl_want_write(struct dentry *dentry) |
| @@ -258,8 +262,11 @@ static void ovl_dentry_release(struct dentry *dentry) | |||
| 258 | struct ovl_entry *oe = dentry->d_fsdata; | 262 | struct ovl_entry *oe = dentry->d_fsdata; |
| 259 | 263 | ||
| 260 | if (oe) { | 264 | if (oe) { |
| 265 | unsigned int i; | ||
| 266 | |||
| 261 | dput(oe->__upperdentry); | 267 | dput(oe->__upperdentry); |
| 262 | dput(oe->lowerdentry); | 268 | for (i = 0; i < oe->numlower; i++) |
| 269 | dput(oe->lowerstack[i].dentry); | ||
| 263 | kfree_rcu(oe, rcu); | 270 | kfree_rcu(oe, rcu); |
| 264 | } | 271 | } |
| 265 | } | 272 | } |
| @@ -268,9 +275,15 @@ static const struct dentry_operations ovl_dentry_operations = { | |||
| 268 | .d_release = ovl_dentry_release, | 275 | .d_release = ovl_dentry_release, |
| 269 | }; | 276 | }; |
| 270 | 277 | ||
| 271 | static struct ovl_entry *ovl_alloc_entry(void) | 278 | static struct ovl_entry *ovl_alloc_entry(unsigned int numlower) |
| 272 | { | 279 | { |
| 273 | return kzalloc(sizeof(struct ovl_entry), GFP_KERNEL); | 280 | size_t size = offsetof(struct ovl_entry, lowerstack[numlower]); |
| 281 | struct ovl_entry *oe = kzalloc(size, GFP_KERNEL); | ||
| 282 | |||
| 283 | if (oe) | ||
| 284 | oe->numlower = numlower; | ||
| 285 | |||
| 286 | return oe; | ||
| 274 | } | 287 | } |
| 275 | 288 | ||
| 276 | static inline struct dentry *ovl_lookup_real(struct dentry *dir, | 289 | static inline struct dentry *ovl_lookup_real(struct dentry *dir, |
| @@ -297,19 +310,19 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
| 297 | { | 310 | { |
| 298 | struct ovl_entry *oe; | 311 | struct ovl_entry *oe; |
| 299 | struct dentry *upperdir; | 312 | struct dentry *upperdir; |
| 300 | struct dentry *lowerdir; | 313 | struct path lowerdir; |
| 301 | struct dentry *upperdentry = NULL; | 314 | struct dentry *upperdentry = NULL; |
| 302 | struct dentry *lowerdentry = NULL; | 315 | struct dentry *lowerdentry = NULL; |
| 303 | struct inode *inode = NULL; | 316 | struct inode *inode = NULL; |
| 304 | int err; | 317 | int err; |
| 305 | 318 | ||
| 306 | err = -ENOMEM; | 319 | err = -ENOMEM; |
| 307 | oe = ovl_alloc_entry(); | 320 | oe = ovl_alloc_entry(1); |
| 308 | if (!oe) | 321 | if (!oe) |
| 309 | goto out; | 322 | goto out; |
| 310 | 323 | ||
| 311 | upperdir = ovl_dentry_upper(dentry->d_parent); | 324 | upperdir = ovl_dentry_upper(dentry->d_parent); |
| 312 | lowerdir = ovl_dentry_lower(dentry->d_parent); | 325 | ovl_path_lower(dentry->d_parent, &lowerdir); |
| 313 | 326 | ||
| 314 | if (upperdir) { | 327 | if (upperdir) { |
| 315 | upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); | 328 | upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); |
| @@ -317,7 +330,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
| 317 | if (IS_ERR(upperdentry)) | 330 | if (IS_ERR(upperdentry)) |
| 318 | goto out_put_dir; | 331 | goto out_put_dir; |
| 319 | 332 | ||
| 320 | if (lowerdir && upperdentry) { | 333 | if (lowerdir.dentry && upperdentry) { |
| 321 | if (ovl_is_whiteout(upperdentry)) { | 334 | if (ovl_is_whiteout(upperdentry)) { |
| 322 | dput(upperdentry); | 335 | dput(upperdentry); |
| 323 | upperdentry = NULL; | 336 | upperdentry = NULL; |
| @@ -327,8 +340,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
| 327 | } | 340 | } |
| 328 | } | 341 | } |
| 329 | } | 342 | } |
| 330 | if (lowerdir && !oe->opaque) { | 343 | if (lowerdir.dentry && !oe->opaque) { |
| 331 | lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name); | 344 | lowerdentry = ovl_lookup_real(lowerdir.dentry, &dentry->d_name); |
| 332 | err = PTR_ERR(lowerdentry); | 345 | err = PTR_ERR(lowerdentry); |
| 333 | if (IS_ERR(lowerdentry)) | 346 | if (IS_ERR(lowerdentry)) |
| 334 | goto out_dput_upper; | 347 | goto out_dput_upper; |
| @@ -355,8 +368,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
| 355 | } | 368 | } |
| 356 | 369 | ||
| 357 | oe->__upperdentry = upperdentry; | 370 | oe->__upperdentry = upperdentry; |
| 358 | oe->lowerdentry = lowerdentry; | 371 | if (lowerdentry) { |
| 359 | 372 | oe->lowerstack[0].dentry = lowerdentry; | |
| 373 | oe->lowerstack[0].mnt = lowerdir.mnt; | ||
| 374 | } else { | ||
| 375 | oe->numlower = 0; | ||
| 376 | } | ||
| 360 | dentry->d_fsdata = oe; | 377 | dentry->d_fsdata = oe; |
| 361 | d_add(dentry, inode); | 378 | d_add(dentry, inode); |
| 362 | 379 | ||
| @@ -380,10 +397,12 @@ struct file *ovl_path_open(struct path *path, int flags) | |||
| 380 | static void ovl_put_super(struct super_block *sb) | 397 | static void ovl_put_super(struct super_block *sb) |
| 381 | { | 398 | { |
| 382 | struct ovl_fs *ufs = sb->s_fs_info; | 399 | struct ovl_fs *ufs = sb->s_fs_info; |
| 400 | unsigned i; | ||
| 383 | 401 | ||
| 384 | dput(ufs->workdir); | 402 | dput(ufs->workdir); |
| 385 | mntput(ufs->upper_mnt); | 403 | mntput(ufs->upper_mnt); |
| 386 | mntput(ufs->lower_mnt); | 404 | for (i = 0; i < ufs->numlower; i++) |
| 405 | mntput(ufs->lower_mnt[i]); | ||
| 387 | 406 | ||
| 388 | kfree(ufs->config.lowerdir); | 407 | kfree(ufs->config.lowerdir); |
| 389 | kfree(ufs->config.upperdir); | 408 | kfree(ufs->config.upperdir); |
| @@ -641,6 +660,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
| 641 | struct ovl_entry *oe; | 660 | struct ovl_entry *oe; |
| 642 | struct ovl_fs *ufs; | 661 | struct ovl_fs *ufs; |
| 643 | struct kstatfs statfs; | 662 | struct kstatfs statfs; |
| 663 | struct vfsmount *mnt; | ||
| 664 | unsigned int i; | ||
| 644 | int err; | 665 | int err; |
| 645 | 666 | ||
| 646 | err = -ENOMEM; | 667 | err = -ENOMEM; |
| @@ -661,7 +682,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
| 661 | } | 682 | } |
| 662 | 683 | ||
| 663 | err = -ENOMEM; | 684 | err = -ENOMEM; |
| 664 | oe = ovl_alloc_entry(); | 685 | oe = ovl_alloc_entry(1); |
| 665 | if (oe == NULL) | 686 | if (oe == NULL) |
| 666 | goto out_free_config; | 687 | goto out_free_config; |
| 667 | 688 | ||
| @@ -727,12 +748,24 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
| 727 | goto out_put_workpath; | 748 | goto out_put_workpath; |
| 728 | } | 749 | } |
| 729 | 750 | ||
| 730 | ufs->lower_mnt = clone_private_mount(&lowerpath); | 751 | ufs->lower_mnt = kcalloc(1, sizeof(struct vfsmount *), GFP_KERNEL); |
| 731 | err = PTR_ERR(ufs->lower_mnt); | 752 | if (ufs->lower_mnt == NULL) |
| 732 | if (IS_ERR(ufs->lower_mnt)) { | ||
| 733 | pr_err("overlayfs: failed to clone lowerpath\n"); | ||
| 734 | goto out_put_upper_mnt; | 753 | goto out_put_upper_mnt; |
| 754 | |||
| 755 | mnt = clone_private_mount(&lowerpath); | ||
| 756 | err = PTR_ERR(mnt); | ||
| 757 | if (IS_ERR(mnt)) { | ||
| 758 | pr_err("overlayfs: failed to clone lowerpath\n"); | ||
| 759 | goto out_put_lower_mnt; | ||
| 735 | } | 760 | } |
| 761 | /* | ||
| 762 | * Make lower_mnt R/O. That way fchmod/fchown on lower file | ||
| 763 | * will fail instead of modifying lower fs. | ||
| 764 | */ | ||
| 765 | mnt->mnt_flags |= MNT_READONLY; | ||
| 766 | |||
| 767 | ufs->lower_mnt[0] = mnt; | ||
| 768 | ufs->numlower = 1; | ||
| 736 | 769 | ||
| 737 | ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry); | 770 | ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry); |
| 738 | err = PTR_ERR(ufs->workdir); | 771 | err = PTR_ERR(ufs->workdir); |
| @@ -742,12 +775,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
| 742 | goto out_put_lower_mnt; | 775 | goto out_put_lower_mnt; |
| 743 | } | 776 | } |
| 744 | 777 | ||
| 745 | /* | ||
| 746 | * Make lower_mnt R/O. That way fchmod/fchown on lower file | ||
| 747 | * will fail instead of modifying lower fs. | ||
| 748 | */ | ||
| 749 | ufs->lower_mnt->mnt_flags |= MNT_READONLY; | ||
| 750 | |||
| 751 | /* If the upper fs is r/o, we mark overlayfs r/o too */ | 778 | /* If the upper fs is r/o, we mark overlayfs r/o too */ |
| 752 | if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY) | 779 | if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY) |
| 753 | sb->s_flags |= MS_RDONLY; | 780 | sb->s_flags |= MS_RDONLY; |
| @@ -768,7 +795,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
| 768 | path_put(&workpath); | 795 | path_put(&workpath); |
| 769 | 796 | ||
| 770 | oe->__upperdentry = upperpath.dentry; | 797 | oe->__upperdentry = upperpath.dentry; |
| 771 | oe->lowerdentry = lowerpath.dentry; | 798 | oe->lowerstack[0].dentry = lowerpath.dentry; |
| 799 | oe->lowerstack[0].mnt = ufs->lower_mnt[0]; | ||
| 772 | 800 | ||
| 773 | root_dentry->d_fsdata = oe; | 801 | root_dentry->d_fsdata = oe; |
| 774 | 802 | ||
| @@ -782,7 +810,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
| 782 | out_put_workdir: | 810 | out_put_workdir: |
| 783 | dput(ufs->workdir); | 811 | dput(ufs->workdir); |
| 784 | out_put_lower_mnt: | 812 | out_put_lower_mnt: |
| 785 | mntput(ufs->lower_mnt); | 813 | for (i = 0; i < ufs->numlower; i++) |
| 814 | mntput(ufs->lower_mnt[i]); | ||
| 815 | kfree(ufs->lower_mnt); | ||
| 786 | out_put_upper_mnt: | 816 | out_put_upper_mnt: |
| 787 | mntput(ufs->upper_mnt); | 817 | mntput(ufs->upper_mnt); |
| 788 | out_put_workpath: | 818 | out_put_workpath: |
