Commit be3c2131 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-update-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs

Pull overlayfs update from Amir Goldstein:

 - fix two NULL pointer deref bugs (Zhihao Cheng)

 - add support for "data-only" lower layers destined to be used by
   composefs

 - port overlayfs to the new mount api (Christian Brauner)

* tag 'ovl-update-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs: (26 commits)
  ovl: add Amir as co-maintainer
  ovl: reserve ability to reconfigure mount options with new mount api
  ovl: modify layer parameter parsing
  ovl: port to new mount api
  ovl: factor out ovl_parse_options() helper
  ovl: store enum redirect_mode in config instead of a string
  ovl: pass ovl_fs to xino helpers
  ovl: clarify ovl_get_root() semantics
  ovl: negate the ofs->share_whiteout boolean
  ovl: check type and offset of struct vfsmount in ovl_entry
  ovl: implement lazy lookup of lowerdata in data-only layers
  ovl: prepare for lazy lookup of lowerdata inode
  ovl: prepare to store lowerdata redirect for lazy lowerdata lookup
  ovl: implement lookup in data-only layers
  ovl: introduce data-only lower layers
  ovl: remove unneeded goto instructions
  ovl: deduplicate lowerdata and lowerstack[]
  ovl: deduplicate lowerpath and lowerstack[]
  ovl: move ovl_entry into ovl_inode
  ovl: factor out ovl_free_entry() and ovl_stack_*() helpers
  ...
parents eee9c708 62149a74
...@@ -231,12 +231,11 @@ Mount options: ...@@ -231,12 +231,11 @@ Mount options:
Redirects are enabled. Redirects are enabled.
- "redirect_dir=follow": - "redirect_dir=follow":
Redirects are not created, but followed. 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": - "redirect_dir=nofollow":
Redirects are not created and not followed (equivalent to "redirect_dir=off" Redirects are not created and not followed.
if "redirect_always_follow" feature is not enabled). - "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 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 indexed by the file handle of the lower inode and a file handle of the
...@@ -371,6 +370,41 @@ conflict with metacopy=on, and will result in an error. ...@@ -371,6 +370,41 @@ conflict with metacopy=on, and will result in an error.
[*] redirect_dir=follow only conflicts with metacopy=on if upperdir=... is [*] redirect_dir=follow only conflicts with metacopy=on if upperdir=... is
given. given.
Data-only lower layers
----------------------
With "metacopy" feature enabled, an overlayfs regular file may be a composition
of information from up to three different layers:
1) metadata from a file in the upper layer
2) st_ino and st_dev object identifier from a file in a lower layer
3) data from a file in another lower layer (further below)
The "lower data" file can be on any lower layer, except from the top most
lower layer.
Below the top most lower layer, any number of lower most layers may be defined
as "data-only" lower layers, using double colon ("::") separators.
A normal lower layer is not allowed to be below a data-only layer, so single
colon separators are not allowed to the right of double colon ("::") separators.
For example:
mount -t overlay overlay -olowerdir=/l1:/l2:/l3::/do1::/do2 /merged
The paths of files in the "data-only" lower layers are not visible in the
merged overlayfs directories and the metadata and st_ino/st_dev of files
in the "data-only" lower layers are not visible in overlayfs inodes.
Only the data of the files in the "data-only" lower layers may be visible
when a "metacopy" file in one of the lower layers above it, has a "redirect"
to the absolute path of the "lower data" file in the "data-only" lower layer.
Sharing and copying layers Sharing and copying layers
-------------------------- --------------------------
......
...@@ -15937,6 +15937,7 @@ F: include/media/i2c/ov2659.h ...@@ -15937,6 +15937,7 @@ F: include/media/i2c/ov2659.h
OVERLAY FILESYSTEM OVERLAY FILESYSTEM
M: Miklos Szeredi <miklos@szeredi.hu> M: Miklos Szeredi <miklos@szeredi.hu>
M: Amir Goldstein <amir73il@gmail.com>
L: linux-unionfs@vger.kernel.org L: linux-unionfs@vger.kernel.org
S: Supported S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
obj-$(CONFIG_OVERLAY_FS) += overlay.o obj-$(CONFIG_OVERLAY_FS) += overlay.o
overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \ overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \
copy_up.o export.o copy_up.o export.o params.o
...@@ -575,6 +575,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -575,6 +575,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
/* Restore timestamps on parent (best effort) */ /* Restore timestamps on parent (best effort) */
ovl_set_timestamps(ofs, upperdir, &c->pstat); ovl_set_timestamps(ofs, upperdir, &c->pstat);
ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_set_upper_alias(c->dentry);
ovl_dentry_update_reval(c->dentry, upper);
} }
} }
inode_unlock(udir); inode_unlock(udir);
...@@ -894,6 +895,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -894,6 +895,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
inode_unlock(udir); inode_unlock(udir);
ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_set_upper_alias(c->dentry);
ovl_dentry_update_reval(c->dentry, ovl_dentry_upper(c->dentry));
} }
out: out:
...@@ -1071,6 +1073,15 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags) ...@@ -1071,6 +1073,15 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
if (WARN_ON(disconnected && d_is_dir(dentry))) if (WARN_ON(disconnected && d_is_dir(dentry)))
return -EIO; 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); old_cred = ovl_override_creds(dentry->d_sb);
while (!err) { while (!err) {
struct dentry *next; struct dentry *next;
......
...@@ -83,7 +83,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) ...@@ -83,7 +83,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
ofs->whiteout = whiteout; ofs->whiteout = whiteout;
} }
if (ofs->share_whiteout) { if (!ofs->no_shared_whiteout) {
whiteout = ovl_lookup_temp(ofs, workdir); whiteout = ovl_lookup_temp(ofs, workdir);
if (IS_ERR(whiteout)) if (IS_ERR(whiteout))
goto out; goto out;
...@@ -95,7 +95,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) ...@@ -95,7 +95,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
if (err != -EMLINK) { if (err != -EMLINK) {
pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%i)\n", pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%i)\n",
ofs->whiteout->d_inode->i_nlink, err); ofs->whiteout->d_inode->i_nlink, err);
ofs->share_whiteout = false; ofs->no_shared_whiteout = true;
} }
dput(whiteout); dput(whiteout);
} }
...@@ -269,8 +269,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode, ...@@ -269,8 +269,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
ovl_dir_modified(dentry->d_parent, false); ovl_dir_modified(dentry->d_parent, false);
ovl_dentry_set_upper_alias(dentry); ovl_dentry_set_upper_alias(dentry);
ovl_dentry_update_reval(dentry, newdentry, ovl_dentry_init_reval(dentry, newdentry, NULL);
DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE);
if (!hardlink) { if (!hardlink) {
/* /*
...@@ -953,7 +952,7 @@ static bool ovl_type_merge_or_lower(struct dentry *dentry) ...@@ -953,7 +952,7 @@ static bool ovl_type_merge_or_lower(struct dentry *dentry)
static bool ovl_can_move(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); !d_is_dir(dentry) || !ovl_type_merge_or_lower(dentry);
} }
......
...@@ -80,7 +80,7 @@ static int ovl_connectable_layer(struct dentry *dentry) ...@@ -80,7 +80,7 @@ static int ovl_connectable_layer(struct dentry *dentry)
/* We can get overlay root from root of any layer */ /* We can get overlay root from root of any layer */
if (dentry == dentry->d_sb->s_root) 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 * 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) ...@@ -91,7 +91,7 @@ static int ovl_connectable_layer(struct dentry *dentry)
return 0; return 0;
/* We can get upper/overlay path from indexed/lower dentry */ /* 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) ...@@ -105,6 +105,7 @@ static int ovl_connectable_layer(struct dentry *dentry)
static int ovl_connect_layer(struct dentry *dentry) static int ovl_connect_layer(struct dentry *dentry)
{ {
struct dentry *next, *parent = NULL; struct dentry *next, *parent = NULL;
struct ovl_entry *oe = OVL_E(dentry);
int origin_layer; int origin_layer;
int err = 0; int err = 0;
...@@ -112,7 +113,7 @@ static int ovl_connect_layer(struct dentry *dentry) ...@@ -112,7 +113,7 @@ static int ovl_connect_layer(struct dentry *dentry)
WARN_ON(!ovl_dentry_lower(dentry))) WARN_ON(!ovl_dentry_lower(dentry)))
return -EIO; 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)) if (ovl_dentry_test_flag(OVL_E_CONNECTED, dentry))
return origin_layer; return origin_layer;
...@@ -285,21 +286,29 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, ...@@ -285,21 +286,29 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
struct dentry *lower = lowerpath ? lowerpath->dentry : NULL; struct dentry *lower = lowerpath ? lowerpath->dentry : NULL;
struct dentry *upper = upper_alias ?: index; struct dentry *upper = upper_alias ?: index;
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode = NULL;
struct ovl_entry *oe; struct ovl_entry *oe;
struct ovl_inode_params oip = { struct ovl_inode_params oip = {
.lowerpath = lowerpath,
.index = index, .index = index,
.numlower = !!lower
}; };
/* We get overlay directory dentries with ovl_lookup_real() */ /* We get overlay directory dentries with ovl_lookup_real() */
if (d_is_dir(upper ?: lower)) if (d_is_dir(upper ?: lower))
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
oe = ovl_alloc_entry(!!lower);
if (!oe)
return ERR_PTR(-ENOMEM);
oip.upperdentry = dget(upper); 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); inode = ovl_get_inode(sb, &oip);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
ovl_free_entry(oe);
dput(upper); dput(upper);
return ERR_CAST(inode); return ERR_CAST(inode);
} }
...@@ -314,20 +323,11 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, ...@@ -314,20 +323,11 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
dentry = d_alloc_anon(inode->i_sb); dentry = d_alloc_anon(inode->i_sb);
if (unlikely(!dentry)) if (unlikely(!dentry))
goto nomem; goto nomem;
oe = ovl_alloc_entry(lower ? 1 : 0);
if (!oe)
goto nomem;
if (lower) {
oe->lowerstack->dentry = dget(lower);
oe->lowerstack->layer = lowerpath->layer;
}
dentry->d_fsdata = oe;
if (upper_alias) if (upper_alias)
ovl_dentry_set_upper_alias(dentry); ovl_dentry_set_upper_alias(dentry);
ovl_dentry_update_reval(dentry, upper, ovl_dentry_init_reval(dentry, upper, OVL_I_E(inode));
DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE);
return d_instantiate_anon(dentry, inode); return d_instantiate_anon(dentry, inode);
...@@ -342,15 +342,16 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, ...@@ -342,15 +342,16 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
/* Get the upper or lower dentry in stack whose on layer @idx */ /* Get the upper or lower dentry in stack whose on layer @idx */
static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int 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);
struct ovl_path *lowerstack = ovl_lowerstack(oe);
int i; int i;
if (!idx) if (!idx)
return ovl_dentry_upper(dentry); return ovl_dentry_upper(dentry);
for (i = 0; i < oe->numlower; i++) { for (i = 0; i < ovl_numlower(oe); i++) {
if (oe->lowerstack[i].layer->idx == idx) if (lowerstack[i].layer->idx == idx)
return oe->lowerstack[i].dentry; return lowerstack[i].dentry;
} }
return NULL; return NULL;
......
...@@ -107,14 +107,23 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real, ...@@ -107,14 +107,23 @@ static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
{ {
struct dentry *dentry = file_dentry(file); struct dentry *dentry = file_dentry(file);
struct path realpath; struct path realpath;
int err;
real->flags = 0; real->flags = 0;
real->file = file->private_data; real->file = file->private_data;
if (allow_meta) if (allow_meta) {
ovl_path_real(dentry, &realpath); 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); ovl_path_realdata(dentry, &realpath);
}
if (!realpath.dentry)
return -EIO;
/* Has it been copied up since we'd opened it? */ /* Has it been copied up since we'd opened it? */
if (unlikely(file_inode(real->file) != d_inode(realpath.dentry))) { if (unlikely(file_inode(real->file) != d_inode(realpath.dentry))) {
...@@ -150,6 +159,11 @@ static int ovl_open(struct inode *inode, struct file *file) ...@@ -150,6 +159,11 @@ static int ovl_open(struct inode *inode, struct file *file)
struct path realpath; struct path realpath;
int err; 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); err = ovl_maybe_copy_up(dentry, file->f_flags);
if (err) if (err)
return err; return err;
...@@ -158,6 +172,9 @@ static int ovl_open(struct inode *inode, struct file *file) ...@@ -158,6 +172,9 @@ static int ovl_open(struct inode *inode, struct file *file)
file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
ovl_path_realdata(dentry, &realpath); ovl_path_realdata(dentry, &realpath);
if (!realpath.dentry)
return -EIO;
realfile = ovl_open_realfile(file, &realpath); realfile = ovl_open_realfile(file, &realpath);
if (IS_ERR(realfile)) if (IS_ERR(realfile))
return PTR_ERR(realfile); return PTR_ERR(realfile);
......
...@@ -97,8 +97,9 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ...@@ -97,8 +97,9 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid) static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid)
{ {
bool samefs = ovl_same_fs(dentry->d_sb); struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
unsigned int xinobits = ovl_xino_bits(dentry->d_sb); bool samefs = ovl_same_fs(ofs);
unsigned int xinobits = ovl_xino_bits(ofs);
unsigned int xinoshift = 64 - xinobits; unsigned int xinoshift = 64 - xinobits;
if (samefs) { if (samefs) {
...@@ -123,7 +124,7 @@ static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid) ...@@ -123,7 +124,7 @@ static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid)
stat->ino |= ((u64)fsid) << (xinoshift + 1); stat->ino |= ((u64)fsid) << (xinoshift + 1);
stat->dev = dentry->d_sb->s_dev; stat->dev = dentry->d_sb->s_dev;
return; return;
} else if (ovl_xino_warn(dentry->d_sb)) { } else if (ovl_xino_warn(ofs)) {
pr_warn_ratelimited("inode number too big (%pd2, ino=%llu, xinobits=%d)\n", pr_warn_ratelimited("inode number too big (%pd2, ino=%llu, xinobits=%d)\n",
dentry, stat->ino, xinobits); dentry, stat->ino, xinobits);
} }
...@@ -149,7 +150,7 @@ static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid) ...@@ -149,7 +150,7 @@ static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid)
* is unique per underlying fs, so we use the unique anonymous * is unique per underlying fs, so we use the unique anonymous
* bdev assigned to the underlying fs. * bdev assigned to the underlying fs.
*/ */
stat->dev = OVL_FS(dentry->d_sb)->fs[fsid].pseudo_dev; stat->dev = ofs->fs[fsid].pseudo_dev;
} }
} }
...@@ -186,7 +187,7 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, ...@@ -186,7 +187,7 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
* If lower filesystem supports NFS file handles, this also guaranties * If lower filesystem supports NFS file handles, this also guaranties
* persistent st_ino across mount cycle. * persistent st_ino across mount cycle.
*/ */
if (!is_dir || ovl_same_dev(dentry->d_sb)) { if (!is_dir || ovl_same_dev(OVL_FS(dentry->d_sb))) {
if (!OVL_TYPE_UPPER(type)) { if (!OVL_TYPE_UPPER(type)) {
fsid = ovl_layer_lower(dentry)->fsid; fsid = ovl_layer_lower(dentry)->fsid;
} else if (OVL_TYPE_ORIGIN(type)) { } else if (OVL_TYPE_ORIGIN(type)) {
...@@ -240,15 +241,22 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, ...@@ -240,15 +241,22 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
/* /*
* If lower is not same as lowerdata or if there was * If lower is not same as lowerdata or if there was
* no origin on upper, we can end up here. * 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; struct kstat lowerdatastat;
u32 lowermask = STATX_BLOCKS; u32 lowermask = STATX_BLOCKS;
ovl_path_lowerdata(dentry, &realpath); ovl_path_lowerdata(dentry, &realpath);
err = vfs_getattr(&realpath, &lowerdatastat, if (realpath.dentry) {
lowermask, flags); err = vfs_getattr(&realpath, &lowerdatastat,
if (err) lowermask, flags);
goto out; if (err)
goto out;
} else {
lowerdatastat.blocks =
round_up(stat->size, stat->blksize) >> 9;
}
stat->blocks = lowerdatastat.blocks; stat->blocks = lowerdatastat.blocks;
} }
} }
...@@ -288,8 +296,8 @@ int ovl_permission(struct mnt_idmap *idmap, ...@@ -288,8 +296,8 @@ int ovl_permission(struct mnt_idmap *idmap,
int err; int err;
/* Careful in RCU walk mode */ /* Careful in RCU walk mode */
ovl_i_path_real(inode, &realpath); realinode = ovl_i_path_real(inode, &realpath);
if (!realpath.dentry) { if (!realinode) {
WARN_ON(!(mask & MAY_NOT_BLOCK)); WARN_ON(!(mask & MAY_NOT_BLOCK));
return -ECHILD; return -ECHILD;
} }
...@@ -302,7 +310,6 @@ int ovl_permission(struct mnt_idmap *idmap, ...@@ -302,7 +310,6 @@ int ovl_permission(struct mnt_idmap *idmap,
if (err) if (err)
return err; return err;
realinode = d_inode(realpath.dentry);
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
if (!upperinode && if (!upperinode &&
!special_file(realinode->i_mode) && mask & MAY_WRITE) { !special_file(realinode->i_mode) && mask & MAY_WRITE) {
...@@ -559,20 +566,20 @@ struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap, ...@@ -559,20 +566,20 @@ struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
struct inode *inode, int type, struct inode *inode, int type,
bool rcu, bool noperm) bool rcu, bool noperm)
{ {
struct inode *realinode = ovl_inode_real(inode); struct inode *realinode;
struct posix_acl *acl; struct posix_acl *acl;
struct path realpath; struct path realpath;
if (!IS_POSIXACL(realinode))
return NULL;
/* Careful in RCU walk mode */ /* Careful in RCU walk mode */
ovl_i_path_real(inode, &realpath); realinode = ovl_i_path_real(inode, &realpath);
if (!realpath.dentry) { if (!realinode) {
WARN_ON(!rcu); WARN_ON(!rcu);
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
} }
if (!IS_POSIXACL(realinode))
return NULL;
if (rcu) { if (rcu) {
/* /*
* If the layer is idmapped drop out of RCU path walk * If the layer is idmapped drop out of RCU path walk
...@@ -710,6 +717,9 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -710,6 +717,9 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
struct inode *realinode = ovl_inode_realdata(inode); struct inode *realinode = ovl_inode_realdata(inode);
const struct cred *old_cred; const struct cred *old_cred;
if (!realinode)
return -EIO;
if (!realinode->i_op->fiemap) if (!realinode->i_op->fiemap)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -952,7 +962,7 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode) ...@@ -952,7 +962,7 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode)
static void ovl_next_ino(struct inode *inode) static void ovl_next_ino(struct inode *inode)
{ {
struct ovl_fs *ofs = inode->i_sb->s_fs_info; struct ovl_fs *ofs = OVL_FS(inode->i_sb);
inode->i_ino = atomic_long_inc_return(&ofs->last_ino); inode->i_ino = atomic_long_inc_return(&ofs->last_ino);
if (unlikely(!inode->i_ino)) if (unlikely(!inode->i_ino))
...@@ -961,7 +971,8 @@ static void ovl_next_ino(struct inode *inode) ...@@ -961,7 +971,8 @@ static void ovl_next_ino(struct inode *inode)
static void ovl_map_ino(struct inode *inode, unsigned long ino, int fsid) static void ovl_map_ino(struct inode *inode, unsigned long ino, int fsid)
{ {
int xinobits = ovl_xino_bits(inode->i_sb); struct ovl_fs *ofs = OVL_FS(inode->i_sb);
int xinobits = ovl_xino_bits(ofs);
unsigned int xinoshift = 64 - xinobits; unsigned int xinoshift = 64 - xinobits;
/* /*
...@@ -972,7 +983,7 @@ static void ovl_map_ino(struct inode *inode, unsigned long ino, int fsid) ...@@ -972,7 +983,7 @@ static void ovl_map_ino(struct inode *inode, unsigned long ino, int fsid)
* with d_ino also causes nfsd readdirplus to fail. * with d_ino also causes nfsd readdirplus to fail.
*/ */
inode->i_ino = ino; inode->i_ino = ino;
if (ovl_same_fs(inode->i_sb)) { if (ovl_same_fs(ofs)) {
return; return;
} else if (xinobits && likely(!(ino >> xinoshift))) { } else if (xinobits && likely(!(ino >> xinoshift))) {
inode->i_ino |= (unsigned long)fsid << (xinoshift + 1); inode->i_ino |= (unsigned long)fsid << (xinoshift + 1);
...@@ -1003,14 +1014,10 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, ...@@ -1003,14 +1014,10 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip,
struct inode *realinode; struct inode *realinode;
struct ovl_inode *oi = OVL_I(inode); struct ovl_inode *oi = OVL_I(inode);
if (oip->upperdentry) oi->__upperdentry = oip->upperdentry;
oi->__upperdentry = oip->upperdentry; oi->oe = oip->oe;
if (oip->lowerpath && oip->lowerpath->dentry) { oi->redirect = oip->redirect;
oi->lowerpath.dentry = dget(oip->lowerpath->dentry); oi->lowerdata_redirect = oip->lowerdata_redirect;
oi->lowerpath.layer = oip->lowerpath->layer;
}
if (oip->lowerdata)
oi->lowerdata = igrab(d_inode(oip->lowerdata));
realinode = ovl_inode_real(inode); realinode = ovl_inode_real(inode);
ovl_copyattr(inode); ovl_copyattr(inode);
...@@ -1325,7 +1332,7 @@ struct inode *ovl_get_inode(struct super_block *sb, ...@@ -1325,7 +1332,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
{ {
struct ovl_fs *ofs = OVL_FS(sb); struct ovl_fs *ofs = OVL_FS(sb);
struct dentry *upperdentry = oip->upperdentry; 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 *realinode = upperdentry ? d_inode(upperdentry) : NULL;
struct inode *inode; struct inode *inode;
struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL; struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL;
...@@ -1369,7 +1376,9 @@ struct inode *ovl_get_inode(struct super_block *sb, ...@@ -1369,7 +1376,9 @@ struct inode *ovl_get_inode(struct super_block *sb,
} }
dput(upperdentry); dput(upperdentry);
ovl_free_entry(oip->oe);
kfree(oip->redirect); kfree(oip->redirect);
kfree(oip->lowerdata_redirect);
goto out; goto out;
} }
...@@ -1398,14 +1407,12 @@ struct inode *ovl_get_inode(struct super_block *sb, ...@@ -1398,14 +1407,12 @@ struct inode *ovl_get_inode(struct super_block *sb,
if (oip->index) if (oip->index)
ovl_set_flag(OVL_INDEX, inode); ovl_set_flag(OVL_INDEX, inode);
OVL_I(inode)->redirect = oip->redirect;
if (bylower) if (bylower)
ovl_set_flag(OVL_CONST_INO, inode); ovl_set_flag(OVL_CONST_INO, inode);
/* Check for non-merge dir that may have whiteouts */ /* Check for non-merge dir that may have whiteouts */
if (is_dir) { 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_path_check_origin_xattr(ofs, &realpath)) {
ovl_set_flag(OVL_WHITEOUTS, inode); ovl_set_flag(OVL_WHITEOUTS, inode);
} }
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include "overlayfs.h" #include "overlayfs.h"
#include "../internal.h" /* for vfs_path_lookup */
struct ovl_lookup_data { struct ovl_lookup_data {
struct super_block *sb; struct super_block *sb;
struct vfsmount *mnt; struct vfsmount *mnt;
...@@ -24,6 +26,8 @@ struct ovl_lookup_data { ...@@ -24,6 +26,8 @@ struct ovl_lookup_data {
bool last; bool last;
char *redirect; char *redirect;
bool metacopy; bool metacopy;
/* Referring to last redirect xattr */
bool absolute_redirect;
}; };
static int ovl_check_redirect(const struct path *path, struct ovl_lookup_data *d, static int ovl_check_redirect(const struct path *path, struct ovl_lookup_data *d,
...@@ -33,11 +37,13 @@ static int ovl_check_redirect(const struct path *path, struct ovl_lookup_data *d ...@@ -33,11 +37,13 @@ static int ovl_check_redirect(const struct path *path, struct ovl_lookup_data *d
char *buf; char *buf;
struct ovl_fs *ofs = OVL_FS(d->sb); struct ovl_fs *ofs = OVL_FS(d->sb);
d->absolute_redirect = false;
buf = ovl_get_redirect_xattr(ofs, path, prelen + strlen(post)); buf = ovl_get_redirect_xattr(ofs, path, prelen + strlen(post));
if (IS_ERR_OR_NULL(buf)) if (IS_ERR_OR_NULL(buf))
return PTR_ERR(buf); return PTR_ERR(buf);
if (buf[0] == '/') { if (buf[0] == '/') {
d->absolute_redirect = true;
/* /*
* One of the ancestor path elements in an absolute path * One of the ancestor path elements in an absolute path
* lookup in ovl_lookup_layer() could have been opaque and * lookup in ovl_lookup_layer() could have been opaque and
...@@ -349,6 +355,61 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, ...@@ -349,6 +355,61 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
return 0; return 0;
} }
static int ovl_lookup_data_layer(struct dentry *dentry, const char *redirect,
const struct ovl_layer *layer,
struct path *datapath)
{
int err;
err = vfs_path_lookup(layer->mnt->mnt_root, layer->mnt, redirect,
LOOKUP_BENEATH | LOOKUP_NO_SYMLINKS | LOOKUP_NO_XDEV,
datapath);
pr_debug("lookup lowerdata (%pd2, redirect=\"%s\", layer=%d, err=%i)\n",
dentry, redirect, layer->idx, err);
if (err)
return err;
err = -EREMOTE;
if (ovl_dentry_weird(datapath->dentry))
goto out_path_put;
err = -ENOENT;
/* Only regular file is acceptable as lower data */
if (!d_is_reg(datapath->dentry))
goto out_path_put;
return 0;
out_path_put:
path_put(datapath);
return err;
}
/* Lookup in data-only layers by absolute redirect to layer root */
static int ovl_lookup_data_layers(struct dentry *dentry, const char *redirect,
struct ovl_path *lowerdata)
{
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
const struct ovl_layer *layer;
struct path datapath;
int err = -ENOENT;
int i;
layer = &ofs->layers[ofs->numlayer - ofs->numdatalayer];
for (i = 0; i < ofs->numdatalayer; i++, layer++) {
err = ovl_lookup_data_layer(dentry, redirect, layer, &datapath);
if (!err) {
mntput(datapath.mnt);
lowerdata->dentry = datapath.dentry;
lowerdata->layer = layer;
return 0;
}
}
return err;
}
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
struct dentry *upperdentry, struct ovl_path **stackp) struct dentry *upperdentry, struct ovl_path **stackp)
...@@ -356,7 +417,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, ...@@ -356,7 +417,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
struct dentry *origin = NULL; struct dentry *origin = NULL;
int i; int i;
for (i = 1; i < ofs->numlayer; i++) { for (i = 1; i <= ovl_numlowerlayer(ofs); i++) {
/* /*
* If lower fs uuid is not unique among lower fs we cannot match * If lower fs uuid is not unique among lower fs we cannot match
* fh->uuid to layer. * fh->uuid to layer.
...@@ -790,20 +851,21 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper, ...@@ -790,20 +851,21 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
*/ */
int ovl_path_next(int idx, struct dentry *dentry, struct path *path) 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);
struct ovl_path *lowerstack = ovl_lowerstack(oe);
BUG_ON(idx < 0); BUG_ON(idx < 0);
if (idx == 0) { if (idx == 0) {
ovl_path_upper(dentry, path); ovl_path_upper(dentry, path);
if (path->dentry) if (path->dentry)
return oe->numlower ? 1 : -1; return ovl_numlower(oe) ? 1 : -1;
idx++; idx++;
} }
BUG_ON(idx > oe->numlower); BUG_ON(idx > ovl_numlower(oe));
path->dentry = oe->lowerstack[idx - 1].dentry; path->dentry = lowerstack[idx - 1].dentry;
path->mnt = oe->lowerstack[idx - 1].layer->mnt; 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 */ /* Fix missing 'origin' xattr */
...@@ -827,14 +889,60 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -827,14 +889,60 @@ static int ovl_fix_origin(struct ovl_fs *ofs, struct dentry *dentry,
return err; 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, struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags) unsigned int flags)
{ {
struct ovl_entry *oe; struct ovl_entry *oe = NULL;
const struct cred *old_cred; const struct cred *old_cred;
struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
struct ovl_entry *poe = dentry->d_parent->d_fsdata; struct ovl_entry *poe = OVL_E(dentry->d_parent);
struct ovl_entry *roe = dentry->d_sb->s_root->d_fsdata; struct ovl_entry *roe = OVL_E(dentry->d_sb->s_root);
struct ovl_path *stack = NULL, *origin_path = NULL; struct ovl_path *stack = NULL, *origin_path = NULL;
struct dentry *upperdir, *upperdentry = NULL; struct dentry *upperdir, *upperdentry = NULL;
struct dentry *origin = NULL; struct dentry *origin = NULL;
...@@ -853,7 +961,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -853,7 +961,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
.is_dir = false, .is_dir = false,
.opaque = false, .opaque = false,
.stop = false, .stop = false,
.last = ofs->config.redirect_follow ? false : !poe->numlower, .last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
.redirect = NULL, .redirect = NULL,
.metacopy = false, .metacopy = false,
}; };
...@@ -904,21 +1012,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -904,21 +1012,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
upperopaque = d.opaque; upperopaque = d.opaque;
} }
if (!d.stop && poe->numlower) { if (!d.stop && ovl_numlower(poe)) {
err = -ENOMEM; err = -ENOMEM;
stack = kcalloc(ofs->numlayer - 1, sizeof(struct ovl_path), stack = ovl_stack_alloc(ofs->numlayer - 1);
GFP_KERNEL);
if (!stack) if (!stack)
goto out_put_upper; goto out_put_upper;
} }
for (i = 0; !d.stop && i < poe->numlower; i++) { for (i = 0; !d.stop && i < ovl_numlower(poe); i++) {
struct ovl_path lower = poe->lowerstack[i]; struct ovl_path lower = ovl_lowerstack(poe)[i];
if (!ofs->config.redirect_follow) if (!ovl_redirect_follow(ofs))
d.last = i == poe->numlower - 1; d.last = i == ovl_numlower(poe) - 1;
else else if (d.is_dir || !ofs->numdatalayer)
d.last = lower.layer->idx == roe->numlower; d.last = lower.layer->idx == ovl_numlower(roe);
d.mnt = lower.layer->mnt; d.mnt = lower.layer->mnt;
err = ovl_lookup_layer(lower.dentry, &d, &this, false); err = ovl_lookup_layer(lower.dentry, &d, &this, false);
...@@ -995,7 +1102,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -995,7 +1102,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
* this attack vector when not necessary. * this attack vector when not necessary.
*/ */
err = -EPERM; 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", pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n",
dentry); dentry);
goto out_put; goto out_put;
...@@ -1011,6 +1118,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1011,6 +1118,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
} }
} }
/* Defer lookup of lowerdata in data-only layers to first access */
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
d.metacopy = false;
ctr++;
}
/* /*
* For regular non-metacopy upper dentries, there is no lower * For regular non-metacopy upper dentries, there is no lower
* path based lookup, hence ctr will be zero. If a dentry is found * path based lookup, hence ctr will be zero. If a dentry is found
...@@ -1067,13 +1180,14 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1067,13 +1180,14 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
} }
} }
oe = ovl_alloc_entry(ctr); if (ctr) {
err = -ENOMEM; oe = ovl_alloc_entry(ctr);
if (!oe) err = -ENOMEM;
goto out_put; if (!oe)
goto out_put;
memcpy(oe->lowerstack, stack, sizeof(struct ovl_path) * ctr); ovl_stack_cpy(ovl_lowerstack(oe), stack, ctr);
dentry->d_fsdata = oe; }
if (upperopaque) if (upperopaque)
ovl_dentry_set_opaque(dentry); ovl_dentry_set_opaque(dentry);
...@@ -1106,14 +1220,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1106,14 +1220,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (upperdentry || ctr) { if (upperdentry || ctr) {
struct ovl_inode_params oip = { struct ovl_inode_params oip = {
.upperdentry = upperdentry, .upperdentry = upperdentry,
.lowerpath = stack, .oe = oe,
.index = index, .index = index,
.numlower = ctr,
.redirect = upperredirect, .redirect = upperredirect,
.lowerdata = (ctr > 1 && !d.is_dir) ?
stack[ctr - 1].dentry : NULL,
}; };
/* 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); inode = ovl_get_inode(dentry->d_sb, &oip);
err = PTR_ERR(inode); err = PTR_ERR(inode);
if (IS_ERR(inode)) if (IS_ERR(inode))
...@@ -1122,8 +1238,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1122,8 +1238,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
ovl_set_flag(OVL_UPPERDATA, inode); ovl_set_flag(OVL_UPPERDATA, inode);
} }
ovl_dentry_update_reval(dentry, upperdentry, ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode));
DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE);
revert_creds(old_cred); revert_creds(old_cred);
if (origin_path) { if (origin_path) {
...@@ -1131,18 +1246,15 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1131,18 +1246,15 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
kfree(origin_path); kfree(origin_path);
} }
dput(index); dput(index);
kfree(stack); ovl_stack_free(stack, ctr);
kfree(d.redirect); kfree(d.redirect);
return d_splice_alias(inode, dentry); return d_splice_alias(inode, dentry);
out_free_oe: out_free_oe:
dentry->d_fsdata = NULL; ovl_free_entry(oe);
kfree(oe);
out_put: out_put:
dput(index); dput(index);
for (i = 0; i < ctr; i++) ovl_stack_free(stack, ctr);
dput(stack[i].dentry);
kfree(stack);
out_put_upper: out_put_upper:
if (origin_path) { if (origin_path) {
dput(origin_path->dentry); dput(origin_path->dentry);
...@@ -1158,7 +1270,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1158,7 +1270,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
bool ovl_lower_positive(struct dentry *dentry) 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 qstr *name = &dentry->d_name;
const struct cred *old_cred; const struct cred *old_cred;
unsigned int i; unsigned int i;
...@@ -1178,12 +1290,13 @@ bool ovl_lower_positive(struct dentry *dentry) ...@@ -1178,12 +1290,13 @@ bool ovl_lower_positive(struct dentry *dentry)
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
/* Positive upper -> have to look up lower to see whether it exists */ /* 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 *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), this = lookup_one_positive_unlocked(
name->name, lowerdir, name->len); mnt_idmap(parentpath->layer->mnt),
name->name, parentpath->dentry, name->len);
if (IS_ERR(this)) { if (IS_ERR(this)) {
switch (PTR_ERR(this)) { switch (PTR_ERR(this)) {
case -ENOENT: case -ENOENT:
......
...@@ -57,12 +57,27 @@ enum ovl_entry_flag { ...@@ -57,12 +57,27 @@ enum ovl_entry_flag {
OVL_E_CONNECTED, 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 { enum {
OVL_XINO_OFF, OVL_XINO_OFF,
OVL_XINO_AUTO, OVL_XINO_AUTO,
OVL_XINO_ON, OVL_XINO_ON,
}; };
/* The set of options that user requested explicitly via mount options */
struct ovl_opt_set {
bool metacopy;
bool redirect;
bool nfs_export;
bool index;
};
/* /*
* The tuple (fh,uuid) is a universal unique identifier for a copy up origin, * The tuple (fh,uuid) is a universal unique identifier for a copy up origin,
* where: * where:
...@@ -353,17 +368,29 @@ static inline bool ovl_open_flags_need_copy_up(int flags) ...@@ -353,17 +368,29 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)); 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);
}
/* params.c */
#define OVL_MAX_STACK 500
struct ovl_fs_context_layer {
char *name;
struct path path;
};
struct ovl_fs_context {
struct path upper;
struct path work;
size_t capacity;
size_t nr; /* includes nr_data */
size_t nr_data;
struct ovl_opt_set set;
struct ovl_fs_context_layer *lower;
};
int ovl_parse_param_upperdir(const char *name, struct fs_context *fc,
bool workdir);
int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc);
void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx);
/* util.c */ /* util.c */
int ovl_want_write(struct dentry *dentry); int ovl_want_write(struct dentry *dentry);
...@@ -374,21 +401,30 @@ int ovl_can_decode_fh(struct super_block *sb); ...@@ -374,21 +401,30 @@ int ovl_can_decode_fh(struct super_block *sb);
struct dentry *ovl_indexdir(struct super_block *sb); struct dentry *ovl_indexdir(struct super_block *sb);
bool ovl_index_all(struct super_block *sb); bool ovl_index_all(struct super_block *sb);
bool ovl_verify_lower(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); struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
void ovl_free_entry(struct ovl_entry *oe);
bool ovl_dentry_remote(struct dentry *dentry); bool ovl_dentry_remote(struct dentry *dentry);
void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *upperdentry, void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry);
unsigned int mask); 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,
struct ovl_entry *oe, unsigned int mask);
bool ovl_dentry_weird(struct dentry *dentry); bool ovl_dentry_weird(struct dentry *dentry);
enum ovl_path_type ovl_path_type(struct dentry *dentry); enum ovl_path_type ovl_path_type(struct dentry *dentry);
void ovl_path_upper(struct dentry *dentry, struct path *path); void ovl_path_upper(struct dentry *dentry, struct path *path);
void ovl_path_lower(struct dentry *dentry, struct path *path); void ovl_path_lower(struct dentry *dentry, struct path *path);
void ovl_path_lowerdata(struct dentry *dentry, struct path *path); void ovl_path_lowerdata(struct dentry *dentry, struct path *path);
void ovl_i_path_real(struct inode *inode, struct path *path); struct inode *ovl_i_path_real(struct inode *inode, struct path *path);
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path); 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_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_lowerdata(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_i_layer_lower(struct inode *inode);
const struct ovl_layer *ovl_layer_lower(struct dentry *dentry); const struct ovl_layer *ovl_layer_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
...@@ -398,6 +434,7 @@ struct inode *ovl_inode_lower(struct inode *inode); ...@@ -398,6 +434,7 @@ struct inode *ovl_inode_lower(struct inode *inode);
struct inode *ovl_inode_lowerdata(struct inode *inode); struct inode *ovl_inode_lowerdata(struct inode *inode);
struct inode *ovl_inode_real(struct inode *inode); struct inode *ovl_inode_real(struct inode *inode);
struct inode *ovl_inode_realdata(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); 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_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache);
void ovl_dentry_set_flag(unsigned long flag, struct dentry *dentry); void ovl_dentry_set_flag(unsigned long flag, struct dentry *dentry);
...@@ -412,7 +449,6 @@ bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags); ...@@ -412,7 +449,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_dentry_needs_data_copy_up_locked(struct dentry *dentry, int flags);
bool ovl_has_upperdata(struct inode *inode); bool ovl_has_upperdata(struct inode *inode);
void ovl_set_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); const char *ovl_dentry_get_redirect(struct dentry *dentry);
void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect); void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
void ovl_inode_update(struct inode *inode, struct dentry *upperdentry); void ovl_inode_update(struct inode *inode, struct dentry *upperdentry);
...@@ -480,31 +516,51 @@ static inline bool ovl_is_impuredir(struct super_block *sb, ...@@ -480,31 +516,51 @@ static inline bool ovl_is_impuredir(struct super_block *sb,
return ovl_path_check_dir_xattr(ofs, &upperpath, OVL_XATTR_IMPURE); 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 * With xino=auto, we do best effort to keep all inodes on same st_dev and
* d_ino consistent with st_ino. * d_ino consistent with st_ino.
* With xino=on, we do the same effort but we warn if we failed. * With xino=on, we do the same effort but we warn if we failed.
*/ */
static inline bool ovl_xino_warn(struct super_block *sb) static inline bool ovl_xino_warn(struct ovl_fs *ofs)
{ {
return OVL_FS(sb)->config.xino == OVL_XINO_ON; 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? */ /* All layers on same fs? */
static inline bool ovl_same_fs(struct super_block *sb) static inline bool ovl_same_fs(struct ovl_fs *ofs)
{ {
return OVL_FS(sb)->xino_mode == 0; return ofs->xino_mode == 0;
} }
/* All overlay inodes have same st_dev? */ /* All overlay inodes have same st_dev? */
static inline bool ovl_same_dev(struct super_block *sb) static inline bool ovl_same_dev(struct ovl_fs *ofs)
{ {
return OVL_FS(sb)->xino_mode >= 0; return ofs->xino_mode >= 0;
} }
static inline unsigned int ovl_xino_bits(struct super_block *sb) static inline unsigned int ovl_xino_bits(struct ovl_fs *ofs)
{ {
return ovl_same_dev(sb) ? OVL_FS(sb)->xino_mode : 0; return ovl_same_dev(ofs) ? ofs->xino_mode : 0;
} }
static inline void ovl_inode_lock(struct inode *inode) static inline void ovl_inode_lock(struct inode *inode)
...@@ -550,6 +606,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh); ...@@ -550,6 +606,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 *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
struct dentry *origin, bool verify); struct dentry *origin, bool verify);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path); 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, struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags); unsigned int flags);
bool ovl_lower_positive(struct dentry *dentry); bool ovl_lower_positive(struct dentry *dentry);
...@@ -646,11 +703,10 @@ bool ovl_is_private_xattr(struct super_block *sb, const char *name); ...@@ -646,11 +703,10 @@ bool ovl_is_private_xattr(struct super_block *sb, const char *name);
struct ovl_inode_params { struct ovl_inode_params {
struct inode *newinode; struct inode *newinode;
struct dentry *upperdentry; struct dentry *upperdentry;
struct ovl_path *lowerpath; struct ovl_entry *oe;
bool index; bool index;
unsigned int numlower;
char *redirect; char *redirect;
struct dentry *lowerdata; char *lowerdata_redirect;
}; };
void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip,
unsigned long ino, int fsid); unsigned long ino, int fsid);
......
...@@ -6,13 +6,10 @@ ...@@ -6,13 +6,10 @@
*/ */
struct ovl_config { struct ovl_config {
char *lowerdir;
char *upperdir; char *upperdir;
char *workdir; char *workdir;
bool default_permissions; bool default_permissions;
bool redirect_dir; int redirect_mode;
bool redirect_follow;
const char *redirect_mode;
bool index; bool index;
bool uuid; bool uuid;
bool nfs_export; bool nfs_export;
...@@ -32,6 +29,7 @@ struct ovl_sb { ...@@ -32,6 +29,7 @@ struct ovl_sb {
}; };
struct ovl_layer { struct ovl_layer {
/* ovl_free_fs() relies on @mnt being the first member! */
struct vfsmount *mnt; struct vfsmount *mnt;
/* Trap in ovl inode cache */ /* Trap in ovl inode cache */
struct inode *trap; struct inode *trap;
...@@ -40,18 +38,34 @@ struct ovl_layer { ...@@ -40,18 +38,34 @@ struct ovl_layer {
int idx; int idx;
/* One fsid per unique underlying sb (upper fsid == 0) */ /* One fsid per unique underlying sb (upper fsid == 0) */
int fsid; int fsid;
char *name;
}; };
/*
* ovl_free_fs() relies on @mnt being the first member when unmounting
* the private mounts created for each layer. Let's check both the
* offset and type.
*/
static_assert(offsetof(struct ovl_layer, mnt) == 0);
static_assert(__same_type(typeof_member(struct ovl_layer, mnt), struct vfsmount *));
struct ovl_path { struct ovl_path {
const struct ovl_layer *layer; const struct ovl_layer *layer;
struct dentry *dentry; struct dentry *dentry;
}; };
struct ovl_entry {
unsigned int __numlower;
struct ovl_path __lowerstack[];
};
/* private information held for overlayfs's superblock */ /* private information held for overlayfs's superblock */
struct ovl_fs { struct ovl_fs {
unsigned int numlayer; unsigned int numlayer;
/* Number of unique fs among layers including upper fs */ /* Number of unique fs among layers including upper fs */
unsigned int numfs; unsigned int numfs;
/* Number of data-only lower layers */
unsigned int numdatalayer;
const struct ovl_layer *layers; const struct ovl_layer *layers;
struct ovl_sb *fs; struct ovl_sb *fs;
/* workbasedir is the path at workdir= mount option */ /* workbasedir is the path at workdir= mount option */
...@@ -70,7 +84,6 @@ struct ovl_fs { ...@@ -70,7 +84,6 @@ struct ovl_fs {
/* Did we take the inuse lock? */ /* Did we take the inuse lock? */
bool upperdir_locked; bool upperdir_locked;
bool workdir_locked; bool workdir_locked;
bool share_whiteout;
/* Traps in ovl inode cache */ /* Traps in ovl inode cache */
struct inode *workbasedir_trap; struct inode *workbasedir_trap;
struct inode *workdir_trap; struct inode *workdir_trap;
...@@ -79,12 +92,19 @@ struct ovl_fs { ...@@ -79,12 +92,19 @@ struct ovl_fs {
int xino_mode; int xino_mode;
/* For allocation of non-persistent inode numbers */ /* For allocation of non-persistent inode numbers */
atomic_long_t last_ino; atomic_long_t last_ino;
/* Whiteout dentry cache */ /* Shared whiteout cache */
struct dentry *whiteout; struct dentry *whiteout;
bool no_shared_whiteout;
/* r/o snapshot of upperdir sb's only taken on volatile mounts */ /* r/o snapshot of upperdir sb's only taken on volatile mounts */
errseq_t errseq; errseq_t errseq;
}; };
/* Number of lower layers, not including data-only layers */
static inline unsigned int ovl_numlowerlayer(struct ovl_fs *ofs)
{
return ofs->numlayer - ofs->numdatalayer - 1;
}
static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs) static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs)
{ {
return ofs->layers[0].mnt; return ofs->layers[0].mnt;
...@@ -105,36 +125,53 @@ static inline bool ovl_should_sync(struct ovl_fs *ofs) ...@@ -105,36 +125,53 @@ static inline bool ovl_should_sync(struct ovl_fs *ofs)
return !ofs->config.ovl_volatile; return !ofs->config.ovl_volatile;
} }
/* private information held for every overlayfs dentry */ static inline unsigned int ovl_numlower(struct ovl_entry *oe)
struct ovl_entry { {
union { return oe ? oe->__numlower : 0;
struct { }
unsigned long flags;
};
struct rcu_head rcu;
};
unsigned numlower;
struct ovl_path lowerstack[];
};
struct ovl_entry *ovl_alloc_entry(unsigned int numlower); 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) static inline struct ovl_path *ovl_lowerpath(struct ovl_entry *oe)
{ {
return (struct ovl_entry *) dentry->d_fsdata; 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;
}
/* 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);
return lowerdata ? READ_ONCE(lowerdata->dentry) : NULL;
}
/* private information held for every overlayfs dentry */
static inline unsigned long *OVL_E_FLAGS(struct dentry *dentry)
{
return (unsigned long *) &dentry->d_fsdata;
} }
struct ovl_inode { struct ovl_inode {
union { union {
struct ovl_dir_cache *cache; /* directory */ struct ovl_dir_cache *cache; /* directory */
struct inode *lowerdata; /* regular file */ const char *lowerdata_redirect; /* regular file */
}; };
const char *redirect; const char *redirect;
u64 version; u64 version;
unsigned long flags; unsigned long flags;
struct inode vfs_inode; struct inode vfs_inode;
struct dentry *__upperdentry; struct dentry *__upperdentry;
struct ovl_path lowerpath; struct ovl_entry *oe;
/* synchronize copy up and more */ /* synchronize copy up and more */
struct mutex lock; struct mutex lock;
...@@ -145,6 +182,16 @@ static inline struct ovl_inode *OVL_I(struct inode *inode) ...@@ -145,6 +182,16 @@ static inline struct ovl_inode *OVL_I(struct inode *inode)
return container_of(inode, struct ovl_inode, vfs_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) static inline struct dentry *ovl_upperdentry_dereference(struct ovl_inode *oi)
{ {
return READ_ONCE(oi->__upperdentry); return READ_ONCE(oi->__upperdentry);
......
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/posix_acl_xattr.h>
#include <linux/xattr.h>
#include "overlayfs.h"
static ssize_t ovl_parse_param_split_lowerdirs(char *str)
{
ssize_t nr_layers = 1, nr_colons = 0;
char *s, *d;
for (s = d = str;; s++, d++) {
if (*s == '\\') {
s++;
} else if (*s == ':') {
bool next_colon = (*(s + 1) == ':');
nr_colons++;
if (nr_colons == 2 && next_colon) {
pr_err("only single ':' or double '::' sequences of unescaped colons in lowerdir mount option allowed.\n");
return -EINVAL;
}
/* count layers, not colons */
if (!next_colon)
nr_layers++;
*d = '\0';
continue;
}
*d = *s;
if (!*s) {
/* trailing colons */
if (nr_colons) {
pr_err("unescaped trailing colons in lowerdir mount option.\n");
return -EINVAL;
}
break;
}
nr_colons = 0;
}
return nr_layers;
}
static int ovl_mount_dir_noesc(const char *name, struct path *path)
{
int err = -EINVAL;
if (!*name) {
pr_err("empty lowerdir\n");
goto out;
}
err = kern_path(name, LOOKUP_FOLLOW, path);
if (err) {
pr_err("failed to resolve '%s': %i\n", name, err);
goto out;
}
err = -EINVAL;
if (ovl_dentry_weird(path->dentry)) {
pr_err("filesystem on '%s' not supported\n", name);
goto out_put;
}
if (!d_is_dir(path->dentry)) {
pr_err("'%s' not a directory\n", name);
goto out_put;
}
return 0;
out_put:
path_put_init(path);
out:
return err;
}
static void ovl_unescape(char *s)
{
char *d = s;
for (;; s++, d++) {
if (*s == '\\')
s++;
*d = *s;
if (!*s)
break;
}
}
static int ovl_mount_dir(const char *name, struct path *path)
{
int err = -ENOMEM;
char *tmp = kstrdup(name, GFP_KERNEL);
if (tmp) {
ovl_unescape(tmp);
err = ovl_mount_dir_noesc(tmp, path);
if (!err && path->dentry->d_flags & DCACHE_OP_REAL) {
pr_err("filesystem on '%s' not supported as upperdir\n",
tmp);
path_put_init(path);
err = -EINVAL;
}
kfree(tmp);
}
return err;
}
int ovl_parse_param_upperdir(const char *name, struct fs_context *fc,
bool workdir)
{
int err;
struct ovl_fs *ofs = fc->s_fs_info;
struct ovl_config *config = &ofs->config;
struct ovl_fs_context *ctx = fc->fs_private;
struct path path;
char *dup;
err = ovl_mount_dir(name, &path);
if (err)
return err;
/*
* Check whether upper path is read-only here to report failures
* early. Don't forget to recheck when the superblock is created
* as the mount attributes could change.
*/
if (__mnt_is_readonly(path.mnt)) {
path_put(&path);
return -EINVAL;
}
dup = kstrdup(name, GFP_KERNEL);
if (!dup) {
path_put(&path);
return -ENOMEM;
}
if (workdir) {
kfree(config->workdir);
config->workdir = dup;
path_put(&ctx->work);
ctx->work = path;
} else {
kfree(config->upperdir);
config->upperdir = dup;
path_put(&ctx->upper);
ctx->upper = path;
}
return 0;
}
void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx)
{
for (size_t nr = 0; nr < ctx->nr; nr++) {
path_put(&ctx->lower[nr].path);
kfree(ctx->lower[nr].name);
ctx->lower[nr].name = NULL;
}
ctx->nr = 0;
ctx->nr_data = 0;
}
/*
* Parse lowerdir= mount option:
*
* (1) lowerdir=/lower1:/lower2:/lower3::/data1::/data2
* Set "/lower1", "/lower2", and "/lower3" as lower layers and
* "/data1" and "/data2" as data lower layers. Any existing lower
* layers are replaced.
* (2) lowerdir=:/lower4
* Append "/lower4" to current stack of lower layers. This requires
* that there already is at least one lower layer configured.
* (3) lowerdir=::/lower5
* Append data "/lower5" as data lower layer. This requires that
* there's at least one regular lower layer present.
*/
int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
{
int err;
struct ovl_fs_context *ctx = fc->fs_private;
struct ovl_fs_context_layer *l;
char *dup = NULL, *dup_iter;
ssize_t nr_lower = 0, nr = 0, nr_data = 0;
bool append = false, data_layer = false;
/*
* Ensure we're backwards compatible with mount(2)
* by allowing relative paths.
*/
/* drop all existing lower layers */
if (!*name) {
ovl_parse_param_drop_lowerdir(ctx);
return 0;
}
if (strncmp(name, "::", 2) == 0) {
/*
* This is a data layer.
* There must be at least one regular lower layer
* specified.
*/
if (ctx->nr == 0) {
pr_err("data lower layers without regular lower layers not allowed");
return -EINVAL;
}
/* Skip the leading "::". */
name += 2;
data_layer = true;
/*
* A data layer is automatically an append as there
* must've been at least one regular lower layer.
*/
append = true;
} else if (*name == ':') {
/*
* This is a regular lower layer.
* If users want to append a layer enforce that they
* have already specified a first layer before. It's
* better to be strict.
*/
if (ctx->nr == 0) {
pr_err("cannot append layer if no previous layer has been specified");
return -EINVAL;
}
/*
* Once a sequence of data layers has started regular
* lower layers are forbidden.
*/
if (ctx->nr_data > 0) {
pr_err("regular lower layers cannot follow data lower layers");
return -EINVAL;
}
/* Skip the leading ":". */
name++;
append = true;
}
dup = kstrdup(name, GFP_KERNEL);
if (!dup)
return -ENOMEM;
err = -EINVAL;
nr_lower = ovl_parse_param_split_lowerdirs(dup);
if (nr_lower < 0)
goto out_err;
if ((nr_lower > OVL_MAX_STACK) ||
(append && (size_add(ctx->nr, nr_lower) > OVL_MAX_STACK))) {
pr_err("too many lower directories, limit is %d\n", OVL_MAX_STACK);
goto out_err;
}
if (!append)
ovl_parse_param_drop_lowerdir(ctx);
/*
* (1) append
*
* We want nr <= nr_lower <= capacity We know nr > 0 and nr <=
* capacity. If nr == 0 this wouldn't be append. If nr +
* nr_lower is <= capacity then nr <= nr_lower <= capacity
* already holds. If nr + nr_lower exceeds capacity, we realloc.
*
* (2) replace
*
* Ensure we're backwards compatible with mount(2) which allows
* "lowerdir=/a:/b:/c,lowerdir=/d:/e:/f" causing the last
* specified lowerdir mount option to win.
*
* We want nr <= nr_lower <= capacity We know either (i) nr == 0
* or (ii) nr > 0. We also know nr_lower > 0. The capacity
* could've been changed multiple times already so we only know
* nr <= capacity. If nr + nr_lower > capacity we realloc,
* otherwise nr <= nr_lower <= capacity holds already.
*/
nr_lower += ctx->nr;
if (nr_lower > ctx->capacity) {
err = -ENOMEM;
l = krealloc_array(ctx->lower, nr_lower, sizeof(*ctx->lower),
GFP_KERNEL_ACCOUNT);
if (!l)
goto out_err;
ctx->lower = l;
ctx->capacity = nr_lower;
}
/*
* (3) By (1) and (2) we know nr <= nr_lower <= capacity.
* (4) If ctx->nr == 0 => replace
* We have verified above that the lowerdir mount option
* isn't an append, i.e., the lowerdir mount option
* doesn't start with ":" or "::".
* (4.1) The lowerdir mount options only contains regular lower
* layers ":".
* => Nothing to verify.
* (4.2) The lowerdir mount options contains regular ":" and
* data "::" layers.
* => We need to verify that data lower layers "::" aren't
* followed by regular ":" lower layers
* (5) If ctx->nr > 0 => append
* We know that there's at least one regular layer
* otherwise we would've failed when parsing the previous
* lowerdir mount option.
* (5.1) The lowerdir mount option is a regular layer ":" append
* => We need to verify that no data layers have been
* specified before.
* (5.2) The lowerdir mount option is a data layer "::" append
* We know that there's at least one regular layer or
* other data layers. => There's nothing to verify.
*/
dup_iter = dup;
for (nr = ctx->nr; nr < nr_lower; nr++) {
l = &ctx->lower[nr];
memset(l, 0, sizeof(*l));
err = ovl_mount_dir_noesc(dup_iter, &l->path);
if (err)
goto out_put;
err = -ENOMEM;
l->name = kstrdup(dup_iter, GFP_KERNEL_ACCOUNT);
if (!l->name)
goto out_put;
if (data_layer)
nr_data++;
/* Calling strchr() again would overrun. */
if ((nr + 1) == nr_lower)
break;
err = -EINVAL;
dup_iter = strchr(dup_iter, '\0') + 1;
if (*dup_iter) {
/*
* This is a regular layer so we require that
* there are no data layers.
*/
if ((ctx->nr_data + nr_data) > 0) {
pr_err("regular lower layers cannot follow data lower layers");
goto out_put;
}
data_layer = false;
continue;
}
/* This is a data lower layer. */
data_layer = true;
dup_iter++;
}
ctx->nr = nr_lower;
ctx->nr_data += nr_data;
kfree(dup);
return 0;
out_put:
/*
* We know nr >= ctx->nr < nr_lower. If we failed somewhere
* we want to undo until nr == ctx->nr. This is correct for
* both ctx->nr == 0 and ctx->nr > 0.
*/
for (; nr >= ctx->nr; nr--) {
l = &ctx->lower[nr];
kfree(l->name);
l->name = NULL;
path_put(&l->path);
/* don't overflow */
if (nr == 0)
break;
}
out_err:
kfree(dup);
/* Intentionally don't realloc to a smaller size. */
return err;
}
...@@ -118,7 +118,7 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd, ...@@ -118,7 +118,7 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
return false; return false;
/* Always recalc d_ino when remapping lower inode numbers */ /* Always recalc d_ino when remapping lower inode numbers */
if (ovl_xino_bits(rdd->dentry->d_sb)) if (ovl_xino_bits(OVL_FS(rdd->dentry->d_sb)))
return true; return true;
/* Always recalc d_ino for parent */ /* Always recalc d_ino for parent */
...@@ -460,13 +460,14 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry ...@@ -460,13 +460,14 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
{ {
struct dentry *dir = path->dentry; struct dentry *dir = path->dentry;
struct ovl_fs *ofs = OVL_FS(dir->d_sb);
struct dentry *this = NULL; struct dentry *this = NULL;
enum ovl_path_type type; enum ovl_path_type type;
u64 ino = p->real_ino; u64 ino = p->real_ino;
int xinobits = ovl_xino_bits(dir->d_sb); int xinobits = ovl_xino_bits(ofs);
int err = 0; int err = 0;
if (!ovl_same_dev(dir->d_sb)) if (!ovl_same_dev(ofs))
goto out; goto out;
if (p->name[0] == '.') { if (p->name[0] == '.') {
...@@ -515,7 +516,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry ...@@ -515,7 +516,7 @@ static int ovl_cache_update_ino(const struct path *path, struct ovl_cache_entry
ino = ovl_remap_lower_ino(ino, xinobits, ino = ovl_remap_lower_ino(ino, xinobits,
ovl_layer_lower(this)->fsid, ovl_layer_lower(this)->fsid,
p->name, p->len, p->name, p->len,
ovl_xino_warn(dir->d_sb)); ovl_xino_warn(ofs));
} }
out: out:
...@@ -694,12 +695,13 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx) ...@@ -694,12 +695,13 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
int err; int err;
struct ovl_dir_file *od = file->private_data; struct ovl_dir_file *od = file->private_data;
struct dentry *dir = file->f_path.dentry; struct dentry *dir = file->f_path.dentry;
struct ovl_fs *ofs = OVL_FS(dir->d_sb);
const struct ovl_layer *lower_layer = ovl_layer_lower(dir); const struct ovl_layer *lower_layer = ovl_layer_lower(dir);
struct ovl_readdir_translate rdt = { struct ovl_readdir_translate rdt = {
.ctx.actor = ovl_fill_real, .ctx.actor = ovl_fill_real,
.orig_ctx = ctx, .orig_ctx = ctx,
.xinobits = ovl_xino_bits(dir->d_sb), .xinobits = ovl_xino_bits(ofs),
.xinowarn = ovl_xino_warn(dir->d_sb), .xinowarn = ovl_xino_warn(ofs),
}; };
if (rdt.xinobits && lower_layer) if (rdt.xinobits && lower_layer)
...@@ -735,6 +737,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -735,6 +737,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
{ {
struct ovl_dir_file *od = file->private_data; struct ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
const struct cred *old_cred; const struct cred *old_cred;
int err; int err;
...@@ -749,8 +752,8 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -749,8 +752,8 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
* dir is impure then need to adjust d_ino for copied up * dir is impure then need to adjust d_ino for copied up
* entries. * entries.
*/ */
if (ovl_xino_bits(dentry->d_sb) || if (ovl_xino_bits(ofs) ||
(ovl_same_fs(dentry->d_sb) && (ovl_same_fs(ofs) &&
(ovl_is_impure_dir(file) || (ovl_is_impure_dir(file) ||
OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) { OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) {
err = ovl_iterate_real(file, ctx); err = ovl_iterate_real(file, ctx);
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include "overlayfs.h" #include "overlayfs.h"
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
...@@ -25,8 +27,6 @@ MODULE_LICENSE("GPL"); ...@@ -25,8 +27,6 @@ MODULE_LICENSE("GPL");
struct ovl_dir_cache; struct ovl_dir_cache;
#define OVL_MAX_STACK 500
static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR); static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR);
module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644); module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);
MODULE_PARM_DESC(redirect_dir, MODULE_PARM_DESC(redirect_dir,
...@@ -54,29 +54,11 @@ module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644); ...@@ -54,29 +54,11 @@ module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644);
MODULE_PARM_DESC(xino_auto, MODULE_PARM_DESC(xino_auto,
"Auto enable xino feature"); "Auto enable xino feature");
static void ovl_entry_stack_free(struct ovl_entry *oe)
{
unsigned int i;
for (i = 0; i < oe->numlower; i++)
dput(oe->lowerstack[i].dentry);
}
static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY); static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY);
module_param_named(metacopy, ovl_metacopy_def, bool, 0644); module_param_named(metacopy, ovl_metacopy_def, bool, 0644);
MODULE_PARM_DESC(metacopy, MODULE_PARM_DESC(metacopy,
"Default to on or off for the metadata only copy up feature"); "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_entry_stack_free(oe);
kfree_rcu(oe, rcu);
}
}
static struct dentry *ovl_d_real(struct dentry *dentry, static struct dentry *ovl_d_real(struct dentry *dentry,
const struct inode *inode) const struct inode *inode)
{ {
...@@ -99,6 +81,15 @@ static struct dentry *ovl_d_real(struct dentry *dentry, ...@@ -99,6 +81,15 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
if (real && !inode && ovl_has_upperdata(d_inode(dentry))) if (real && !inode && ovl_has_upperdata(d_inode(dentry)))
return real; return real;
/*
* 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); lower = ovl_dentry_lowerdata(dentry);
if (!lower) if (!lower)
goto bug; goto bug;
...@@ -121,6 +112,9 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak) ...@@ -121,6 +112,9 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak)
{ {
int ret = 1; int ret = 1;
if (!d)
return 1;
if (weak) { if (weak) {
if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE) if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE)
ret = d->d_op->d_weak_revalidate(d, flags); ret = d->d_op->d_weak_revalidate(d, flags);
...@@ -138,7 +132,8 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak) ...@@ -138,7 +132,8 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak)
static int ovl_dentry_revalidate_common(struct dentry *dentry, static int ovl_dentry_revalidate_common(struct dentry *dentry,
unsigned int flags, bool weak) unsigned int flags, bool weak)
{ {
struct ovl_entry *oe = dentry->d_fsdata; struct ovl_entry *oe = OVL_E(dentry);
struct ovl_path *lowerstack = ovl_lowerstack(oe);
struct inode *inode = d_inode_rcu(dentry); struct inode *inode = d_inode_rcu(dentry);
struct dentry *upper; struct dentry *upper;
unsigned int i; unsigned int i;
...@@ -152,10 +147,9 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry, ...@@ -152,10 +147,9 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry,
if (upper) if (upper)
ret = ovl_revalidate_real(upper, flags, weak); ret = ovl_revalidate_real(upper, flags, weak);
for (i = 0; ret > 0 && i < oe->numlower; i++) { for (i = 0; ret > 0 && i < ovl_numlower(oe); i++)
ret = ovl_revalidate_real(oe->lowerstack[i].dentry, flags, ret = ovl_revalidate_real(lowerstack[i].dentry, flags, weak);
weak);
}
return ret; return ret;
} }
...@@ -170,7 +164,6 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -170,7 +164,6 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
} }
static const struct dentry_operations ovl_dentry_operations = { static const struct dentry_operations ovl_dentry_operations = {
.d_release = ovl_dentry_release,
.d_real = ovl_d_real, .d_real = ovl_d_real,
.d_revalidate = ovl_dentry_revalidate, .d_revalidate = ovl_dentry_revalidate,
.d_weak_revalidate = ovl_dentry_weak_revalidate, .d_weak_revalidate = ovl_dentry_weak_revalidate,
...@@ -190,9 +183,8 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) ...@@ -190,9 +183,8 @@ static struct inode *ovl_alloc_inode(struct super_block *sb)
oi->version = 0; oi->version = 0;
oi->flags = 0; oi->flags = 0;
oi->__upperdentry = NULL; oi->__upperdentry = NULL;
oi->lowerpath.dentry = NULL; oi->lowerdata_redirect = NULL;
oi->lowerpath.layer = NULL; oi->oe = NULL;
oi->lowerdata = NULL;
mutex_init(&oi->lock); mutex_init(&oi->lock);
return &oi->vfs_inode; return &oi->vfs_inode;
...@@ -212,11 +204,11 @@ static void ovl_destroy_inode(struct inode *inode) ...@@ -212,11 +204,11 @@ static void ovl_destroy_inode(struct inode *inode)
struct ovl_inode *oi = OVL_I(inode); struct ovl_inode *oi = OVL_I(inode);
dput(oi->__upperdentry); dput(oi->__upperdentry);
dput(oi->lowerpath.dentry); ovl_free_entry(oi->oe);
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
ovl_dir_cache_free(inode); ovl_dir_cache_free(inode);
else else
iput(oi->lowerdata); kfree(oi->lowerdata_redirect);
} }
static void ovl_free_fs(struct ovl_fs *ofs) static void ovl_free_fs(struct ovl_fs *ofs)
...@@ -241,6 +233,7 @@ static void ovl_free_fs(struct ovl_fs *ofs) ...@@ -241,6 +233,7 @@ static void ovl_free_fs(struct ovl_fs *ofs)
for (i = 0; i < ofs->numlayer; i++) { for (i = 0; i < ofs->numlayer; i++) {
iput(ofs->layers[i].trap); iput(ofs->layers[i].trap);
mounts[i] = ofs->layers[i].mnt; mounts[i] = ofs->layers[i].mnt;
kfree(ofs->layers[i].name);
} }
kern_unmount_array(mounts, ofs->numlayer); kern_unmount_array(mounts, ofs->numlayer);
kfree(ofs->layers); kfree(ofs->layers);
...@@ -248,10 +241,8 @@ static void ovl_free_fs(struct ovl_fs *ofs) ...@@ -248,10 +241,8 @@ static void ovl_free_fs(struct ovl_fs *ofs)
free_anon_bdev(ofs->fs[i].pseudo_dev); free_anon_bdev(ofs->fs[i].pseudo_dev);
kfree(ofs->fs); kfree(ofs->fs);
kfree(ofs->config.lowerdir);
kfree(ofs->config.upperdir); kfree(ofs->config.upperdir);
kfree(ofs->config.workdir); kfree(ofs->config.workdir);
kfree(ofs->config.redirect_mode);
if (ofs->creator_cred) if (ofs->creator_cred)
put_cred(ofs->creator_cred); put_cred(ofs->creator_cred);
kfree(ofs); kfree(ofs);
...@@ -261,7 +252,8 @@ static void ovl_put_super(struct super_block *sb) ...@@ -261,7 +252,8 @@ static void ovl_put_super(struct super_block *sb)
{ {
struct ovl_fs *ofs = sb->s_fs_info; struct ovl_fs *ofs = sb->s_fs_info;
ovl_free_fs(ofs); if (ofs)
ovl_free_fs(ofs);
} }
/* Sync real dirty inodes in upper filesystem (if it exists) */ /* Sync real dirty inodes in upper filesystem (if it exists) */
...@@ -337,17 +329,38 @@ static bool ovl_force_readonly(struct ovl_fs *ofs) ...@@ -337,17 +329,38 @@ static bool ovl_force_readonly(struct ovl_fs *ofs)
return (!ovl_upper_mnt(ofs) || !ofs->workdir); 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 const char * const ovl_xino_str[] = { static int ovl_redirect_mode_def(void)
"off", {
"auto", return ovl_redirect_dir_def ? OVL_REDIRECT_ON :
"on", ovl_redirect_always_follow ? OVL_REDIRECT_FOLLOW :
OVL_REDIRECT_NOFOLLOW;
}
static const struct constant_table ovl_parameter_xino[] = {
{ "off", OVL_XINO_OFF },
{ "auto", OVL_XINO_AUTO },
{ "on", OVL_XINO_ON },
{}
}; };
static const char *ovl_xino_mode(struct ovl_config *config)
{
return ovl_parameter_xino[config->xino].name;
}
static inline int ovl_xino_def(void) static inline int ovl_xino_def(void)
{ {
return ovl_xino_auto_def ? OVL_XINO_AUTO : OVL_XINO_OFF; return ovl_xino_auto_def ? OVL_XINO_AUTO : OVL_XINO_OFF;
...@@ -365,16 +378,26 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -365,16 +378,26 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
{ {
struct super_block *sb = dentry->d_sb; struct super_block *sb = dentry->d_sb;
struct ovl_fs *ofs = sb->s_fs_info; struct ovl_fs *ofs = sb->s_fs_info;
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer;
seq_show_option(m, "lowerdir", ofs->config.lowerdir); const struct ovl_layer *data_layers = &ofs->layers[nr_merged_lower];
/* ofs->layers[0] is the upper layer */
seq_printf(m, ",lowerdir=%s", ofs->layers[1].name);
/* dump regular lower layers */
for (nr = 2; nr < nr_merged_lower; nr++)
seq_printf(m, ":%s", ofs->layers[nr].name);
/* dump data lower layers */
for (nr = 0; nr < ofs->numdatalayer; nr++)
seq_printf(m, "::%s", data_layers[nr].name);
if (ofs->config.upperdir) { if (ofs->config.upperdir) {
seq_show_option(m, "upperdir", ofs->config.upperdir); seq_show_option(m, "upperdir", ofs->config.upperdir);
seq_show_option(m, "workdir", ofs->config.workdir); seq_show_option(m, "workdir", ofs->config.workdir);
} }
if (ofs->config.default_permissions) if (ofs->config.default_permissions)
seq_puts(m, ",default_permissions"); seq_puts(m, ",default_permissions");
if (strcmp(ofs->config.redirect_mode, ovl_redirect_mode_def()) != 0) if (ofs->config.redirect_mode != ovl_redirect_mode_def())
seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode); seq_printf(m, ",redirect_dir=%s",
ovl_redirect_mode(&ofs->config));
if (ofs->config.index != ovl_index_def) if (ofs->config.index != ovl_index_def)
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off"); seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
if (!ofs->config.uuid) if (!ofs->config.uuid)
...@@ -382,8 +405,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -382,8 +405,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
if (ofs->config.nfs_export != ovl_nfs_export_def) if (ofs->config.nfs_export != ovl_nfs_export_def)
seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ? seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
"on" : "off"); "on" : "off");
if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(sb)) if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(ofs))
seq_printf(m, ",xino=%s", ovl_xino_str[ofs->config.xino]); seq_printf(m, ",xino=%s", ovl_xino_mode(&ofs->config));
if (ofs->config.metacopy != ovl_metacopy_def) if (ofs->config.metacopy != ovl_metacopy_def)
seq_printf(m, ",metacopy=%s", seq_printf(m, ",metacopy=%s",
ofs->config.metacopy ? "on" : "off"); ofs->config.metacopy ? "on" : "off");
...@@ -394,16 +417,17 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -394,16 +417,17 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
return 0; return 0;
} }
static int ovl_remount(struct super_block *sb, int *flags, char *data) static int ovl_reconfigure(struct fs_context *fc)
{ {
struct super_block *sb = fc->root->d_sb;
struct ovl_fs *ofs = sb->s_fs_info; struct ovl_fs *ofs = sb->s_fs_info;
struct super_block *upper_sb; struct super_block *upper_sb;
int ret = 0; int ret = 0;
if (!(*flags & SB_RDONLY) && ovl_force_readonly(ofs)) if (!(fc->sb_flags & SB_RDONLY) && ovl_force_readonly(ofs))
return -EROFS; return -EROFS;
if (*flags & SB_RDONLY && !sb_rdonly(sb)) { if (fc->sb_flags & SB_RDONLY && !sb_rdonly(sb)) {
upper_sb = ovl_upper_mnt(ofs)->mnt_sb; upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
if (ovl_should_sync(ofs)) { if (ovl_should_sync(ofs)) {
down_read(&upper_sb->s_umount); down_read(&upper_sb->s_umount);
...@@ -424,215 +448,143 @@ static const struct super_operations ovl_super_operations = { ...@@ -424,215 +448,143 @@ static const struct super_operations ovl_super_operations = {
.sync_fs = ovl_sync_fs, .sync_fs = ovl_sync_fs,
.statfs = ovl_statfs, .statfs = ovl_statfs,
.show_options = ovl_show_options, .show_options = ovl_show_options,
.remount_fs = ovl_remount,
}; };
enum { enum {
OPT_LOWERDIR, Opt_lowerdir,
OPT_UPPERDIR, Opt_upperdir,
OPT_WORKDIR, Opt_workdir,
OPT_DEFAULT_PERMISSIONS, Opt_default_permissions,
OPT_REDIRECT_DIR, Opt_redirect_dir,
OPT_INDEX_ON, Opt_index,
OPT_INDEX_OFF, Opt_uuid,
OPT_UUID_ON, Opt_nfs_export,
OPT_UUID_OFF, Opt_userxattr,
OPT_NFS_EXPORT_ON, Opt_xino,
OPT_USERXATTR, Opt_metacopy,
OPT_NFS_EXPORT_OFF, Opt_volatile,
OPT_XINO_ON,
OPT_XINO_OFF,
OPT_XINO_AUTO,
OPT_METACOPY_ON,
OPT_METACOPY_OFF,
OPT_VOLATILE,
OPT_ERR,
}; };
static const match_table_t ovl_tokens = { static const struct constant_table ovl_parameter_bool[] = {
{OPT_LOWERDIR, "lowerdir=%s"}, { "on", true },
{OPT_UPPERDIR, "upperdir=%s"}, { "off", false },
{OPT_WORKDIR, "workdir=%s"}, {}
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
{OPT_REDIRECT_DIR, "redirect_dir=%s"},
{OPT_INDEX_ON, "index=on"},
{OPT_INDEX_OFF, "index=off"},
{OPT_USERXATTR, "userxattr"},
{OPT_UUID_ON, "uuid=on"},
{OPT_UUID_OFF, "uuid=off"},
{OPT_NFS_EXPORT_ON, "nfs_export=on"},
{OPT_NFS_EXPORT_OFF, "nfs_export=off"},
{OPT_XINO_ON, "xino=on"},
{OPT_XINO_OFF, "xino=off"},
{OPT_XINO_AUTO, "xino=auto"},
{OPT_METACOPY_ON, "metacopy=on"},
{OPT_METACOPY_OFF, "metacopy=off"},
{OPT_VOLATILE, "volatile"},
{OPT_ERR, NULL}
}; };
static char *ovl_next_opt(char **s) #define fsparam_string_empty(NAME, OPT) \
{ __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
char *sbegin = *s;
char *p; static const struct fs_parameter_spec ovl_parameter_spec[] = {
fsparam_string_empty("lowerdir", Opt_lowerdir),
fsparam_string("upperdir", Opt_upperdir),
fsparam_string("workdir", Opt_workdir),
fsparam_flag("default_permissions", Opt_default_permissions),
fsparam_enum("redirect_dir", Opt_redirect_dir, ovl_parameter_redirect_dir),
fsparam_enum("index", Opt_index, ovl_parameter_bool),
fsparam_enum("uuid", Opt_uuid, ovl_parameter_bool),
fsparam_enum("nfs_export", Opt_nfs_export, ovl_parameter_bool),
fsparam_flag("userxattr", Opt_userxattr),
fsparam_enum("xino", Opt_xino, ovl_parameter_xino),
fsparam_enum("metacopy", Opt_metacopy, ovl_parameter_bool),
fsparam_flag("volatile", Opt_volatile),
{}
};
if (sbegin == NULL) static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
return NULL; {
int err = 0;
struct fs_parse_result result;
struct ovl_fs *ofs = fc->s_fs_info;
struct ovl_config *config = &ofs->config;
struct ovl_fs_context *ctx = fc->fs_private;
int opt;
for (p = sbegin; *p; p++) { if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
if (*p == '\\') { /*
p++; * On remount overlayfs has always ignored all mount
if (!*p) * options no matter if malformed or not so for
break; * backwards compatibility we do the same here.
} else if (*p == ',') { */
*p = '\0'; if (fc->oldapi)
*s = p + 1; return 0;
return sbegin;
}
}
*s = NULL;
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 * Give us the freedom to allow changing mount options
* redirect following. * with the new mount api in the future. So instead of
* silently ignoring everything we report a proper
* error. This is only visible for users of the new
* mount api.
*/ */
config->redirect_follow = true; return invalfc(fc, "No changes allowed in reconfigure");
} else if (strcmp(mode, "follow") == 0) { }
config->redirect_follow = true;
} else if (strcmp(mode, "off") == 0) { opt = fs_parse(fc, ovl_parameter_spec, param, &result);
if (ovl_redirect_always_follow) if (opt < 0)
config->redirect_follow = true; return opt;
} else if (strcmp(mode, "nofollow") != 0) {
pr_err("bad mount option \"redirect_dir=%s\"\n", switch (opt) {
mode); case Opt_lowerdir:
err = ovl_parse_param_lowerdir(param->string, fc);
break;
case Opt_upperdir:
fallthrough;
case Opt_workdir:
err = ovl_parse_param_upperdir(param->string, fc,
(Opt_workdir == opt));
break;
case Opt_default_permissions:
config->default_permissions = true;
break;
case Opt_redirect_dir:
config->redirect_mode = result.uint_32;
if (config->redirect_mode == OVL_REDIRECT_OFF) {
config->redirect_mode = ovl_redirect_always_follow ?
OVL_REDIRECT_FOLLOW :
OVL_REDIRECT_NOFOLLOW;
}
ctx->set.redirect = true;
break;
case Opt_index:
config->index = result.uint_32;
ctx->set.index = true;
break;
case Opt_uuid:
config->uuid = result.uint_32;
break;
case Opt_nfs_export:
config->nfs_export = result.uint_32;
ctx->set.nfs_export = true;
break;
case Opt_xino:
config->xino = result.uint_32;
break;
case Opt_metacopy:
config->metacopy = result.uint_32;
ctx->set.metacopy = true;
break;
case Opt_volatile:
config->ovl_volatile = true;
break;
case Opt_userxattr:
config->userxattr = true;
break;
default:
pr_err("unrecognized mount option \"%s\" or missing value\n",
param->key);
return -EINVAL; return -EINVAL;
} }
return 0; return err;
} }
static int ovl_parse_opt(char *opt, struct ovl_config *config) static int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
struct ovl_config *config)
{ {
char *p; struct ovl_opt_set set = ctx->set;
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];
if (!*p)
continue;
token = match_token(p, ovl_tokens, args);
switch (token) {
case OPT_UPPERDIR:
kfree(config->upperdir);
config->upperdir = match_strdup(&args[0]);
if (!config->upperdir)
return -ENOMEM;
break;
case OPT_LOWERDIR:
kfree(config->lowerdir);
config->lowerdir = match_strdup(&args[0]);
if (!config->lowerdir)
return -ENOMEM;
break;
case OPT_WORKDIR:
kfree(config->workdir);
config->workdir = match_strdup(&args[0]);
if (!config->workdir)
return -ENOMEM;
break;
case OPT_DEFAULT_PERMISSIONS:
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;
redirect_opt = true;
break;
case OPT_INDEX_ON:
config->index = true;
index_opt = true;
break;
case OPT_INDEX_OFF:
config->index = false;
index_opt = true;
break;
case OPT_UUID_ON:
config->uuid = true;
break;
case OPT_UUID_OFF:
config->uuid = false;
break;
case OPT_NFS_EXPORT_ON:
config->nfs_export = true;
nfs_export_opt = true;
break;
case OPT_NFS_EXPORT_OFF:
config->nfs_export = false;
nfs_export_opt = true;
break;
case OPT_XINO_ON:
config->xino = OVL_XINO_ON;
break;
case OPT_XINO_OFF: if (ctx->nr_data > 0 && !config->metacopy) {
config->xino = OVL_XINO_OFF; pr_err("lower data-only dirs require metacopy support.\n");
break; return -EINVAL;
case OPT_XINO_AUTO:
config->xino = OVL_XINO_AUTO;
break;
case OPT_METACOPY_ON:
config->metacopy = true;
metacopy_opt = true;
break;
case OPT_METACOPY_OFF:
config->metacopy = false;
metacopy_opt = true;
break;
case OPT_VOLATILE:
config->ovl_volatile = true;
break;
case OPT_USERXATTR:
config->userxattr = true;
break;
default:
pr_err("unrecognized mount option \"%s\" or missing value\n",
p);
return -EINVAL;
}
} }
/* Workdir/index are useless in non-upper mount */ /* Workdir/index are useless in non-upper mount */
...@@ -643,9 +595,9 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -643,9 +595,9 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
kfree(config->workdir); kfree(config->workdir);
config->workdir = NULL; config->workdir = NULL;
} }
if (config->index && index_opt) { if (config->index && set.index) {
pr_info("option \"index=on\" is useless in a non-upper mount, ignore\n"); pr_info("option \"index=on\" is useless in a non-upper mount, ignore\n");
index_opt = false; set.index = false;
} }
config->index = false; config->index = false;
} }
...@@ -655,47 +607,44 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -655,47 +607,44 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
config->ovl_volatile = false; 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 * 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) if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
config->redirect_dir = true; config->redirect_mode = OVL_REDIRECT_ON;
/* Resolve metacopy -> redirect_dir dependency */ /* 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) { if (set.metacopy && set.redirect) {
pr_err("conflicting options: metacopy=on,redirect_dir=%s\n", pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
config->redirect_mode); ovl_redirect_mode(config));
return -EINVAL; return -EINVAL;
} }
if (redirect_opt) { if (set.redirect) {
/* /*
* There was an explicit redirect_dir=... that resulted * There was an explicit redirect_dir=... that resulted
* in this conflict. * in this conflict.
*/ */
pr_info("disabling metacopy due to redirect_dir=%s\n", pr_info("disabling metacopy due to redirect_dir=%s\n",
config->redirect_mode); ovl_redirect_mode(config));
config->metacopy = false; config->metacopy = false;
} else { } else {
/* Automatically enable redirect otherwise. */ /* Automatically enable redirect otherwise. */
config->redirect_follow = config->redirect_dir = true; config->redirect_mode = OVL_REDIRECT_ON;
} }
} }
/* Resolve nfs_export -> index dependency */ /* Resolve nfs_export -> index dependency */
if (config->nfs_export && !config->index) { 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"); pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
config->nfs_export = false; config->nfs_export = false;
} else if (nfs_export_opt && index_opt) { } else if (set.nfs_export && set.index) {
pr_err("conflicting options: nfs_export=on,index=off\n"); pr_err("conflicting options: nfs_export=on,index=off\n");
return -EINVAL; return -EINVAL;
} else if (index_opt) { } else if (set.index) {
/* /*
* There was an explicit index=off that resulted * There was an explicit index=off that resulted
* in this conflict. * in this conflict.
...@@ -710,11 +659,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -710,11 +659,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
/* Resolve nfs_export -> !metacopy dependency */ /* Resolve nfs_export -> !metacopy dependency */
if (config->nfs_export && config->metacopy) { if (config->nfs_export && config->metacopy) {
if (nfs_export_opt && metacopy_opt) { if (set.nfs_export && set.metacopy) {
pr_err("conflicting options: nfs_export=on,metacopy=on\n"); pr_err("conflicting options: nfs_export=on,metacopy=on\n");
return -EINVAL; return -EINVAL;
} }
if (metacopy_opt) { if (set.metacopy) {
/* /*
* There was an explicit metacopy=on that resulted * There was an explicit metacopy=on that resulted
* in this conflict. * in this conflict.
...@@ -734,12 +683,13 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -734,12 +683,13 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
/* Resolve userxattr -> !redirect && !metacopy dependency */ /* Resolve userxattr -> !redirect && !metacopy dependency */
if (config->userxattr) { if (config->userxattr) {
if (config->redirect_follow && redirect_opt) { if (set.redirect &&
config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
pr_err("conflicting options: userxattr,redirect_dir=%s\n", pr_err("conflicting options: userxattr,redirect_dir=%s\n",
config->redirect_mode); ovl_redirect_mode(config));
return -EINVAL; return -EINVAL;
} }
if (config->metacopy && metacopy_opt) { if (config->metacopy && set.metacopy) {
pr_err("conflicting options: userxattr,metacopy=on\n"); pr_err("conflicting options: userxattr,metacopy=on\n");
return -EINVAL; return -EINVAL;
} }
...@@ -749,7 +699,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -749,7 +699,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
* options must be explicitly enabled if used together with * options must be explicitly enabled if used together with
* userxattr. * userxattr.
*/ */
config->redirect_dir = config->redirect_follow = false; config->redirect_mode = OVL_REDIRECT_NOFOLLOW;
config->metacopy = false; config->metacopy = false;
} }
...@@ -849,69 +799,6 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs, ...@@ -849,69 +799,6 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
goto out_unlock; goto out_unlock;
} }
static void ovl_unescape(char *s)
{
char *d = s;
for (;; s++, d++) {
if (*s == '\\')
s++;
*d = *s;
if (!*s)
break;
}
}
static int ovl_mount_dir_noesc(const char *name, struct path *path)
{
int err = -EINVAL;
if (!*name) {
pr_err("empty lowerdir\n");
goto out;
}
err = kern_path(name, LOOKUP_FOLLOW, path);
if (err) {
pr_err("failed to resolve '%s': %i\n", name, err);
goto out;
}
err = -EINVAL;
if (ovl_dentry_weird(path->dentry)) {
pr_err("filesystem on '%s' not supported\n", name);
goto out_put;
}
if (!d_is_dir(path->dentry)) {
pr_err("'%s' not a directory\n", name);
goto out_put;
}
return 0;
out_put:
path_put_init(path);
out:
return err;
}
static int ovl_mount_dir(const char *name, struct path *path)
{
int err = -ENOMEM;
char *tmp = kstrdup(name, GFP_KERNEL);
if (tmp) {
ovl_unescape(tmp);
err = ovl_mount_dir_noesc(tmp, path);
if (!err && path->dentry->d_flags & DCACHE_OP_REAL) {
pr_err("filesystem on '%s' not supported as upperdir\n",
tmp);
path_put_init(path);
err = -EINVAL;
}
kfree(tmp);
}
return err;
}
static int ovl_check_namelen(const struct path *path, struct ovl_fs *ofs, static int ovl_check_namelen(const struct path *path, struct ovl_fs *ofs,
const char *name) const char *name)
{ {
...@@ -932,10 +819,6 @@ static int ovl_lower_dir(const char *name, struct path *path, ...@@ -932,10 +819,6 @@ static int ovl_lower_dir(const char *name, struct path *path,
int fh_type; int fh_type;
int err; int err;
err = ovl_mount_dir_noesc(name, path);
if (err)
return err;
err = ovl_check_namelen(path, ofs, name); err = ovl_check_namelen(path, ofs, name);
if (err) if (err)
return err; return err;
...@@ -984,26 +867,6 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir) ...@@ -984,26 +867,6 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
return ok; return ok;
} }
static unsigned int ovl_split_lowerdirs(char *str)
{
unsigned int ctr = 1;
char *s, *d;
for (s = d = str;; s++, d++) {
if (*s == '\\') {
s++;
} else if (*s == ':') {
*d = '\0';
ctr++;
continue;
}
*d = *s;
if (!*s)
break;
}
return ctr;
}
static int ovl_own_xattr_get(const struct xattr_handler *handler, static int ovl_own_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode, struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size) const char *name, void *buffer, size_t size)
...@@ -1104,15 +967,12 @@ static int ovl_report_in_use(struct ovl_fs *ofs, const char *name) ...@@ -1104,15 +967,12 @@ static int ovl_report_in_use(struct ovl_fs *ofs, const char *name)
} }
static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
struct ovl_layer *upper_layer, struct path *upperpath) struct ovl_layer *upper_layer,
const struct path *upperpath)
{ {
struct vfsmount *upper_mnt; struct vfsmount *upper_mnt;
int err; int err;
err = ovl_mount_dir(ofs->config.upperdir, upperpath);
if (err)
goto out;
/* Upperdir path should not be r/o */ /* Upperdir path should not be r/o */
if (__mnt_is_readonly(upperpath->mnt)) { if (__mnt_is_readonly(upperpath->mnt)) {
pr_err("upper fs is r/o, try multi-lower layers mount\n"); pr_err("upper fs is r/o, try multi-lower layers mount\n");
...@@ -1142,6 +1002,11 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1142,6 +1002,11 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
upper_layer->idx = 0; upper_layer->idx = 0;
upper_layer->fsid = 0; upper_layer->fsid = 0;
err = -ENOMEM;
upper_layer->name = kstrdup(ofs->config.upperdir, GFP_KERNEL);
if (!upper_layer->name)
goto out;
/* /*
* Inherit SB_NOSEC flag from upperdir. * Inherit SB_NOSEC flag from upperdir.
* *
...@@ -1333,10 +1198,17 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1333,10 +1198,17 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
if (err) { if (err) {
pr_warn("failed to set xattr on upper\n"); pr_warn("failed to set xattr on upper\n");
ofs->noxattr = true; ofs->noxattr = true;
if (ofs->config.index || ofs->config.metacopy) { if (ovl_redirect_follow(ofs)) {
ofs->config.index = false; ofs->config.redirect_mode = OVL_REDIRECT_NOFOLLOW;
pr_warn("...falling back to redirect_dir=nofollow.\n");
}
if (ofs->config.metacopy) {
ofs->config.metacopy = false; 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. * xattr support is required for persistent st_ino.
...@@ -1399,46 +1271,37 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1399,46 +1271,37 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
} }
static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs, static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs,
const struct path *upperpath) const struct path *upperpath,
const struct path *workpath)
{ {
int err; int err;
struct path workpath = { };
err = ovl_mount_dir(ofs->config.workdir, &workpath);
if (err)
goto out;
err = -EINVAL; err = -EINVAL;
if (upperpath->mnt != workpath.mnt) { if (upperpath->mnt != workpath->mnt) {
pr_err("workdir and upperdir must reside under the same mount\n"); pr_err("workdir and upperdir must reside under the same mount\n");
goto out; return err;
} }
if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) { if (!ovl_workdir_ok(workpath->dentry, upperpath->dentry)) {
pr_err("workdir and upperdir must be separate subtrees\n"); pr_err("workdir and upperdir must be separate subtrees\n");
goto out; return err;
} }
ofs->workbasedir = dget(workpath.dentry); ofs->workbasedir = dget(workpath->dentry);
if (ovl_inuse_trylock(ofs->workbasedir)) { if (ovl_inuse_trylock(ofs->workbasedir)) {
ofs->workdir_locked = true; ofs->workdir_locked = true;
} else { } else {
err = ovl_report_in_use(ofs, "workdir"); err = ovl_report_in_use(ofs, "workdir");
if (err) if (err)
goto out; return err;
} }
err = ovl_setup_trap(sb, ofs->workbasedir, &ofs->workbasedir_trap, err = ovl_setup_trap(sb, ofs->workbasedir, &ofs->workbasedir_trap,
"workdir"); "workdir");
if (err) if (err)
goto out; return err;
err = ovl_make_workdir(sb, ofs, &workpath);
out:
path_put(&workpath);
return err; return ovl_make_workdir(sb, ofs, workpath);
} }
static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
...@@ -1454,7 +1317,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1454,7 +1317,7 @@ static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
/* Verify lower root is upper root origin */ /* Verify lower root is upper root origin */
err = ovl_verify_origin(ofs, upperpath->dentry, err = ovl_verify_origin(ofs, upperpath->dentry,
oe->lowerstack[0].dentry, true); ovl_lowerstack(oe)->dentry, true);
if (err) { if (err) {
pr_err("failed to verify upper root origin\n"); pr_err("failed to verify upper root origin\n");
goto out; goto out;
...@@ -1574,7 +1437,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) ...@@ -1574,7 +1437,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
pr_warn("%s uuid detected in lower fs '%pd2', falling back to xino=%s,index=off,nfs_export=off.\n", pr_warn("%s uuid detected in lower fs '%pd2', falling back to xino=%s,index=off,nfs_export=off.\n",
uuid_is_null(&sb->s_uuid) ? "null" : uuid_is_null(&sb->s_uuid) ? "null" :
"conflicting", "conflicting",
path->dentry, ovl_xino_str[ofs->config.xino]); path->dentry, ovl_xino_mode(&ofs->config));
} }
} }
...@@ -1591,19 +1454,31 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) ...@@ -1591,19 +1454,31 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
return ofs->numfs++; return ofs->numfs++;
} }
/*
* The fsid after the last lower fsid is used for the data layers.
* It is a "null fs" with a null sb, null uuid, and no pseudo dev.
*/
static int ovl_get_data_fsid(struct ovl_fs *ofs)
{
return ofs->numfs;
}
static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
struct path *stack, unsigned int numlower, struct ovl_fs_context *ctx, struct ovl_layer *layers)
struct ovl_layer *layers)
{ {
int err; int err;
unsigned int i; unsigned int i;
size_t nr_merged_lower;
err = -ENOMEM; ofs->fs = kcalloc(ctx->nr + 2, sizeof(struct ovl_sb), GFP_KERNEL);
ofs->fs = kcalloc(numlower + 1, sizeof(struct ovl_sb), GFP_KERNEL);
if (ofs->fs == NULL) if (ofs->fs == NULL)
goto out; return -ENOMEM;
/* idx/fsid 0 are reserved for upper fs even with lower only overlay */ /*
* idx/fsid 0 are reserved for upper fs even with lower only overlay
* and the last fsid is reserved for "null fs" of the data layers.
*/
ofs->numfs++; ofs->numfs++;
/* /*
...@@ -1615,7 +1490,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1615,7 +1490,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
err = get_anon_bdev(&ofs->fs[0].pseudo_dev); err = get_anon_bdev(&ofs->fs[0].pseudo_dev);
if (err) { if (err) {
pr_err("failed to get anonymous bdev for upper fs\n"); pr_err("failed to get anonymous bdev for upper fs\n");
goto out; return err;
} }
if (ovl_upper_mnt(ofs)) { if (ovl_upper_mnt(ofs)) {
...@@ -1623,14 +1498,19 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1623,14 +1498,19 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
ofs->fs[0].is_lower = false; ofs->fs[0].is_lower = false;
} }
for (i = 0; i < numlower; i++) { nr_merged_lower = ctx->nr - ctx->nr_data;
for (i = 0; i < ctx->nr; i++) {
struct ovl_fs_context_layer *l = &ctx->lower[i];
struct vfsmount *mnt; struct vfsmount *mnt;
struct inode *trap; struct inode *trap;
int fsid; int fsid;
err = fsid = ovl_get_fsid(ofs, &stack[i]); if (i < nr_merged_lower)
if (err < 0) fsid = ovl_get_fsid(ofs, &l->path);
goto out; else
fsid = ovl_get_data_fsid(ofs);
if (fsid < 0)
return fsid;
/* /*
* Check if lower root conflicts with this overlay layers before * Check if lower root conflicts with this overlay layers before
...@@ -1639,24 +1519,24 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1639,24 +1519,24 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
* the upperdir/workdir is in fact in-use by our * the upperdir/workdir is in fact in-use by our
* upperdir/workdir. * upperdir/workdir.
*/ */
err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir"); err = ovl_setup_trap(sb, l->path.dentry, &trap, "lowerdir");
if (err) if (err)
goto out; return err;
if (ovl_is_inuse(stack[i].dentry)) { if (ovl_is_inuse(l->path.dentry)) {
err = ovl_report_in_use(ofs, "lowerdir"); err = ovl_report_in_use(ofs, "lowerdir");
if (err) { if (err) {
iput(trap); iput(trap);
goto out; return err;
} }
} }
mnt = clone_private_mount(&stack[i]); mnt = clone_private_mount(&l->path);
err = PTR_ERR(mnt); err = PTR_ERR(mnt);
if (IS_ERR(mnt)) { if (IS_ERR(mnt)) {
pr_err("failed to clone lowerpath\n"); pr_err("failed to clone lowerpath\n");
iput(trap); iput(trap);
goto out; return err;
} }
/* /*
...@@ -1670,6 +1550,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1670,6 +1550,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
layers[ofs->numlayer].idx = ofs->numlayer; layers[ofs->numlayer].idx = ofs->numlayer;
layers[ofs->numlayer].fsid = fsid; layers[ofs->numlayer].fsid = fsid;
layers[ofs->numlayer].fs = &ofs->fs[fsid]; layers[ofs->numlayer].fs = &ofs->fs[fsid];
layers[ofs->numlayer].name = l->name;
l->name = NULL;
ofs->numlayer++; ofs->numlayer++;
ofs->fs[fsid].is_lower = true; ofs->fs[fsid].is_lower = true;
} }
...@@ -1706,69 +1588,63 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1706,69 +1588,63 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
ofs->xino_mode); ofs->xino_mode);
} }
err = 0; return 0;
out:
return err;
} }
static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
const char *lower, unsigned int numlower, struct ovl_fs_context *ctx,
struct ovl_fs *ofs, struct ovl_layer *layers) struct ovl_fs *ofs,
struct ovl_layer *layers)
{ {
int err; int err;
struct path *stack = NULL;
unsigned int i; unsigned int i;
size_t nr_merged_lower;
struct ovl_entry *oe; struct ovl_entry *oe;
struct ovl_path *lowerstack;
if (!ofs->config.upperdir && numlower == 1) { struct ovl_fs_context_layer *l;
if (!ofs->config.upperdir && ctx->nr == 1) {
pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n"); pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
stack = kcalloc(numlower, sizeof(struct path), GFP_KERNEL);
if (!stack)
return ERR_PTR(-ENOMEM);
err = -EINVAL; err = -EINVAL;
for (i = 0; i < numlower; i++) { for (i = 0; i < ctx->nr; i++) {
err = ovl_lower_dir(lower, &stack[i], ofs, &sb->s_stack_depth); l = &ctx->lower[i];
if (err)
goto out_err;
lower = strchr(lower, '\0') + 1; err = ovl_lower_dir(l->name, &l->path, ofs, &sb->s_stack_depth);
if (err)
return ERR_PTR(err);
} }
err = -EINVAL; err = -EINVAL;
sb->s_stack_depth++; sb->s_stack_depth++;
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
pr_err("maximum fs stacking depth exceeded\n"); pr_err("maximum fs stacking depth exceeded\n");
goto out_err; return ERR_PTR(err);
} }
err = ovl_get_layers(sb, ofs, stack, numlower, layers); err = ovl_get_layers(sb, ofs, ctx, layers);
if (err) if (err)
goto out_err; return ERR_PTR(err);
err = -ENOMEM; err = -ENOMEM;
oe = ovl_alloc_entry(numlower); /* Data-only layers are not merged in root directory */
nr_merged_lower = ctx->nr - ctx->nr_data;
oe = ovl_alloc_entry(nr_merged_lower);
if (!oe) if (!oe)
goto out_err; return ERR_PTR(err);
for (i = 0; i < numlower; i++) { lowerstack = ovl_lowerstack(oe);
oe->lowerstack[i].dentry = dget(stack[i].dentry); for (i = 0; i < nr_merged_lower; i++) {
oe->lowerstack[i].layer = &ofs->layers[i+1]; l = &ctx->lower[i];
lowerstack[i].dentry = dget(l->path.dentry);
lowerstack[i].layer = &ofs->layers[i + 1];
} }
ofs->numdatalayer = ctx->nr_data;
out:
for (i = 0; i < numlower; i++)
path_put(&stack[i]);
kfree(stack);
return oe; return oe;
out_err:
oe = ERR_PTR(err);
goto out;
} }
/* /*
...@@ -1849,20 +1725,18 @@ static struct dentry *ovl_get_root(struct super_block *sb, ...@@ -1849,20 +1725,18 @@ static struct dentry *ovl_get_root(struct super_block *sb,
struct ovl_entry *oe) struct ovl_entry *oe)
{ {
struct dentry *root; 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; unsigned long ino = d_inode(lowerpath->dentry)->i_ino;
int fsid = lowerpath->layer->fsid; int fsid = lowerpath->layer->fsid;
struct ovl_inode_params oip = { struct ovl_inode_params oip = {
.upperdentry = upperdentry, .upperdentry = upperdentry,
.lowerpath = lowerpath, .oe = oe,
}; };
root = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); root = d_make_root(ovl_new_inode(sb, S_IFDIR, 0));
if (!root) if (!root)
return NULL; return NULL;
root->d_fsdata = oe;
if (upperdentry) { if (upperdentry) {
/* Root inode uses upper st_ino/i_ino */ /* Root inode uses upper st_ino/i_ino */
ino = d_inode(upperdentry)->i_ino; ino = d_inode(upperdentry)->i_ino;
...@@ -1877,73 +1751,47 @@ static struct dentry *ovl_get_root(struct super_block *sb, ...@@ -1877,73 +1751,47 @@ static struct dentry *ovl_get_root(struct super_block *sb,
ovl_dentry_set_flag(OVL_E_CONNECTED, root); ovl_dentry_set_flag(OVL_E_CONNECTED, root);
ovl_set_upperdata(d_inode(root)); ovl_set_upperdata(d_inode(root));
ovl_inode_init(d_inode(root), &oip, ino, fsid); 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, oe, DCACHE_OP_WEAK_REVALIDATE);
/* root keeps a reference of upperdentry */
dget(upperdentry);
return root; return root;
} }
static int ovl_fill_super(struct super_block *sb, void *data, int silent) static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
struct path upperpath = { }; struct ovl_fs *ofs = sb->s_fs_info;
struct ovl_fs_context *ctx = fc->fs_private;
struct dentry *root_dentry; struct dentry *root_dentry;
struct ovl_entry *oe; struct ovl_entry *oe;
struct ovl_fs *ofs;
struct ovl_layer *layers; struct ovl_layer *layers;
struct cred *cred; struct cred *cred;
char *splitlower = NULL;
unsigned int numlower;
int err; int err;
err = -EIO; err = -EIO;
if (WARN_ON(sb->s_user_ns != current_user_ns())) if (WARN_ON(fc->user_ns != current_user_ns()))
goto out; goto out_err;
sb->s_d_op = &ovl_dentry_operations; sb->s_d_op = &ovl_dentry_operations;
err = -ENOMEM;
ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
if (!ofs)
goto out;
err = -ENOMEM; err = -ENOMEM;
ofs->creator_cred = cred = prepare_creds(); ofs->creator_cred = cred = prepare_creds();
if (!cred) if (!cred)
goto out_err; goto out_err;
/* Is there a reason anyone would want not to share whiteouts? */ err = ovl_fs_params_verify(ctx, &ofs->config);
ofs->share_whiteout = true;
ofs->config.index = ovl_index_def;
ofs->config.uuid = true;
ofs->config.nfs_export = ovl_nfs_export_def;
ofs->config.xino = ovl_xino_def();
ofs->config.metacopy = ovl_metacopy_def;
err = ovl_parse_opt((char *) data, &ofs->config);
if (err) if (err)
goto out_err; goto out_err;
err = -EINVAL; err = -EINVAL;
if (!ofs->config.lowerdir) { if (ctx->nr == 0) {
if (!silent) if (!(fc->sb_flags & SB_SILENT))
pr_err("missing 'lowerdir'\n"); pr_err("missing 'lowerdir'\n");
goto out_err; goto out_err;
} }
err = -ENOMEM; err = -ENOMEM;
splitlower = kstrdup(ofs->config.lowerdir, GFP_KERNEL); layers = kcalloc(ctx->nr + 1, sizeof(struct ovl_layer), GFP_KERNEL);
if (!splitlower)
goto out_err;
err = -EINVAL;
numlower = ovl_split_lowerdirs(splitlower);
if (numlower > OVL_MAX_STACK) {
pr_err("too many lower directories, limit is %d\n",
OVL_MAX_STACK);
goto out_err;
}
err = -ENOMEM;
layers = kcalloc(numlower + 1, sizeof(struct ovl_layer), GFP_KERNEL);
if (!layers) if (!layers)
goto out_err; goto out_err;
...@@ -1975,7 +1823,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1975,7 +1823,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
goto out_err; goto out_err;
} }
err = ovl_get_upper(sb, ofs, &layers[0], &upperpath); err = ovl_get_upper(sb, ofs, &layers[0], &ctx->upper);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1989,7 +1837,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1989,7 +1837,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
} }
} }
err = ovl_get_workdir(sb, ofs, &upperpath); err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1999,7 +1847,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1999,7 +1847,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
sb->s_stack_depth = upper_sb->s_stack_depth; sb->s_stack_depth = upper_sb->s_stack_depth;
sb->s_time_gran = upper_sb->s_time_gran; sb->s_time_gran = upper_sb->s_time_gran;
} }
oe = ovl_get_lowerstack(sb, splitlower, numlower, ofs, layers); oe = ovl_get_lowerstack(sb, ctx, ofs, layers);
err = PTR_ERR(oe); err = PTR_ERR(oe);
if (IS_ERR(oe)) if (IS_ERR(oe))
goto out_err; goto out_err;
...@@ -2014,7 +1862,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2014,7 +1862,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
} }
if (!ovl_force_readonly(ofs) && ofs->config.index) { if (!ovl_force_readonly(ofs) && ofs->config.index) {
err = ovl_get_indexdir(sb, ofs, oe, &upperpath); err = ovl_get_indexdir(sb, ofs, oe, &ctx->upper);
if (err) if (err)
goto out_free_oe; goto out_free_oe;
...@@ -2055,40 +1903,115 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2055,40 +1903,115 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
sb->s_iflags |= SB_I_SKIP_SYNC; sb->s_iflags |= SB_I_SKIP_SYNC;
err = -ENOMEM; err = -ENOMEM;
root_dentry = ovl_get_root(sb, upperpath.dentry, oe); root_dentry = ovl_get_root(sb, ctx->upper.dentry, oe);
if (!root_dentry) if (!root_dentry)
goto out_free_oe; goto out_free_oe;
mntput(upperpath.mnt);
kfree(splitlower);
sb->s_root = root_dentry; sb->s_root = root_dentry;
return 0; return 0;
out_free_oe: out_free_oe:
ovl_entry_stack_free(oe); ovl_free_entry(oe);
kfree(oe);
out_err: out_err:
kfree(splitlower);
path_put(&upperpath);
ovl_free_fs(ofs); ovl_free_fs(ofs);
out: sb->s_fs_info = NULL;
return err; return err;
} }
static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags, static int ovl_get_tree(struct fs_context *fc)
const char *dev_name, void *raw_data)
{ {
return mount_nodev(fs_type, flags, raw_data, ovl_fill_super); return get_tree_nodev(fc, ovl_fill_super);
}
static inline void ovl_fs_context_free(struct ovl_fs_context *ctx)
{
ovl_parse_param_drop_lowerdir(ctx);
path_put(&ctx->upper);
path_put(&ctx->work);
kfree(ctx->lower);
kfree(ctx);
}
static void ovl_free(struct fs_context *fc)
{
struct ovl_fs *ofs = fc->s_fs_info;
struct ovl_fs_context *ctx = fc->fs_private;
/*
* ofs is stored in the fs_context when it is initialized.
* ofs is transferred to the superblock on a successful mount,
* but if an error occurs before the transfer we have to free
* it here.
*/
if (ofs)
ovl_free_fs(ofs);
if (ctx)
ovl_fs_context_free(ctx);
}
static const struct fs_context_operations ovl_context_ops = {
.parse_param = ovl_parse_param,
.get_tree = ovl_get_tree,
.reconfigure = ovl_reconfigure,
.free = ovl_free,
};
/*
* This is called during fsopen() and will record the user namespace of
* the caller in fc->user_ns since we've raised FS_USERNS_MOUNT. We'll
* need it when we actually create the superblock to verify that the
* process creating the superblock is in the same user namespace as
* process that called fsopen().
*/
static int ovl_init_fs_context(struct fs_context *fc)
{
struct ovl_fs_context *ctx;
struct ovl_fs *ofs;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
if (!ctx)
return -ENOMEM;
/*
* By default we allocate for three lower layers. It's likely
* that it'll cover most users.
*/
ctx->lower = kmalloc_array(3, sizeof(*ctx->lower), GFP_KERNEL_ACCOUNT);
if (!ctx->lower)
goto out_err;
ctx->capacity = 3;
ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
if (!ofs)
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;
ofs->config.xino = ovl_xino_def();
ofs->config.metacopy = ovl_metacopy_def;
fc->s_fs_info = ofs;
fc->fs_private = ctx;
fc->ops = &ovl_context_ops;
return 0;
out_err:
ovl_fs_context_free(ctx);
return -ENOMEM;
} }
static struct file_system_type ovl_fs_type = { static struct file_system_type ovl_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "overlay", .name = "overlay",
.fs_flags = FS_USERNS_MOUNT, .init_fs_context = ovl_init_fs_context,
.mount = ovl_mount, .parameters = ovl_parameter_spec,
.kill_sb = kill_anon_super, .fs_flags = FS_USERNS_MOUNT,
.kill_sb = kill_anon_super,
}; };
MODULE_ALIAS_FS("overlay"); MODULE_ALIAS_FS("overlay");
......
...@@ -83,33 +83,84 @@ bool ovl_verify_lower(struct super_block *sb) ...@@ -83,33 +83,84 @@ bool ovl_verify_lower(struct super_block *sb)
return ofs->config.nfs_export && ofs->config.index; 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) 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); struct ovl_entry *oe = kzalloc(size, GFP_KERNEL);
if (oe) if (oe)
oe->numlower = numlower; oe->__numlower = numlower;
return oe; 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) bool ovl_dentry_remote(struct dentry *dentry)
{ {
return dentry->d_flags & return dentry->d_flags & OVL_D_REVALIDATE;
(DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE);
} }
void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *upperdentry, void ovl_dentry_update_reval(struct dentry *dentry, struct dentry *realdentry)
unsigned int mask)
{ {
struct ovl_entry *oe = OVL_E(dentry); 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,
struct ovl_entry *oe)
{
return ovl_dentry_init_flags(dentry, upperdentry, oe, OVL_D_REVALIDATE);
}
void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry,
struct ovl_entry *oe, unsigned int mask)
{
struct ovl_path *lowerstack = ovl_lowerstack(oe);
unsigned int i, flags = 0; unsigned int i, flags = 0;
if (upperdentry) if (upperdentry)
flags |= upperdentry->d_flags; flags |= upperdentry->d_flags;
for (i = 0; i < oe->numlower; i++) for (i = 0; i < ovl_numlower(oe) && lowerstack[i].dentry; i++)
flags |= oe->lowerstack[i].dentry->d_flags; flags |= lowerstack[i].dentry->d_flags;
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
dentry->d_flags &= ~mask; dentry->d_flags &= ~mask;
...@@ -127,7 +178,7 @@ bool ovl_dentry_weird(struct dentry *dentry) ...@@ -127,7 +178,7 @@ bool ovl_dentry_weird(struct dentry *dentry)
enum ovl_path_type ovl_path_type(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; enum ovl_path_type type = 0;
if (ovl_dentry_upper(dentry)) { if (ovl_dentry_upper(dentry)) {
...@@ -136,7 +187,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry) ...@@ -136,7 +187,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
/* /*
* Non-dir dentry can hold lower dentry of its copy up origin. * 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))) if (ovl_test_flag(OVL_CONST_INO, d_inode(dentry)))
type |= __OVL_PATH_ORIGIN; type |= __OVL_PATH_ORIGIN;
if (d_is_dir(dentry) || if (d_is_dir(dentry) ||
...@@ -144,7 +195,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry) ...@@ -144,7 +195,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
type |= __OVL_PATH_MERGE; type |= __OVL_PATH_MERGE;
} }
} else { } else {
if (oe->numlower > 1) if (ovl_numlower(oe) > 1)
type |= __OVL_PATH_MERGE; type |= __OVL_PATH_MERGE;
} }
return type; return type;
...@@ -160,11 +211,12 @@ void ovl_path_upper(struct dentry *dentry, struct path *path) ...@@ -160,11 +211,12 @@ void ovl_path_upper(struct dentry *dentry, struct path *path)
void ovl_path_lower(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);
struct ovl_path *lowerpath = ovl_lowerstack(oe);
if (oe->numlower) { if (ovl_numlower(oe)) {
path->mnt = oe->lowerstack[0].layer->mnt; path->mnt = lowerpath->layer->mnt;
path->dentry = oe->lowerstack[0].dentry; path->dentry = lowerpath->dentry;
} else { } else {
*path = (struct path) { }; *path = (struct path) { };
} }
...@@ -172,11 +224,19 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) ...@@ -172,11 +224,19 @@ void ovl_path_lower(struct dentry *dentry, struct path *path)
void ovl_path_lowerdata(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);
struct ovl_path *lowerdata = ovl_lowerdata(oe);
struct dentry *lowerdata_dentry = ovl_lowerdata_dentry(oe);
if (oe->numlower) { if (lowerdata_dentry) {
path->mnt = oe->lowerstack[oe->numlower - 1].layer->mnt; path->dentry = lowerdata_dentry;
path->dentry = oe->lowerstack[oe->numlower - 1].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 { } else {
*path = (struct path) { }; *path = (struct path) { };
} }
...@@ -215,16 +275,16 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry) ...@@ -215,16 +275,16 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry)
struct dentry *ovl_dentry_lower(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; return ovl_numlower(oe) ? ovl_lowerstack(oe)->dentry : NULL;
} }
const struct ovl_layer *ovl_layer_lower(struct dentry *dentry) 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; return ovl_numlower(oe) ? ovl_lowerstack(oe)->layer : NULL;
} }
/* /*
...@@ -235,9 +295,30 @@ const struct ovl_layer *ovl_layer_lower(struct dentry *dentry) ...@@ -235,9 +295,30 @@ const struct ovl_layer *ovl_layer_lower(struct dentry *dentry)
*/ */
struct dentry *ovl_dentry_lowerdata(struct dentry *dentry) struct dentry *ovl_dentry_lowerdata(struct dentry *dentry)
{ {
struct ovl_entry *oe = dentry->d_fsdata; 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));
return oe->numlower ? oe->lowerstack[oe->numlower - 1].dentry : NULL; ovl_dentry_update_reval(dentry, datadentry);
return 0;
} }
struct dentry *ovl_dentry_real(struct dentry *dentry) struct dentry *ovl_dentry_real(struct dentry *dentry)
...@@ -250,15 +331,19 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode) ...@@ -250,15 +331,19 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode)
return ovl_upperdentry_dereference(OVL_I(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)
{ {
struct ovl_path *lowerpath = ovl_lowerpath(OVL_I_E(inode));
path->dentry = ovl_i_dentry_upper(inode); path->dentry = ovl_i_dentry_upper(inode);
if (!path->dentry) { if (!path->dentry) {
path->dentry = OVL_I(inode)->lowerpath.dentry; path->dentry = lowerpath->dentry;
path->mnt = OVL_I(inode)->lowerpath.layer->mnt; path->mnt = lowerpath->layer->mnt;
} else { } else {
path->mnt = ovl_upper_mnt(OVL_FS(inode->i_sb)); 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) struct inode *ovl_inode_upper(struct inode *inode)
...@@ -270,9 +355,9 @@ struct inode *ovl_inode_upper(struct inode *inode) ...@@ -270,9 +355,9 @@ struct inode *ovl_inode_upper(struct inode *inode)
struct inode *ovl_inode_lower(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) struct inode *ovl_inode_real(struct inode *inode)
...@@ -283,10 +368,12 @@ struct inode *ovl_inode_real(struct inode *inode) ...@@ -283,10 +368,12 @@ struct inode *ovl_inode_real(struct inode *inode)
/* Return inode which contains lower data. Do not return metacopy */ /* Return inode which contains lower data. Do not return metacopy */
struct inode *ovl_inode_lowerdata(struct inode *inode) 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))) if (WARN_ON(!S_ISREG(inode->i_mode)))
return NULL; 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 */ /* Return real inode which contains data. Does not return metacopy inode */
...@@ -301,9 +388,15 @@ struct inode *ovl_inode_realdata(struct inode *inode) ...@@ -301,9 +388,15 @@ struct inode *ovl_inode_realdata(struct inode *inode)
return ovl_inode_lowerdata(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) 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) void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache)
...@@ -313,17 +406,17 @@ void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache) ...@@ -313,17 +406,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) 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) 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) 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) bool ovl_dentry_is_opaque(struct dentry *dentry)
...@@ -413,13 +506,6 @@ bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags) ...@@ -413,13 +506,6 @@ bool ovl_dentry_needs_data_copy_up(struct dentry *dentry, int flags)
return !ovl_has_upperdata(d_inode(dentry)); 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) const char *ovl_dentry_get_redirect(struct dentry *dentry)
{ {
return OVL_I(d_inode(dentry))->redirect; return OVL_I(d_inode(dentry))->redirect;
...@@ -999,7 +1085,7 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path) ...@@ -999,7 +1085,7 @@ int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path)
bool ovl_is_metacopy_dentry(struct dentry *dentry) 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)) if (!d_is_reg(dentry))
return false; return false;
...@@ -1010,7 +1096,7 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry) ...@@ -1010,7 +1096,7 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)
return false; 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) char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding)
...@@ -1105,8 +1191,7 @@ void ovl_copyattr(struct inode *inode) ...@@ -1105,8 +1191,7 @@ void ovl_copyattr(struct inode *inode)
vfsuid_t vfsuid; vfsuid_t vfsuid;
vfsgid_t vfsgid; vfsgid_t vfsgid;
ovl_i_path_real(inode, &realpath); realinode = ovl_i_path_real(inode, &realpath);
realinode = d_inode(realpath.dentry);
real_idmap = mnt_idmap(realpath.mnt); real_idmap = mnt_idmap(realpath.mnt);
vfsuid = i_uid_into_vfsuid(real_idmap, realinode); vfsuid = i_uid_into_vfsuid(real_idmap, realinode);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment