Hi, Torge. Please take a look at this patch and check could it solve your problem or not. Thanks, Yi. On 2022/6/30 17:01, Zhang Yi wrote: > Since commit 6493792d3299 ("ext4: convert symlink external data block > mapping to bdev"), create new symlink with inline_data is not supported, > but it missing to handle the leftover inlined symlinks, which could > cause below error message and fail to read symlink. > > ls: cannot read symbolic link 'foo': Structure needs cleaning > > EXT4-fs error (device sda): ext4_map_blocks:605: inode #12: block > 2021161080: comm ls: lblock 0 mapped to illegal pblock 2021161080 > (length 1) > > Fix this regression by adding ext4_read_inline_link(), which read the > inline data directly and convert it through a kmalloced buffer. > > Fixes: 6493792d3299 ("ext4: convert symlink external data block mapping to bdev") > Reported-by: Torge Matthies <openglfreak@xxxxxxxxxxxxxx> > Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx> > --- > fs/ext4/ext4.h | 1 + > fs/ext4/inline.c | 30 ++++++++++++++++++++++++++++++ > fs/ext4/symlink.c | 15 +++++++++++++++ > 3 files changed, 46 insertions(+) > > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h > index 75b8d81b2469..adfc30ee4b7b 100644 > --- a/fs/ext4/ext4.h > +++ b/fs/ext4/ext4.h > @@ -3583,6 +3583,7 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode, > extern int ext4_inline_data_fiemap(struct inode *inode, > struct fiemap_extent_info *fieinfo, > int *has_inline, __u64 start, __u64 len); > +extern void *ext4_read_inline_link(struct inode *inode); > > struct iomap; > extern int ext4_inline_data_iomap(struct inode *inode, struct iomap *iomap); > diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c > index cff52ff6549d..1fa36cbe09ec 100644 > --- a/fs/ext4/inline.c > +++ b/fs/ext4/inline.c > @@ -6,6 +6,7 @@ > > #include <linux/iomap.h> > #include <linux/fiemap.h> > +#include <linux/namei.h> > #include <linux/iversion.h> > #include <linux/sched/mm.h> > > @@ -1588,6 +1589,35 @@ int ext4_read_inline_dir(struct file *file, > return ret; > } > > +void *ext4_read_inline_link(struct inode *inode) > +{ > + struct ext4_iloc iloc; > + int ret, inline_size; > + void *link; > + > + ret = ext4_get_inode_loc(inode, &iloc); > + if (ret) > + return ERR_PTR(ret); > + > + ret = -ENOMEM; > + inline_size = ext4_get_inline_size(inode); > + link = kmalloc(inline_size + 1, GFP_NOFS); > + if (!link) > + goto out; > + > + ret = ext4_read_inline_data(inode, link, inline_size, &iloc); > + if (ret < 0) { > + kfree(link); > + goto out; > + } > + nd_terminate_link(link, inode->i_size, ret); > +out: > + if (ret < 0) > + link = ERR_PTR(ret); > + brelse(iloc.bh); > + return link; > +} > + > struct buffer_head *ext4_get_first_inline_block(struct inode *inode, > struct ext4_dir_entry_2 **parent_de, > int *retval) > diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c > index d281f5bcc526..3d3ed3c38f56 100644 > --- a/fs/ext4/symlink.c > +++ b/fs/ext4/symlink.c > @@ -74,6 +74,21 @@ static const char *ext4_get_link(struct dentry *dentry, struct inode *inode, > struct delayed_call *callback) > { > struct buffer_head *bh; > + char *inline_link; > + > + /* > + * Create a new inlined symlink is not supported, just provide a > + * method to read the leftovers. > + */ > + if (ext4_has_inline_data(inode)) { > + if (!dentry) > + return ERR_PTR(-ECHILD); > + > + inline_link = ext4_read_inline_link(inode); > + if (!IS_ERR(inline_link)) > + set_delayed_call(callback, kfree_link, inline_link); > + return inline_link; > + } > > if (!dentry) { > bh = ext4_getblk(NULL, inode, 0, EXT4_GET_BLOCKS_CACHED_NOWAIT); >