Skip to content

Commit

Permalink
ovl: consistent i_ino for non-samefs with xino
Browse files Browse the repository at this point in the history
When overlay layers are not all on the same fs, but all inode numbers
of underlying fs do not use the high 'xino' bits, overlay st_ino values
are constant and persistent.

In that case, set i_ino value to the same value as st_ino for nfsd
readdirplus validator.

Signed-off-by: Amir Goldstein <[email protected]>
Signed-off-by: Miklos Szeredi <[email protected]>
  • Loading branch information
amir73il authored and Miklos Szeredi committed Apr 12, 2018
1 parent e487d88 commit 12574a9
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 14 deletions.
2 changes: 1 addition & 1 deletion fs/overlayfs/export.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ static struct dentry *ovl_obtain_alias(struct super_block *sb,
if (d_is_dir(upper ?: lower))
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)) {
dput(upper);
return ERR_CAST(inode);
Expand Down
27 changes: 18 additions & 9 deletions fs/overlayfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,19 +489,26 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode)
}

static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev,
unsigned long ino)
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), set the same value 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
* (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))
if (inode->i_sb->s_export_op &&
(ovl_same_sb(inode->i_sb) || xinobits)) {
inode->i_ino = ino;
else
if (xinobits && fsid && !(ino >> (64 - xinobits)))
inode->i_ino |= (unsigned long)fsid << (64 - xinobits);
} else {
inode->i_ino = get_next_ino();
}
inode->i_mode = mode;
inode->i_flags |= S_NOCMTIME;
#ifdef CONFIG_FS_POSIX_ACL
Expand Down Expand Up @@ -637,7 +644,7 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)

inode = new_inode(sb);
if (inode)
ovl_fill_inode(inode, mode, rdev, 0);
ovl_fill_inode(inode, mode, rdev, 0, 0);

return inode;
}
Expand Down Expand Up @@ -743,12 +750,14 @@ 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 dentry *lowerdentry, struct dentry *index,
struct ovl_path *lowerpath, struct dentry *index,
unsigned int numlower)
{
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
struct inode *inode;
struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL;
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index);
int fsid = bylower ? lowerpath->layer->fsid : 0;
bool is_dir;
unsigned long ino = 0;

Expand Down Expand Up @@ -796,7 +805,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
if (!inode)
goto out_nomem;
}
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev, ino);
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev, ino, fsid);
ovl_inode_init(inode, upperdentry, lowerdentry);

if (upperdentry && ovl_is_impuredir(upperdentry))
Expand Down
4 changes: 1 addition & 3 deletions fs/overlayfs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -1004,9 +1004,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
upperdentry = dget(index);

if (upperdentry || ctr) {
if (ctr)
origin = stack[0].dentry;
inode = ovl_get_inode(dentry->d_sb, upperdentry, origin, index,
inode = ovl_get_inode(dentry->d_sb, upperdentry, stack, index,
ctr);
err = PTR_ERR(inode);
if (IS_ERR(inode))
Expand Down
2 changes: 1 addition & 1 deletion fs/overlayfs/overlayfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,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,
bool is_upper);
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);
static inline void ovl_copyattr(struct inode *from, struct inode *to)
{
Expand Down

0 comments on commit 12574a9

Please sign in to comment.