Commit 719ea861 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs fixes from Miklos Szeredi:
 "This fixes a corner case for NFS exporting (introduced in this cycle)
  as well as fixing miscellaneous bugs"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: update Kconfig texts
  ovl: redirect_dir=nofollow should not follow redirect for opaque lower
  ovl: fix ptr_ret.cocci warnings
  ovl: check ERR_PTR() return value from ovl_lookup_real()
  ovl: check lower ancestry on encode of lower dir file handle
  ovl: hash non-dir by lower inode for fsnotify
parents 2d9b1d69 36cd95df
...@@ -24,6 +24,8 @@ config OVERLAY_FS_REDIRECT_DIR ...@@ -24,6 +24,8 @@ config OVERLAY_FS_REDIRECT_DIR
an overlay which has redirects on a kernel that doesn't support this an overlay which has redirects on a kernel that doesn't support this
feature will have unexpected results. feature will have unexpected results.
If unsure, say N.
config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW
bool "Overlayfs: follow redirects even if redirects are turned off" bool "Overlayfs: follow redirects even if redirects are turned off"
default y default y
...@@ -32,8 +34,13 @@ config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW ...@@ -32,8 +34,13 @@ config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW
Disable this to get a possibly more secure configuration, but that Disable this to get a possibly more secure configuration, but that
might not be backward compatible with previous kernels. might not be backward compatible with previous kernels.
If backward compatibility is not an issue, then it is safe and
recommended to say N here.
For more information, see Documentation/filesystems/overlayfs.txt For more information, see Documentation/filesystems/overlayfs.txt
If unsure, say Y.
config OVERLAY_FS_INDEX config OVERLAY_FS_INDEX
bool "Overlayfs: turn on inodes index feature by default" bool "Overlayfs: turn on inodes index feature by default"
depends on OVERLAY_FS depends on OVERLAY_FS
...@@ -51,6 +58,8 @@ config OVERLAY_FS_INDEX ...@@ -51,6 +58,8 @@ config OVERLAY_FS_INDEX
That is, mounting an overlay which has an inodes index on a kernel That is, mounting an overlay which has an inodes index on a kernel
that doesn't support this feature will have unexpected results. that doesn't support this feature will have unexpected results.
If unsure, say N.
config OVERLAY_FS_NFS_EXPORT config OVERLAY_FS_NFS_EXPORT
bool "Overlayfs: turn on NFS export feature by default" bool "Overlayfs: turn on NFS export feature by default"
depends on OVERLAY_FS depends on OVERLAY_FS
...@@ -72,3 +81,8 @@ config OVERLAY_FS_NFS_EXPORT ...@@ -72,3 +81,8 @@ config OVERLAY_FS_NFS_EXPORT
Note, that the NFS export feature is not backward compatible. Note, that the NFS export feature is not backward compatible.
That is, mounting an overlay which has a full index on a kernel That is, mounting an overlay which has a full index on a kernel
that doesn't support this feature will have unexpected results. that doesn't support this feature will have unexpected results.
Most users should say N here and enable this feature on a case-by-
case basis with the "nfs_export=on" mount option.
Say N unless you fully understand the consequences.
...@@ -19,6 +19,142 @@ ...@@ -19,6 +19,142 @@
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include "overlayfs.h" #include "overlayfs.h"
static int ovl_encode_maybe_copy_up(struct dentry *dentry)
{
int err;
if (ovl_dentry_upper(dentry))
return 0;
err = ovl_want_write(dentry);
if (!err) {
err = ovl_copy_up(dentry);
ovl_drop_write(dentry);
}
if (err) {
pr_warn_ratelimited("overlayfs: failed to copy up on encode (%pd2, err=%i)\n",
dentry, err);
}
return err;
}
/*
* Before encoding a non-upper directory file handle from real layer N, we need
* to check if it will be possible to reconnect an overlay dentry from the real
* lower decoded dentry. This is done by following the overlay ancestry up to a
* "layer N connected" ancestor and verifying that all parents along the way are
* "layer N connectable". If an ancestor that is NOT "layer N connectable" is
* found, we need to copy up an ancestor, which is "layer N connectable", thus
* making that ancestor "layer N connected". For example:
*
* layer 1: /a
* layer 2: /a/b/c
*
* The overlay dentry /a is NOT "layer 2 connectable", because if dir /a is
* copied up and renamed, upper dir /a will be indexed by lower dir /a from
* layer 1. The dir /a from layer 2 will never be indexed, so the algorithm (*)
* in ovl_lookup_real_ancestor() will not be able to lookup a connected overlay
* dentry from the connected lower dentry /a/b/c.
*
* To avoid this problem on decode time, we need to copy up an ancestor of
* /a/b/c, which is "layer 2 connectable", on encode time. That ancestor is
* /a/b. After copy up (and index) of /a/b, it will become "layer 2 connected"
* and when the time comes to decode the file handle from lower dentry /a/b/c,
* ovl_lookup_real_ancestor() will find the indexed ancestor /a/b and decoding
* a connected overlay dentry will be accomplished.
*
* (*) the algorithm in ovl_lookup_real_ancestor() can be improved to lookup an
* entry /a in the lower layers above layer N and find the indexed dir /a from
* layer 1. If that improvement is made, then the check for "layer N connected"
* will need to verify there are no redirects in lower layers above N. In the
* example above, /a will be "layer 2 connectable". However, if layer 2 dir /a
* is a target of a layer 1 redirect, then /a will NOT be "layer 2 connectable":
*
* layer 1: /A (redirect = /a)
* layer 2: /a/b/c
*/
/* Return the lowest layer for encoding a connectable file handle */
static int ovl_connectable_layer(struct dentry *dentry)
{
struct ovl_entry *oe = OVL_E(dentry);
/* We can get overlay root from root of any layer */
if (dentry == dentry->d_sb->s_root)
return oe->numlower;
/*
* If it's an unindexed merge dir, then it's not connectable with any
* lower layer
*/
if (ovl_dentry_upper(dentry) &&
!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
return 0;
/* We can get upper/overlay path from indexed/lower dentry */
return oe->lowerstack[0].layer->idx;
}
/*
* @dentry is "connected" if all ancestors up to root or a "connected" ancestor
* have the same uppermost lower layer as the origin's layer. We may need to
* copy up a "connectable" ancestor to make it "connected". A "connected" dentry
* cannot become non "connected", so cache positive result in dentry flags.
*
* Return the connected origin layer or < 0 on error.
*/
static int ovl_connect_layer(struct dentry *dentry)
{
struct dentry *next, *parent = NULL;
int origin_layer;
int err = 0;
if (WARN_ON(dentry == dentry->d_sb->s_root) ||
WARN_ON(!ovl_dentry_lower(dentry)))
return -EIO;
origin_layer = OVL_E(dentry)->lowerstack[0].layer->idx;
if (ovl_dentry_test_flag(OVL_E_CONNECTED, dentry))
return origin_layer;
/* Find the topmost origin layer connectable ancestor of @dentry */
next = dget(dentry);
for (;;) {
parent = dget_parent(next);
if (WARN_ON(parent == next)) {
err = -EIO;
break;
}
/*
* If @parent is not origin layer connectable, then copy up
* @next which is origin layer connectable and we are done.
*/
if (ovl_connectable_layer(parent) < origin_layer) {
err = ovl_encode_maybe_copy_up(next);
break;
}
/* If @parent is connected or indexed we are done */
if (ovl_dentry_test_flag(OVL_E_CONNECTED, parent) ||
ovl_test_flag(OVL_INDEX, d_inode(parent)))
break;
dput(next);
next = parent;
}
dput(parent);
dput(next);
if (!err)
ovl_dentry_set_flag(OVL_E_CONNECTED, dentry);
return err ?: origin_layer;
}
/* /*
* We only need to encode origin if there is a chance that the same object was * We only need to encode origin if there is a chance that the same object was
* encoded pre copy up and then we need to stay consistent with the same * encoded pre copy up and then we need to stay consistent with the same
...@@ -41,73 +177,59 @@ ...@@ -41,73 +177,59 @@
* L = lower file handle * L = lower file handle
* *
* (*) Connecting an overlay dir from real lower dentry is not always * (*) Connecting an overlay dir from real lower dentry is not always
* possible when there are redirects in lower layers. To mitigate this case, * possible when there are redirects in lower layers and non-indexed merge dirs.
* we copy up the lower dir first and then encode an upper dir file handle. * To mitigate those case, we may copy up the lower dir ancestor before encode
* a lower dir file handle.
*
* Return 0 for upper file handle, > 0 for lower file handle or < 0 on error.
*/ */
static bool ovl_should_encode_origin(struct dentry *dentry) static int ovl_check_encode_origin(struct dentry *dentry)
{ {
struct ovl_fs *ofs = dentry->d_sb->s_fs_info; struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
/* Upper file handle for pure upper */
if (!ovl_dentry_lower(dentry)) if (!ovl_dentry_lower(dentry))
return false; return 0;
/* /*
* Decoding a merge dir, whose origin's parent is under a redirected * Upper file handle for non-indexed upper.
* lower dir is not always possible. As a simple aproximation, we do
* not encode lower dir file handles when overlay has multiple lower
* layers and origin is below the topmost lower layer.
* *
* TODO: copy up only the parent that is under redirected lower. * Root is never indexed, so if there's an upper layer, encode upper for
* root.
*/ */
if (d_is_dir(dentry) && ofs->upper_mnt &&
OVL_E(dentry)->lowerstack[0].layer->idx > 1)
return false;
/* Decoding a non-indexed upper from origin is not implemented */
if (ovl_dentry_upper(dentry) && if (ovl_dentry_upper(dentry) &&
!ovl_test_flag(OVL_INDEX, d_inode(dentry))) !ovl_test_flag(OVL_INDEX, d_inode(dentry)))
return false;
return true;
}
static int ovl_encode_maybe_copy_up(struct dentry *dentry)
{
int err;
if (ovl_dentry_upper(dentry))
return 0; return 0;
err = ovl_want_write(dentry); /*
if (err) * Decoding a merge dir, whose origin's ancestor is under a redirected
return err; * lower dir or under a non-indexed upper is not always possible.
* ovl_connect_layer() will try to make origin's layer "connected" by
err = ovl_copy_up(dentry); * copying up a "connectable" ancestor.
*/
if (d_is_dir(dentry) && ofs->upper_mnt)
return ovl_connect_layer(dentry);
ovl_drop_write(dentry); /* Lower file handle for indexed and non-upper dir/non-dir */
return err; return 1;
} }
static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen) static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
{ {
struct dentry *origin = ovl_dentry_lower(dentry);
struct ovl_fh *fh = NULL; struct ovl_fh *fh = NULL;
int err; int err, enc_lower;
/* /*
* If we should not encode a lower dir file handle, copy up and encode * Check if we should encode a lower or upper file handle and maybe
* an upper dir file handle. * copy up an ancestor to make lower file handle connectable.
*/ */
if (!ovl_should_encode_origin(dentry)) { err = enc_lower = ovl_check_encode_origin(dentry);
err = ovl_encode_maybe_copy_up(dentry); if (enc_lower < 0)
if (err)
goto fail; goto fail;
origin = NULL; /* Encode an upper or lower file handle */
} fh = ovl_encode_fh(enc_lower ? ovl_dentry_lower(dentry) :
ovl_dentry_upper(dentry), !enc_lower);
/* Encode an upper or origin file handle */
fh = ovl_encode_fh(origin ?: ovl_dentry_upper(dentry), !origin);
err = PTR_ERR(fh); err = PTR_ERR(fh);
if (IS_ERR(fh)) if (IS_ERR(fh))
goto fail; goto fail;
...@@ -355,8 +477,8 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb, ...@@ -355,8 +477,8 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb,
dput(upper); dput(upper);
} }
if (!this) if (IS_ERR_OR_NULL(this))
return NULL; return this;
if (WARN_ON(ovl_dentry_real_at(this, layer->idx) != real)) { if (WARN_ON(ovl_dentry_real_at(this, layer->idx) != real)) {
dput(this); dput(this);
...@@ -498,7 +620,7 @@ static struct dentry *ovl_lookup_real(struct super_block *sb, ...@@ -498,7 +620,7 @@ static struct dentry *ovl_lookup_real(struct super_block *sb,
if (err == -ECHILD) { if (err == -ECHILD) {
this = ovl_lookup_real_ancestor(sb, real, this = ovl_lookup_real_ancestor(sb, real,
layer); layer);
err = IS_ERR(this) ? PTR_ERR(this) : 0; err = PTR_ERR_OR_ZERO(this);
} }
if (!err) { if (!err) {
dput(connected); dput(connected);
......
...@@ -669,38 +669,59 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, ...@@ -669,38 +669,59 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
return inode; return inode;
} }
/*
* Does overlay inode need to be hashed by lower inode?
*/
static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
struct dentry *lower, struct dentry *index)
{
struct ovl_fs *ofs = sb->s_fs_info;
/* No, if pure upper */
if (!lower)
return false;
/* Yes, if already indexed */
if (index)
return true;
/* Yes, if won't be copied up */
if (!ofs->upper_mnt)
return true;
/* No, if lower hardlink is or will be broken on copy up */
if ((upper || !ovl_indexdir(sb)) &&
!d_is_dir(lower) && d_inode(lower)->i_nlink > 1)
return false;
/* No, if non-indexed upper with NFS export */
if (sb->s_export_op && upper)
return false;
/* Otherwise, hash by lower inode for fsnotify */
return true;
}
struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
struct dentry *lowerdentry, struct dentry *index, struct dentry *lowerdentry, struct dentry *index,
unsigned int numlower) unsigned int numlower)
{ {
struct ovl_fs *ofs = sb->s_fs_info;
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL; struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
struct inode *inode; struct inode *inode;
/* Already indexed or could be indexed on copy up? */ bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index);
bool indexed = (index || (ovl_indexdir(sb) && !upperdentry));
struct dentry *origin = indexed ? lowerdentry : NULL;
bool is_dir; bool is_dir;
if (WARN_ON(upperdentry && indexed && !lowerdentry))
return ERR_PTR(-EIO);
if (!realinode) if (!realinode)
realinode = d_inode(lowerdentry); realinode = d_inode(lowerdentry);
/* /*
* Copy up origin (lower) may exist for non-indexed non-dir upper, but * Copy up origin (lower) may exist for non-indexed upper, but we must
* we must not use lower as hash key in that case. * not use lower as hash key if this is a broken hardlink.
* Hash non-dir that is or could be indexed by origin inode.
* Hash dir that is or could be merged by origin inode.
* Hash pure upper and non-indexed non-dir by upper inode.
* Hash non-indexed dir by upper inode for NFS export.
*/ */
is_dir = S_ISDIR(realinode->i_mode); is_dir = S_ISDIR(realinode->i_mode);
if (is_dir && (indexed || !sb->s_export_op || !ofs->upper_mnt)) if (upperdentry || bylower) {
origin = lowerdentry; struct inode *key = d_inode(bylower ? lowerdentry :
upperdentry);
if (upperdentry || origin) {
struct inode *key = d_inode(origin ?: upperdentry);
unsigned int nlink = is_dir ? 1 : realinode->i_nlink; unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
inode = iget5_locked(sb, (unsigned long) key, inode = iget5_locked(sb, (unsigned long) key,
...@@ -728,6 +749,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, ...@@ -728,6 +749,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink); nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
set_nlink(inode, nlink); set_nlink(inode, nlink);
} else { } else {
/* Lower hardlink that will be broken on copy up */
inode = new_inode(sb); inode = new_inode(sb);
if (!inode) if (!inode)
goto out_nomem; goto out_nomem;
......
...@@ -913,9 +913,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -913,9 +913,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
stack[ctr].layer = lower.layer; stack[ctr].layer = lower.layer;
ctr++; ctr++;
if (d.stop)
break;
/* /*
* Following redirects can have security consequences: it's like * Following redirects can have security consequences: it's like
* a symlink into the lower layer without the permission checks. * a symlink into the lower layer without the permission checks.
...@@ -933,6 +930,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -933,6 +930,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
goto out_put; goto out_put;
} }
if (d.stop)
break;
if (d.redirect && d.redirect[0] == '/' && poe != roe) { if (d.redirect && d.redirect[0] == '/' && poe != roe) {
poe = roe; poe = roe;
/* Find the current layer on the root dentry */ /* Find the current layer on the root dentry */
......
...@@ -40,6 +40,7 @@ enum ovl_inode_flag { ...@@ -40,6 +40,7 @@ enum ovl_inode_flag {
enum ovl_entry_flag { enum ovl_entry_flag {
OVL_E_UPPER_ALIAS, OVL_E_UPPER_ALIAS,
OVL_E_OPAQUE, OVL_E_OPAQUE,
OVL_E_CONNECTED,
}; };
/* /*
......
...@@ -1359,6 +1359,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1359,6 +1359,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
/* Root is always merge -> can have whiteouts */ /* Root is always merge -> can have whiteouts */
ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry)); ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
ovl_dentry_set_flag(OVL_E_CONNECTED, root_dentry);
ovl_inode_init(d_inode(root_dentry), upperpath.dentry, ovl_inode_init(d_inode(root_dentry), upperpath.dentry,
ovl_dentry_lower(root_dentry)); ovl_dentry_lower(root_dentry));
......
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