// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Pocessing of EA's
*
* Part of this file is based on code from the NTFS-3G.
*
* Copyright (c) 2014-2021 Jean-Pierre Andre
* Copyright (c) 2025 LG Electronics Co., Ltd.
*/
#include <linux/fs.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/xattr.h>
#include "layout.h"
#include "attrib.h"
#include "index.h"
#include "dir.h"
#include "ea.h"
static int ntfs_write_ea(struct ntfs_inode *ni, __le32 type, char *value, s64 ea_off,
s64 ea_size, bool need_truncate)
{
struct inode *ea_vi;
int err = 0;
s64 written;
ea_vi = ntfs_attr_iget(VFS_I(ni), type, AT_UNNAMED, 0);
if (IS_ERR(ea_vi))
return PTR_ERR(ea_vi);
written = ntfs_inode_attr_pwrite(ea_vi, ea_off, ea_size, value, false);
if (written != ea_size)
err = -EIO;
else {
struct ntfs_inode *ea_ni = NTFS_I(ea_vi);
if (need_truncate && ea_ni->data_size > ea_off + ea_size)
ntfs_attr_truncate(ea_ni, ea_off + ea_size);
mark_mft_record_dirty(ni);
}
iput(ea_vi);
return err;
}
static int ntfs_ea_lookup(char *ea_buf, s64 ea_buf_size, const char *name,
int name_len, s64 *ea_offset, s64 *ea_size)
{
const struct ea_attr *p_ea;
size_t actual_size;
loff_t offset, p_ea_size;
unsigned int next;
if (ea_buf_size < sizeof(struct ea_attr))
goto out;
offset = 0;
do {
p_ea = (const struct ea_attr *)&ea_buf[offset];
next = le32_to_cpu(p_ea->next_entry_offset);
p_ea_size = next ? next : (ea_buf_size - offset);
if (p_ea_size < sizeof(struct ea_attr) ||
offset + p_ea_size > ea_buf_size)
break;
if ((s64)p_ea->ea_name_length + 1 >
p_ea_size - offsetof(struct ea_attr, ea_name))
break;
actual_size = ALIGN(struct_size(p_ea, ea_name, 1 + p_ea->ea_name_length +
le16_to_cpu(p_ea->ea_value_length)), 4);
if (actual_size > p_ea_size)
break;
if (p_ea->ea_name_length == name_len &&
!memcmp(p_ea->ea_name, name, name_len)) {
*ea_offset = offset;
*ea_size = next ? next : actual_size;
if (ea_buf_size < *ea_offset + *ea_size)
goto out;
return 0;
}
offset += next;
} while (next > 0 && offset < ea_buf_size);
out:
return -ENOENT;
}
/*
* Return the existing EA
*
* The EA_INFORMATION is not examined and the consistency of the
* existing EA is not checked.
*
* If successful, the full attribute is returned unchanged
* and its size is returned.
* If the designated buffer is too small, the needed size is
* returned, and the buffer is left unchanged.
* If there is an error, a negative value is returned and errno
* is set according to the error.
*/
static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len,
void *buffer, size_t size)
{
struct ntfs_inode *ni = NTFS_I(inode);
const struct ea_attr *p_ea;
char *ea_buf;
s64 ea_off, ea_size, all_ea_size, ea_info_size;
int err;
u32 ea_info_qlen;
u16 ea_value_len;
struct ea_information *p_ea_info;
if (!NInoHasEA(ni))
return -ENODATA;
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)) {
kvfree(p_ea_info);
return -ENODATA;
}
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 (ea_info_qlen > all_ea_size) {
err = -EIO;
goto free_ea_buf;