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