ntfs: return errors from ntfs_attr_readall

ntfs_attr_readall() currently loses the failure reason for attribute
lookup, allocation, and read failures by returning NULL. Return ERR_PTR()
with the original error instead.

The reparse parser can then propagate allocation and I/O errors without
treating them as filesystem corruption.

Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Hyunchul Lee
2026-08-21 14:00:53 +09:00
committed by Namjae Jeon
parent d38ce59d16
commit 56cc42a7b3
4 changed files with 44 additions and 10 deletions

View File

@@ -5296,6 +5296,7 @@ int ntfs_attr_remove(struct ntfs_inode *ni, const __le32 type, __le16 *name,
* On success a buffer is allocated with the content of the attribute
* and which needs to be freed when it's not needed anymore. If the
* @data_size parameter is non-NULL then the data size is set there.
* On error, an ERR_PTR() containing the negative error code is returned.
*/
void *ntfs_attr_readall(struct ntfs_inode *ni, const __le32 type,
__le16 *name, u32 name_len, s64 *data_size)
@@ -5310,6 +5311,7 @@ void *ntfs_attr_readall(struct ntfs_inode *ni, const __le32 type,
bmp_vi = ntfs_attr_iget(VFS_I(ni), type, name, name_len);
if (IS_ERR(bmp_vi)) {
ret = ERR_PTR(PTR_ERR(bmp_vi));
ntfs_debug("ntfs_attr_iget failed");
goto err_exit;
}
@@ -5319,17 +5321,21 @@ void *ntfs_attr_readall(struct ntfs_inode *ni, const __le32 type,
(bmp_ni->type != AT_BITMAP ||
bmp_ni->data_size > ((ni->vol->nr_clusters + 7) >> 3))) {
ntfs_error(sb, "Invalid attribute data size");
ret = ERR_PTR(-EIO);
goto out;
}
data = kvmalloc(bmp_ni->data_size, GFP_NOFS);
if (!data)
if (!data) {
ret = ERR_PTR(-ENOMEM);
goto out;
}
size = ntfs_inode_attr_pread(VFS_I(bmp_ni), 0, bmp_ni->data_size,
(u8 *)data);
if (size != bmp_ni->data_size) {
ntfs_error(sb, "ntfs_attr_pread failed");
ret = size < 0 ? ERR_PTR((int)size) : ERR_PTR(-EIO);
kvfree(data);
goto out;
}

View File

@@ -122,17 +122,19 @@ static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len,
p_ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0,
&ea_info_size);
if (!p_ea_info || ea_info_size != sizeof(struct ea_information)) {
if (IS_ERR(p_ea_info))
return PTR_ERR(p_ea_info);
if (ea_info_size != sizeof(struct ea_information)) {
kvfree(p_ea_info);
return -ENODATA;
return -EIO;
}
ea_info_qlen = le32_to_cpu(p_ea_info->ea_query_length);
kvfree(p_ea_info);
ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &all_ea_size);
if (!ea_buf)
return -ENODATA;
if (IS_ERR(ea_buf))
return PTR_ERR(ea_buf);
if (ea_info_qlen > all_ea_size) {
err = -EIO;
@@ -208,10 +210,22 @@ static int ntfs_set_ea(struct inode *inode, const char *name, size_t name_len,
if (ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0)) {
p_ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0,
&ea_info_size);
if (!p_ea_info || ea_info_size != sizeof(struct ea_information))
if (IS_ERR(p_ea_info)) {
err = PTR_ERR(p_ea_info);
p_ea_info = NULL;
goto out;
}
if (ea_info_size != sizeof(struct ea_information)) {
err = -EIO;
goto out;
}
ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &all_ea_size);
if (IS_ERR(ea_buf)) {
err = PTR_ERR(ea_buf);
ea_buf = NULL;
goto out;
}
if (!ea_buf) {
ea_info_qsize = 0;
kvfree(p_ea_info);
@@ -514,14 +528,24 @@ ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
mutex_lock(&NTFS_I(inode)->mrec_lock);
ea_info = ntfs_attr_readall(ni, AT_EA_INFORMATION, NULL, 0,
&ea_info_size);
if (!ea_info || ea_info_size != sizeof(struct ea_information))
if (IS_ERR(ea_info)) {
err = PTR_ERR(ea_info);
ea_info = NULL;
goto out;
}
if (ea_info_size != sizeof(struct ea_information)) {
err = -EIO;
goto out;
}
ea_info_qsize = le32_to_cpu(ea_info->ea_query_length);
ea_buf = ntfs_attr_readall(ni, AT_EA, NULL, 0, &ea_buf_size);
if (!ea_buf)
if (IS_ERR(ea_buf)) {
err = PTR_ERR(ea_buf);
ea_buf = NULL;
goto out;
}
if (ea_info_qsize > ea_buf_size || ea_info_qsize == 0)
goto out;

View File

@@ -1108,8 +1108,8 @@ static s64 ntfs_ibm_get_free(struct ntfs_index_context *icx)
bm = ntfs_attr_readall(icx->idx_ni, AT_BITMAP, icx->name, icx->name_len,
&size);
if (!bm)
return -EIO;
if (IS_ERR(bm))
return PTR_ERR(bm);
for (byte = 0; byte < size; byte++) {
if (bm[byte] == 255)

View File

@@ -256,6 +256,8 @@ unsigned int ntfs_make_symlink(struct ntfs_inode *ni)
reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0,
&attr_size);
if (IS_ERR(reparse_attr))
reparse_attr = NULL;
if (reparse_attr &&
valid_reparse_data(ni, reparse_attr, attr_size)) {
err = -EINVAL;
@@ -335,6 +337,8 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr
reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi),
AT_REPARSE_POINT, NULL, 0, &attr_size);
if (IS_ERR(reparse_attr))
reparse_attr = NULL;
if (reparse_attr && attr_size >= sizeof(*reparse_attr)) {
switch (reparse_attr->reparse_tag) {