mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
erofs: fix file handle encoding for 64-bit NIDs
EROFS uses NID to indicate the on-disk inode offset, which can
exceed 32 bits. However, the default encode_fh uses the ino32,
thus it doesn't work if the image is larger than 128GiB.
Let's introduce our own helpers to encode file handles.
It's easy to reproduce:
1. prepare an erofs image with nid bigger than U32_MAX
2. mount -t erofs foo.img /mnt/erofs
3. set exportfs with configuration: /mnt/erofs *(rw,sync,
no_root_squash)
4. mount -t nfs $IP:/mnt/erofs /mnt/nfs
5. md5sum /mnt/nfs/foo # foo is the file which nid bigger
than U32_MAX. # you will get ESTALE error.
In the case of overlayfs, the underlying filesystem's file
handle is encoded in ovl_fb.fid, which is similar to NFS's
case. If the NID of file is larger than U32_MAX, the overlay
will get -ESTALE error when calls exportfs_decode_fh.
Fixes: 3e917cc305
("erofs: make filesystem exportable")
Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Link: https://lore.kernel.org/r/20250507094015.14007-1-lihongbo22@huawei.com
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
This commit is contained in:
parent
82f2b0b97b
commit
510de8363f
1 changed files with 36 additions and 8 deletions
|
@ -510,24 +510,52 @@ static int erofs_fc_parse_param(struct fs_context *fc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct inode *erofs_nfs_get_inode(struct super_block *sb,
|
||||
u64 ino, u32 generation)
|
||||
static int erofs_encode_fh(struct inode *inode, u32 *fh, int *max_len,
|
||||
struct inode *parent)
|
||||
{
|
||||
return erofs_iget(sb, ino);
|
||||
erofs_nid_t nid = EROFS_I(inode)->nid;
|
||||
int len = parent ? 6 : 3;
|
||||
|
||||
if (*max_len < len) {
|
||||
*max_len = len;
|
||||
return FILEID_INVALID;
|
||||
}
|
||||
|
||||
fh[0] = (u32)(nid >> 32);
|
||||
fh[1] = (u32)(nid & 0xffffffff);
|
||||
fh[2] = inode->i_generation;
|
||||
|
||||
if (parent) {
|
||||
nid = EROFS_I(parent)->nid;
|
||||
|
||||
fh[3] = (u32)(nid >> 32);
|
||||
fh[4] = (u32)(nid & 0xffffffff);
|
||||
fh[5] = parent->i_generation;
|
||||
}
|
||||
|
||||
*max_len = len;
|
||||
return parent ? FILEID_INO64_GEN_PARENT : FILEID_INO64_GEN;
|
||||
}
|
||||
|
||||
static struct dentry *erofs_fh_to_dentry(struct super_block *sb,
|
||||
struct fid *fid, int fh_len, int fh_type)
|
||||
{
|
||||
return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
|
||||
erofs_nfs_get_inode);
|
||||
if ((fh_type != FILEID_INO64_GEN &&
|
||||
fh_type != FILEID_INO64_GEN_PARENT) || fh_len < 3)
|
||||
return NULL;
|
||||
|
||||
return d_obtain_alias(erofs_iget(sb,
|
||||
((u64)fid->raw[0] << 32) | fid->raw[1]));
|
||||
}
|
||||
|
||||
static struct dentry *erofs_fh_to_parent(struct super_block *sb,
|
||||
struct fid *fid, int fh_len, int fh_type)
|
||||
{
|
||||
return generic_fh_to_parent(sb, fid, fh_len, fh_type,
|
||||
erofs_nfs_get_inode);
|
||||
if (fh_type != FILEID_INO64_GEN_PARENT || fh_len < 6)
|
||||
return NULL;
|
||||
|
||||
return d_obtain_alias(erofs_iget(sb,
|
||||
((u64)fid->raw[3] << 32) | fid->raw[4]));
|
||||
}
|
||||
|
||||
static struct dentry *erofs_get_parent(struct dentry *child)
|
||||
|
@ -543,7 +571,7 @@ static struct dentry *erofs_get_parent(struct dentry *child)
|
|||
}
|
||||
|
||||
static const struct export_operations erofs_export_ops = {
|
||||
.encode_fh = generic_encode_ino32_fh,
|
||||
.encode_fh = erofs_encode_fh,
|
||||
.fh_to_dentry = erofs_fh_to_dentry,
|
||||
.fh_to_parent = erofs_fh_to_parent,
|
||||
.get_parent = erofs_get_parent,
|
||||
|
|
Loading…
Add table
Reference in a new issue