summaryrefslogtreecommitdiff
path: root/fs/overlayfs/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/overlayfs/util.c')
-rw-r--r--fs/overlayfs/util.c31
1 files changed, 30 insertions, 1 deletions
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);