162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/f2fs/xattr.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2012 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci *             http://www.samsung.com/
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Portions of this code from linux/fs/ext2/xattr.c
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Copyright (C) 2001-2003 Andreas Gruenbacher <agruen@suse.de>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Fix by Harrison Xing <harrison@mountainviewdata.com>.
1362306a36Sopenharmony_ci * Extended attributes for symlinks and special files added per
1462306a36Sopenharmony_ci *  suggestion of Luka Renko <luka.renko@hermes.si>.
1562306a36Sopenharmony_ci * xattr consolidation Copyright (c) 2004 James Morris <jmorris@redhat.com>,
1662306a36Sopenharmony_ci *  Red Hat Inc.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci#include <linux/rwsem.h>
1962306a36Sopenharmony_ci#include <linux/f2fs_fs.h>
2062306a36Sopenharmony_ci#include <linux/security.h>
2162306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h>
2262306a36Sopenharmony_ci#include "f2fs.h"
2362306a36Sopenharmony_ci#include "xattr.h"
2462306a36Sopenharmony_ci#include "segment.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic void *xattr_alloc(struct f2fs_sb_info *sbi, int size, bool *is_inline)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	if (likely(size == sbi->inline_xattr_slab_size)) {
2962306a36Sopenharmony_ci		*is_inline = true;
3062306a36Sopenharmony_ci		return f2fs_kmem_cache_alloc(sbi->inline_xattr_slab,
3162306a36Sopenharmony_ci					GFP_F2FS_ZERO, false, sbi);
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci	*is_inline = false;
3462306a36Sopenharmony_ci	return f2fs_kzalloc(sbi, size, GFP_NOFS);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void xattr_free(struct f2fs_sb_info *sbi, void *xattr_addr,
3862306a36Sopenharmony_ci							bool is_inline)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	if (is_inline)
4162306a36Sopenharmony_ci		kmem_cache_free(sbi->inline_xattr_slab, xattr_addr);
4262306a36Sopenharmony_ci	else
4362306a36Sopenharmony_ci		kfree(xattr_addr);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int f2fs_xattr_generic_get(const struct xattr_handler *handler,
4762306a36Sopenharmony_ci		struct dentry *unused, struct inode *inode,
4862306a36Sopenharmony_ci		const char *name, void *buffer, size_t size)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	switch (handler->flags) {
5362306a36Sopenharmony_ci	case F2FS_XATTR_INDEX_USER:
5462306a36Sopenharmony_ci		if (!test_opt(sbi, XATTR_USER))
5562306a36Sopenharmony_ci			return -EOPNOTSUPP;
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	case F2FS_XATTR_INDEX_TRUSTED:
5862306a36Sopenharmony_ci	case F2FS_XATTR_INDEX_SECURITY:
5962306a36Sopenharmony_ci		break;
6062306a36Sopenharmony_ci	default:
6162306a36Sopenharmony_ci		return -EINVAL;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	return f2fs_getxattr(inode, handler->flags, name,
6462306a36Sopenharmony_ci			     buffer, size, NULL);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int f2fs_xattr_generic_set(const struct xattr_handler *handler,
6862306a36Sopenharmony_ci		struct mnt_idmap *idmap,
6962306a36Sopenharmony_ci		struct dentry *unused, struct inode *inode,
7062306a36Sopenharmony_ci		const char *name, const void *value,
7162306a36Sopenharmony_ci		size_t size, int flags)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	switch (handler->flags) {
7662306a36Sopenharmony_ci	case F2FS_XATTR_INDEX_USER:
7762306a36Sopenharmony_ci		if (!test_opt(sbi, XATTR_USER))
7862306a36Sopenharmony_ci			return -EOPNOTSUPP;
7962306a36Sopenharmony_ci		break;
8062306a36Sopenharmony_ci	case F2FS_XATTR_INDEX_TRUSTED:
8162306a36Sopenharmony_ci	case F2FS_XATTR_INDEX_SECURITY:
8262306a36Sopenharmony_ci		break;
8362306a36Sopenharmony_ci	default:
8462306a36Sopenharmony_ci		return -EINVAL;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci	return f2fs_setxattr(inode, handler->flags, name,
8762306a36Sopenharmony_ci					value, size, NULL, flags);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic bool f2fs_xattr_user_list(struct dentry *dentry)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	return test_opt(sbi, XATTR_USER);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic bool f2fs_xattr_trusted_list(struct dentry *dentry)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	return capable(CAP_SYS_ADMIN);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int f2fs_xattr_advise_get(const struct xattr_handler *handler,
10362306a36Sopenharmony_ci		struct dentry *unused, struct inode *inode,
10462306a36Sopenharmony_ci		const char *name, void *buffer, size_t size)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	if (buffer)
10762306a36Sopenharmony_ci		*((char *)buffer) = F2FS_I(inode)->i_advise;
10862306a36Sopenharmony_ci	return sizeof(char);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int f2fs_xattr_advise_set(const struct xattr_handler *handler,
11262306a36Sopenharmony_ci		struct mnt_idmap *idmap,
11362306a36Sopenharmony_ci		struct dentry *unused, struct inode *inode,
11462306a36Sopenharmony_ci		const char *name, const void *value,
11562306a36Sopenharmony_ci		size_t size, int flags)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	unsigned char old_advise = F2FS_I(inode)->i_advise;
11862306a36Sopenharmony_ci	unsigned char new_advise;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
12162306a36Sopenharmony_ci		return -EPERM;
12262306a36Sopenharmony_ci	if (value == NULL)
12362306a36Sopenharmony_ci		return -EINVAL;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	new_advise = *(char *)value;
12662306a36Sopenharmony_ci	if (new_advise & ~FADVISE_MODIFIABLE_BITS)
12762306a36Sopenharmony_ci		return -EINVAL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	new_advise = new_advise & FADVISE_MODIFIABLE_BITS;
13062306a36Sopenharmony_ci	new_advise |= old_advise & ~FADVISE_MODIFIABLE_BITS;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	F2FS_I(inode)->i_advise = new_advise;
13362306a36Sopenharmony_ci	f2fs_mark_inode_dirty_sync(inode, true);
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci#ifdef CONFIG_F2FS_FS_SECURITY
13862306a36Sopenharmony_cistatic int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
13962306a36Sopenharmony_ci		void *page)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	const struct xattr *xattr;
14262306a36Sopenharmony_ci	int err = 0;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
14562306a36Sopenharmony_ci		err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_SECURITY,
14662306a36Sopenharmony_ci				xattr->name, xattr->value,
14762306a36Sopenharmony_ci				xattr->value_len, (struct page *)page, 0);
14862306a36Sopenharmony_ci		if (err < 0)
14962306a36Sopenharmony_ci			break;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	return err;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciint f2fs_init_security(struct inode *inode, struct inode *dir,
15562306a36Sopenharmony_ci				const struct qstr *qstr, struct page *ipage)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	return security_inode_init_security(inode, dir, qstr,
15862306a36Sopenharmony_ci				&f2fs_initxattrs, ipage);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci#endif
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ciconst struct xattr_handler f2fs_xattr_user_handler = {
16362306a36Sopenharmony_ci	.prefix	= XATTR_USER_PREFIX,
16462306a36Sopenharmony_ci	.flags	= F2FS_XATTR_INDEX_USER,
16562306a36Sopenharmony_ci	.list	= f2fs_xattr_user_list,
16662306a36Sopenharmony_ci	.get	= f2fs_xattr_generic_get,
16762306a36Sopenharmony_ci	.set	= f2fs_xattr_generic_set,
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciconst struct xattr_handler f2fs_xattr_trusted_handler = {
17162306a36Sopenharmony_ci	.prefix	= XATTR_TRUSTED_PREFIX,
17262306a36Sopenharmony_ci	.flags	= F2FS_XATTR_INDEX_TRUSTED,
17362306a36Sopenharmony_ci	.list	= f2fs_xattr_trusted_list,
17462306a36Sopenharmony_ci	.get	= f2fs_xattr_generic_get,
17562306a36Sopenharmony_ci	.set	= f2fs_xattr_generic_set,
17662306a36Sopenharmony_ci};
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciconst struct xattr_handler f2fs_xattr_advise_handler = {
17962306a36Sopenharmony_ci	.name	= F2FS_SYSTEM_ADVISE_NAME,
18062306a36Sopenharmony_ci	.flags	= F2FS_XATTR_INDEX_ADVISE,
18162306a36Sopenharmony_ci	.get	= f2fs_xattr_advise_get,
18262306a36Sopenharmony_ci	.set	= f2fs_xattr_advise_set,
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ciconst struct xattr_handler f2fs_xattr_security_handler = {
18662306a36Sopenharmony_ci	.prefix	= XATTR_SECURITY_PREFIX,
18762306a36Sopenharmony_ci	.flags	= F2FS_XATTR_INDEX_SECURITY,
18862306a36Sopenharmony_ci	.get	= f2fs_xattr_generic_get,
18962306a36Sopenharmony_ci	.set	= f2fs_xattr_generic_set,
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const struct xattr_handler *f2fs_xattr_handler_map[] = {
19362306a36Sopenharmony_ci	[F2FS_XATTR_INDEX_USER] = &f2fs_xattr_user_handler,
19462306a36Sopenharmony_ci#ifdef CONFIG_F2FS_FS_POSIX_ACL
19562306a36Sopenharmony_ci	[F2FS_XATTR_INDEX_POSIX_ACL_ACCESS] = &nop_posix_acl_access,
19662306a36Sopenharmony_ci	[F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &nop_posix_acl_default,
19762306a36Sopenharmony_ci#endif
19862306a36Sopenharmony_ci	[F2FS_XATTR_INDEX_TRUSTED] = &f2fs_xattr_trusted_handler,
19962306a36Sopenharmony_ci#ifdef CONFIG_F2FS_FS_SECURITY
20062306a36Sopenharmony_ci	[F2FS_XATTR_INDEX_SECURITY] = &f2fs_xattr_security_handler,
20162306a36Sopenharmony_ci#endif
20262306a36Sopenharmony_ci	[F2FS_XATTR_INDEX_ADVISE] = &f2fs_xattr_advise_handler,
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ciconst struct xattr_handler *f2fs_xattr_handlers[] = {
20662306a36Sopenharmony_ci	&f2fs_xattr_user_handler,
20762306a36Sopenharmony_ci	&f2fs_xattr_trusted_handler,
20862306a36Sopenharmony_ci#ifdef CONFIG_F2FS_FS_SECURITY
20962306a36Sopenharmony_ci	&f2fs_xattr_security_handler,
21062306a36Sopenharmony_ci#endif
21162306a36Sopenharmony_ci	&f2fs_xattr_advise_handler,
21262306a36Sopenharmony_ci	NULL,
21362306a36Sopenharmony_ci};
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic inline const char *f2fs_xattr_prefix(int index,
21662306a36Sopenharmony_ci					    struct dentry *dentry)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	const struct xattr_handler *handler = NULL;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (index > 0 && index < ARRAY_SIZE(f2fs_xattr_handler_map))
22162306a36Sopenharmony_ci		handler = f2fs_xattr_handler_map[index];
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!xattr_handler_can_list(handler, dentry))
22462306a36Sopenharmony_ci		return NULL;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return xattr_prefix(handler);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic struct f2fs_xattr_entry *__find_xattr(void *base_addr,
23062306a36Sopenharmony_ci				void *last_base_addr, void **last_addr,
23162306a36Sopenharmony_ci				int index, size_t len, const char *name)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct f2fs_xattr_entry *entry;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	list_for_each_xattr(entry, base_addr) {
23662306a36Sopenharmony_ci		if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
23762306a36Sopenharmony_ci			(void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) {
23862306a36Sopenharmony_ci			if (last_addr)
23962306a36Sopenharmony_ci				*last_addr = entry;
24062306a36Sopenharmony_ci			return NULL;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		if (entry->e_name_index != index)
24462306a36Sopenharmony_ci			continue;
24562306a36Sopenharmony_ci		if (entry->e_name_len != len)
24662306a36Sopenharmony_ci			continue;
24762306a36Sopenharmony_ci		if (!memcmp(entry->e_name, name, len))
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	return entry;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic struct f2fs_xattr_entry *__find_inline_xattr(struct inode *inode,
25462306a36Sopenharmony_ci				void *base_addr, void **last_addr, int index,
25562306a36Sopenharmony_ci				size_t len, const char *name)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct f2fs_xattr_entry *entry;
25862306a36Sopenharmony_ci	unsigned int inline_size = inline_xattr_size(inode);
25962306a36Sopenharmony_ci	void *max_addr = base_addr + inline_size;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	entry = __find_xattr(base_addr, max_addr, last_addr, index, len, name);
26262306a36Sopenharmony_ci	if (!entry)
26362306a36Sopenharmony_ci		return NULL;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* inline xattr header or entry across max inline xattr size */
26662306a36Sopenharmony_ci	if (IS_XATTR_LAST_ENTRY(entry) &&
26762306a36Sopenharmony_ci		(void *)entry + sizeof(__u32) > max_addr) {
26862306a36Sopenharmony_ci		*last_addr = entry;
26962306a36Sopenharmony_ci		return NULL;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci	return entry;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int read_inline_xattr(struct inode *inode, struct page *ipage,
27562306a36Sopenharmony_ci							void *txattr_addr)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
27862306a36Sopenharmony_ci	unsigned int inline_size = inline_xattr_size(inode);
27962306a36Sopenharmony_ci	struct page *page = NULL;
28062306a36Sopenharmony_ci	void *inline_addr;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (ipage) {
28362306a36Sopenharmony_ci		inline_addr = inline_xattr_addr(inode, ipage);
28462306a36Sopenharmony_ci	} else {
28562306a36Sopenharmony_ci		page = f2fs_get_node_page(sbi, inode->i_ino);
28662306a36Sopenharmony_ci		if (IS_ERR(page))
28762306a36Sopenharmony_ci			return PTR_ERR(page);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		inline_addr = inline_xattr_addr(inode, page);
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci	memcpy(txattr_addr, inline_addr, inline_size);
29262306a36Sopenharmony_ci	f2fs_put_page(page, 1);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int read_xattr_block(struct inode *inode, void *txattr_addr)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
30062306a36Sopenharmony_ci	nid_t xnid = F2FS_I(inode)->i_xattr_nid;
30162306a36Sopenharmony_ci	unsigned int inline_size = inline_xattr_size(inode);
30262306a36Sopenharmony_ci	struct page *xpage;
30362306a36Sopenharmony_ci	void *xattr_addr;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* The inode already has an extended attribute block. */
30662306a36Sopenharmony_ci	xpage = f2fs_get_node_page(sbi, xnid);
30762306a36Sopenharmony_ci	if (IS_ERR(xpage))
30862306a36Sopenharmony_ci		return PTR_ERR(xpage);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	xattr_addr = page_address(xpage);
31162306a36Sopenharmony_ci	memcpy(txattr_addr + inline_size, xattr_addr, VALID_XATTR_BLOCK_SIZE);
31262306a36Sopenharmony_ci	f2fs_put_page(xpage, 1);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int lookup_all_xattrs(struct inode *inode, struct page *ipage,
31862306a36Sopenharmony_ci				unsigned int index, unsigned int len,
31962306a36Sopenharmony_ci				const char *name, struct f2fs_xattr_entry **xe,
32062306a36Sopenharmony_ci				void **base_addr, int *base_size,
32162306a36Sopenharmony_ci				bool *is_inline)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	void *cur_addr, *txattr_addr, *last_txattr_addr;
32462306a36Sopenharmony_ci	void *last_addr = NULL;
32562306a36Sopenharmony_ci	nid_t xnid = F2FS_I(inode)->i_xattr_nid;
32662306a36Sopenharmony_ci	unsigned int inline_size = inline_xattr_size(inode);
32762306a36Sopenharmony_ci	int err;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (!xnid && !inline_size)
33062306a36Sopenharmony_ci		return -ENODATA;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	*base_size = XATTR_SIZE(inode) + XATTR_PADDING_SIZE;
33362306a36Sopenharmony_ci	txattr_addr = xattr_alloc(F2FS_I_SB(inode), *base_size, is_inline);
33462306a36Sopenharmony_ci	if (!txattr_addr)
33562306a36Sopenharmony_ci		return -ENOMEM;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	last_txattr_addr = (void *)txattr_addr + XATTR_SIZE(inode);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* read from inline xattr */
34062306a36Sopenharmony_ci	if (inline_size) {
34162306a36Sopenharmony_ci		err = read_inline_xattr(inode, ipage, txattr_addr);
34262306a36Sopenharmony_ci		if (err)
34362306a36Sopenharmony_ci			goto out;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		*xe = __find_inline_xattr(inode, txattr_addr, &last_addr,
34662306a36Sopenharmony_ci						index, len, name);
34762306a36Sopenharmony_ci		if (*xe) {
34862306a36Sopenharmony_ci			*base_size = inline_size;
34962306a36Sopenharmony_ci			goto check;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* read from xattr node block */
35462306a36Sopenharmony_ci	if (xnid) {
35562306a36Sopenharmony_ci		err = read_xattr_block(inode, txattr_addr);
35662306a36Sopenharmony_ci		if (err)
35762306a36Sopenharmony_ci			goto out;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (last_addr)
36162306a36Sopenharmony_ci		cur_addr = XATTR_HDR(last_addr) - 1;
36262306a36Sopenharmony_ci	else
36362306a36Sopenharmony_ci		cur_addr = txattr_addr;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	*xe = __find_xattr(cur_addr, last_txattr_addr, NULL, index, len, name);
36662306a36Sopenharmony_ci	if (!*xe) {
36762306a36Sopenharmony_ci		f2fs_err(F2FS_I_SB(inode), "lookup inode (%lu) has corrupted xattr",
36862306a36Sopenharmony_ci								inode->i_ino);
36962306a36Sopenharmony_ci		set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
37062306a36Sopenharmony_ci		err = -ENODATA;
37162306a36Sopenharmony_ci		f2fs_handle_error(F2FS_I_SB(inode),
37262306a36Sopenharmony_ci					ERROR_CORRUPTED_XATTR);
37362306a36Sopenharmony_ci		goto out;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_cicheck:
37662306a36Sopenharmony_ci	if (IS_XATTR_LAST_ENTRY(*xe)) {
37762306a36Sopenharmony_ci		err = -ENODATA;
37862306a36Sopenharmony_ci		goto out;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	*base_addr = txattr_addr;
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ciout:
38462306a36Sopenharmony_ci	xattr_free(F2FS_I_SB(inode), txattr_addr, *is_inline);
38562306a36Sopenharmony_ci	return err;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int read_all_xattrs(struct inode *inode, struct page *ipage,
38962306a36Sopenharmony_ci							void **base_addr)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct f2fs_xattr_header *header;
39262306a36Sopenharmony_ci	nid_t xnid = F2FS_I(inode)->i_xattr_nid;
39362306a36Sopenharmony_ci	unsigned int size = VALID_XATTR_BLOCK_SIZE;
39462306a36Sopenharmony_ci	unsigned int inline_size = inline_xattr_size(inode);
39562306a36Sopenharmony_ci	void *txattr_addr;
39662306a36Sopenharmony_ci	int err;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode),
39962306a36Sopenharmony_ci			inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS);
40062306a36Sopenharmony_ci	if (!txattr_addr)
40162306a36Sopenharmony_ci		return -ENOMEM;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* read from inline xattr */
40462306a36Sopenharmony_ci	if (inline_size) {
40562306a36Sopenharmony_ci		err = read_inline_xattr(inode, ipage, txattr_addr);
40662306a36Sopenharmony_ci		if (err)
40762306a36Sopenharmony_ci			goto fail;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* read from xattr node block */
41162306a36Sopenharmony_ci	if (xnid) {
41262306a36Sopenharmony_ci		err = read_xattr_block(inode, txattr_addr);
41362306a36Sopenharmony_ci		if (err)
41462306a36Sopenharmony_ci			goto fail;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	header = XATTR_HDR(txattr_addr);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* never been allocated xattrs */
42062306a36Sopenharmony_ci	if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) {
42162306a36Sopenharmony_ci		header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
42262306a36Sopenharmony_ci		header->h_refcount = cpu_to_le32(1);
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci	*base_addr = txattr_addr;
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_cifail:
42762306a36Sopenharmony_ci	kfree(txattr_addr);
42862306a36Sopenharmony_ci	return err;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic inline int write_all_xattrs(struct inode *inode, __u32 hsize,
43262306a36Sopenharmony_ci				void *txattr_addr, struct page *ipage)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
43562306a36Sopenharmony_ci	size_t inline_size = inline_xattr_size(inode);
43662306a36Sopenharmony_ci	struct page *in_page = NULL;
43762306a36Sopenharmony_ci	void *xattr_addr;
43862306a36Sopenharmony_ci	void *inline_addr = NULL;
43962306a36Sopenharmony_ci	struct page *xpage;
44062306a36Sopenharmony_ci	nid_t new_nid = 0;
44162306a36Sopenharmony_ci	int err = 0;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid)
44462306a36Sopenharmony_ci		if (!f2fs_alloc_nid(sbi, &new_nid))
44562306a36Sopenharmony_ci			return -ENOSPC;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* write to inline xattr */
44862306a36Sopenharmony_ci	if (inline_size) {
44962306a36Sopenharmony_ci		if (ipage) {
45062306a36Sopenharmony_ci			inline_addr = inline_xattr_addr(inode, ipage);
45162306a36Sopenharmony_ci		} else {
45262306a36Sopenharmony_ci			in_page = f2fs_get_node_page(sbi, inode->i_ino);
45362306a36Sopenharmony_ci			if (IS_ERR(in_page)) {
45462306a36Sopenharmony_ci				f2fs_alloc_nid_failed(sbi, new_nid);
45562306a36Sopenharmony_ci				return PTR_ERR(in_page);
45662306a36Sopenharmony_ci			}
45762306a36Sopenharmony_ci			inline_addr = inline_xattr_addr(inode, in_page);
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		f2fs_wait_on_page_writeback(ipage ? ipage : in_page,
46162306a36Sopenharmony_ci							NODE, true, true);
46262306a36Sopenharmony_ci		/* no need to use xattr node block */
46362306a36Sopenharmony_ci		if (hsize <= inline_size) {
46462306a36Sopenharmony_ci			err = f2fs_truncate_xattr_node(inode);
46562306a36Sopenharmony_ci			f2fs_alloc_nid_failed(sbi, new_nid);
46662306a36Sopenharmony_ci			if (err) {
46762306a36Sopenharmony_ci				f2fs_put_page(in_page, 1);
46862306a36Sopenharmony_ci				return err;
46962306a36Sopenharmony_ci			}
47062306a36Sopenharmony_ci			memcpy(inline_addr, txattr_addr, inline_size);
47162306a36Sopenharmony_ci			set_page_dirty(ipage ? ipage : in_page);
47262306a36Sopenharmony_ci			goto in_page_out;
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* write to xattr node block */
47762306a36Sopenharmony_ci	if (F2FS_I(inode)->i_xattr_nid) {
47862306a36Sopenharmony_ci		xpage = f2fs_get_node_page(sbi, F2FS_I(inode)->i_xattr_nid);
47962306a36Sopenharmony_ci		if (IS_ERR(xpage)) {
48062306a36Sopenharmony_ci			err = PTR_ERR(xpage);
48162306a36Sopenharmony_ci			f2fs_alloc_nid_failed(sbi, new_nid);
48262306a36Sopenharmony_ci			goto in_page_out;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci		f2fs_bug_on(sbi, new_nid);
48562306a36Sopenharmony_ci		f2fs_wait_on_page_writeback(xpage, NODE, true, true);
48662306a36Sopenharmony_ci	} else {
48762306a36Sopenharmony_ci		struct dnode_of_data dn;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		set_new_dnode(&dn, inode, NULL, NULL, new_nid);
49062306a36Sopenharmony_ci		xpage = f2fs_new_node_page(&dn, XATTR_NODE_OFFSET);
49162306a36Sopenharmony_ci		if (IS_ERR(xpage)) {
49262306a36Sopenharmony_ci			err = PTR_ERR(xpage);
49362306a36Sopenharmony_ci			f2fs_alloc_nid_failed(sbi, new_nid);
49462306a36Sopenharmony_ci			goto in_page_out;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci		f2fs_alloc_nid_done(sbi, new_nid);
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	xattr_addr = page_address(xpage);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (inline_size)
50162306a36Sopenharmony_ci		memcpy(inline_addr, txattr_addr, inline_size);
50262306a36Sopenharmony_ci	memcpy(xattr_addr, txattr_addr + inline_size, VALID_XATTR_BLOCK_SIZE);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (inline_size)
50562306a36Sopenharmony_ci		set_page_dirty(ipage ? ipage : in_page);
50662306a36Sopenharmony_ci	set_page_dirty(xpage);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	f2fs_put_page(xpage, 1);
50962306a36Sopenharmony_ciin_page_out:
51062306a36Sopenharmony_ci	f2fs_put_page(in_page, 1);
51162306a36Sopenharmony_ci	return err;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ciint f2fs_getxattr(struct inode *inode, int index, const char *name,
51562306a36Sopenharmony_ci		void *buffer, size_t buffer_size, struct page *ipage)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct f2fs_xattr_entry *entry = NULL;
51862306a36Sopenharmony_ci	int error;
51962306a36Sopenharmony_ci	unsigned int size, len;
52062306a36Sopenharmony_ci	void *base_addr = NULL;
52162306a36Sopenharmony_ci	int base_size;
52262306a36Sopenharmony_ci	bool is_inline;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (name == NULL)
52562306a36Sopenharmony_ci		return -EINVAL;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	len = strlen(name);
52862306a36Sopenharmony_ci	if (len > F2FS_NAME_LEN)
52962306a36Sopenharmony_ci		return -ERANGE;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (!ipage)
53262306a36Sopenharmony_ci		f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
53362306a36Sopenharmony_ci	error = lookup_all_xattrs(inode, ipage, index, len, name,
53462306a36Sopenharmony_ci				&entry, &base_addr, &base_size, &is_inline);
53562306a36Sopenharmony_ci	if (!ipage)
53662306a36Sopenharmony_ci		f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
53762306a36Sopenharmony_ci	if (error)
53862306a36Sopenharmony_ci		return error;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	size = le16_to_cpu(entry->e_value_size);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (buffer && size > buffer_size) {
54362306a36Sopenharmony_ci		error = -ERANGE;
54462306a36Sopenharmony_ci		goto out;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (buffer) {
54862306a36Sopenharmony_ci		char *pval = entry->e_name + entry->e_name_len;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		if (base_size - (pval - (char *)base_addr) < size) {
55162306a36Sopenharmony_ci			error = -ERANGE;
55262306a36Sopenharmony_ci			goto out;
55362306a36Sopenharmony_ci		}
55462306a36Sopenharmony_ci		memcpy(buffer, pval, size);
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	error = size;
55762306a36Sopenharmony_ciout:
55862306a36Sopenharmony_ci	xattr_free(F2FS_I_SB(inode), base_addr, is_inline);
55962306a36Sopenharmony_ci	return error;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cissize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
56562306a36Sopenharmony_ci	struct f2fs_xattr_entry *entry;
56662306a36Sopenharmony_ci	void *base_addr, *last_base_addr;
56762306a36Sopenharmony_ci	int error;
56862306a36Sopenharmony_ci	size_t rest = buffer_size;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
57162306a36Sopenharmony_ci	error = read_all_xattrs(inode, NULL, &base_addr);
57262306a36Sopenharmony_ci	f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
57362306a36Sopenharmony_ci	if (error)
57462306a36Sopenharmony_ci		return error;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	last_base_addr = (void *)base_addr + XATTR_SIZE(inode);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	list_for_each_xattr(entry, base_addr) {
57962306a36Sopenharmony_ci		const char *prefix;
58062306a36Sopenharmony_ci		size_t prefix_len;
58162306a36Sopenharmony_ci		size_t size;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
58662306a36Sopenharmony_ci			(void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) {
58762306a36Sopenharmony_ci			f2fs_err(F2FS_I_SB(inode), "list inode (%lu) has corrupted xattr",
58862306a36Sopenharmony_ci						inode->i_ino);
58962306a36Sopenharmony_ci			set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
59062306a36Sopenharmony_ci			f2fs_handle_error(F2FS_I_SB(inode),
59162306a36Sopenharmony_ci						ERROR_CORRUPTED_XATTR);
59262306a36Sopenharmony_ci			break;
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (!prefix)
59662306a36Sopenharmony_ci			continue;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		prefix_len = strlen(prefix);
59962306a36Sopenharmony_ci		size = prefix_len + entry->e_name_len + 1;
60062306a36Sopenharmony_ci		if (buffer) {
60162306a36Sopenharmony_ci			if (size > rest) {
60262306a36Sopenharmony_ci				error = -ERANGE;
60362306a36Sopenharmony_ci				goto cleanup;
60462306a36Sopenharmony_ci			}
60562306a36Sopenharmony_ci			memcpy(buffer, prefix, prefix_len);
60662306a36Sopenharmony_ci			buffer += prefix_len;
60762306a36Sopenharmony_ci			memcpy(buffer, entry->e_name, entry->e_name_len);
60862306a36Sopenharmony_ci			buffer += entry->e_name_len;
60962306a36Sopenharmony_ci			*buffer++ = 0;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci		rest -= size;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci	error = buffer_size - rest;
61462306a36Sopenharmony_cicleanup:
61562306a36Sopenharmony_ci	kfree(base_addr);
61662306a36Sopenharmony_ci	return error;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic bool f2fs_xattr_value_same(struct f2fs_xattr_entry *entry,
62062306a36Sopenharmony_ci					const void *value, size_t size)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	void *pval = entry->e_name + entry->e_name_len;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return (le16_to_cpu(entry->e_value_size) == size) &&
62562306a36Sopenharmony_ci					!memcmp(pval, value, size);
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic int __f2fs_setxattr(struct inode *inode, int index,
62962306a36Sopenharmony_ci			const char *name, const void *value, size_t size,
63062306a36Sopenharmony_ci			struct page *ipage, int flags)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct f2fs_xattr_entry *here, *last;
63362306a36Sopenharmony_ci	void *base_addr, *last_base_addr;
63462306a36Sopenharmony_ci	int found, newsize;
63562306a36Sopenharmony_ci	size_t len;
63662306a36Sopenharmony_ci	__u32 new_hsize;
63762306a36Sopenharmony_ci	int error;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (name == NULL)
64062306a36Sopenharmony_ci		return -EINVAL;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (value == NULL)
64362306a36Sopenharmony_ci		size = 0;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	len = strlen(name);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (len > F2FS_NAME_LEN)
64862306a36Sopenharmony_ci		return -ERANGE;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (size > MAX_VALUE_LEN(inode))
65162306a36Sopenharmony_ci		return -E2BIG;
65262306a36Sopenharmony_ciretry:
65362306a36Sopenharmony_ci	error = read_all_xattrs(inode, ipage, &base_addr);
65462306a36Sopenharmony_ci	if (error)
65562306a36Sopenharmony_ci		return error;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	last_base_addr = (void *)base_addr + XATTR_SIZE(inode);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* find entry with wanted name. */
66062306a36Sopenharmony_ci	here = __find_xattr(base_addr, last_base_addr, NULL, index, len, name);
66162306a36Sopenharmony_ci	if (!here) {
66262306a36Sopenharmony_ci		if (!F2FS_I(inode)->i_xattr_nid) {
66362306a36Sopenharmony_ci			error = f2fs_recover_xattr_data(inode, NULL);
66462306a36Sopenharmony_ci			f2fs_notice(F2FS_I_SB(inode),
66562306a36Sopenharmony_ci				"recover xattr in inode (%lu), error(%d)",
66662306a36Sopenharmony_ci					inode->i_ino, error);
66762306a36Sopenharmony_ci			if (!error) {
66862306a36Sopenharmony_ci				kfree(base_addr);
66962306a36Sopenharmony_ci				goto retry;
67062306a36Sopenharmony_ci			}
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci		f2fs_err(F2FS_I_SB(inode), "set inode (%lu) has corrupted xattr",
67362306a36Sopenharmony_ci								inode->i_ino);
67462306a36Sopenharmony_ci		set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
67562306a36Sopenharmony_ci		error = -EFSCORRUPTED;
67662306a36Sopenharmony_ci		f2fs_handle_error(F2FS_I_SB(inode),
67762306a36Sopenharmony_ci					ERROR_CORRUPTED_XATTR);
67862306a36Sopenharmony_ci		goto exit;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (found) {
68462306a36Sopenharmony_ci		if ((flags & XATTR_CREATE)) {
68562306a36Sopenharmony_ci			error = -EEXIST;
68662306a36Sopenharmony_ci			goto exit;
68762306a36Sopenharmony_ci		}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		if (value && f2fs_xattr_value_same(here, value, size))
69062306a36Sopenharmony_ci			goto same;
69162306a36Sopenharmony_ci	} else if ((flags & XATTR_REPLACE)) {
69262306a36Sopenharmony_ci		error = -ENODATA;
69362306a36Sopenharmony_ci		goto exit;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	last = here;
69762306a36Sopenharmony_ci	while (!IS_XATTR_LAST_ENTRY(last)) {
69862306a36Sopenharmony_ci		if ((void *)(last) + sizeof(__u32) > last_base_addr ||
69962306a36Sopenharmony_ci			(void *)XATTR_NEXT_ENTRY(last) > last_base_addr) {
70062306a36Sopenharmony_ci			f2fs_err(F2FS_I_SB(inode), "inode (%lu) has invalid last xattr entry, entry_size: %zu",
70162306a36Sopenharmony_ci					inode->i_ino, ENTRY_SIZE(last));
70262306a36Sopenharmony_ci			set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
70362306a36Sopenharmony_ci			error = -EFSCORRUPTED;
70462306a36Sopenharmony_ci			f2fs_handle_error(F2FS_I_SB(inode),
70562306a36Sopenharmony_ci						ERROR_CORRUPTED_XATTR);
70662306a36Sopenharmony_ci			goto exit;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci		last = XATTR_NEXT_ENTRY(last);
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/* 1. Check space */
71462306a36Sopenharmony_ci	if (value) {
71562306a36Sopenharmony_ci		int free;
71662306a36Sopenharmony_ci		/*
71762306a36Sopenharmony_ci		 * If value is NULL, it is remove operation.
71862306a36Sopenharmony_ci		 * In case of update operation, we calculate free.
71962306a36Sopenharmony_ci		 */
72062306a36Sopenharmony_ci		free = MIN_OFFSET(inode) - ((char *)last - (char *)base_addr);
72162306a36Sopenharmony_ci		if (found)
72262306a36Sopenharmony_ci			free = free + ENTRY_SIZE(here);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		if (unlikely(free < newsize)) {
72562306a36Sopenharmony_ci			error = -E2BIG;
72662306a36Sopenharmony_ci			goto exit;
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* 2. Remove old entry */
73162306a36Sopenharmony_ci	if (found) {
73262306a36Sopenharmony_ci		/*
73362306a36Sopenharmony_ci		 * If entry is found, remove old entry.
73462306a36Sopenharmony_ci		 * If not found, remove operation is not needed.
73562306a36Sopenharmony_ci		 */
73662306a36Sopenharmony_ci		struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here);
73762306a36Sopenharmony_ci		int oldsize = ENTRY_SIZE(here);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		memmove(here, next, (char *)last - (char *)next);
74062306a36Sopenharmony_ci		last = (struct f2fs_xattr_entry *)((char *)last - oldsize);
74162306a36Sopenharmony_ci		memset(last, 0, oldsize);
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	new_hsize = (char *)last - (char *)base_addr;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/* 3. Write new entry */
74762306a36Sopenharmony_ci	if (value) {
74862306a36Sopenharmony_ci		char *pval;
74962306a36Sopenharmony_ci		/*
75062306a36Sopenharmony_ci		 * Before we come here, old entry is removed.
75162306a36Sopenharmony_ci		 * We just write new entry.
75262306a36Sopenharmony_ci		 */
75362306a36Sopenharmony_ci		last->e_name_index = index;
75462306a36Sopenharmony_ci		last->e_name_len = len;
75562306a36Sopenharmony_ci		memcpy(last->e_name, name, len);
75662306a36Sopenharmony_ci		pval = last->e_name + len;
75762306a36Sopenharmony_ci		memcpy(pval, value, size);
75862306a36Sopenharmony_ci		last->e_value_size = cpu_to_le16(size);
75962306a36Sopenharmony_ci		new_hsize += newsize;
76062306a36Sopenharmony_ci		/*
76162306a36Sopenharmony_ci		 * Explicitly add the null terminator.  The unused xattr space
76262306a36Sopenharmony_ci		 * is supposed to always be zeroed, which would make this
76362306a36Sopenharmony_ci		 * unnecessary, but don't depend on that.
76462306a36Sopenharmony_ci		 */
76562306a36Sopenharmony_ci		*(u32 *)((u8 *)last + newsize) = 0;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	error = write_all_xattrs(inode, new_hsize, base_addr, ipage);
76962306a36Sopenharmony_ci	if (error)
77062306a36Sopenharmony_ci		goto exit;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
77362306a36Sopenharmony_ci			!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
77462306a36Sopenharmony_ci		f2fs_set_encrypted_inode(inode);
77562306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
77662306a36Sopenharmony_ci		set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cisame:
77962306a36Sopenharmony_ci	if (is_inode_flag_set(inode, FI_ACL_MODE)) {
78062306a36Sopenharmony_ci		inode->i_mode = F2FS_I(inode)->i_acl_mode;
78162306a36Sopenharmony_ci		clear_inode_flag(inode, FI_ACL_MODE);
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	inode_set_ctime_current(inode);
78562306a36Sopenharmony_ci	f2fs_mark_inode_dirty_sync(inode, true);
78662306a36Sopenharmony_ciexit:
78762306a36Sopenharmony_ci	kfree(base_addr);
78862306a36Sopenharmony_ci	return error;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ciint f2fs_setxattr(struct inode *inode, int index, const char *name,
79262306a36Sopenharmony_ci				const void *value, size_t size,
79362306a36Sopenharmony_ci				struct page *ipage, int flags)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
79662306a36Sopenharmony_ci	int err;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (unlikely(f2fs_cp_error(sbi)))
79962306a36Sopenharmony_ci		return -EIO;
80062306a36Sopenharmony_ci	if (!f2fs_is_checkpoint_ready(sbi))
80162306a36Sopenharmony_ci		return -ENOSPC;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	err = f2fs_dquot_initialize(inode);
80462306a36Sopenharmony_ci	if (err)
80562306a36Sopenharmony_ci		return err;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* this case is only from f2fs_init_inode_metadata */
80862306a36Sopenharmony_ci	if (ipage)
80962306a36Sopenharmony_ci		return __f2fs_setxattr(inode, index, name, value,
81062306a36Sopenharmony_ci						size, ipage, flags);
81162306a36Sopenharmony_ci	f2fs_balance_fs(sbi, true);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	f2fs_lock_op(sbi);
81462306a36Sopenharmony_ci	f2fs_down_write(&F2FS_I(inode)->i_xattr_sem);
81562306a36Sopenharmony_ci	err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags);
81662306a36Sopenharmony_ci	f2fs_up_write(&F2FS_I(inode)->i_xattr_sem);
81762306a36Sopenharmony_ci	f2fs_unlock_op(sbi);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	f2fs_update_time(sbi, REQ_TIME);
82062306a36Sopenharmony_ci	return err;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ciint f2fs_init_xattr_caches(struct f2fs_sb_info *sbi)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	dev_t dev = sbi->sb->s_bdev->bd_dev;
82662306a36Sopenharmony_ci	char slab_name[32];
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	sprintf(slab_name, "f2fs_xattr_entry-%u:%u", MAJOR(dev), MINOR(dev));
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	sbi->inline_xattr_slab_size = F2FS_OPTION(sbi).inline_xattr_size *
83162306a36Sopenharmony_ci					sizeof(__le32) + XATTR_PADDING_SIZE;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	sbi->inline_xattr_slab = f2fs_kmem_cache_create(slab_name,
83462306a36Sopenharmony_ci					sbi->inline_xattr_slab_size);
83562306a36Sopenharmony_ci	if (!sbi->inline_xattr_slab)
83662306a36Sopenharmony_ci		return -ENOMEM;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	return 0;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_civoid f2fs_destroy_xattr_caches(struct f2fs_sb_info *sbi)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	kmem_cache_destroy(sbi->inline_xattr_slab);
84462306a36Sopenharmony_ci}
845