From 4eae06de482bf370144704e31f65cd6dfbcebe94 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Fri, 27 Oct 2017 15:44:08 +0300 Subject: ovl: lockdep annotate of nested OVL_I(inode)->lock This fixes a lockdep splat when mounting a nested overlayfs. Fixes: a015dafcaf5b ("ovl: use ovl_inode mutex to synchronize...") Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/inode.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 321511ed8c42..03f0ec2b73eb 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -14,6 +14,7 @@ #include #include #include "overlayfs.h" +#include "ovl_entry.h" int ovl_setattr(struct dentry *dentry, struct iattr *attr) { @@ -409,6 +410,7 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode) #ifdef CONFIG_LOCKDEP static struct lock_class_key ovl_i_mutex_key[OVL_MAX_NESTING]; static struct lock_class_key ovl_i_mutex_dir_key[OVL_MAX_NESTING]; + static struct lock_class_key ovl_i_lock_key[OVL_MAX_NESTING]; int depth = inode->i_sb->s_stack_depth - 1; @@ -419,6 +421,8 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode) lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_dir_key[depth]); else lockdep_set_class(&inode->i_rwsem, &ovl_i_mutex_key[depth]); + + lockdep_set_class(&OVL_I(inode)->lock, &ovl_i_lock_key[depth]); #endif } -- cgit From b79e05aaa166755fafbf02db275175edb5175df8 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 25 Jun 2017 16:37:17 +0300 Subject: ovl: no direct iteration for dir with origin xattr If a non-merge dir in an overlay mount has an overlay.origin xattr, it means it was once an upper merge dir, which may contain whiteouts and then the lower dir was removed under it. Do not iterate real dir directly in this case to avoid exposing whiteouts. [SzM] Set OVL_WHITEOUT for all merge directories as well. [amir] A directory that was just copied up does not have the OVL_WHITEOUTS flag. We need to set it to fix merge dir iteration. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 7 ++++++- fs/overlayfs/inode.c | 10 ++++++++++ fs/overlayfs/overlayfs.h | 4 ++++ fs/overlayfs/readdir.c | 24 ++++++++++++++++++++---- fs/overlayfs/super.c | 2 ++ fs/overlayfs/util.c | 13 +++++++++++++ 6 files changed, 55 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index c441f9387a1b..d07ad7bbd041 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -486,6 +486,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c) { struct inode *udir = c->destdir->d_inode; + struct inode *inode; struct dentry *newdentry = NULL; struct dentry *temp = NULL; int err; @@ -508,7 +509,11 @@ static int ovl_copy_up_locked(struct ovl_copy_up_ctx *c) if (err) goto out_cleanup; - ovl_inode_update(d_inode(c->dentry), newdentry); + inode = d_inode(c->dentry); + ovl_inode_update(inode, newdentry); + if (S_ISDIR(inode->i_mode)) + ovl_set_flag(OVL_WHITEOUTS, inode); + out: dput(temp); return err; diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 03f0ec2b73eb..e5a20fd3cbd4 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -661,6 +661,16 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry, if (upperdentry && ovl_is_impuredir(upperdentry)) ovl_set_flag(OVL_IMPURE, inode); + /* Check for non-merge dir that may have whiteouts */ + if (S_ISDIR(realinode->i_mode)) { + struct ovl_entry *oe = dentry->d_fsdata; + + if (((upperdentry && lowerdentry) || oe->numlower > 1) || + ovl_check_origin_xattr(upperdentry ?: lowerdentry)) { + ovl_set_flag(OVL_WHITEOUTS, inode); + } + } + if (inode->i_state & I_NEW) unlock_new_inode(inode); out: diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index d9a0edd4e57e..d53157ccf0d7 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -28,7 +28,10 @@ enum ovl_path_type { #define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink" enum ovl_flag { + /* Pure upper dir that may contain non pure upper entries */ OVL_IMPURE, + /* Non-merge dir that may contain whiteout entries */ + OVL_WHITEOUTS, OVL_INDEX, }; @@ -223,6 +226,7 @@ bool ovl_is_whiteout(struct dentry *dentry); struct file *ovl_path_open(struct path *path, int flags); int ovl_copy_up_start(struct dentry *dentry); void ovl_copy_up_end(struct dentry *dentry); +bool ovl_check_origin_xattr(struct dentry *dentry); bool ovl_check_dir_xattr(struct dentry *dentry, const char *name); int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry, const char *name, const void *value, size_t size, diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 698b74dd750e..95d12755f847 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -316,21 +316,37 @@ static inline int ovl_dir_read(struct path *realpath, return err; } +/* + * Can we iterate real dir directly? + * + * Non-merge dir may contain whiteouts from a time it was a merge upper, before + * lower dir was removed under it and possibly before it was rotated from upper + * to lower layer. + */ +static bool ovl_dir_is_real(struct dentry *dir) +{ + return !ovl_test_flag(OVL_WHITEOUTS, d_inode(dir)); +} + static void ovl_dir_reset(struct file *file) { struct ovl_dir_file *od = file->private_data; struct ovl_dir_cache *cache = od->cache; struct dentry *dentry = file->f_path.dentry; - enum ovl_path_type type = ovl_path_type(dentry); + bool is_real; if (cache && ovl_dentry_version_get(dentry) != cache->version) { ovl_cache_put(od, dentry); od->cache = NULL; od->cursor = NULL; } - WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type)); - if (od->is_real && OVL_TYPE_MERGE(type)) + is_real = ovl_dir_is_real(dentry); + if (od->is_real != is_real) { + /* is_real can only become false when dir is copied up */ + if (WARN_ON(is_real)) + return; od->is_real = false; + } } static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list, @@ -816,7 +832,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file) return PTR_ERR(realfile); } od->realfile = realfile; - od->is_real = !OVL_TYPE_MERGE(type); + od->is_real = ovl_dir_is_real(file->f_path.dentry); od->is_upper = OVL_TYPE_UPPER(type); file->private_data = od; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index f5738e96a052..8d82a1cb655f 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1141,6 +1141,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) root_dentry->d_fsdata = oe; + /* Root is always merge -> can have whiteouts */ + ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry)); ovl_inode_init(d_inode(root_dentry), upperpath.dentry, ovl_dentry_lower(root_dentry)); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index b9b239fa5cfd..51ca8bd16009 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -329,6 +329,19 @@ void ovl_copy_up_end(struct dentry *dentry) mutex_unlock(&OVL_I(d_inode(dentry))->lock); } +bool ovl_check_origin_xattr(struct dentry *dentry) +{ + int res; + + res = vfs_getxattr(dentry, OVL_XATTR_ORIGIN, NULL, 0); + + /* Zero size value means "copied up but origin unknown" */ + if (res >= 0) + return true; + + return false; +} + bool ovl_check_dir_xattr(struct dentry *dentry, const char *name) { int res; -- cgit From 95e598e7ace2d89717cc3370c2126570667e2007 Mon Sep 17 00:00:00 2001 From: "zhangyi (F)" Date: Tue, 31 Oct 2017 22:57:00 +0200 Subject: ovl: simplify ovl_check_empty_and_clear() Filter out non-whiteout non-upper entries from list of merge dir entries while checking if merge dir is empty in ovl_check_empty_dir(). The remaining work for ovl_clear_empty() is to clear all entries on the list. [amir: split patch from rmdir bug fix] Signed-off-by: zhangyi (F) Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/dir.c | 5 ++--- fs/overlayfs/readdir.c | 27 ++++++++++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index cc961a3bd3bd..4edef400fe51 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -300,7 +300,6 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) { int err; struct dentry *ret = NULL; - enum ovl_path_type type = ovl_path_type(dentry); LIST_HEAD(list); err = ovl_check_empty_dir(dentry, &list); @@ -313,13 +312,13 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry) * When removing an empty opaque directory, then it makes no sense to * replace it with an exact replica of itself. * - * If no upperdentry then skip clearing whiteouts. + * If upperdentry has whiteouts, clear them. * * Can race with copy-up, since we don't hold the upperdir mutex. * Doesn't matter, since copy-up can't create a non-empty directory * from an empty one. */ - if (OVL_TYPE_UPPER(type) && OVL_TYPE_MERGE(type)) + if (!list_empty(&list)) ret = ovl_clear_empty(dentry, &list); out_free: diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 95d12755f847..a5ad5b33b599 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -26,6 +26,7 @@ struct ovl_cache_entry { struct list_head l_node; struct rb_node node; struct ovl_cache_entry *next_maybe_whiteout; + bool is_upper; bool is_whiteout; char name[]; }; @@ -158,6 +159,7 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd, /* Defer setting d_ino for upper entry to ovl_iterate() */ if (ovl_calc_d_ino(rdd, p)) p->ino = 0; + p->is_upper = rdd->is_upper; p->is_whiteout = false; if (d_type == DT_CHR) { @@ -851,7 +853,7 @@ const struct file_operations ovl_dir_operations = { int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) { int err; - struct ovl_cache_entry *p; + struct ovl_cache_entry *p, *n; struct rb_root root = RB_ROOT; err = ovl_dir_read_merged(dentry, list, &root); @@ -860,18 +862,29 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) err = 0; - list_for_each_entry(p, list, l_node) { - if (p->is_whiteout) - continue; + list_for_each_entry_safe(p, n, list, l_node) { + /* + * Select whiteouts in upperdir, they should + * be cleared when deleting this directory. + */ + if (p->is_whiteout) { + if (p->is_upper) + continue; + goto del_entry; + } if (p->name[0] == '.') { if (p->len == 1) - continue; + goto del_entry; if (p->len == 2 && p->name[1] == '.') - continue; + goto del_entry; } err = -ENOTEMPTY; break; + +del_entry: + list_del(&p->l_node); + kfree(p); } return err; @@ -885,7 +898,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) list_for_each_entry(p, list, l_node) { struct dentry *dentry; - if (!p->is_whiteout) + if (WARN_ON(!p->is_whiteout || !p->is_upper)) continue; dentry = lookup_one_len(p->name, upper, p->len); -- cgit From 07f6fff148364ad7c0174d6536a124a0679177a2 Mon Sep 17 00:00:00 2001 From: "zhangyi (F)" Date: Tue, 4 Jul 2017 13:02:27 +0300 Subject: ovl: fix rmdir problem on non-merge dir with origin xattr An "origin && non-merge" upper dir may have leftover whiteouts that were created in past mount. overlayfs does no clear this dir when we delete it, which may lead to rmdir fail or temp file left in workdir. Simple reproducer: mkdir lower upper work merge mkdir -p lower/dir touch lower/dir/a mount -t overlay overlay -olowerdir=lower,upperdir=upper,\ workdir=work merge rm merge/dir/a umount merge rm -rf lower/* touch lower/dir (*) mount -t overlay overlay -olowerdir=lower,upperdir=upper,\ workdir=work merge rm -rf merge/dir Syslog dump: overlayfs: cleanup of 'work/#7' failed (-39) (*): if we do not create the regular file, the result is different: rm: cannot remove "dir/": Directory not empty This patch adds a check for the case of non-merge dir that may contain whiteouts, and calls ovl_check_empty_dir() to check and clear whiteouts from upper dir when an empty dir is being deleted. [amir: split patch from ovl_check_empty_dir() cleanup rename ovl_is_origin() to ovl_may_have_whiteouts() check OVL_WHITEOUTS flag instead of checking origin xattr] Signed-off-by: zhangyi (F) Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/dir.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 4edef400fe51..ef533198be45 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -181,6 +181,11 @@ static bool ovl_type_origin(struct dentry *dentry) return OVL_TYPE_ORIGIN(ovl_path_type(dentry)); } +static bool ovl_may_have_whiteouts(struct dentry *dentry) +{ + return ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry)); +} + static int ovl_create_upper(struct dentry *dentry, struct inode *inode, struct cattr *attr, struct dentry *hardlink) { @@ -697,8 +702,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir) struct dentry *opaquedir = NULL; int err; - /* Redirect dir can be !ovl_lower_positive && OVL_TYPE_MERGE */ - if (is_dir && ovl_dentry_get_redirect(dentry)) { + /* Redirect/origin dir can be !ovl_lower_positive && not clean */ + if (is_dir && (ovl_dentry_get_redirect(dentry) || + ovl_may_have_whiteouts(dentry))) { opaquedir = ovl_check_empty_and_clear(dentry); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) @@ -945,7 +951,8 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, old_cred = ovl_override_creds(old->d_sb); - if (overwrite && new_is_dir && ovl_type_merge_or_lower(new)) { + if (overwrite && new_is_dir && (ovl_type_merge_or_lower(new) || + ovl_may_have_whiteouts(new))) { opaquedir = ovl_check_empty_and_clear(new); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) { -- cgit From ee023c30d7d6bc21d3a85f3625a30209bdcc41e6 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 30 Oct 2017 13:33:11 +0200 Subject: ovl: move include of ovl_entry.h into overlayfs.h Most overlayfs c files already explicitly include ovl_entry.h to use overlay entry struct definitions and upcoming changes are going to require even more c files to include this header. All overlayfs c files include overlayfs.h and overlayfs.h itself refers to some structs defined in ovl_entry.h, so it seems more logic to include ovl_entry.h from overlayfs.h than from c files. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 1 - fs/overlayfs/inode.c | 1 - fs/overlayfs/namei.c | 1 - fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/super.c | 1 - fs/overlayfs/util.c | 1 - 6 files changed, 1 insertion(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index d07ad7bbd041..eb3b8d39fb61 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -22,7 +22,6 @@ #include #include #include "overlayfs.h" -#include "ovl_entry.h" #define OVL_COPY_UP_CHUNK_SIZE (1 << 20) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index e5a20fd3cbd4..52aaa8530710 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -14,7 +14,6 @@ #include #include #include "overlayfs.h" -#include "ovl_entry.h" int ovl_setattr(struct dentry *dentry, struct iattr *attr) { diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index a12dc10bf726..505a4b8902fc 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -15,7 +15,6 @@ #include #include #include "overlayfs.h" -#include "ovl_entry.h" struct ovl_lookup_data { struct qstr name; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index d53157ccf0d7..1cf3bdd193a4 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -9,6 +9,7 @@ #include #include +#include "ovl_entry.h" enum ovl_path_type { __OVL_PATH_UPPER = (1 << 0), diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 8d82a1cb655f..e3d49e965224 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -18,7 +18,6 @@ #include #include #include "overlayfs.h" -#include "ovl_entry.h" MODULE_AUTHOR("Miklos Szeredi "); MODULE_DESCRIPTION("Overlay filesystem"); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 51ca8bd16009..9158d17bb320 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -17,7 +17,6 @@ #include #include #include "overlayfs.h" -#include "ovl_entry.h" int ovl_want_write(struct dentry *dentry) { -- cgit From b93436320c1e9089a055941523571cd7c037f7cb Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Mon, 24 Jul 2017 01:57:54 -0500 Subject: ovl: re-structure overlay lower layers in-memory Define new structures to represent overlay instance lower layers and overlay merge dir lower layers to make room for storing more per layer information in-memory. Instead of keeping the fs instance lower layers in an array of struct vfsmount, keep them in an array of new struct ovl_layer, that has a pointer to struct vfsmount. Instead of keeping the dentry lower layers in an array of struct path, keep them in an array of new struct ovl_path, that has a pointer to struct dentry and to struct ovl_layer. Add a small helper to find the fs layer id that correspopnds to a lower struct ovl_path and use it in ovl_lookup(). [amir: split re-structure from anonymous bdev patch] Signed-off-by: Chandan Rajendra Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/namei.c | 52 ++++++++++++++++++++++++---------------- fs/overlayfs/overlayfs.h | 4 ++-- fs/overlayfs/ovl_entry.h | 13 ++++++++-- fs/overlayfs/readdir.c | 4 ++-- fs/overlayfs/super.c | 62 ++++++++++++++++++++++++++---------------------- fs/overlayfs/util.c | 7 +++++- 6 files changed, 86 insertions(+), 56 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 505a4b8902fc..6cc3ece3417d 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -285,16 +285,15 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, static int ovl_check_origin(struct dentry *upperdentry, - struct path *lowerstack, unsigned int numlower, - struct path **stackp, unsigned int *ctrp) + struct ovl_path *lower, unsigned int numlower, + struct ovl_path **stackp, unsigned int *ctrp) { struct vfsmount *mnt; struct dentry *origin = NULL; int i; - for (i = 0; i < numlower; i++) { - mnt = lowerstack[i].mnt; + mnt = lower[i].layer->mnt; origin = ovl_get_origin(upperdentry, mnt); if (IS_ERR(origin)) return PTR_ERR(origin); @@ -308,12 +307,12 @@ static int ovl_check_origin(struct dentry *upperdentry, BUG_ON(*ctrp); if (!*stackp) - *stackp = kmalloc(sizeof(struct path), GFP_KERNEL); + *stackp = kmalloc(sizeof(struct ovl_path), GFP_KERNEL); if (!*stackp) { dput(origin); return -ENOMEM; } - **stackp = (struct path) { .dentry = origin, .mnt = mnt }; + **stackp = (struct ovl_path){.dentry = origin, .layer = lower[i].layer}; *ctrp = 1; return 0; @@ -383,13 +382,13 @@ fail: * OVL_XATTR_ORIGIN and that origin file handle can be decoded to lower path. * Return 0 on match, -ESTALE on mismatch or stale origin, < 0 on error. */ -int ovl_verify_index(struct dentry *index, struct path *lowerstack, +int ovl_verify_index(struct dentry *index, struct ovl_path *lower, unsigned int numlower) { struct ovl_fh *fh = NULL; size_t len; - struct path origin = { }; - struct path *stack = &origin; + struct ovl_path origin = { }; + struct ovl_path *stack = &origin; unsigned int ctr = 0; int err; @@ -428,7 +427,7 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack, if (err) goto fail; - err = ovl_check_origin(index, lowerstack, numlower, &stack, &ctr); + err = ovl_check_origin(index, lower, numlower, &stack, &ctr); if (!err && !ctr) err = -ESTALE; if (err) @@ -567,11 +566,24 @@ int ovl_path_next(int idx, struct dentry *dentry, struct path *path) idx++; } BUG_ON(idx > oe->numlower); - *path = oe->lowerstack[idx - 1]; + path->dentry = oe->lowerstack[idx - 1].dentry; + path->mnt = oe->lowerstack[idx - 1].layer->mnt; return (idx < oe->numlower) ? idx + 1 : -1; } +static int ovl_find_layer(struct ovl_fs *ofs, struct ovl_path *path) +{ + int i; + + for (i = 0; i < ofs->numlower; i++) { + if (ofs->lower_layers[i].mnt == path->layer->mnt) + break; + } + + return i; +} + struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { @@ -580,7 +592,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_entry *poe = dentry->d_parent->d_fsdata; struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata; - struct path *stack = NULL; + struct ovl_path *stack = NULL; struct dentry *upperdir, *upperdentry = NULL; struct dentry *index = NULL; unsigned int ctr = 0; @@ -645,17 +657,17 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (!d.stop && poe->numlower) { err = -ENOMEM; - stack = kcalloc(ofs->numlower, sizeof(struct path), + stack = kcalloc(ofs->numlower, sizeof(struct ovl_path), GFP_KERNEL); if (!stack) goto out_put_upper; } for (i = 0; !d.stop && i < poe->numlower; i++) { - struct path lowerpath = poe->lowerstack[i]; + struct ovl_path lower = poe->lowerstack[i]; d.last = i == poe->numlower - 1; - err = ovl_lookup_layer(lowerpath.dentry, &d, &this); + err = ovl_lookup_layer(lower.dentry, &d, &this); if (err) goto out_put; @@ -663,7 +675,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, continue; stack[ctr].dentry = this; - stack[ctr].mnt = lowerpath.mnt; + stack[ctr].layer = lower.layer; ctr++; if (d.stop) @@ -673,10 +685,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, poe = roe; /* Find the current layer on the root dentry */ - for (i = 0; i < poe->numlower; i++) - if (poe->lowerstack[i].mnt == lowerpath.mnt) - break; - if (WARN_ON(i == poe->numlower)) + i = ovl_find_layer(ofs, &lower); + if (WARN_ON(i == ofs->numlower)) break; } } @@ -699,7 +709,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out_put; oe->opaque = upperopaque; - memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr); + memcpy(oe->lowerstack, stack, sizeof(struct ovl_path) * ctr); dentry->d_fsdata = oe; if (upperdentry) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 1cf3bdd193a4..cefe5a97d048 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -251,7 +251,7 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) /* namei.c */ int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, struct dentry *origin, bool is_upper, bool set); -int ovl_verify_index(struct dentry *index, struct path *lowerstack, +int ovl_verify_index(struct dentry *index, struct ovl_path *lower, unsigned int numlower); int ovl_get_index_name(struct dentry *origin, struct qstr *name); int ovl_path_next(int idx, struct dentry *dentry, struct path *path); @@ -268,7 +268,7 @@ int ovl_check_d_type_supported(struct path *realpath); void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, struct dentry *dentry, int level); int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, - struct path *lowerstack, unsigned int numlower); + struct ovl_path *lower, unsigned int numlower); /* inode.c */ int ovl_set_nlink_upper(struct dentry *dentry); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 25d9b5adcd42..1e28329b5db8 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -17,11 +17,20 @@ struct ovl_config { bool index; }; +struct ovl_layer { + struct vfsmount *mnt; +}; + +struct ovl_path { + struct ovl_layer *layer; + struct dentry *dentry; +}; + /* private information held for overlayfs's superblock */ struct ovl_fs { struct vfsmount *upper_mnt; unsigned numlower; - struct vfsmount **lower_mnt; + struct ovl_layer *lower_layers; /* workbasedir is the path at workdir= mount option */ struct dentry *workbasedir; /* workdir is the 'work' directory under workbasedir */ @@ -52,7 +61,7 @@ struct ovl_entry { struct rcu_head rcu; }; unsigned numlower; - struct path lowerstack[]; + struct ovl_path lowerstack[]; }; struct ovl_entry *ovl_alloc_entry(unsigned int numlower); diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index a5ad5b33b599..914e77e10f0f 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -1014,7 +1014,7 @@ void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, } int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, - struct path *lowerstack, unsigned int numlower) + struct ovl_path *lower, unsigned int numlower) { int err; struct dentry *index = NULL; @@ -1049,7 +1049,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, index = NULL; break; } - err = ovl_verify_index(index, lowerstack, numlower); + err = ovl_verify_index(index, lower, numlower); /* Cleanup stale and orphan index entries */ if (err && (err == -ESTALE || err == -ENOENT)) err = ovl_cleanup(dir, index); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index e3d49e965224..a10fff49194b 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -220,8 +220,8 @@ static void ovl_put_super(struct super_block *sb) ovl_inuse_unlock(ufs->upper_mnt->mnt_root); mntput(ufs->upper_mnt); for (i = 0; i < ufs->numlower; i++) - mntput(ufs->lower_mnt[i]); - kfree(ufs->lower_mnt); + mntput(ufs->lower_layers[i].mnt); + kfree(ufs->lower_layers); kfree(ufs->config.lowerdir); kfree(ufs->config.upperdir); @@ -1026,24 +1026,26 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } err = -ENOMEM; - ufs->lower_mnt = kcalloc(numlower, sizeof(struct vfsmount *), GFP_KERNEL); - if (ufs->lower_mnt == NULL) + ufs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), + GFP_KERNEL); + if (ufs->lower_layers == NULL) goto out_put_workdir; for (i = 0; i < numlower; i++) { - struct vfsmount *mnt = clone_private_mount(&stack[i]); + struct vfsmount *mnt; + mnt = clone_private_mount(&stack[i]); err = PTR_ERR(mnt); if (IS_ERR(mnt)) { pr_err("overlayfs: failed to clone lowerpath\n"); - goto out_put_lower_mnt; + goto out_put_lower_layers; } /* - * Make lower_mnt R/O. That way fchmod/fchown on lower file + * Make lower layers R/O. That way fchmod/fchown on lower file * will fail instead of modifying lower fs. */ mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; - ufs->lower_mnt[ufs->numlower] = mnt; + ufs->lower_layers[ufs->numlower].mnt = mnt; ufs->numlower++; /* Check if all lower layers are on same sb */ @@ -1059,13 +1061,25 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) else if (ufs->upper_mnt->mnt_sb != ufs->same_sb) ufs->same_sb = NULL; + err = -ENOMEM; + oe = ovl_alloc_entry(numlower); + if (!oe) + goto out_put_lower_layers; + + for (i = 0; i < numlower; i++) { + oe->lowerstack[i].dentry = stack[i].dentry; + oe->lowerstack[i].layer = &(ufs->lower_layers[i]); + } + if (!(ovl_force_readonly(ufs)) && ufs->config.index) { /* Verify lower root is upper root origin */ - err = ovl_verify_origin(upperpath.dentry, ufs->lower_mnt[0], - stack[0].dentry, false, true); + err = ovl_verify_origin(upperpath.dentry, + oe->lowerstack[0].layer->mnt, + oe->lowerstack[0].dentry, + false, true); if (err) { pr_err("overlayfs: failed to verify upper root origin\n"); - goto out_put_lower_mnt; + goto out_free_oe; } ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry, @@ -1081,7 +1095,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!err) err = ovl_indexdir_cleanup(ufs->indexdir, ufs->upper_mnt, - stack, numlower); + oe->lowerstack, + numlower); } if (err || !ufs->indexdir) pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n"); @@ -1106,11 +1121,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) /* Never override disk quota limits or use reserved space */ cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); - err = -ENOMEM; - oe = ovl_alloc_entry(numlower); - if (!oe) - goto out_put_cred; - sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_op = &ovl_super_operations; sb->s_xattr = ovl_xattr_handlers; @@ -1119,11 +1129,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); if (!root_dentry) - goto out_free_oe; + goto out_put_cred; mntput(upperpath.mnt); for (i = 0; i < numlower; i++) mntput(stack[i].mnt); + kfree(stack); mntput(workpath.mnt); kfree(lowertmp); @@ -1132,11 +1143,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (ovl_is_impuredir(upperpath.dentry)) ovl_set_flag(OVL_IMPURE, d_inode(root_dentry)); } - for (i = 0; i < numlower; i++) { - oe->lowerstack[i].dentry = stack[i].dentry; - oe->lowerstack[i].mnt = ufs->lower_mnt[i]; - } - kfree(stack); root_dentry->d_fsdata = oe; @@ -1149,16 +1155,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) return 0; -out_free_oe: - kfree(oe); out_put_cred: put_cred(ufs->creator_cred); out_put_indexdir: dput(ufs->indexdir); -out_put_lower_mnt: +out_free_oe: + kfree(oe); +out_put_lower_layers: for (i = 0; i < ufs->numlower; i++) - mntput(ufs->lower_mnt[i]); - kfree(ufs->lower_mnt); + mntput(ufs->lower_layers[i].mnt); + kfree(ufs->lower_layers); out_put_workdir: dput(ufs->workdir); mntput(ufs->upper_mnt); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 9158d17bb320..d6bb1c9f5e7a 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -124,7 +124,12 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) { struct ovl_entry *oe = dentry->d_fsdata; - *path = oe->numlower ? oe->lowerstack[0] : (struct path) { }; + if (oe->numlower) { + path->mnt = oe->lowerstack[0].layer->mnt; + path->dentry = oe->lowerstack[0].dentry; + } else { + *path = (struct path) { }; + } } enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path) -- cgit From 2a9c6d066e98c1fe51a735b1439929f2f2afd891 Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Wed, 1 Nov 2017 20:12:49 +0200 Subject: ovl: allocate anonymous devs for lowerdirs Generate unique values of st_dev per lower layer for non-samefs overlay mount. The unique values are obtained by allocating anonymous bdevs for each of the lowerdirs in the overlayfs instance. The anonymous bdev is going to be returned by stat(2) for lowerdir non-dir entries in non-samefs case. [amir: split from ovl_getattr() and re-structure patches] Signed-off-by: Chandan Rajendra Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/ovl_entry.h | 1 + fs/overlayfs/super.c | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 1e28329b5db8..93eb6a044dd2 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -19,6 +19,7 @@ struct ovl_config { struct ovl_layer { struct vfsmount *mnt; + dev_t pseudo_dev; }; struct ovl_path { diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index a10fff49194b..2c9f48096ff0 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -219,8 +219,10 @@ static void ovl_put_super(struct super_block *sb) if (ufs->upper_mnt && ufs->upperdir_locked) ovl_inuse_unlock(ufs->upper_mnt->mnt_root); mntput(ufs->upper_mnt); - for (i = 0; i < ufs->numlower; i++) + for (i = 0; i < ufs->numlower; i++) { mntput(ufs->lower_layers[i].mnt); + free_anon_bdev(ufs->lower_layers[i].pseudo_dev); + } kfree(ufs->lower_layers); kfree(ufs->config.lowerdir); @@ -1032,11 +1034,19 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) goto out_put_workdir; for (i = 0; i < numlower; i++) { struct vfsmount *mnt; + dev_t dev; + + err = get_anon_bdev(&dev); + if (err) { + pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n"); + goto out_put_lower_layers; + } mnt = clone_private_mount(&stack[i]); err = PTR_ERR(mnt); if (IS_ERR(mnt)) { pr_err("overlayfs: failed to clone lowerpath\n"); + free_anon_bdev(dev); goto out_put_lower_layers; } /* @@ -1046,6 +1056,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; ufs->lower_layers[ufs->numlower].mnt = mnt; + ufs->lower_layers[ufs->numlower].pseudo_dev = dev; ufs->numlower++; /* Check if all lower layers are on same sb */ @@ -1162,8 +1173,11 @@ out_put_indexdir: out_free_oe: kfree(oe); out_put_lower_layers: - for (i = 0; i < ufs->numlower; i++) + for (i = 0; i < ufs->numlower; i++) { + if (ufs->lower_layers[i].mnt) + free_anon_bdev(ufs->lower_layers[i].pseudo_dev); mntput(ufs->lower_layers[i].mnt); + } kfree(ufs->lower_layers); out_put_workdir: dput(ufs->workdir); -- cgit From ba1e563cdc6b07f2d5d9eee854fb3cdf3596470f Mon Sep 17 00:00:00 2001 From: Chandan Rajendra Date: Mon, 24 Jul 2017 01:57:54 -0500 Subject: ovl: return anonymous st_dev for lower inodes For non-samefs setup, to make sure that st_dev/st_ino pair is unique across the system, we return a unique anonymous st_dev for stat(2) of lower layer inode. A following patch is going to fix constant st_dev/st_ino across copy up by returning origin st_dev/st_ino for copied up objects. If the st_dev/st_ino for copied up object would have been the same as that of the real underlying lower file, running diff on underlying lower file and overlay copied up file would result in diff reporting that the 2 files are equal when in fact, they may have different content. [amir: simplify ovl_get_pseudo_dev() split from allocate anonymous bdev patch] Signed-off-by: Chandan Rajendra Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/inode.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'fs') diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 52aaa8530710..b599059d6f78 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -15,6 +15,14 @@ #include #include "overlayfs.h" + +static dev_t ovl_get_pseudo_dev(struct dentry *dentry) +{ + struct ovl_entry *oe = dentry->d_fsdata; + + return oe->lowerstack[0].layer->pseudo_dev; +} + int ovl_setattr(struct dentry *dentry, struct iattr *attr) { int err; @@ -121,6 +129,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat, */ stat->dev = dentry->d_sb->s_dev; stat->ino = dentry->d_inode->i_ino; + } else if (!OVL_TYPE_UPPER(type)) { + /* + * For non-samefs setup, to make sure that st_dev/st_ino pair + * is unique across the system, we use a unique anonymous + * st_dev for lower layer inode. + */ + stat->dev = ovl_get_pseudo_dev(dentry); } /* -- cgit From a0c5ad307ac09fa1c73b57bfd94f4c3fd6ba92d8 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Wed, 1 Nov 2017 00:45:40 +0200 Subject: ovl: relax same fs constraint for constant st_ino For the case of all layers not on the same fs, return the copy up origin inode st_dev/st_ino for non-dir from stat(2). This guaranties constant st_dev/st_ino for non-dir across copy up. Like the same fs case, st_ino of non-dir is also persistent. If the st_dev/st_ino for copied up object would have been the same as that of the real underlying lower file, running diff on underlying lower file and overlay copied up file would result in diff reporting that the two files are equal when in fact, they may have different content. Therefore, unlike the same fs case, st_dev is not persistent because it uses the unique anonymous bdev allocated for the lower layer. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/inode.c | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index b599059d6f78..00b6b294272a 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -74,6 +74,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, struct path realpath; const struct cred *old_cred; bool is_dir = S_ISDIR(dentry->d_inode->i_mode); + bool samefs = ovl_same_sb(dentry->d_sb); int err; type = ovl_path_real(dentry, &realpath); @@ -83,16 +84,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat, goto out; /* - * When all layers are on the same fs, all real inode number are - * unique, so we use the overlay st_dev, which is friendly to du -x. - * - * We also use st_ino of the copy up origin, if we know it. - * This guaranties constant st_dev/st_ino across copy up. + * For non-dir or same fs, we use st_ino of the copy up origin, if we + * know it. This guaranties constant st_dev/st_ino across copy up. * * If filesystem supports NFS export ops, this also guaranties * persistent st_ino across mount cycle. */ - if (ovl_same_sb(dentry->d_sb)) { + if (!is_dir || samefs) { if (OVL_TYPE_ORIGIN(type)) { struct kstat lowerstat; u32 lowermask = STATX_INO | (!is_dir ? STATX_NLINK : 0); @@ -103,7 +101,6 @@ int ovl_getattr(const struct path *path, struct kstat *stat, if (err) goto out; - WARN_ON_ONCE(stat->dev != lowerstat.dev); /* * Lower hardlinks may be broken on copy up to different * upper files, so we cannot use the lower origin st_ino @@ -115,27 +112,39 @@ int ovl_getattr(const struct path *path, struct kstat *stat, if (is_dir || lowerstat.nlink == 1 || ovl_test_flag(OVL_INDEX, d_inode(dentry))) stat->ino = lowerstat.ino; + + if (samefs) + WARN_ON_ONCE(stat->dev != lowerstat.dev); + else + stat->dev = ovl_get_pseudo_dev(dentry); } - stat->dev = dentry->d_sb->s_dev; - } else if (is_dir) { + if (samefs) { + /* + * When all layers are on the same fs, all real inode + * number are unique, so we use the overlay st_dev, + * which is friendly to du -x. + */ + stat->dev = dentry->d_sb->s_dev; + } else if (!OVL_TYPE_UPPER(type)) { + /* + * For non-samefs setup, to make sure that st_dev/st_ino + * pair is unique across the system, we use a unique + * anonymous st_dev for lower layer inode. + */ + stat->dev = ovl_get_pseudo_dev(dentry); + } + } else { /* - * If not all layers are on the same fs the pair {real st_ino; - * overlay st_dev} is not unique, so use the non persistent - * overlay st_ino. - * * Always use the overlay st_dev for directories, so 'find * -xdev' will scan the entire overlay mount and won't cross the * overlay mount boundaries. + * + * If not all layers are on the same fs the pair {real st_ino; + * overlay st_dev} is not unique, so use the non persistent + * overlay st_ino for directories. */ stat->dev = dentry->d_sb->s_dev; stat->ino = dentry->d_inode->i_ino; - } else if (!OVL_TYPE_UPPER(type)) { - /* - * For non-samefs setup, to make sure that st_dev/st_ino pair - * is unique across the system, we use a unique anonymous - * st_dev for lower layer inode. - */ - stat->dev = ovl_get_pseudo_dev(dentry); } /* -- cgit From f30536f0f955d9d3eb5a7e32033af4e3649de173 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Wed, 1 Nov 2017 17:33:13 +0200 Subject: ovl: update cache version of impure parent on rename ovl_rename() updates dir cache version for impure old parent if an entry with copy up origin is moved into old parent, but it did not update cache version if the entry moved out of old parent has a copy up origin. [SzM] Same for new dir: we updated the version if an entry with origin was moved in, but not if an entry with origin was moved out. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/dir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index ef533198be45..e13921824c70 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1075,9 +1075,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, drop_nlink(d_inode(new)); } - ovl_dentry_version_inc(old->d_parent, - !overwrite && ovl_type_origin(new)); - ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old)); + ovl_dentry_version_inc(old->d_parent, ovl_type_origin(old) || + (!overwrite && ovl_type_origin(new))); + ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old) || + (d_inode(new) && ovl_type_origin(new))); out_dput: dput(newdentry); -- cgit From 8aafcb593d25e81c13be49310550e80d8788b995 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: use path_put_init() in error paths for ovl_fill_super() This allows simplifying the error cleanup later. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 2c9f48096ff0..bc8729491362 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -586,7 +586,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path) return 0; out_put: - path_put(path); + path_put_init(path); out: return err; } @@ -604,7 +604,7 @@ static int ovl_mount_dir(const char *name, struct path *path) if (ovl_dentry_remote(path->dentry)) { pr_err("overlayfs: filesystem on '%s' not supported as upperdir\n", tmp); - path_put(path); + path_put_init(path); err = -EINVAL; } kfree(tmp); @@ -656,7 +656,7 @@ static int ovl_lower_dir(const char *name, struct path *path, return 0; out_put: - path_put(path); + path_put_init(path); out: return err; } -- cgit From 6ee8acf0f72b89b3c6d9df9cbe9b815711af3c7b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: split out ovl_get_upperpath() from ovl_fill_super() It's okay to get rid of the intermediate error label due to ufs being zeroed on allocation. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 59 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index bc8729491362..c1abd66527ce 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -827,6 +827,39 @@ static const struct xattr_handler *ovl_xattr_handlers[] = { NULL }; +static int ovl_get_upperpath(struct ovl_fs *ufs, struct path *upperpath) +{ + int err; + + err = ovl_mount_dir(ufs->config.upperdir, upperpath); + if (err) + goto out; + + /* Upper fs should not be r/o */ + if (sb_rdonly(upperpath->mnt->mnt_sb)) { + pr_err("overlayfs: upper fs is r/o, try multi-lower layers mount\n"); + err = -EINVAL; + goto out; + } + + err = ovl_check_namelen(upperpath, ufs, ufs->config.upperdir); + if (err) + goto out; + + err = -EBUSY; + if (ovl_inuse_trylock(upperpath->dentry)) { + ufs->upperdir_locked = true; + } else if (ufs->config.index) { + pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n"); + goto out; + } else { + pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); + } + err = 0; +out: + return err; +} + static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; @@ -870,30 +903,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) goto out_free_config; } - err = ovl_mount_dir(ufs->config.upperdir, &upperpath); + err = ovl_get_upperpath(ufs, &upperpath); if (err) - goto out_free_config; - - /* Upper fs should not be r/o */ - if (sb_rdonly(upperpath.mnt->mnt_sb)) { - pr_err("overlayfs: upper fs is r/o, try multi-lower layers mount\n"); - err = -EINVAL; - goto out_put_upperpath; - } - - err = ovl_check_namelen(&upperpath, ufs, ufs->config.upperdir); - if (err) - goto out_put_upperpath; - - err = -EBUSY; - if (ovl_inuse_trylock(upperpath.dentry)) { - ufs->upperdir_locked = true; - } else if (ufs->config.index) { - pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n"); - goto out_put_upperpath; - } else { - pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); - } + goto out_unlock_upperdentry; err = ovl_mount_dir(ufs->config.workdir, &workpath); if (err) @@ -1196,7 +1208,6 @@ out_put_workpath: out_unlock_upperdentry: if (ufs->upperdir_locked) ovl_inuse_unlock(upperpath.dentry); -out_put_upperpath: path_put(&upperpath); out_free_config: kfree(ufs->config.lowerdir); -- cgit From 87ad447a9d4f807b5542e6d92c6d740103b82bda Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: split out ovl_get_workpath() from ovl_fill_super() It's okay to get rid of the intermediate error label due to ufs being zeroed on allocation. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 61 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index c1abd66527ce..6d02ca31bb90 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -860,6 +860,41 @@ out: return err; } +static int ovl_get_workpath(struct ovl_fs *ufs, struct path *upperpath, + struct path *workpath) +{ + int err; + + err = ovl_mount_dir(ufs->config.workdir, workpath); + if (err) + goto out; + + err = -EINVAL; + if (upperpath->mnt != workpath->mnt) { + pr_err("overlayfs: workdir and upperdir must reside under the same mount\n"); + goto out; + } + if (!ovl_workdir_ok(workpath->dentry, upperpath->dentry)) { + pr_err("overlayfs: workdir and upperdir must be separate subtrees\n"); + goto out; + } + + err = -EBUSY; + if (ovl_inuse_trylock(workpath->dentry)) { + ufs->workdir_locked = true; + } else if (ufs->config.index) { + pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); + goto out; + } else { + pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); + } + + ufs->workbasedir = workpath->dentry; + err = 0; +out: + return err; +} + static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; @@ -907,31 +942,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (err) goto out_unlock_upperdentry; - err = ovl_mount_dir(ufs->config.workdir, &workpath); + err = ovl_get_workpath(ufs, &upperpath, &workpath); if (err) - goto out_unlock_upperdentry; - - err = -EINVAL; - if (upperpath.mnt != workpath.mnt) { - pr_err("overlayfs: workdir and upperdir must reside under the same mount\n"); - goto out_put_workpath; - } - if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) { - pr_err("overlayfs: workdir and upperdir must be separate subtrees\n"); - goto out_put_workpath; - } - - err = -EBUSY; - if (ovl_inuse_trylock(workpath.dentry)) { - ufs->workdir_locked = true; - } else if (ufs->config.index) { - pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); - goto out_put_workpath; - } else { - pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); - } + goto out_unlock_workdentry; - ufs->workbasedir = workpath.dentry; sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth; } err = -ENOMEM; @@ -1203,7 +1217,6 @@ out_free_lowertmp: out_unlock_workdentry: if (ufs->workdir_locked) ovl_inuse_unlock(workpath.dentry); -out_put_workpath: path_put(&workpath); out_unlock_upperdentry: if (ufs->upperdir_locked) -- cgit From 53dbb0b4787ef57834f74bdccfba4c63eb12da69 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: split out ovl_get_lowerstack() from ovl_fill_super() Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 122 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 50 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 6d02ca31bb90..6c8703112b8f 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -895,6 +895,75 @@ out: return err; } +static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, + struct path **stackp, unsigned int *stacklenp) +{ + int err; + char *lowertmp, *lower; + struct path *stack; + unsigned int stacklen, numlower, i; + bool remote = false; + + err = -ENOMEM; + lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL); + if (!lowertmp) + goto out; + + err = -EINVAL; + stacklen = ovl_split_lowerdirs(lowertmp); + if (stacklen > OVL_MAX_STACK) { + pr_err("overlayfs: too many lower directories, limit is %d\n", + OVL_MAX_STACK); + goto out; + } else if (!ufs->config.upperdir && stacklen == 1) { + pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n"); + goto out; + } + + err = -ENOMEM; + stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL); + if (!stack) + goto out; + + err = -EINVAL; + lower = lowertmp; + for (numlower = 0; numlower < stacklen; numlower++) { + err = ovl_lower_dir(lower, &stack[numlower], ufs, + &sb->s_stack_depth, &remote); + if (err) + goto out_free_stack; + + lower = strchr(lower, '\0') + 1; + } + + err = -EINVAL; + sb->s_stack_depth++; + if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { + pr_err("overlayfs: maximum fs stacking depth exceeded\n"); + goto out_free_stack; + } + + *stackp = stack; + *stacklenp = numlower; + + if (remote) + sb->s_d_op = &ovl_reval_dentry_operations; + else + sb->s_d_op = &ovl_dentry_operations; + + err = 0; + +out: + kfree(lowertmp); + return err; + +out_free_stack: + for (i = 0; i < numlower; i++) + path_put(&stack[i]); + kfree(stack); + goto out; +} + static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; @@ -903,12 +972,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) struct ovl_entry *oe; struct ovl_fs *ufs; struct path *stack = NULL; - char *lowertmp; - char *lower; - unsigned int numlower; - unsigned int stacklen = 0; + unsigned int numlower = 0; unsigned int i; - bool remote = false; struct cred *cred; int err; @@ -948,45 +1013,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth; } - err = -ENOMEM; - lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL); - if (!lowertmp) + err = ovl_get_lowerstack(sb, ufs, &stack, &numlower); + if (err) goto out_unlock_workdentry; - err = -EINVAL; - stacklen = ovl_split_lowerdirs(lowertmp); - if (stacklen > OVL_MAX_STACK) { - pr_err("overlayfs: too many lower directories, limit is %d\n", - OVL_MAX_STACK); - goto out_free_lowertmp; - } else if (!ufs->config.upperdir && stacklen == 1) { - pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n"); - goto out_free_lowertmp; - } - - err = -ENOMEM; - stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL); - if (!stack) - goto out_free_lowertmp; - - err = -EINVAL; - lower = lowertmp; - for (numlower = 0; numlower < stacklen; numlower++) { - err = ovl_lower_dir(lower, &stack[numlower], ufs, - &sb->s_stack_depth, &remote); - if (err) - goto out_put_lowerpath; - - lower = strchr(lower, '\0') + 1; - } - - err = -EINVAL; - sb->s_stack_depth++; - if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { - pr_err("overlayfs: maximum fs stacking depth exceeded\n"); - goto out_put_lowerpath; - } - if (ufs->config.upperdir) { ufs->upper_mnt = clone_private_mount(&upperpath); err = PTR_ERR(ufs->upper_mnt); @@ -1145,11 +1175,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!ufs->indexdir) ufs->config.index = false; - if (remote) - sb->s_d_op = &ovl_reval_dentry_operations; - else - sb->s_d_op = &ovl_dentry_operations; - err = -ENOMEM; ufs->creator_cred = cred = prepare_creds(); if (!cred) @@ -1173,7 +1198,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) mntput(stack[i].mnt); kfree(stack); mntput(workpath.mnt); - kfree(lowertmp); if (upperpath.dentry) { oe->has_upper = true; @@ -1212,8 +1236,6 @@ out_put_lowerpath: for (i = 0; i < numlower; i++) path_put(&stack[i]); kfree(stack); -out_free_lowertmp: - kfree(lowertmp); out_unlock_workdentry: if (ufs->workdir_locked) ovl_inuse_unlock(workpath.dentry); -- cgit From 21a3b317a60197b19c8f67e8016b633942e36bbf Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: split out ovl_get_upper() from ovl_fill_super() And don't clobber ufs->upper_mnt on error. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 6c8703112b8f..21f93cd5782f 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -895,6 +895,23 @@ out: return err; } +static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) +{ + struct vfsmount *upper_mnt; + + upper_mnt = clone_private_mount(upperpath); + if (IS_ERR(upper_mnt)) { + pr_err("overlayfs: failed to clone upperpath\n"); + return PTR_ERR(upper_mnt); + } + + /* Don't inherit atime flags */ + upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); + ufs->upper_mnt = upper_mnt; + + return 0; +} + static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, struct path **stackp, unsigned int *stacklenp) { @@ -1018,15 +1035,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) goto out_unlock_workdentry; if (ufs->config.upperdir) { - ufs->upper_mnt = clone_private_mount(&upperpath); - err = PTR_ERR(ufs->upper_mnt); - if (IS_ERR(ufs->upper_mnt)) { - pr_err("overlayfs: failed to clone upperpath\n"); + err = ovl_get_upper(ufs, &upperpath); + if (err) goto out_put_lowerpath; - } - - /* Don't inherit atime flags */ - ufs->upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; -- cgit From 8ed61dc37ee0a33c7581d38d16977a5d1897de9f Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: split out ovl_get_workdir() from ovl_fill_super() Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 111 ++++++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 51 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 21f93cd5782f..aadd07a1e9c1 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -912,6 +912,63 @@ static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) return 0; } +static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ufs, + struct path *workpath) +{ + struct dentry *temp; + int err; + + ufs->workdir = ovl_workdir_create(sb, ufs, workpath->dentry, + OVL_WORKDIR_NAME, false); + if (!ufs->workdir) + return 0; + + /* + * Upper should support d_type, else whiteouts are visible. Given + * workdir and upper are on same fs, we can do iterate_dir() on + * workdir. This check requires successful creation of workdir in + * previous step. + */ + err = ovl_check_d_type_supported(workpath); + if (err < 0) + return err; + + /* + * We allowed this configuration and don't want to break users over + * kernel upgrade. So warn instead of erroring out. + */ + if (!err) + pr_warn("overlayfs: upper fs needs to support d_type.\n"); + + /* Check if upper/work fs supports O_TMPFILE */ + temp = ovl_do_tmpfile(ufs->workdir, S_IFREG | 0); + ufs->tmpfile = !IS_ERR(temp); + if (ufs->tmpfile) + dput(temp); + else + pr_warn("overlayfs: upper fs does not support tmpfile.\n"); + + /* + * Check if upper/work fs supports trusted.overlay.* xattr + */ + err = ovl_do_setxattr(ufs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0); + if (err) { + ufs->noxattr = true; + pr_warn("overlayfs: upper fs does not support xattr.\n"); + } else { + vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE); + } + + /* Check if upper/work fs supports file handles */ + if (ufs->config.index && + !ovl_can_decode_fh(ufs->workdir->d_sb)) { + ufs->config.index = false; + pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n"); + } + + return 0; +} + static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, struct path **stackp, unsigned int *stacklenp) { @@ -1041,57 +1098,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; - ufs->workdir = ovl_workdir_create(sb, ufs, workpath.dentry, - OVL_WORKDIR_NAME, false); - /* - * Upper should support d_type, else whiteouts are visible. - * Given workdir and upper are on same fs, we can do - * iterate_dir() on workdir. This check requires successful - * creation of workdir in previous step. - */ - if (ufs->workdir) { - struct dentry *temp; - - err = ovl_check_d_type_supported(&workpath); - if (err < 0) - goto out_put_workdir; - - /* - * We allowed this configuration and don't want to - * break users over kernel upgrade. So warn instead - * of erroring out. - */ - if (!err) - pr_warn("overlayfs: upper fs needs to support d_type.\n"); - - /* Check if upper/work fs supports O_TMPFILE */ - temp = ovl_do_tmpfile(ufs->workdir, S_IFREG | 0); - ufs->tmpfile = !IS_ERR(temp); - if (ufs->tmpfile) - dput(temp); - else - pr_warn("overlayfs: upper fs does not support tmpfile.\n"); - - /* - * Check if upper/work fs supports trusted.overlay.* - * xattr - */ - err = ovl_do_setxattr(ufs->workdir, OVL_XATTR_OPAQUE, - "0", 1, 0); - if (err) { - ufs->noxattr = true; - pr_warn("overlayfs: upper fs does not support xattr.\n"); - } else { - vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE); - } - - /* Check if upper/work fs supports file handles */ - if (ufs->config.index && - !ovl_can_decode_fh(ufs->workdir->d_sb)) { - ufs->config.index = false; - pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n"); - } - } + err = ovl_get_workdir(sb, ufs, &workpath); + if (err) + goto out_put_workdir; } err = -ENOMEM; -- cgit From c0d91fb9101100b8c13779b95f70b2ef75b54526 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: split out ovl_get_lower_layers() from ovl_fill_super() Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 90 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index aadd07a1e9c1..537412ad12a0 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1038,6 +1038,55 @@ out_free_stack: goto out; } +static int ovl_get_lower_layers(struct ovl_fs *ufs, struct path *stack, + unsigned int numlower) +{ + int err; + unsigned int i; + + err = -ENOMEM; + ufs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), + GFP_KERNEL); + if (ufs->lower_layers == NULL) + goto out; + for (i = 0; i < numlower; i++) { + struct vfsmount *mnt; + dev_t dev; + + err = get_anon_bdev(&dev); + if (err) { + pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n"); + goto out; + } + + mnt = clone_private_mount(&stack[i]); + err = PTR_ERR(mnt); + if (IS_ERR(mnt)) { + pr_err("overlayfs: failed to clone lowerpath\n"); + free_anon_bdev(dev); + goto out; + } + /* + * Make lower layers R/O. That way fchmod/fchown on lower file + * will fail instead of modifying lower fs. + */ + mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; + + ufs->lower_layers[ufs->numlower].mnt = mnt; + ufs->lower_layers[ufs->numlower].pseudo_dev = dev; + ufs->numlower++; + + /* Check if all lower layers are on same sb */ + if (i == 0) + ufs->same_sb = mnt->mnt_sb; + else if (ufs->same_sb != mnt->mnt_sb) + ufs->same_sb = NULL; + } + err = 0; +out: + return err; +} + static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; @@ -1103,44 +1152,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) goto out_put_workdir; } - err = -ENOMEM; - ufs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), - GFP_KERNEL); - if (ufs->lower_layers == NULL) - goto out_put_workdir; - for (i = 0; i < numlower; i++) { - struct vfsmount *mnt; - dev_t dev; - - err = get_anon_bdev(&dev); - if (err) { - pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n"); - goto out_put_lower_layers; - } - - mnt = clone_private_mount(&stack[i]); - err = PTR_ERR(mnt); - if (IS_ERR(mnt)) { - pr_err("overlayfs: failed to clone lowerpath\n"); - free_anon_bdev(dev); - goto out_put_lower_layers; - } - /* - * Make lower layers R/O. That way fchmod/fchown on lower file - * will fail instead of modifying lower fs. - */ - mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; - - ufs->lower_layers[ufs->numlower].mnt = mnt; - ufs->lower_layers[ufs->numlower].pseudo_dev = dev; - ufs->numlower++; - - /* Check if all lower layers are on same sb */ - if (i == 0) - ufs->same_sb = mnt->mnt_sb; - else if (ufs->same_sb != mnt->mnt_sb) - ufs->same_sb = NULL; - } + err = ovl_get_lower_layers(ufs, stack, numlower); + if (err) + goto out_put_lower_layers; /* If the upper fs is nonexistent, we mark overlayfs r/o too */ if (!ufs->upper_mnt) -- cgit From f7e3a7d947f83684f6622b592136da54bed922e6 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: ovl: split out ovl_get_indexdir() from ovl_fill_super() It's okay to get rid of the intermediate error label due to ufs being zeroed on allocation. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 69 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 537412ad12a0..6bb874da174d 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -969,6 +969,45 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ufs, return 0; } +static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ufs, + struct ovl_entry *oe, + struct path *upperpath, struct path *workpath) +{ + int err; + + /* Verify lower root is upper root origin */ + err = ovl_verify_origin(upperpath->dentry, + oe->lowerstack[0].layer->mnt, + oe->lowerstack[0].dentry, + false, true); + if (err) { + pr_err("overlayfs: failed to verify upper root origin\n"); + goto out; + } + + ufs->indexdir = ovl_workdir_create(sb, ufs, workpath->dentry, + OVL_INDEXDIR_NAME, true); + if (ufs->indexdir) { + /* Verify upper root is index dir origin */ + err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt, + upperpath->dentry, true, true); + if (err) + pr_err("overlayfs: failed to verify index dir origin\n"); + + /* Cleanup bad/stale/orphan index entries */ + if (!err) + err = ovl_indexdir_cleanup(ufs->indexdir, + ufs->upper_mnt, + oe->lowerstack, + oe->numlower); + } + if (err || !ufs->indexdir) + pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n"); + +out: + return err; +} + static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, struct path **stackp, unsigned int *stacklenp) { @@ -1173,34 +1212,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } if (!(ovl_force_readonly(ufs)) && ufs->config.index) { - /* Verify lower root is upper root origin */ - err = ovl_verify_origin(upperpath.dentry, - oe->lowerstack[0].layer->mnt, - oe->lowerstack[0].dentry, - false, true); - if (err) { - pr_err("overlayfs: failed to verify upper root origin\n"); - goto out_free_oe; - } - - ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry, - OVL_INDEXDIR_NAME, true); - if (ufs->indexdir) { - /* Verify upper root is index dir origin */ - err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt, - upperpath.dentry, true, true); - if (err) - pr_err("overlayfs: failed to verify index dir origin\n"); - - /* Cleanup bad/stale/orphan index entries */ - if (!err) - err = ovl_indexdir_cleanup(ufs->indexdir, - ufs->upper_mnt, - oe->lowerstack, - numlower); - } - if (err || !ufs->indexdir) - pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n"); + err = ovl_get_indexdir(sb, ufs, oe, &upperpath, &workpath); if (err) goto out_put_indexdir; } @@ -1254,7 +1266,6 @@ out_put_cred: put_cred(ufs->creator_cred); out_put_indexdir: dput(ufs->indexdir); -out_free_oe: kfree(oe); out_put_lower_layers: for (i = 0; i < ufs->numlower; i++) { -- cgit From 95e6d4177cb7a2d7a760180e13f32adaf4188833 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:29 +0100 Subject: ovl: grab reference to workbasedir early and related cleanups. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 6bb874da174d..255c0523148f 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -889,7 +889,7 @@ static int ovl_get_workpath(struct ovl_fs *ufs, struct path *upperpath, pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); } - ufs->workbasedir = workpath->dentry; + ufs->workbasedir = dget(workpath->dentry); err = 0; out: return err; @@ -918,7 +918,7 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ufs, struct dentry *temp; int err; - ufs->workdir = ovl_workdir_create(sb, ufs, workpath->dentry, + ufs->workdir = ovl_workdir_create(sb, ufs, ufs->workbasedir, OVL_WORKDIR_NAME, false); if (!ufs->workdir) return 0; @@ -971,7 +971,7 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ufs, static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ufs, struct ovl_entry *oe, - struct path *upperpath, struct path *workpath) + struct path *upperpath) { int err; @@ -985,7 +985,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ufs, goto out; } - ufs->indexdir = ovl_workdir_create(sb, ufs, workpath->dentry, + ufs->indexdir = ovl_workdir_create(sb, ufs, ufs->workbasedir, OVL_INDEXDIR_NAME, true); if (ufs->indexdir) { /* Verify upper root is index dir origin */ @@ -1212,7 +1212,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } if (!(ovl_force_readonly(ufs)) && ufs->config.index) { - err = ovl_get_indexdir(sb, ufs, oe, &upperpath, &workpath); + err = ovl_get_indexdir(sb, ufs, oe, &upperpath); if (err) goto out_put_indexdir; } @@ -1243,7 +1243,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) for (i = 0; i < numlower; i++) mntput(stack[i].mnt); kfree(stack); - mntput(workpath.mnt); + path_put(&workpath); if (upperpath.dentry) { oe->has_upper = true; @@ -1283,7 +1283,8 @@ out_put_lowerpath: kfree(stack); out_unlock_workdentry: if (ufs->workdir_locked) - ovl_inuse_unlock(workpath.dentry); + ovl_inuse_unlock(ufs->workbasedir); + dput(ufs->workbasedir); path_put(&workpath); out_unlock_upperdentry: if (ufs->upperdir_locked) -- cgit From a9075cdb467dd3b01b57f8786e6ea1eca05a76b4 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:15 +0100 Subject: ovl: factor out ovl_free_fs() helper This can be called both from ovl_put_super() and in the error cleanup path from ovl_fill_super(). Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 72 ++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 45 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 255c0523148f..5a5b41c542fb 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -206,9 +206,8 @@ static void ovl_destroy_inode(struct inode *inode) call_rcu(&inode->i_rcu, ovl_i_callback); } -static void ovl_put_super(struct super_block *sb) +static void ovl_free_fs(struct ovl_fs *ufs) { - struct ovl_fs *ufs = sb->s_fs_info; unsigned i; dput(ufs->indexdir); @@ -216,7 +215,7 @@ static void ovl_put_super(struct super_block *sb) if (ufs->workdir_locked) ovl_inuse_unlock(ufs->workbasedir); dput(ufs->workbasedir); - if (ufs->upper_mnt && ufs->upperdir_locked) + if (ufs->upperdir_locked) ovl_inuse_unlock(ufs->upper_mnt->mnt_root); mntput(ufs->upper_mnt); for (i = 0; i < ufs->numlower; i++) { @@ -228,10 +227,18 @@ static void ovl_put_super(struct super_block *sb) kfree(ufs->config.lowerdir); kfree(ufs->config.upperdir); kfree(ufs->config.workdir); - put_cred(ufs->creator_cred); + if (ufs->creator_cred) + put_cred(ufs->creator_cred); kfree(ufs); } +static void ovl_put_super(struct super_block *sb) +{ + struct ovl_fs *ofs = sb->s_fs_info; + + ovl_free_fs(ofs); +} + static int ovl_sync_fs(struct super_block *sb, int wait) { struct ovl_fs *ufs = sb->s_fs_info; @@ -1131,7 +1138,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) struct path upperpath = { }; struct path workpath = { }; struct dentry *root_dentry; - struct ovl_entry *oe; + struct ovl_entry *oe = NULL; struct ovl_fs *ufs; struct path *stack = NULL; unsigned int numlower = 0; @@ -1148,13 +1155,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ufs->config.index = ovl_index_def; err = ovl_parse_opt((char *) data, &ufs->config); if (err) - goto out_free_config; + goto out_err; err = -EINVAL; if (!ufs->config.lowerdir) { if (!silent) pr_err("overlayfs: missing 'lowerdir'\n"); - goto out_free_config; + goto out_err; } sb->s_stack_depth = 0; @@ -1162,38 +1169,38 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (ufs->config.upperdir) { if (!ufs->config.workdir) { pr_err("overlayfs: missing 'workdir'\n"); - goto out_free_config; + goto out_err; } err = ovl_get_upperpath(ufs, &upperpath); if (err) - goto out_unlock_upperdentry; + goto out_err; err = ovl_get_workpath(ufs, &upperpath, &workpath); if (err) - goto out_unlock_workdentry; + goto out_err; sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth; } err = ovl_get_lowerstack(sb, ufs, &stack, &numlower); if (err) - goto out_unlock_workdentry; + goto out_err; if (ufs->config.upperdir) { err = ovl_get_upper(ufs, &upperpath); if (err) - goto out_put_lowerpath; + goto out_err; sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; err = ovl_get_workdir(sb, ufs, &workpath); if (err) - goto out_put_workdir; + goto out_err; } err = ovl_get_lower_layers(ufs, stack, numlower); if (err) - goto out_put_lower_layers; + goto out_err; /* If the upper fs is nonexistent, we mark overlayfs r/o too */ if (!ufs->upper_mnt) @@ -1204,7 +1211,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) err = -ENOMEM; oe = ovl_alloc_entry(numlower); if (!oe) - goto out_put_lower_layers; + goto out_err; for (i = 0; i < numlower; i++) { oe->lowerstack[i].dentry = stack[i].dentry; @@ -1214,7 +1221,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!(ovl_force_readonly(ufs)) && ufs->config.index) { err = ovl_get_indexdir(sb, ufs, oe, &upperpath); if (err) - goto out_put_indexdir; + goto out_err; } /* Show index=off/on in /proc/mounts for any of the reasons above */ @@ -1224,7 +1231,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) err = -ENOMEM; ufs->creator_cred = cred = prepare_creds(); if (!cred) - goto out_put_indexdir; + goto out_err; /* Never override disk quota limits or use reserved space */ cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); @@ -1237,7 +1244,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); if (!root_dentry) - goto out_put_cred; + goto out_err; mntput(upperpath.mnt); for (i = 0; i < numlower; i++) @@ -1262,39 +1269,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) return 0; -out_put_cred: - put_cred(ufs->creator_cred); -out_put_indexdir: - dput(ufs->indexdir); +out_err: kfree(oe); -out_put_lower_layers: - for (i = 0; i < ufs->numlower; i++) { - if (ufs->lower_layers[i].mnt) - free_anon_bdev(ufs->lower_layers[i].pseudo_dev); - mntput(ufs->lower_layers[i].mnt); - } - kfree(ufs->lower_layers); -out_put_workdir: - dput(ufs->workdir); - mntput(ufs->upper_mnt); -out_put_lowerpath: for (i = 0; i < numlower; i++) path_put(&stack[i]); kfree(stack); -out_unlock_workdentry: - if (ufs->workdir_locked) - ovl_inuse_unlock(ufs->workbasedir); - dput(ufs->workbasedir); path_put(&workpath); -out_unlock_upperdentry: - if (ufs->upperdir_locked) - ovl_inuse_unlock(upperpath.dentry); path_put(&upperpath); -out_free_config: - kfree(ufs->config.lowerdir); - kfree(ufs->config.upperdir); - kfree(ufs->config.workdir); - kfree(ufs); + ovl_free_fs(ufs); out: return err; } -- cgit From c6fe62549313555ed56b83bb8338383d78e768cb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:15 +0100 Subject: ovl: change order of setup in ovl_fill_super() Move ovl_get_upper() immediately after ovl_get_upperpath(), ovl_get_workdir() immediately after ovl_get_workdir() and ovl_get_lower_layers() immediately after ovl_get_lowerstack(). Also move prepare_creds() up to where other allocations are happening. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 5a5b41c542fb..c695ea569a99 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1151,6 +1151,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!ufs) goto out; + ufs->creator_cred = cred = prepare_creds(); + if (!cred) + goto out_err; + ufs->config.redirect_dir = ovl_redirect_dir_def; ufs->config.index = ovl_index_def; err = ovl_parse_opt((char *) data, &ufs->config); @@ -1176,27 +1180,25 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (err) goto out_err; - err = ovl_get_workpath(ufs, &upperpath, &workpath); + err = ovl_get_upper(ufs, &upperpath); if (err) goto out_err; - sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth; - } - err = ovl_get_lowerstack(sb, ufs, &stack, &numlower); - if (err) - goto out_err; - - if (ufs->config.upperdir) { - err = ovl_get_upper(ufs, &upperpath); + err = ovl_get_workpath(ufs, &upperpath, &workpath); if (err) goto out_err; - sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; - err = ovl_get_workdir(sb, ufs, &workpath); if (err) goto out_err; + + sb->s_stack_depth = ufs->upper_mnt->mnt_sb->s_stack_depth; + sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; + } + err = ovl_get_lowerstack(sb, ufs, &stack, &numlower); + if (err) + goto out_err; err = ovl_get_lower_layers(ufs, stack, numlower); if (err) @@ -1228,11 +1230,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!ufs->indexdir) ufs->config.index = false; - err = -ENOMEM; - ufs->creator_cred = cred = prepare_creds(); - if (!cred) - goto out_err; - /* Never override disk quota limits or use reserved space */ cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); @@ -1242,6 +1239,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_fs_info = ufs; sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK; + err = -ENOMEM; root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); if (!root_dentry) goto out_err; -- cgit From 6e88256e197d2a3b5aa0d054e6e2f553469bf389 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:15 +0100 Subject: ovl: reduce the number of arguments for ovl_workdir_create() Remove "sb" and "dentry" arguments of ovl_workdir_create() and related functions. Move setting MS_RDONLY flag to callers. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index c695ea569a99..c1344c8a9520 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -459,12 +459,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) #define OVL_WORKDIR_NAME "work" #define OVL_INDEXDIR_NAME "index" -static struct dentry *ovl_workdir_create(struct super_block *sb, - struct ovl_fs *ufs, - struct dentry *dentry, +static struct dentry *ovl_workdir_create(struct ovl_fs *ufs, const char *name, bool persist) { - struct inode *dir = dentry->d_inode; + struct inode *dir = ufs->workbasedir->d_inode; struct vfsmount *mnt = ufs->upper_mnt; struct dentry *work; int err; @@ -479,7 +477,7 @@ static struct dentry *ovl_workdir_create(struct super_block *sb, locked = true; retry: - work = lookup_one_len(name, dentry, strlen(name)); + work = lookup_one_len(name, ufs->workbasedir, strlen(name)); if (!IS_ERR(work)) { struct iattr attr = { @@ -550,7 +548,6 @@ out_dput: out_err: pr_warn("overlayfs: failed to create directory %s/%s (errno: %i); mounting read-only\n", ufs->config.workdir, name, -err); - sb->s_flags |= MS_RDONLY; work = NULL; goto out_unlock; } @@ -919,14 +916,12 @@ static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) return 0; } -static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ufs, - struct path *workpath) +static int ovl_get_workdir(struct ovl_fs *ufs, struct path *workpath) { struct dentry *temp; int err; - ufs->workdir = ovl_workdir_create(sb, ufs, ufs->workbasedir, - OVL_WORKDIR_NAME, false); + ufs->workdir = ovl_workdir_create(ufs, OVL_WORKDIR_NAME, false); if (!ufs->workdir) return 0; @@ -976,8 +971,7 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ufs, return 0; } -static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ufs, - struct ovl_entry *oe, +static int ovl_get_indexdir(struct ovl_fs *ufs, struct ovl_entry *oe, struct path *upperpath) { int err; @@ -992,8 +986,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ufs, goto out; } - ufs->indexdir = ovl_workdir_create(sb, ufs, ufs->workbasedir, - OVL_INDEXDIR_NAME, true); + ufs->indexdir = ovl_workdir_create(ufs, OVL_INDEXDIR_NAME, true); if (ufs->indexdir) { /* Verify upper root is index dir origin */ err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt, @@ -1188,10 +1181,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (err) goto out_err; - err = ovl_get_workdir(sb, ufs, &workpath); + err = ovl_get_workdir(ufs, &workpath); if (err) goto out_err; + if (!ufs->workdir) + sb->s_flags |= MS_RDONLY; + sb->s_stack_depth = ufs->upper_mnt->mnt_sb->s_stack_depth; sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; @@ -1221,9 +1217,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) } if (!(ovl_force_readonly(ufs)) && ufs->config.index) { - err = ovl_get_indexdir(sb, ufs, oe, &upperpath); + err = ovl_get_indexdir(ufs, oe, &upperpath); if (err) goto out_err; + + if (!ufs->indexdir) + sb->s_flags |= MS_RDONLY; } /* Show index=off/on in /proc/mounts for any of the reasons above */ -- cgit From 520d7c867f26d216730e0d0e43b2f2ac90953a2d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:15 +0100 Subject: ovl: move ovl_get_workdir() and ovl_get_lower_layers() Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 202 +++++++++++++++++++++++++-------------------------- 1 file changed, 101 insertions(+), 101 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index c1344c8a9520..3807472b718e 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -864,58 +864,6 @@ out: return err; } -static int ovl_get_workpath(struct ovl_fs *ufs, struct path *upperpath, - struct path *workpath) -{ - int err; - - err = ovl_mount_dir(ufs->config.workdir, workpath); - if (err) - goto out; - - err = -EINVAL; - if (upperpath->mnt != workpath->mnt) { - pr_err("overlayfs: workdir and upperdir must reside under the same mount\n"); - goto out; - } - if (!ovl_workdir_ok(workpath->dentry, upperpath->dentry)) { - pr_err("overlayfs: workdir and upperdir must be separate subtrees\n"); - goto out; - } - - err = -EBUSY; - if (ovl_inuse_trylock(workpath->dentry)) { - ufs->workdir_locked = true; - } else if (ufs->config.index) { - pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); - goto out; - } else { - pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); - } - - ufs->workbasedir = dget(workpath->dentry); - err = 0; -out: - return err; -} - -static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) -{ - struct vfsmount *upper_mnt; - - upper_mnt = clone_private_mount(upperpath); - if (IS_ERR(upper_mnt)) { - pr_err("overlayfs: failed to clone upperpath\n"); - return PTR_ERR(upper_mnt); - } - - /* Don't inherit atime flags */ - upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); - ufs->upper_mnt = upper_mnt; - - return 0; -} - static int ovl_get_workdir(struct ovl_fs *ufs, struct path *workpath) { struct dentry *temp; @@ -971,6 +919,58 @@ static int ovl_get_workdir(struct ovl_fs *ufs, struct path *workpath) return 0; } +static int ovl_get_workpath(struct ovl_fs *ufs, struct path *upperpath, + struct path *workpath) +{ + int err; + + err = ovl_mount_dir(ufs->config.workdir, workpath); + if (err) + goto out; + + err = -EINVAL; + if (upperpath->mnt != workpath->mnt) { + pr_err("overlayfs: workdir and upperdir must reside under the same mount\n"); + goto out; + } + if (!ovl_workdir_ok(workpath->dentry, upperpath->dentry)) { + pr_err("overlayfs: workdir and upperdir must be separate subtrees\n"); + goto out; + } + + err = -EBUSY; + if (ovl_inuse_trylock(workpath->dentry)) { + ufs->workdir_locked = true; + } else if (ufs->config.index) { + pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); + goto out; + } else { + pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); + } + + ufs->workbasedir = dget(workpath->dentry); + err = 0; +out: + return err; +} + +static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) +{ + struct vfsmount *upper_mnt; + + upper_mnt = clone_private_mount(upperpath); + if (IS_ERR(upper_mnt)) { + pr_err("overlayfs: failed to clone upperpath\n"); + return PTR_ERR(upper_mnt); + } + + /* Don't inherit atime flags */ + upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); + ufs->upper_mnt = upper_mnt; + + return 0; +} + static int ovl_get_indexdir(struct ovl_fs *ufs, struct ovl_entry *oe, struct path *upperpath) { @@ -1008,6 +1008,55 @@ out: return err; } +static int ovl_get_lower_layers(struct ovl_fs *ufs, struct path *stack, + unsigned int numlower) +{ + int err; + unsigned int i; + + err = -ENOMEM; + ufs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), + GFP_KERNEL); + if (ufs->lower_layers == NULL) + goto out; + for (i = 0; i < numlower; i++) { + struct vfsmount *mnt; + dev_t dev; + + err = get_anon_bdev(&dev); + if (err) { + pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n"); + goto out; + } + + mnt = clone_private_mount(&stack[i]); + err = PTR_ERR(mnt); + if (IS_ERR(mnt)) { + pr_err("overlayfs: failed to clone lowerpath\n"); + free_anon_bdev(dev); + goto out; + } + /* + * Make lower layers R/O. That way fchmod/fchown on lower file + * will fail instead of modifying lower fs. + */ + mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; + + ufs->lower_layers[ufs->numlower].mnt = mnt; + ufs->lower_layers[ufs->numlower].pseudo_dev = dev; + ufs->numlower++; + + /* Check if all lower layers are on same sb */ + if (i == 0) + ufs->same_sb = mnt->mnt_sb; + else if (ufs->same_sb != mnt->mnt_sb) + ufs->same_sb = NULL; + } + err = 0; +out: + return err; +} + static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, struct path **stackp, unsigned int *stacklenp) { @@ -1077,55 +1126,6 @@ out_free_stack: goto out; } -static int ovl_get_lower_layers(struct ovl_fs *ufs, struct path *stack, - unsigned int numlower) -{ - int err; - unsigned int i; - - err = -ENOMEM; - ufs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), - GFP_KERNEL); - if (ufs->lower_layers == NULL) - goto out; - for (i = 0; i < numlower; i++) { - struct vfsmount *mnt; - dev_t dev; - - err = get_anon_bdev(&dev); - if (err) { - pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n"); - goto out; - } - - mnt = clone_private_mount(&stack[i]); - err = PTR_ERR(mnt); - if (IS_ERR(mnt)) { - pr_err("overlayfs: failed to clone lowerpath\n"); - free_anon_bdev(dev); - goto out; - } - /* - * Make lower layers R/O. That way fchmod/fchown on lower file - * will fail instead of modifying lower fs. - */ - mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; - - ufs->lower_layers[ufs->numlower].mnt = mnt; - ufs->lower_layers[ufs->numlower].pseudo_dev = dev; - ufs->numlower++; - - /* Check if all lower layers are on same sb */ - if (i == 0) - ufs->same_sb = mnt->mnt_sb; - else if (ufs->same_sb != mnt->mnt_sb) - ufs->same_sb = NULL; - } - err = 0; -out: - return err; -} - static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; -- cgit From 5064975e7fecdf9c7fc550ddef345aa50d0b04bb Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:15 +0100 Subject: ovl: clean up getting upper layer Merge ovl_get_upper() and ovl_get_upperpath(). The resulting function is named ovl_get_upper(), though it still returns upperpath as well. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 3807472b718e..03202ad7b481 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -831,8 +831,9 @@ static const struct xattr_handler *ovl_xattr_handlers[] = { NULL }; -static int ovl_get_upperpath(struct ovl_fs *ufs, struct path *upperpath) +static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) { + struct vfsmount *upper_mnt; int err; err = ovl_mount_dir(ufs->config.upperdir, upperpath); @@ -859,6 +860,17 @@ static int ovl_get_upperpath(struct ovl_fs *ufs, struct path *upperpath) } else { pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); } + + upper_mnt = clone_private_mount(upperpath); + err = PTR_ERR(upper_mnt); + if (IS_ERR(upper_mnt)) { + pr_err("overlayfs: failed to clone upperpath\n"); + goto out; + } + + /* Don't inherit atime flags */ + upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); + ufs->upper_mnt = upper_mnt; err = 0; out: return err; @@ -954,23 +966,6 @@ out: return err; } -static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) -{ - struct vfsmount *upper_mnt; - - upper_mnt = clone_private_mount(upperpath); - if (IS_ERR(upper_mnt)) { - pr_err("overlayfs: failed to clone upperpath\n"); - return PTR_ERR(upper_mnt); - } - - /* Don't inherit atime flags */ - upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); - ufs->upper_mnt = upper_mnt; - - return 0; -} - static int ovl_get_indexdir(struct ovl_fs *ufs, struct ovl_entry *oe, struct path *upperpath) { @@ -1169,10 +1164,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) goto out_err; } - err = ovl_get_upperpath(ufs, &upperpath); - if (err) - goto out_err; - err = ovl_get_upper(ufs, &upperpath); if (err) goto out_err; -- cgit From bca44b52f8355b9b391fadfe6e6e8662f23238c2 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:15 +0100 Subject: ovl: clean up workdir creation Move calling ovl_get_workdir() into ovl_get_workpath(). Rename ovl_get_workdir() to ovl_make_workdir() and ovl_get_workpath() to ovl_get_workdir(). Workpath is now not needed outside ovl_get_workdir(). Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 03202ad7b481..115cbf3303de 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -876,7 +876,7 @@ out: return err; } -static int ovl_get_workdir(struct ovl_fs *ufs, struct path *workpath) +static int ovl_make_workdir(struct ovl_fs *ufs, struct path *workpath) { struct dentry *temp; int err; @@ -931,27 +931,27 @@ static int ovl_get_workdir(struct ovl_fs *ufs, struct path *workpath) return 0; } -static int ovl_get_workpath(struct ovl_fs *ufs, struct path *upperpath, - struct path *workpath) +static int ovl_get_workdir(struct ovl_fs *ufs, struct path *upperpath) { int err; + struct path workpath = { }; - err = ovl_mount_dir(ufs->config.workdir, workpath); + err = ovl_mount_dir(ufs->config.workdir, &workpath); if (err) goto out; err = -EINVAL; - if (upperpath->mnt != workpath->mnt) { + if (upperpath->mnt != workpath.mnt) { pr_err("overlayfs: workdir and upperdir must reside under the same mount\n"); goto out; } - if (!ovl_workdir_ok(workpath->dentry, upperpath->dentry)) { + if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) { pr_err("overlayfs: workdir and upperdir must be separate subtrees\n"); goto out; } err = -EBUSY; - if (ovl_inuse_trylock(workpath->dentry)) { + if (ovl_inuse_trylock(workpath.dentry)) { ufs->workdir_locked = true; } else if (ufs->config.index) { pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); @@ -960,9 +960,15 @@ static int ovl_get_workpath(struct ovl_fs *ufs, struct path *upperpath, pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); } - ufs->workbasedir = dget(workpath->dentry); + ufs->workbasedir = dget(workpath.dentry); + err = ovl_make_workdir(ufs, &workpath); + if (err) + goto out; + err = 0; out: + path_put(&workpath); + return err; } @@ -1124,7 +1130,6 @@ out_free_stack: static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; - struct path workpath = { }; struct dentry *root_dentry; struct ovl_entry *oe = NULL; struct ovl_fs *ufs; @@ -1168,11 +1173,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (err) goto out_err; - err = ovl_get_workpath(ufs, &upperpath, &workpath); - if (err) - goto out_err; - - err = ovl_get_workdir(ufs, &workpath); + err = ovl_get_workdir(ufs, &upperpath); if (err) goto out_err; @@ -1238,7 +1239,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) for (i = 0; i < numlower; i++) mntput(stack[i].mnt); kfree(stack); - path_put(&workpath); if (upperpath.dentry) { oe->has_upper = true; @@ -1262,7 +1262,6 @@ out_err: for (i = 0; i < numlower; i++) path_put(&stack[i]); kfree(stack); - path_put(&workpath); path_put(&upperpath); ovl_free_fs(ufs); out: -- cgit From 4155c10a0309523924b934b81d08aac03f15c990 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:15 +0100 Subject: ovl: clean up getting lower layers Move calling ovl_get_lower_layers() into ovl_get_lowerstack(). ovl_get_lowerstack() now returns the root dentry's filled in ovl_entry. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 100 +++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 52 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 115cbf3303de..e4852cb1ec64 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -38,15 +38,20 @@ module_param_named(index, ovl_index_def, bool, 0644); MODULE_PARM_DESC(ovl_index_def, "Default to on or off for the inodes index feature"); +static void ovl_entry_stack_free(struct ovl_entry *oe) +{ + unsigned int i; + + for (i = 0; i < oe->numlower; i++) + dput(oe->lowerstack[i].dentry); +} + static void ovl_dentry_release(struct dentry *dentry) { struct ovl_entry *oe = dentry->d_fsdata; if (oe) { - unsigned int i; - - for (i = 0; i < oe->numlower; i++) - dput(oe->lowerstack[i].dentry); + ovl_entry_stack_free(oe); kfree_rcu(oe, rcu); } } @@ -1058,35 +1063,36 @@ out: return err; } -static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, - struct path **stackp, unsigned int *stacklenp) +static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, + struct ovl_fs *ufs) { int err; char *lowertmp, *lower; - struct path *stack; - unsigned int stacklen, numlower, i; + struct path *stack = NULL; + unsigned int stacklen, numlower = 0, i; bool remote = false; + struct ovl_entry *oe; err = -ENOMEM; lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL); if (!lowertmp) - goto out; + goto out_err; err = -EINVAL; stacklen = ovl_split_lowerdirs(lowertmp); if (stacklen > OVL_MAX_STACK) { pr_err("overlayfs: too many lower directories, limit is %d\n", OVL_MAX_STACK); - goto out; + goto out_err; } else if (!ufs->config.upperdir && stacklen == 1) { pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n"); - goto out; + goto out_err; } err = -ENOMEM; stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL); if (!stack) - goto out; + goto out_err; err = -EINVAL; lower = lowertmp; @@ -1094,7 +1100,7 @@ static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, err = ovl_lower_dir(lower, &stack[numlower], ufs, &sb->s_stack_depth, &remote); if (err) - goto out_free_stack; + goto out_err; lower = strchr(lower, '\0') + 1; } @@ -1103,27 +1109,38 @@ static int ovl_get_lowerstack(struct super_block *sb, struct ovl_fs *ufs, sb->s_stack_depth++; if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { pr_err("overlayfs: maximum fs stacking depth exceeded\n"); - goto out_free_stack; + goto out_err; } - *stackp = stack; - *stacklenp = numlower; + err = ovl_get_lower_layers(ufs, stack, numlower); + if (err) + goto out_err; + + err = -ENOMEM; + oe = ovl_alloc_entry(numlower); + if (!oe) + goto out_err; + + for (i = 0; i < numlower; i++) { + oe->lowerstack[i].dentry = dget(stack[i].dentry); + oe->lowerstack[i].layer = &ufs->lower_layers[i]; + } if (remote) sb->s_d_op = &ovl_reval_dentry_operations; else sb->s_d_op = &ovl_dentry_operations; - err = 0; - out: - kfree(lowertmp); - return err; - -out_free_stack: for (i = 0; i < numlower; i++) path_put(&stack[i]); kfree(stack); + kfree(lowertmp); + + return oe; + +out_err: + oe = ERR_PTR(err); goto out; } @@ -1131,11 +1148,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; struct dentry *root_dentry; - struct ovl_entry *oe = NULL; + struct ovl_entry *oe; struct ovl_fs *ufs; - struct path *stack = NULL; - unsigned int numlower = 0; - unsigned int i; struct cred *cred; int err; @@ -1184,12 +1198,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; } - err = ovl_get_lowerstack(sb, ufs, &stack, &numlower); - if (err) - goto out_err; - - err = ovl_get_lower_layers(ufs, stack, numlower); - if (err) + oe = ovl_get_lowerstack(sb, ufs); + err = PTR_ERR(oe); + if (IS_ERR(oe)) goto out_err; /* If the upper fs is nonexistent, we mark overlayfs r/o too */ @@ -1198,20 +1209,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) else if (ufs->upper_mnt->mnt_sb != ufs->same_sb) ufs->same_sb = NULL; - err = -ENOMEM; - oe = ovl_alloc_entry(numlower); - if (!oe) - goto out_err; - - for (i = 0; i < numlower; i++) { - oe->lowerstack[i].dentry = stack[i].dentry; - oe->lowerstack[i].layer = &(ufs->lower_layers[i]); - } - if (!(ovl_force_readonly(ufs)) && ufs->config.index) { err = ovl_get_indexdir(ufs, oe, &upperpath); if (err) - goto out_err; + goto out_free_oe; if (!ufs->indexdir) sb->s_flags |= MS_RDONLY; @@ -1233,13 +1234,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) err = -ENOMEM; root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); if (!root_dentry) - goto out_err; + goto out_free_oe; mntput(upperpath.mnt); - for (i = 0; i < numlower; i++) - mntput(stack[i].mnt); - kfree(stack); - if (upperpath.dentry) { oe->has_upper = true; if (ovl_is_impuredir(upperpath.dentry)) @@ -1257,11 +1254,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) return 0; -out_err: +out_free_oe: + ovl_entry_stack_free(oe); kfree(oe); - for (i = 0; i < numlower; i++) - path_put(&stack[i]); - kfree(stack); +out_err: path_put(&upperpath); ovl_free_fs(ufs); out: -- cgit From ad204488d3046b3baee2d2b1b05323d956a7c45b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 10 Nov 2017 09:39:16 +0100 Subject: ovl: rename ufs to ofs Rename all "struct ovl_fs" pointers to "ofs". The "ufs" name is historical and can only be found in overlayfs/super.c. Signed-off-by: Miklos Szeredi --- fs/overlayfs/super.c | 228 +++++++++++++++++++++++++-------------------------- 1 file changed, 114 insertions(+), 114 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index e4852cb1ec64..3156e908423e 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -211,30 +211,30 @@ static void ovl_destroy_inode(struct inode *inode) call_rcu(&inode->i_rcu, ovl_i_callback); } -static void ovl_free_fs(struct ovl_fs *ufs) +static void ovl_free_fs(struct ovl_fs *ofs) { unsigned i; - dput(ufs->indexdir); - dput(ufs->workdir); - if (ufs->workdir_locked) - ovl_inuse_unlock(ufs->workbasedir); - dput(ufs->workbasedir); - if (ufs->upperdir_locked) - ovl_inuse_unlock(ufs->upper_mnt->mnt_root); - mntput(ufs->upper_mnt); - for (i = 0; i < ufs->numlower; i++) { - mntput(ufs->lower_layers[i].mnt); - free_anon_bdev(ufs->lower_layers[i].pseudo_dev); + dput(ofs->indexdir); + dput(ofs->workdir); + if (ofs->workdir_locked) + ovl_inuse_unlock(ofs->workbasedir); + dput(ofs->workbasedir); + if (ofs->upperdir_locked) + ovl_inuse_unlock(ofs->upper_mnt->mnt_root); + mntput(ofs->upper_mnt); + for (i = 0; i < ofs->numlower; i++) { + mntput(ofs->lower_layers[i].mnt); + free_anon_bdev(ofs->lower_layers[i].pseudo_dev); } - kfree(ufs->lower_layers); - - kfree(ufs->config.lowerdir); - kfree(ufs->config.upperdir); - kfree(ufs->config.workdir); - if (ufs->creator_cred) - put_cred(ufs->creator_cred); - kfree(ufs); + kfree(ofs->lower_layers); + + kfree(ofs->config.lowerdir); + kfree(ofs->config.upperdir); + kfree(ofs->config.workdir); + if (ofs->creator_cred) + put_cred(ofs->creator_cred); + kfree(ofs); } static void ovl_put_super(struct super_block *sb) @@ -246,13 +246,13 @@ static void ovl_put_super(struct super_block *sb) static int ovl_sync_fs(struct super_block *sb, int wait) { - struct ovl_fs *ufs = sb->s_fs_info; + struct ovl_fs *ofs = sb->s_fs_info; struct super_block *upper_sb; int ret; - if (!ufs->upper_mnt) + if (!ofs->upper_mnt) return 0; - upper_sb = ufs->upper_mnt->mnt_sb; + upper_sb = ofs->upper_mnt->mnt_sb; if (!upper_sb->s_op->sync_fs) return 0; @@ -290,9 +290,9 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) } /* Will this overlay be forced to mount/remount ro? */ -static bool ovl_force_readonly(struct ovl_fs *ufs) +static bool ovl_force_readonly(struct ovl_fs *ofs) { - return (!ufs->upper_mnt || !ufs->workdir); + return (!ofs->upper_mnt || !ofs->workdir); } /** @@ -304,29 +304,29 @@ static bool ovl_force_readonly(struct ovl_fs *ufs) static int ovl_show_options(struct seq_file *m, struct dentry *dentry) { struct super_block *sb = dentry->d_sb; - struct ovl_fs *ufs = sb->s_fs_info; + struct ovl_fs *ofs = sb->s_fs_info; - seq_show_option(m, "lowerdir", ufs->config.lowerdir); - if (ufs->config.upperdir) { - seq_show_option(m, "upperdir", ufs->config.upperdir); - seq_show_option(m, "workdir", ufs->config.workdir); + seq_show_option(m, "lowerdir", ofs->config.lowerdir); + if (ofs->config.upperdir) { + seq_show_option(m, "upperdir", ofs->config.upperdir); + seq_show_option(m, "workdir", ofs->config.workdir); } - if (ufs->config.default_permissions) + if (ofs->config.default_permissions) seq_puts(m, ",default_permissions"); - if (ufs->config.redirect_dir != ovl_redirect_dir_def) + if (ofs->config.redirect_dir != ovl_redirect_dir_def) seq_printf(m, ",redirect_dir=%s", - ufs->config.redirect_dir ? "on" : "off"); - if (ufs->config.index != ovl_index_def) + ofs->config.redirect_dir ? "on" : "off"); + if (ofs->config.index != ovl_index_def) seq_printf(m, ",index=%s", - ufs->config.index ? "on" : "off"); + ofs->config.index ? "on" : "off"); return 0; } static int ovl_remount(struct super_block *sb, int *flags, char *data) { - struct ovl_fs *ufs = sb->s_fs_info; + struct ovl_fs *ofs = sb->s_fs_info; - if (!(*flags & MS_RDONLY) && ovl_force_readonly(ufs)) + if (!(*flags & MS_RDONLY) && ovl_force_readonly(ofs)) return -EROFS; return 0; @@ -464,11 +464,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) #define OVL_WORKDIR_NAME "work" #define OVL_INDEXDIR_NAME "index" -static struct dentry *ovl_workdir_create(struct ovl_fs *ufs, +static struct dentry *ovl_workdir_create(struct ovl_fs *ofs, const char *name, bool persist) { - struct inode *dir = ufs->workbasedir->d_inode; - struct vfsmount *mnt = ufs->upper_mnt; + struct inode *dir = ofs->workbasedir->d_inode; + struct vfsmount *mnt = ofs->upper_mnt; struct dentry *work; int err; bool retried = false; @@ -482,7 +482,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ufs, locked = true; retry: - work = lookup_one_len(name, ufs->workbasedir, strlen(name)); + work = lookup_one_len(name, ofs->workbasedir, strlen(name)); if (!IS_ERR(work)) { struct iattr attr = { @@ -552,7 +552,7 @@ out_dput: dput(work); out_err: pr_warn("overlayfs: failed to create directory %s/%s (errno: %i); mounting read-only\n", - ufs->config.workdir, name, -err); + ofs->config.workdir, name, -err); work = NULL; goto out_unlock; } @@ -836,12 +836,12 @@ static const struct xattr_handler *ovl_xattr_handlers[] = { NULL }; -static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) +static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath) { struct vfsmount *upper_mnt; int err; - err = ovl_mount_dir(ufs->config.upperdir, upperpath); + err = ovl_mount_dir(ofs->config.upperdir, upperpath); if (err) goto out; @@ -852,14 +852,14 @@ static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) goto out; } - err = ovl_check_namelen(upperpath, ufs, ufs->config.upperdir); + err = ovl_check_namelen(upperpath, ofs, ofs->config.upperdir); if (err) goto out; err = -EBUSY; if (ovl_inuse_trylock(upperpath->dentry)) { - ufs->upperdir_locked = true; - } else if (ufs->config.index) { + ofs->upperdir_locked = true; + } else if (ofs->config.index) { pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n"); goto out; } else { @@ -875,19 +875,19 @@ static int ovl_get_upper(struct ovl_fs *ufs, struct path *upperpath) /* Don't inherit atime flags */ upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME); - ufs->upper_mnt = upper_mnt; + ofs->upper_mnt = upper_mnt; err = 0; out: return err; } -static int ovl_make_workdir(struct ovl_fs *ufs, struct path *workpath) +static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) { struct dentry *temp; int err; - ufs->workdir = ovl_workdir_create(ufs, OVL_WORKDIR_NAME, false); - if (!ufs->workdir) + ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false); + if (!ofs->workdir) return 0; /* @@ -908,9 +908,9 @@ static int ovl_make_workdir(struct ovl_fs *ufs, struct path *workpath) pr_warn("overlayfs: upper fs needs to support d_type.\n"); /* Check if upper/work fs supports O_TMPFILE */ - temp = ovl_do_tmpfile(ufs->workdir, S_IFREG | 0); - ufs->tmpfile = !IS_ERR(temp); - if (ufs->tmpfile) + temp = ovl_do_tmpfile(ofs->workdir, S_IFREG | 0); + ofs->tmpfile = !IS_ERR(temp); + if (ofs->tmpfile) dput(temp); else pr_warn("overlayfs: upper fs does not support tmpfile.\n"); @@ -918,30 +918,30 @@ static int ovl_make_workdir(struct ovl_fs *ufs, struct path *workpath) /* * Check if upper/work fs supports trusted.overlay.* xattr */ - err = ovl_do_setxattr(ufs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0); + err = ovl_do_setxattr(ofs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0); if (err) { - ufs->noxattr = true; + ofs->noxattr = true; pr_warn("overlayfs: upper fs does not support xattr.\n"); } else { - vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE); + vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE); } /* Check if upper/work fs supports file handles */ - if (ufs->config.index && - !ovl_can_decode_fh(ufs->workdir->d_sb)) { - ufs->config.index = false; + if (ofs->config.index && + !ovl_can_decode_fh(ofs->workdir->d_sb)) { + ofs->config.index = false; pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n"); } return 0; } -static int ovl_get_workdir(struct ovl_fs *ufs, struct path *upperpath) +static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath) { int err; struct path workpath = { }; - err = ovl_mount_dir(ufs->config.workdir, &workpath); + err = ovl_mount_dir(ofs->config.workdir, &workpath); if (err) goto out; @@ -957,16 +957,16 @@ static int ovl_get_workdir(struct ovl_fs *ufs, struct path *upperpath) err = -EBUSY; if (ovl_inuse_trylock(workpath.dentry)) { - ufs->workdir_locked = true; - } else if (ufs->config.index) { + ofs->workdir_locked = true; + } else if (ofs->config.index) { pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); goto out; } else { pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); } - ufs->workbasedir = dget(workpath.dentry); - err = ovl_make_workdir(ufs, &workpath); + ofs->workbasedir = dget(workpath.dentry); + err = ovl_make_workdir(ofs, &workpath); if (err) goto out; @@ -977,7 +977,7 @@ out: return err; } -static int ovl_get_indexdir(struct ovl_fs *ufs, struct ovl_entry *oe, +static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, struct path *upperpath) { int err; @@ -992,38 +992,38 @@ static int ovl_get_indexdir(struct ovl_fs *ufs, struct ovl_entry *oe, goto out; } - ufs->indexdir = ovl_workdir_create(ufs, OVL_INDEXDIR_NAME, true); - if (ufs->indexdir) { + ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true); + if (ofs->indexdir) { /* Verify upper root is index dir origin */ - err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt, + err = ovl_verify_origin(ofs->indexdir, ofs->upper_mnt, upperpath->dentry, true, true); if (err) pr_err("overlayfs: failed to verify index dir origin\n"); /* Cleanup bad/stale/orphan index entries */ if (!err) - err = ovl_indexdir_cleanup(ufs->indexdir, - ufs->upper_mnt, + err = ovl_indexdir_cleanup(ofs->indexdir, + ofs->upper_mnt, oe->lowerstack, oe->numlower); } - if (err || !ufs->indexdir) + if (err || !ofs->indexdir) pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n"); out: return err; } -static int ovl_get_lower_layers(struct ovl_fs *ufs, struct path *stack, +static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, unsigned int numlower) { int err; unsigned int i; err = -ENOMEM; - ufs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), + ofs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), GFP_KERNEL); - if (ufs->lower_layers == NULL) + if (ofs->lower_layers == NULL) goto out; for (i = 0; i < numlower; i++) { struct vfsmount *mnt; @@ -1048,15 +1048,15 @@ static int ovl_get_lower_layers(struct ovl_fs *ufs, struct path *stack, */ mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; - ufs->lower_layers[ufs->numlower].mnt = mnt; - ufs->lower_layers[ufs->numlower].pseudo_dev = dev; - ufs->numlower++; + ofs->lower_layers[ofs->numlower].mnt = mnt; + ofs->lower_layers[ofs->numlower].pseudo_dev = dev; + ofs->numlower++; /* Check if all lower layers are on same sb */ if (i == 0) - ufs->same_sb = mnt->mnt_sb; - else if (ufs->same_sb != mnt->mnt_sb) - ufs->same_sb = NULL; + ofs->same_sb = mnt->mnt_sb; + else if (ofs->same_sb != mnt->mnt_sb) + ofs->same_sb = NULL; } err = 0; out: @@ -1064,7 +1064,7 @@ out: } static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, - struct ovl_fs *ufs) + struct ovl_fs *ofs) { int err; char *lowertmp, *lower; @@ -1074,7 +1074,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, struct ovl_entry *oe; err = -ENOMEM; - lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL); + lowertmp = kstrdup(ofs->config.lowerdir, GFP_KERNEL); if (!lowertmp) goto out_err; @@ -1084,7 +1084,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, pr_err("overlayfs: too many lower directories, limit is %d\n", OVL_MAX_STACK); goto out_err; - } else if (!ufs->config.upperdir && stacklen == 1) { + } else if (!ofs->config.upperdir && stacklen == 1) { pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n"); goto out_err; } @@ -1097,7 +1097,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, err = -EINVAL; lower = lowertmp; for (numlower = 0; numlower < stacklen; numlower++) { - err = ovl_lower_dir(lower, &stack[numlower], ufs, + err = ovl_lower_dir(lower, &stack[numlower], ofs, &sb->s_stack_depth, &remote); if (err) goto out_err; @@ -1112,7 +1112,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, goto out_err; } - err = ovl_get_lower_layers(ufs, stack, numlower); + err = ovl_get_lower_layers(ofs, stack, numlower); if (err) goto out_err; @@ -1123,7 +1123,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, for (i = 0; i < numlower; i++) { oe->lowerstack[i].dentry = dget(stack[i].dentry); - oe->lowerstack[i].layer = &ufs->lower_layers[i]; + oe->lowerstack[i].layer = &ofs->lower_layers[i]; } if (remote) @@ -1149,27 +1149,27 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) struct path upperpath = { }; struct dentry *root_dentry; struct ovl_entry *oe; - struct ovl_fs *ufs; + struct ovl_fs *ofs; struct cred *cred; int err; err = -ENOMEM; - ufs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL); - if (!ufs) + ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL); + if (!ofs) goto out; - ufs->creator_cred = cred = prepare_creds(); + ofs->creator_cred = cred = prepare_creds(); if (!cred) goto out_err; - ufs->config.redirect_dir = ovl_redirect_dir_def; - ufs->config.index = ovl_index_def; - err = ovl_parse_opt((char *) data, &ufs->config); + ofs->config.redirect_dir = ovl_redirect_dir_def; + ofs->config.index = ovl_index_def; + err = ovl_parse_opt((char *) data, &ofs->config); if (err) goto out_err; err = -EINVAL; - if (!ufs->config.lowerdir) { + if (!ofs->config.lowerdir) { if (!silent) pr_err("overlayfs: missing 'lowerdir'\n"); goto out_err; @@ -1177,50 +1177,50 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_stack_depth = 0; sb->s_maxbytes = MAX_LFS_FILESIZE; - if (ufs->config.upperdir) { - if (!ufs->config.workdir) { + if (ofs->config.upperdir) { + if (!ofs->config.workdir) { pr_err("overlayfs: missing 'workdir'\n"); goto out_err; } - err = ovl_get_upper(ufs, &upperpath); + err = ovl_get_upper(ofs, &upperpath); if (err) goto out_err; - err = ovl_get_workdir(ufs, &upperpath); + err = ovl_get_workdir(ofs, &upperpath); if (err) goto out_err; - if (!ufs->workdir) + if (!ofs->workdir) sb->s_flags |= MS_RDONLY; - sb->s_stack_depth = ufs->upper_mnt->mnt_sb->s_stack_depth; - sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran; + sb->s_stack_depth = ofs->upper_mnt->mnt_sb->s_stack_depth; + sb->s_time_gran = ofs->upper_mnt->mnt_sb->s_time_gran; } - oe = ovl_get_lowerstack(sb, ufs); + oe = ovl_get_lowerstack(sb, ofs); err = PTR_ERR(oe); if (IS_ERR(oe)) goto out_err; /* If the upper fs is nonexistent, we mark overlayfs r/o too */ - if (!ufs->upper_mnt) + if (!ofs->upper_mnt) sb->s_flags |= MS_RDONLY; - else if (ufs->upper_mnt->mnt_sb != ufs->same_sb) - ufs->same_sb = NULL; + else if (ofs->upper_mnt->mnt_sb != ofs->same_sb) + ofs->same_sb = NULL; - if (!(ovl_force_readonly(ufs)) && ufs->config.index) { - err = ovl_get_indexdir(ufs, oe, &upperpath); + if (!(ovl_force_readonly(ofs)) && ofs->config.index) { + err = ovl_get_indexdir(ofs, oe, &upperpath); if (err) goto out_free_oe; - if (!ufs->indexdir) + if (!ofs->indexdir) sb->s_flags |= MS_RDONLY; } /* Show index=off/on in /proc/mounts for any of the reasons above */ - if (!ufs->indexdir) - ufs->config.index = false; + if (!ofs->indexdir) + ofs->config.index = false; /* Never override disk quota limits or use reserved space */ cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); @@ -1228,7 +1228,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_op = &ovl_super_operations; sb->s_xattr = ovl_xattr_handlers; - sb->s_fs_info = ufs; + sb->s_fs_info = ofs; sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK; err = -ENOMEM; @@ -1259,7 +1259,7 @@ out_free_oe: kfree(oe); out_err: path_put(&upperpath); - ovl_free_fs(ufs); + ovl_free_fs(ofs); out: return err; } -- cgit From 5455f92b54e516995a9ca45bbf790d3629c27a93 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 1 Nov 2017 15:37:22 -0400 Subject: ovl: Put upperdentry if ovl_check_origin() fails If ovl_check_origin() fails, we should put upperdentry. We have a reference on it by now. So goto out_put_upper instead of out. Fixes: a9d019573e88 ("ovl: lookup non-dir copy-up-origin by file handle") Cc: #4.12 Signed-off-by: Vivek Goyal Signed-off-by: Miklos Szeredi --- fs/overlayfs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 6cc3ece3417d..7d4099143a57 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -641,7 +641,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, err = ovl_check_origin(upperdentry, roe->lowerstack, roe->numlower, &stack, &ctr); if (err) - goto out; + goto out_put_upper; } if (d.redirect) { -- cgit From d9768076068f81687b3dbddbd76b5e108f5d53d0 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 24 Sep 2017 13:00:19 +0300 Subject: ovl: remove unneeded arg from ovl_verify_origin() Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/namei.c | 4 ++-- fs/overlayfs/overlayfs.h | 4 ++-- fs/overlayfs/super.c | 8 +++----- 3 files changed, 7 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 7d4099143a57..625ed8066570 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -348,8 +348,8 @@ static int ovl_verify_origin_fh(struct dentry *dentry, const struct ovl_fh *fh) * * Return 0 on match, -ESTALE on mismatch, < 0 on error. */ -int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, - struct dentry *origin, bool is_upper, bool set) +int ovl_verify_origin(struct dentry *dentry, struct dentry *origin, + bool is_upper, bool set) { struct inode *inode; struct ovl_fh *fh; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index cefe5a97d048..13eab09a6b6f 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -249,8 +249,8 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) /* namei.c */ -int ovl_verify_origin(struct dentry *dentry, struct vfsmount *mnt, - struct dentry *origin, bool is_upper, bool set); +int ovl_verify_origin(struct dentry *dentry, struct dentry *origin, + bool is_upper, bool set); int ovl_verify_index(struct dentry *index, struct ovl_path *lower, unsigned int numlower); int ovl_get_index_name(struct dentry *origin, struct qstr *name); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 3156e908423e..be03578181d2 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -983,9 +983,7 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, int err; /* Verify lower root is upper root origin */ - err = ovl_verify_origin(upperpath->dentry, - oe->lowerstack[0].layer->mnt, - oe->lowerstack[0].dentry, + err = ovl_verify_origin(upperpath->dentry, oe->lowerstack[0].dentry, false, true); if (err) { pr_err("overlayfs: failed to verify upper root origin\n"); @@ -995,8 +993,8 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true); if (ofs->indexdir) { /* Verify upper root is index dir origin */ - err = ovl_verify_origin(ofs->indexdir, ofs->upper_mnt, - upperpath->dentry, true, true); + err = ovl_verify_origin(ofs->indexdir, upperpath->dentry, + true, true); if (err) pr_err("overlayfs: failed to verify index dir origin\n"); -- cgit