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;
{
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;
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;
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;
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)
{
}
}
- /* 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++;
}
/*
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);
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);
{
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 */
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;
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) { };
}
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);