// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
*/
#include <linux/fs.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/xattr.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
// clang-format off
#define SYSTEM_DOS_ATTRIB "system.dos_attrib"
#define SYSTEM_NTFS_ATTRIB "system.ntfs_attrib"
#define SYSTEM_NTFS_SECURITY "system.ntfs_security"
// clang-format on
static inline size_t unpacked_ea_size(const struct EA_FULL *ea)
{
return ea->size ? le32_to_cpu(ea->size)
: ALIGN(struct_size(ea, name,
1 + ea->name_len +
le16_to_cpu(ea->elength)),
4);
}
static inline size_t packed_ea_size(const struct EA_FULL *ea)
{
return struct_size(ea, name,
1 + ea->name_len + le16_to_cpu(ea->elength)) -
offsetof(struct EA_FULL, flags);
}
/*
* find_ea
*
* Assume there is at least one xattr in the list.
*/
static inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes,
const char *name, u8 name_len, u32 *off)
{
*off = 0;
if (!ea_all || !bytes)
return false;
for (;;) {
const struct EA_FULL *ea = Add2Ptr(ea_all, *off);
u32 next_off = *off + unpacked_ea_size(ea);
if (next_off > bytes)
return false;
if (ea->name_len == name_len &&
!memcmp(ea->name, name, name_len))
return true;
*off = next_off;
if (next_off >= bytes)
return false;
}
}
/*
* ntfs_read_ea - Read all extended attributes.
* @ea: New allocated memory.
* @info: Pointer into resident data.
*/
static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
size_t add_bytes, const struct EA_INFO **info)
{
int err;
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct ATTR_LIST_ENTRY *le = NULL;
struct ATTRIB *attr_info, *attr_ea;
void *ea_p;
u32 size;
static_assert(le32_to_cpu(ATTR_EA_INFO) < le32_to_cpu(ATTR_EA));
*ea = NULL;
*info = NULL;
attr_info =
ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, NULL);
attr_ea =
ni_find_attr(ni, attr_info, &le, ATTR_EA, NULL, 0, NULL, NULL);
if (!attr_ea || !attr_info)
return 0;
*info = resident_data_ex(attr_info, sizeof(struct EA_INFO));
if (!*info)
return -EINVAL;
/* Check Ea limit. */
size = le32_to_cpu((*info)->size);
if (size > sbi->ea_max_size)
return -EFBIG;
if (attr_size(attr_ea) > sbi->ea_max_size)
return -EFBIG;
/* Allocate memory for packed Ea. */
ea_p = kmalloc(size + add_bytes, GFP_NOFS);
if (!ea_p)
return -ENOMEM;
if (!size) {
;
} else if (attr_ea->non_res) {
struct runs_tree run;
run_init(&run);
err = attr_load_runs(attr_ea, ni, &run, NULL);
if (!err)
err = ntfs_read_run_nb(sbi, &run, 0, ea_p, size, NULL);
run_close(&run);
if (err)
goto out;
} else {
void *p = resident_data_ex(attr_ea, size);
if (!p) {
err = -EINVAL;
goto out;
}
memcpy(ea_p, p, size);
}
memset(Add2Ptr(ea_p, size), 0, add_bytes);
*ea = ea_p;
return 0;
out:
kfree(ea_p);
*ea = NULL;
return err;
}
/*
* ntfs_list_ea
*
* Copy a list of xattrs names into the buffer
* provided, or compute the buffer size required.
*
* Return:
* * Number of bytes used / required on
* * -ERRNO - on failure
*/
static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
size_t bytes_per_buffer)
{
const struct EA_INFO *info;
struct EA_FULL *ea_all = NULL;
const struct EA_FULL *ea;
u32 off, size;
int err;
size_t ret;
err = ntfs_read_ea(ni, &ea_all, 0, &info);
if (err)
return err;
if (!info || !ea_all)
return 0;
size = le32_to_cpu(info->size);
/* Enumerate all xattrs. */
for (ret = 0, off = 0; off < size; off += unpacked_ea_size(ea)) {
ea = Add2Ptr(ea_all, off);
if (buffer) {
if (ret + ea->name_len + 1 > bytes_per_buffer) {
err = -ERANGE;
goto out;
}
memcpy(buffer + ret, ea->name, ea->name_len);
buffer[ret + ea->name_len] = 0;
}
ret += ea->name_len + 1;
}
out:
kfree(ea_all);
return err ? err : ret;
}
static int ntfs_get_ea