Commit e487d889 authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi

ovl: constant st_ino for non-samefs with xino

On 64bit systems, when overlay layers are not all on the same fs, but
all inode numbers of underlying fs are not using the high bits, use the
high bits to partition the overlay st_ino address space.  The high bits
hold 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.

Currently, our only indication for available high ino bits is from a
filesystem that supports file handles and uses the default encode_fh()
operation, which encodes a 32bit inode number.
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 5148626b
...@@ -63,6 +63,7 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, ...@@ -63,6 +63,7 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat,
struct ovl_layer *lower_layer) struct ovl_layer *lower_layer)
{ {
bool samefs = ovl_same_sb(dentry->d_sb); bool samefs = ovl_same_sb(dentry->d_sb);
unsigned int xinobits = ovl_xino_bits(dentry->d_sb);
if (samefs) { if (samefs) {
/* /*
...@@ -71,7 +72,31 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, ...@@ -71,7 +72,31 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat,
* which is friendly to du -x. * which is friendly to du -x.
*/ */
stat->dev = dentry->d_sb->s_dev; stat->dev = dentry->d_sb->s_dev;
} else if (S_ISDIR(dentry->d_inode->i_mode)) { 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 * Always use the overlay st_dev for directories, so 'find
* -xdev' will scan the entire overlay mount and won't cross the * -xdev' will scan the entire overlay mount and won't cross the
...@@ -118,11 +143,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat, ...@@ -118,11 +143,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
/* /*
* For non-dir or same fs, we use st_ino of the copy up origin. * For non-dir or same fs, we use st_ino of the copy up origin.
* 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 lower filesystem supports NFS file handles, this also guaranties * If lower filesystem supports NFS file handles, this also guaranties
* persistent st_ino across mount cycle. * persistent st_ino across mount cycle.
*/ */
if (!is_dir || samefs) { if (!is_dir || samefs || ovl_xino_bits(dentry->d_sb)) {
if (!OVL_TYPE_UPPER(type)) { if (!OVL_TYPE_UPPER(type)) {
lower_layer = ovl_layer_lower(dentry); lower_layer = ovl_layer_lower(dentry);
} else if (OVL_TYPE_ORIGIN(type)) { } else if (OVL_TYPE_ORIGIN(type)) {
......
...@@ -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);
...@@ -264,6 +264,13 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) ...@@ -264,6 +264,13 @@ 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);
......
...@@ -63,6 +63,8 @@ struct ovl_fs { ...@@ -63,6 +63,8 @@ struct ovl_fs {
/* Did we take the inuse lock? */ /* Did we take the inuse lock? */
bool upperdir_locked; bool upperdir_locked;
bool workdir_locked; bool workdir_locked;
/* 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 */
......
...@@ -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>");
...@@ -701,6 +702,7 @@ static int ovl_check_namelen(struct path *path, struct ovl_fs *ofs, ...@@ -701,6 +702,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);
...@@ -720,15 +722,19 @@ static int ovl_lower_dir(const char *name, struct path *path, ...@@ -720,15 +722,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:
...@@ -952,6 +958,7 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ...@@ -952,6 +958,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);
...@@ -1001,12 +1008,16 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ...@@ -1001,12 +1008,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");
...@@ -1185,6 +1196,11 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, ...@@ -1185,6 +1196,11 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
} }
ofs->numlower++; ofs->numlower++;
} }
/* When all layers on same fs, overlay can use real inode numbers */
if (!ofs->numlowerfs || (ofs->numlowerfs == 1 && !ofs->upper_mnt))
ofs->xino_bits = 0;
err = 0; err = 0;
out: out:
return err; return err;
...@@ -1308,6 +1324,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1308,6 +1324,8 @@ 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 */
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");
......
...@@ -55,10 +55,21 @@ struct super_block *ovl_same_sb(struct super_block *sb) ...@@ -55,10 +55,21 @@ struct super_block *ovl_same_sb(struct super_block *sb)
return NULL; 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)
......
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