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
This commit is contained in:
commit
719ea86151
@ -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
|
|||||||
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
|
|||||||
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
|
|||||||
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 @@
|
|||||||
#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 @@
|
|||||||
* 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;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decoding a merge dir, whose origin's parent is under a redirected
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
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) &&
|
|
||||||
!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)
|
* Upper file handle for non-indexed upper.
|
||||||
return err;
|
*
|
||||||
|
* Root is never indexed, so if there's an upper layer, encode upper for
|
||||||
|
* root.
|
||||||
|
*/
|
||||||
|
if (ovl_dentry_upper(dentry) &&
|
||||||
|
!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
err = ovl_copy_up(dentry);
|
/*
|
||||||
|
* Decoding a merge dir, whose origin's ancestor is under a redirected
|
||||||
|
* lower dir or under a non-indexed upper is not always possible.
|
||||||
|
* ovl_connect_layer() will try to make origin's layer "connected" by
|
||||||
|
* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 {
|
|||||||
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)
|
|||||||
|
|
||||||
/* 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));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user