lkml.org 
[lkml]   [2022]   [Aug]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.19 1116/1157] ext4: fix reading leftover inlined symlinks
    Date
    From: Zhang Yi <yi.zhang@huawei.com>

    [ Upstream commit 5a57bca9050d740ca37184302e23d0e7633e3ebc ]

    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")
    Cc: stable@kernel.org
    Reported-by: Torge Matthies <openglfreak@googlemail.com>
    Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
    Tested-by: Torge Matthies <openglfreak@googlemail.com>
    Link: https://lore.kernel.org/r/20220630090100.2769490-1-yi.zhang@huawei.com
    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    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);
    --
    2.35.1


    \
     
     \ /
      Last update: 2022-08-16 06:59    [W:4.028 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site