From b2dd05f107b11966e26fe52a313b418364cf497b Mon Sep 17 00:00:00 2001 From: Zhihao Cheng Date: Tue, 16 May 2023 22:16:17 +0800 Subject: ovl: let helper ovl_i_path_real() return the realinode Let helper ovl_i_path_real() return the realinode to prepare for checking non-null realinode in RCU walking path. [msz] Use d_inode_rcu() since we are depending on the consitency between dentry and inode being non-NULL in an RCU setting. Signed-off-by: Zhihao Cheng Signed-off-by: Amir Goldstein Fixes: ffa5723c6d25 ("ovl: store lower path in ovl_inode") Cc: # v5.19 Signed-off-by: Miklos Szeredi --- fs/overlayfs/util.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 923d66d131c1..5a6f34c7ed03 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -250,7 +250,7 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode) return ovl_upperdentry_dereference(OVL_I(inode)); } -void ovl_i_path_real(struct inode *inode, struct path *path) +struct inode *ovl_i_path_real(struct inode *inode, struct path *path) { path->dentry = ovl_i_dentry_upper(inode); if (!path->dentry) { @@ -259,6 +259,8 @@ void ovl_i_path_real(struct inode *inode, struct path *path) } else { path->mnt = ovl_upper_mnt(OVL_FS(inode->i_sb)); } + + return path->dentry ? d_inode_rcu(path->dentry) : NULL; } struct inode *ovl_inode_upper(struct inode *inode) @@ -1105,8 +1107,7 @@ void ovl_copyattr(struct inode *inode) vfsuid_t vfsuid; vfsgid_t vfsgid; - ovl_i_path_real(inode, &realpath); - realinode = d_inode(realpath.dentry); + realinode = ovl_i_path_real(inode, &realpath); real_idmap = mnt_idmap(realpath.mnt); vfsuid = i_uid_into_vfsuid(real_idmap, realinode); -- cgit v1.2.3 From b07d5cc93e1b28df47a72c519d09d0a836043613 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 3 Apr 2023 11:29:59 +0300 Subject: ovl: update of dentry revalidate flags after copy up After copy up, we may need to update d_flags if upper dentry is on a remote fs and lower dentries are not. Add helpers to allow incremental update of the revalidate flags. Fixes: bccece1ead36 ("ovl: allow remote upper") Reviewed-by: Gao Xiang Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 2 ++ fs/overlayfs/dir.c | 3 +-- fs/overlayfs/export.c | 3 +-- fs/overlayfs/namei.c | 3 +-- fs/overlayfs/overlayfs.h | 6 ++++-- fs/overlayfs/super.c | 2 +- fs/overlayfs/util.c | 24 ++++++++++++++++++++---- 7 files changed, 30 insertions(+), 13 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index f658cc8ea492..95dce240ba17 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -575,6 +575,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) /* Restore timestamps on parent (best effort) */ ovl_set_timestamps(ofs, upperdir, &c->pstat); ovl_dentry_set_upper_alias(c->dentry); + ovl_dentry_update_reval(c->dentry, upper); } } inode_unlock(udir); @@ -894,6 +895,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) inode_unlock(udir); ovl_dentry_set_upper_alias(c->dentry); + ovl_dentry_update_reval(c->dentry, ovl_dentry_upper(c->dentry)); } out: diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index fc25fb95d5fc..9be52d8013c8 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -269,8 +269,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode, ovl_dir_modified(dentry->d_parent, false); ovl_dentry_set_upper_alias(dentry); - ovl_dentry_update_reval(dentry, newdentry, - DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_reval(dentry, newdentry); if (!hardlink) { /* diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index defd4e231ad2..5c36fb3a7bab 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -326,8 +326,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, if (upper_alias) ovl_dentry_set_upper_alias(dentry); - ovl_dentry_update_reval(dentry, upper, - DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_reval(dentry, upper); return d_instantiate_anon(dentry, inode); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index cfb3420b7df0..100a492d2b2a 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1122,8 +1122,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ovl_set_flag(OVL_UPPERDATA, inode); } - ovl_dentry_update_reval(dentry, upperdentry, - DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_reval(dentry, upperdentry); revert_creds(old_cred); if (origin_path) { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 7398de332527..2b79a9398c13 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -375,8 +375,10 @@ bool ovl_index_all(struct super_block *sb); bool ovl_verify_lower(struct super_block *sb); struct ovl_entry *ovl_alloc_entry(unsigned int numlower); bool ovl_dentry_remote(struct dentry *dentry); -void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *upperdentry, - unsigned int mask); +void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry); +void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry); +void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, + unsigned int mask); bool ovl_dentry_weird(struct dentry *dentry); enum ovl_path_type ovl_path_type(struct dentry *dentry); void ovl_path_upper(struct dentry *dentry, struct path *path); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index f97ad8b40dbb..ae1058fbfb5b 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1877,7 +1877,7 @@ static struct dentry *ovl_get_root(struct super_block *sb, ovl_dentry_set_flag(OVL_E_CONNECTED, root); ovl_set_upperdata(d_inode(root)); ovl_inode_init(d_inode(root), &oip, ino, fsid); - ovl_dentry_update_reval(root, upperdentry, DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_flags(root, upperdentry, DCACHE_OP_WEAK_REVALIDATE); return root; } diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 5a6f34c7ed03..fb12e7fa8548 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -94,14 +94,30 @@ struct ovl_entry *ovl_alloc_entry(unsigned int numlower) return oe; } +#define OVL_D_REVALIDATE (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE) + bool ovl_dentry_remote(struct dentry *dentry) { - return dentry->d_flags & - (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE); + return dentry->d_flags & OVL_D_REVALIDATE; +} + +void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry) +{ + if (!ovl_dentry_remote(realdentry)) + return; + + spin_lock(&dentry->d_lock); + dentry->d_flags |= realdentry->d_flags & OVL_D_REVALIDATE; + spin_unlock(&dentry->d_lock); +} + +void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry) +{ + return ovl_dentry_init_flags(dentry, upperdentry, OVL_D_REVALIDATE); } -void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *upperdentry, - unsigned int mask) +void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, + unsigned int mask) { struct ovl_entry *oe = OVL_E(dentry); unsigned int i, flags = 0; -- cgit v1.2.3 From a6ff2bc0be179eea72832b5396481a08ccd22d5a Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Wed, 15 Mar 2023 04:31:37 +0200 Subject: ovl: use OVL_E() and OVL_E_FLAGS() accessors Instead of open coded instances, because we are about to split the two apart. Reviewed-by: Alexander Larsson Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/export.c | 2 +- fs/overlayfs/namei.c | 8 ++++---- fs/overlayfs/ovl_entry.h | 5 +++++ fs/overlayfs/super.c | 2 +- fs/overlayfs/util.c | 20 ++++++++++---------- 5 files changed, 21 insertions(+), 16 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 5c36fb3a7bab..2cfdfcca5659 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -341,7 +341,7 @@ out_iput: /* Get the upper or lower dentry in stack whose on layer @idx */ static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); int i; if (!idx) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 100a492d2b2a..e66352f19755 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -790,7 +790,7 @@ fail: */ int ovl_path_next(int idx, struct dentry *dentry, struct path *path) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); BUG_ON(idx < 0); if (idx == 0) { @@ -833,8 +833,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, struct ovl_entry *oe; const struct cred *old_cred; 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 ovl_entry *poe = OVL_E(dentry->d_parent); + struct ovl_entry *roe = OVL_E(dentry->d_sb->s_root); struct ovl_path *stack = NULL, *origin_path = NULL; struct dentry *upperdir, *upperdentry = NULL; struct dentry *origin = NULL; @@ -1157,7 +1157,7 @@ out: bool ovl_lower_positive(struct dentry *dentry) { - struct ovl_entry *poe = dentry->d_parent->d_fsdata; + struct ovl_entry *poe = OVL_E(dentry->d_parent); const struct qstr *name = &dentry->d_name; const struct cred *old_cred; unsigned int i; diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index fd11fe6d6d45..4c7312126b3b 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -124,6 +124,11 @@ static inline struct ovl_entry *OVL_E(struct dentry *dentry) return (struct ovl_entry *) dentry->d_fsdata; } +static inline unsigned long *OVL_E_FLAGS(struct dentry *dentry) +{ + return &OVL_E(dentry)->flags; +} + struct ovl_inode { union { struct ovl_dir_cache *cache; /* directory */ diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index ae1058fbfb5b..ea34edd9d624 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -138,7 +138,7 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak) static int ovl_dentry_revalidate_common(struct dentry *dentry, unsigned int flags, bool weak) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); struct inode *inode = d_inode_rcu(dentry); struct dentry *upper; unsigned int i; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index fb12e7fa8548..73a5316d7ed5 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -143,7 +143,7 @@ bool ovl_dentry_weird(struct dentry *dentry) enum ovl_path_type ovl_path_type(struct dentry *dentry) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); enum ovl_path_type type = 0; if (ovl_dentry_upper(dentry)) { @@ -176,7 +176,7 @@ void ovl_path_upper(struct dentry *dentry, struct path *path) void ovl_path_lower(struct dentry *dentry, struct path *path) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); if (oe->numlower) { path->mnt = oe->lowerstack[0].layer->mnt; @@ -188,7 +188,7 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) void ovl_path_lowerdata(struct dentry *dentry, struct path *path) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); if (oe->numlower) { path->mnt = oe->lowerstack[oe->numlower - 1].layer->mnt; @@ -231,14 +231,14 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry) struct dentry *ovl_dentry_lower(struct dentry *dentry) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); return oe->numlower ? oe->lowerstack[0].dentry : NULL; } const struct ovl_layer *ovl_layer_lower(struct dentry *dentry) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); return oe->numlower ? oe->lowerstack[0].layer : NULL; } @@ -251,7 +251,7 @@ const struct ovl_layer *ovl_layer_lower(struct dentry *dentry) */ struct dentry *ovl_dentry_lowerdata(struct dentry *dentry) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); return oe->numlower ? oe->lowerstack[oe->numlower - 1].dentry : NULL; } @@ -331,17 +331,17 @@ void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache) void ovl_dentry_set_flag(unsigned long flag, struct dentry *dentry) { - set_bit(flag, &OVL_E(dentry)->flags); + set_bit(flag, OVL_E_FLAGS(dentry)); } void ovl_dentry_clear_flag(unsigned long flag, struct dentry *dentry) { - clear_bit(flag, &OVL_E(dentry)->flags); + clear_bit(flag, OVL_E_FLAGS(dentry)); } bool ovl_dentry_test_flag(unsigned long flag, struct dentry *dentry) { - return test_bit(flag, &OVL_E(dentry)->flags); + return test_bit(flag, OVL_E_FLAGS(dentry)); } bool ovl_dentry_is_opaque(struct dentry *dentry) @@ -1017,7 +1017,7 @@ out: bool ovl_is_metacopy_dentry(struct dentry *dentry) { - struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *oe = OVL_E(dentry); if (!d_is_reg(dentry)) return false; -- cgit v1.2.3 From 5522c9c7cbd2ab4e886fa1c70896f0bdd483ce0b Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 3 Apr 2023 11:51:47 +0300 Subject: ovl: use ovl_numlower() and ovl_lowerstack() accessors This helps fortify against dereferencing a NULL ovl_entry, before we move the ovl_entry reference into ovl_inode. Reviewed-by: Alexander Larsson Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/export.c | 18 ++++++++++-------- fs/overlayfs/namei.c | 34 ++++++++++++++++++---------------- fs/overlayfs/ovl_entry.h | 14 ++++++++++++-- fs/overlayfs/super.c | 23 +++++++++++++---------- fs/overlayfs/util.c | 36 ++++++++++++++++++++---------------- 5 files changed, 73 insertions(+), 52 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 2cfdfcca5659..ddb546627749 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -80,7 +80,7 @@ static int ovl_connectable_layer(struct dentry *dentry) /* We can get overlay root from root of any layer */ if (dentry == dentry->d_sb->s_root) - return oe->numlower; + return ovl_numlower(oe); /* * If it's an unindexed merge dir, then it's not connectable with any @@ -91,7 +91,7 @@ static int ovl_connectable_layer(struct dentry *dentry) return 0; /* We can get upper/overlay path from indexed/lower dentry */ - return oe->lowerstack[0].layer->idx; + return ovl_lowerstack(oe)->layer->idx; } /* @@ -105,6 +105,7 @@ static int ovl_connectable_layer(struct dentry *dentry) static int ovl_connect_layer(struct dentry *dentry) { struct dentry *next, *parent = NULL; + struct ovl_entry *oe = OVL_E(dentry); int origin_layer; int err = 0; @@ -112,7 +113,7 @@ static int ovl_connect_layer(struct dentry *dentry) WARN_ON(!ovl_dentry_lower(dentry))) return -EIO; - origin_layer = OVL_E(dentry)->lowerstack[0].layer->idx; + origin_layer = ovl_lowerstack(oe)->layer->idx; if (ovl_dentry_test_flag(OVL_E_CONNECTED, dentry)) return origin_layer; @@ -319,8 +320,8 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, goto nomem; if (lower) { - oe->lowerstack->dentry = dget(lower); - oe->lowerstack->layer = lowerpath->layer; + ovl_lowerstack(oe)->dentry = dget(lower); + ovl_lowerstack(oe)->layer = lowerpath->layer; } dentry->d_fsdata = oe; if (upper_alias) @@ -342,14 +343,15 @@ out_iput: static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx) { struct ovl_entry *oe = OVL_E(dentry); + struct ovl_path *lowerstack = ovl_lowerstack(oe); int i; if (!idx) return ovl_dentry_upper(dentry); - for (i = 0; i < oe->numlower; i++) { - if (oe->lowerstack[i].layer->idx == idx) - return oe->lowerstack[i].dentry; + for (i = 0; i < ovl_numlower(oe); i++) { + if (lowerstack[i].layer->idx == idx) + return lowerstack[i].dentry; } return NULL; diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index e66352f19755..31f889d27083 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -791,19 +791,20 @@ fail: int ovl_path_next(int idx, struct dentry *dentry, struct path *path) { struct ovl_entry *oe = OVL_E(dentry); + struct ovl_path *lowerstack = ovl_lowerstack(oe); BUG_ON(idx < 0); if (idx == 0) { ovl_path_upper(dentry, path); if (path->dentry) - return oe->numlower ? 1 : -1; + return ovl_numlower(oe) ? 1 : -1; idx++; } - BUG_ON(idx > oe->numlower); - path->dentry = oe->lowerstack[idx - 1].dentry; - path->mnt = oe->lowerstack[idx - 1].layer->mnt; + BUG_ON(idx > ovl_numlower(oe)); + path->dentry = lowerstack[idx - 1].dentry; + path->mnt = lowerstack[idx - 1].layer->mnt; - return (idx < oe->numlower) ? idx + 1 : -1; + return (idx < ovl_numlower(oe)) ? idx + 1 : -1; } /* Fix missing 'origin' xattr */ @@ -853,7 +854,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .is_dir = false, .opaque = false, .stop = false, - .last = ofs->config.redirect_follow ? false : !poe->numlower, + .last = ofs->config.redirect_follow ? false : !ovl_numlower(poe), .redirect = NULL, .metacopy = false, }; @@ -904,7 +905,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, upperopaque = d.opaque; } - if (!d.stop && poe->numlower) { + if (!d.stop && ovl_numlower(poe)) { err = -ENOMEM; stack = kcalloc(ofs->numlayer - 1, sizeof(struct ovl_path), GFP_KERNEL); @@ -912,13 +913,13 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, goto out_put_upper; } - for (i = 0; !d.stop && i < poe->numlower; i++) { - struct ovl_path lower = poe->lowerstack[i]; + for (i = 0; !d.stop && i < ovl_numlower(poe); i++) { + struct ovl_path lower = ovl_lowerstack(poe)[i]; if (!ofs->config.redirect_follow) - d.last = i == poe->numlower - 1; + d.last = i == ovl_numlower(poe) - 1; else - d.last = lower.layer->idx == roe->numlower; + d.last = lower.layer->idx == ovl_numlower(roe); d.mnt = lower.layer->mnt; err = ovl_lookup_layer(lower.dentry, &d, &this, false); @@ -1072,7 +1073,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (!oe) goto out_put; - memcpy(oe->lowerstack, stack, sizeof(struct ovl_path) * ctr); + memcpy(ovl_lowerstack(oe), stack, sizeof(struct ovl_path) * ctr); dentry->d_fsdata = oe; if (upperopaque) @@ -1177,12 +1178,13 @@ bool ovl_lower_positive(struct dentry *dentry) old_cred = ovl_override_creds(dentry->d_sb); /* Positive upper -> have to look up lower to see whether it exists */ - for (i = 0; !done && !positive && i < poe->numlower; i++) { + for (i = 0; !done && !positive && i < ovl_numlower(poe); i++) { struct dentry *this; - struct dentry *lowerdir = poe->lowerstack[i].dentry; + struct ovl_path *parentpath = &ovl_lowerstack(poe)[i]; - this = lookup_one_positive_unlocked(mnt_idmap(poe->lowerstack[i].layer->mnt), - name->name, lowerdir, name->len); + this = lookup_one_positive_unlocked( + mnt_idmap(parentpath->layer->mnt), + name->name, parentpath->dentry, name->len); if (IS_ERR(this)) { switch (PTR_ERR(this)) { case -ENOENT: diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 4c7312126b3b..0e009fa39d54 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -113,10 +113,20 @@ struct ovl_entry { }; struct rcu_head rcu; }; - unsigned numlower; - struct ovl_path lowerstack[]; + unsigned int __numlower; + struct ovl_path __lowerstack[]; }; +static inline unsigned int ovl_numlower(struct ovl_entry *oe) +{ + return oe ? oe->__numlower : 0; +} + +static inline struct ovl_path *ovl_lowerstack(struct ovl_entry *oe) +{ + return ovl_numlower(oe) ? oe->__lowerstack : NULL; +} + struct ovl_entry *ovl_alloc_entry(unsigned int numlower); static inline struct ovl_entry *OVL_E(struct dentry *dentry) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index ea34edd9d624..baa970a2b398 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -56,10 +56,11 @@ MODULE_PARM_DESC(xino_auto, static void ovl_entry_stack_free(struct ovl_entry *oe) { + struct ovl_path *lowerstack = ovl_lowerstack(oe); unsigned int i; - for (i = 0; i < oe->numlower; i++) - dput(oe->lowerstack[i].dentry); + for (i = 0; i < ovl_numlower(oe); i++) + dput(lowerstack[i].dentry); } static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY); @@ -139,6 +140,7 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry, unsigned int flags, bool weak) { struct ovl_entry *oe = OVL_E(dentry); + struct ovl_path *lowerstack = ovl_lowerstack(oe); struct inode *inode = d_inode_rcu(dentry); struct dentry *upper; unsigned int i; @@ -152,10 +154,9 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry, if (upper) ret = ovl_revalidate_real(upper, flags, weak); - for (i = 0; ret > 0 && i < oe->numlower; i++) { - ret = ovl_revalidate_real(oe->lowerstack[i].dentry, flags, - weak); - } + for (i = 0; ret > 0 && i < ovl_numlower(oe); i++) + ret = ovl_revalidate_real(lowerstack[i].dentry, flags, weak); + return ret; } @@ -1454,7 +1455,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, /* Verify lower root is upper root origin */ err = ovl_verify_origin(ofs, upperpath->dentry, - oe->lowerstack[0].dentry, true); + ovl_lowerstack(oe)->dentry, true); if (err) { pr_err("failed to verify upper root origin\n"); goto out; @@ -1717,6 +1718,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, { int err; struct path *stack = NULL; + struct ovl_path *lowerstack; unsigned int i; struct ovl_entry *oe; @@ -1754,9 +1756,10 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, if (!oe) goto out_err; + lowerstack = ovl_lowerstack(oe); for (i = 0; i < numlower; i++) { - oe->lowerstack[i].dentry = dget(stack[i].dentry); - oe->lowerstack[i].layer = &ofs->layers[i+1]; + lowerstack[i].dentry = dget(stack[i].dentry); + lowerstack[i].layer = &ofs->layers[i+1]; } out: @@ -1849,7 +1852,7 @@ static struct dentry *ovl_get_root(struct super_block *sb, struct ovl_entry *oe) { struct dentry *root; - struct ovl_path *lowerpath = &oe->lowerstack[0]; + struct ovl_path *lowerpath = ovl_lowerstack(oe); unsigned long ino = d_inode(lowerpath->dentry)->i_ino; int fsid = lowerpath->layer->fsid; struct ovl_inode_params oip = { diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 73a5316d7ed5..a146c7ab8d8e 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -85,11 +85,11 @@ bool ovl_verify_lower(struct super_block *sb) struct ovl_entry *ovl_alloc_entry(unsigned int numlower) { - size_t size = offsetof(struct ovl_entry, lowerstack[numlower]); + size_t size = offsetof(struct ovl_entry, __lowerstack[numlower]); struct ovl_entry *oe = kzalloc(size, GFP_KERNEL); if (oe) - oe->numlower = numlower; + oe->__numlower = numlower; return oe; } @@ -120,12 +120,13 @@ void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, unsigned int mask) { struct ovl_entry *oe = OVL_E(dentry); + struct ovl_path *lowerstack = ovl_lowerstack(oe); unsigned int i, flags = 0; if (upperdentry) flags |= upperdentry->d_flags; - for (i = 0; i < oe->numlower; i++) - flags |= oe->lowerstack[i].dentry->d_flags; + for (i = 0; i < ovl_numlower(oe); i++) + flags |= lowerstack[i].dentry->d_flags; spin_lock(&dentry->d_lock); dentry->d_flags &= ~mask; @@ -152,7 +153,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry) /* * Non-dir dentry can hold lower dentry of its copy up origin. */ - if (oe->numlower) { + if (ovl_numlower(oe)) { if (ovl_test_flag(OVL_CONST_INO, d_inode(dentry))) type |= __OVL_PATH_ORIGIN; if (d_is_dir(dentry) || @@ -160,7 +161,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry) type |= __OVL_PATH_MERGE; } } else { - if (oe->numlower > 1) + if (ovl_numlower(oe) > 1) type |= __OVL_PATH_MERGE; } return type; @@ -177,10 +178,11 @@ void ovl_path_upper(struct dentry *dentry, struct path *path) void ovl_path_lower(struct dentry *dentry, struct path *path) { struct ovl_entry *oe = OVL_E(dentry); + struct ovl_path *lowerpath = ovl_lowerstack(oe); - if (oe->numlower) { - path->mnt = oe->lowerstack[0].layer->mnt; - path->dentry = oe->lowerstack[0].dentry; + if (ovl_numlower(oe)) { + path->mnt = lowerpath->layer->mnt; + path->dentry = lowerpath->dentry; } else { *path = (struct path) { }; } @@ -189,10 +191,11 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) void ovl_path_lowerdata(struct dentry *dentry, struct path *path) { struct ovl_entry *oe = OVL_E(dentry); + struct ovl_path *lowerstack = ovl_lowerstack(oe); - if (oe->numlower) { - path->mnt = oe->lowerstack[oe->numlower - 1].layer->mnt; - path->dentry = oe->lowerstack[oe->numlower - 1].dentry; + if (ovl_numlower(oe)) { + path->mnt = lowerstack[ovl_numlower(oe) - 1].layer->mnt; + path->dentry = lowerstack[ovl_numlower(oe) - 1].dentry; } else { *path = (struct path) { }; } @@ -233,14 +236,14 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry) { struct ovl_entry *oe = OVL_E(dentry); - return oe->numlower ? oe->lowerstack[0].dentry : NULL; + return ovl_numlower(oe) ? ovl_lowerstack(oe)->dentry : NULL; } const struct ovl_layer *ovl_layer_lower(struct dentry *dentry) { struct ovl_entry *oe = OVL_E(dentry); - return oe->numlower ? oe->lowerstack[0].layer : NULL; + return ovl_numlower(oe) ? ovl_lowerstack(oe)->layer : NULL; } /* @@ -253,7 +256,8 @@ struct dentry *ovl_dentry_lowerdata(struct dentry *dentry) { struct ovl_entry *oe = OVL_E(dentry); - return oe->numlower ? oe->lowerstack[oe->numlower - 1].dentry : NULL; + return ovl_numlower(oe) ? + ovl_lowerstack(oe)[ovl_numlower(oe) - 1].dentry : NULL; } struct dentry *ovl_dentry_real(struct dentry *dentry) @@ -1028,7 +1032,7 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry) return false; } - return (oe->numlower > 1); + return (ovl_numlower(oe) > 1); } char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding) -- cgit v1.2.3 From 163db0da3515d1d32343906a2fb7854492191d19 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 3 Apr 2023 20:36:16 +0300 Subject: ovl: factor out ovl_free_entry() and ovl_stack_*() helpers In preparation for moving lowerstack into ovl_inode. Note that in ovl_lookup() the temp stack dentry refs are now cloned into the final ovl_lowerstack instead of being transferred, so cleanup always needs to call ovl_stack_free(stack). Reviewed-by: Alexander Larsson Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/namei.c | 13 +++++-------- fs/overlayfs/overlayfs.h | 5 +++++ fs/overlayfs/ovl_entry.h | 2 -- fs/overlayfs/super.c | 14 ++------------ fs/overlayfs/util.c | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 22 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 31f889d27083..c237c8dbff09 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -907,8 +907,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (!d.stop && ovl_numlower(poe)) { err = -ENOMEM; - stack = kcalloc(ofs->numlayer - 1, sizeof(struct ovl_path), - GFP_KERNEL); + stack = ovl_stack_alloc(ofs->numlayer - 1); if (!stack) goto out_put_upper; } @@ -1073,7 +1072,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (!oe) goto out_put; - memcpy(ovl_lowerstack(oe), stack, sizeof(struct ovl_path) * ctr); + ovl_stack_cpy(ovl_lowerstack(oe), stack, ctr); dentry->d_fsdata = oe; if (upperopaque) @@ -1131,18 +1130,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, kfree(origin_path); } dput(index); - kfree(stack); + ovl_stack_free(stack, ctr); kfree(d.redirect); return d_splice_alias(inode, dentry); out_free_oe: dentry->d_fsdata = NULL; - kfree(oe); + ovl_free_entry(oe); out_put: dput(index); - for (i = 0; i < ctr; i++) - dput(stack[i].dentry); - kfree(stack); + ovl_stack_free(stack, ctr); out_put_upper: if (origin_path) { dput(origin_path->dentry); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 2b79a9398c13..4f83509a0294 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -373,7 +373,12 @@ int ovl_can_decode_fh(struct super_block *sb); struct dentry *ovl_indexdir(struct super_block *sb); bool ovl_index_all(struct super_block *sb); bool ovl_verify_lower(struct super_block *sb); +struct ovl_path *ovl_stack_alloc(unsigned int n); +void ovl_stack_cpy(struct ovl_path *dst, struct ovl_path *src, unsigned int n); +void ovl_stack_put(struct ovl_path *stack, unsigned int n); +void ovl_stack_free(struct ovl_path *stack, unsigned int n); struct ovl_entry *ovl_alloc_entry(unsigned int numlower); +void ovl_free_entry(struct ovl_entry *oe); bool ovl_dentry_remote(struct dentry *dentry); void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry); void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 0e009fa39d54..608742f60037 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -127,8 +127,6 @@ static inline struct ovl_path *ovl_lowerstack(struct ovl_entry *oe) return ovl_numlower(oe) ? oe->__lowerstack : NULL; } -struct ovl_entry *ovl_alloc_entry(unsigned int numlower); - static inline struct ovl_entry *OVL_E(struct dentry *dentry) { return (struct ovl_entry *) dentry->d_fsdata; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index baa970a2b398..35ecea4b60b0 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -54,15 +54,6 @@ module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644); MODULE_PARM_DESC(xino_auto, "Auto enable xino feature"); -static void ovl_entry_stack_free(struct ovl_entry *oe) -{ - struct ovl_path *lowerstack = ovl_lowerstack(oe); - unsigned int i; - - for (i = 0; i < ovl_numlower(oe); i++) - dput(lowerstack[i].dentry); -} - static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY); module_param_named(metacopy, ovl_metacopy_def, bool, 0644); MODULE_PARM_DESC(metacopy, @@ -73,7 +64,7 @@ static void ovl_dentry_release(struct dentry *dentry) struct ovl_entry *oe = dentry->d_fsdata; if (oe) { - ovl_entry_stack_free(oe); + ovl_stack_put(ovl_lowerstack(oe), ovl_numlower(oe)); kfree_rcu(oe, rcu); } } @@ -2070,8 +2061,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) return 0; out_free_oe: - ovl_entry_stack_free(oe); - kfree(oe); + ovl_free_entry(oe); out_err: kfree(splitlower); path_put(&upperpath); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index a146c7ab8d8e..4ff58f92100d 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -83,6 +83,34 @@ bool ovl_verify_lower(struct super_block *sb) return ofs->config.nfs_export && ofs->config.index; } +struct ovl_path *ovl_stack_alloc(unsigned int n) +{ + return kcalloc(n, sizeof(struct ovl_path), GFP_KERNEL); +} + +void ovl_stack_cpy(struct ovl_path *dst, struct ovl_path *src, unsigned int n) +{ + unsigned int i; + + memcpy(dst, src, sizeof(struct ovl_path) * n); + for (i = 0; i < n; i++) + dget(src[i].dentry); +} + +void ovl_stack_put(struct ovl_path *stack, unsigned int n) +{ + unsigned int i; + + for (i = 0; stack && i < n; i++) + dput(stack[i].dentry); +} + +void ovl_stack_free(struct ovl_path *stack, unsigned int n) +{ + ovl_stack_put(stack, n); + kfree(stack); +} + struct ovl_entry *ovl_alloc_entry(unsigned int numlower) { size_t size = offsetof(struct ovl_entry, __lowerstack[numlower]); @@ -94,6 +122,12 @@ struct ovl_entry *ovl_alloc_entry(unsigned int numlower) return oe; } +void ovl_free_entry(struct ovl_entry *oe) +{ + ovl_stack_put(ovl_lowerstack(oe), ovl_numlower(oe)); + kfree(oe); +} + #define OVL_D_REVALIDATE (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE) bool ovl_dentry_remote(struct dentry *dentry) -- cgit v1.2.3 From 0af950f57fefabab628f1963af881e6b9bfe7f38 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sat, 8 Apr 2023 12:31:13 +0300 Subject: ovl: move ovl_entry into ovl_inode The lower stacks of all the ovl inode aliases should be identical and there is redundant information in ovl_entry and ovl_inode. Move lowerstack into ovl_inode and keep only the OVL_E_FLAGS per overlay dentry. Following patches will deduplicate redundant ovl_inode fields. Note that for pure upper and negative dentries, OVL_E(dentry) may be NULL now, so it is imporatnt to use the ovl_numlower() accessor. Reviewed-by: Alexander Larsson Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/dir.c | 2 +- fs/overlayfs/export.c | 22 ++++++++++++---------- fs/overlayfs/inode.c | 8 ++++---- fs/overlayfs/namei.c | 19 ++++++++++--------- fs/overlayfs/overlayfs.h | 6 ++++-- fs/overlayfs/ovl_entry.h | 36 ++++++++++++++++++------------------ fs/overlayfs/super.c | 18 ++++-------------- fs/overlayfs/util.c | 8 ++++---- 8 files changed, 57 insertions(+), 62 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 9be52d8013c8..92bdcedfaaec 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -269,7 +269,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode, ovl_dir_modified(dentry->d_parent, false); ovl_dentry_set_upper_alias(dentry); - ovl_dentry_init_reval(dentry, newdentry); + ovl_dentry_init_reval(dentry, newdentry, NULL); if (!hardlink) { /* diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index ddb546627749..be142ea73fad 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -286,7 +286,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, struct dentry *lower = lowerpath ? lowerpath->dentry : NULL; struct dentry *upper = upper_alias ?: index; struct dentry *dentry; - struct inode *inode; + struct inode *inode = NULL; struct ovl_entry *oe; struct ovl_inode_params oip = { .lowerpath = lowerpath, @@ -298,9 +298,19 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, if (d_is_dir(upper ?: lower)) return ERR_PTR(-EIO); + oe = ovl_alloc_entry(!!lower); + if (!oe) + return ERR_PTR(-ENOMEM); + oip.upperdentry = dget(upper); + if (lower) { + ovl_lowerstack(oe)->dentry = dget(lower); + ovl_lowerstack(oe)->layer = lowerpath->layer; + } + oip.oe = oe; inode = ovl_get_inode(sb, &oip); if (IS_ERR(inode)) { + ovl_free_entry(oe); dput(upper); return ERR_CAST(inode); } @@ -315,19 +325,11 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, dentry = d_alloc_anon(inode->i_sb); if (unlikely(!dentry)) goto nomem; - oe = ovl_alloc_entry(lower ? 1 : 0); - if (!oe) - goto nomem; - if (lower) { - ovl_lowerstack(oe)->dentry = dget(lower); - ovl_lowerstack(oe)->layer = lowerpath->layer; - } - dentry->d_fsdata = oe; if (upper_alias) ovl_dentry_set_upper_alias(dentry); - ovl_dentry_init_reval(dentry, upper); + ovl_dentry_init_reval(dentry, upper, OVL_I_E(inode)); return d_instantiate_anon(dentry, inode); diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index e7e888dea634..995135ffec98 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -1002,8 +1002,9 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, struct inode *realinode; struct ovl_inode *oi = OVL_I(inode); - if (oip->upperdentry) - oi->__upperdentry = oip->upperdentry; + oi->__upperdentry = oip->upperdentry; + oi->oe = oip->oe; + oi->redirect = oip->redirect; if (oip->lowerpath && oip->lowerpath->dentry) { oi->lowerpath.dentry = dget(oip->lowerpath->dentry); oi->lowerpath.layer = oip->lowerpath->layer; @@ -1368,6 +1369,7 @@ struct inode *ovl_get_inode(struct super_block *sb, } dput(upperdentry); + ovl_free_entry(oip->oe); kfree(oip->redirect); goto out; } @@ -1397,8 +1399,6 @@ struct inode *ovl_get_inode(struct super_block *sb, if (oip->index) ovl_set_flag(OVL_INDEX, inode); - OVL_I(inode)->redirect = oip->redirect; - if (bylower) ovl_set_flag(OVL_CONST_INO, inode); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index c237c8dbff09..a7d975bf9e48 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -831,7 +831,7 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry, struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { - struct ovl_entry *oe; + struct ovl_entry *oe = NULL; const struct cred *old_cred; struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_entry *poe = OVL_E(dentry->d_parent); @@ -1067,13 +1067,14 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } } - oe = ovl_alloc_entry(ctr); - err = -ENOMEM; - if (!oe) - goto out_put; + if (ctr) { + oe = ovl_alloc_entry(ctr); + err = -ENOMEM; + if (!oe) + goto out_put; - ovl_stack_cpy(ovl_lowerstack(oe), stack, ctr); - dentry->d_fsdata = oe; + ovl_stack_cpy(ovl_lowerstack(oe), stack, ctr); + } if (upperopaque) ovl_dentry_set_opaque(dentry); @@ -1107,6 +1108,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, struct ovl_inode_params oip = { .upperdentry = upperdentry, .lowerpath = stack, + .oe = oe, .index = index, .numlower = ctr, .redirect = upperredirect, @@ -1122,7 +1124,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ovl_set_flag(OVL_UPPERDATA, inode); } - ovl_dentry_init_reval(dentry, upperdentry); + ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode)); revert_creds(old_cred); if (origin_path) { @@ -1135,7 +1137,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, return d_splice_alias(inode, dentry); out_free_oe: - dentry->d_fsdata = NULL; ovl_free_entry(oe); out_put: dput(index); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 4f83509a0294..cd74d7466a58 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -381,9 +381,10 @@ struct ovl_entry *ovl_alloc_entry(unsigned int numlower); void ovl_free_entry(struct ovl_entry *oe); bool ovl_dentry_remote(struct dentry *dentry); void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry); -void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry); +void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry, + struct ovl_entry *oe); void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, - unsigned int mask); + struct ovl_entry *oe, unsigned int mask); bool ovl_dentry_weird(struct dentry *dentry); enum ovl_path_type ovl_path_type(struct dentry *dentry); void ovl_path_upper(struct dentry *dentry, struct path *path); @@ -653,6 +654,7 @@ struct ovl_inode_params { struct inode *newinode; struct dentry *upperdentry; struct ovl_path *lowerpath; + struct ovl_entry *oe; bool index; unsigned int numlower; char *redirect; diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 608742f60037..f511ac78c5bd 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -47,6 +47,11 @@ struct ovl_path { struct dentry *dentry; }; +struct ovl_entry { + unsigned int __numlower; + struct ovl_path __lowerstack[]; +}; + /* private information held for overlayfs's superblock */ struct ovl_fs { unsigned int numlayer; @@ -105,18 +110,6 @@ static inline bool ovl_should_sync(struct ovl_fs *ofs) return !ofs->config.ovl_volatile; } -/* private information held for every overlayfs dentry */ -struct ovl_entry { - union { - struct { - unsigned long flags; - }; - struct rcu_head rcu; - }; - unsigned int __numlower; - struct ovl_path __lowerstack[]; -}; - static inline unsigned int ovl_numlower(struct ovl_entry *oe) { return oe ? oe->__numlower : 0; @@ -127,14 +120,10 @@ static inline struct ovl_path *ovl_lowerstack(struct ovl_entry *oe) return ovl_numlower(oe) ? oe->__lowerstack : NULL; } -static inline struct ovl_entry *OVL_E(struct dentry *dentry) -{ - return (struct ovl_entry *) dentry->d_fsdata; -} - +/* private information held for every overlayfs dentry */ static inline unsigned long *OVL_E_FLAGS(struct dentry *dentry) { - return &OVL_E(dentry)->flags; + return (unsigned long *) &dentry->d_fsdata; } struct ovl_inode { @@ -148,6 +137,7 @@ struct ovl_inode { struct inode vfs_inode; struct dentry *__upperdentry; struct ovl_path lowerpath; + struct ovl_entry *oe; /* synchronize copy up and more */ struct mutex lock; @@ -158,6 +148,16 @@ static inline struct ovl_inode *OVL_I(struct inode *inode) return container_of(inode, struct ovl_inode, vfs_inode); } +static inline struct ovl_entry *OVL_I_E(struct inode *inode) +{ + return inode ? OVL_I(inode)->oe : NULL; +} + +static inline struct ovl_entry *OVL_E(struct dentry *dentry) +{ + return OVL_I_E(d_inode(dentry)); +} + static inline struct dentry *ovl_upperdentry_dereference(struct ovl_inode *oi) { return READ_ONCE(oi->__upperdentry); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 35ecea4b60b0..bb4a062ab0d5 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -59,16 +59,6 @@ module_param_named(metacopy, ovl_metacopy_def, bool, 0644); MODULE_PARM_DESC(metacopy, "Default to on or off for the metadata only copy up feature"); -static void ovl_dentry_release(struct dentry *dentry) -{ - struct ovl_entry *oe = dentry->d_fsdata; - - if (oe) { - ovl_stack_put(ovl_lowerstack(oe), ovl_numlower(oe)); - kfree_rcu(oe, rcu); - } -} - static struct dentry *ovl_d_real(struct dentry *dentry, const struct inode *inode) { @@ -162,7 +152,6 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags) } static const struct dentry_operations ovl_dentry_operations = { - .d_release = ovl_dentry_release, .d_real = ovl_d_real, .d_revalidate = ovl_dentry_revalidate, .d_weak_revalidate = ovl_dentry_weak_revalidate, @@ -182,6 +171,7 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) oi->version = 0; oi->flags = 0; oi->__upperdentry = NULL; + oi->oe = NULL; oi->lowerpath.dentry = NULL; oi->lowerpath.layer = NULL; oi->lowerdata = NULL; @@ -205,6 +195,7 @@ static void ovl_destroy_inode(struct inode *inode) dput(oi->__upperdentry); dput(oi->lowerpath.dentry); + ovl_free_entry(oi->oe); if (S_ISDIR(inode->i_mode)) ovl_dir_cache_free(inode); else @@ -1849,14 +1840,13 @@ static struct dentry *ovl_get_root(struct super_block *sb, struct ovl_inode_params oip = { .upperdentry = upperdentry, .lowerpath = lowerpath, + .oe = oe, }; root = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); if (!root) return NULL; - root->d_fsdata = oe; - if (upperdentry) { /* Root inode uses upper st_ino/i_ino */ ino = d_inode(upperdentry)->i_ino; @@ -1871,7 +1861,7 @@ static struct dentry *ovl_get_root(struct super_block *sb, ovl_dentry_set_flag(OVL_E_CONNECTED, root); ovl_set_upperdata(d_inode(root)); ovl_inode_init(d_inode(root), &oip, ino, fsid); - ovl_dentry_init_flags(root, upperdentry, DCACHE_OP_WEAK_REVALIDATE); + ovl_dentry_init_flags(root, upperdentry, oe, DCACHE_OP_WEAK_REVALIDATE); return root; } diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 4ff58f92100d..c4bd901f3bda 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -145,15 +145,15 @@ void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry) spin_unlock(&dentry->d_lock); } -void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry) +void ovl_dentry_init_reval(struct dentry *dentry, struct dentry *upperdentry, + struct ovl_entry *oe) { - return ovl_dentry_init_flags(dentry, upperdentry, OVL_D_REVALIDATE); + return ovl_dentry_init_flags(dentry, upperdentry, oe, OVL_D_REVALIDATE); } void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, - unsigned int mask) + struct ovl_entry *oe, unsigned int mask) { - struct ovl_entry *oe = OVL_E(dentry); struct ovl_path *lowerstack = ovl_lowerstack(oe); unsigned int i, flags = 0; -- cgit v1.2.3 From ac900ed4f253434e099a025fc1da3d04bc741bd3 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sat, 1 Apr 2023 10:29:19 +0300 Subject: ovl: deduplicate lowerpath and lowerstack[] The ovl_inode contains a copy of lowerpath in lowerstack[0], so the lowerpath member can be removed. Use accessor ovl_lowerpath() to get the lowerpath whereever the member was accessed directly. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/export.c | 2 -- fs/overlayfs/inode.c | 8 ++------ fs/overlayfs/namei.c | 2 -- fs/overlayfs/overlayfs.h | 2 -- fs/overlayfs/ovl_entry.h | 6 +++++- fs/overlayfs/super.c | 4 ---- fs/overlayfs/util.c | 10 ++++++---- 7 files changed, 13 insertions(+), 21 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index be142ea73fad..35680b6e175b 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -289,9 +289,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, struct inode *inode = NULL; struct ovl_entry *oe; struct ovl_inode_params oip = { - .lowerpath = lowerpath, .index = index, - .numlower = !!lower }; /* We get overlay directory dentries with ovl_lookup_real() */ diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 995135ffec98..4dbf8d880c0f 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -1005,10 +1005,6 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, oi->__upperdentry = oip->upperdentry; oi->oe = oip->oe; oi->redirect = oip->redirect; - if (oip->lowerpath && oip->lowerpath->dentry) { - oi->lowerpath.dentry = dget(oip->lowerpath->dentry); - oi->lowerpath.layer = oip->lowerpath->layer; - } if (oip->lowerdata) oi->lowerdata = igrab(d_inode(oip->lowerdata)); @@ -1325,7 +1321,7 @@ struct inode *ovl_get_inode(struct super_block *sb, { struct ovl_fs *ofs = OVL_FS(sb); struct dentry *upperdentry = oip->upperdentry; - struct ovl_path *lowerpath = oip->lowerpath; + struct ovl_path *lowerpath = ovl_lowerpath(oip->oe); struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL; struct inode *inode; struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL; @@ -1404,7 +1400,7 @@ struct inode *ovl_get_inode(struct super_block *sb, /* Check for non-merge dir that may have whiteouts */ if (is_dir) { - if (((upperdentry && lowerdentry) || oip->numlower > 1) || + if (((upperdentry && lowerdentry) || ovl_numlower(oip->oe) > 1) || ovl_path_check_origin_xattr(ofs, &realpath)) { ovl_set_flag(OVL_WHITEOUTS, inode); } diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index a7d975bf9e48..317ab1efc236 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1107,10 +1107,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (upperdentry || ctr) { struct ovl_inode_params oip = { .upperdentry = upperdentry, - .lowerpath = stack, .oe = oe, .index = index, - .numlower = ctr, .redirect = upperredirect, .lowerdata = (ctr > 1 && !d.is_dir) ? stack[ctr - 1].dentry : NULL, diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index cd74d7466a58..8b38844ae341 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -653,10 +653,8 @@ bool ovl_is_private_xattr(struct super_block *sb, const char *name); struct ovl_inode_params { struct inode *newinode; struct dentry *upperdentry; - struct ovl_path *lowerpath; struct ovl_entry *oe; bool index; - unsigned int numlower; char *redirect; struct dentry *lowerdata; }; diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index f511ac78c5bd..0bb8ab3aa8a7 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -120,6 +120,11 @@ static inline struct ovl_path *ovl_lowerstack(struct ovl_entry *oe) return ovl_numlower(oe) ? oe->__lowerstack : NULL; } +static inline struct ovl_path *ovl_lowerpath(struct ovl_entry *oe) +{ + return ovl_lowerstack(oe); +} + /* private information held for every overlayfs dentry */ static inline unsigned long *OVL_E_FLAGS(struct dentry *dentry) { @@ -136,7 +141,6 @@ struct ovl_inode { unsigned long flags; struct inode vfs_inode; struct dentry *__upperdentry; - struct ovl_path lowerpath; struct ovl_entry *oe; /* synchronize copy up and more */ diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index bb4a062ab0d5..1efc4f252561 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -172,8 +172,6 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) oi->flags = 0; oi->__upperdentry = NULL; oi->oe = NULL; - oi->lowerpath.dentry = NULL; - oi->lowerpath.layer = NULL; oi->lowerdata = NULL; mutex_init(&oi->lock); @@ -194,7 +192,6 @@ static void ovl_destroy_inode(struct inode *inode) struct ovl_inode *oi = OVL_I(inode); dput(oi->__upperdentry); - dput(oi->lowerpath.dentry); ovl_free_entry(oi->oe); if (S_ISDIR(inode->i_mode)) ovl_dir_cache_free(inode); @@ -1839,7 +1836,6 @@ static struct dentry *ovl_get_root(struct super_block *sb, int fsid = lowerpath->layer->fsid; struct ovl_inode_params oip = { .upperdentry = upperdentry, - .lowerpath = lowerpath, .oe = oe, }; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index c4bd901f3bda..143e6898f50d 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -306,10 +306,12 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode) struct inode *ovl_i_path_real(struct inode *inode, struct path *path) { + struct ovl_path *lowerpath = ovl_lowerpath(OVL_I_E(inode)); + path->dentry = ovl_i_dentry_upper(inode); if (!path->dentry) { - path->dentry = OVL_I(inode)->lowerpath.dentry; - path->mnt = OVL_I(inode)->lowerpath.layer->mnt; + path->dentry = lowerpath->dentry; + path->mnt = lowerpath->layer->mnt; } else { path->mnt = ovl_upper_mnt(OVL_FS(inode->i_sb)); } @@ -326,9 +328,9 @@ struct inode *ovl_inode_upper(struct inode *inode) struct inode *ovl_inode_lower(struct inode *inode) { - struct dentry *lowerdentry = OVL_I(inode)->lowerpath.dentry; + struct ovl_path *lowerpath = ovl_lowerpath(OVL_I_E(inode)); - return lowerdentry ? d_inode(lowerdentry) : NULL; + return lowerpath ? d_inode(lowerpath->dentry) : NULL; } struct inode *ovl_inode_real(struct inode *inode) -- cgit v1.2.3 From ab1eb5ffb7d2a69c9b24083026a63cbdd758d96b Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sat, 1 Apr 2023 10:29:19 +0300 Subject: ovl: deduplicate lowerdata and lowerstack[] The ovl_inode contains a copy of lowerdata in lowerstack[], so the lowerdata inode member can be removed. Use accessors ovl_lowerdata*() to get the lowerdata whereever the member was accessed directly. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/inode.c | 2 -- fs/overlayfs/namei.c | 2 -- fs/overlayfs/overlayfs.h | 1 - fs/overlayfs/ovl_entry.h | 16 +++++++++++++++- fs/overlayfs/super.c | 3 --- fs/overlayfs/util.c | 15 +++++++-------- 6 files changed, 22 insertions(+), 17 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 4dbf8d880c0f..5239a5bfc6d1 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -1005,8 +1005,6 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, oi->__upperdentry = oip->upperdentry; oi->oe = oip->oe; oi->redirect = oip->redirect; - if (oip->lowerdata) - oi->lowerdata = igrab(d_inode(oip->lowerdata)); realinode = ovl_inode_real(inode); ovl_copyattr(inode); diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 317ab1efc236..8ea2a8358a92 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1110,8 +1110,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .oe = oe, .index = index, .redirect = upperredirect, - .lowerdata = (ctr > 1 && !d.is_dir) ? - stack[ctr - 1].dentry : NULL, }; inode = ovl_get_inode(dentry->d_sb, &oip); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 8b38844ae341..2ccd6326c2c7 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -656,7 +656,6 @@ struct ovl_inode_params { struct ovl_entry *oe; bool index; char *redirect; - struct dentry *lowerdata; }; void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, unsigned long ino, int fsid); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 0bb8ab3aa8a7..548c93e030fc 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -125,6 +125,20 @@ static inline struct ovl_path *ovl_lowerpath(struct ovl_entry *oe) return ovl_lowerstack(oe); } +static inline struct ovl_path *ovl_lowerdata(struct ovl_entry *oe) +{ + struct ovl_path *lowerstack = ovl_lowerstack(oe); + + return lowerstack ? &lowerstack[oe->__numlower - 1] : NULL; +} + +static inline struct dentry *ovl_lowerdata_dentry(struct ovl_entry *oe) +{ + struct ovl_path *lowerdata = ovl_lowerdata(oe); + + return lowerdata ? lowerdata->dentry : NULL; +} + /* private information held for every overlayfs dentry */ static inline unsigned long *OVL_E_FLAGS(struct dentry *dentry) { @@ -134,7 +148,7 @@ static inline unsigned long *OVL_E_FLAGS(struct dentry *dentry) struct ovl_inode { union { struct ovl_dir_cache *cache; /* directory */ - struct inode *lowerdata; /* regular file */ + /* place holder for non-dir */ /* regular file */ }; const char *redirect; u64 version; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 1efc4f252561..dace72ed36c2 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -172,7 +172,6 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) oi->flags = 0; oi->__upperdentry = NULL; oi->oe = NULL; - oi->lowerdata = NULL; mutex_init(&oi->lock); return &oi->vfs_inode; @@ -195,8 +194,6 @@ static void ovl_destroy_inode(struct inode *inode) ovl_free_entry(oi->oe); if (S_ISDIR(inode->i_mode)) ovl_dir_cache_free(inode); - else - iput(oi->lowerdata); } static void ovl_free_fs(struct ovl_fs *ofs) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 143e6898f50d..b6e66bf8135f 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -225,11 +225,11 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) void ovl_path_lowerdata(struct dentry *dentry, struct path *path) { struct ovl_entry *oe = OVL_E(dentry); - struct ovl_path *lowerstack = ovl_lowerstack(oe); + struct ovl_path *lowerdata = ovl_lowerdata(oe); if (ovl_numlower(oe)) { - path->mnt = lowerstack[ovl_numlower(oe) - 1].layer->mnt; - path->dentry = lowerstack[ovl_numlower(oe) - 1].dentry; + path->mnt = lowerdata->layer->mnt; + path->dentry = lowerdata->dentry; } else { *path = (struct path) { }; } @@ -288,10 +288,7 @@ const struct ovl_layer *ovl_layer_lower(struct dentry *dentry) */ struct dentry *ovl_dentry_lowerdata(struct dentry *dentry) { - struct ovl_entry *oe = OVL_E(dentry); - - return ovl_numlower(oe) ? - ovl_lowerstack(oe)[ovl_numlower(oe) - 1].dentry : NULL; + return ovl_lowerdata_dentry(OVL_E(dentry)); } struct dentry *ovl_dentry_real(struct dentry *dentry) @@ -341,10 +338,12 @@ struct inode *ovl_inode_real(struct inode *inode) /* Return inode which contains lower data. Do not return metacopy */ struct inode *ovl_inode_lowerdata(struct inode *inode) { + struct dentry *lowerdata = ovl_lowerdata_dentry(OVL_I_E(inode)); + if (WARN_ON(!S_ISREG(inode->i_mode))) return NULL; - return OVL_I(inode)->lowerdata ?: ovl_inode_lower(inode); + return lowerdata ? d_inode(lowerdata) : NULL; } /* Return real inode which contains data. Does not return metacopy inode */ -- cgit v1.2.3 From 2b21da920866ad20b5e3119f3b8d2267774b7b0a Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Thu, 27 Apr 2023 12:21:46 +0300 Subject: ovl: prepare to store lowerdata redirect for lazy lowerdata lookup Prepare to allow ovl_lookup() to leave the last entry in a non-dir lowerstack empty to signify lazy lowerdata lookup. In this case, ovl_lookup() stores the redirect path from metacopy to lowerdata in ovl_inode, which is going to be used later to perform the lazy lowerdata lookup. Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/inode.c | 2 ++ fs/overlayfs/namei.c | 5 +++++ fs/overlayfs/overlayfs.h | 2 ++ fs/overlayfs/ovl_entry.h | 3 ++- fs/overlayfs/super.c | 3 +++ fs/overlayfs/util.c | 13 ++++++++++--- 6 files changed, 24 insertions(+), 4 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 5239a5bfc6d1..552059e759b6 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -1005,6 +1005,7 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, oi->__upperdentry = oip->upperdentry; oi->oe = oip->oe; oi->redirect = oip->redirect; + oi->lowerdata_redirect = oip->lowerdata_redirect; realinode = ovl_inode_real(inode); ovl_copyattr(inode); @@ -1365,6 +1366,7 @@ struct inode *ovl_get_inode(struct super_block *sb, dput(upperdentry); ovl_free_entry(oip->oe); kfree(oip->redirect); + kfree(oip->lowerdata_redirect); goto out; } diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 517f31a28f72..605108f0dbe9 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1183,6 +1183,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .redirect = upperredirect, }; + /* Store lowerdata redirect for lazy lookup */ + if (ctr > 1 && !d.is_dir && !stack[ctr - 1].dentry) { + oip.lowerdata_redirect = d.redirect; + d.redirect = NULL; + } inode = ovl_get_inode(dentry->d_sb, &oip); err = PTR_ERR(inode); if (IS_ERR(inode)) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 2ccd6326c2c7..5cc03b8d7b25 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -405,6 +405,7 @@ struct inode *ovl_inode_lower(struct inode *inode); struct inode *ovl_inode_lowerdata(struct inode *inode); struct inode *ovl_inode_real(struct inode *inode); struct inode *ovl_inode_realdata(struct inode *inode); +const char *ovl_lowerdata_redirect(struct inode *inode); struct ovl_dir_cache *ovl_dir_cache(struct inode *inode); void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache); void ovl_dentry_set_flag(unsigned long flag, struct dentry *dentry); @@ -656,6 +657,7 @@ struct ovl_inode_params { struct ovl_entry *oe; bool index; char *redirect; + char *lowerdata_redirect; }; void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, unsigned long ino, int fsid); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 93ff299da0dd..513c2c499e41 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -141,6 +141,7 @@ static inline struct ovl_path *ovl_lowerdata(struct ovl_entry *oe) return lowerstack ? &lowerstack[oe->__numlower - 1] : NULL; } +/* May return NULL if lazy lookup of lowerdata is needed */ static inline struct dentry *ovl_lowerdata_dentry(struct ovl_entry *oe) { struct ovl_path *lowerdata = ovl_lowerdata(oe); @@ -157,7 +158,7 @@ static inline unsigned long *OVL_E_FLAGS(struct dentry *dentry) struct ovl_inode { union { struct ovl_dir_cache *cache; /* directory */ - /* place holder for non-dir */ /* regular file */ + const char *lowerdata_redirect; /* regular file */ }; const char *redirect; u64 version; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index be2bdf37b0d5..9de02ca2387e 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -171,6 +171,7 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) oi->version = 0; oi->flags = 0; oi->__upperdentry = NULL; + oi->lowerdata_redirect = NULL; oi->oe = NULL; mutex_init(&oi->lock); @@ -194,6 +195,8 @@ static void ovl_destroy_inode(struct inode *inode) ovl_free_entry(oi->oe); if (S_ISDIR(inode->i_mode)) ovl_dir_cache_free(inode); + else + kfree(oi->lowerdata_redirect); } static void ovl_free_fs(struct ovl_fs *ofs) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index b6e66bf8135f..72f565f640fa 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -226,10 +226,11 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path) { struct ovl_entry *oe = OVL_E(dentry); struct ovl_path *lowerdata = ovl_lowerdata(oe); + struct dentry *lowerdata_dentry = ovl_lowerdata_dentry(oe); - if (ovl_numlower(oe)) { + if (lowerdata_dentry) { path->mnt = lowerdata->layer->mnt; - path->dentry = lowerdata->dentry; + path->dentry = lowerdata_dentry; } else { *path = (struct path) { }; } @@ -358,9 +359,15 @@ struct inode *ovl_inode_realdata(struct inode *inode) return ovl_inode_lowerdata(inode); } +const char *ovl_lowerdata_redirect(struct inode *inode) +{ + return inode && S_ISREG(inode->i_mode) ? + OVL_I(inode)->lowerdata_redirect : NULL; +} + struct ovl_dir_cache *ovl_dir_cache(struct inode *inode) { - return OVL_I(inode)->cache; + return inode && S_ISDIR(inode->i_mode) ? OVL_I(inode)->cache : NULL; } void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache) -- cgit v1.2.3 From 416656447864ef0fc2430525499f52a4a370a0ab Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sun, 2 Apr 2023 21:56:49 +0300 Subject: ovl: prepare for lazy lookup of lowerdata inode Make the code handle the case of numlower > 1 and missing lowerdata dentry gracefully. Missing lowerdata dentry is an indication for lazy lookup of lowerdata and in that case the lowerdata_redirect path is stored in ovl_inode. Following commits will defer lookup and perform the lazy lookup on access. Reviewed-by: Alexander Larsson Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/file.c | 7 +++++++ fs/overlayfs/inode.c | 18 ++++++++++++++---- fs/overlayfs/super.c | 11 +++++++++++ fs/overlayfs/util.c | 2 +- 4 files changed, 33 insertions(+), 5 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 7c04f033aadd..951683a66ff6 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -115,6 +115,9 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real, ovl_path_real(dentry, &realpath); else ovl_path_realdata(dentry, &realpath); + /* TODO: lazy lookup of lowerdata */ + if (!realpath.dentry) + return -EIO; /* Has it been copied up since we'd opened it? */ if (unlikely(file_inode(real->file) != d_inode(realpath.dentry))) { @@ -158,6 +161,10 @@ static int ovl_open(struct inode *inode, struct file *file) file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); ovl_path_realdata(dentry, &realpath); + /* TODO: lazy lookup of lowerdata */ + if (!realpath.dentry) + return -EIO; + realfile = ovl_open_realfile(file, &realpath); if (IS_ERR(realfile)) return PTR_ERR(realfile); diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 552059e759b6..bf47a0a94d1e 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -240,15 +240,22 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, /* * If lower is not same as lowerdata or if there was * no origin on upper, we can end up here. + * With lazy lowerdata lookup, guess lowerdata blocks + * from size to avoid lowerdata lookup on stat(2). */ struct kstat lowerdatastat; u32 lowermask = STATX_BLOCKS; ovl_path_lowerdata(dentry, &realpath); - err = vfs_getattr(&realpath, &lowerdatastat, - lowermask, flags); - if (err) - goto out; + if (realpath.dentry) { + err = vfs_getattr(&realpath, &lowerdatastat, + lowermask, flags); + if (err) + goto out; + } else { + lowerdatastat.blocks = + round_up(stat->size, stat->blksize) >> 9; + } stat->blocks = lowerdatastat.blocks; } } @@ -709,6 +716,9 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, struct inode *realinode = ovl_inode_realdata(inode); const struct cred *old_cred; + if (!realinode) + return -EIO; + if (!realinode->i_op->fiemap) return -EOPNOTSUPP; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 9de02ca2387e..f3e5b43c4106 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -81,6 +81,14 @@ static struct dentry *ovl_d_real(struct dentry *dentry, if (real && !inode && ovl_has_upperdata(d_inode(dentry))) return real; + /* + * XXX: We may need lazy lookup of lowerdata for !inode case to return + * the real lowerdata dentry. The only current caller of d_real() with + * NULL inode is d_real_inode() from trace_uprobe and this caller is + * likely going to be followed reading from the file, before placing + * uprobes on offset within the file, so lowerdata should be available + * when setting the uprobe. + */ lower = ovl_dentry_lowerdata(dentry); if (!lower) goto bug; @@ -103,6 +111,9 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak) { int ret = 1; + if (!d) + return 1; + if (weak) { if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE) ret = d->d_op->d_weak_revalidate(d, flags); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 72f565f640fa..23c0224779be 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -159,7 +159,7 @@ void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry, if (upperdentry) flags |= upperdentry->d_flags; - for (i = 0; i < ovl_numlower(oe); i++) + for (i = 0; i < ovl_numlower(oe) && lowerstack[i].dentry; i++) flags |= lowerstack[i].dentry->d_flags; spin_lock(&dentry->d_lock); -- cgit v1.2.3 From 42dd69ae1af42cf72d167e63a039b8c63653eb7f Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Thu, 27 Apr 2023 13:39:09 +0300 Subject: ovl: implement lazy lookup of lowerdata in data-only layers Defer lookup of lowerdata in the data-only layers to first data access or before copy up. We perform lowerdata lookup before copy up even if copy up is metadata only copy up. We can further optimize this lookup later if needed. We do best effort lazy lookup of lowerdata for d_real_inode(), because this interface does not expect errors. The only current in-tree caller of d_real_inode() is trace_uprobe and this caller is likely going to be followed reading from the file, before placing uprobes on offset within the file, so lowerdata should be available when setting the uprobe. Tested-by: kernel test robot Reviewed-by: Alexander Larsson Signed-off-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/overlayfs/copy_up.c | 9 ++++++++ fs/overlayfs/file.c | 18 ++++++++++++---- fs/overlayfs/namei.c | 56 ++++++++++++++++++++++++++++++++++++++++++------ fs/overlayfs/overlayfs.h | 2 ++ fs/overlayfs/ovl_entry.h | 2 +- fs/overlayfs/super.c | 3 ++- fs/overlayfs/util.c | 31 ++++++++++++++++++++++++++- 7 files changed, 107 insertions(+), 14 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 95dce240ba17..568f743a5584 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -1073,6 +1073,15 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags) if (WARN_ON(disconnected && d_is_dir(dentry))) return -EIO; + /* + * We may not need lowerdata if we are only doing metacopy up, but it is + * not very important to optimize this case, so do lazy lowerdata lookup + * before any copy up, so we can do it before taking ovl_inode_lock(). + */ + err = ovl_maybe_lookup_lowerdata(dentry); + if (err) + return err; + old_cred = ovl_override_creds(dentry->d_sb); while (!err) { struct dentry *next; diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 951683a66ff6..39737c2aaa84 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -107,15 +107,21 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real, { struct dentry *dentry = file_dentry(file); struct path realpath; + int err; real->flags = 0; real->file = file->private_data; - if (allow_meta) + if (allow_meta) { ovl_path_real(dentry, &realpath); - else + } else { + /* lazy lookup of lowerdata */ + err = ovl_maybe_lookup_lowerdata(dentry); + if (err) + return err; + ovl_path_realdata(dentry, &realpath); - /* TODO: lazy lookup of lowerdata */ + } if (!realpath.dentry) return -EIO; @@ -153,6 +159,11 @@ static int ovl_open(struct inode *inode, struct file *file) struct path realpath; int err; + /* lazy lookup of lowerdata */ + err = ovl_maybe_lookup_lowerdata(dentry); + if (err) + return err; + err = ovl_maybe_copy_up(dentry, file->f_flags); if (err) return err; @@ -161,7 +172,6 @@ static int ovl_open(struct inode *inode, struct file *file) file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); ovl_path_realdata(dentry, &realpath); - /* TODO: lazy lookup of lowerdata */ if (!realpath.dentry) return -EIO; diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 605108f0dbe9..b3b40bc9a5ab 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -889,6 +889,52 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry, return err; } +/* Lazy lookup of lowerdata */ +int ovl_maybe_lookup_lowerdata(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + const char *redirect = ovl_lowerdata_redirect(inode); + struct ovl_path datapath = {}; + const struct cred *old_cred; + int err; + + if (!redirect || ovl_dentry_lowerdata(dentry)) + return 0; + + if (redirect[0] != '/') + return -EIO; + + err = ovl_inode_lock_interruptible(inode); + if (err) + return err; + + err = 0; + /* Someone got here before us? */ + if (ovl_dentry_lowerdata(dentry)) + goto out; + + old_cred = ovl_override_creds(dentry->d_sb); + err = ovl_lookup_data_layers(dentry, redirect, &datapath); + revert_creds(old_cred); + if (err) + goto out_err; + + err = ovl_dentry_set_lowerdata(dentry, &datapath); + if (err) + goto out_err; + +out: + ovl_inode_unlock(inode); + dput(datapath.dentry); + + return err; + +out_err: + pr_warn_ratelimited("lazy lowerdata lookup failed (%pd2, err=%i)\n", + dentry, err); + goto out; +} + struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { @@ -1072,14 +1118,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, } } - /* Lookup absolute redirect from lower metacopy in data-only layers */ + /* Defer lookup of lowerdata in data-only layers to first access */ if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) { - err = ovl_lookup_data_layers(dentry, d.redirect, - &stack[ctr]); - if (!err) { - d.metacopy = false; - ctr++; - } + d.metacopy = false; + ctr++; } /* diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 5cc03b8d7b25..fcac4e2c56ab 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -396,6 +396,7 @@ enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path); struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lowerdata(struct dentry *dentry); +int ovl_dentry_set_lowerdata(struct dentry *dentry, struct ovl_path *datapath); const struct ovl_layer *ovl_i_layer_lower(struct inode *inode); const struct ovl_layer *ovl_layer_lower(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry); @@ -558,6 +559,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh); struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper, struct dentry *origin, bool verify); int ovl_path_next(int idx, struct dentry *dentry, struct path *path); +int ovl_maybe_lookup_lowerdata(struct dentry *dentry); struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); bool ovl_lower_positive(struct dentry *dentry); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 513c2c499e41..c6c7d09b494e 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -146,7 +146,7 @@ static inline struct dentry *ovl_lowerdata_dentry(struct ovl_entry *oe) { struct ovl_path *lowerdata = ovl_lowerdata(oe); - return lowerdata ? lowerdata->dentry : NULL; + return lowerdata ? READ_ONCE(lowerdata->dentry) : NULL; } /* private information held for every overlayfs dentry */ diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index f3e5b43c4106..14a2ebdc8126 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -82,13 +82,14 @@ static struct dentry *ovl_d_real(struct dentry *dentry, return real; /* - * XXX: We may need lazy lookup of lowerdata for !inode case to return + * Best effort lazy lookup of lowerdata for !inode case to return * the real lowerdata dentry. The only current caller of d_real() with * NULL inode is d_real_inode() from trace_uprobe and this caller is * likely going to be followed reading from the file, before placing * uprobes on offset within the file, so lowerdata should be available * when setting the uprobe. */ + ovl_maybe_lookup_lowerdata(dentry); lower = ovl_dentry_lowerdata(dentry); if (!lower) goto bug; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 23c0224779be..939e4d586ec2 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -229,8 +229,14 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path) struct dentry *lowerdata_dentry = ovl_lowerdata_dentry(oe); if (lowerdata_dentry) { - path->mnt = lowerdata->layer->mnt; path->dentry = lowerdata_dentry; + /* + * Pairs with smp_wmb() in ovl_dentry_set_lowerdata(). + * Make sure that if lowerdata->dentry is visible, then + * datapath->layer is visible as well. + */ + smp_rmb(); + path->mnt = READ_ONCE(lowerdata->layer)->mnt; } else { *path = (struct path) { }; } @@ -292,6 +298,29 @@ struct dentry *ovl_dentry_lowerdata(struct dentry *dentry) return ovl_lowerdata_dentry(OVL_E(dentry)); } +int ovl_dentry_set_lowerdata(struct dentry *dentry, struct ovl_path *datapath) +{ + struct ovl_entry *oe = OVL_E(dentry); + struct ovl_path *lowerdata = ovl_lowerdata(oe); + struct dentry *datadentry = datapath->dentry; + + if (WARN_ON_ONCE(ovl_numlower(oe) <= 1)) + return -EIO; + + WRITE_ONCE(lowerdata->layer, datapath->layer); + /* + * Pairs with smp_rmb() in ovl_path_lowerdata(). + * Make sure that if lowerdata->dentry is visible, then + * lowerdata->layer is visible as well. + */ + smp_wmb(); + WRITE_ONCE(lowerdata->dentry, dget(datadentry)); + + ovl_dentry_update_reval(dentry, datadentry); + + return 0; +} + struct dentry *ovl_dentry_real(struct dentry *dentry) { return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry); -- cgit v1.2.3 From af5f2396b671c9857b113584d4bd43413b39cfdb Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sat, 17 Jun 2023 09:42:36 +0300 Subject: ovl: store enum redirect_mode in config instead of a string Do all the logic to set the mode during mount options parsing and do not keep the option string around. Use a constant_table to translate from enum redirect mode to string in preperation for new mount api option parsing. The mount option "off" is translated to either "follow" or "nofollow", depending on the "redirect_always_follow" build/module config, so in effect, there are only three possible redirect modes. This results in a minor change to the string that is displayed in show_options() - when redirect_dir is enabled by default and the user mounts with the option "redirect_dir=off", instead of displaying the mode "redirect_dir=off" in show_options(), the displayed mode will be either "redirect_dir=follow" or "redirect_dir=nofollow", depending on the value of "redirect_always_follow" build/module config. The displayed mode reflects the effective mode, so mounting overlayfs again with the dispalyed redirect_dir option will result with the same effective and displayed mode. Signed-off-by: Amir Goldstein --- Documentation/filesystems/overlayfs.rst | 9 +-- fs/overlayfs/dir.c | 2 +- fs/overlayfs/namei.c | 6 +- fs/overlayfs/overlayfs.h | 39 +++++++--- fs/overlayfs/ovl_entry.h | 4 +- fs/overlayfs/super.c | 129 ++++++++++++++++++-------------- fs/overlayfs/util.c | 7 -- 7 files changed, 107 insertions(+), 89 deletions(-) (limited to 'fs/overlayfs/util.c') diff --git a/Documentation/filesystems/overlayfs.rst b/Documentation/filesystems/overlayfs.rst index 4f36b8919f7c..eb7d2c88ddec 100644 --- a/Documentation/filesystems/overlayfs.rst +++ b/Documentation/filesystems/overlayfs.rst @@ -231,12 +231,11 @@ Mount options: Redirects are enabled. - "redirect_dir=follow": Redirects are not created, but followed. -- "redirect_dir=off": - Redirects are not created and only followed if "redirect_always_follow" - feature is enabled in the kernel/module config. - "redirect_dir=nofollow": - Redirects are not created and not followed (equivalent to "redirect_dir=off" - if "redirect_always_follow" feature is not enabled). + Redirects are not created and not followed. +- "redirect_dir=off": + If "redirect_always_follow" is enabled in the kernel/module config, + this "off" traslates to "follow", otherwise it translates to "nofollow". When the NFS export feature is enabled, every copied up directory is indexed by the file handle of the lower inode and a file handle of the diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 0da45727099b..033fc0458a3d 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -952,7 +952,7 @@ static bool ovl_type_merge_or_lower(struct dentry *dentry) static bool ovl_can_move(struct dentry *dentry) { - return ovl_redirect_dir(dentry->d_sb) || + return ovl_redirect_dir(OVL_FS(dentry->d_sb)) || !d_is_dir(dentry) || !ovl_type_merge_or_lower(dentry); } diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index b3b40bc9a5ab..57adf911735f 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -961,7 +961,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, .is_dir = false, .opaque = false, .stop = false, - .last = ofs->config.redirect_follow ? false : !ovl_numlower(poe), + .last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe), .redirect = NULL, .metacopy = false, }; @@ -1022,7 +1022,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, for (i = 0; !d.stop && i < ovl_numlower(poe); i++) { struct ovl_path lower = ovl_lowerstack(poe)[i]; - if (!ofs->config.redirect_follow) + if (!ovl_redirect_follow(ofs)) d.last = i == ovl_numlower(poe) - 1; else if (d.is_dir || !ofs->numdatalayer) d.last = lower.layer->idx == ovl_numlower(roe); @@ -1102,7 +1102,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * this attack vector when not necessary. */ err = -EPERM; - if (d.redirect && !ofs->config.redirect_follow) { + if (d.redirect && !ovl_redirect_follow(ofs)) { pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n", dentry); goto out_put; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 05e9acfe1590..80c10228bd64 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -57,6 +57,13 @@ enum ovl_entry_flag { OVL_E_CONNECTED, }; +enum { + OVL_REDIRECT_OFF, /* "off" mode is never used. In effect */ + OVL_REDIRECT_FOLLOW, /* ...it translates to either "follow" */ + OVL_REDIRECT_NOFOLLOW, /* ...or "nofollow". */ + OVL_REDIRECT_ON, +}; + enum { OVL_XINO_OFF, OVL_XINO_AUTO, @@ -352,17 +359,6 @@ static inline bool ovl_open_flags_need_copy_up(int flags) return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)); } -static inline bool ovl_allow_offline_changes(struct ovl_fs *ofs) -{ - /* - * To avoid regressions in existing setups with overlay lower offline - * changes, we allow lower changes only if none of the new features - * are used. - */ - return (!ofs->config.index && !ofs->config.metacopy && - !ofs->config.redirect_dir && ofs->config.xino != OVL_XINO_ON); -} - /* util.c */ int ovl_want_write(struct dentry *dentry); @@ -421,7 +417,6 @@ bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags); bool ovl_dentry_needs_data_copy_up_locked(struct dentry *dentry, int flags); bool ovl_has_upperdata(struct inode *inode); void ovl_set_upperdata(struct inode *inode); -bool ovl_redirect_dir(struct super_block *sb); const char *ovl_dentry_get_redirect(struct dentry *dentry); void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect); void ovl_inode_update(struct inode *inode, struct dentry *upperdentry); @@ -489,6 +484,16 @@ static inline bool ovl_is_impuredir(struct super_block *sb, return ovl_path_check_dir_xattr(ofs, &upperpath, OVL_XATTR_IMPURE); } +static inline bool ovl_redirect_follow(struct ovl_fs *ofs) +{ + return ofs->config.redirect_mode != OVL_REDIRECT_NOFOLLOW; +} + +static inline bool ovl_redirect_dir(struct ovl_fs *ofs) +{ + return ofs->config.redirect_mode == OVL_REDIRECT_ON; +} + /* * With xino=auto, we do best effort to keep all inodes on same st_dev and * d_ino consistent with st_ino. @@ -499,6 +504,16 @@ static inline bool ovl_xino_warn(struct ovl_fs *ofs) return ofs->config.xino == OVL_XINO_ON; } +/* + * To avoid regressions in existing setups with overlay lower offline changes, + * we allow lower changes only if none of the new features are used. + */ +static inline bool ovl_allow_offline_changes(struct ovl_fs *ofs) +{ + return (!ofs->config.index && !ofs->config.metacopy && + !ovl_redirect_dir(ofs) && !ovl_xino_warn(ofs)); +} + /* All layers on same fs? */ static inline bool ovl_same_fs(struct ovl_fs *ofs) { diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 40232b056be8..b4eb0bd5d0b6 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -10,9 +10,7 @@ struct ovl_config { char *upperdir; char *workdir; bool default_permissions; - bool redirect_dir; - bool redirect_follow; - const char *redirect_mode; + int redirect_mode; bool index; bool uuid; bool nfs_export; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 5bcb26528408..5a84af92c91e 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -244,7 +244,6 @@ static void ovl_free_fs(struct ovl_fs *ofs) kfree(ofs->config.lowerdir); kfree(ofs->config.upperdir); kfree(ofs->config.workdir); - kfree(ofs->config.redirect_mode); if (ofs->creator_cred) put_cred(ofs->creator_cred); kfree(ofs); @@ -330,9 +329,24 @@ static bool ovl_force_readonly(struct ovl_fs *ofs) return (!ovl_upper_mnt(ofs) || !ofs->workdir); } -static const char *ovl_redirect_mode_def(void) +static const struct constant_table ovl_parameter_redirect_dir[] = { + { "off", OVL_REDIRECT_OFF }, + { "follow", OVL_REDIRECT_FOLLOW }, + { "nofollow", OVL_REDIRECT_NOFOLLOW }, + { "on", OVL_REDIRECT_ON }, + {} +}; + +static const char *ovl_redirect_mode(struct ovl_config *config) { - return ovl_redirect_dir_def ? "on" : "off"; + return ovl_parameter_redirect_dir[config->redirect_mode].name; +} + +static int ovl_redirect_mode_def(void) +{ + return ovl_redirect_dir_def ? OVL_REDIRECT_ON : + ovl_redirect_always_follow ? OVL_REDIRECT_FOLLOW : + OVL_REDIRECT_NOFOLLOW; } static const struct constant_table ovl_parameter_xino[] = { @@ -372,8 +386,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) } if (ofs->config.default_permissions) seq_puts(m, ",default_permissions"); - if (strcmp(ofs->config.redirect_mode, ovl_redirect_mode_def()) != 0) - seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode); + if (ofs->config.redirect_mode != ovl_redirect_mode_def()) + seq_printf(m, ",redirect_dir=%s", + ovl_redirect_mode(&ofs->config)); if (ofs->config.index != ovl_index_def) seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off"); if (!ofs->config.uuid) @@ -431,7 +446,10 @@ enum { OPT_UPPERDIR, OPT_WORKDIR, OPT_DEFAULT_PERMISSIONS, - OPT_REDIRECT_DIR, + OPT_REDIRECT_DIR_ON, + OPT_REDIRECT_DIR_OFF, + OPT_REDIRECT_DIR_FOLLOW, + OPT_REDIRECT_DIR_NOFOLLOW, OPT_INDEX_ON, OPT_INDEX_OFF, OPT_UUID_ON, @@ -453,7 +471,10 @@ static const match_table_t ovl_tokens = { {OPT_UPPERDIR, "upperdir=%s"}, {OPT_WORKDIR, "workdir=%s"}, {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, - {OPT_REDIRECT_DIR, "redirect_dir=%s"}, + {OPT_REDIRECT_DIR_ON, "redirect_dir=on"}, + {OPT_REDIRECT_DIR_OFF, "redirect_dir=off"}, + {OPT_REDIRECT_DIR_FOLLOW, "redirect_dir=follow"}, + {OPT_REDIRECT_DIR_NOFOLLOW, "redirect_dir=nofollow"}, {OPT_INDEX_ON, "index=on"}, {OPT_INDEX_OFF, "index=off"}, {OPT_USERXATTR, "userxattr"}, @@ -493,40 +514,12 @@ static char *ovl_next_opt(char **s) return sbegin; } -static int ovl_parse_redirect_mode(struct ovl_config *config, const char *mode) -{ - if (strcmp(mode, "on") == 0) { - config->redirect_dir = true; - /* - * Does not make sense to have redirect creation without - * redirect following. - */ - config->redirect_follow = true; - } else if (strcmp(mode, "follow") == 0) { - config->redirect_follow = true; - } else if (strcmp(mode, "off") == 0) { - if (ovl_redirect_always_follow) - config->redirect_follow = true; - } else if (strcmp(mode, "nofollow") != 0) { - pr_err("bad mount option \"redirect_dir=%s\"\n", - mode); - return -EINVAL; - } - - return 0; -} - static int ovl_parse_opt(char *opt, struct ovl_config *config) { char *p; - int err; bool metacopy_opt = false, redirect_opt = false; bool nfs_export_opt = false, index_opt = false; - config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL); - if (!config->redirect_mode) - return -ENOMEM; - while ((p = ovl_next_opt(&opt)) != NULL) { int token; substring_t args[MAX_OPT_ARGS]; @@ -561,11 +554,25 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->default_permissions = true; break; - case OPT_REDIRECT_DIR: - kfree(config->redirect_mode); - config->redirect_mode = match_strdup(&args[0]); - if (!config->redirect_mode) - return -ENOMEM; + case OPT_REDIRECT_DIR_ON: + config->redirect_mode = OVL_REDIRECT_ON; + redirect_opt = true; + break; + + case OPT_REDIRECT_DIR_OFF: + config->redirect_mode = ovl_redirect_always_follow ? + OVL_REDIRECT_FOLLOW : + OVL_REDIRECT_NOFOLLOW; + redirect_opt = true; + break; + + case OPT_REDIRECT_DIR_FOLLOW: + config->redirect_mode = OVL_REDIRECT_FOLLOW; + redirect_opt = true; + break; + + case OPT_REDIRECT_DIR_NOFOLLOW: + config->redirect_mode = OVL_REDIRECT_NOFOLLOW; redirect_opt = true; break; @@ -654,22 +661,18 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) config->ovl_volatile = false; } - err = ovl_parse_redirect_mode(config, config->redirect_mode); - if (err) - return err; - /* * This is to make the logic below simpler. It doesn't make any other - * difference, since config->redirect_dir is only used for upper. + * difference, since redirect_dir=on is only used for upper. */ - if (!config->upperdir && config->redirect_follow) - config->redirect_dir = true; + if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW) + config->redirect_mode = OVL_REDIRECT_ON; /* Resolve metacopy -> redirect_dir dependency */ - if (config->metacopy && !config->redirect_dir) { + if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) { if (metacopy_opt && redirect_opt) { pr_err("conflicting options: metacopy=on,redirect_dir=%s\n", - config->redirect_mode); + ovl_redirect_mode(config)); return -EINVAL; } if (redirect_opt) { @@ -678,17 +681,18 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) * in this conflict. */ pr_info("disabling metacopy due to redirect_dir=%s\n", - config->redirect_mode); + ovl_redirect_mode(config)); config->metacopy = false; } else { /* Automatically enable redirect otherwise. */ - config->redirect_follow = config->redirect_dir = true; + config->redirect_mode = OVL_REDIRECT_ON; } } /* Resolve nfs_export -> index dependency */ if (config->nfs_export && !config->index) { - if (!config->upperdir && config->redirect_follow) { + if (!config->upperdir && + config->redirect_mode != OVL_REDIRECT_NOFOLLOW) { pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n"); config->nfs_export = false; } else if (nfs_export_opt && index_opt) { @@ -733,9 +737,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) /* Resolve userxattr -> !redirect && !metacopy dependency */ if (config->userxattr) { - if (config->redirect_follow && redirect_opt) { + if (redirect_opt && + config->redirect_mode != OVL_REDIRECT_NOFOLLOW) { pr_err("conflicting options: userxattr,redirect_dir=%s\n", - config->redirect_mode); + ovl_redirect_mode(config)); return -EINVAL; } if (config->metacopy && metacopy_opt) { @@ -748,7 +753,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) * options must be explicitly enabled if used together with * userxattr. */ - config->redirect_dir = config->redirect_follow = false; + config->redirect_mode = OVL_REDIRECT_NOFOLLOW; config->metacopy = false; } @@ -1332,10 +1337,17 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, if (err) { pr_warn("failed to set xattr on upper\n"); ofs->noxattr = true; - if (ofs->config.index || ofs->config.metacopy) { - ofs->config.index = false; + if (ovl_redirect_follow(ofs)) { + ofs->config.redirect_mode = OVL_REDIRECT_NOFOLLOW; + pr_warn("...falling back to redirect_dir=nofollow.\n"); + } + if (ofs->config.metacopy) { ofs->config.metacopy = false; - pr_warn("...falling back to index=off,metacopy=off.\n"); + pr_warn("...falling back to metacopy=off.\n"); + } + if (ofs->config.index) { + ofs->config.index = false; + pr_warn("...falling back to index=off.\n"); } /* * xattr support is required for persistent st_ino. @@ -1963,6 +1975,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!cred) goto out_err; + ofs->config.redirect_mode = ovl_redirect_mode_def(); ofs->config.index = ovl_index_def; ofs->config.uuid = true; ofs->config.nfs_export = ovl_nfs_export_def; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 939e4d586ec2..7ef9e13c404a 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -506,13 +506,6 @@ bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags) return !ovl_has_upperdata(d_inode(dentry)); } -bool ovl_redirect_dir(struct super_block *sb) -{ - struct ovl_fs *ofs = sb->s_fs_info; - - return ofs->config.redirect_dir && !ofs->noxattr; -} - const char *ovl_dentry_get_redirect(struct dentry *dentry) { return OVL_I(d_inode(dentry))->redirect; -- cgit v1.2.3