Commit 48023102 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 updates from Miklos Szeredi:
 "In addition to bug fixes and cleanups there are two new features from
  Amir:

   - Consistent inode number support for the case when layers are not
     all on the same filesystem (feature is dubbed "xino").

   - Optimize overlayfs file handle decoding. This one touches the
     exportfs interface to allow detecting the disconnected directory
     case"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: update documentation w.r.t "xino" feature
  ovl: add support for "xino" mount and config options
  ovl: consistent d_ino for non-samefs with xino
  ovl: consistent i_ino for non-samefs with xino
  ovl: constant st_ino for non-samefs with xino
  ovl: allocate anon bdev per unique lower fs
  ovl: factor out ovl_map_dev_ino() helper
  ovl: cleanup ovl_update_time()
  ovl: add WARN_ON() for non-dir redirect cases
  ovl: cleanup setting OVL_INDEX
  ovl: set d->is_dir and d->opaque for last path element
  ovl: Do not check for redirect if this is last layer
  ovl: lookup in inode cache first when decoding lower file handle
  ovl: do not try to reconnect a disconnected origin dentry
  ovl: disambiguate ovl_encode_fh()
  ovl: set lower layer st_dev only if setting lower st_ino
  ovl: fix lookup with middle layer opaque dir and absolute path redirects
  ovl: Set d->last properly during lookup
  ovl: set i_ino to the value of st_ino for NFS export
parents ba2b137d 16149013
...@@ -14,9 +14,13 @@ The result will inevitably fail to look exactly like a normal ...@@ -14,9 +14,13 @@ The result will inevitably fail to look exactly like a normal
filesystem for various technical reasons. The expectation is that filesystem for various technical reasons. The expectation is that
many use cases will be able to ignore these differences. many use cases will be able to ignore these differences.
This approach is 'hybrid' because the objects that appear in the
filesystem do not all appear to belong to that filesystem. In many Overlay objects
cases an object accessed in the union will be indistinguishable ---------------
The overlay filesystem approach is 'hybrid', because the objects that
appear in the filesystem do not always appear to belong to that filesystem.
In many cases, an object accessed in the union will be indistinguishable
from accessing the corresponding object from the original filesystem. from accessing the corresponding object from the original filesystem.
This is most obvious from the 'st_dev' field returned by stat(2). This is most obvious from the 'st_dev' field returned by stat(2).
...@@ -34,6 +38,19 @@ make the overlay mount more compliant with filesystem scanners and ...@@ -34,6 +38,19 @@ make the overlay mount more compliant with filesystem scanners and
overlay objects will be distinguishable from the corresponding overlay objects will be distinguishable from the corresponding
objects in the original filesystem. objects in the original filesystem.
On 64bit systems, even if all overlay layers are not on the same
underlying filesystem, the same compliant behavior could be achieved
with the "xino" feature. The "xino" feature composes a unique object
identifier from the real object st_ino and an underlying fsid index.
If all underlying filesystems support NFS file handles and export file
handles with 32bit inode number encoding (e.g. ext4), overlay filesystem
will use the high inode number bits for fsid. Even when the underlying
filesystem uses 64bit inode numbers, users can still enable the "xino"
feature with the "-o xino=on" overlay mount option. That is useful for the
case of underlying filesystems like xfs and tmpfs, which use 64bit inode
numbers, but are very unlikely to use the high inode number bit.
Upper and Lower Upper and Lower
--------------- ---------------
...@@ -290,10 +307,19 @@ Non-standard behavior ...@@ -290,10 +307,19 @@ Non-standard behavior
--------------------- ---------------------
The copy_up operation essentially creates a new, identical file and The copy_up operation essentially creates a new, identical file and
moves it over to the old name. The new file may be on a different moves it over to the old name. Any open files referring to this inode
filesystem, so both st_dev and st_ino of the file may change. will access the old data.
The new file may be on a different filesystem, so both st_dev and st_ino
of the real file may change. The values of st_dev and st_ino returned by
stat(2) on an overlay object are often not the same as the real file
stat(2) values to prevent the values from changing on copy_up.
Any open files referring to this inode will access the old data. Unless "xino" feature is enabled, when overlay layers are not all on the
same underlying filesystem, the value of st_dev may be different for two
non-directory objects in the same overlay filesystem and the value of
st_ino for directory objects may be non persistent and could change even
while the overlay filesystem is still mounted.
Unless "inode index" feature is enabled, if a file with multiple hard Unless "inode index" feature is enabled, if a file with multiple hard
links is copied up, then this will "break" the link. Changes will not be links is copied up, then this will "break" the link. Changes will not be
...@@ -302,6 +328,7 @@ propagated to other names referring to the same inode. ...@@ -302,6 +328,7 @@ propagated to other names referring to the same inode.
Unless "redirect_dir" feature is enabled, rename(2) on a lower or merged Unless "redirect_dir" feature is enabled, rename(2) on a lower or merged
directory will fail with EXDEV. directory will fail with EXDEV.
Changes to underlying filesystems Changes to underlying filesystems
--------------------------------- ---------------------------------
......
...@@ -435,6 +435,15 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, ...@@ -435,6 +435,15 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
if (IS_ERR_OR_NULL(result)) if (IS_ERR_OR_NULL(result))
return ERR_PTR(-ESTALE); return ERR_PTR(-ESTALE);
/*
* If no acceptance criteria was specified by caller, a disconnected
* dentry is also accepatable. Callers may use this mode to query if
* file handle is stale or to get a reference to an inode without
* risking the high overhead caused by directory reconnect.
*/
if (!acceptable)
return result;
if (d_is_dir(result)) { if (d_is_dir(result)) {
/* /*
* This request is for a directory. * This request is for a directory.
......
...@@ -86,3 +86,20 @@ config OVERLAY_FS_NFS_EXPORT ...@@ -86,3 +86,20 @@ config OVERLAY_FS_NFS_EXPORT
case basis with the "nfs_export=on" mount option. case basis with the "nfs_export=on" mount option.
Say N unless you fully understand the consequences. Say N unless you fully understand the consequences.
config OVERLAY_FS_XINO_AUTO
bool "Overlayfs: auto enable inode number mapping"
default n
depends on OVERLAY_FS
help
If this config option is enabled then overlay filesystems will use
unused high bits in undelying filesystem inode numbers to map all
inodes to a unified address space. The mapped 64bit inode numbers
might not be compatible with applications that expect 32bit inodes.
If compatibility with applications that expect 32bit inodes is not an
issue, then it is safe and recommended to say Y here.
For more information, see Documentation/filesystems/overlayfs.txt
If unsure, say N.
...@@ -232,7 +232,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) ...@@ -232,7 +232,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
return err; return err;
} }
struct ovl_fh *ovl_encode_fh(struct dentry *real, bool is_upper) struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper)
{ {
struct ovl_fh *fh; struct ovl_fh *fh;
int fh_type, fh_len, dwords; int fh_type, fh_len, dwords;
...@@ -300,7 +300,7 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower, ...@@ -300,7 +300,7 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
* up and a pure upper inode. * up and a pure upper inode.
*/ */
if (ovl_can_decode_fh(lower->d_sb)) { if (ovl_can_decode_fh(lower->d_sb)) {
fh = ovl_encode_fh(lower, false); fh = ovl_encode_real_fh(lower, false);
if (IS_ERR(fh)) if (IS_ERR(fh))
return PTR_ERR(fh); return PTR_ERR(fh);
} }
...@@ -321,7 +321,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index) ...@@ -321,7 +321,7 @@ static int ovl_set_upper_fh(struct dentry *upper, struct dentry *index)
const struct ovl_fh *fh; const struct ovl_fh *fh;
int err; int err;
fh = ovl_encode_fh(upper, true); fh = ovl_encode_real_fh(upper, true);
if (IS_ERR(fh)) if (IS_ERR(fh))
return PTR_ERR(fh); return PTR_ERR(fh);
......
...@@ -228,7 +228,7 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen) ...@@ -228,7 +228,7 @@ static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
goto fail; goto fail;
/* Encode an upper or lower file handle */ /* Encode an upper or lower file handle */
fh = ovl_encode_fh(enc_lower ? ovl_dentry_lower(dentry) : fh = ovl_encode_real_fh(enc_lower ? ovl_dentry_lower(dentry) :
ovl_dentry_upper(dentry), !enc_lower); ovl_dentry_upper(dentry), !enc_lower);
err = PTR_ERR(fh); err = PTR_ERR(fh);
if (IS_ERR(fh)) if (IS_ERR(fh))
...@@ -267,7 +267,7 @@ static int ovl_dentry_to_fh(struct dentry *dentry, u32 *fid, int *max_len) ...@@ -267,7 +267,7 @@ static int ovl_dentry_to_fh(struct dentry *dentry, u32 *fid, int *max_len)
return OVL_FILEID; return OVL_FILEID;
} }
static int ovl_encode_inode_fh(struct inode *inode, u32 *fid, int *max_len, static int ovl_encode_fh(struct inode *inode, u32 *fid, int *max_len,
struct inode *parent) struct inode *parent)
{ {
struct dentry *dentry; struct dentry *dentry;
...@@ -305,15 +305,12 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb, ...@@ -305,15 +305,12 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
if (d_is_dir(upper ?: lower)) if (d_is_dir(upper ?: lower))
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
inode = ovl_get_inode(sb, dget(upper), lower, index, !!lower); inode = ovl_get_inode(sb, dget(upper), lowerpath, index, !!lower);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
dput(upper); dput(upper);
return ERR_CAST(inode); return ERR_CAST(inode);
} }
if (index)
ovl_set_flag(OVL_INDEX, inode);
dentry = d_find_any_alias(inode); dentry = d_find_any_alias(inode);
if (!dentry) { if (!dentry) {
dentry = d_alloc_anon(inode->i_sb); dentry = d_alloc_anon(inode->i_sb);
...@@ -685,7 +682,7 @@ static struct dentry *ovl_upper_fh_to_d(struct super_block *sb, ...@@ -685,7 +682,7 @@ static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
if (!ofs->upper_mnt) if (!ofs->upper_mnt)
return ERR_PTR(-EACCES); return ERR_PTR(-EACCES);
upper = ovl_decode_fh(fh, ofs->upper_mnt); upper = ovl_decode_real_fh(fh, ofs->upper_mnt, true);
if (IS_ERR_OR_NULL(upper)) if (IS_ERR_OR_NULL(upper))
return upper; return upper;
...@@ -703,25 +700,39 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, ...@@ -703,25 +700,39 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
struct ovl_path *stack = &origin; struct ovl_path *stack = &origin;
struct dentry *dentry = NULL; struct dentry *dentry = NULL;
struct dentry *index = NULL; struct dentry *index = NULL;
struct inode *inode = NULL; struct inode *inode;
bool is_deleted = false;
int err; int err;
/* First lookup indexed upper by fh */ /* First lookup overlay inode in inode cache by origin fh */
err = ovl_check_origin_fh(ofs, fh, false, NULL, &stack);
if (err)
return ERR_PTR(err);
if (!d_is_dir(origin.dentry) ||
!(origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
inode = ovl_lookup_inode(sb, origin.dentry, false);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_err;
if (inode) {
dentry = d_find_any_alias(inode);
iput(inode);
if (dentry)
goto out;
}
}
/* Then lookup indexed upper/whiteout by origin fh */
if (ofs->indexdir) { if (ofs->indexdir) {
index = ovl_get_index_fh(ofs, fh); index = ovl_get_index_fh(ofs, fh);
err = PTR_ERR(index); err = PTR_ERR(index);
if (IS_ERR(index)) { if (IS_ERR(index)) {
if (err != -ESTALE)
return ERR_PTR(err);
/* Found a whiteout index - treat as deleted inode */
is_deleted = true;
index = NULL; index = NULL;
goto out_err;
} }
} }
/* Then try to get upper dir by index */ /* Then try to get a connected upper dir by index */
if (index && d_is_dir(index)) { if (index && d_is_dir(index)) {
struct dentry *upper = ovl_index_upper(ofs, index); struct dentry *upper = ovl_index_upper(ofs, index);
...@@ -734,24 +745,19 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, ...@@ -734,24 +745,19 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
goto out; goto out;
} }
/* Then lookup origin by fh */ /* Otherwise, get a connected non-upper dir or disconnected non-dir */
err = ovl_check_origin_fh(ofs, fh, NULL, &stack); if (d_is_dir(origin.dentry) &&
if (err) { (origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
dput(origin.dentry);
origin.dentry = NULL;
err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
if (err)
goto out_err; goto out_err;
} else if (index) { }
if (index) {
err = ovl_verify_origin(index, origin.dentry, false); err = ovl_verify_origin(index, origin.dentry, false);
if (err) if (err)
goto out_err; goto out_err;
} else if (is_deleted) {
/* Lookup deleted non-dir by origin inode */
if (!d_is_dir(origin.dentry))
inode = ovl_lookup_inode(sb, origin.dentry, false);
err = -ESTALE;
if (!inode || atomic_read(&inode->i_count) == 1)
goto out_err;
/* Deleted but still open? */
index = dget(ovl_i_dentry_upper(inode));
} }
dentry = ovl_get_dentry(sb, NULL, &origin, index); dentry = ovl_get_dentry(sb, NULL, &origin, index);
...@@ -759,7 +765,6 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, ...@@ -759,7 +765,6 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb,
out: out:
dput(origin.dentry); dput(origin.dentry);
dput(index); dput(index);
iput(inode);
return dentry; return dentry;
out_err: out_err:
...@@ -829,7 +834,7 @@ static struct dentry *ovl_get_parent(struct dentry *dentry) ...@@ -829,7 +834,7 @@ static struct dentry *ovl_get_parent(struct dentry *dentry)
} }
const struct export_operations ovl_export_operations = { const struct export_operations ovl_export_operations = {
.encode_fh = ovl_encode_inode_fh, .encode_fh = ovl_encode_fh,
.fh_to_dentry = ovl_fh_to_dentry, .fh_to_dentry = ovl_fh_to_dentry,
.fh_to_parent = ovl_fh_to_parent, .fh_to_parent = ovl_fh_to_parent,
.get_name = ovl_get_name, .get_name = ovl_get_name,
......
...@@ -16,13 +16,6 @@ ...@@ -16,13 +16,6 @@
#include "overlayfs.h" #include "overlayfs.h"
static dev_t ovl_get_pseudo_dev(struct dentry *dentry)
{
struct ovl_entry *oe = dentry->d_fsdata;
return oe->lowerstack[0].layer->pseudo_dev;
}
int ovl_setattr(struct dentry *dentry, struct iattr *attr) int ovl_setattr(struct dentry *dentry, struct iattr *attr)
{ {
int err; int err;
...@@ -66,6 +59,69 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -66,6 +59,69 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
return err; return err;
} }
static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat,
struct ovl_layer *lower_layer)
{
bool samefs = ovl_same_sb(dentry->d_sb);
unsigned int xinobits = ovl_xino_bits(dentry->d_sb);
if (samefs) {
/*
* When all layers are on the same fs, all real inode
* number are unique, so we use the overlay st_dev,
* which is friendly to du -x.
*/
stat->dev = dentry->d_sb->s_dev;
return 0;
} else if (xinobits) {
unsigned int shift = 64 - xinobits;
/*
* All inode numbers of underlying fs should not be using the
* high xinobits, so we use high xinobits to partition the
* overlay st_ino address space. The high bits holds the fsid
* (upper fsid is 0). This way overlay inode numbers are unique
* and all inodes use overlay st_dev. Inode numbers are also
* persistent for a given layer configuration.
*/
if (stat->ino >> shift) {
pr_warn_ratelimited("overlayfs: inode number too big (%pd2, ino=%llu, xinobits=%d)\n",
dentry, stat->ino, xinobits);
} else {
if (lower_layer)
stat->ino |= ((u64)lower_layer->fsid) << shift;
stat->dev = dentry->d_sb->s_dev;
return 0;
}
}
/* The inode could not be mapped to a unified st_ino address space */
if (S_ISDIR(dentry->d_inode->i_mode)) {
/*
* Always use the overlay st_dev for directories, so 'find
* -xdev' will scan the entire overlay mount and won't cross the
* overlay mount boundaries.
*
* If not all layers are on the same fs the pair {real st_ino;
* overlay st_dev} is not unique, so use the non persistent
* overlay st_ino for directories.
*/
stat->dev = dentry->d_sb->s_dev;
stat->ino = dentry->d_inode->i_ino;
} else if (lower_layer && lower_layer->fsid) {
/*
* For non-samefs setup, if we cannot map all layers st_ino
* to a unified address space, we need to make sure that st_dev
* is unique per lower fs. Upper layer uses real st_dev and
* lower layers use the unique anonymous bdev assigned to the
* lower fs.
*/
stat->dev = lower_layer->fs->pseudo_dev;
}
return 0;
}
int ovl_getattr(const struct path *path, struct kstat *stat, int ovl_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int flags) u32 request_mask, unsigned int flags)
{ {
...@@ -75,6 +131,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -75,6 +131,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
const struct cred *old_cred; const struct cred *old_cred;
bool is_dir = S_ISDIR(dentry->d_inode->i_mode); bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
bool samefs = ovl_same_sb(dentry->d_sb); bool samefs = ovl_same_sb(dentry->d_sb);
struct ovl_layer *lower_layer = NULL;
int err; int err;
type = ovl_path_real(dentry, &realpath); type = ovl_path_real(dentry, &realpath);
...@@ -84,14 +141,18 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -84,14 +141,18 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
goto out; goto out;
/* /*
* For non-dir or same fs, we use st_ino of the copy up origin, if we * For non-dir or same fs, we use st_ino of the copy up origin.
* know it. This guaranties constant st_dev/st_ino across copy up. * This guaranties constant st_dev/st_ino across copy up.
* With xino feature and non-samefs, we use st_ino of the copy up
* origin masked with high bits that represent the layer id.
* *
* If filesystem supports NFS export ops, 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 || samefs) { if (!is_dir || samefs || ovl_xino_bits(dentry->d_sb)) {
if (OVL_TYPE_ORIGIN(type)) { if (!OVL_TYPE_UPPER(type)) {
lower_layer = ovl_layer_lower(dentry);
} else if (OVL_TYPE_ORIGIN(type)) {
struct kstat lowerstat; struct kstat lowerstat;
u32 lowermask = STATX_INO | (!is_dir ? STATX_NLINK : 0); u32 lowermask = STATX_INO | (!is_dir ? STATX_NLINK : 0);
...@@ -118,43 +179,17 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -118,43 +179,17 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
*/ */
if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) || if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) ||
(!ovl_verify_lower(dentry->d_sb) && (!ovl_verify_lower(dentry->d_sb) &&
(is_dir || lowerstat.nlink == 1))) (is_dir || lowerstat.nlink == 1))) {
stat->ino = lowerstat.ino; stat->ino = lowerstat.ino;
lower_layer = ovl_layer_lower(dentry);
if (samefs)
WARN_ON_ONCE(stat->dev != lowerstat.dev);
else
stat->dev = ovl_get_pseudo_dev(dentry);
} }
if (samefs) {
/*
* When all layers are on the same fs, all real inode
* number are unique, so we use the overlay st_dev,
* which is friendly to du -x.
*/
stat->dev = dentry->d_sb->s_dev;
} else if (!OVL_TYPE_UPPER(type)) {
/*
* For non-samefs setup, to make sure that st_dev/st_ino
* pair is unique across the system, we use a unique
* anonymous st_dev for lower layer inode.
*/
stat->dev = ovl_get_pseudo_dev(dentry);
} }
} else {
/*
* Always use the overlay st_dev for directories, so 'find
* -xdev' will scan the entire overlay mount and won't cross the
* overlay mount boundaries.
*
* If not all layers are on the same fs the pair {real st_ino;
* overlay st_dev} is not unique, so use the non persistent
* overlay st_ino for directories.
*/
stat->dev = dentry->d_sb->s_dev;
stat->ino = dentry->d_inode->i_ino;
} }
err = ovl_map_dev_ino(dentry, stat, lower_layer);
if (err)
goto out;
/* /*
* It's probably not worth it to count subdirs to get the * It's probably not worth it to count subdirs to get the
* correct link count. nlink=1 seems to pacify 'find' and * correct link count. nlink=1 seems to pacify 'find' and
...@@ -383,24 +418,18 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags) ...@@ -383,24 +418,18 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
int ovl_update_time(struct inode *inode, struct timespec *ts, int flags) int ovl_update_time(struct inode *inode, struct timespec *ts, int flags)
{ {
struct dentry *alias; if (flags & S_ATIME) {
struct path upperpath; struct ovl_fs *ofs = inode->i_sb->s_fs_info;
struct path upperpath = {
if (!(flags & S_ATIME)) .mnt = ofs->upper_mnt,
return 0; .dentry = ovl_upperdentry_dereference(OVL_I(inode)),
};
alias = d_find_any_alias(inode);
if (!alias)
return 0;
ovl_path_upper(alias, &upperpath);
if (upperpath.dentry) { if (upperpath.dentry) {
touch_atime(&upperpath); touch_atime(&upperpath);
inode->i_atime = d_inode(upperpath.dentry)->i_atime; inode->i_atime = d_inode(upperpath.dentry)->i_atime;
} }
}
dput(alias);
return 0; return 0;
} }
...@@ -459,9 +488,27 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode) ...@@ -459,9 +488,27 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode)
#endif #endif
} }
static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev) static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev,
unsigned long ino, int fsid)
{ {
int xinobits = ovl_xino_bits(inode->i_sb);
/*
* When NFS export is enabled and d_ino is consistent with st_ino
* (samefs or i_ino has enough bits to encode layer), set the same
* value used for d_ino to i_ino, because nfsd readdirplus compares
* d_ino values to i_ino values of child entries. When called from
* ovl_new_inode(), ino arg is 0, so i_ino will be updated to real
* upper inode i_ino on ovl_inode_init() or ovl_inode_update().
*/
if (inode->i_sb->s_export_op &&
(ovl_same_sb(inode->i_sb) || xinobits)) {
inode->i_ino = ino;
if (xinobits && fsid && !(ino >> (64 - xinobits)))
inode->i_ino |= (unsigned long)fsid << (64 - xinobits);
} else {
inode->i_ino = get_next_ino(); inode->i_ino = get_next_ino();
}
inode->i_mode = mode; inode->i_mode = mode;
inode->i_flags |= S_NOCMTIME; inode->i_flags |= S_NOCMTIME;
#ifdef CONFIG_FS_POSIX_ACL #ifdef CONFIG_FS_POSIX_ACL
...@@ -597,7 +644,7 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev) ...@@ -597,7 +644,7 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)
inode = new_inode(sb); inode = new_inode(sb);
if (inode) if (inode)
ovl_fill_inode(inode, mode, rdev); ovl_fill_inode(inode, mode, rdev, 0, 0);
return inode; return inode;
} }
...@@ -703,13 +750,16 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper, ...@@ -703,13 +750,16 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
} }
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 ovl_path *lowerpath, struct dentry *index,
unsigned int numlower) unsigned int numlower)
{ {
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;
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index); bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index);
int fsid = bylower ? lowerpath->layer->fsid : 0;
bool is_dir; bool is_dir;
unsigned long ino = 0;
if (!realinode) if (!realinode)
realinode = d_inode(lowerdentry); realinode = d_inode(lowerdentry);
...@@ -748,18 +798,22 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, ...@@ -748,18 +798,22 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
if (!is_dir) if (!is_dir)
nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink); nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
set_nlink(inode, nlink); set_nlink(inode, nlink);
ino = key->i_ino;
} else { } else {
/* Lower hardlink that will be broken on copy up */ /* 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;
} }
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev); ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev, ino, fsid);
ovl_inode_init(inode, upperdentry, lowerdentry); ovl_inode_init(inode, upperdentry, lowerdentry);
if (upperdentry && ovl_is_impuredir(upperdentry)) if (upperdentry && ovl_is_impuredir(upperdentry))
ovl_set_flag(OVL_IMPURE, inode); ovl_set_flag(OVL_IMPURE, inode);
if (index)
ovl_set_flag(OVL_INDEX, 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) || numlower > 1) || if (((upperdentry && lowerdentry) || numlower > 1) ||
......
...@@ -56,6 +56,15 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d, ...@@ -56,6 +56,15 @@ static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
if (s == next) if (s == next)
goto invalid; goto invalid;
} }
/*
* One of the ancestor path elements in an absolute path
* lookup in ovl_lookup_layer() could have been opaque and
* that will stop further lookup in lower layers (d->stop=true)
* But we have found an absolute redirect in decendant path
* element and that should force continue lookup in lower
* layers (reset d->stop).
*/
d->stop = false;
} else { } else {
if (strchr(buf, '/') != NULL) if (strchr(buf, '/') != NULL)
goto invalid; goto invalid;
...@@ -171,7 +180,8 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name) ...@@ -171,7 +180,8 @@ static struct ovl_fh *ovl_get_fh(struct dentry *dentry, const char *name)
goto out; goto out;
} }
struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt) struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
bool connected)
{ {
struct dentry *real; struct dentry *real;
int bytes; int bytes;
...@@ -186,7 +196,7 @@ struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt) ...@@ -186,7 +196,7 @@ struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
bytes = (fh->len - offsetof(struct ovl_fh, fid)); bytes = (fh->len - offsetof(struct ovl_fh, fid));
real = exportfs_decode_fh(mnt, (struct fid *)fh->fid, real = exportfs_decode_fh(mnt, (struct fid *)fh->fid,
bytes >> 2, (int)fh->type, bytes >> 2, (int)fh->type,
ovl_acceptable, mnt); connected ? ovl_acceptable : NULL, mnt);
if (IS_ERR(real)) { if (IS_ERR(real)) {
/* /*
* Treat stale file handle to lower file as "origin unknown". * Treat stale file handle to lower file as "origin unknown".
...@@ -220,6 +230,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -220,6 +230,7 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
{ {
struct dentry *this; struct dentry *this;
int err; int err;
bool last_element = !post[0];
this = lookup_one_len_unlocked(name, base, namelen); this = lookup_one_len_unlocked(name, base, namelen);
if (IS_ERR(this)) { if (IS_ERR(this)) {
...@@ -245,11 +256,23 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -245,11 +256,23 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
d->stop = true; d->stop = true;
if (d->is_dir) if (d->is_dir)
goto put_and_out; goto put_and_out;
/*
* NB: handle failure to lookup non-last element when non-dir
* redirects become possible
*/
WARN_ON(!last_element);
goto out; goto out;
} }
if (last_element)
d->is_dir = true; d->is_dir = true;
if (!d->last && ovl_is_opaquedir(this)) { if (d->last)
d->stop = d->opaque = true; goto out;
if (ovl_is_opaquedir(this)) {
d->stop = true;
if (last_element)
d->opaque = true;
goto out; goto out;
} }
err = ovl_check_redirect(this, d, prelen, post); err = ovl_check_redirect(this, d, prelen, post);
...@@ -310,14 +333,15 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, ...@@ -310,14 +333,15 @@ static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
} }
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, 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)
{ {
struct dentry *origin = NULL; struct dentry *origin = NULL;
int i; int i;
for (i = 0; i < ofs->numlower; i++) { for (i = 0; i < ofs->numlower; i++) {
origin = ovl_decode_fh(fh, ofs->lower_layers[i].mnt); origin = ovl_decode_real_fh(fh, ofs->lower_layers[i].mnt,
connected);
if (origin) if (origin)
break; break;
} }
...@@ -361,7 +385,7 @@ static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry, ...@@ -361,7 +385,7 @@ static int ovl_check_origin(struct ovl_fs *ofs, struct dentry *upperdentry,
if (IS_ERR_OR_NULL(fh)) if (IS_ERR_OR_NULL(fh))
return PTR_ERR(fh); return PTR_ERR(fh);
err = ovl_check_origin_fh(ofs, fh, upperdentry, stackp); err = ovl_check_origin_fh(ofs, fh, false, upperdentry, stackp);
kfree(fh); kfree(fh);
if (err) { if (err) {
...@@ -415,7 +439,7 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name, ...@@ -415,7 +439,7 @@ int ovl_verify_set_fh(struct dentry *dentry, const char *name,
struct ovl_fh *fh; struct ovl_fh *fh;
int err; int err;
fh = ovl_encode_fh(real, is_upper); fh = ovl_encode_real_fh(real, is_upper);
err = PTR_ERR(fh); err = PTR_ERR(fh);
if (IS_ERR(fh)) if (IS_ERR(fh))
goto fail; goto fail;
...@@ -451,7 +475,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index) ...@@ -451,7 +475,7 @@ struct dentry *ovl_index_upper(struct ovl_fs *ofs, struct dentry *index)
if (IS_ERR_OR_NULL(fh)) if (IS_ERR_OR_NULL(fh))
return ERR_CAST(fh); return ERR_CAST(fh);
upper = ovl_decode_fh(fh, ofs->upper_mnt); upper = ovl_decode_real_fh(fh, ofs->upper_mnt, true);
kfree(fh); kfree(fh);
if (IS_ERR_OR_NULL(upper)) if (IS_ERR_OR_NULL(upper))
...@@ -558,7 +582,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index) ...@@ -558,7 +582,7 @@ int ovl_verify_index(struct ovl_fs *ofs, struct dentry *index)
/* Check if non-dir index is orphan and don't warn before cleaning it */ /* Check if non-dir index is orphan and don't warn before cleaning it */
if (!d_is_dir(index) && d_inode(index)->i_nlink == 1) { if (!d_is_dir(index) && d_inode(index)->i_nlink == 1) {
err = ovl_check_origin_fh(ofs, fh, index, &stack); err = ovl_check_origin_fh(ofs, fh, false, index, &stack);
if (err) if (err)
goto fail; goto fail;
...@@ -619,7 +643,7 @@ int ovl_get_index_name(struct dentry *origin, struct qstr *name) ...@@ -619,7 +643,7 @@ int ovl_get_index_name(struct dentry *origin, struct qstr *name)
struct ovl_fh *fh; struct ovl_fh *fh;
int err; int err;
fh = ovl_encode_fh(origin, false); fh = ovl_encode_real_fh(origin, false);
if (IS_ERR(fh)) if (IS_ERR(fh))
return PTR_ERR(fh); return PTR_ERR(fh);
...@@ -815,7 +839,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -815,7 +839,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 = !poe->numlower, .last = ofs->config.redirect_follow ? false : !poe->numlower,
.redirect = NULL, .redirect = NULL,
}; };
...@@ -873,7 +897,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -873,7 +897,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
for (i = 0; !d.stop && i < poe->numlower; i++) { for (i = 0; !d.stop && i < poe->numlower; i++) {
struct ovl_path lower = poe->lowerstack[i]; struct ovl_path lower = poe->lowerstack[i];
if (!ofs->config.redirect_follow)
d.last = i == poe->numlower - 1; d.last = i == poe->numlower - 1;
else
d.last = lower.layer->idx == roe->numlower;
err = ovl_lookup_layer(lower.dentry, &d, &this); err = ovl_lookup_layer(lower.dentry, &d, &this);
if (err) if (err)
goto out_put; goto out_put;
...@@ -976,17 +1004,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -976,17 +1004,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
upperdentry = dget(index); upperdentry = dget(index);
if (upperdentry || ctr) { if (upperdentry || ctr) {
if (ctr) inode = ovl_get_inode(dentry->d_sb, upperdentry, stack, index,
origin = stack[0].dentry;
inode = ovl_get_inode(dentry->d_sb, upperdentry, origin, index,
ctr); ctr);
err = PTR_ERR(inode); err = PTR_ERR(inode);
if (IS_ERR(inode)) if (IS_ERR(inode))
goto out_free_oe; goto out_free_oe;
/*
* NB: handle redirected hard links when non-dir redirects
* become possible
*/
WARN_ON(OVL_I(inode)->redirect);
OVL_I(inode)->redirect = upperredirect; OVL_I(inode)->redirect = upperredirect;
if (index)
ovl_set_flag(OVL_INDEX, inode);
} }
revert_creds(old_cred); revert_creds(old_cred);
......
...@@ -202,7 +202,7 @@ void ovl_drop_write(struct dentry *dentry); ...@@ -202,7 +202,7 @@ void ovl_drop_write(struct dentry *dentry);
struct dentry *ovl_workdir(struct dentry *dentry); struct dentry *ovl_workdir(struct dentry *dentry);
const struct cred *ovl_override_creds(struct super_block *sb); const struct cred *ovl_override_creds(struct super_block *sb);
struct super_block *ovl_same_sb(struct super_block *sb); struct super_block *ovl_same_sb(struct super_block *sb);
bool ovl_can_decode_fh(struct super_block *sb); 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);
...@@ -215,6 +215,7 @@ void ovl_path_lower(struct dentry *dentry, struct path *path); ...@@ -215,6 +215,7 @@ void ovl_path_lower(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_real(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 ovl_layer *ovl_layer_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
struct dentry *ovl_i_dentry_upper(struct inode *inode); struct dentry *ovl_i_dentry_upper(struct inode *inode);
struct inode *ovl_inode_upper(struct inode *inode); struct inode *ovl_inode_upper(struct inode *inode);
...@@ -263,11 +264,19 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) ...@@ -263,11 +264,19 @@ static inline bool ovl_is_impuredir(struct dentry *dentry)
return ovl_check_dir_xattr(dentry, OVL_XATTR_IMPURE); return ovl_check_dir_xattr(dentry, OVL_XATTR_IMPURE);
} }
static inline unsigned int ovl_xino_bits(struct super_block *sb)
{
struct ovl_fs *ofs = sb->s_fs_info;
return ofs->xino_bits;
}
/* namei.c */ /* namei.c */
int ovl_check_fh_len(struct ovl_fh *fh, int fh_len); int ovl_check_fh_len(struct ovl_fh *fh, int fh_len);
struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt); struct dentry *ovl_decode_real_fh(struct ovl_fh *fh, struct vfsmount *mnt,
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);
int ovl_verify_set_fh(struct dentry *dentry, const char *name, int ovl_verify_set_fh(struct dentry *dentry, const char *name,
struct dentry *real, bool is_upper, bool set); struct dentry *real, bool is_upper, bool set);
...@@ -329,7 +338,7 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); ...@@ -329,7 +338,7 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
bool is_upper); bool is_upper);
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 ovl_path *lowerpath, struct dentry *index,
unsigned int numlower); unsigned int numlower);
static inline void ovl_copyattr(struct inode *from, struct inode *to) static inline void ovl_copyattr(struct inode *from, struct inode *to)
{ {
...@@ -361,7 +370,7 @@ int ovl_copy_up(struct dentry *dentry); ...@@ -361,7 +370,7 @@ int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_flags(struct dentry *dentry, int flags); int ovl_copy_up_flags(struct dentry *dentry, int flags);
int ovl_copy_xattr(struct dentry *old, struct dentry *new); int ovl_copy_xattr(struct dentry *old, struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat); int ovl_set_attr(struct dentry *upper, struct kstat *stat);
struct ovl_fh *ovl_encode_fh(struct dentry *real, bool is_upper); struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper);
int ovl_set_origin(struct dentry *dentry, struct dentry *lower, int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
struct dentry *upper); struct dentry *upper);
......
...@@ -18,13 +18,21 @@ struct ovl_config { ...@@ -18,13 +18,21 @@ struct ovl_config {
const char *redirect_mode; const char *redirect_mode;
bool index; bool index;
bool nfs_export; bool nfs_export;
int xino;
};
struct ovl_sb {
struct super_block *sb;
dev_t pseudo_dev;
}; };
struct ovl_layer { struct ovl_layer {
struct vfsmount *mnt; struct vfsmount *mnt;
dev_t pseudo_dev; struct ovl_sb *fs;
/* Index of this layer in fs root (upper == 0) */ /* Index of this layer in fs root (upper idx == 0) */
int idx; int idx;
/* One fsid per unique underlying sb (upper fsid == 0) */
int fsid;
}; };
struct ovl_path { struct ovl_path {
...@@ -35,8 +43,11 @@ struct ovl_path { ...@@ -35,8 +43,11 @@ struct ovl_path {
/* private information held for overlayfs's superblock */ /* private information held for overlayfs's superblock */
struct ovl_fs { struct ovl_fs {
struct vfsmount *upper_mnt; struct vfsmount *upper_mnt;
unsigned numlower; unsigned int numlower;
/* Number of unique lower sb that differ from upper sb */
unsigned int numlowerfs;
struct ovl_layer *lower_layers; struct ovl_layer *lower_layers;
struct ovl_sb *lower_fs;
/* workbasedir is the path at workdir= mount option */ /* workbasedir is the path at workdir= mount option */
struct dentry *workbasedir; struct dentry *workbasedir;
/* workdir is the 'work' directory under workbasedir */ /* workdir is the 'work' directory under workbasedir */
...@@ -50,11 +61,11 @@ struct ovl_fs { ...@@ -50,11 +61,11 @@ struct ovl_fs {
const struct cred *creator_cred; const struct cred *creator_cred;
bool tmpfile; bool tmpfile;
bool noxattr; bool noxattr;
/* sb common to all layers */
struct super_block *same_sb;
/* Did we take the inuse lock? */ /* Did we take the inuse lock? */
bool upperdir_locked; bool upperdir_locked;
bool workdir_locked; bool workdir_locked;
/* Inode numbers in all layers do not use the high xino_bits */
unsigned int xino_bits;
}; };
/* private information held for every overlayfs dentry */ /* private information held for every overlayfs dentry */
......
...@@ -120,6 +120,10 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd, ...@@ -120,6 +120,10 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
if (!rdd->dentry) if (!rdd->dentry)
return false; return false;
/* Always recalc d_ino when remapping lower inode numbers */
if (ovl_xino_bits(rdd->dentry->d_sb))
return true;
/* Always recalc d_ino for parent */ /* Always recalc d_ino for parent */
if (strcmp(p->name, "..") == 0) if (strcmp(p->name, "..") == 0)
return true; return true;
...@@ -435,6 +439,19 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) ...@@ -435,6 +439,19 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
return cache; return cache;
} }
/* Map inode number to lower fs unique range */
static u64 ovl_remap_lower_ino(u64 ino, int xinobits, int fsid,
const char *name, int namelen)
{
if (ino >> (64 - xinobits)) {
pr_warn_ratelimited("overlayfs: d_ino too big (%.*s, ino=%llu, xinobits=%d)\n",
namelen, name, ino, xinobits);
return ino;
}
return ino | ((u64)fsid) << (64 - xinobits);
}
/* /*
* Set d_ino for upper entries. Non-upper entries should always report * Set d_ino for upper entries. Non-upper entries should always report
* the uppermost real inode ino and should not call this function. * the uppermost real inode ino and should not call this function.
...@@ -452,9 +469,10 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p) ...@@ -452,9 +469,10 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p)
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 err = 0; int err = 0;
if (!ovl_same_sb(dir->d_sb)) if (!ovl_same_sb(dir->d_sb) && !xinobits)
goto out; goto out;
if (p->name[0] == '.') { if (p->name[0] == '.') {
...@@ -491,6 +509,10 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p) ...@@ -491,6 +509,10 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p)
WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev); WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev);
ino = stat.ino; ino = stat.ino;
} else if (xinobits && !OVL_TYPE_UPPER(type)) {
ino = ovl_remap_lower_ino(ino, xinobits,
ovl_layer_lower(this)->fsid,
p->name, p->len);
} }
out: out:
...@@ -618,6 +640,8 @@ struct ovl_readdir_translate { ...@@ -618,6 +640,8 @@ struct ovl_readdir_translate {
struct ovl_dir_cache *cache; struct ovl_dir_cache *cache;
struct dir_context ctx; struct dir_context ctx;
u64 parent_ino; u64 parent_ino;
int fsid;
int xinobits;
}; };
static int ovl_fill_real(struct dir_context *ctx, const char *name, static int ovl_fill_real(struct dir_context *ctx, const char *name,
...@@ -628,14 +652,17 @@ static int ovl_fill_real(struct dir_context *ctx, const char *name, ...@@ -628,14 +652,17 @@ static int ovl_fill_real(struct dir_context *ctx, const char *name,
container_of(ctx, struct ovl_readdir_translate, ctx); container_of(ctx, struct ovl_readdir_translate, ctx);
struct dir_context *orig_ctx = rdt->orig_ctx; struct dir_context *orig_ctx = rdt->orig_ctx;
if (rdt->parent_ino && strcmp(name, "..") == 0) if (rdt->parent_ino && strcmp(name, "..") == 0) {
ino = rdt->parent_ino; ino = rdt->parent_ino;
else if (rdt->cache) { } else if (rdt->cache) {
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
p = ovl_cache_entry_find(&rdt->cache->root, name, namelen); p = ovl_cache_entry_find(&rdt->cache->root, name, namelen);
if (p) if (p)
ino = p->ino; ino = p->ino;
} else if (rdt->xinobits) {
ino = ovl_remap_lower_ino(ino, rdt->xinobits, rdt->fsid,
name, namelen);
} }
return orig_ctx->actor(orig_ctx, name, namelen, offset, ino, d_type); return orig_ctx->actor(orig_ctx, name, namelen, offset, ino, d_type);
...@@ -646,11 +673,16 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx) ...@@ -646,11 +673,16 @@ 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_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),
}; };
if (rdt.xinobits && lower_layer)
rdt.fsid = lower_layer->fsid;
if (OVL_TYPE_MERGE(ovl_path_type(dir->d_parent))) { if (OVL_TYPE_MERGE(ovl_path_type(dir->d_parent))) {
struct kstat stat; struct kstat stat;
struct path statpath = file->f_path; struct path statpath = file->f_path;
...@@ -693,9 +725,10 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -693,9 +725,10 @@ 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_same_sb(dentry->d_sb) && if (ovl_xino_bits(dentry->d_sb) ||
(ovl_same_sb(dentry->d_sb) &&
(ovl_test_flag(OVL_IMPURE, d_inode(dentry)) || (ovl_test_flag(OVL_IMPURE, d_inode(dentry)) ||
OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent)))) { OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) {
return ovl_iterate_real(file, ctx); return ovl_iterate_real(file, ctx);
} }
return iterate_dir(od->realfile, ctx); return iterate_dir(od->realfile, ctx);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include <linux/exportfs.h>
#include "overlayfs.h" #include "overlayfs.h"
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
...@@ -50,6 +51,11 @@ module_param_named(nfs_export, ovl_nfs_export_def, bool, 0644); ...@@ -50,6 +51,11 @@ module_param_named(nfs_export, ovl_nfs_export_def, bool, 0644);
MODULE_PARM_DESC(ovl_nfs_export_def, MODULE_PARM_DESC(ovl_nfs_export_def,
"Default to on or off for the NFS export feature"); "Default to on or off for the NFS export feature");
static bool ovl_xino_auto_def = IS_ENABLED(CONFIG_OVERLAY_FS_XINO_AUTO);
module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644);
MODULE_PARM_DESC(ovl_xino_auto_def,
"Auto enable xino feature");
static void ovl_entry_stack_free(struct ovl_entry *oe) static void ovl_entry_stack_free(struct ovl_entry *oe)
{ {
unsigned int i; unsigned int i;
...@@ -236,11 +242,12 @@ static void ovl_free_fs(struct ovl_fs *ofs) ...@@ -236,11 +242,12 @@ static void ovl_free_fs(struct ovl_fs *ofs)
if (ofs->upperdir_locked) if (ofs->upperdir_locked)
ovl_inuse_unlock(ofs->upper_mnt->mnt_root); ovl_inuse_unlock(ofs->upper_mnt->mnt_root);
mntput(ofs->upper_mnt); mntput(ofs->upper_mnt);
for (i = 0; i < ofs->numlower; i++) { for (i = 0; i < ofs->numlower; i++)
mntput(ofs->lower_layers[i].mnt); mntput(ofs->lower_layers[i].mnt);
free_anon_bdev(ofs->lower_layers[i].pseudo_dev); for (i = 0; i < ofs->numlowerfs; i++)
} free_anon_bdev(ofs->lower_fs[i].pseudo_dev);
kfree(ofs->lower_layers); kfree(ofs->lower_layers);
kfree(ofs->lower_fs);
kfree(ofs->config.lowerdir); kfree(ofs->config.lowerdir);
kfree(ofs->config.upperdir); kfree(ofs->config.upperdir);
...@@ -325,6 +332,23 @@ static const char *ovl_redirect_mode_def(void) ...@@ -325,6 +332,23 @@ static const char *ovl_redirect_mode_def(void)
return ovl_redirect_dir_def ? "on" : "off"; return ovl_redirect_dir_def ? "on" : "off";
} }
enum {
OVL_XINO_OFF,
OVL_XINO_AUTO,
OVL_XINO_ON,
};
static const char * const ovl_xino_str[] = {
"off",
"auto",
"on",
};
static inline int ovl_xino_def(void)
{
return ovl_xino_auto_def ? OVL_XINO_AUTO : OVL_XINO_OFF;
}
/** /**
* ovl_show_options * ovl_show_options
* *
...@@ -350,6 +374,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -350,6 +374,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())
seq_printf(m, ",xino=%s", ovl_xino_str[ofs->config.xino]);
return 0; return 0;
} }
...@@ -384,6 +410,9 @@ enum { ...@@ -384,6 +410,9 @@ enum {
OPT_INDEX_OFF, OPT_INDEX_OFF,
OPT_NFS_EXPORT_ON, OPT_NFS_EXPORT_ON,
OPT_NFS_EXPORT_OFF, OPT_NFS_EXPORT_OFF,
OPT_XINO_ON,
OPT_XINO_OFF,
OPT_XINO_AUTO,
OPT_ERR, OPT_ERR,
}; };
...@@ -397,6 +426,9 @@ static const match_table_t ovl_tokens = { ...@@ -397,6 +426,9 @@ static const match_table_t ovl_tokens = {
{OPT_INDEX_OFF, "index=off"}, {OPT_INDEX_OFF, "index=off"},
{OPT_NFS_EXPORT_ON, "nfs_export=on"}, {OPT_NFS_EXPORT_ON, "nfs_export=on"},
{OPT_NFS_EXPORT_OFF, "nfs_export=off"}, {OPT_NFS_EXPORT_OFF, "nfs_export=off"},
{OPT_XINO_ON, "xino=on"},
{OPT_XINO_OFF, "xino=off"},
{OPT_XINO_AUTO, "xino=auto"},
{OPT_ERR, NULL} {OPT_ERR, NULL}
}; };
...@@ -511,6 +543,18 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) ...@@ -511,6 +543,18 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
config->nfs_export = false; config->nfs_export = false;
break; break;
case OPT_XINO_ON:
config->xino = OVL_XINO_ON;
break;
case OPT_XINO_OFF:
config->xino = OVL_XINO_OFF;
break;
case OPT_XINO_AUTO:
config->xino = OVL_XINO_AUTO;
break;
default: default:
pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p); pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
return -EINVAL; return -EINVAL;
...@@ -700,6 +744,7 @@ static int ovl_check_namelen(struct path *path, struct ovl_fs *ofs, ...@@ -700,6 +744,7 @@ static int ovl_check_namelen(struct path *path, struct ovl_fs *ofs,
static int ovl_lower_dir(const char *name, struct path *path, static int ovl_lower_dir(const char *name, struct path *path,
struct ovl_fs *ofs, int *stack_depth, bool *remote) struct ovl_fs *ofs, int *stack_depth, bool *remote)
{ {
int fh_type;
int err; int err;
err = ovl_mount_dir_noesc(name, path); err = ovl_mount_dir_noesc(name, path);
...@@ -719,15 +764,19 @@ static int ovl_lower_dir(const char *name, struct path *path, ...@@ -719,15 +764,19 @@ static int ovl_lower_dir(const char *name, struct path *path,
* The inodes index feature and NFS export need to encode and decode * The inodes index feature and NFS export need to encode and decode
* file handles, so they require that all layers support them. * file handles, so they require that all layers support them.
*/ */
fh_type = ovl_can_decode_fh(path->dentry->d_sb);
if ((ofs->config.nfs_export || if ((ofs->config.nfs_export ||
(ofs->config.index && ofs->config.upperdir)) && (ofs->config.index && ofs->config.upperdir)) && !fh_type) {
!ovl_can_decode_fh(path->dentry->d_sb)) {
ofs->config.index = false; ofs->config.index = false;
ofs->config.nfs_export = false; ofs->config.nfs_export = false;
pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n", pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n",
name); name);
} }
/* Check if lower fs has 32bit inode numbers */
if (fh_type != FILEID_INO32_GEN)
ofs->xino_bits = 0;
return 0; return 0;
out_put: out_put:
...@@ -951,6 +1000,7 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ...@@ -951,6 +1000,7 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
{ {
struct vfsmount *mnt = ofs->upper_mnt; struct vfsmount *mnt = ofs->upper_mnt;
struct dentry *temp; struct dentry *temp;
int fh_type;
int err; int err;
err = mnt_want_write(mnt); err = mnt_want_write(mnt);
...@@ -1000,12 +1050,16 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ...@@ -1000,12 +1050,16 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
} }
/* Check if upper/work fs supports file handles */ /* Check if upper/work fs supports file handles */
if (ofs->config.index && fh_type = ovl_can_decode_fh(ofs->workdir->d_sb);
!ovl_can_decode_fh(ofs->workdir->d_sb)) { if (ofs->config.index && !fh_type) {
ofs->config.index = false; ofs->config.index = false;
pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n"); pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
} }
/* Check if upper fs has 32bit inode numbers */
if (fh_type != FILEID_INO32_GEN)
ofs->xino_bits = 0;
/* NFS export of r/w mount depends on index */ /* NFS export of r/w mount depends on index */
if (ofs->config.nfs_export && !ofs->config.index) { if (ofs->config.nfs_export && !ofs->config.index) {
pr_warn("overlayfs: NFS export requires \"index=on\", falling back to nfs_export=off.\n"); pr_warn("overlayfs: NFS export requires \"index=on\", falling back to nfs_export=off.\n");
...@@ -1108,6 +1162,35 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, ...@@ -1108,6 +1162,35 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
return err; return err;
} }
/* Get a unique fsid for the layer */
static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb)
{
unsigned int i;
dev_t dev;
int err;
/* fsid 0 is reserved for upper fs even with non upper overlay */
if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb)
return 0;
for (i = 0; i < ofs->numlowerfs; i++) {
if (ofs->lower_fs[i].sb == sb)
return i + 1;
}
err = get_anon_bdev(&dev);
if (err) {
pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n");
return err;
}
ofs->lower_fs[ofs->numlowerfs].sb = sb;
ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev;
ofs->numlowerfs++;
return ofs->numlowerfs;
}
static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
unsigned int numlower) unsigned int numlower)
{ {
...@@ -1119,23 +1202,27 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, ...@@ -1119,23 +1202,27 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
GFP_KERNEL); GFP_KERNEL);
if (ofs->lower_layers == NULL) if (ofs->lower_layers == NULL)
goto out; goto out;
ofs->lower_fs = kcalloc(numlower, sizeof(struct ovl_sb),
GFP_KERNEL);
if (ofs->lower_fs == NULL)
goto out;
for (i = 0; i < numlower; i++) { for (i = 0; i < numlower; i++) {
struct vfsmount *mnt; struct vfsmount *mnt;
dev_t dev; int fsid;
err = get_anon_bdev(&dev); err = fsid = ovl_get_fsid(ofs, stack[i].mnt->mnt_sb);
if (err) { if (err < 0)
pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n");
goto out; goto out;
}
mnt = clone_private_mount(&stack[i]); mnt = clone_private_mount(&stack[i]);
err = PTR_ERR(mnt); err = PTR_ERR(mnt);
if (IS_ERR(mnt)) { if (IS_ERR(mnt)) {
pr_err("overlayfs: failed to clone lowerpath\n"); pr_err("overlayfs: failed to clone lowerpath\n");
free_anon_bdev(dev);
goto out; goto out;
} }
/* /*
* Make lower layers R/O. That way fchmod/fchown on lower file * Make lower layers R/O. That way fchmod/fchown on lower file
* will fail instead of modifying lower fs. * will fail instead of modifying lower fs.
...@@ -1143,16 +1230,41 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, ...@@ -1143,16 +1230,41 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
ofs->lower_layers[ofs->numlower].mnt = mnt; ofs->lower_layers[ofs->numlower].mnt = mnt;
ofs->lower_layers[ofs->numlower].pseudo_dev = dev;
ofs->lower_layers[ofs->numlower].idx = i + 1; ofs->lower_layers[ofs->numlower].idx = i + 1;
ofs->lower_layers[ofs->numlower].fsid = fsid;
if (fsid) {
ofs->lower_layers[ofs->numlower].fs =
&ofs->lower_fs[fsid - 1];
}
ofs->numlower++; ofs->numlower++;
}
/*
* When all layers on same fs, overlay can use real inode numbers.
* With mount option "xino=on", mounter declares that there are enough
* free high bits in underlying fs to hold the unique fsid.
* If overlayfs does encounter underlying inodes using the high xino
* bits reserved for fsid, it emits a warning and uses the original
* inode number.
*/
if (!ofs->numlowerfs || (ofs->numlowerfs == 1 && !ofs->upper_mnt)) {
ofs->xino_bits = 0;
ofs->config.xino = OVL_XINO_OFF;
} else if (ofs->config.xino == OVL_XINO_ON && !ofs->xino_bits) {
/*
* This is a roundup of number of bits needed for numlowerfs+1
* (i.e. ilog2(numlowerfs+1 - 1) + 1). fsid 0 is reserved for
* upper fs even with non upper overlay.
*/
BUILD_BUG_ON(ilog2(OVL_MAX_STACK) > 31);
ofs->xino_bits = ilog2(ofs->numlowerfs) + 1;
}
/* Check if all lower layers are on same sb */ if (ofs->xino_bits) {
if (i == 0) pr_info("overlayfs: \"xino\" feature enabled using %d upper inode bits.\n",
ofs->same_sb = mnt->mnt_sb; ofs->xino_bits);
else if (ofs->same_sb != mnt->mnt_sb)
ofs->same_sb = NULL;
} }
err = 0; err = 0;
out: out:
return err; return err;
...@@ -1263,6 +1375,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1263,6 +1375,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
ofs->config.index = ovl_index_def; ofs->config.index = ovl_index_def;
ofs->config.nfs_export = ovl_nfs_export_def; ofs->config.nfs_export = ovl_nfs_export_def;
ofs->config.xino = ovl_xino_def();
err = ovl_parse_opt((char *) data, &ofs->config); err = ovl_parse_opt((char *) data, &ofs->config);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1276,6 +1389,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1276,6 +1389,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
sb->s_stack_depth = 0; sb->s_stack_depth = 0;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
/* Assume underlaying fs uses 32bit inodes unless proven otherwise */
if (ofs->config.xino != OVL_XINO_OFF)
ofs->xino_bits = BITS_PER_LONG - 32;
if (ofs->config.upperdir) { if (ofs->config.upperdir) {
if (!ofs->config.workdir) { if (!ofs->config.workdir) {
pr_err("overlayfs: missing 'workdir'\n"); pr_err("overlayfs: missing 'workdir'\n");
...@@ -1305,8 +1422,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1305,8 +1422,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
/* If the upper fs is nonexistent, we mark overlayfs r/o too */ /* If the upper fs is nonexistent, we mark overlayfs r/o too */
if (!ofs->upper_mnt) if (!ofs->upper_mnt)
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
else if (ofs->upper_mnt->mnt_sb != ofs->same_sb)
ofs->same_sb = NULL;
if (!(ovl_force_readonly(ofs)) && ofs->config.index) { if (!(ovl_force_readonly(ofs)) && ofs->config.index) {
err = ovl_get_indexdir(ofs, oe, &upperpath); err = ovl_get_indexdir(ofs, oe, &upperpath);
......
...@@ -47,13 +47,29 @@ struct super_block *ovl_same_sb(struct super_block *sb) ...@@ -47,13 +47,29 @@ struct super_block *ovl_same_sb(struct super_block *sb)
{ {
struct ovl_fs *ofs = sb->s_fs_info; struct ovl_fs *ofs = sb->s_fs_info;
return ofs->same_sb; if (!ofs->numlowerfs)
return ofs->upper_mnt->mnt_sb;
else if (ofs->numlowerfs == 1 && !ofs->upper_mnt)
return ofs->lower_fs[0].sb;
else
return NULL;
} }
bool ovl_can_decode_fh(struct super_block *sb) /*
* Check if underlying fs supports file handles and try to determine encoding
* type, in order to deduce maximum inode number used by fs.
*
* Return 0 if file handles are not supported.
* Return 1 (FILEID_INO32_GEN) if fs uses the default 32bit inode encoding.
* Return -1 if fs uses a non default encoding with unknown inode size.
*/
int ovl_can_decode_fh(struct super_block *sb)
{ {
return (sb->s_export_op && sb->s_export_op->fh_to_dentry && if (!sb->s_export_op || !sb->s_export_op->fh_to_dentry ||
!uuid_is_null(&sb->s_uuid)); uuid_is_null(&sb->s_uuid))
return 0;
return sb->s_export_op->encode_fh ? -1 : FILEID_INO32_GEN;
} }
struct dentry *ovl_indexdir(struct super_block *sb) struct dentry *ovl_indexdir(struct super_block *sb)
...@@ -172,6 +188,13 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry) ...@@ -172,6 +188,13 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry)
return oe->numlower ? oe->lowerstack[0].dentry : NULL; return oe->numlower ? oe->lowerstack[0].dentry : NULL;
} }
struct ovl_layer *ovl_layer_lower(struct dentry *dentry)
{
struct ovl_entry *oe = dentry->d_fsdata;
return oe->numlower ? oe->lowerstack[0].layer : NULL;
}
struct dentry *ovl_dentry_real(struct dentry *dentry) struct dentry *ovl_dentry_real(struct dentry *dentry)
{ {
return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry); return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry);
...@@ -279,12 +302,16 @@ void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect) ...@@ -279,12 +302,16 @@ void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect)
void ovl_inode_init(struct inode *inode, struct dentry *upperdentry, void ovl_inode_init(struct inode *inode, struct dentry *upperdentry,
struct dentry *lowerdentry) struct dentry *lowerdentry)
{ {
struct inode *realinode = d_inode(upperdentry ?: lowerdentry);
if (upperdentry) if (upperdentry)
OVL_I(inode)->__upperdentry = upperdentry; OVL_I(inode)->__upperdentry = upperdentry;
if (lowerdentry) if (lowerdentry)
OVL_I(inode)->lower = igrab(d_inode(lowerdentry)); OVL_I(inode)->lower = igrab(d_inode(lowerdentry));
ovl_copyattr(d_inode(upperdentry ?: lowerdentry), inode); ovl_copyattr(realinode, inode);
if (!inode->i_ino)
inode->i_ino = realinode->i_ino;
} }
void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
...@@ -299,6 +326,8 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) ...@@ -299,6 +326,8 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
smp_wmb(); smp_wmb();
OVL_I(inode)->__upperdentry = upperdentry; OVL_I(inode)->__upperdentry = upperdentry;
if (inode_unhashed(inode)) { if (inode_unhashed(inode)) {
if (!inode->i_ino)
inode->i_ino = upperinode->i_ino;
inode->i_private = upperinode; inode->i_private = upperinode;
__insert_inode_hash(inode, (unsigned long) upperinode); __insert_inode_hash(inode, (unsigned long) upperinode);
} }
......
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