162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci#include <linux/posix_acl.h>
1062306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h>
1162306a36Sopenharmony_ci#include <linux/xattr.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "debug.h"
1462306a36Sopenharmony_ci#include "ntfs.h"
1562306a36Sopenharmony_ci#include "ntfs_fs.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci// clang-format off
1862306a36Sopenharmony_ci#define SYSTEM_DOS_ATTRIB     "system.dos_attrib"
1962306a36Sopenharmony_ci#define SYSTEM_NTFS_ATTRIB    "system.ntfs_attrib"
2062306a36Sopenharmony_ci#define SYSTEM_NTFS_ATTRIB_BE "system.ntfs_attrib_be"
2162306a36Sopenharmony_ci#define SYSTEM_NTFS_SECURITY  "system.ntfs_security"
2262306a36Sopenharmony_ci// clang-format on
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic inline size_t unpacked_ea_size(const struct EA_FULL *ea)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return ea->size ? le32_to_cpu(ea->size) :
2762306a36Sopenharmony_ci			  ALIGN(struct_size(ea, name,
2862306a36Sopenharmony_ci					    1 + ea->name_len +
2962306a36Sopenharmony_ci						    le16_to_cpu(ea->elength)),
3062306a36Sopenharmony_ci				4);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline size_t packed_ea_size(const struct EA_FULL *ea)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	return struct_size(ea, name,
3662306a36Sopenharmony_ci			   1 + ea->name_len + le16_to_cpu(ea->elength)) -
3762306a36Sopenharmony_ci	       offsetof(struct EA_FULL, flags);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * find_ea
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * Assume there is at least one xattr in the list.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_cistatic inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes,
4662306a36Sopenharmony_ci			   const char *name, u8 name_len, u32 *off, u32 *ea_sz)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	u32 ea_size;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	*off = 0;
5162306a36Sopenharmony_ci	if (!ea_all)
5262306a36Sopenharmony_ci		return false;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (; *off < bytes; *off += ea_size) {
5562306a36Sopenharmony_ci		const struct EA_FULL *ea = Add2Ptr(ea_all, *off);
5662306a36Sopenharmony_ci		ea_size = unpacked_ea_size(ea);
5762306a36Sopenharmony_ci		if (ea->name_len == name_len &&
5862306a36Sopenharmony_ci		    !memcmp(ea->name, name, name_len)) {
5962306a36Sopenharmony_ci			if (ea_sz)
6062306a36Sopenharmony_ci				*ea_sz = ea_size;
6162306a36Sopenharmony_ci			return true;
6262306a36Sopenharmony_ci		}
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return false;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * ntfs_read_ea - Read all extended attributes.
7062306a36Sopenharmony_ci * @ea:		New allocated memory.
7162306a36Sopenharmony_ci * @info:	Pointer into resident data.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
7462306a36Sopenharmony_ci			size_t add_bytes, const struct EA_INFO **info)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int err = -EINVAL;
7762306a36Sopenharmony_ci	struct ntfs_sb_info *sbi = ni->mi.sbi;
7862306a36Sopenharmony_ci	struct ATTR_LIST_ENTRY *le = NULL;
7962306a36Sopenharmony_ci	struct ATTRIB *attr_info, *attr_ea;
8062306a36Sopenharmony_ci	void *ea_p;
8162306a36Sopenharmony_ci	u32 size, off, ea_size;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	static_assert(le32_to_cpu(ATTR_EA_INFO) < le32_to_cpu(ATTR_EA));
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	*ea = NULL;
8662306a36Sopenharmony_ci	*info = NULL;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	attr_info =
8962306a36Sopenharmony_ci		ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, NULL);
9062306a36Sopenharmony_ci	attr_ea =
9162306a36Sopenharmony_ci		ni_find_attr(ni, attr_info, &le, ATTR_EA, NULL, 0, NULL, NULL);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!attr_ea || !attr_info)
9462306a36Sopenharmony_ci		return 0;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	*info = resident_data_ex(attr_info, sizeof(struct EA_INFO));
9762306a36Sopenharmony_ci	if (!*info)
9862306a36Sopenharmony_ci		goto out;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Check Ea limit. */
10162306a36Sopenharmony_ci	size = le32_to_cpu((*info)->size);
10262306a36Sopenharmony_ci	if (size > sbi->ea_max_size) {
10362306a36Sopenharmony_ci		err = -EFBIG;
10462306a36Sopenharmony_ci		goto out;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (attr_size(attr_ea) > sbi->ea_max_size) {
10862306a36Sopenharmony_ci		err = -EFBIG;
10962306a36Sopenharmony_ci		goto out;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (!size) {
11362306a36Sopenharmony_ci		/* EA info persists, but xattr is empty. Looks like EA problem. */
11462306a36Sopenharmony_ci		goto out;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Allocate memory for packed Ea. */
11862306a36Sopenharmony_ci	ea_p = kmalloc(size_add(size, add_bytes), GFP_NOFS);
11962306a36Sopenharmony_ci	if (!ea_p)
12062306a36Sopenharmony_ci		return -ENOMEM;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (attr_ea->non_res) {
12362306a36Sopenharmony_ci		struct runs_tree run;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		run_init(&run);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		err = attr_load_runs_range(ni, ATTR_EA, NULL, 0, &run, 0, size);
12862306a36Sopenharmony_ci		if (!err)
12962306a36Sopenharmony_ci			err = ntfs_read_run_nb(sbi, &run, 0, ea_p, size, NULL);
13062306a36Sopenharmony_ci		run_close(&run);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		if (err)
13362306a36Sopenharmony_ci			goto out1;
13462306a36Sopenharmony_ci	} else {
13562306a36Sopenharmony_ci		void *p = resident_data_ex(attr_ea, size);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		if (!p)
13862306a36Sopenharmony_ci			goto out1;
13962306a36Sopenharmony_ci		memcpy(ea_p, p, size);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	memset(Add2Ptr(ea_p, size), 0, add_bytes);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	err = -EINVAL;
14562306a36Sopenharmony_ci	/* Check all attributes for consistency. */
14662306a36Sopenharmony_ci	for (off = 0; off < size; off += ea_size) {
14762306a36Sopenharmony_ci		const struct EA_FULL *ef = Add2Ptr(ea_p, off);
14862306a36Sopenharmony_ci		u32 bytes = size - off;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		/* Check if we can use field ea->size. */
15162306a36Sopenharmony_ci		if (bytes < sizeof(ef->size))
15262306a36Sopenharmony_ci			goto out1;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		if (ef->size) {
15562306a36Sopenharmony_ci			ea_size = le32_to_cpu(ef->size);
15662306a36Sopenharmony_ci			if (ea_size > bytes)
15762306a36Sopenharmony_ci				goto out1;
15862306a36Sopenharmony_ci			continue;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		/* Check if we can use fields ef->name_len and ef->elength. */
16262306a36Sopenharmony_ci		if (bytes < offsetof(struct EA_FULL, name))
16362306a36Sopenharmony_ci			goto out1;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		ea_size = ALIGN(struct_size(ef, name,
16662306a36Sopenharmony_ci					    1 + ef->name_len +
16762306a36Sopenharmony_ci						    le16_to_cpu(ef->elength)),
16862306a36Sopenharmony_ci				4);
16962306a36Sopenharmony_ci		if (ea_size > bytes)
17062306a36Sopenharmony_ci			goto out1;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	*ea = ea_p;
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ciout1:
17762306a36Sopenharmony_ci	kfree(ea_p);
17862306a36Sopenharmony_ciout:
17962306a36Sopenharmony_ci	ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
18062306a36Sopenharmony_ci	return err;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/*
18462306a36Sopenharmony_ci * ntfs_list_ea
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci * Copy a list of xattrs names into the buffer
18762306a36Sopenharmony_ci * provided, or compute the buffer size required.
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci * Return:
19062306a36Sopenharmony_ci * * Number of bytes used / required on
19162306a36Sopenharmony_ci * * -ERRNO - on failure
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
19462306a36Sopenharmony_ci			    size_t bytes_per_buffer)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	const struct EA_INFO *info;
19762306a36Sopenharmony_ci	struct EA_FULL *ea_all = NULL;
19862306a36Sopenharmony_ci	const struct EA_FULL *ea;
19962306a36Sopenharmony_ci	u32 off, size;
20062306a36Sopenharmony_ci	int err;
20162306a36Sopenharmony_ci	int ea_size;
20262306a36Sopenharmony_ci	size_t ret;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	err = ntfs_read_ea(ni, &ea_all, 0, &info);
20562306a36Sopenharmony_ci	if (err)
20662306a36Sopenharmony_ci		return err;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!info || !ea_all)
20962306a36Sopenharmony_ci		return 0;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	size = le32_to_cpu(info->size);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Enumerate all xattrs. */
21462306a36Sopenharmony_ci	ret = 0;
21562306a36Sopenharmony_ci	for (off = 0; off + sizeof(struct EA_FULL) < size; off += ea_size) {
21662306a36Sopenharmony_ci		ea = Add2Ptr(ea_all, off);
21762306a36Sopenharmony_ci		ea_size = unpacked_ea_size(ea);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		if (!ea->name_len)
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		if (ea->name_len > ea_size)
22362306a36Sopenharmony_ci			break;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		if (buffer) {
22662306a36Sopenharmony_ci			/* Check if we can use field ea->name */
22762306a36Sopenharmony_ci			if (off + ea_size > size)
22862306a36Sopenharmony_ci				break;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci			if (ret + ea->name_len + 1 > bytes_per_buffer) {
23162306a36Sopenharmony_ci				err = -ERANGE;
23262306a36Sopenharmony_ci				goto out;
23362306a36Sopenharmony_ci			}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci			memcpy(buffer + ret, ea->name, ea->name_len);
23662306a36Sopenharmony_ci			buffer[ret + ea->name_len] = 0;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		ret += ea->name_len + 1;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciout:
24362306a36Sopenharmony_ci	kfree(ea_all);
24462306a36Sopenharmony_ci	return err ? err : ret;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len,
24862306a36Sopenharmony_ci		       void *buffer, size_t size, size_t *required)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct ntfs_inode *ni = ntfs_i(inode);
25162306a36Sopenharmony_ci	const struct EA_INFO *info;
25262306a36Sopenharmony_ci	struct EA_FULL *ea_all = NULL;
25362306a36Sopenharmony_ci	const struct EA_FULL *ea;
25462306a36Sopenharmony_ci	u32 off, len;
25562306a36Sopenharmony_ci	int err;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (!(ni->ni_flags & NI_FLAG_EA))
25862306a36Sopenharmony_ci		return -ENODATA;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (!required)
26162306a36Sopenharmony_ci		ni_lock(ni);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	len = 0;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (name_len > 255) {
26662306a36Sopenharmony_ci		err = -ENAMETOOLONG;
26762306a36Sopenharmony_ci		goto out;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	err = ntfs_read_ea(ni, &ea_all, 0, &info);
27162306a36Sopenharmony_ci	if (err)
27262306a36Sopenharmony_ci		goto out;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!info)
27562306a36Sopenharmony_ci		goto out;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* Enumerate all xattrs. */
27862306a36Sopenharmony_ci	if (!find_ea(ea_all, le32_to_cpu(info->size), name, name_len, &off,
27962306a36Sopenharmony_ci		     NULL)) {
28062306a36Sopenharmony_ci		err = -ENODATA;
28162306a36Sopenharmony_ci		goto out;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	ea = Add2Ptr(ea_all, off);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	len = le16_to_cpu(ea->elength);
28662306a36Sopenharmony_ci	if (!buffer) {
28762306a36Sopenharmony_ci		err = 0;
28862306a36Sopenharmony_ci		goto out;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (len > size) {
29262306a36Sopenharmony_ci		err = -ERANGE;
29362306a36Sopenharmony_ci		if (required)
29462306a36Sopenharmony_ci			*required = len;
29562306a36Sopenharmony_ci		goto out;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	memcpy(buffer, ea->name + ea->name_len + 1, len);
29962306a36Sopenharmony_ci	err = 0;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ciout:
30262306a36Sopenharmony_ci	kfree(ea_all);
30362306a36Sopenharmony_ci	if (!required)
30462306a36Sopenharmony_ci		ni_unlock(ni);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return err ? err : len;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic noinline int ntfs_set_ea(struct inode *inode, const char *name,
31062306a36Sopenharmony_ci				size_t name_len, const void *value,
31162306a36Sopenharmony_ci				size_t val_size, int flags, bool locked,
31262306a36Sopenharmony_ci				__le16 *ea_size)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct ntfs_inode *ni = ntfs_i(inode);
31562306a36Sopenharmony_ci	struct ntfs_sb_info *sbi = ni->mi.sbi;
31662306a36Sopenharmony_ci	int err;
31762306a36Sopenharmony_ci	struct EA_INFO ea_info;
31862306a36Sopenharmony_ci	const struct EA_INFO *info;
31962306a36Sopenharmony_ci	struct EA_FULL *new_ea;
32062306a36Sopenharmony_ci	struct EA_FULL *ea_all = NULL;
32162306a36Sopenharmony_ci	size_t add, new_pack;
32262306a36Sopenharmony_ci	u32 off, size, ea_sz;
32362306a36Sopenharmony_ci	__le16 size_pack;
32462306a36Sopenharmony_ci	struct ATTRIB *attr;
32562306a36Sopenharmony_ci	struct ATTR_LIST_ENTRY *le;
32662306a36Sopenharmony_ci	struct mft_inode *mi;
32762306a36Sopenharmony_ci	struct runs_tree ea_run;
32862306a36Sopenharmony_ci	u64 new_sz;
32962306a36Sopenharmony_ci	void *p;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (!locked)
33262306a36Sopenharmony_ci		ni_lock(ni);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	run_init(&ea_run);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (name_len > 255) {
33762306a36Sopenharmony_ci		err = -ENAMETOOLONG;
33862306a36Sopenharmony_ci		goto out;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	add = ALIGN(struct_size(ea_all, name, 1 + name_len + val_size), 4);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	err = ntfs_read_ea(ni, &ea_all, add, &info);
34462306a36Sopenharmony_ci	if (err)
34562306a36Sopenharmony_ci		goto out;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (!info) {
34862306a36Sopenharmony_ci		memset(&ea_info, 0, sizeof(ea_info));
34962306a36Sopenharmony_ci		size = 0;
35062306a36Sopenharmony_ci		size_pack = 0;
35162306a36Sopenharmony_ci	} else {
35262306a36Sopenharmony_ci		memcpy(&ea_info, info, sizeof(ea_info));
35362306a36Sopenharmony_ci		size = le32_to_cpu(ea_info.size);
35462306a36Sopenharmony_ci		size_pack = ea_info.size_pack;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (info && find_ea(ea_all, size, name, name_len, &off, &ea_sz)) {
35862306a36Sopenharmony_ci		struct EA_FULL *ea;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		if (flags & XATTR_CREATE) {
36162306a36Sopenharmony_ci			err = -EEXIST;
36262306a36Sopenharmony_ci			goto out;
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		ea = Add2Ptr(ea_all, off);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		/*
36862306a36Sopenharmony_ci		 * Check simple case when we try to insert xattr with the same value
36962306a36Sopenharmony_ci		 * e.g. ntfs_save_wsl_perm
37062306a36Sopenharmony_ci		 */
37162306a36Sopenharmony_ci		if (val_size && le16_to_cpu(ea->elength) == val_size &&
37262306a36Sopenharmony_ci		    !memcmp(ea->name + ea->name_len + 1, value, val_size)) {
37362306a36Sopenharmony_ci			/* xattr already contains the required value. */
37462306a36Sopenharmony_ci			goto out;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		/* Remove current xattr. */
37862306a36Sopenharmony_ci		if (ea->flags & FILE_NEED_EA)
37962306a36Sopenharmony_ci			le16_add_cpu(&ea_info.count, -1);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		le16_add_cpu(&ea_info.size_pack, 0 - packed_ea_size(ea));
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		memmove(ea, Add2Ptr(ea, ea_sz), size - off - ea_sz);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		size -= ea_sz;
38662306a36Sopenharmony_ci		memset(Add2Ptr(ea_all, size), 0, ea_sz);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		ea_info.size = cpu_to_le32(size);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		if ((flags & XATTR_REPLACE) && !val_size) {
39162306a36Sopenharmony_ci			/* Remove xattr. */
39262306a36Sopenharmony_ci			goto update_ea;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci	} else {
39562306a36Sopenharmony_ci		if (flags & XATTR_REPLACE) {
39662306a36Sopenharmony_ci			err = -ENODATA;
39762306a36Sopenharmony_ci			goto out;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		if (!ea_all) {
40162306a36Sopenharmony_ci			ea_all = kzalloc(add, GFP_NOFS);
40262306a36Sopenharmony_ci			if (!ea_all) {
40362306a36Sopenharmony_ci				err = -ENOMEM;
40462306a36Sopenharmony_ci				goto out;
40562306a36Sopenharmony_ci			}
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Append new xattr. */
41062306a36Sopenharmony_ci	new_ea = Add2Ptr(ea_all, size);
41162306a36Sopenharmony_ci	new_ea->size = cpu_to_le32(add);
41262306a36Sopenharmony_ci	new_ea->flags = 0;
41362306a36Sopenharmony_ci	new_ea->name_len = name_len;
41462306a36Sopenharmony_ci	new_ea->elength = cpu_to_le16(val_size);
41562306a36Sopenharmony_ci	memcpy(new_ea->name, name, name_len);
41662306a36Sopenharmony_ci	new_ea->name[name_len] = 0;
41762306a36Sopenharmony_ci	memcpy(new_ea->name + name_len + 1, value, val_size);
41862306a36Sopenharmony_ci	new_pack = le16_to_cpu(ea_info.size_pack) + packed_ea_size(new_ea);
41962306a36Sopenharmony_ci	ea_info.size_pack = cpu_to_le16(new_pack);
42062306a36Sopenharmony_ci	/* New size of ATTR_EA. */
42162306a36Sopenharmony_ci	size += add;
42262306a36Sopenharmony_ci	ea_info.size = cpu_to_le32(size);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/*
42562306a36Sopenharmony_ci	 * 1. Check ea_info.size_pack for overflow.
42662306a36Sopenharmony_ci	 * 2. New attribute size must fit value from $AttrDef
42762306a36Sopenharmony_ci	 */
42862306a36Sopenharmony_ci	if (new_pack > 0xffff || size > sbi->ea_max_size) {
42962306a36Sopenharmony_ci		ntfs_inode_warn(
43062306a36Sopenharmony_ci			inode,
43162306a36Sopenharmony_ci			"The size of extended attributes must not exceed 64KiB");
43262306a36Sopenharmony_ci		err = -EFBIG; // -EINVAL?
43362306a36Sopenharmony_ci		goto out;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ciupdate_ea:
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (!info) {
43962306a36Sopenharmony_ci		/* Create xattr. */
44062306a36Sopenharmony_ci		if (!size) {
44162306a36Sopenharmony_ci			err = 0;
44262306a36Sopenharmony_ci			goto out;
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		err = ni_insert_resident(ni, sizeof(struct EA_INFO),
44662306a36Sopenharmony_ci					 ATTR_EA_INFO, NULL, 0, NULL, NULL,
44762306a36Sopenharmony_ci					 NULL);
44862306a36Sopenharmony_ci		if (err)
44962306a36Sopenharmony_ci			goto out;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL,
45262306a36Sopenharmony_ci					 NULL);
45362306a36Sopenharmony_ci		if (err)
45462306a36Sopenharmony_ci			goto out;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	new_sz = size;
45862306a36Sopenharmony_ci	err = attr_set_size(ni, ATTR_EA, NULL, 0, &ea_run, new_sz, &new_sz,
45962306a36Sopenharmony_ci			    false, NULL);
46062306a36Sopenharmony_ci	if (err)
46162306a36Sopenharmony_ci		goto out;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	le = NULL;
46462306a36Sopenharmony_ci	attr = ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, &mi);
46562306a36Sopenharmony_ci	if (!attr) {
46662306a36Sopenharmony_ci		err = -EINVAL;
46762306a36Sopenharmony_ci		goto out;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (!size) {
47162306a36Sopenharmony_ci		/* Delete xattr, ATTR_EA_INFO */
47262306a36Sopenharmony_ci		ni_remove_attr_le(ni, attr, mi, le);
47362306a36Sopenharmony_ci	} else {
47462306a36Sopenharmony_ci		p = resident_data_ex(attr, sizeof(struct EA_INFO));
47562306a36Sopenharmony_ci		if (!p) {
47662306a36Sopenharmony_ci			err = -EINVAL;
47762306a36Sopenharmony_ci			goto out;
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci		memcpy(p, &ea_info, sizeof(struct EA_INFO));
48062306a36Sopenharmony_ci		mi->dirty = true;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	le = NULL;
48462306a36Sopenharmony_ci	attr = ni_find_attr(ni, NULL, &le, ATTR_EA, NULL, 0, NULL, &mi);
48562306a36Sopenharmony_ci	if (!attr) {
48662306a36Sopenharmony_ci		err = -EINVAL;
48762306a36Sopenharmony_ci		goto out;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (!size) {
49162306a36Sopenharmony_ci		/* Delete xattr, ATTR_EA */
49262306a36Sopenharmony_ci		ni_remove_attr_le(ni, attr, mi, le);
49362306a36Sopenharmony_ci	} else if (attr->non_res) {
49462306a36Sopenharmony_ci		err = attr_load_runs_range(ni, ATTR_EA, NULL, 0, &ea_run, 0,
49562306a36Sopenharmony_ci					   size);
49662306a36Sopenharmony_ci		if (err)
49762306a36Sopenharmony_ci			goto out;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size, 0);
50062306a36Sopenharmony_ci		if (err)
50162306a36Sopenharmony_ci			goto out;
50262306a36Sopenharmony_ci	} else {
50362306a36Sopenharmony_ci		p = resident_data_ex(attr, size);
50462306a36Sopenharmony_ci		if (!p) {
50562306a36Sopenharmony_ci			err = -EINVAL;
50662306a36Sopenharmony_ci			goto out;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		memcpy(p, ea_all, size);
50962306a36Sopenharmony_ci		mi->dirty = true;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* Check if we delete the last xattr. */
51362306a36Sopenharmony_ci	if (size)
51462306a36Sopenharmony_ci		ni->ni_flags |= NI_FLAG_EA;
51562306a36Sopenharmony_ci	else
51662306a36Sopenharmony_ci		ni->ni_flags &= ~NI_FLAG_EA;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (ea_info.size_pack != size_pack)
51962306a36Sopenharmony_ci		ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
52062306a36Sopenharmony_ci	if (ea_size)
52162306a36Sopenharmony_ci		*ea_size = ea_info.size_pack;
52262306a36Sopenharmony_ci	mark_inode_dirty(&ni->vfs_inode);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ciout:
52562306a36Sopenharmony_ci	if (!locked)
52662306a36Sopenharmony_ci		ni_unlock(ni);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	run_close(&ea_run);
52962306a36Sopenharmony_ci	kfree(ea_all);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return err;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci#ifdef CONFIG_NTFS3_FS_POSIX_ACL
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/*
53762306a36Sopenharmony_ci * ntfs_get_acl - inode_operations::get_acl
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_cistruct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry,
54062306a36Sopenharmony_ci			       int type)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
54362306a36Sopenharmony_ci	struct ntfs_inode *ni = ntfs_i(inode);
54462306a36Sopenharmony_ci	const char *name;
54562306a36Sopenharmony_ci	size_t name_len;
54662306a36Sopenharmony_ci	struct posix_acl *acl;
54762306a36Sopenharmony_ci	size_t req;
54862306a36Sopenharmony_ci	int err;
54962306a36Sopenharmony_ci	void *buf;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Allocate PATH_MAX bytes. */
55262306a36Sopenharmony_ci	buf = __getname();
55362306a36Sopenharmony_ci	if (!buf)
55462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Possible values of 'type' was already checked above. */
55762306a36Sopenharmony_ci	if (type == ACL_TYPE_ACCESS) {
55862306a36Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_ACCESS;
55962306a36Sopenharmony_ci		name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1;
56062306a36Sopenharmony_ci	} else {
56162306a36Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_DEFAULT;
56262306a36Sopenharmony_ci		name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	ni_lock(ni);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	err = ntfs_get_ea(inode, name, name_len, buf, PATH_MAX, &req);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	ni_unlock(ni);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* Translate extended attribute to acl. */
57262306a36Sopenharmony_ci	if (err >= 0) {
57362306a36Sopenharmony_ci		acl = posix_acl_from_xattr(&init_user_ns, buf, err);
57462306a36Sopenharmony_ci	} else if (err == -ENODATA) {
57562306a36Sopenharmony_ci		acl = NULL;
57662306a36Sopenharmony_ci	} else {
57762306a36Sopenharmony_ci		acl = ERR_PTR(err);
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (!IS_ERR(acl))
58162306a36Sopenharmony_ci		set_cached_acl(inode, type, acl);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	__putname(buf);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return acl;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap,
58962306a36Sopenharmony_ci				    struct inode *inode, struct posix_acl *acl,
59062306a36Sopenharmony_ci				    int type, bool init_acl)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	const char *name;
59362306a36Sopenharmony_ci	size_t size, name_len;
59462306a36Sopenharmony_ci	void *value;
59562306a36Sopenharmony_ci	int err;
59662306a36Sopenharmony_ci	int flags;
59762306a36Sopenharmony_ci	umode_t mode;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (S_ISLNK(inode->i_mode))
60062306a36Sopenharmony_ci		return -EOPNOTSUPP;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	mode = inode->i_mode;
60362306a36Sopenharmony_ci	switch (type) {
60462306a36Sopenharmony_ci	case ACL_TYPE_ACCESS:
60562306a36Sopenharmony_ci		/* Do not change i_mode if we are in init_acl */
60662306a36Sopenharmony_ci		if (acl && !init_acl) {
60762306a36Sopenharmony_ci			err = posix_acl_update_mode(idmap, inode, &mode, &acl);
60862306a36Sopenharmony_ci			if (err)
60962306a36Sopenharmony_ci				return err;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_ACCESS;
61262306a36Sopenharmony_ci		name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1;
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	case ACL_TYPE_DEFAULT:
61662306a36Sopenharmony_ci		if (!S_ISDIR(inode->i_mode))
61762306a36Sopenharmony_ci			return acl ? -EACCES : 0;
61862306a36Sopenharmony_ci		name = XATTR_NAME_POSIX_ACL_DEFAULT;
61962306a36Sopenharmony_ci		name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1;
62062306a36Sopenharmony_ci		break;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	default:
62362306a36Sopenharmony_ci		return -EINVAL;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (!acl) {
62762306a36Sopenharmony_ci		/* Remove xattr if it can be presented via mode. */
62862306a36Sopenharmony_ci		size = 0;
62962306a36Sopenharmony_ci		value = NULL;
63062306a36Sopenharmony_ci		flags = XATTR_REPLACE;
63162306a36Sopenharmony_ci	} else {
63262306a36Sopenharmony_ci		size = posix_acl_xattr_size(acl->a_count);
63362306a36Sopenharmony_ci		value = kmalloc(size, GFP_NOFS);
63462306a36Sopenharmony_ci		if (!value)
63562306a36Sopenharmony_ci			return -ENOMEM;
63662306a36Sopenharmony_ci		err = posix_acl_to_xattr(&init_user_ns, acl, value, size);
63762306a36Sopenharmony_ci		if (err < 0)
63862306a36Sopenharmony_ci			goto out;
63962306a36Sopenharmony_ci		flags = 0;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0, NULL);
64362306a36Sopenharmony_ci	if (err == -ENODATA && !size)
64462306a36Sopenharmony_ci		err = 0; /* Removing non existed xattr. */
64562306a36Sopenharmony_ci	if (!err) {
64662306a36Sopenharmony_ci		set_cached_acl(inode, type, acl);
64762306a36Sopenharmony_ci		inode->i_mode = mode;
64862306a36Sopenharmony_ci		inode_set_ctime_current(inode);
64962306a36Sopenharmony_ci		mark_inode_dirty(inode);
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ciout:
65362306a36Sopenharmony_ci	kfree(value);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return err;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/*
65962306a36Sopenharmony_ci * ntfs_set_acl - inode_operations::set_acl
66062306a36Sopenharmony_ci */
66162306a36Sopenharmony_ciint ntfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
66262306a36Sopenharmony_ci		 struct posix_acl *acl, int type)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	return ntfs_set_acl_ex(idmap, d_inode(dentry), acl, type, false);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/*
66862306a36Sopenharmony_ci * ntfs_init_acl - Initialize the ACLs of a new inode.
66962306a36Sopenharmony_ci *
67062306a36Sopenharmony_ci * Called from ntfs_create_inode().
67162306a36Sopenharmony_ci */
67262306a36Sopenharmony_ciint ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode,
67362306a36Sopenharmony_ci		  struct inode *dir)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct posix_acl *default_acl, *acl;
67662306a36Sopenharmony_ci	int err;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
67962306a36Sopenharmony_ci	if (err)
68062306a36Sopenharmony_ci		return err;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (default_acl) {
68362306a36Sopenharmony_ci		err = ntfs_set_acl_ex(idmap, inode, default_acl,
68462306a36Sopenharmony_ci				      ACL_TYPE_DEFAULT, true);
68562306a36Sopenharmony_ci		posix_acl_release(default_acl);
68662306a36Sopenharmony_ci	} else {
68762306a36Sopenharmony_ci		inode->i_default_acl = NULL;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (acl) {
69162306a36Sopenharmony_ci		if (!err)
69262306a36Sopenharmony_ci			err = ntfs_set_acl_ex(idmap, inode, acl,
69362306a36Sopenharmony_ci					      ACL_TYPE_ACCESS, true);
69462306a36Sopenharmony_ci		posix_acl_release(acl);
69562306a36Sopenharmony_ci	} else {
69662306a36Sopenharmony_ci		inode->i_acl = NULL;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return err;
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci#endif
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci/*
70462306a36Sopenharmony_ci * ntfs_acl_chmod - Helper for ntfs3_setattr().
70562306a36Sopenharmony_ci */
70662306a36Sopenharmony_ciint ntfs_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
70962306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (!(sb->s_flags & SB_POSIXACL))
71262306a36Sopenharmony_ci		return 0;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (S_ISLNK(inode->i_mode))
71562306a36Sopenharmony_ci		return -EOPNOTSUPP;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return posix_acl_chmod(idmap, dentry, inode->i_mode);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/*
72162306a36Sopenharmony_ci * ntfs_listxattr - inode_operations::listxattr
72262306a36Sopenharmony_ci */
72362306a36Sopenharmony_cissize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
72662306a36Sopenharmony_ci	struct ntfs_inode *ni = ntfs_i(inode);
72762306a36Sopenharmony_ci	ssize_t ret;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (!(ni->ni_flags & NI_FLAG_EA)) {
73062306a36Sopenharmony_ci		/* no xattr in file */
73162306a36Sopenharmony_ci		return 0;
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	ni_lock(ni);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	ret = ntfs_list_ea(ni, buffer, size);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	ni_unlock(ni);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	return ret;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de,
74462306a36Sopenharmony_ci			 struct inode *inode, const char *name, void *buffer,
74562306a36Sopenharmony_ci			 size_t size)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	int err;
74862306a36Sopenharmony_ci	struct ntfs_inode *ni = ntfs_i(inode);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
75162306a36Sopenharmony_ci		return -EIO;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/* Dispatch request. */
75462306a36Sopenharmony_ci	if (!strcmp(name, SYSTEM_DOS_ATTRIB)) {
75562306a36Sopenharmony_ci		/* system.dos_attrib */
75662306a36Sopenharmony_ci		if (!buffer) {
75762306a36Sopenharmony_ci			err = sizeof(u8);
75862306a36Sopenharmony_ci		} else if (size < sizeof(u8)) {
75962306a36Sopenharmony_ci			err = -ENODATA;
76062306a36Sopenharmony_ci		} else {
76162306a36Sopenharmony_ci			err = sizeof(u8);
76262306a36Sopenharmony_ci			*(u8 *)buffer = le32_to_cpu(ni->std_fa);
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci		goto out;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (!strcmp(name, SYSTEM_NTFS_ATTRIB) ||
76862306a36Sopenharmony_ci	    !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) {
76962306a36Sopenharmony_ci		/* system.ntfs_attrib */
77062306a36Sopenharmony_ci		if (!buffer) {
77162306a36Sopenharmony_ci			err = sizeof(u32);
77262306a36Sopenharmony_ci		} else if (size < sizeof(u32)) {
77362306a36Sopenharmony_ci			err = -ENODATA;
77462306a36Sopenharmony_ci		} else {
77562306a36Sopenharmony_ci			err = sizeof(u32);
77662306a36Sopenharmony_ci			*(u32 *)buffer = le32_to_cpu(ni->std_fa);
77762306a36Sopenharmony_ci			if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE))
77862306a36Sopenharmony_ci				*(__be32 *)buffer = cpu_to_be32(*(u32 *)buffer);
77962306a36Sopenharmony_ci		}
78062306a36Sopenharmony_ci		goto out;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (!strcmp(name, SYSTEM_NTFS_SECURITY)) {
78462306a36Sopenharmony_ci		/* system.ntfs_security*/
78562306a36Sopenharmony_ci		struct SECURITY_DESCRIPTOR_RELATIVE *sd = NULL;
78662306a36Sopenharmony_ci		size_t sd_size = 0;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		if (!is_ntfs3(ni->mi.sbi)) {
78962306a36Sopenharmony_ci			/* We should get nt4 security. */
79062306a36Sopenharmony_ci			err = -EINVAL;
79162306a36Sopenharmony_ci			goto out;
79262306a36Sopenharmony_ci		} else if (le32_to_cpu(ni->std_security_id) <
79362306a36Sopenharmony_ci			   SECURITY_ID_FIRST) {
79462306a36Sopenharmony_ci			err = -ENOENT;
79562306a36Sopenharmony_ci			goto out;
79662306a36Sopenharmony_ci		}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		err = ntfs_get_security_by_id(ni->mi.sbi, ni->std_security_id,
79962306a36Sopenharmony_ci					      &sd, &sd_size);
80062306a36Sopenharmony_ci		if (err)
80162306a36Sopenharmony_ci			goto out;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		if (!is_sd_valid(sd, sd_size)) {
80462306a36Sopenharmony_ci			ntfs_inode_warn(
80562306a36Sopenharmony_ci				inode,
80662306a36Sopenharmony_ci				"looks like you get incorrect security descriptor id=%u",
80762306a36Sopenharmony_ci				ni->std_security_id);
80862306a36Sopenharmony_ci		}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		if (!buffer) {
81162306a36Sopenharmony_ci			err = sd_size;
81262306a36Sopenharmony_ci		} else if (size < sd_size) {
81362306a36Sopenharmony_ci			err = -ENODATA;
81462306a36Sopenharmony_ci		} else {
81562306a36Sopenharmony_ci			err = sd_size;
81662306a36Sopenharmony_ci			memcpy(buffer, sd, sd_size);
81762306a36Sopenharmony_ci		}
81862306a36Sopenharmony_ci		kfree(sd);
81962306a36Sopenharmony_ci		goto out;
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	/* Deal with NTFS extended attribute. */
82362306a36Sopenharmony_ci	err = ntfs_get_ea(inode, name, strlen(name), buffer, size, NULL);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ciout:
82662306a36Sopenharmony_ci	return err;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/*
83062306a36Sopenharmony_ci * ntfs_setxattr - inode_operations::setxattr
83162306a36Sopenharmony_ci */
83262306a36Sopenharmony_cistatic noinline int ntfs_setxattr(const struct xattr_handler *handler,
83362306a36Sopenharmony_ci				  struct mnt_idmap *idmap, struct dentry *de,
83462306a36Sopenharmony_ci				  struct inode *inode, const char *name,
83562306a36Sopenharmony_ci				  const void *value, size_t size, int flags)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	int err = -EINVAL;
83862306a36Sopenharmony_ci	struct ntfs_inode *ni = ntfs_i(inode);
83962306a36Sopenharmony_ci	enum FILE_ATTRIBUTE new_fa;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* Dispatch request. */
84262306a36Sopenharmony_ci	if (!strcmp(name, SYSTEM_DOS_ATTRIB)) {
84362306a36Sopenharmony_ci		if (sizeof(u8) != size)
84462306a36Sopenharmony_ci			goto out;
84562306a36Sopenharmony_ci		new_fa = cpu_to_le32(*(u8 *)value);
84662306a36Sopenharmony_ci		goto set_new_fa;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	if (!strcmp(name, SYSTEM_NTFS_ATTRIB) ||
85062306a36Sopenharmony_ci	    !strcmp(name, SYSTEM_NTFS_ATTRIB_BE)) {
85162306a36Sopenharmony_ci		if (size != sizeof(u32))
85262306a36Sopenharmony_ci			goto out;
85362306a36Sopenharmony_ci		if (!strcmp(name, SYSTEM_NTFS_ATTRIB_BE))
85462306a36Sopenharmony_ci			new_fa = cpu_to_le32(be32_to_cpu(*(__be32 *)value));
85562306a36Sopenharmony_ci		else
85662306a36Sopenharmony_ci			new_fa = cpu_to_le32(*(u32 *)value);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		if (S_ISREG(inode->i_mode)) {
85962306a36Sopenharmony_ci			/* Process compressed/sparsed in special way. */
86062306a36Sopenharmony_ci			ni_lock(ni);
86162306a36Sopenharmony_ci			err = ni_new_attr_flags(ni, new_fa);
86262306a36Sopenharmony_ci			ni_unlock(ni);
86362306a36Sopenharmony_ci			if (err)
86462306a36Sopenharmony_ci				goto out;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ciset_new_fa:
86762306a36Sopenharmony_ci		/*
86862306a36Sopenharmony_ci		 * Thanks Mark Harmstone:
86962306a36Sopenharmony_ci		 * Keep directory bit consistency.
87062306a36Sopenharmony_ci		 */
87162306a36Sopenharmony_ci		if (S_ISDIR(inode->i_mode))
87262306a36Sopenharmony_ci			new_fa |= FILE_ATTRIBUTE_DIRECTORY;
87362306a36Sopenharmony_ci		else
87462306a36Sopenharmony_ci			new_fa &= ~FILE_ATTRIBUTE_DIRECTORY;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		if (ni->std_fa != new_fa) {
87762306a36Sopenharmony_ci			ni->std_fa = new_fa;
87862306a36Sopenharmony_ci			if (new_fa & FILE_ATTRIBUTE_READONLY)
87962306a36Sopenharmony_ci				inode->i_mode &= ~0222;
88062306a36Sopenharmony_ci			else
88162306a36Sopenharmony_ci				inode->i_mode |= 0222;
88262306a36Sopenharmony_ci			/* Std attribute always in primary record. */
88362306a36Sopenharmony_ci			ni->mi.dirty = true;
88462306a36Sopenharmony_ci			mark_inode_dirty(inode);
88562306a36Sopenharmony_ci		}
88662306a36Sopenharmony_ci		err = 0;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci		goto out;
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	if (!strcmp(name, SYSTEM_NTFS_SECURITY)) {
89262306a36Sopenharmony_ci		/* system.ntfs_security*/
89362306a36Sopenharmony_ci		__le32 security_id;
89462306a36Sopenharmony_ci		bool inserted;
89562306a36Sopenharmony_ci		struct ATTR_STD_INFO5 *std;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		if (!is_ntfs3(ni->mi.sbi)) {
89862306a36Sopenharmony_ci			/*
89962306a36Sopenharmony_ci			 * We should replace ATTR_SECURE.
90062306a36Sopenharmony_ci			 * Skip this way cause it is nt4 feature.
90162306a36Sopenharmony_ci			 */
90262306a36Sopenharmony_ci			err = -EINVAL;
90362306a36Sopenharmony_ci			goto out;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		if (!is_sd_valid(value, size)) {
90762306a36Sopenharmony_ci			err = -EINVAL;
90862306a36Sopenharmony_ci			ntfs_inode_warn(
90962306a36Sopenharmony_ci				inode,
91062306a36Sopenharmony_ci				"you try to set invalid security descriptor");
91162306a36Sopenharmony_ci			goto out;
91262306a36Sopenharmony_ci		}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci		err = ntfs_insert_security(ni->mi.sbi, value, size,
91562306a36Sopenharmony_ci					   &security_id, &inserted);
91662306a36Sopenharmony_ci		if (err)
91762306a36Sopenharmony_ci			goto out;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		ni_lock(ni);
92062306a36Sopenharmony_ci		std = ni_std5(ni);
92162306a36Sopenharmony_ci		if (!std) {
92262306a36Sopenharmony_ci			err = -EINVAL;
92362306a36Sopenharmony_ci		} else if (std->security_id != security_id) {
92462306a36Sopenharmony_ci			std->security_id = ni->std_security_id = security_id;
92562306a36Sopenharmony_ci			/* Std attribute always in primary record. */
92662306a36Sopenharmony_ci			ni->mi.dirty = true;
92762306a36Sopenharmony_ci			mark_inode_dirty(&ni->vfs_inode);
92862306a36Sopenharmony_ci		}
92962306a36Sopenharmony_ci		ni_unlock(ni);
93062306a36Sopenharmony_ci		goto out;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* Deal with NTFS extended attribute. */
93462306a36Sopenharmony_ci	err = ntfs_set_ea(inode, name, strlen(name), value, size, flags, 0,
93562306a36Sopenharmony_ci			  NULL);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ciout:
93862306a36Sopenharmony_ci	inode_set_ctime_current(inode);
93962306a36Sopenharmony_ci	mark_inode_dirty(inode);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	return err;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/*
94562306a36Sopenharmony_ci * ntfs_save_wsl_perm
94662306a36Sopenharmony_ci *
94762306a36Sopenharmony_ci * save uid/gid/mode in xattr
94862306a36Sopenharmony_ci */
94962306a36Sopenharmony_ciint ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	int err;
95262306a36Sopenharmony_ci	__le32 value;
95362306a36Sopenharmony_ci	struct ntfs_inode *ni = ntfs_i(inode);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	ni_lock(ni);
95662306a36Sopenharmony_ci	value = cpu_to_le32(i_uid_read(inode));
95762306a36Sopenharmony_ci	err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value,
95862306a36Sopenharmony_ci			  sizeof(value), 0, true, ea_size);
95962306a36Sopenharmony_ci	if (err)
96062306a36Sopenharmony_ci		goto out;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	value = cpu_to_le32(i_gid_read(inode));
96362306a36Sopenharmony_ci	err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value,
96462306a36Sopenharmony_ci			  sizeof(value), 0, true, ea_size);
96562306a36Sopenharmony_ci	if (err)
96662306a36Sopenharmony_ci		goto out;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	value = cpu_to_le32(inode->i_mode);
96962306a36Sopenharmony_ci	err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value,
97062306a36Sopenharmony_ci			  sizeof(value), 0, true, ea_size);
97162306a36Sopenharmony_ci	if (err)
97262306a36Sopenharmony_ci		goto out;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
97562306a36Sopenharmony_ci		value = cpu_to_le32(inode->i_rdev);
97662306a36Sopenharmony_ci		err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value,
97762306a36Sopenharmony_ci				  sizeof(value), 0, true, ea_size);
97862306a36Sopenharmony_ci		if (err)
97962306a36Sopenharmony_ci			goto out;
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ciout:
98362306a36Sopenharmony_ci	ni_unlock(ni);
98462306a36Sopenharmony_ci	/* In case of error should we delete all WSL xattr? */
98562306a36Sopenharmony_ci	return err;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci/*
98962306a36Sopenharmony_ci * ntfs_get_wsl_perm
99062306a36Sopenharmony_ci *
99162306a36Sopenharmony_ci * get uid/gid/mode from xattr
99262306a36Sopenharmony_ci * it is called from ntfs_iget5->ntfs_read_mft
99362306a36Sopenharmony_ci */
99462306a36Sopenharmony_civoid ntfs_get_wsl_perm(struct inode *inode)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	size_t sz;
99762306a36Sopenharmony_ci	__le32 value[3];
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value[0],
100062306a36Sopenharmony_ci			sizeof(value[0]), &sz) == sizeof(value[0]) &&
100162306a36Sopenharmony_ci	    ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value[1],
100262306a36Sopenharmony_ci			sizeof(value[1]), &sz) == sizeof(value[1]) &&
100362306a36Sopenharmony_ci	    ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value[2],
100462306a36Sopenharmony_ci			sizeof(value[2]), &sz) == sizeof(value[2])) {
100562306a36Sopenharmony_ci		i_uid_write(inode, (uid_t)le32_to_cpu(value[0]));
100662306a36Sopenharmony_ci		i_gid_write(inode, (gid_t)le32_to_cpu(value[1]));
100762306a36Sopenharmony_ci		inode->i_mode = le32_to_cpu(value[2]);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1,
101062306a36Sopenharmony_ci				&value[0], sizeof(value),
101162306a36Sopenharmony_ci				&sz) == sizeof(value[0])) {
101262306a36Sopenharmony_ci			inode->i_rdev = le32_to_cpu(value[0]);
101362306a36Sopenharmony_ci		}
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic bool ntfs_xattr_user_list(struct dentry *dentry)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	return true;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci// clang-format off
102362306a36Sopenharmony_cistatic const struct xattr_handler ntfs_other_xattr_handler = {
102462306a36Sopenharmony_ci	.prefix	= "",
102562306a36Sopenharmony_ci	.get	= ntfs_getxattr,
102662306a36Sopenharmony_ci	.set	= ntfs_setxattr,
102762306a36Sopenharmony_ci	.list	= ntfs_xattr_user_list,
102862306a36Sopenharmony_ci};
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ciconst struct xattr_handler *ntfs_xattr_handlers[] = {
103162306a36Sopenharmony_ci	&ntfs_other_xattr_handler,
103262306a36Sopenharmony_ci	NULL,
103362306a36Sopenharmony_ci};
103462306a36Sopenharmony_ci// clang-format on
1035