18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/ext4/xattr.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Fix by Harrison Xing <harrison@mountainviewdata.com>. 88c2ecf20Sopenharmony_ci * Ext4 code with a lot of help from Eric Jarman <ejarman@acm.org>. 98c2ecf20Sopenharmony_ci * Extended attributes for symlinks and special files added per 108c2ecf20Sopenharmony_ci * suggestion of Luka Renko <luka.renko@hermes.si>. 118c2ecf20Sopenharmony_ci * xattr consolidation Copyright (c) 2004 James Morris <jmorris@redhat.com>, 128c2ecf20Sopenharmony_ci * Red Hat Inc. 138c2ecf20Sopenharmony_ci * ea-in-inode support by Alex Tomas <alex@clusterfs.com> aka bzzz 148c2ecf20Sopenharmony_ci * and Andreas Gruenbacher <agruen@suse.de>. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Extended attributes are stored directly in inodes (on file systems with 198c2ecf20Sopenharmony_ci * inodes bigger than 128 bytes) and on additional disk blocks. The i_file_acl 208c2ecf20Sopenharmony_ci * field contains the block number if an inode uses an additional block. All 218c2ecf20Sopenharmony_ci * attributes must fit in the inode and one additional block. Blocks that 228c2ecf20Sopenharmony_ci * contain the identical set of attributes may be shared among several inodes. 238c2ecf20Sopenharmony_ci * Identical blocks are detected by keeping a cache of blocks that have 248c2ecf20Sopenharmony_ci * recently been accessed. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * The attributes in inodes and on blocks have a different header; the entries 278c2ecf20Sopenharmony_ci * are stored in the same format: 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * +------------------+ 308c2ecf20Sopenharmony_ci * | header | 318c2ecf20Sopenharmony_ci * | entry 1 | | 328c2ecf20Sopenharmony_ci * | entry 2 | | growing downwards 338c2ecf20Sopenharmony_ci * | entry 3 | v 348c2ecf20Sopenharmony_ci * | four null bytes | 358c2ecf20Sopenharmony_ci * | . . . | 368c2ecf20Sopenharmony_ci * | value 1 | ^ 378c2ecf20Sopenharmony_ci * | value 3 | | growing upwards 388c2ecf20Sopenharmony_ci * | value 2 | | 398c2ecf20Sopenharmony_ci * +------------------+ 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * The header is followed by multiple entry descriptors. In disk blocks, the 428c2ecf20Sopenharmony_ci * entry descriptors are kept sorted. In inodes, they are unsorted. The 438c2ecf20Sopenharmony_ci * attribute values are aligned to the end of the block in no specific order. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * Locking strategy 468c2ecf20Sopenharmony_ci * ---------------- 478c2ecf20Sopenharmony_ci * EXT4_I(inode)->i_file_acl is protected by EXT4_I(inode)->xattr_sem. 488c2ecf20Sopenharmony_ci * EA blocks are only changed if they are exclusive to an inode, so 498c2ecf20Sopenharmony_ci * holding xattr_sem also means that nothing but the EA block's reference 508c2ecf20Sopenharmony_ci * count can change. Multiple writers to the same block are synchronized 518c2ecf20Sopenharmony_ci * by the buffer lock. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <linux/init.h> 558c2ecf20Sopenharmony_ci#include <linux/fs.h> 568c2ecf20Sopenharmony_ci#include <linux/slab.h> 578c2ecf20Sopenharmony_ci#include <linux/mbcache.h> 588c2ecf20Sopenharmony_ci#include <linux/quotaops.h> 598c2ecf20Sopenharmony_ci#include <linux/iversion.h> 608c2ecf20Sopenharmony_ci#include "ext4_jbd2.h" 618c2ecf20Sopenharmony_ci#include "ext4.h" 628c2ecf20Sopenharmony_ci#include "xattr.h" 638c2ecf20Sopenharmony_ci#include "acl.h" 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#ifdef EXT4_XATTR_DEBUG 668c2ecf20Sopenharmony_ci# define ea_idebug(inode, fmt, ...) \ 678c2ecf20Sopenharmony_ci printk(KERN_DEBUG "inode %s:%lu: " fmt "\n", \ 688c2ecf20Sopenharmony_ci inode->i_sb->s_id, inode->i_ino, ##__VA_ARGS__) 698c2ecf20Sopenharmony_ci# define ea_bdebug(bh, fmt, ...) \ 708c2ecf20Sopenharmony_ci printk(KERN_DEBUG "block %pg:%lu: " fmt "\n", \ 718c2ecf20Sopenharmony_ci bh->b_bdev, (unsigned long)bh->b_blocknr, ##__VA_ARGS__) 728c2ecf20Sopenharmony_ci#else 738c2ecf20Sopenharmony_ci# define ea_idebug(inode, fmt, ...) no_printk(fmt, ##__VA_ARGS__) 748c2ecf20Sopenharmony_ci# define ea_bdebug(bh, fmt, ...) no_printk(fmt, ##__VA_ARGS__) 758c2ecf20Sopenharmony_ci#endif 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void ext4_xattr_block_cache_insert(struct mb_cache *, 788c2ecf20Sopenharmony_ci struct buffer_head *); 798c2ecf20Sopenharmony_cistatic struct buffer_head * 808c2ecf20Sopenharmony_ciext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *, 818c2ecf20Sopenharmony_ci struct mb_cache_entry **); 828c2ecf20Sopenharmony_cistatic __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value, 838c2ecf20Sopenharmony_ci size_t value_count); 848c2ecf20Sopenharmony_cistatic void ext4_xattr_rehash(struct ext4_xattr_header *); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct xattr_handler * const ext4_xattr_handler_map[] = { 878c2ecf20Sopenharmony_ci [EXT4_XATTR_INDEX_USER] = &ext4_xattr_user_handler, 888c2ecf20Sopenharmony_ci#ifdef CONFIG_EXT4_FS_POSIX_ACL 898c2ecf20Sopenharmony_ci [EXT4_XATTR_INDEX_POSIX_ACL_ACCESS] = &posix_acl_access_xattr_handler, 908c2ecf20Sopenharmony_ci [EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT] = &posix_acl_default_xattr_handler, 918c2ecf20Sopenharmony_ci#endif 928c2ecf20Sopenharmony_ci [EXT4_XATTR_INDEX_TRUSTED] = &ext4_xattr_trusted_handler, 938c2ecf20Sopenharmony_ci#ifdef CONFIG_EXT4_FS_SECURITY 948c2ecf20Sopenharmony_ci [EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler, 958c2ecf20Sopenharmony_ci#endif 968c2ecf20Sopenharmony_ci [EXT4_XATTR_INDEX_HURD] = &ext4_xattr_hurd_handler, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciconst struct xattr_handler *ext4_xattr_handlers[] = { 1008c2ecf20Sopenharmony_ci &ext4_xattr_user_handler, 1018c2ecf20Sopenharmony_ci &ext4_xattr_trusted_handler, 1028c2ecf20Sopenharmony_ci#ifdef CONFIG_EXT4_FS_POSIX_ACL 1038c2ecf20Sopenharmony_ci &posix_acl_access_xattr_handler, 1048c2ecf20Sopenharmony_ci &posix_acl_default_xattr_handler, 1058c2ecf20Sopenharmony_ci#endif 1068c2ecf20Sopenharmony_ci#ifdef CONFIG_EXT4_FS_SECURITY 1078c2ecf20Sopenharmony_ci &ext4_xattr_security_handler, 1088c2ecf20Sopenharmony_ci#endif 1098c2ecf20Sopenharmony_ci &ext4_xattr_hurd_handler, 1108c2ecf20Sopenharmony_ci NULL 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define EA_BLOCK_CACHE(inode) (((struct ext4_sb_info *) \ 1148c2ecf20Sopenharmony_ci inode->i_sb->s_fs_info)->s_ea_block_cache) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define EA_INODE_CACHE(inode) (((struct ext4_sb_info *) \ 1178c2ecf20Sopenharmony_ci inode->i_sb->s_fs_info)->s_ea_inode_cache) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int 1208c2ecf20Sopenharmony_ciext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array, 1218c2ecf20Sopenharmony_ci struct inode *inode); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#ifdef CONFIG_LOCKDEP 1248c2ecf20Sopenharmony_civoid ext4_xattr_inode_set_class(struct inode *ea_inode) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(ea_inode); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci lockdep_set_subclass(&ea_inode->i_rwsem, 1); 1298c2ecf20Sopenharmony_ci (void) ei; /* shut up clang warning if !CONFIG_LOCKDEP */ 1308c2ecf20Sopenharmony_ci lockdep_set_subclass(&ei->i_data_sem, I_DATA_SEM_EA); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci#endif 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic __le32 ext4_xattr_block_csum(struct inode *inode, 1358c2ecf20Sopenharmony_ci sector_t block_nr, 1368c2ecf20Sopenharmony_ci struct ext4_xattr_header *hdr) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 1398c2ecf20Sopenharmony_ci __u32 csum; 1408c2ecf20Sopenharmony_ci __le64 dsk_block_nr = cpu_to_le64(block_nr); 1418c2ecf20Sopenharmony_ci __u32 dummy_csum = 0; 1428c2ecf20Sopenharmony_ci int offset = offsetof(struct ext4_xattr_header, h_checksum); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&dsk_block_nr, 1458c2ecf20Sopenharmony_ci sizeof(dsk_block_nr)); 1468c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, csum, (__u8 *)hdr, offset); 1478c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum)); 1488c2ecf20Sopenharmony_ci offset += sizeof(dummy_csum); 1498c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, csum, (__u8 *)hdr + offset, 1508c2ecf20Sopenharmony_ci EXT4_BLOCK_SIZE(inode->i_sb) - offset); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return cpu_to_le32(csum); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int ext4_xattr_block_csum_verify(struct inode *inode, 1568c2ecf20Sopenharmony_ci struct buffer_head *bh) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct ext4_xattr_header *hdr = BHDR(bh); 1598c2ecf20Sopenharmony_ci int ret = 1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(inode->i_sb)) { 1628c2ecf20Sopenharmony_ci lock_buffer(bh); 1638c2ecf20Sopenharmony_ci ret = (hdr->h_checksum == ext4_xattr_block_csum(inode, 1648c2ecf20Sopenharmony_ci bh->b_blocknr, hdr)); 1658c2ecf20Sopenharmony_ci unlock_buffer(bh); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void ext4_xattr_block_csum_set(struct inode *inode, 1718c2ecf20Sopenharmony_ci struct buffer_head *bh) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(inode->i_sb)) 1748c2ecf20Sopenharmony_ci BHDR(bh)->h_checksum = ext4_xattr_block_csum(inode, 1758c2ecf20Sopenharmony_ci bh->b_blocknr, BHDR(bh)); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic inline const struct xattr_handler * 1798c2ecf20Sopenharmony_ciext4_xattr_handler(int name_index) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci const struct xattr_handler *handler = NULL; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (name_index > 0 && name_index < ARRAY_SIZE(ext4_xattr_handler_map)) 1848c2ecf20Sopenharmony_ci handler = ext4_xattr_handler_map[name_index]; 1858c2ecf20Sopenharmony_ci return handler; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int 1898c2ecf20Sopenharmony_ciext4_xattr_check_entries(struct ext4_xattr_entry *entry, void *end, 1908c2ecf20Sopenharmony_ci void *value_start) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct ext4_xattr_entry *e = entry; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Find the end of the names list */ 1958c2ecf20Sopenharmony_ci while (!IS_LAST_ENTRY(e)) { 1968c2ecf20Sopenharmony_ci struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e); 1978c2ecf20Sopenharmony_ci if ((void *)next >= end) 1988c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 1998c2ecf20Sopenharmony_ci if (strnlen(e->e_name, e->e_name_len) != e->e_name_len) 2008c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 2018c2ecf20Sopenharmony_ci e = next; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Check the values */ 2058c2ecf20Sopenharmony_ci while (!IS_LAST_ENTRY(entry)) { 2068c2ecf20Sopenharmony_ci u32 size = le32_to_cpu(entry->e_value_size); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (size > EXT4_XATTR_SIZE_MAX) 2098c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (size != 0 && entry->e_value_inum == 0) { 2128c2ecf20Sopenharmony_ci u16 offs = le16_to_cpu(entry->e_value_offs); 2138c2ecf20Sopenharmony_ci void *value; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * The value cannot overlap the names, and the value 2178c2ecf20Sopenharmony_ci * with padding cannot extend beyond 'end'. Check both 2188c2ecf20Sopenharmony_ci * the padded and unpadded sizes, since the size may 2198c2ecf20Sopenharmony_ci * overflow to 0 when adding padding. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci if (offs > end - value_start) 2228c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 2238c2ecf20Sopenharmony_ci value = value_start + offs; 2248c2ecf20Sopenharmony_ci if (value < (void *)e + sizeof(u32) || 2258c2ecf20Sopenharmony_ci size > end - value || 2268c2ecf20Sopenharmony_ci EXT4_XATTR_SIZE(size) > end - value) 2278c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci entry = EXT4_XATTR_NEXT(entry); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic inline int 2368c2ecf20Sopenharmony_ci__ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh, 2378c2ecf20Sopenharmony_ci const char *function, unsigned int line) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int error = -EFSCORRUPTED; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) || 2428c2ecf20Sopenharmony_ci BHDR(bh)->h_blocks != cpu_to_le32(1)) 2438c2ecf20Sopenharmony_ci goto errout; 2448c2ecf20Sopenharmony_ci if (buffer_verified(bh)) 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci error = -EFSBADCRC; 2488c2ecf20Sopenharmony_ci if (!ext4_xattr_block_csum_verify(inode, bh)) 2498c2ecf20Sopenharmony_ci goto errout; 2508c2ecf20Sopenharmony_ci error = ext4_xattr_check_entries(BFIRST(bh), bh->b_data + bh->b_size, 2518c2ecf20Sopenharmony_ci bh->b_data); 2528c2ecf20Sopenharmony_cierrout: 2538c2ecf20Sopenharmony_ci if (error) 2548c2ecf20Sopenharmony_ci __ext4_error_inode(inode, function, line, 0, -error, 2558c2ecf20Sopenharmony_ci "corrupted xattr block %llu", 2568c2ecf20Sopenharmony_ci (unsigned long long) bh->b_blocknr); 2578c2ecf20Sopenharmony_ci else 2588c2ecf20Sopenharmony_ci set_buffer_verified(bh); 2598c2ecf20Sopenharmony_ci return error; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci#define ext4_xattr_check_block(inode, bh) \ 2638c2ecf20Sopenharmony_ci __ext4_xattr_check_block((inode), (bh), __func__, __LINE__) 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int 2678c2ecf20Sopenharmony_ci__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header, 2688c2ecf20Sopenharmony_ci void *end, const char *function, unsigned int line) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci int error = -EFSCORRUPTED; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (end - (void *)header < sizeof(*header) + sizeof(u32) || 2738c2ecf20Sopenharmony_ci (header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC))) 2748c2ecf20Sopenharmony_ci goto errout; 2758c2ecf20Sopenharmony_ci error = ext4_xattr_check_entries(IFIRST(header), end, IFIRST(header)); 2768c2ecf20Sopenharmony_cierrout: 2778c2ecf20Sopenharmony_ci if (error) 2788c2ecf20Sopenharmony_ci __ext4_error_inode(inode, function, line, 0, -error, 2798c2ecf20Sopenharmony_ci "corrupted in-inode xattr"); 2808c2ecf20Sopenharmony_ci return error; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci#define xattr_check_inode(inode, header, end) \ 2848c2ecf20Sopenharmony_ci __xattr_check_inode((inode), (header), (end), __func__, __LINE__) 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int 2878c2ecf20Sopenharmony_cixattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry, 2888c2ecf20Sopenharmony_ci void *end, int name_index, const char *name, int sorted) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry, *next; 2918c2ecf20Sopenharmony_ci size_t name_len; 2928c2ecf20Sopenharmony_ci int cmp = 1; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (name == NULL) 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci name_len = strlen(name); 2978c2ecf20Sopenharmony_ci for (entry = *pentry; !IS_LAST_ENTRY(entry); entry = next) { 2988c2ecf20Sopenharmony_ci next = EXT4_XATTR_NEXT(entry); 2998c2ecf20Sopenharmony_ci if ((void *) next >= end) { 3008c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "corrupted xattr entries"); 3018c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci cmp = name_index - entry->e_name_index; 3048c2ecf20Sopenharmony_ci if (!cmp) 3058c2ecf20Sopenharmony_ci cmp = name_len - entry->e_name_len; 3068c2ecf20Sopenharmony_ci if (!cmp) 3078c2ecf20Sopenharmony_ci cmp = memcmp(name, entry->e_name, name_len); 3088c2ecf20Sopenharmony_ci if (cmp <= 0 && (sorted || cmp == 0)) 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci *pentry = entry; 3128c2ecf20Sopenharmony_ci return cmp ? -ENODATA : 0; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic u32 3168c2ecf20Sopenharmony_ciext4_xattr_inode_hash(struct ext4_sb_info *sbi, const void *buffer, size_t size) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci return ext4_chksum(sbi, sbi->s_csum_seed, buffer, size); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic u64 ext4_xattr_inode_get_ref(struct inode *ea_inode) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci return ((u64)ea_inode->i_ctime.tv_sec << 32) | 3248c2ecf20Sopenharmony_ci (u32) inode_peek_iversion_raw(ea_inode); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic void ext4_xattr_inode_set_ref(struct inode *ea_inode, u64 ref_count) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci ea_inode->i_ctime.tv_sec = (u32)(ref_count >> 32); 3308c2ecf20Sopenharmony_ci inode_set_iversion_raw(ea_inode, ref_count & 0xffffffff); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic u32 ext4_xattr_inode_get_hash(struct inode *ea_inode) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci return (u32)ea_inode->i_atime.tv_sec; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void ext4_xattr_inode_set_hash(struct inode *ea_inode, u32 hash) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci ea_inode->i_atime.tv_sec = hash; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* 3448c2ecf20Sopenharmony_ci * Read the EA value from an inode. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci int blocksize = 1 << ea_inode->i_blkbits; 3498c2ecf20Sopenharmony_ci int bh_count = (size + blocksize - 1) >> ea_inode->i_blkbits; 3508c2ecf20Sopenharmony_ci int tail_size = (size % blocksize) ?: blocksize; 3518c2ecf20Sopenharmony_ci struct buffer_head *bhs_inline[8]; 3528c2ecf20Sopenharmony_ci struct buffer_head **bhs = bhs_inline; 3538c2ecf20Sopenharmony_ci int i, ret; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (bh_count > ARRAY_SIZE(bhs_inline)) { 3568c2ecf20Sopenharmony_ci bhs = kmalloc_array(bh_count, sizeof(*bhs), GFP_NOFS); 3578c2ecf20Sopenharmony_ci if (!bhs) 3588c2ecf20Sopenharmony_ci return -ENOMEM; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci ret = ext4_bread_batch(ea_inode, 0 /* block */, bh_count, 3628c2ecf20Sopenharmony_ci true /* wait */, bhs); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci goto free_bhs; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (i = 0; i < bh_count; i++) { 3678c2ecf20Sopenharmony_ci /* There shouldn't be any holes in ea_inode. */ 3688c2ecf20Sopenharmony_ci if (!bhs[i]) { 3698c2ecf20Sopenharmony_ci ret = -EFSCORRUPTED; 3708c2ecf20Sopenharmony_ci goto put_bhs; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci memcpy((char *)buf + blocksize * i, bhs[i]->b_data, 3738c2ecf20Sopenharmony_ci i < bh_count - 1 ? blocksize : tail_size); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci ret = 0; 3768c2ecf20Sopenharmony_ciput_bhs: 3778c2ecf20Sopenharmony_ci for (i = 0; i < bh_count; i++) 3788c2ecf20Sopenharmony_ci brelse(bhs[i]); 3798c2ecf20Sopenharmony_cifree_bhs: 3808c2ecf20Sopenharmony_ci if (bhs != bhs_inline) 3818c2ecf20Sopenharmony_ci kfree(bhs); 3828c2ecf20Sopenharmony_ci return ret; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci#define EXT4_XATTR_INODE_GET_PARENT(inode) ((__u32)(inode)->i_mtime.tv_sec) 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, 3888c2ecf20Sopenharmony_ci u32 ea_inode_hash, struct inode **ea_inode) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct inode *inode; 3918c2ecf20Sopenharmony_ci int err; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * We have to check for this corruption early as otherwise 3958c2ecf20Sopenharmony_ci * iget_locked() could wait indefinitely for the state of our 3968c2ecf20Sopenharmony_ci * parent inode. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci if (parent->i_ino == ea_ino) { 3998c2ecf20Sopenharmony_ci ext4_error(parent->i_sb, 4008c2ecf20Sopenharmony_ci "Parent and EA inode have the same ino %lu", ea_ino); 4018c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci inode = ext4_iget(parent->i_sb, ea_ino, EXT4_IGET_EA_INODE); 4058c2ecf20Sopenharmony_ci if (IS_ERR(inode)) { 4068c2ecf20Sopenharmony_ci err = PTR_ERR(inode); 4078c2ecf20Sopenharmony_ci ext4_error(parent->i_sb, 4088c2ecf20Sopenharmony_ci "error while reading EA inode %lu err=%d", ea_ino, 4098c2ecf20Sopenharmony_ci err); 4108c2ecf20Sopenharmony_ci return err; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci ext4_xattr_inode_set_class(inode); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * Check whether this is an old Lustre-style xattr inode. Lustre 4168c2ecf20Sopenharmony_ci * implementation does not have hash validation, rather it has a 4178c2ecf20Sopenharmony_ci * backpointer from ea_inode to the parent inode. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci if (ea_inode_hash != ext4_xattr_inode_get_hash(inode) && 4208c2ecf20Sopenharmony_ci EXT4_XATTR_INODE_GET_PARENT(inode) == parent->i_ino && 4218c2ecf20Sopenharmony_ci inode->i_generation == parent->i_generation) { 4228c2ecf20Sopenharmony_ci ext4_set_inode_state(inode, EXT4_STATE_LUSTRE_EA_INODE); 4238c2ecf20Sopenharmony_ci ext4_xattr_inode_set_ref(inode, 1); 4248c2ecf20Sopenharmony_ci } else { 4258c2ecf20Sopenharmony_ci inode_lock(inode); 4268c2ecf20Sopenharmony_ci inode->i_flags |= S_NOQUOTA; 4278c2ecf20Sopenharmony_ci inode_unlock(inode); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci *ea_inode = inode; 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/* Remove entry from mbcache when EA inode is getting evicted */ 4358c2ecf20Sopenharmony_civoid ext4_evict_ea_inode(struct inode *inode) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct mb_cache_entry *oe; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (!EA_INODE_CACHE(inode)) 4408c2ecf20Sopenharmony_ci return; 4418c2ecf20Sopenharmony_ci /* Wait for entry to get unused so that we can remove it */ 4428c2ecf20Sopenharmony_ci while ((oe = mb_cache_entry_delete_or_get(EA_INODE_CACHE(inode), 4438c2ecf20Sopenharmony_ci ext4_xattr_inode_get_hash(inode), inode->i_ino))) { 4448c2ecf20Sopenharmony_ci mb_cache_entry_wait_unused(oe); 4458c2ecf20Sopenharmony_ci mb_cache_entry_put(EA_INODE_CACHE(inode), oe); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int 4508c2ecf20Sopenharmony_ciext4_xattr_inode_verify_hashes(struct inode *ea_inode, 4518c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry, void *buffer, 4528c2ecf20Sopenharmony_ci size_t size) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci u32 hash; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Verify stored hash matches calculated hash. */ 4578c2ecf20Sopenharmony_ci hash = ext4_xattr_inode_hash(EXT4_SB(ea_inode->i_sb), buffer, size); 4588c2ecf20Sopenharmony_ci if (hash != ext4_xattr_inode_get_hash(ea_inode)) 4598c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (entry) { 4628c2ecf20Sopenharmony_ci __le32 e_hash, tmp_data; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* Verify entry hash. */ 4658c2ecf20Sopenharmony_ci tmp_data = cpu_to_le32(hash); 4668c2ecf20Sopenharmony_ci e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len, 4678c2ecf20Sopenharmony_ci &tmp_data, 1); 4688c2ecf20Sopenharmony_ci if (e_hash != entry->e_hash) 4698c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/* 4758c2ecf20Sopenharmony_ci * Read xattr value from the EA inode. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_cistatic int 4788c2ecf20Sopenharmony_ciext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry, 4798c2ecf20Sopenharmony_ci void *buffer, size_t size) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct mb_cache *ea_inode_cache = EA_INODE_CACHE(inode); 4828c2ecf20Sopenharmony_ci struct inode *ea_inode; 4838c2ecf20Sopenharmony_ci int err; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci err = ext4_xattr_inode_iget(inode, le32_to_cpu(entry->e_value_inum), 4868c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_hash), &ea_inode); 4878c2ecf20Sopenharmony_ci if (err) { 4888c2ecf20Sopenharmony_ci ea_inode = NULL; 4898c2ecf20Sopenharmony_ci goto out; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (i_size_read(ea_inode) != size) { 4938c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, 4948c2ecf20Sopenharmony_ci "ea_inode file size=%llu entry size=%zu", 4958c2ecf20Sopenharmony_ci i_size_read(ea_inode), size); 4968c2ecf20Sopenharmony_ci err = -EFSCORRUPTED; 4978c2ecf20Sopenharmony_ci goto out; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci err = ext4_xattr_inode_read(ea_inode, buffer, size); 5018c2ecf20Sopenharmony_ci if (err) 5028c2ecf20Sopenharmony_ci goto out; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (!ext4_test_inode_state(ea_inode, EXT4_STATE_LUSTRE_EA_INODE)) { 5058c2ecf20Sopenharmony_ci err = ext4_xattr_inode_verify_hashes(ea_inode, entry, buffer, 5068c2ecf20Sopenharmony_ci size); 5078c2ecf20Sopenharmony_ci if (err) { 5088c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, 5098c2ecf20Sopenharmony_ci "EA inode hash validation failed"); 5108c2ecf20Sopenharmony_ci goto out; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (ea_inode_cache) 5148c2ecf20Sopenharmony_ci mb_cache_entry_create(ea_inode_cache, GFP_NOFS, 5158c2ecf20Sopenharmony_ci ext4_xattr_inode_get_hash(ea_inode), 5168c2ecf20Sopenharmony_ci ea_inode->i_ino, true /* reusable */); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ciout: 5198c2ecf20Sopenharmony_ci iput(ea_inode); 5208c2ecf20Sopenharmony_ci return err; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic int 5248c2ecf20Sopenharmony_ciext4_xattr_block_get(struct inode *inode, int name_index, const char *name, 5258c2ecf20Sopenharmony_ci void *buffer, size_t buffer_size) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 5288c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry; 5298c2ecf20Sopenharmony_ci size_t size; 5308c2ecf20Sopenharmony_ci void *end; 5318c2ecf20Sopenharmony_ci int error; 5328c2ecf20Sopenharmony_ci struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", 5358c2ecf20Sopenharmony_ci name_index, name, buffer, (long)buffer_size); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (!EXT4_I(inode)->i_file_acl) 5388c2ecf20Sopenharmony_ci return -ENODATA; 5398c2ecf20Sopenharmony_ci ea_idebug(inode, "reading block %llu", 5408c2ecf20Sopenharmony_ci (unsigned long long)EXT4_I(inode)->i_file_acl); 5418c2ecf20Sopenharmony_ci bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); 5428c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 5438c2ecf20Sopenharmony_ci return PTR_ERR(bh); 5448c2ecf20Sopenharmony_ci ea_bdebug(bh, "b_count=%d, refcount=%d", 5458c2ecf20Sopenharmony_ci atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); 5468c2ecf20Sopenharmony_ci error = ext4_xattr_check_block(inode, bh); 5478c2ecf20Sopenharmony_ci if (error) 5488c2ecf20Sopenharmony_ci goto cleanup; 5498c2ecf20Sopenharmony_ci ext4_xattr_block_cache_insert(ea_block_cache, bh); 5508c2ecf20Sopenharmony_ci entry = BFIRST(bh); 5518c2ecf20Sopenharmony_ci end = bh->b_data + bh->b_size; 5528c2ecf20Sopenharmony_ci error = xattr_find_entry(inode, &entry, end, name_index, name, 1); 5538c2ecf20Sopenharmony_ci if (error) 5548c2ecf20Sopenharmony_ci goto cleanup; 5558c2ecf20Sopenharmony_ci size = le32_to_cpu(entry->e_value_size); 5568c2ecf20Sopenharmony_ci error = -ERANGE; 5578c2ecf20Sopenharmony_ci if (unlikely(size > EXT4_XATTR_SIZE_MAX)) 5588c2ecf20Sopenharmony_ci goto cleanup; 5598c2ecf20Sopenharmony_ci if (buffer) { 5608c2ecf20Sopenharmony_ci if (size > buffer_size) 5618c2ecf20Sopenharmony_ci goto cleanup; 5628c2ecf20Sopenharmony_ci if (entry->e_value_inum) { 5638c2ecf20Sopenharmony_ci error = ext4_xattr_inode_get(inode, entry, buffer, 5648c2ecf20Sopenharmony_ci size); 5658c2ecf20Sopenharmony_ci if (error) 5668c2ecf20Sopenharmony_ci goto cleanup; 5678c2ecf20Sopenharmony_ci } else { 5688c2ecf20Sopenharmony_ci u16 offset = le16_to_cpu(entry->e_value_offs); 5698c2ecf20Sopenharmony_ci void *p = bh->b_data + offset; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (unlikely(p + size > end)) 5728c2ecf20Sopenharmony_ci goto cleanup; 5738c2ecf20Sopenharmony_ci memcpy(buffer, p, size); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci error = size; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cicleanup: 5798c2ecf20Sopenharmony_ci brelse(bh); 5808c2ecf20Sopenharmony_ci return error; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ciint 5848c2ecf20Sopenharmony_ciext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name, 5858c2ecf20Sopenharmony_ci void *buffer, size_t buffer_size) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header; 5888c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry; 5898c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode; 5908c2ecf20Sopenharmony_ci struct ext4_iloc iloc; 5918c2ecf20Sopenharmony_ci size_t size; 5928c2ecf20Sopenharmony_ci void *end; 5938c2ecf20Sopenharmony_ci int error; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR)) 5968c2ecf20Sopenharmony_ci return -ENODATA; 5978c2ecf20Sopenharmony_ci error = ext4_get_inode_loc(inode, &iloc); 5988c2ecf20Sopenharmony_ci if (error) 5998c2ecf20Sopenharmony_ci return error; 6008c2ecf20Sopenharmony_ci raw_inode = ext4_raw_inode(&iloc); 6018c2ecf20Sopenharmony_ci header = IHDR(inode, raw_inode); 6028c2ecf20Sopenharmony_ci end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; 6038c2ecf20Sopenharmony_ci error = xattr_check_inode(inode, header, end); 6048c2ecf20Sopenharmony_ci if (error) 6058c2ecf20Sopenharmony_ci goto cleanup; 6068c2ecf20Sopenharmony_ci entry = IFIRST(header); 6078c2ecf20Sopenharmony_ci error = xattr_find_entry(inode, &entry, end, name_index, name, 0); 6088c2ecf20Sopenharmony_ci if (error) 6098c2ecf20Sopenharmony_ci goto cleanup; 6108c2ecf20Sopenharmony_ci size = le32_to_cpu(entry->e_value_size); 6118c2ecf20Sopenharmony_ci error = -ERANGE; 6128c2ecf20Sopenharmony_ci if (unlikely(size > EXT4_XATTR_SIZE_MAX)) 6138c2ecf20Sopenharmony_ci goto cleanup; 6148c2ecf20Sopenharmony_ci if (buffer) { 6158c2ecf20Sopenharmony_ci if (size > buffer_size) 6168c2ecf20Sopenharmony_ci goto cleanup; 6178c2ecf20Sopenharmony_ci if (entry->e_value_inum) { 6188c2ecf20Sopenharmony_ci error = ext4_xattr_inode_get(inode, entry, buffer, 6198c2ecf20Sopenharmony_ci size); 6208c2ecf20Sopenharmony_ci if (error) 6218c2ecf20Sopenharmony_ci goto cleanup; 6228c2ecf20Sopenharmony_ci } else { 6238c2ecf20Sopenharmony_ci u16 offset = le16_to_cpu(entry->e_value_offs); 6248c2ecf20Sopenharmony_ci void *p = (void *)IFIRST(header) + offset; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (unlikely(p + size > end)) 6278c2ecf20Sopenharmony_ci goto cleanup; 6288c2ecf20Sopenharmony_ci memcpy(buffer, p, size); 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci error = size; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cicleanup: 6348c2ecf20Sopenharmony_ci brelse(iloc.bh); 6358c2ecf20Sopenharmony_ci return error; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci/* 6398c2ecf20Sopenharmony_ci * ext4_xattr_get() 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * Copy an extended attribute into the buffer 6428c2ecf20Sopenharmony_ci * provided, or compute the buffer size required. 6438c2ecf20Sopenharmony_ci * Buffer is NULL to compute the size of the buffer required. 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * Returns a negative error number on failure, or the number of bytes 6468c2ecf20Sopenharmony_ci * used / required on success. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ciint 6498c2ecf20Sopenharmony_ciext4_xattr_get(struct inode *inode, int name_index, const char *name, 6508c2ecf20Sopenharmony_ci void *buffer, size_t buffer_size) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci int error; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) 6558c2ecf20Sopenharmony_ci return -EIO; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (strlen(name) > 255) 6588c2ecf20Sopenharmony_ci return -ERANGE; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci down_read(&EXT4_I(inode)->xattr_sem); 6618c2ecf20Sopenharmony_ci error = ext4_xattr_ibody_get(inode, name_index, name, buffer, 6628c2ecf20Sopenharmony_ci buffer_size); 6638c2ecf20Sopenharmony_ci if (error == -ENODATA) 6648c2ecf20Sopenharmony_ci error = ext4_xattr_block_get(inode, name_index, name, buffer, 6658c2ecf20Sopenharmony_ci buffer_size); 6668c2ecf20Sopenharmony_ci up_read(&EXT4_I(inode)->xattr_sem); 6678c2ecf20Sopenharmony_ci return error; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic int 6718c2ecf20Sopenharmony_ciext4_xattr_list_entries(struct dentry *dentry, struct ext4_xattr_entry *entry, 6728c2ecf20Sopenharmony_ci char *buffer, size_t buffer_size) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci size_t rest = buffer_size; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { 6778c2ecf20Sopenharmony_ci const struct xattr_handler *handler = 6788c2ecf20Sopenharmony_ci ext4_xattr_handler(entry->e_name_index); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (handler && (!handler->list || handler->list(dentry))) { 6818c2ecf20Sopenharmony_ci const char *prefix = handler->prefix ?: handler->name; 6828c2ecf20Sopenharmony_ci size_t prefix_len = strlen(prefix); 6838c2ecf20Sopenharmony_ci size_t size = prefix_len + entry->e_name_len + 1; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (buffer) { 6868c2ecf20Sopenharmony_ci if (size > rest) 6878c2ecf20Sopenharmony_ci return -ERANGE; 6888c2ecf20Sopenharmony_ci memcpy(buffer, prefix, prefix_len); 6898c2ecf20Sopenharmony_ci buffer += prefix_len; 6908c2ecf20Sopenharmony_ci memcpy(buffer, entry->e_name, entry->e_name_len); 6918c2ecf20Sopenharmony_ci buffer += entry->e_name_len; 6928c2ecf20Sopenharmony_ci *buffer++ = 0; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci rest -= size; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci return buffer_size - rest; /* total size */ 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic int 7018c2ecf20Sopenharmony_ciext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct inode *inode = d_inode(dentry); 7048c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 7058c2ecf20Sopenharmony_ci int error; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci ea_idebug(inode, "buffer=%p, buffer_size=%ld", 7088c2ecf20Sopenharmony_ci buffer, (long)buffer_size); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (!EXT4_I(inode)->i_file_acl) 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci ea_idebug(inode, "reading block %llu", 7138c2ecf20Sopenharmony_ci (unsigned long long)EXT4_I(inode)->i_file_acl); 7148c2ecf20Sopenharmony_ci bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); 7158c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 7168c2ecf20Sopenharmony_ci return PTR_ERR(bh); 7178c2ecf20Sopenharmony_ci ea_bdebug(bh, "b_count=%d, refcount=%d", 7188c2ecf20Sopenharmony_ci atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); 7198c2ecf20Sopenharmony_ci error = ext4_xattr_check_block(inode, bh); 7208c2ecf20Sopenharmony_ci if (error) 7218c2ecf20Sopenharmony_ci goto cleanup; 7228c2ecf20Sopenharmony_ci ext4_xattr_block_cache_insert(EA_BLOCK_CACHE(inode), bh); 7238c2ecf20Sopenharmony_ci error = ext4_xattr_list_entries(dentry, BFIRST(bh), buffer, 7248c2ecf20Sopenharmony_ci buffer_size); 7258c2ecf20Sopenharmony_cicleanup: 7268c2ecf20Sopenharmony_ci brelse(bh); 7278c2ecf20Sopenharmony_ci return error; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int 7318c2ecf20Sopenharmony_ciext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct inode *inode = d_inode(dentry); 7348c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header; 7358c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode; 7368c2ecf20Sopenharmony_ci struct ext4_iloc iloc; 7378c2ecf20Sopenharmony_ci void *end; 7388c2ecf20Sopenharmony_ci int error; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR)) 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci error = ext4_get_inode_loc(inode, &iloc); 7438c2ecf20Sopenharmony_ci if (error) 7448c2ecf20Sopenharmony_ci return error; 7458c2ecf20Sopenharmony_ci raw_inode = ext4_raw_inode(&iloc); 7468c2ecf20Sopenharmony_ci header = IHDR(inode, raw_inode); 7478c2ecf20Sopenharmony_ci end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; 7488c2ecf20Sopenharmony_ci error = xattr_check_inode(inode, header, end); 7498c2ecf20Sopenharmony_ci if (error) 7508c2ecf20Sopenharmony_ci goto cleanup; 7518c2ecf20Sopenharmony_ci error = ext4_xattr_list_entries(dentry, IFIRST(header), 7528c2ecf20Sopenharmony_ci buffer, buffer_size); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cicleanup: 7558c2ecf20Sopenharmony_ci brelse(iloc.bh); 7568c2ecf20Sopenharmony_ci return error; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci/* 7608c2ecf20Sopenharmony_ci * Inode operation listxattr() 7618c2ecf20Sopenharmony_ci * 7628c2ecf20Sopenharmony_ci * d_inode(dentry)->i_rwsem: don't care 7638c2ecf20Sopenharmony_ci * 7648c2ecf20Sopenharmony_ci * Copy a list of attribute names into the buffer 7658c2ecf20Sopenharmony_ci * provided, or compute the buffer size required. 7668c2ecf20Sopenharmony_ci * Buffer is NULL to compute the size of the buffer required. 7678c2ecf20Sopenharmony_ci * 7688c2ecf20Sopenharmony_ci * Returns a negative error number on failure, or the number of bytes 7698c2ecf20Sopenharmony_ci * used / required on success. 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_cissize_t 7728c2ecf20Sopenharmony_ciext4_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci int ret, ret2; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci down_read(&EXT4_I(d_inode(dentry))->xattr_sem); 7778c2ecf20Sopenharmony_ci ret = ret2 = ext4_xattr_ibody_list(dentry, buffer, buffer_size); 7788c2ecf20Sopenharmony_ci if (ret < 0) 7798c2ecf20Sopenharmony_ci goto errout; 7808c2ecf20Sopenharmony_ci if (buffer) { 7818c2ecf20Sopenharmony_ci buffer += ret; 7828c2ecf20Sopenharmony_ci buffer_size -= ret; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci ret = ext4_xattr_block_list(dentry, buffer, buffer_size); 7858c2ecf20Sopenharmony_ci if (ret < 0) 7868c2ecf20Sopenharmony_ci goto errout; 7878c2ecf20Sopenharmony_ci ret += ret2; 7888c2ecf20Sopenharmony_cierrout: 7898c2ecf20Sopenharmony_ci up_read(&EXT4_I(d_inode(dentry))->xattr_sem); 7908c2ecf20Sopenharmony_ci return ret; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci/* 7948c2ecf20Sopenharmony_ci * If the EXT4_FEATURE_COMPAT_EXT_ATTR feature of this file system is 7958c2ecf20Sopenharmony_ci * not set, set it. 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_cistatic void ext4_xattr_update_super_block(handle_t *handle, 7988c2ecf20Sopenharmony_ci struct super_block *sb) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci if (ext4_has_feature_xattr(sb)) 8018c2ecf20Sopenharmony_ci return; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access"); 8048c2ecf20Sopenharmony_ci if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) { 8058c2ecf20Sopenharmony_ci lock_buffer(EXT4_SB(sb)->s_sbh); 8068c2ecf20Sopenharmony_ci ext4_set_feature_xattr(sb); 8078c2ecf20Sopenharmony_ci ext4_superblock_csum_set(sb); 8088c2ecf20Sopenharmony_ci unlock_buffer(EXT4_SB(sb)->s_sbh); 8098c2ecf20Sopenharmony_ci ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ciint ext4_get_inode_usage(struct inode *inode, qsize_t *usage) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct ext4_iloc iloc = { .bh = NULL }; 8168c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 8178c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode; 8188c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header; 8198c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry; 8208c2ecf20Sopenharmony_ci qsize_t ea_inode_refs = 0; 8218c2ecf20Sopenharmony_ci void *end; 8228c2ecf20Sopenharmony_ci int ret; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { 8278c2ecf20Sopenharmony_ci ret = ext4_get_inode_loc(inode, &iloc); 8288c2ecf20Sopenharmony_ci if (ret) 8298c2ecf20Sopenharmony_ci goto out; 8308c2ecf20Sopenharmony_ci raw_inode = ext4_raw_inode(&iloc); 8318c2ecf20Sopenharmony_ci header = IHDR(inode, raw_inode); 8328c2ecf20Sopenharmony_ci end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; 8338c2ecf20Sopenharmony_ci ret = xattr_check_inode(inode, header, end); 8348c2ecf20Sopenharmony_ci if (ret) 8358c2ecf20Sopenharmony_ci goto out; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci for (entry = IFIRST(header); !IS_LAST_ENTRY(entry); 8388c2ecf20Sopenharmony_ci entry = EXT4_XATTR_NEXT(entry)) 8398c2ecf20Sopenharmony_ci if (entry->e_value_inum) 8408c2ecf20Sopenharmony_ci ea_inode_refs++; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (EXT4_I(inode)->i_file_acl) { 8448c2ecf20Sopenharmony_ci bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); 8458c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 8468c2ecf20Sopenharmony_ci ret = PTR_ERR(bh); 8478c2ecf20Sopenharmony_ci bh = NULL; 8488c2ecf20Sopenharmony_ci goto out; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = ext4_xattr_check_block(inode, bh); 8528c2ecf20Sopenharmony_ci if (ret) 8538c2ecf20Sopenharmony_ci goto out; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry); 8568c2ecf20Sopenharmony_ci entry = EXT4_XATTR_NEXT(entry)) 8578c2ecf20Sopenharmony_ci if (entry->e_value_inum) 8588c2ecf20Sopenharmony_ci ea_inode_refs++; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci *usage = ea_inode_refs + 1; 8618c2ecf20Sopenharmony_ci ret = 0; 8628c2ecf20Sopenharmony_ciout: 8638c2ecf20Sopenharmony_ci brelse(iloc.bh); 8648c2ecf20Sopenharmony_ci brelse(bh); 8658c2ecf20Sopenharmony_ci return ret; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic inline size_t round_up_cluster(struct inode *inode, size_t length) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 8718c2ecf20Sopenharmony_ci size_t cluster_size = 1 << (EXT4_SB(sb)->s_cluster_bits + 8728c2ecf20Sopenharmony_ci inode->i_blkbits); 8738c2ecf20Sopenharmony_ci size_t mask = ~(cluster_size - 1); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci return (length + cluster_size - 1) & mask; 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_alloc_quota(struct inode *inode, size_t len) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci int err; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci err = dquot_alloc_inode(inode); 8838c2ecf20Sopenharmony_ci if (err) 8848c2ecf20Sopenharmony_ci return err; 8858c2ecf20Sopenharmony_ci err = dquot_alloc_space_nodirty(inode, round_up_cluster(inode, len)); 8868c2ecf20Sopenharmony_ci if (err) 8878c2ecf20Sopenharmony_ci dquot_free_inode(inode); 8888c2ecf20Sopenharmony_ci return err; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic void ext4_xattr_inode_free_quota(struct inode *parent, 8928c2ecf20Sopenharmony_ci struct inode *ea_inode, 8938c2ecf20Sopenharmony_ci size_t len) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci if (ea_inode && 8968c2ecf20Sopenharmony_ci ext4_test_inode_state(ea_inode, EXT4_STATE_LUSTRE_EA_INODE)) 8978c2ecf20Sopenharmony_ci return; 8988c2ecf20Sopenharmony_ci dquot_free_space_nodirty(parent, round_up_cluster(parent, len)); 8998c2ecf20Sopenharmony_ci dquot_free_inode(parent); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ciint __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode, 9038c2ecf20Sopenharmony_ci struct buffer_head *block_bh, size_t value_len, 9048c2ecf20Sopenharmony_ci bool is_create) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci int credits; 9078c2ecf20Sopenharmony_ci int blocks; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* 9108c2ecf20Sopenharmony_ci * 1) Owner inode update 9118c2ecf20Sopenharmony_ci * 2) Ref count update on old xattr block 9128c2ecf20Sopenharmony_ci * 3) new xattr block 9138c2ecf20Sopenharmony_ci * 4) block bitmap update for new xattr block 9148c2ecf20Sopenharmony_ci * 5) group descriptor for new xattr block 9158c2ecf20Sopenharmony_ci * 6) block bitmap update for old xattr block 9168c2ecf20Sopenharmony_ci * 7) group descriptor for old block 9178c2ecf20Sopenharmony_ci * 9188c2ecf20Sopenharmony_ci * 6 & 7 can happen if we have two racing threads T_a and T_b 9198c2ecf20Sopenharmony_ci * which are each trying to set an xattr on inodes I_a and I_b 9208c2ecf20Sopenharmony_ci * which were both initially sharing an xattr block. 9218c2ecf20Sopenharmony_ci */ 9228c2ecf20Sopenharmony_ci credits = 7; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Quota updates. */ 9258c2ecf20Sopenharmony_ci credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(sb); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * In case of inline data, we may push out the data to a block, 9298c2ecf20Sopenharmony_ci * so we need to reserve credits for this eventuality 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci if (inode && ext4_has_inline_data(inode)) 9328c2ecf20Sopenharmony_ci credits += ext4_writepage_trans_blocks(inode) + 1; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* We are done if ea_inode feature is not enabled. */ 9358c2ecf20Sopenharmony_ci if (!ext4_has_feature_ea_inode(sb)) 9368c2ecf20Sopenharmony_ci return credits; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* New ea_inode, inode map, block bitmap, group descriptor. */ 9398c2ecf20Sopenharmony_ci credits += 4; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* Data blocks. */ 9428c2ecf20Sopenharmony_ci blocks = (value_len + sb->s_blocksize - 1) >> sb->s_blocksize_bits; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Indirection block or one level of extent tree. */ 9458c2ecf20Sopenharmony_ci blocks += 1; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* Block bitmap and group descriptor updates for each block. */ 9488c2ecf20Sopenharmony_ci credits += blocks * 2; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* Blocks themselves. */ 9518c2ecf20Sopenharmony_ci credits += blocks; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (!is_create) { 9548c2ecf20Sopenharmony_ci /* Dereference ea_inode holding old xattr value. 9558c2ecf20Sopenharmony_ci * Old ea_inode, inode map, block bitmap, group descriptor. 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_ci credits += 4; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci /* Data blocks for old ea_inode. */ 9608c2ecf20Sopenharmony_ci blocks = XATTR_SIZE_MAX >> sb->s_blocksize_bits; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* Indirection block or one level of extent tree for old 9638c2ecf20Sopenharmony_ci * ea_inode. 9648c2ecf20Sopenharmony_ci */ 9658c2ecf20Sopenharmony_ci blocks += 1; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* Block bitmap and group descriptor updates for each block. */ 9688c2ecf20Sopenharmony_ci credits += blocks * 2; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* We may need to clone the existing xattr block in which case we need 9728c2ecf20Sopenharmony_ci * to increment ref counts for existing ea_inodes referenced by it. 9738c2ecf20Sopenharmony_ci */ 9748c2ecf20Sopenharmony_ci if (block_bh) { 9758c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry = BFIRST(block_bh); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) 9788c2ecf20Sopenharmony_ci if (entry->e_value_inum) 9798c2ecf20Sopenharmony_ci /* Ref count update on ea_inode. */ 9808c2ecf20Sopenharmony_ci credits += 1; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci return credits; 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode, 9868c2ecf20Sopenharmony_ci int ref_change) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct ext4_iloc iloc; 9898c2ecf20Sopenharmony_ci s64 ref_count; 9908c2ecf20Sopenharmony_ci int ret; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci inode_lock(ea_inode); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci ret = ext4_reserve_inode_write(handle, ea_inode, &iloc); 9958c2ecf20Sopenharmony_ci if (ret) 9968c2ecf20Sopenharmony_ci goto out; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci ref_count = ext4_xattr_inode_get_ref(ea_inode); 9998c2ecf20Sopenharmony_ci ref_count += ref_change; 10008c2ecf20Sopenharmony_ci ext4_xattr_inode_set_ref(ea_inode, ref_count); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (ref_change > 0) { 10038c2ecf20Sopenharmony_ci WARN_ONCE(ref_count <= 0, "EA inode %lu ref_count=%lld", 10048c2ecf20Sopenharmony_ci ea_inode->i_ino, ref_count); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (ref_count == 1) { 10078c2ecf20Sopenharmony_ci WARN_ONCE(ea_inode->i_nlink, "EA inode %lu i_nlink=%u", 10088c2ecf20Sopenharmony_ci ea_inode->i_ino, ea_inode->i_nlink); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci set_nlink(ea_inode, 1); 10118c2ecf20Sopenharmony_ci ext4_orphan_del(handle, ea_inode); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci } else { 10148c2ecf20Sopenharmony_ci WARN_ONCE(ref_count < 0, "EA inode %lu ref_count=%lld", 10158c2ecf20Sopenharmony_ci ea_inode->i_ino, ref_count); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (ref_count == 0) { 10188c2ecf20Sopenharmony_ci WARN_ONCE(ea_inode->i_nlink != 1, 10198c2ecf20Sopenharmony_ci "EA inode %lu i_nlink=%u", 10208c2ecf20Sopenharmony_ci ea_inode->i_ino, ea_inode->i_nlink); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci clear_nlink(ea_inode); 10238c2ecf20Sopenharmony_ci ext4_orphan_add(handle, ea_inode); 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci ret = ext4_mark_iloc_dirty(handle, ea_inode, &iloc); 10288c2ecf20Sopenharmony_ci if (ret) 10298c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, 10308c2ecf20Sopenharmony_ci "ext4_mark_iloc_dirty() failed ret=%d", ret); 10318c2ecf20Sopenharmony_ciout: 10328c2ecf20Sopenharmony_ci inode_unlock(ea_inode); 10338c2ecf20Sopenharmony_ci return ret; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_inc_ref(handle_t *handle, struct inode *ea_inode) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci return ext4_xattr_inode_update_ref(handle, ea_inode, 1); 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_dec_ref(handle_t *handle, struct inode *ea_inode) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci return ext4_xattr_inode_update_ref(handle, ea_inode, -1); 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_inc_ref_all(handle_t *handle, struct inode *parent, 10478c2ecf20Sopenharmony_ci struct ext4_xattr_entry *first) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct inode *ea_inode; 10508c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry; 10518c2ecf20Sopenharmony_ci struct ext4_xattr_entry *failed_entry; 10528c2ecf20Sopenharmony_ci unsigned int ea_ino; 10538c2ecf20Sopenharmony_ci int err, saved_err; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci for (entry = first; !IS_LAST_ENTRY(entry); 10568c2ecf20Sopenharmony_ci entry = EXT4_XATTR_NEXT(entry)) { 10578c2ecf20Sopenharmony_ci if (!entry->e_value_inum) 10588c2ecf20Sopenharmony_ci continue; 10598c2ecf20Sopenharmony_ci ea_ino = le32_to_cpu(entry->e_value_inum); 10608c2ecf20Sopenharmony_ci err = ext4_xattr_inode_iget(parent, ea_ino, 10618c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_hash), 10628c2ecf20Sopenharmony_ci &ea_inode); 10638c2ecf20Sopenharmony_ci if (err) 10648c2ecf20Sopenharmony_ci goto cleanup; 10658c2ecf20Sopenharmony_ci err = ext4_xattr_inode_inc_ref(handle, ea_inode); 10668c2ecf20Sopenharmony_ci if (err) { 10678c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, "inc ref error %d", err); 10688c2ecf20Sopenharmony_ci iput(ea_inode); 10698c2ecf20Sopenharmony_ci goto cleanup; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci iput(ea_inode); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci return 0; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cicleanup: 10768c2ecf20Sopenharmony_ci saved_err = err; 10778c2ecf20Sopenharmony_ci failed_entry = entry; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci for (entry = first; entry != failed_entry; 10808c2ecf20Sopenharmony_ci entry = EXT4_XATTR_NEXT(entry)) { 10818c2ecf20Sopenharmony_ci if (!entry->e_value_inum) 10828c2ecf20Sopenharmony_ci continue; 10838c2ecf20Sopenharmony_ci ea_ino = le32_to_cpu(entry->e_value_inum); 10848c2ecf20Sopenharmony_ci err = ext4_xattr_inode_iget(parent, ea_ino, 10858c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_hash), 10868c2ecf20Sopenharmony_ci &ea_inode); 10878c2ecf20Sopenharmony_ci if (err) { 10888c2ecf20Sopenharmony_ci ext4_warning(parent->i_sb, 10898c2ecf20Sopenharmony_ci "cleanup ea_ino %u iget error %d", ea_ino, 10908c2ecf20Sopenharmony_ci err); 10918c2ecf20Sopenharmony_ci continue; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci err = ext4_xattr_inode_dec_ref(handle, ea_inode); 10948c2ecf20Sopenharmony_ci if (err) 10958c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, "cleanup dec ref error %d", 10968c2ecf20Sopenharmony_ci err); 10978c2ecf20Sopenharmony_ci iput(ea_inode); 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci return saved_err; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int ext4_xattr_restart_fn(handle_t *handle, struct inode *inode, 11038c2ecf20Sopenharmony_ci struct buffer_head *bh, bool block_csum, bool dirty) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci int error; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (bh && dirty) { 11088c2ecf20Sopenharmony_ci if (block_csum) 11098c2ecf20Sopenharmony_ci ext4_xattr_block_csum_set(inode, bh); 11108c2ecf20Sopenharmony_ci error = ext4_handle_dirty_metadata(handle, NULL, bh); 11118c2ecf20Sopenharmony_ci if (error) { 11128c2ecf20Sopenharmony_ci ext4_warning(inode->i_sb, "Handle metadata (error %d)", 11138c2ecf20Sopenharmony_ci error); 11148c2ecf20Sopenharmony_ci return error; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci return 0; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_cistatic void 11218c2ecf20Sopenharmony_ciext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent, 11228c2ecf20Sopenharmony_ci struct buffer_head *bh, 11238c2ecf20Sopenharmony_ci struct ext4_xattr_entry *first, bool block_csum, 11248c2ecf20Sopenharmony_ci struct ext4_xattr_inode_array **ea_inode_array, 11258c2ecf20Sopenharmony_ci int extra_credits, bool skip_quota) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct inode *ea_inode; 11288c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry; 11298c2ecf20Sopenharmony_ci bool dirty = false; 11308c2ecf20Sopenharmony_ci unsigned int ea_ino; 11318c2ecf20Sopenharmony_ci int err; 11328c2ecf20Sopenharmony_ci int credits; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* One credit for dec ref on ea_inode, one for orphan list addition, */ 11358c2ecf20Sopenharmony_ci credits = 2 + extra_credits; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci for (entry = first; !IS_LAST_ENTRY(entry); 11388c2ecf20Sopenharmony_ci entry = EXT4_XATTR_NEXT(entry)) { 11398c2ecf20Sopenharmony_ci if (!entry->e_value_inum) 11408c2ecf20Sopenharmony_ci continue; 11418c2ecf20Sopenharmony_ci ea_ino = le32_to_cpu(entry->e_value_inum); 11428c2ecf20Sopenharmony_ci err = ext4_xattr_inode_iget(parent, ea_ino, 11438c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_hash), 11448c2ecf20Sopenharmony_ci &ea_inode); 11458c2ecf20Sopenharmony_ci if (err) 11468c2ecf20Sopenharmony_ci continue; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci err = ext4_expand_inode_array(ea_inode_array, ea_inode); 11498c2ecf20Sopenharmony_ci if (err) { 11508c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, 11518c2ecf20Sopenharmony_ci "Expand inode array err=%d", err); 11528c2ecf20Sopenharmony_ci iput(ea_inode); 11538c2ecf20Sopenharmony_ci continue; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci err = ext4_journal_ensure_credits_fn(handle, credits, credits, 11578c2ecf20Sopenharmony_ci ext4_free_metadata_revoke_credits(parent->i_sb, 1), 11588c2ecf20Sopenharmony_ci ext4_xattr_restart_fn(handle, parent, bh, block_csum, 11598c2ecf20Sopenharmony_ci dirty)); 11608c2ecf20Sopenharmony_ci if (err < 0) { 11618c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, "Ensure credits err=%d", 11628c2ecf20Sopenharmony_ci err); 11638c2ecf20Sopenharmony_ci continue; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci if (err > 0) { 11668c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, bh); 11678c2ecf20Sopenharmony_ci if (err) { 11688c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, 11698c2ecf20Sopenharmony_ci "Re-get write access err=%d", 11708c2ecf20Sopenharmony_ci err); 11718c2ecf20Sopenharmony_ci continue; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci err = ext4_xattr_inode_dec_ref(handle, ea_inode); 11768c2ecf20Sopenharmony_ci if (err) { 11778c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, "ea_inode dec ref err=%d", 11788c2ecf20Sopenharmony_ci err); 11798c2ecf20Sopenharmony_ci continue; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (!skip_quota) 11838c2ecf20Sopenharmony_ci ext4_xattr_inode_free_quota(parent, ea_inode, 11848c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_value_size)); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* 11878c2ecf20Sopenharmony_ci * Forget about ea_inode within the same transaction that 11888c2ecf20Sopenharmony_ci * decrements the ref count. This avoids duplicate decrements in 11898c2ecf20Sopenharmony_ci * case the rest of the work spills over to subsequent 11908c2ecf20Sopenharmony_ci * transactions. 11918c2ecf20Sopenharmony_ci */ 11928c2ecf20Sopenharmony_ci entry->e_value_inum = 0; 11938c2ecf20Sopenharmony_ci entry->e_value_size = 0; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci dirty = true; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if (dirty) { 11998c2ecf20Sopenharmony_ci /* 12008c2ecf20Sopenharmony_ci * Note that we are deliberately skipping csum calculation for 12018c2ecf20Sopenharmony_ci * the final update because we do not expect any journal 12028c2ecf20Sopenharmony_ci * restarts until xattr block is freed. 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, bh); 12068c2ecf20Sopenharmony_ci if (err) 12078c2ecf20Sopenharmony_ci ext4_warning_inode(parent, 12088c2ecf20Sopenharmony_ci "handle dirty metadata err=%d", err); 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci/* 12138c2ecf20Sopenharmony_ci * Release the xattr block BH: If the reference count is > 1, decrement it; 12148c2ecf20Sopenharmony_ci * otherwise free the block. 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_cistatic void 12178c2ecf20Sopenharmony_ciext4_xattr_release_block(handle_t *handle, struct inode *inode, 12188c2ecf20Sopenharmony_ci struct buffer_head *bh, 12198c2ecf20Sopenharmony_ci struct ext4_xattr_inode_array **ea_inode_array, 12208c2ecf20Sopenharmony_ci int extra_credits) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode); 12238c2ecf20Sopenharmony_ci u32 hash, ref; 12248c2ecf20Sopenharmony_ci int error = 0; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 12278c2ecf20Sopenharmony_ci error = ext4_journal_get_write_access(handle, bh); 12288c2ecf20Sopenharmony_ci if (error) 12298c2ecf20Sopenharmony_ci goto out; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ciretry_ref: 12328c2ecf20Sopenharmony_ci lock_buffer(bh); 12338c2ecf20Sopenharmony_ci hash = le32_to_cpu(BHDR(bh)->h_hash); 12348c2ecf20Sopenharmony_ci ref = le32_to_cpu(BHDR(bh)->h_refcount); 12358c2ecf20Sopenharmony_ci if (ref == 1) { 12368c2ecf20Sopenharmony_ci ea_bdebug(bh, "refcount now=0; freeing"); 12378c2ecf20Sopenharmony_ci /* 12388c2ecf20Sopenharmony_ci * This must happen under buffer lock for 12398c2ecf20Sopenharmony_ci * ext4_xattr_block_set() to reliably detect freed block 12408c2ecf20Sopenharmony_ci */ 12418c2ecf20Sopenharmony_ci if (ea_block_cache) { 12428c2ecf20Sopenharmony_ci struct mb_cache_entry *oe; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci oe = mb_cache_entry_delete_or_get(ea_block_cache, hash, 12458c2ecf20Sopenharmony_ci bh->b_blocknr); 12468c2ecf20Sopenharmony_ci if (oe) { 12478c2ecf20Sopenharmony_ci unlock_buffer(bh); 12488c2ecf20Sopenharmony_ci mb_cache_entry_wait_unused(oe); 12498c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_block_cache, oe); 12508c2ecf20Sopenharmony_ci goto retry_ref; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci get_bh(bh); 12548c2ecf20Sopenharmony_ci unlock_buffer(bh); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci if (ext4_has_feature_ea_inode(inode->i_sb)) 12578c2ecf20Sopenharmony_ci ext4_xattr_inode_dec_ref_all(handle, inode, bh, 12588c2ecf20Sopenharmony_ci BFIRST(bh), 12598c2ecf20Sopenharmony_ci true /* block_csum */, 12608c2ecf20Sopenharmony_ci ea_inode_array, 12618c2ecf20Sopenharmony_ci extra_credits, 12628c2ecf20Sopenharmony_ci true /* skip_quota */); 12638c2ecf20Sopenharmony_ci ext4_free_blocks(handle, inode, bh, 0, 1, 12648c2ecf20Sopenharmony_ci EXT4_FREE_BLOCKS_METADATA | 12658c2ecf20Sopenharmony_ci EXT4_FREE_BLOCKS_FORGET); 12668c2ecf20Sopenharmony_ci } else { 12678c2ecf20Sopenharmony_ci ref--; 12688c2ecf20Sopenharmony_ci BHDR(bh)->h_refcount = cpu_to_le32(ref); 12698c2ecf20Sopenharmony_ci if (ref == EXT4_XATTR_REFCOUNT_MAX - 1) { 12708c2ecf20Sopenharmony_ci struct mb_cache_entry *ce; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (ea_block_cache) { 12738c2ecf20Sopenharmony_ci ce = mb_cache_entry_get(ea_block_cache, hash, 12748c2ecf20Sopenharmony_ci bh->b_blocknr); 12758c2ecf20Sopenharmony_ci if (ce) { 12768c2ecf20Sopenharmony_ci set_bit(MBE_REUSABLE_B, &ce->e_flags); 12778c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_block_cache, ce); 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci ext4_xattr_block_csum_set(inode, bh); 12838c2ecf20Sopenharmony_ci /* 12848c2ecf20Sopenharmony_ci * Beware of this ugliness: Releasing of xattr block references 12858c2ecf20Sopenharmony_ci * from different inodes can race and so we have to protect 12868c2ecf20Sopenharmony_ci * from a race where someone else frees the block (and releases 12878c2ecf20Sopenharmony_ci * its journal_head) before we are done dirtying the buffer. In 12888c2ecf20Sopenharmony_ci * nojournal mode this race is harmless and we actually cannot 12898c2ecf20Sopenharmony_ci * call ext4_handle_dirty_metadata() with locked buffer as 12908c2ecf20Sopenharmony_ci * that function can call sync_dirty_buffer() so for that case 12918c2ecf20Sopenharmony_ci * we handle the dirtying after unlocking the buffer. 12928c2ecf20Sopenharmony_ci */ 12938c2ecf20Sopenharmony_ci if (ext4_handle_valid(handle)) 12948c2ecf20Sopenharmony_ci error = ext4_handle_dirty_metadata(handle, inode, bh); 12958c2ecf20Sopenharmony_ci unlock_buffer(bh); 12968c2ecf20Sopenharmony_ci if (!ext4_handle_valid(handle)) 12978c2ecf20Sopenharmony_ci error = ext4_handle_dirty_metadata(handle, inode, bh); 12988c2ecf20Sopenharmony_ci if (IS_SYNC(inode)) 12998c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 13008c2ecf20Sopenharmony_ci dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1)); 13018c2ecf20Sopenharmony_ci ea_bdebug(bh, "refcount now=%d; releasing", 13028c2ecf20Sopenharmony_ci le32_to_cpu(BHDR(bh)->h_refcount)); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ciout: 13058c2ecf20Sopenharmony_ci ext4_std_error(inode->i_sb, error); 13068c2ecf20Sopenharmony_ci return; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci/* 13108c2ecf20Sopenharmony_ci * Find the available free space for EAs. This also returns the total number of 13118c2ecf20Sopenharmony_ci * bytes used by EA entries. 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_cistatic size_t ext4_xattr_free_space(struct ext4_xattr_entry *last, 13148c2ecf20Sopenharmony_ci size_t *min_offs, void *base, int *total) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { 13178c2ecf20Sopenharmony_ci if (!last->e_value_inum && last->e_value_size) { 13188c2ecf20Sopenharmony_ci size_t offs = le16_to_cpu(last->e_value_offs); 13198c2ecf20Sopenharmony_ci if (offs < *min_offs) 13208c2ecf20Sopenharmony_ci *min_offs = offs; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci if (total) 13238c2ecf20Sopenharmony_ci *total += EXT4_XATTR_LEN(last->e_name_len); 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci return (*min_offs - ((void *)last - base) - sizeof(__u32)); 13268c2ecf20Sopenharmony_ci} 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci/* 13298c2ecf20Sopenharmony_ci * Write the value of the EA in an inode. 13308c2ecf20Sopenharmony_ci */ 13318c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_write(handle_t *handle, struct inode *ea_inode, 13328c2ecf20Sopenharmony_ci const void *buf, int bufsize) 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 13358c2ecf20Sopenharmony_ci unsigned long block = 0; 13368c2ecf20Sopenharmony_ci int blocksize = ea_inode->i_sb->s_blocksize; 13378c2ecf20Sopenharmony_ci int max_blocks = (bufsize + blocksize - 1) >> ea_inode->i_blkbits; 13388c2ecf20Sopenharmony_ci int csize, wsize = 0; 13398c2ecf20Sopenharmony_ci int ret = 0, ret2 = 0; 13408c2ecf20Sopenharmony_ci int retries = 0; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ciretry: 13438c2ecf20Sopenharmony_ci while (ret >= 0 && ret < max_blocks) { 13448c2ecf20Sopenharmony_ci struct ext4_map_blocks map; 13458c2ecf20Sopenharmony_ci map.m_lblk = block += ret; 13468c2ecf20Sopenharmony_ci map.m_len = max_blocks -= ret; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci ret = ext4_map_blocks(handle, ea_inode, &map, 13498c2ecf20Sopenharmony_ci EXT4_GET_BLOCKS_CREATE); 13508c2ecf20Sopenharmony_ci if (ret <= 0) { 13518c2ecf20Sopenharmony_ci ext4_mark_inode_dirty(handle, ea_inode); 13528c2ecf20Sopenharmony_ci if (ret == -ENOSPC && 13538c2ecf20Sopenharmony_ci ext4_should_retry_alloc(ea_inode->i_sb, &retries)) { 13548c2ecf20Sopenharmony_ci ret = 0; 13558c2ecf20Sopenharmony_ci goto retry; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci break; 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (ret < 0) 13628c2ecf20Sopenharmony_ci return ret; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci block = 0; 13658c2ecf20Sopenharmony_ci while (wsize < bufsize) { 13668c2ecf20Sopenharmony_ci brelse(bh); 13678c2ecf20Sopenharmony_ci csize = (bufsize - wsize) > blocksize ? blocksize : 13688c2ecf20Sopenharmony_ci bufsize - wsize; 13698c2ecf20Sopenharmony_ci bh = ext4_getblk(handle, ea_inode, block, 0); 13708c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 13718c2ecf20Sopenharmony_ci return PTR_ERR(bh); 13728c2ecf20Sopenharmony_ci if (!bh) { 13738c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 13748c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(ea_inode, 13758c2ecf20Sopenharmony_ci "ext4_getblk() return bh = NULL"); 13768c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci ret = ext4_journal_get_write_access(handle, bh); 13798c2ecf20Sopenharmony_ci if (ret) 13808c2ecf20Sopenharmony_ci goto out; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci memcpy(bh->b_data, buf, csize); 13838c2ecf20Sopenharmony_ci set_buffer_uptodate(bh); 13848c2ecf20Sopenharmony_ci ext4_handle_dirty_metadata(handle, ea_inode, bh); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci buf += csize; 13878c2ecf20Sopenharmony_ci wsize += csize; 13888c2ecf20Sopenharmony_ci block += 1; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci inode_lock(ea_inode); 13928c2ecf20Sopenharmony_ci i_size_write(ea_inode, wsize); 13938c2ecf20Sopenharmony_ci ext4_update_i_disksize(ea_inode, wsize); 13948c2ecf20Sopenharmony_ci inode_unlock(ea_inode); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci ret2 = ext4_mark_inode_dirty(handle, ea_inode); 13978c2ecf20Sopenharmony_ci if (unlikely(ret2 && !ret)) 13988c2ecf20Sopenharmony_ci ret = ret2; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ciout: 14018c2ecf20Sopenharmony_ci brelse(bh); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci return ret; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci/* 14078c2ecf20Sopenharmony_ci * Create an inode to store the value of a large EA. 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_cistatic struct inode *ext4_xattr_inode_create(handle_t *handle, 14108c2ecf20Sopenharmony_ci struct inode *inode, u32 hash) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci struct inode *ea_inode = NULL; 14138c2ecf20Sopenharmony_ci uid_t owner[2] = { i_uid_read(inode), i_gid_read(inode) }; 14148c2ecf20Sopenharmony_ci int err; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (inode->i_sb->s_root == NULL) { 14178c2ecf20Sopenharmony_ci ext4_warning(inode->i_sb, 14188c2ecf20Sopenharmony_ci "refuse to create EA inode when umounting"); 14198c2ecf20Sopenharmony_ci WARN_ON(1); 14208c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* 14248c2ecf20Sopenharmony_ci * Let the next inode be the goal, so we try and allocate the EA inode 14258c2ecf20Sopenharmony_ci * in the same group, or nearby one. 14268c2ecf20Sopenharmony_ci */ 14278c2ecf20Sopenharmony_ci ea_inode = ext4_new_inode(handle, inode->i_sb->s_root->d_inode, 14288c2ecf20Sopenharmony_ci S_IFREG | 0600, NULL, inode->i_ino + 1, owner, 14298c2ecf20Sopenharmony_ci EXT4_EA_INODE_FL); 14308c2ecf20Sopenharmony_ci if (!IS_ERR(ea_inode)) { 14318c2ecf20Sopenharmony_ci ea_inode->i_op = &ext4_file_inode_operations; 14328c2ecf20Sopenharmony_ci ea_inode->i_fop = &ext4_file_operations; 14338c2ecf20Sopenharmony_ci ext4_set_aops(ea_inode); 14348c2ecf20Sopenharmony_ci ext4_xattr_inode_set_class(ea_inode); 14358c2ecf20Sopenharmony_ci unlock_new_inode(ea_inode); 14368c2ecf20Sopenharmony_ci ext4_xattr_inode_set_ref(ea_inode, 1); 14378c2ecf20Sopenharmony_ci ext4_xattr_inode_set_hash(ea_inode, hash); 14388c2ecf20Sopenharmony_ci err = ext4_mark_inode_dirty(handle, ea_inode); 14398c2ecf20Sopenharmony_ci if (!err) 14408c2ecf20Sopenharmony_ci err = ext4_inode_attach_jinode(ea_inode); 14418c2ecf20Sopenharmony_ci if (err) { 14428c2ecf20Sopenharmony_ci if (ext4_xattr_inode_dec_ref(handle, ea_inode)) 14438c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, 14448c2ecf20Sopenharmony_ci "cleanup dec ref error %d", err); 14458c2ecf20Sopenharmony_ci iput(ea_inode); 14468c2ecf20Sopenharmony_ci return ERR_PTR(err); 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* 14508c2ecf20Sopenharmony_ci * Xattr inodes are shared therefore quota charging is performed 14518c2ecf20Sopenharmony_ci * at a higher level. 14528c2ecf20Sopenharmony_ci */ 14538c2ecf20Sopenharmony_ci dquot_free_inode(ea_inode); 14548c2ecf20Sopenharmony_ci dquot_drop(ea_inode); 14558c2ecf20Sopenharmony_ci inode_lock(ea_inode); 14568c2ecf20Sopenharmony_ci ea_inode->i_flags |= S_NOQUOTA; 14578c2ecf20Sopenharmony_ci inode_unlock(ea_inode); 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci return ea_inode; 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_cistatic struct inode * 14648c2ecf20Sopenharmony_ciext4_xattr_inode_cache_find(struct inode *inode, const void *value, 14658c2ecf20Sopenharmony_ci size_t value_len, u32 hash) 14668c2ecf20Sopenharmony_ci{ 14678c2ecf20Sopenharmony_ci struct inode *ea_inode; 14688c2ecf20Sopenharmony_ci struct mb_cache_entry *ce; 14698c2ecf20Sopenharmony_ci struct mb_cache *ea_inode_cache = EA_INODE_CACHE(inode); 14708c2ecf20Sopenharmony_ci void *ea_data; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci if (!ea_inode_cache) 14738c2ecf20Sopenharmony_ci return NULL; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci ce = mb_cache_entry_find_first(ea_inode_cache, hash); 14768c2ecf20Sopenharmony_ci if (!ce) 14778c2ecf20Sopenharmony_ci return NULL; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci WARN_ON_ONCE(ext4_handle_valid(journal_current_handle()) && 14808c2ecf20Sopenharmony_ci !(current->flags & PF_MEMALLOC_NOFS)); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci ea_data = kvmalloc(value_len, GFP_KERNEL); 14838c2ecf20Sopenharmony_ci if (!ea_data) { 14848c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_inode_cache, ce); 14858c2ecf20Sopenharmony_ci return NULL; 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci while (ce) { 14898c2ecf20Sopenharmony_ci ea_inode = ext4_iget(inode->i_sb, ce->e_value, 14908c2ecf20Sopenharmony_ci EXT4_IGET_EA_INODE); 14918c2ecf20Sopenharmony_ci if (IS_ERR(ea_inode)) 14928c2ecf20Sopenharmony_ci goto next_entry; 14938c2ecf20Sopenharmony_ci ext4_xattr_inode_set_class(ea_inode); 14948c2ecf20Sopenharmony_ci if (i_size_read(ea_inode) == value_len && 14958c2ecf20Sopenharmony_ci !ext4_xattr_inode_read(ea_inode, ea_data, value_len) && 14968c2ecf20Sopenharmony_ci !ext4_xattr_inode_verify_hashes(ea_inode, NULL, ea_data, 14978c2ecf20Sopenharmony_ci value_len) && 14988c2ecf20Sopenharmony_ci !memcmp(value, ea_data, value_len)) { 14998c2ecf20Sopenharmony_ci mb_cache_entry_touch(ea_inode_cache, ce); 15008c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_inode_cache, ce); 15018c2ecf20Sopenharmony_ci kvfree(ea_data); 15028c2ecf20Sopenharmony_ci return ea_inode; 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci iput(ea_inode); 15058c2ecf20Sopenharmony_ci next_entry: 15068c2ecf20Sopenharmony_ci ce = mb_cache_entry_find_next(ea_inode_cache, ce); 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci kvfree(ea_data); 15098c2ecf20Sopenharmony_ci return NULL; 15108c2ecf20Sopenharmony_ci} 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci/* 15138c2ecf20Sopenharmony_ci * Add value of the EA in an inode. 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_cistatic int ext4_xattr_inode_lookup_create(handle_t *handle, struct inode *inode, 15168c2ecf20Sopenharmony_ci const void *value, size_t value_len, 15178c2ecf20Sopenharmony_ci struct inode **ret_inode) 15188c2ecf20Sopenharmony_ci{ 15198c2ecf20Sopenharmony_ci struct inode *ea_inode; 15208c2ecf20Sopenharmony_ci u32 hash; 15218c2ecf20Sopenharmony_ci int err; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci hash = ext4_xattr_inode_hash(EXT4_SB(inode->i_sb), value, value_len); 15248c2ecf20Sopenharmony_ci ea_inode = ext4_xattr_inode_cache_find(inode, value, value_len, hash); 15258c2ecf20Sopenharmony_ci if (ea_inode) { 15268c2ecf20Sopenharmony_ci err = ext4_xattr_inode_inc_ref(handle, ea_inode); 15278c2ecf20Sopenharmony_ci if (err) { 15288c2ecf20Sopenharmony_ci iput(ea_inode); 15298c2ecf20Sopenharmony_ci return err; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci *ret_inode = ea_inode; 15338c2ecf20Sopenharmony_ci return 0; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* Create an inode for the EA value */ 15378c2ecf20Sopenharmony_ci ea_inode = ext4_xattr_inode_create(handle, inode, hash); 15388c2ecf20Sopenharmony_ci if (IS_ERR(ea_inode)) 15398c2ecf20Sopenharmony_ci return PTR_ERR(ea_inode); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci err = ext4_xattr_inode_write(handle, ea_inode, value, value_len); 15428c2ecf20Sopenharmony_ci if (err) { 15438c2ecf20Sopenharmony_ci ext4_xattr_inode_dec_ref(handle, ea_inode); 15448c2ecf20Sopenharmony_ci iput(ea_inode); 15458c2ecf20Sopenharmony_ci return err; 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (EA_INODE_CACHE(inode)) 15498c2ecf20Sopenharmony_ci mb_cache_entry_create(EA_INODE_CACHE(inode), GFP_NOFS, hash, 15508c2ecf20Sopenharmony_ci ea_inode->i_ino, true /* reusable */); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci *ret_inode = ea_inode; 15538c2ecf20Sopenharmony_ci return 0; 15548c2ecf20Sopenharmony_ci} 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci/* 15578c2ecf20Sopenharmony_ci * Reserve min(block_size/8, 1024) bytes for xattr entries/names if ea_inode 15588c2ecf20Sopenharmony_ci * feature is enabled. 15598c2ecf20Sopenharmony_ci */ 15608c2ecf20Sopenharmony_ci#define EXT4_XATTR_BLOCK_RESERVE(inode) min(i_blocksize(inode)/8, 1024U) 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_cistatic int ext4_xattr_set_entry(struct ext4_xattr_info *i, 15638c2ecf20Sopenharmony_ci struct ext4_xattr_search *s, 15648c2ecf20Sopenharmony_ci handle_t *handle, struct inode *inode, 15658c2ecf20Sopenharmony_ci bool is_block) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct ext4_xattr_entry *last, *next; 15688c2ecf20Sopenharmony_ci struct ext4_xattr_entry *here = s->here; 15698c2ecf20Sopenharmony_ci size_t min_offs = s->end - s->base, name_len = strlen(i->name); 15708c2ecf20Sopenharmony_ci int in_inode = i->in_inode; 15718c2ecf20Sopenharmony_ci struct inode *old_ea_inode = NULL; 15728c2ecf20Sopenharmony_ci struct inode *new_ea_inode = NULL; 15738c2ecf20Sopenharmony_ci size_t old_size, new_size; 15748c2ecf20Sopenharmony_ci int ret; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* Space used by old and new values. */ 15778c2ecf20Sopenharmony_ci old_size = (!s->not_found && !here->e_value_inum) ? 15788c2ecf20Sopenharmony_ci EXT4_XATTR_SIZE(le32_to_cpu(here->e_value_size)) : 0; 15798c2ecf20Sopenharmony_ci new_size = (i->value && !in_inode) ? EXT4_XATTR_SIZE(i->value_len) : 0; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci /* 15828c2ecf20Sopenharmony_ci * Optimization for the simple case when old and new values have the 15838c2ecf20Sopenharmony_ci * same padded sizes. Not applicable if external inodes are involved. 15848c2ecf20Sopenharmony_ci */ 15858c2ecf20Sopenharmony_ci if (new_size && new_size == old_size) { 15868c2ecf20Sopenharmony_ci size_t offs = le16_to_cpu(here->e_value_offs); 15878c2ecf20Sopenharmony_ci void *val = s->base + offs; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci here->e_value_size = cpu_to_le32(i->value_len); 15908c2ecf20Sopenharmony_ci if (i->value == EXT4_ZERO_XATTR_VALUE) { 15918c2ecf20Sopenharmony_ci memset(val, 0, new_size); 15928c2ecf20Sopenharmony_ci } else { 15938c2ecf20Sopenharmony_ci memcpy(val, i->value, i->value_len); 15948c2ecf20Sopenharmony_ci /* Clear padding bytes. */ 15958c2ecf20Sopenharmony_ci memset(val + i->value_len, 0, new_size - i->value_len); 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci goto update_hash; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci /* Compute min_offs and last. */ 16018c2ecf20Sopenharmony_ci last = s->first; 16028c2ecf20Sopenharmony_ci for (; !IS_LAST_ENTRY(last); last = next) { 16038c2ecf20Sopenharmony_ci next = EXT4_XATTR_NEXT(last); 16048c2ecf20Sopenharmony_ci if ((void *)next >= s->end) { 16058c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "corrupted xattr entries"); 16068c2ecf20Sopenharmony_ci ret = -EFSCORRUPTED; 16078c2ecf20Sopenharmony_ci goto out; 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci if (!last->e_value_inum && last->e_value_size) { 16108c2ecf20Sopenharmony_ci size_t offs = le16_to_cpu(last->e_value_offs); 16118c2ecf20Sopenharmony_ci if (offs < min_offs) 16128c2ecf20Sopenharmony_ci min_offs = offs; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci /* Check whether we have enough space. */ 16178c2ecf20Sopenharmony_ci if (i->value) { 16188c2ecf20Sopenharmony_ci size_t free; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci free = min_offs - ((void *)last - s->base) - sizeof(__u32); 16218c2ecf20Sopenharmony_ci if (!s->not_found) 16228c2ecf20Sopenharmony_ci free += EXT4_XATTR_LEN(name_len) + old_size; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci if (free < EXT4_XATTR_LEN(name_len) + new_size) { 16258c2ecf20Sopenharmony_ci ret = -ENOSPC; 16268c2ecf20Sopenharmony_ci goto out; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* 16308c2ecf20Sopenharmony_ci * If storing the value in an external inode is an option, 16318c2ecf20Sopenharmony_ci * reserve space for xattr entries/names in the external 16328c2ecf20Sopenharmony_ci * attribute block so that a long value does not occupy the 16338c2ecf20Sopenharmony_ci * whole space and prevent further entries being added. 16348c2ecf20Sopenharmony_ci */ 16358c2ecf20Sopenharmony_ci if (ext4_has_feature_ea_inode(inode->i_sb) && 16368c2ecf20Sopenharmony_ci new_size && is_block && 16378c2ecf20Sopenharmony_ci (min_offs + old_size - new_size) < 16388c2ecf20Sopenharmony_ci EXT4_XATTR_BLOCK_RESERVE(inode)) { 16398c2ecf20Sopenharmony_ci ret = -ENOSPC; 16408c2ecf20Sopenharmony_ci goto out; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci /* 16458c2ecf20Sopenharmony_ci * Getting access to old and new ea inodes is subject to failures. 16468c2ecf20Sopenharmony_ci * Finish that work before doing any modifications to the xattr data. 16478c2ecf20Sopenharmony_ci */ 16488c2ecf20Sopenharmony_ci if (!s->not_found && here->e_value_inum) { 16498c2ecf20Sopenharmony_ci ret = ext4_xattr_inode_iget(inode, 16508c2ecf20Sopenharmony_ci le32_to_cpu(here->e_value_inum), 16518c2ecf20Sopenharmony_ci le32_to_cpu(here->e_hash), 16528c2ecf20Sopenharmony_ci &old_ea_inode); 16538c2ecf20Sopenharmony_ci if (ret) { 16548c2ecf20Sopenharmony_ci old_ea_inode = NULL; 16558c2ecf20Sopenharmony_ci goto out; 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci if (i->value && in_inode) { 16598c2ecf20Sopenharmony_ci WARN_ON_ONCE(!i->value_len); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci ret = ext4_xattr_inode_alloc_quota(inode, i->value_len); 16628c2ecf20Sopenharmony_ci if (ret) 16638c2ecf20Sopenharmony_ci goto out; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci ret = ext4_xattr_inode_lookup_create(handle, inode, i->value, 16668c2ecf20Sopenharmony_ci i->value_len, 16678c2ecf20Sopenharmony_ci &new_ea_inode); 16688c2ecf20Sopenharmony_ci if (ret) { 16698c2ecf20Sopenharmony_ci new_ea_inode = NULL; 16708c2ecf20Sopenharmony_ci ext4_xattr_inode_free_quota(inode, NULL, i->value_len); 16718c2ecf20Sopenharmony_ci goto out; 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (old_ea_inode) { 16768c2ecf20Sopenharmony_ci /* We are ready to release ref count on the old_ea_inode. */ 16778c2ecf20Sopenharmony_ci ret = ext4_xattr_inode_dec_ref(handle, old_ea_inode); 16788c2ecf20Sopenharmony_ci if (ret) { 16798c2ecf20Sopenharmony_ci /* Release newly required ref count on new_ea_inode. */ 16808c2ecf20Sopenharmony_ci if (new_ea_inode) { 16818c2ecf20Sopenharmony_ci int err; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci err = ext4_xattr_inode_dec_ref(handle, 16848c2ecf20Sopenharmony_ci new_ea_inode); 16858c2ecf20Sopenharmony_ci if (err) 16868c2ecf20Sopenharmony_ci ext4_warning_inode(new_ea_inode, 16878c2ecf20Sopenharmony_ci "dec ref new_ea_inode err=%d", 16888c2ecf20Sopenharmony_ci err); 16898c2ecf20Sopenharmony_ci ext4_xattr_inode_free_quota(inode, new_ea_inode, 16908c2ecf20Sopenharmony_ci i->value_len); 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci goto out; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci ext4_xattr_inode_free_quota(inode, old_ea_inode, 16968c2ecf20Sopenharmony_ci le32_to_cpu(here->e_value_size)); 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci /* No failures allowed past this point. */ 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci if (!s->not_found && here->e_value_size && !here->e_value_inum) { 17028c2ecf20Sopenharmony_ci /* Remove the old value. */ 17038c2ecf20Sopenharmony_ci void *first_val = s->base + min_offs; 17048c2ecf20Sopenharmony_ci size_t offs = le16_to_cpu(here->e_value_offs); 17058c2ecf20Sopenharmony_ci void *val = s->base + offs; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci memmove(first_val + old_size, first_val, val - first_val); 17088c2ecf20Sopenharmony_ci memset(first_val, 0, old_size); 17098c2ecf20Sopenharmony_ci min_offs += old_size; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* Adjust all value offsets. */ 17128c2ecf20Sopenharmony_ci last = s->first; 17138c2ecf20Sopenharmony_ci while (!IS_LAST_ENTRY(last)) { 17148c2ecf20Sopenharmony_ci size_t o = le16_to_cpu(last->e_value_offs); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci if (!last->e_value_inum && 17178c2ecf20Sopenharmony_ci last->e_value_size && o < offs) 17188c2ecf20Sopenharmony_ci last->e_value_offs = cpu_to_le16(o + old_size); 17198c2ecf20Sopenharmony_ci last = EXT4_XATTR_NEXT(last); 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (!i->value) { 17248c2ecf20Sopenharmony_ci /* Remove old name. */ 17258c2ecf20Sopenharmony_ci size_t size = EXT4_XATTR_LEN(name_len); 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci last = ENTRY((void *)last - size); 17288c2ecf20Sopenharmony_ci memmove(here, (void *)here + size, 17298c2ecf20Sopenharmony_ci (void *)last - (void *)here + sizeof(__u32)); 17308c2ecf20Sopenharmony_ci memset(last, 0, size); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* 17338c2ecf20Sopenharmony_ci * Update i_inline_off - moved ibody region might contain 17348c2ecf20Sopenharmony_ci * system.data attribute. Handling a failure here won't 17358c2ecf20Sopenharmony_ci * cause other complications for setting an xattr. 17368c2ecf20Sopenharmony_ci */ 17378c2ecf20Sopenharmony_ci if (!is_block && ext4_has_inline_data(inode)) { 17388c2ecf20Sopenharmony_ci ret = ext4_find_inline_data_nolock(inode); 17398c2ecf20Sopenharmony_ci if (ret) { 17408c2ecf20Sopenharmony_ci ext4_warning_inode(inode, 17418c2ecf20Sopenharmony_ci "unable to update i_inline_off"); 17428c2ecf20Sopenharmony_ci goto out; 17438c2ecf20Sopenharmony_ci } 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci } else if (s->not_found) { 17468c2ecf20Sopenharmony_ci /* Insert new name. */ 17478c2ecf20Sopenharmony_ci size_t size = EXT4_XATTR_LEN(name_len); 17488c2ecf20Sopenharmony_ci size_t rest = (void *)last - (void *)here + sizeof(__u32); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci memmove((void *)here + size, here, rest); 17518c2ecf20Sopenharmony_ci memset(here, 0, size); 17528c2ecf20Sopenharmony_ci here->e_name_index = i->name_index; 17538c2ecf20Sopenharmony_ci here->e_name_len = name_len; 17548c2ecf20Sopenharmony_ci memcpy(here->e_name, i->name, name_len); 17558c2ecf20Sopenharmony_ci } else { 17568c2ecf20Sopenharmony_ci /* This is an update, reset value info. */ 17578c2ecf20Sopenharmony_ci here->e_value_inum = 0; 17588c2ecf20Sopenharmony_ci here->e_value_offs = 0; 17598c2ecf20Sopenharmony_ci here->e_value_size = 0; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (i->value) { 17638c2ecf20Sopenharmony_ci /* Insert new value. */ 17648c2ecf20Sopenharmony_ci if (in_inode) { 17658c2ecf20Sopenharmony_ci here->e_value_inum = cpu_to_le32(new_ea_inode->i_ino); 17668c2ecf20Sopenharmony_ci } else if (i->value_len) { 17678c2ecf20Sopenharmony_ci void *val = s->base + min_offs - new_size; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci here->e_value_offs = cpu_to_le16(min_offs - new_size); 17708c2ecf20Sopenharmony_ci if (i->value == EXT4_ZERO_XATTR_VALUE) { 17718c2ecf20Sopenharmony_ci memset(val, 0, new_size); 17728c2ecf20Sopenharmony_ci } else { 17738c2ecf20Sopenharmony_ci memcpy(val, i->value, i->value_len); 17748c2ecf20Sopenharmony_ci /* Clear padding bytes. */ 17758c2ecf20Sopenharmony_ci memset(val + i->value_len, 0, 17768c2ecf20Sopenharmony_ci new_size - i->value_len); 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci here->e_value_size = cpu_to_le32(i->value_len); 17808c2ecf20Sopenharmony_ci } 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ciupdate_hash: 17838c2ecf20Sopenharmony_ci if (i->value) { 17848c2ecf20Sopenharmony_ci __le32 hash = 0; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* Entry hash calculation. */ 17878c2ecf20Sopenharmony_ci if (in_inode) { 17888c2ecf20Sopenharmony_ci __le32 crc32c_hash; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci /* 17918c2ecf20Sopenharmony_ci * Feed crc32c hash instead of the raw value for entry 17928c2ecf20Sopenharmony_ci * hash calculation. This is to avoid walking 17938c2ecf20Sopenharmony_ci * potentially long value buffer again. 17948c2ecf20Sopenharmony_ci */ 17958c2ecf20Sopenharmony_ci crc32c_hash = cpu_to_le32( 17968c2ecf20Sopenharmony_ci ext4_xattr_inode_get_hash(new_ea_inode)); 17978c2ecf20Sopenharmony_ci hash = ext4_xattr_hash_entry(here->e_name, 17988c2ecf20Sopenharmony_ci here->e_name_len, 17998c2ecf20Sopenharmony_ci &crc32c_hash, 1); 18008c2ecf20Sopenharmony_ci } else if (is_block) { 18018c2ecf20Sopenharmony_ci __le32 *value = s->base + le16_to_cpu( 18028c2ecf20Sopenharmony_ci here->e_value_offs); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci hash = ext4_xattr_hash_entry(here->e_name, 18058c2ecf20Sopenharmony_ci here->e_name_len, value, 18068c2ecf20Sopenharmony_ci new_size >> 2); 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci here->e_hash = hash; 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci if (is_block) 18128c2ecf20Sopenharmony_ci ext4_xattr_rehash((struct ext4_xattr_header *)s->base); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci ret = 0; 18158c2ecf20Sopenharmony_ciout: 18168c2ecf20Sopenharmony_ci iput(old_ea_inode); 18178c2ecf20Sopenharmony_ci iput(new_ea_inode); 18188c2ecf20Sopenharmony_ci return ret; 18198c2ecf20Sopenharmony_ci} 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_cistruct ext4_xattr_block_find { 18228c2ecf20Sopenharmony_ci struct ext4_xattr_search s; 18238c2ecf20Sopenharmony_ci struct buffer_head *bh; 18248c2ecf20Sopenharmony_ci}; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cistatic int 18278c2ecf20Sopenharmony_ciext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i, 18288c2ecf20Sopenharmony_ci struct ext4_xattr_block_find *bs) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 18318c2ecf20Sopenharmony_ci int error; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld", 18348c2ecf20Sopenharmony_ci i->name_index, i->name, i->value, (long)i->value_len); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (EXT4_I(inode)->i_file_acl) { 18378c2ecf20Sopenharmony_ci /* The inode already has an extended attribute block. */ 18388c2ecf20Sopenharmony_ci bs->bh = ext4_sb_bread(sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); 18398c2ecf20Sopenharmony_ci if (IS_ERR(bs->bh)) { 18408c2ecf20Sopenharmony_ci error = PTR_ERR(bs->bh); 18418c2ecf20Sopenharmony_ci bs->bh = NULL; 18428c2ecf20Sopenharmony_ci return error; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci ea_bdebug(bs->bh, "b_count=%d, refcount=%d", 18458c2ecf20Sopenharmony_ci atomic_read(&(bs->bh->b_count)), 18468c2ecf20Sopenharmony_ci le32_to_cpu(BHDR(bs->bh)->h_refcount)); 18478c2ecf20Sopenharmony_ci error = ext4_xattr_check_block(inode, bs->bh); 18488c2ecf20Sopenharmony_ci if (error) 18498c2ecf20Sopenharmony_ci return error; 18508c2ecf20Sopenharmony_ci /* Find the named attribute. */ 18518c2ecf20Sopenharmony_ci bs->s.base = BHDR(bs->bh); 18528c2ecf20Sopenharmony_ci bs->s.first = BFIRST(bs->bh); 18538c2ecf20Sopenharmony_ci bs->s.end = bs->bh->b_data + bs->bh->b_size; 18548c2ecf20Sopenharmony_ci bs->s.here = bs->s.first; 18558c2ecf20Sopenharmony_ci error = xattr_find_entry(inode, &bs->s.here, bs->s.end, 18568c2ecf20Sopenharmony_ci i->name_index, i->name, 1); 18578c2ecf20Sopenharmony_ci if (error && error != -ENODATA) 18588c2ecf20Sopenharmony_ci return error; 18598c2ecf20Sopenharmony_ci bs->s.not_found = error; 18608c2ecf20Sopenharmony_ci } 18618c2ecf20Sopenharmony_ci return 0; 18628c2ecf20Sopenharmony_ci} 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_cistatic int 18658c2ecf20Sopenharmony_ciext4_xattr_block_set(handle_t *handle, struct inode *inode, 18668c2ecf20Sopenharmony_ci struct ext4_xattr_info *i, 18678c2ecf20Sopenharmony_ci struct ext4_xattr_block_find *bs) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 18708c2ecf20Sopenharmony_ci struct buffer_head *new_bh = NULL; 18718c2ecf20Sopenharmony_ci struct ext4_xattr_search s_copy = bs->s; 18728c2ecf20Sopenharmony_ci struct ext4_xattr_search *s = &s_copy; 18738c2ecf20Sopenharmony_ci struct mb_cache_entry *ce = NULL; 18748c2ecf20Sopenharmony_ci int error = 0; 18758c2ecf20Sopenharmony_ci struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode); 18768c2ecf20Sopenharmony_ci struct inode *ea_inode = NULL, *tmp_inode; 18778c2ecf20Sopenharmony_ci size_t old_ea_inode_quota = 0; 18788c2ecf20Sopenharmony_ci unsigned int ea_ino; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci#define header(x) ((struct ext4_xattr_header *)(x)) 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci if (s->base) { 18848c2ecf20Sopenharmony_ci int offset = (char *)s->here - bs->bh->b_data; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci BUFFER_TRACE(bs->bh, "get_write_access"); 18878c2ecf20Sopenharmony_ci error = ext4_journal_get_write_access(handle, bs->bh); 18888c2ecf20Sopenharmony_ci if (error) 18898c2ecf20Sopenharmony_ci goto cleanup; 18908c2ecf20Sopenharmony_ci lock_buffer(bs->bh); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci if (header(s->base)->h_refcount == cpu_to_le32(1)) { 18938c2ecf20Sopenharmony_ci __u32 hash = le32_to_cpu(BHDR(bs->bh)->h_hash); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* 18968c2ecf20Sopenharmony_ci * This must happen under buffer lock for 18978c2ecf20Sopenharmony_ci * ext4_xattr_block_set() to reliably detect modified 18988c2ecf20Sopenharmony_ci * block 18998c2ecf20Sopenharmony_ci */ 19008c2ecf20Sopenharmony_ci if (ea_block_cache) { 19018c2ecf20Sopenharmony_ci struct mb_cache_entry *oe; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci oe = mb_cache_entry_delete_or_get(ea_block_cache, 19048c2ecf20Sopenharmony_ci hash, bs->bh->b_blocknr); 19058c2ecf20Sopenharmony_ci if (oe) { 19068c2ecf20Sopenharmony_ci /* 19078c2ecf20Sopenharmony_ci * Xattr block is getting reused. Leave 19088c2ecf20Sopenharmony_ci * it alone. 19098c2ecf20Sopenharmony_ci */ 19108c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_block_cache, oe); 19118c2ecf20Sopenharmony_ci goto clone_block; 19128c2ecf20Sopenharmony_ci } 19138c2ecf20Sopenharmony_ci } 19148c2ecf20Sopenharmony_ci ea_bdebug(bs->bh, "modifying in-place"); 19158c2ecf20Sopenharmony_ci error = ext4_xattr_set_entry(i, s, handle, inode, 19168c2ecf20Sopenharmony_ci true /* is_block */); 19178c2ecf20Sopenharmony_ci ext4_xattr_block_csum_set(inode, bs->bh); 19188c2ecf20Sopenharmony_ci unlock_buffer(bs->bh); 19198c2ecf20Sopenharmony_ci if (error == -EFSCORRUPTED) 19208c2ecf20Sopenharmony_ci goto bad_block; 19218c2ecf20Sopenharmony_ci if (!error) 19228c2ecf20Sopenharmony_ci error = ext4_handle_dirty_metadata(handle, 19238c2ecf20Sopenharmony_ci inode, 19248c2ecf20Sopenharmony_ci bs->bh); 19258c2ecf20Sopenharmony_ci if (error) 19268c2ecf20Sopenharmony_ci goto cleanup; 19278c2ecf20Sopenharmony_ci goto inserted; 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ciclone_block: 19308c2ecf20Sopenharmony_ci unlock_buffer(bs->bh); 19318c2ecf20Sopenharmony_ci ea_bdebug(bs->bh, "cloning"); 19328c2ecf20Sopenharmony_ci s->base = kmemdup(BHDR(bs->bh), bs->bh->b_size, GFP_NOFS); 19338c2ecf20Sopenharmony_ci error = -ENOMEM; 19348c2ecf20Sopenharmony_ci if (s->base == NULL) 19358c2ecf20Sopenharmony_ci goto cleanup; 19368c2ecf20Sopenharmony_ci s->first = ENTRY(header(s->base)+1); 19378c2ecf20Sopenharmony_ci header(s->base)->h_refcount = cpu_to_le32(1); 19388c2ecf20Sopenharmony_ci s->here = ENTRY(s->base + offset); 19398c2ecf20Sopenharmony_ci s->end = s->base + bs->bh->b_size; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci /* 19428c2ecf20Sopenharmony_ci * If existing entry points to an xattr inode, we need 19438c2ecf20Sopenharmony_ci * to prevent ext4_xattr_set_entry() from decrementing 19448c2ecf20Sopenharmony_ci * ref count on it because the reference belongs to the 19458c2ecf20Sopenharmony_ci * original block. In this case, make the entry look 19468c2ecf20Sopenharmony_ci * like it has an empty value. 19478c2ecf20Sopenharmony_ci */ 19488c2ecf20Sopenharmony_ci if (!s->not_found && s->here->e_value_inum) { 19498c2ecf20Sopenharmony_ci ea_ino = le32_to_cpu(s->here->e_value_inum); 19508c2ecf20Sopenharmony_ci error = ext4_xattr_inode_iget(inode, ea_ino, 19518c2ecf20Sopenharmony_ci le32_to_cpu(s->here->e_hash), 19528c2ecf20Sopenharmony_ci &tmp_inode); 19538c2ecf20Sopenharmony_ci if (error) 19548c2ecf20Sopenharmony_ci goto cleanup; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci if (!ext4_test_inode_state(tmp_inode, 19578c2ecf20Sopenharmony_ci EXT4_STATE_LUSTRE_EA_INODE)) { 19588c2ecf20Sopenharmony_ci /* 19598c2ecf20Sopenharmony_ci * Defer quota free call for previous 19608c2ecf20Sopenharmony_ci * inode until success is guaranteed. 19618c2ecf20Sopenharmony_ci */ 19628c2ecf20Sopenharmony_ci old_ea_inode_quota = le32_to_cpu( 19638c2ecf20Sopenharmony_ci s->here->e_value_size); 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci iput(tmp_inode); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci s->here->e_value_inum = 0; 19688c2ecf20Sopenharmony_ci s->here->e_value_size = 0; 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci } else { 19718c2ecf20Sopenharmony_ci /* Allocate a buffer where we construct the new block. */ 19728c2ecf20Sopenharmony_ci s->base = kzalloc(sb->s_blocksize, GFP_NOFS); 19738c2ecf20Sopenharmony_ci /* assert(header == s->base) */ 19748c2ecf20Sopenharmony_ci error = -ENOMEM; 19758c2ecf20Sopenharmony_ci if (s->base == NULL) 19768c2ecf20Sopenharmony_ci goto cleanup; 19778c2ecf20Sopenharmony_ci header(s->base)->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC); 19788c2ecf20Sopenharmony_ci header(s->base)->h_blocks = cpu_to_le32(1); 19798c2ecf20Sopenharmony_ci header(s->base)->h_refcount = cpu_to_le32(1); 19808c2ecf20Sopenharmony_ci s->first = ENTRY(header(s->base)+1); 19818c2ecf20Sopenharmony_ci s->here = ENTRY(header(s->base)+1); 19828c2ecf20Sopenharmony_ci s->end = s->base + sb->s_blocksize; 19838c2ecf20Sopenharmony_ci } 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci error = ext4_xattr_set_entry(i, s, handle, inode, true /* is_block */); 19868c2ecf20Sopenharmony_ci if (error == -EFSCORRUPTED) 19878c2ecf20Sopenharmony_ci goto bad_block; 19888c2ecf20Sopenharmony_ci if (error) 19898c2ecf20Sopenharmony_ci goto cleanup; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci if (i->value && s->here->e_value_inum) { 19928c2ecf20Sopenharmony_ci /* 19938c2ecf20Sopenharmony_ci * A ref count on ea_inode has been taken as part of the call to 19948c2ecf20Sopenharmony_ci * ext4_xattr_set_entry() above. We would like to drop this 19958c2ecf20Sopenharmony_ci * extra ref but we have to wait until the xattr block is 19968c2ecf20Sopenharmony_ci * initialized and has its own ref count on the ea_inode. 19978c2ecf20Sopenharmony_ci */ 19988c2ecf20Sopenharmony_ci ea_ino = le32_to_cpu(s->here->e_value_inum); 19998c2ecf20Sopenharmony_ci error = ext4_xattr_inode_iget(inode, ea_ino, 20008c2ecf20Sopenharmony_ci le32_to_cpu(s->here->e_hash), 20018c2ecf20Sopenharmony_ci &ea_inode); 20028c2ecf20Sopenharmony_ci if (error) { 20038c2ecf20Sopenharmony_ci ea_inode = NULL; 20048c2ecf20Sopenharmony_ci goto cleanup; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ciinserted: 20098c2ecf20Sopenharmony_ci if (!IS_LAST_ENTRY(s->first)) { 20108c2ecf20Sopenharmony_ci new_bh = ext4_xattr_block_cache_find(inode, header(s->base), 20118c2ecf20Sopenharmony_ci &ce); 20128c2ecf20Sopenharmony_ci if (new_bh) { 20138c2ecf20Sopenharmony_ci /* We found an identical block in the cache. */ 20148c2ecf20Sopenharmony_ci if (new_bh == bs->bh) 20158c2ecf20Sopenharmony_ci ea_bdebug(new_bh, "keeping"); 20168c2ecf20Sopenharmony_ci else { 20178c2ecf20Sopenharmony_ci u32 ref; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci#ifdef EXT4_XATTR_DEBUG 20208c2ecf20Sopenharmony_ci WARN_ON_ONCE(dquot_initialize_needed(inode)); 20218c2ecf20Sopenharmony_ci#endif 20228c2ecf20Sopenharmony_ci /* The old block is released after updating 20238c2ecf20Sopenharmony_ci the inode. */ 20248c2ecf20Sopenharmony_ci error = dquot_alloc_block(inode, 20258c2ecf20Sopenharmony_ci EXT4_C2B(EXT4_SB(sb), 1)); 20268c2ecf20Sopenharmony_ci if (error) 20278c2ecf20Sopenharmony_ci goto cleanup; 20288c2ecf20Sopenharmony_ci BUFFER_TRACE(new_bh, "get_write_access"); 20298c2ecf20Sopenharmony_ci error = ext4_journal_get_write_access(handle, 20308c2ecf20Sopenharmony_ci new_bh); 20318c2ecf20Sopenharmony_ci if (error) 20328c2ecf20Sopenharmony_ci goto cleanup_dquot; 20338c2ecf20Sopenharmony_ci lock_buffer(new_bh); 20348c2ecf20Sopenharmony_ci /* 20358c2ecf20Sopenharmony_ci * We have to be careful about races with 20368c2ecf20Sopenharmony_ci * adding references to xattr block. Once we 20378c2ecf20Sopenharmony_ci * hold buffer lock xattr block's state is 20388c2ecf20Sopenharmony_ci * stable so we can check the additional 20398c2ecf20Sopenharmony_ci * reference fits. 20408c2ecf20Sopenharmony_ci */ 20418c2ecf20Sopenharmony_ci ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1; 20428c2ecf20Sopenharmony_ci if (ref > EXT4_XATTR_REFCOUNT_MAX) { 20438c2ecf20Sopenharmony_ci /* 20448c2ecf20Sopenharmony_ci * Undo everything and check mbcache 20458c2ecf20Sopenharmony_ci * again. 20468c2ecf20Sopenharmony_ci */ 20478c2ecf20Sopenharmony_ci unlock_buffer(new_bh); 20488c2ecf20Sopenharmony_ci dquot_free_block(inode, 20498c2ecf20Sopenharmony_ci EXT4_C2B(EXT4_SB(sb), 20508c2ecf20Sopenharmony_ci 1)); 20518c2ecf20Sopenharmony_ci brelse(new_bh); 20528c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_block_cache, ce); 20538c2ecf20Sopenharmony_ci ce = NULL; 20548c2ecf20Sopenharmony_ci new_bh = NULL; 20558c2ecf20Sopenharmony_ci goto inserted; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci BHDR(new_bh)->h_refcount = cpu_to_le32(ref); 20588c2ecf20Sopenharmony_ci if (ref == EXT4_XATTR_REFCOUNT_MAX) 20598c2ecf20Sopenharmony_ci clear_bit(MBE_REUSABLE_B, &ce->e_flags); 20608c2ecf20Sopenharmony_ci ea_bdebug(new_bh, "reusing; refcount now=%d", 20618c2ecf20Sopenharmony_ci ref); 20628c2ecf20Sopenharmony_ci ext4_xattr_block_csum_set(inode, new_bh); 20638c2ecf20Sopenharmony_ci unlock_buffer(new_bh); 20648c2ecf20Sopenharmony_ci error = ext4_handle_dirty_metadata(handle, 20658c2ecf20Sopenharmony_ci inode, 20668c2ecf20Sopenharmony_ci new_bh); 20678c2ecf20Sopenharmony_ci if (error) 20688c2ecf20Sopenharmony_ci goto cleanup_dquot; 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci mb_cache_entry_touch(ea_block_cache, ce); 20718c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_block_cache, ce); 20728c2ecf20Sopenharmony_ci ce = NULL; 20738c2ecf20Sopenharmony_ci } else if (bs->bh && s->base == bs->bh->b_data) { 20748c2ecf20Sopenharmony_ci /* We were modifying this block in-place. */ 20758c2ecf20Sopenharmony_ci ea_bdebug(bs->bh, "keeping this block"); 20768c2ecf20Sopenharmony_ci ext4_xattr_block_cache_insert(ea_block_cache, bs->bh); 20778c2ecf20Sopenharmony_ci new_bh = bs->bh; 20788c2ecf20Sopenharmony_ci get_bh(new_bh); 20798c2ecf20Sopenharmony_ci } else { 20808c2ecf20Sopenharmony_ci /* We need to allocate a new block */ 20818c2ecf20Sopenharmony_ci ext4_fsblk_t goal, block; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci#ifdef EXT4_XATTR_DEBUG 20848c2ecf20Sopenharmony_ci WARN_ON_ONCE(dquot_initialize_needed(inode)); 20858c2ecf20Sopenharmony_ci#endif 20868c2ecf20Sopenharmony_ci goal = ext4_group_first_block_no(sb, 20878c2ecf20Sopenharmony_ci EXT4_I(inode)->i_block_group); 20888c2ecf20Sopenharmony_ci block = ext4_new_meta_blocks(handle, inode, goal, 0, 20898c2ecf20Sopenharmony_ci NULL, &error); 20908c2ecf20Sopenharmony_ci if (error) 20918c2ecf20Sopenharmony_ci goto cleanup; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci ea_idebug(inode, "creating block %llu", 20948c2ecf20Sopenharmony_ci (unsigned long long)block); 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci new_bh = sb_getblk(sb, block); 20978c2ecf20Sopenharmony_ci if (unlikely(!new_bh)) { 20988c2ecf20Sopenharmony_ci error = -ENOMEM; 20998c2ecf20Sopenharmony_cigetblk_failed: 21008c2ecf20Sopenharmony_ci ext4_free_blocks(handle, inode, NULL, block, 1, 21018c2ecf20Sopenharmony_ci EXT4_FREE_BLOCKS_METADATA); 21028c2ecf20Sopenharmony_ci goto cleanup; 21038c2ecf20Sopenharmony_ci } 21048c2ecf20Sopenharmony_ci error = ext4_xattr_inode_inc_ref_all(handle, inode, 21058c2ecf20Sopenharmony_ci ENTRY(header(s->base)+1)); 21068c2ecf20Sopenharmony_ci if (error) 21078c2ecf20Sopenharmony_ci goto getblk_failed; 21088c2ecf20Sopenharmony_ci if (ea_inode) { 21098c2ecf20Sopenharmony_ci /* Drop the extra ref on ea_inode. */ 21108c2ecf20Sopenharmony_ci error = ext4_xattr_inode_dec_ref(handle, 21118c2ecf20Sopenharmony_ci ea_inode); 21128c2ecf20Sopenharmony_ci if (error) 21138c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, 21148c2ecf20Sopenharmony_ci "dec ref error=%d", 21158c2ecf20Sopenharmony_ci error); 21168c2ecf20Sopenharmony_ci iput(ea_inode); 21178c2ecf20Sopenharmony_ci ea_inode = NULL; 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci lock_buffer(new_bh); 21218c2ecf20Sopenharmony_ci error = ext4_journal_get_create_access(handle, new_bh); 21228c2ecf20Sopenharmony_ci if (error) { 21238c2ecf20Sopenharmony_ci unlock_buffer(new_bh); 21248c2ecf20Sopenharmony_ci error = -EIO; 21258c2ecf20Sopenharmony_ci goto getblk_failed; 21268c2ecf20Sopenharmony_ci } 21278c2ecf20Sopenharmony_ci memcpy(new_bh->b_data, s->base, new_bh->b_size); 21288c2ecf20Sopenharmony_ci ext4_xattr_block_csum_set(inode, new_bh); 21298c2ecf20Sopenharmony_ci set_buffer_uptodate(new_bh); 21308c2ecf20Sopenharmony_ci unlock_buffer(new_bh); 21318c2ecf20Sopenharmony_ci ext4_xattr_block_cache_insert(ea_block_cache, new_bh); 21328c2ecf20Sopenharmony_ci error = ext4_handle_dirty_metadata(handle, inode, 21338c2ecf20Sopenharmony_ci new_bh); 21348c2ecf20Sopenharmony_ci if (error) 21358c2ecf20Sopenharmony_ci goto cleanup; 21368c2ecf20Sopenharmony_ci } 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci if (old_ea_inode_quota) 21408c2ecf20Sopenharmony_ci ext4_xattr_inode_free_quota(inode, NULL, old_ea_inode_quota); 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci /* Update the inode. */ 21438c2ecf20Sopenharmony_ci EXT4_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci /* Drop the previous xattr block. */ 21468c2ecf20Sopenharmony_ci if (bs->bh && bs->bh != new_bh) { 21478c2ecf20Sopenharmony_ci struct ext4_xattr_inode_array *ea_inode_array = NULL; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci ext4_xattr_release_block(handle, inode, bs->bh, 21508c2ecf20Sopenharmony_ci &ea_inode_array, 21518c2ecf20Sopenharmony_ci 0 /* extra_credits */); 21528c2ecf20Sopenharmony_ci ext4_xattr_inode_array_free(ea_inode_array); 21538c2ecf20Sopenharmony_ci } 21548c2ecf20Sopenharmony_ci error = 0; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_cicleanup: 21578c2ecf20Sopenharmony_ci if (ea_inode) { 21588c2ecf20Sopenharmony_ci int error2; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci error2 = ext4_xattr_inode_dec_ref(handle, ea_inode); 21618c2ecf20Sopenharmony_ci if (error2) 21628c2ecf20Sopenharmony_ci ext4_warning_inode(ea_inode, "dec ref error=%d", 21638c2ecf20Sopenharmony_ci error2); 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci /* If there was an error, revert the quota charge. */ 21668c2ecf20Sopenharmony_ci if (error) 21678c2ecf20Sopenharmony_ci ext4_xattr_inode_free_quota(inode, ea_inode, 21688c2ecf20Sopenharmony_ci i_size_read(ea_inode)); 21698c2ecf20Sopenharmony_ci iput(ea_inode); 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci if (ce) 21728c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_block_cache, ce); 21738c2ecf20Sopenharmony_ci brelse(new_bh); 21748c2ecf20Sopenharmony_ci if (!(bs->bh && s->base == bs->bh->b_data)) 21758c2ecf20Sopenharmony_ci kfree(s->base); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci return error; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_cicleanup_dquot: 21808c2ecf20Sopenharmony_ci dquot_free_block(inode, EXT4_C2B(EXT4_SB(sb), 1)); 21818c2ecf20Sopenharmony_ci goto cleanup; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_cibad_block: 21848c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "bad block %llu", 21858c2ecf20Sopenharmony_ci EXT4_I(inode)->i_file_acl); 21868c2ecf20Sopenharmony_ci goto cleanup; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci#undef header 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ciint ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, 21928c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_find *is) 21938c2ecf20Sopenharmony_ci{ 21948c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header; 21958c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode; 21968c2ecf20Sopenharmony_ci int error; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci if (!EXT4_INODE_HAS_XATTR_SPACE(inode)) 21998c2ecf20Sopenharmony_ci return 0; 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci raw_inode = ext4_raw_inode(&is->iloc); 22028c2ecf20Sopenharmony_ci header = IHDR(inode, raw_inode); 22038c2ecf20Sopenharmony_ci is->s.base = is->s.first = IFIRST(header); 22048c2ecf20Sopenharmony_ci is->s.here = is->s.first; 22058c2ecf20Sopenharmony_ci is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; 22068c2ecf20Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { 22078c2ecf20Sopenharmony_ci error = xattr_check_inode(inode, header, is->s.end); 22088c2ecf20Sopenharmony_ci if (error) 22098c2ecf20Sopenharmony_ci return error; 22108c2ecf20Sopenharmony_ci /* Find the named attribute. */ 22118c2ecf20Sopenharmony_ci error = xattr_find_entry(inode, &is->s.here, is->s.end, 22128c2ecf20Sopenharmony_ci i->name_index, i->name, 0); 22138c2ecf20Sopenharmony_ci if (error && error != -ENODATA) 22148c2ecf20Sopenharmony_ci return error; 22158c2ecf20Sopenharmony_ci is->s.not_found = error; 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci return 0; 22188c2ecf20Sopenharmony_ci} 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ciint ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, 22218c2ecf20Sopenharmony_ci struct ext4_xattr_info *i, 22228c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_find *is) 22238c2ecf20Sopenharmony_ci{ 22248c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header; 22258c2ecf20Sopenharmony_ci struct ext4_xattr_search *s = &is->s; 22268c2ecf20Sopenharmony_ci int error; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci if (!EXT4_INODE_HAS_XATTR_SPACE(inode)) 22298c2ecf20Sopenharmony_ci return -ENOSPC; 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci error = ext4_xattr_set_entry(i, s, handle, inode, false /* is_block */); 22328c2ecf20Sopenharmony_ci if (error) 22338c2ecf20Sopenharmony_ci return error; 22348c2ecf20Sopenharmony_ci header = IHDR(inode, ext4_raw_inode(&is->iloc)); 22358c2ecf20Sopenharmony_ci if (!IS_LAST_ENTRY(s->first)) { 22368c2ecf20Sopenharmony_ci header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC); 22378c2ecf20Sopenharmony_ci ext4_set_inode_state(inode, EXT4_STATE_XATTR); 22388c2ecf20Sopenharmony_ci } else { 22398c2ecf20Sopenharmony_ci header->h_magic = cpu_to_le32(0); 22408c2ecf20Sopenharmony_ci ext4_clear_inode_state(inode, EXT4_STATE_XATTR); 22418c2ecf20Sopenharmony_ci } 22428c2ecf20Sopenharmony_ci return 0; 22438c2ecf20Sopenharmony_ci} 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_cistatic int ext4_xattr_value_same(struct ext4_xattr_search *s, 22468c2ecf20Sopenharmony_ci struct ext4_xattr_info *i) 22478c2ecf20Sopenharmony_ci{ 22488c2ecf20Sopenharmony_ci void *value; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci /* When e_value_inum is set the value is stored externally. */ 22518c2ecf20Sopenharmony_ci if (s->here->e_value_inum) 22528c2ecf20Sopenharmony_ci return 0; 22538c2ecf20Sopenharmony_ci if (le32_to_cpu(s->here->e_value_size) != i->value_len) 22548c2ecf20Sopenharmony_ci return 0; 22558c2ecf20Sopenharmony_ci value = ((void *)s->base) + le16_to_cpu(s->here->e_value_offs); 22568c2ecf20Sopenharmony_ci return !memcmp(value, i->value, i->value_len); 22578c2ecf20Sopenharmony_ci} 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_cistatic struct buffer_head *ext4_xattr_get_block(struct inode *inode) 22608c2ecf20Sopenharmony_ci{ 22618c2ecf20Sopenharmony_ci struct buffer_head *bh; 22628c2ecf20Sopenharmony_ci int error; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci if (!EXT4_I(inode)->i_file_acl) 22658c2ecf20Sopenharmony_ci return NULL; 22668c2ecf20Sopenharmony_ci bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); 22678c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 22688c2ecf20Sopenharmony_ci return bh; 22698c2ecf20Sopenharmony_ci error = ext4_xattr_check_block(inode, bh); 22708c2ecf20Sopenharmony_ci if (error) { 22718c2ecf20Sopenharmony_ci brelse(bh); 22728c2ecf20Sopenharmony_ci return ERR_PTR(error); 22738c2ecf20Sopenharmony_ci } 22748c2ecf20Sopenharmony_ci return bh; 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci/* 22788c2ecf20Sopenharmony_ci * ext4_xattr_set_handle() 22798c2ecf20Sopenharmony_ci * 22808c2ecf20Sopenharmony_ci * Create, replace or remove an extended attribute for this inode. Value 22818c2ecf20Sopenharmony_ci * is NULL to remove an existing extended attribute, and non-NULL to 22828c2ecf20Sopenharmony_ci * either replace an existing extended attribute, or create a new extended 22838c2ecf20Sopenharmony_ci * attribute. The flags XATTR_REPLACE and XATTR_CREATE 22848c2ecf20Sopenharmony_ci * specify that an extended attribute must exist and must not exist 22858c2ecf20Sopenharmony_ci * previous to the call, respectively. 22868c2ecf20Sopenharmony_ci * 22878c2ecf20Sopenharmony_ci * Returns 0, or a negative error number on failure. 22888c2ecf20Sopenharmony_ci */ 22898c2ecf20Sopenharmony_ciint 22908c2ecf20Sopenharmony_ciext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index, 22918c2ecf20Sopenharmony_ci const char *name, const void *value, size_t value_len, 22928c2ecf20Sopenharmony_ci int flags) 22938c2ecf20Sopenharmony_ci{ 22948c2ecf20Sopenharmony_ci struct ext4_xattr_info i = { 22958c2ecf20Sopenharmony_ci .name_index = name_index, 22968c2ecf20Sopenharmony_ci .name = name, 22978c2ecf20Sopenharmony_ci .value = value, 22988c2ecf20Sopenharmony_ci .value_len = value_len, 22998c2ecf20Sopenharmony_ci .in_inode = 0, 23008c2ecf20Sopenharmony_ci }; 23018c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_find is = { 23028c2ecf20Sopenharmony_ci .s = { .not_found = -ENODATA, }, 23038c2ecf20Sopenharmony_ci }; 23048c2ecf20Sopenharmony_ci struct ext4_xattr_block_find bs = { 23058c2ecf20Sopenharmony_ci .s = { .not_found = -ENODATA, }, 23068c2ecf20Sopenharmony_ci }; 23078c2ecf20Sopenharmony_ci int no_expand; 23088c2ecf20Sopenharmony_ci int error; 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci if (!name) 23118c2ecf20Sopenharmony_ci return -EINVAL; 23128c2ecf20Sopenharmony_ci if (strlen(name) > 255) 23138c2ecf20Sopenharmony_ci return -ERANGE; 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci ext4_write_lock_xattr(inode, &no_expand); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci /* Check journal credits under write lock. */ 23188c2ecf20Sopenharmony_ci if (ext4_handle_valid(handle)) { 23198c2ecf20Sopenharmony_ci struct buffer_head *bh; 23208c2ecf20Sopenharmony_ci int credits; 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci bh = ext4_xattr_get_block(inode); 23238c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 23248c2ecf20Sopenharmony_ci error = PTR_ERR(bh); 23258c2ecf20Sopenharmony_ci goto cleanup; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci credits = __ext4_xattr_set_credits(inode->i_sb, inode, bh, 23298c2ecf20Sopenharmony_ci value_len, 23308c2ecf20Sopenharmony_ci flags & XATTR_CREATE); 23318c2ecf20Sopenharmony_ci brelse(bh); 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci if (jbd2_handle_buffer_credits(handle) < credits) { 23348c2ecf20Sopenharmony_ci error = -ENOSPC; 23358c2ecf20Sopenharmony_ci goto cleanup; 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(current->flags & PF_MEMALLOC_NOFS)); 23388c2ecf20Sopenharmony_ci } 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci error = ext4_reserve_inode_write(handle, inode, &is.iloc); 23418c2ecf20Sopenharmony_ci if (error) 23428c2ecf20Sopenharmony_ci goto cleanup; 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_NEW)) { 23458c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode = ext4_raw_inode(&is.iloc); 23468c2ecf20Sopenharmony_ci memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size); 23478c2ecf20Sopenharmony_ci ext4_clear_inode_state(inode, EXT4_STATE_NEW); 23488c2ecf20Sopenharmony_ci } 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci error = ext4_xattr_ibody_find(inode, &i, &is); 23518c2ecf20Sopenharmony_ci if (error) 23528c2ecf20Sopenharmony_ci goto cleanup; 23538c2ecf20Sopenharmony_ci if (is.s.not_found) 23548c2ecf20Sopenharmony_ci error = ext4_xattr_block_find(inode, &i, &bs); 23558c2ecf20Sopenharmony_ci if (error) 23568c2ecf20Sopenharmony_ci goto cleanup; 23578c2ecf20Sopenharmony_ci if (is.s.not_found && bs.s.not_found) { 23588c2ecf20Sopenharmony_ci error = -ENODATA; 23598c2ecf20Sopenharmony_ci if (flags & XATTR_REPLACE) 23608c2ecf20Sopenharmony_ci goto cleanup; 23618c2ecf20Sopenharmony_ci error = 0; 23628c2ecf20Sopenharmony_ci if (!value) 23638c2ecf20Sopenharmony_ci goto cleanup; 23648c2ecf20Sopenharmony_ci } else { 23658c2ecf20Sopenharmony_ci error = -EEXIST; 23668c2ecf20Sopenharmony_ci if (flags & XATTR_CREATE) 23678c2ecf20Sopenharmony_ci goto cleanup; 23688c2ecf20Sopenharmony_ci } 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci if (!value) { 23718c2ecf20Sopenharmony_ci if (!is.s.not_found) 23728c2ecf20Sopenharmony_ci error = ext4_xattr_ibody_set(handle, inode, &i, &is); 23738c2ecf20Sopenharmony_ci else if (!bs.s.not_found) 23748c2ecf20Sopenharmony_ci error = ext4_xattr_block_set(handle, inode, &i, &bs); 23758c2ecf20Sopenharmony_ci } else { 23768c2ecf20Sopenharmony_ci error = 0; 23778c2ecf20Sopenharmony_ci /* Xattr value did not change? Save us some work and bail out */ 23788c2ecf20Sopenharmony_ci if (!is.s.not_found && ext4_xattr_value_same(&is.s, &i)) 23798c2ecf20Sopenharmony_ci goto cleanup; 23808c2ecf20Sopenharmony_ci if (!bs.s.not_found && ext4_xattr_value_same(&bs.s, &i)) 23818c2ecf20Sopenharmony_ci goto cleanup; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci if (ext4_has_feature_ea_inode(inode->i_sb) && 23848c2ecf20Sopenharmony_ci (EXT4_XATTR_SIZE(i.value_len) > 23858c2ecf20Sopenharmony_ci EXT4_XATTR_MIN_LARGE_EA_SIZE(inode->i_sb->s_blocksize))) 23868c2ecf20Sopenharmony_ci i.in_inode = 1; 23878c2ecf20Sopenharmony_ciretry_inode: 23888c2ecf20Sopenharmony_ci error = ext4_xattr_ibody_set(handle, inode, &i, &is); 23898c2ecf20Sopenharmony_ci if (!error && !bs.s.not_found) { 23908c2ecf20Sopenharmony_ci i.value = NULL; 23918c2ecf20Sopenharmony_ci error = ext4_xattr_block_set(handle, inode, &i, &bs); 23928c2ecf20Sopenharmony_ci } else if (error == -ENOSPC) { 23938c2ecf20Sopenharmony_ci if (EXT4_I(inode)->i_file_acl && !bs.s.base) { 23948c2ecf20Sopenharmony_ci brelse(bs.bh); 23958c2ecf20Sopenharmony_ci bs.bh = NULL; 23968c2ecf20Sopenharmony_ci error = ext4_xattr_block_find(inode, &i, &bs); 23978c2ecf20Sopenharmony_ci if (error) 23988c2ecf20Sopenharmony_ci goto cleanup; 23998c2ecf20Sopenharmony_ci } 24008c2ecf20Sopenharmony_ci error = ext4_xattr_block_set(handle, inode, &i, &bs); 24018c2ecf20Sopenharmony_ci if (!error && !is.s.not_found) { 24028c2ecf20Sopenharmony_ci i.value = NULL; 24038c2ecf20Sopenharmony_ci error = ext4_xattr_ibody_set(handle, inode, &i, 24048c2ecf20Sopenharmony_ci &is); 24058c2ecf20Sopenharmony_ci } else if (error == -ENOSPC) { 24068c2ecf20Sopenharmony_ci /* 24078c2ecf20Sopenharmony_ci * Xattr does not fit in the block, store at 24088c2ecf20Sopenharmony_ci * external inode if possible. 24098c2ecf20Sopenharmony_ci */ 24108c2ecf20Sopenharmony_ci if (ext4_has_feature_ea_inode(inode->i_sb) && 24118c2ecf20Sopenharmony_ci i.value_len && !i.in_inode) { 24128c2ecf20Sopenharmony_ci i.in_inode = 1; 24138c2ecf20Sopenharmony_ci goto retry_inode; 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci } 24168c2ecf20Sopenharmony_ci } 24178c2ecf20Sopenharmony_ci } 24188c2ecf20Sopenharmony_ci if (!error) { 24198c2ecf20Sopenharmony_ci ext4_xattr_update_super_block(handle, inode->i_sb); 24208c2ecf20Sopenharmony_ci inode->i_ctime = current_time(inode); 24218c2ecf20Sopenharmony_ci if (!value) 24228c2ecf20Sopenharmony_ci no_expand = 0; 24238c2ecf20Sopenharmony_ci error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); 24248c2ecf20Sopenharmony_ci /* 24258c2ecf20Sopenharmony_ci * The bh is consumed by ext4_mark_iloc_dirty, even with 24268c2ecf20Sopenharmony_ci * error != 0. 24278c2ecf20Sopenharmony_ci */ 24288c2ecf20Sopenharmony_ci is.iloc.bh = NULL; 24298c2ecf20Sopenharmony_ci if (IS_SYNC(inode)) 24308c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 24318c2ecf20Sopenharmony_ci } 24328c2ecf20Sopenharmony_ci ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR); 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_cicleanup: 24358c2ecf20Sopenharmony_ci brelse(is.iloc.bh); 24368c2ecf20Sopenharmony_ci brelse(bs.bh); 24378c2ecf20Sopenharmony_ci ext4_write_unlock_xattr(inode, &no_expand); 24388c2ecf20Sopenharmony_ci return error; 24398c2ecf20Sopenharmony_ci} 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ciint ext4_xattr_set_credits(struct inode *inode, size_t value_len, 24428c2ecf20Sopenharmony_ci bool is_create, int *credits) 24438c2ecf20Sopenharmony_ci{ 24448c2ecf20Sopenharmony_ci struct buffer_head *bh; 24458c2ecf20Sopenharmony_ci int err; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci *credits = 0; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci if (!EXT4_SB(inode->i_sb)->s_journal) 24508c2ecf20Sopenharmony_ci return 0; 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci down_read(&EXT4_I(inode)->xattr_sem); 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci bh = ext4_xattr_get_block(inode); 24558c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 24568c2ecf20Sopenharmony_ci err = PTR_ERR(bh); 24578c2ecf20Sopenharmony_ci } else { 24588c2ecf20Sopenharmony_ci *credits = __ext4_xattr_set_credits(inode->i_sb, inode, bh, 24598c2ecf20Sopenharmony_ci value_len, is_create); 24608c2ecf20Sopenharmony_ci brelse(bh); 24618c2ecf20Sopenharmony_ci err = 0; 24628c2ecf20Sopenharmony_ci } 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci up_read(&EXT4_I(inode)->xattr_sem); 24658c2ecf20Sopenharmony_ci return err; 24668c2ecf20Sopenharmony_ci} 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci/* 24698c2ecf20Sopenharmony_ci * ext4_xattr_set() 24708c2ecf20Sopenharmony_ci * 24718c2ecf20Sopenharmony_ci * Like ext4_xattr_set_handle, but start from an inode. This extended 24728c2ecf20Sopenharmony_ci * attribute modification is a filesystem transaction by itself. 24738c2ecf20Sopenharmony_ci * 24748c2ecf20Sopenharmony_ci * Returns 0, or a negative error number on failure. 24758c2ecf20Sopenharmony_ci */ 24768c2ecf20Sopenharmony_ciint 24778c2ecf20Sopenharmony_ciext4_xattr_set(struct inode *inode, int name_index, const char *name, 24788c2ecf20Sopenharmony_ci const void *value, size_t value_len, int flags) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci handle_t *handle; 24818c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 24828c2ecf20Sopenharmony_ci int error, retries = 0; 24838c2ecf20Sopenharmony_ci int credits; 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci error = dquot_initialize(inode); 24868c2ecf20Sopenharmony_ci if (error) 24878c2ecf20Sopenharmony_ci return error; 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ciretry: 24908c2ecf20Sopenharmony_ci error = ext4_xattr_set_credits(inode, value_len, flags & XATTR_CREATE, 24918c2ecf20Sopenharmony_ci &credits); 24928c2ecf20Sopenharmony_ci if (error) 24938c2ecf20Sopenharmony_ci return error; 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits); 24968c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 24978c2ecf20Sopenharmony_ci error = PTR_ERR(handle); 24988c2ecf20Sopenharmony_ci } else { 24998c2ecf20Sopenharmony_ci int error2; 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci error = ext4_xattr_set_handle(handle, inode, name_index, name, 25028c2ecf20Sopenharmony_ci value, value_len, flags); 25038c2ecf20Sopenharmony_ci error2 = ext4_journal_stop(handle); 25048c2ecf20Sopenharmony_ci if (error == -ENOSPC && 25058c2ecf20Sopenharmony_ci ext4_should_retry_alloc(sb, &retries)) 25068c2ecf20Sopenharmony_ci goto retry; 25078c2ecf20Sopenharmony_ci if (error == 0) 25088c2ecf20Sopenharmony_ci error = error2; 25098c2ecf20Sopenharmony_ci } 25108c2ecf20Sopenharmony_ci ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR); 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci return error; 25138c2ecf20Sopenharmony_ci} 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci/* 25168c2ecf20Sopenharmony_ci * Shift the EA entries in the inode to create space for the increased 25178c2ecf20Sopenharmony_ci * i_extra_isize. 25188c2ecf20Sopenharmony_ci */ 25198c2ecf20Sopenharmony_cistatic void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry, 25208c2ecf20Sopenharmony_ci int value_offs_shift, void *to, 25218c2ecf20Sopenharmony_ci void *from, size_t n) 25228c2ecf20Sopenharmony_ci{ 25238c2ecf20Sopenharmony_ci struct ext4_xattr_entry *last = entry; 25248c2ecf20Sopenharmony_ci int new_offs; 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci /* We always shift xattr headers further thus offsets get lower */ 25278c2ecf20Sopenharmony_ci BUG_ON(value_offs_shift > 0); 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci /* Adjust the value offsets of the entries */ 25308c2ecf20Sopenharmony_ci for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { 25318c2ecf20Sopenharmony_ci if (!last->e_value_inum && last->e_value_size) { 25328c2ecf20Sopenharmony_ci new_offs = le16_to_cpu(last->e_value_offs) + 25338c2ecf20Sopenharmony_ci value_offs_shift; 25348c2ecf20Sopenharmony_ci last->e_value_offs = cpu_to_le16(new_offs); 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci /* Shift the entries by n bytes */ 25388c2ecf20Sopenharmony_ci memmove(to, from, n); 25398c2ecf20Sopenharmony_ci} 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci/* 25428c2ecf20Sopenharmony_ci * Move xattr pointed to by 'entry' from inode into external xattr block 25438c2ecf20Sopenharmony_ci */ 25448c2ecf20Sopenharmony_cistatic int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode, 25458c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode, 25468c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry) 25478c2ecf20Sopenharmony_ci{ 25488c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_find *is = NULL; 25498c2ecf20Sopenharmony_ci struct ext4_xattr_block_find *bs = NULL; 25508c2ecf20Sopenharmony_ci char *buffer = NULL, *b_entry_name = NULL; 25518c2ecf20Sopenharmony_ci size_t value_size = le32_to_cpu(entry->e_value_size); 25528c2ecf20Sopenharmony_ci struct ext4_xattr_info i = { 25538c2ecf20Sopenharmony_ci .value = NULL, 25548c2ecf20Sopenharmony_ci .value_len = 0, 25558c2ecf20Sopenharmony_ci .name_index = entry->e_name_index, 25568c2ecf20Sopenharmony_ci .in_inode = !!entry->e_value_inum, 25578c2ecf20Sopenharmony_ci }; 25588c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header = IHDR(inode, raw_inode); 25598c2ecf20Sopenharmony_ci int needs_kvfree = 0; 25608c2ecf20Sopenharmony_ci int error; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS); 25638c2ecf20Sopenharmony_ci bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS); 25648c2ecf20Sopenharmony_ci b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS); 25658c2ecf20Sopenharmony_ci if (!is || !bs || !b_entry_name) { 25668c2ecf20Sopenharmony_ci error = -ENOMEM; 25678c2ecf20Sopenharmony_ci goto out; 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci is->s.not_found = -ENODATA; 25718c2ecf20Sopenharmony_ci bs->s.not_found = -ENODATA; 25728c2ecf20Sopenharmony_ci is->iloc.bh = NULL; 25738c2ecf20Sopenharmony_ci bs->bh = NULL; 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci /* Save the entry name and the entry value */ 25768c2ecf20Sopenharmony_ci if (entry->e_value_inum) { 25778c2ecf20Sopenharmony_ci buffer = kvmalloc(value_size, GFP_NOFS); 25788c2ecf20Sopenharmony_ci if (!buffer) { 25798c2ecf20Sopenharmony_ci error = -ENOMEM; 25808c2ecf20Sopenharmony_ci goto out; 25818c2ecf20Sopenharmony_ci } 25828c2ecf20Sopenharmony_ci needs_kvfree = 1; 25838c2ecf20Sopenharmony_ci error = ext4_xattr_inode_get(inode, entry, buffer, value_size); 25848c2ecf20Sopenharmony_ci if (error) 25858c2ecf20Sopenharmony_ci goto out; 25868c2ecf20Sopenharmony_ci } else { 25878c2ecf20Sopenharmony_ci size_t value_offs = le16_to_cpu(entry->e_value_offs); 25888c2ecf20Sopenharmony_ci buffer = (void *)IFIRST(header) + value_offs; 25898c2ecf20Sopenharmony_ci } 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci memcpy(b_entry_name, entry->e_name, entry->e_name_len); 25928c2ecf20Sopenharmony_ci b_entry_name[entry->e_name_len] = '\0'; 25938c2ecf20Sopenharmony_ci i.name = b_entry_name; 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci error = ext4_get_inode_loc(inode, &is->iloc); 25968c2ecf20Sopenharmony_ci if (error) 25978c2ecf20Sopenharmony_ci goto out; 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci error = ext4_xattr_ibody_find(inode, &i, is); 26008c2ecf20Sopenharmony_ci if (error) 26018c2ecf20Sopenharmony_ci goto out; 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci i.value = buffer; 26048c2ecf20Sopenharmony_ci i.value_len = value_size; 26058c2ecf20Sopenharmony_ci error = ext4_xattr_block_find(inode, &i, bs); 26068c2ecf20Sopenharmony_ci if (error) 26078c2ecf20Sopenharmony_ci goto out; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci /* Move ea entry from the inode into the block */ 26108c2ecf20Sopenharmony_ci error = ext4_xattr_block_set(handle, inode, &i, bs); 26118c2ecf20Sopenharmony_ci if (error) 26128c2ecf20Sopenharmony_ci goto out; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci /* Remove the chosen entry from the inode */ 26158c2ecf20Sopenharmony_ci i.value = NULL; 26168c2ecf20Sopenharmony_ci i.value_len = 0; 26178c2ecf20Sopenharmony_ci error = ext4_xattr_ibody_set(handle, inode, &i, is); 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ciout: 26208c2ecf20Sopenharmony_ci kfree(b_entry_name); 26218c2ecf20Sopenharmony_ci if (needs_kvfree && buffer) 26228c2ecf20Sopenharmony_ci kvfree(buffer); 26238c2ecf20Sopenharmony_ci if (is) 26248c2ecf20Sopenharmony_ci brelse(is->iloc.bh); 26258c2ecf20Sopenharmony_ci if (bs) 26268c2ecf20Sopenharmony_ci brelse(bs->bh); 26278c2ecf20Sopenharmony_ci kfree(is); 26288c2ecf20Sopenharmony_ci kfree(bs); 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci return error; 26318c2ecf20Sopenharmony_ci} 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_cistatic int ext4_xattr_make_inode_space(handle_t *handle, struct inode *inode, 26348c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode, 26358c2ecf20Sopenharmony_ci int isize_diff, size_t ifree, 26368c2ecf20Sopenharmony_ci size_t bfree, int *total_ino) 26378c2ecf20Sopenharmony_ci{ 26388c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header = IHDR(inode, raw_inode); 26398c2ecf20Sopenharmony_ci struct ext4_xattr_entry *small_entry; 26408c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry; 26418c2ecf20Sopenharmony_ci struct ext4_xattr_entry *last; 26428c2ecf20Sopenharmony_ci unsigned int entry_size; /* EA entry size */ 26438c2ecf20Sopenharmony_ci unsigned int total_size; /* EA entry size + value size */ 26448c2ecf20Sopenharmony_ci unsigned int min_total_size; 26458c2ecf20Sopenharmony_ci int error; 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci while (isize_diff > ifree) { 26488c2ecf20Sopenharmony_ci entry = NULL; 26498c2ecf20Sopenharmony_ci small_entry = NULL; 26508c2ecf20Sopenharmony_ci min_total_size = ~0U; 26518c2ecf20Sopenharmony_ci last = IFIRST(header); 26528c2ecf20Sopenharmony_ci /* Find the entry best suited to be pushed into EA block */ 26538c2ecf20Sopenharmony_ci for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { 26548c2ecf20Sopenharmony_ci /* never move system.data out of the inode */ 26558c2ecf20Sopenharmony_ci if ((last->e_name_len == 4) && 26568c2ecf20Sopenharmony_ci (last->e_name_index == EXT4_XATTR_INDEX_SYSTEM) && 26578c2ecf20Sopenharmony_ci !memcmp(last->e_name, "data", 4)) 26588c2ecf20Sopenharmony_ci continue; 26598c2ecf20Sopenharmony_ci total_size = EXT4_XATTR_LEN(last->e_name_len); 26608c2ecf20Sopenharmony_ci if (!last->e_value_inum) 26618c2ecf20Sopenharmony_ci total_size += EXT4_XATTR_SIZE( 26628c2ecf20Sopenharmony_ci le32_to_cpu(last->e_value_size)); 26638c2ecf20Sopenharmony_ci if (total_size <= bfree && 26648c2ecf20Sopenharmony_ci total_size < min_total_size) { 26658c2ecf20Sopenharmony_ci if (total_size + ifree < isize_diff) { 26668c2ecf20Sopenharmony_ci small_entry = last; 26678c2ecf20Sopenharmony_ci } else { 26688c2ecf20Sopenharmony_ci entry = last; 26698c2ecf20Sopenharmony_ci min_total_size = total_size; 26708c2ecf20Sopenharmony_ci } 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci } 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci if (entry == NULL) { 26758c2ecf20Sopenharmony_ci if (small_entry == NULL) 26768c2ecf20Sopenharmony_ci return -ENOSPC; 26778c2ecf20Sopenharmony_ci entry = small_entry; 26788c2ecf20Sopenharmony_ci } 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci entry_size = EXT4_XATTR_LEN(entry->e_name_len); 26818c2ecf20Sopenharmony_ci total_size = entry_size; 26828c2ecf20Sopenharmony_ci if (!entry->e_value_inum) 26838c2ecf20Sopenharmony_ci total_size += EXT4_XATTR_SIZE( 26848c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_value_size)); 26858c2ecf20Sopenharmony_ci error = ext4_xattr_move_to_block(handle, inode, raw_inode, 26868c2ecf20Sopenharmony_ci entry); 26878c2ecf20Sopenharmony_ci if (error) 26888c2ecf20Sopenharmony_ci return error; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci *total_ino -= entry_size; 26918c2ecf20Sopenharmony_ci ifree += total_size; 26928c2ecf20Sopenharmony_ci bfree -= total_size; 26938c2ecf20Sopenharmony_ci } 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci return 0; 26968c2ecf20Sopenharmony_ci} 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci/* 26998c2ecf20Sopenharmony_ci * Expand an inode by new_extra_isize bytes when EAs are present. 27008c2ecf20Sopenharmony_ci * Returns 0 on success or negative error number on failure. 27018c2ecf20Sopenharmony_ci */ 27028c2ecf20Sopenharmony_ciint ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, 27038c2ecf20Sopenharmony_ci struct ext4_inode *raw_inode, handle_t *handle) 27048c2ecf20Sopenharmony_ci{ 27058c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header; 27068c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 27078c2ecf20Sopenharmony_ci static unsigned int mnt_count; 27088c2ecf20Sopenharmony_ci size_t min_offs; 27098c2ecf20Sopenharmony_ci size_t ifree, bfree; 27108c2ecf20Sopenharmony_ci int total_ino; 27118c2ecf20Sopenharmony_ci void *base, *end; 27128c2ecf20Sopenharmony_ci int error = 0, tried_min_extra_isize = 0; 27138c2ecf20Sopenharmony_ci int s_min_extra_isize = le16_to_cpu(sbi->s_es->s_min_extra_isize); 27148c2ecf20Sopenharmony_ci int isize_diff; /* How much do we need to grow i_extra_isize */ 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ciretry: 27178c2ecf20Sopenharmony_ci isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize; 27188c2ecf20Sopenharmony_ci if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) 27198c2ecf20Sopenharmony_ci return 0; 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci header = IHDR(inode, raw_inode); 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci /* 27248c2ecf20Sopenharmony_ci * Check if enough free space is available in the inode to shift the 27258c2ecf20Sopenharmony_ci * entries ahead by new_extra_isize. 27268c2ecf20Sopenharmony_ci */ 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci base = IFIRST(header); 27298c2ecf20Sopenharmony_ci end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; 27308c2ecf20Sopenharmony_ci min_offs = end - base; 27318c2ecf20Sopenharmony_ci total_ino = sizeof(struct ext4_xattr_ibody_header) + sizeof(u32); 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci error = xattr_check_inode(inode, header, end); 27348c2ecf20Sopenharmony_ci if (error) 27358c2ecf20Sopenharmony_ci goto cleanup; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci ifree = ext4_xattr_free_space(base, &min_offs, base, &total_ino); 27388c2ecf20Sopenharmony_ci if (ifree >= isize_diff) 27398c2ecf20Sopenharmony_ci goto shift; 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci /* 27428c2ecf20Sopenharmony_ci * Enough free space isn't available in the inode, check if 27438c2ecf20Sopenharmony_ci * EA block can hold new_extra_isize bytes. 27448c2ecf20Sopenharmony_ci */ 27458c2ecf20Sopenharmony_ci if (EXT4_I(inode)->i_file_acl) { 27468c2ecf20Sopenharmony_ci struct buffer_head *bh; 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); 27498c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 27508c2ecf20Sopenharmony_ci error = PTR_ERR(bh); 27518c2ecf20Sopenharmony_ci goto cleanup; 27528c2ecf20Sopenharmony_ci } 27538c2ecf20Sopenharmony_ci error = ext4_xattr_check_block(inode, bh); 27548c2ecf20Sopenharmony_ci if (error) { 27558c2ecf20Sopenharmony_ci brelse(bh); 27568c2ecf20Sopenharmony_ci goto cleanup; 27578c2ecf20Sopenharmony_ci } 27588c2ecf20Sopenharmony_ci base = BHDR(bh); 27598c2ecf20Sopenharmony_ci end = bh->b_data + bh->b_size; 27608c2ecf20Sopenharmony_ci min_offs = end - base; 27618c2ecf20Sopenharmony_ci bfree = ext4_xattr_free_space(BFIRST(bh), &min_offs, base, 27628c2ecf20Sopenharmony_ci NULL); 27638c2ecf20Sopenharmony_ci brelse(bh); 27648c2ecf20Sopenharmony_ci if (bfree + ifree < isize_diff) { 27658c2ecf20Sopenharmony_ci if (!tried_min_extra_isize && s_min_extra_isize) { 27668c2ecf20Sopenharmony_ci tried_min_extra_isize++; 27678c2ecf20Sopenharmony_ci new_extra_isize = s_min_extra_isize; 27688c2ecf20Sopenharmony_ci goto retry; 27698c2ecf20Sopenharmony_ci } 27708c2ecf20Sopenharmony_ci error = -ENOSPC; 27718c2ecf20Sopenharmony_ci goto cleanup; 27728c2ecf20Sopenharmony_ci } 27738c2ecf20Sopenharmony_ci } else { 27748c2ecf20Sopenharmony_ci bfree = inode->i_sb->s_blocksize; 27758c2ecf20Sopenharmony_ci } 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci error = ext4_xattr_make_inode_space(handle, inode, raw_inode, 27788c2ecf20Sopenharmony_ci isize_diff, ifree, bfree, 27798c2ecf20Sopenharmony_ci &total_ino); 27808c2ecf20Sopenharmony_ci if (error) { 27818c2ecf20Sopenharmony_ci if (error == -ENOSPC && !tried_min_extra_isize && 27828c2ecf20Sopenharmony_ci s_min_extra_isize) { 27838c2ecf20Sopenharmony_ci tried_min_extra_isize++; 27848c2ecf20Sopenharmony_ci new_extra_isize = s_min_extra_isize; 27858c2ecf20Sopenharmony_ci goto retry; 27868c2ecf20Sopenharmony_ci } 27878c2ecf20Sopenharmony_ci goto cleanup; 27888c2ecf20Sopenharmony_ci } 27898c2ecf20Sopenharmony_cishift: 27908c2ecf20Sopenharmony_ci /* Adjust the offsets and shift the remaining entries ahead */ 27918c2ecf20Sopenharmony_ci ext4_xattr_shift_entries(IFIRST(header), EXT4_I(inode)->i_extra_isize 27928c2ecf20Sopenharmony_ci - new_extra_isize, (void *)raw_inode + 27938c2ecf20Sopenharmony_ci EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize, 27948c2ecf20Sopenharmony_ci (void *)header, total_ino); 27958c2ecf20Sopenharmony_ci EXT4_I(inode)->i_extra_isize = new_extra_isize; 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci if (ext4_has_inline_data(inode)) 27988c2ecf20Sopenharmony_ci error = ext4_find_inline_data_nolock(inode); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_cicleanup: 28018c2ecf20Sopenharmony_ci if (error && (mnt_count != le16_to_cpu(sbi->s_es->s_mnt_count))) { 28028c2ecf20Sopenharmony_ci ext4_warning(inode->i_sb, "Unable to expand inode %lu. Delete some EAs or run e2fsck.", 28038c2ecf20Sopenharmony_ci inode->i_ino); 28048c2ecf20Sopenharmony_ci mnt_count = le16_to_cpu(sbi->s_es->s_mnt_count); 28058c2ecf20Sopenharmony_ci } 28068c2ecf20Sopenharmony_ci return error; 28078c2ecf20Sopenharmony_ci} 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci#define EIA_INCR 16 /* must be 2^n */ 28108c2ecf20Sopenharmony_ci#define EIA_MASK (EIA_INCR - 1) 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci/* Add the large xattr @inode into @ea_inode_array for deferred iput(). 28138c2ecf20Sopenharmony_ci * If @ea_inode_array is new or full it will be grown and the old 28148c2ecf20Sopenharmony_ci * contents copied over. 28158c2ecf20Sopenharmony_ci */ 28168c2ecf20Sopenharmony_cistatic int 28178c2ecf20Sopenharmony_ciext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array, 28188c2ecf20Sopenharmony_ci struct inode *inode) 28198c2ecf20Sopenharmony_ci{ 28208c2ecf20Sopenharmony_ci if (*ea_inode_array == NULL) { 28218c2ecf20Sopenharmony_ci /* 28228c2ecf20Sopenharmony_ci * Start with 15 inodes, so it fits into a power-of-two size. 28238c2ecf20Sopenharmony_ci * If *ea_inode_array is NULL, this is essentially offsetof() 28248c2ecf20Sopenharmony_ci */ 28258c2ecf20Sopenharmony_ci (*ea_inode_array) = 28268c2ecf20Sopenharmony_ci kmalloc(offsetof(struct ext4_xattr_inode_array, 28278c2ecf20Sopenharmony_ci inodes[EIA_MASK]), 28288c2ecf20Sopenharmony_ci GFP_NOFS); 28298c2ecf20Sopenharmony_ci if (*ea_inode_array == NULL) 28308c2ecf20Sopenharmony_ci return -ENOMEM; 28318c2ecf20Sopenharmony_ci (*ea_inode_array)->count = 0; 28328c2ecf20Sopenharmony_ci } else if (((*ea_inode_array)->count & EIA_MASK) == EIA_MASK) { 28338c2ecf20Sopenharmony_ci /* expand the array once all 15 + n * 16 slots are full */ 28348c2ecf20Sopenharmony_ci struct ext4_xattr_inode_array *new_array = NULL; 28358c2ecf20Sopenharmony_ci int count = (*ea_inode_array)->count; 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci /* if new_array is NULL, this is essentially offsetof() */ 28388c2ecf20Sopenharmony_ci new_array = kmalloc( 28398c2ecf20Sopenharmony_ci offsetof(struct ext4_xattr_inode_array, 28408c2ecf20Sopenharmony_ci inodes[count + EIA_INCR]), 28418c2ecf20Sopenharmony_ci GFP_NOFS); 28428c2ecf20Sopenharmony_ci if (new_array == NULL) 28438c2ecf20Sopenharmony_ci return -ENOMEM; 28448c2ecf20Sopenharmony_ci memcpy(new_array, *ea_inode_array, 28458c2ecf20Sopenharmony_ci offsetof(struct ext4_xattr_inode_array, inodes[count])); 28468c2ecf20Sopenharmony_ci kfree(*ea_inode_array); 28478c2ecf20Sopenharmony_ci *ea_inode_array = new_array; 28488c2ecf20Sopenharmony_ci } 28498c2ecf20Sopenharmony_ci (*ea_inode_array)->inodes[(*ea_inode_array)->count++] = inode; 28508c2ecf20Sopenharmony_ci return 0; 28518c2ecf20Sopenharmony_ci} 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci/* 28548c2ecf20Sopenharmony_ci * ext4_xattr_delete_inode() 28558c2ecf20Sopenharmony_ci * 28568c2ecf20Sopenharmony_ci * Free extended attribute resources associated with this inode. Traverse 28578c2ecf20Sopenharmony_ci * all entries and decrement reference on any xattr inodes associated with this 28588c2ecf20Sopenharmony_ci * inode. This is called immediately before an inode is freed. We have exclusive 28598c2ecf20Sopenharmony_ci * access to the inode. If an orphan inode is deleted it will also release its 28608c2ecf20Sopenharmony_ci * references on xattr block and xattr inodes. 28618c2ecf20Sopenharmony_ci */ 28628c2ecf20Sopenharmony_ciint ext4_xattr_delete_inode(handle_t *handle, struct inode *inode, 28638c2ecf20Sopenharmony_ci struct ext4_xattr_inode_array **ea_inode_array, 28648c2ecf20Sopenharmony_ci int extra_credits) 28658c2ecf20Sopenharmony_ci{ 28668c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 28678c2ecf20Sopenharmony_ci struct ext4_xattr_ibody_header *header; 28688c2ecf20Sopenharmony_ci struct ext4_iloc iloc = { .bh = NULL }; 28698c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry; 28708c2ecf20Sopenharmony_ci struct inode *ea_inode; 28718c2ecf20Sopenharmony_ci int error; 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci error = ext4_journal_ensure_credits(handle, extra_credits, 28748c2ecf20Sopenharmony_ci ext4_free_metadata_revoke_credits(inode->i_sb, 1)); 28758c2ecf20Sopenharmony_ci if (error < 0) { 28768c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "ensure credits (error %d)", error); 28778c2ecf20Sopenharmony_ci goto cleanup; 28788c2ecf20Sopenharmony_ci } 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci if (ext4_has_feature_ea_inode(inode->i_sb) && 28818c2ecf20Sopenharmony_ci ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci error = ext4_get_inode_loc(inode, &iloc); 28848c2ecf20Sopenharmony_ci if (error) { 28858c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "inode loc (error %d)", error); 28868c2ecf20Sopenharmony_ci goto cleanup; 28878c2ecf20Sopenharmony_ci } 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci error = ext4_journal_get_write_access(handle, iloc.bh); 28908c2ecf20Sopenharmony_ci if (error) { 28918c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "write access (error %d)", 28928c2ecf20Sopenharmony_ci error); 28938c2ecf20Sopenharmony_ci goto cleanup; 28948c2ecf20Sopenharmony_ci } 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_ci header = IHDR(inode, ext4_raw_inode(&iloc)); 28978c2ecf20Sopenharmony_ci if (header->h_magic == cpu_to_le32(EXT4_XATTR_MAGIC)) 28988c2ecf20Sopenharmony_ci ext4_xattr_inode_dec_ref_all(handle, inode, iloc.bh, 28998c2ecf20Sopenharmony_ci IFIRST(header), 29008c2ecf20Sopenharmony_ci false /* block_csum */, 29018c2ecf20Sopenharmony_ci ea_inode_array, 29028c2ecf20Sopenharmony_ci extra_credits, 29038c2ecf20Sopenharmony_ci false /* skip_quota */); 29048c2ecf20Sopenharmony_ci } 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ci if (EXT4_I(inode)->i_file_acl) { 29078c2ecf20Sopenharmony_ci bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO); 29088c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 29098c2ecf20Sopenharmony_ci error = PTR_ERR(bh); 29108c2ecf20Sopenharmony_ci if (error == -EIO) { 29118c2ecf20Sopenharmony_ci EXT4_ERROR_INODE_ERR(inode, EIO, 29128c2ecf20Sopenharmony_ci "block %llu read error", 29138c2ecf20Sopenharmony_ci EXT4_I(inode)->i_file_acl); 29148c2ecf20Sopenharmony_ci } 29158c2ecf20Sopenharmony_ci bh = NULL; 29168c2ecf20Sopenharmony_ci goto cleanup; 29178c2ecf20Sopenharmony_ci } 29188c2ecf20Sopenharmony_ci error = ext4_xattr_check_block(inode, bh); 29198c2ecf20Sopenharmony_ci if (error) 29208c2ecf20Sopenharmony_ci goto cleanup; 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci if (ext4_has_feature_ea_inode(inode->i_sb)) { 29238c2ecf20Sopenharmony_ci for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry); 29248c2ecf20Sopenharmony_ci entry = EXT4_XATTR_NEXT(entry)) { 29258c2ecf20Sopenharmony_ci if (!entry->e_value_inum) 29268c2ecf20Sopenharmony_ci continue; 29278c2ecf20Sopenharmony_ci error = ext4_xattr_inode_iget(inode, 29288c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_value_inum), 29298c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_hash), 29308c2ecf20Sopenharmony_ci &ea_inode); 29318c2ecf20Sopenharmony_ci if (error) 29328c2ecf20Sopenharmony_ci continue; 29338c2ecf20Sopenharmony_ci ext4_xattr_inode_free_quota(inode, ea_inode, 29348c2ecf20Sopenharmony_ci le32_to_cpu(entry->e_value_size)); 29358c2ecf20Sopenharmony_ci iput(ea_inode); 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci } 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci ext4_xattr_release_block(handle, inode, bh, ea_inode_array, 29418c2ecf20Sopenharmony_ci extra_credits); 29428c2ecf20Sopenharmony_ci /* 29438c2ecf20Sopenharmony_ci * Update i_file_acl value in the same transaction that releases 29448c2ecf20Sopenharmony_ci * block. 29458c2ecf20Sopenharmony_ci */ 29468c2ecf20Sopenharmony_ci EXT4_I(inode)->i_file_acl = 0; 29478c2ecf20Sopenharmony_ci error = ext4_mark_inode_dirty(handle, inode); 29488c2ecf20Sopenharmony_ci if (error) { 29498c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "mark inode dirty (error %d)", 29508c2ecf20Sopenharmony_ci error); 29518c2ecf20Sopenharmony_ci goto cleanup; 29528c2ecf20Sopenharmony_ci } 29538c2ecf20Sopenharmony_ci ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR); 29548c2ecf20Sopenharmony_ci } 29558c2ecf20Sopenharmony_ci error = 0; 29568c2ecf20Sopenharmony_cicleanup: 29578c2ecf20Sopenharmony_ci brelse(iloc.bh); 29588c2ecf20Sopenharmony_ci brelse(bh); 29598c2ecf20Sopenharmony_ci return error; 29608c2ecf20Sopenharmony_ci} 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_civoid ext4_xattr_inode_array_free(struct ext4_xattr_inode_array *ea_inode_array) 29638c2ecf20Sopenharmony_ci{ 29648c2ecf20Sopenharmony_ci int idx; 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci if (ea_inode_array == NULL) 29678c2ecf20Sopenharmony_ci return; 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci for (idx = 0; idx < ea_inode_array->count; ++idx) 29708c2ecf20Sopenharmony_ci iput(ea_inode_array->inodes[idx]); 29718c2ecf20Sopenharmony_ci kfree(ea_inode_array); 29728c2ecf20Sopenharmony_ci} 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci/* 29758c2ecf20Sopenharmony_ci * ext4_xattr_block_cache_insert() 29768c2ecf20Sopenharmony_ci * 29778c2ecf20Sopenharmony_ci * Create a new entry in the extended attribute block cache, and insert 29788c2ecf20Sopenharmony_ci * it unless such an entry is already in the cache. 29798c2ecf20Sopenharmony_ci * 29808c2ecf20Sopenharmony_ci * Returns 0, or a negative error number on failure. 29818c2ecf20Sopenharmony_ci */ 29828c2ecf20Sopenharmony_cistatic void 29838c2ecf20Sopenharmony_ciext4_xattr_block_cache_insert(struct mb_cache *ea_block_cache, 29848c2ecf20Sopenharmony_ci struct buffer_head *bh) 29858c2ecf20Sopenharmony_ci{ 29868c2ecf20Sopenharmony_ci struct ext4_xattr_header *header = BHDR(bh); 29878c2ecf20Sopenharmony_ci __u32 hash = le32_to_cpu(header->h_hash); 29888c2ecf20Sopenharmony_ci int reusable = le32_to_cpu(header->h_refcount) < 29898c2ecf20Sopenharmony_ci EXT4_XATTR_REFCOUNT_MAX; 29908c2ecf20Sopenharmony_ci int error; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci if (!ea_block_cache) 29938c2ecf20Sopenharmony_ci return; 29948c2ecf20Sopenharmony_ci error = mb_cache_entry_create(ea_block_cache, GFP_NOFS, hash, 29958c2ecf20Sopenharmony_ci bh->b_blocknr, reusable); 29968c2ecf20Sopenharmony_ci if (error) { 29978c2ecf20Sopenharmony_ci if (error == -EBUSY) 29988c2ecf20Sopenharmony_ci ea_bdebug(bh, "already in cache"); 29998c2ecf20Sopenharmony_ci } else 30008c2ecf20Sopenharmony_ci ea_bdebug(bh, "inserting [%x]", (int)hash); 30018c2ecf20Sopenharmony_ci} 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci/* 30048c2ecf20Sopenharmony_ci * ext4_xattr_cmp() 30058c2ecf20Sopenharmony_ci * 30068c2ecf20Sopenharmony_ci * Compare two extended attribute blocks for equality. 30078c2ecf20Sopenharmony_ci * 30088c2ecf20Sopenharmony_ci * Returns 0 if the blocks are equal, 1 if they differ, and 30098c2ecf20Sopenharmony_ci * a negative error number on errors. 30108c2ecf20Sopenharmony_ci */ 30118c2ecf20Sopenharmony_cistatic int 30128c2ecf20Sopenharmony_ciext4_xattr_cmp(struct ext4_xattr_header *header1, 30138c2ecf20Sopenharmony_ci struct ext4_xattr_header *header2) 30148c2ecf20Sopenharmony_ci{ 30158c2ecf20Sopenharmony_ci struct ext4_xattr_entry *entry1, *entry2; 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci entry1 = ENTRY(header1+1); 30188c2ecf20Sopenharmony_ci entry2 = ENTRY(header2+1); 30198c2ecf20Sopenharmony_ci while (!IS_LAST_ENTRY(entry1)) { 30208c2ecf20Sopenharmony_ci if (IS_LAST_ENTRY(entry2)) 30218c2ecf20Sopenharmony_ci return 1; 30228c2ecf20Sopenharmony_ci if (entry1->e_hash != entry2->e_hash || 30238c2ecf20Sopenharmony_ci entry1->e_name_index != entry2->e_name_index || 30248c2ecf20Sopenharmony_ci entry1->e_name_len != entry2->e_name_len || 30258c2ecf20Sopenharmony_ci entry1->e_value_size != entry2->e_value_size || 30268c2ecf20Sopenharmony_ci entry1->e_value_inum != entry2->e_value_inum || 30278c2ecf20Sopenharmony_ci memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len)) 30288c2ecf20Sopenharmony_ci return 1; 30298c2ecf20Sopenharmony_ci if (!entry1->e_value_inum && 30308c2ecf20Sopenharmony_ci memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs), 30318c2ecf20Sopenharmony_ci (char *)header2 + le16_to_cpu(entry2->e_value_offs), 30328c2ecf20Sopenharmony_ci le32_to_cpu(entry1->e_value_size))) 30338c2ecf20Sopenharmony_ci return 1; 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci entry1 = EXT4_XATTR_NEXT(entry1); 30368c2ecf20Sopenharmony_ci entry2 = EXT4_XATTR_NEXT(entry2); 30378c2ecf20Sopenharmony_ci } 30388c2ecf20Sopenharmony_ci if (!IS_LAST_ENTRY(entry2)) 30398c2ecf20Sopenharmony_ci return 1; 30408c2ecf20Sopenharmony_ci return 0; 30418c2ecf20Sopenharmony_ci} 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci/* 30448c2ecf20Sopenharmony_ci * ext4_xattr_block_cache_find() 30458c2ecf20Sopenharmony_ci * 30468c2ecf20Sopenharmony_ci * Find an identical extended attribute block. 30478c2ecf20Sopenharmony_ci * 30488c2ecf20Sopenharmony_ci * Returns a pointer to the block found, or NULL if such a block was 30498c2ecf20Sopenharmony_ci * not found or an error occurred. 30508c2ecf20Sopenharmony_ci */ 30518c2ecf20Sopenharmony_cistatic struct buffer_head * 30528c2ecf20Sopenharmony_ciext4_xattr_block_cache_find(struct inode *inode, 30538c2ecf20Sopenharmony_ci struct ext4_xattr_header *header, 30548c2ecf20Sopenharmony_ci struct mb_cache_entry **pce) 30558c2ecf20Sopenharmony_ci{ 30568c2ecf20Sopenharmony_ci __u32 hash = le32_to_cpu(header->h_hash); 30578c2ecf20Sopenharmony_ci struct mb_cache_entry *ce; 30588c2ecf20Sopenharmony_ci struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode); 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_ci if (!ea_block_cache) 30618c2ecf20Sopenharmony_ci return NULL; 30628c2ecf20Sopenharmony_ci if (!header->h_hash) 30638c2ecf20Sopenharmony_ci return NULL; /* never share */ 30648c2ecf20Sopenharmony_ci ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); 30658c2ecf20Sopenharmony_ci ce = mb_cache_entry_find_first(ea_block_cache, hash); 30668c2ecf20Sopenharmony_ci while (ce) { 30678c2ecf20Sopenharmony_ci struct buffer_head *bh; 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci bh = ext4_sb_bread(inode->i_sb, ce->e_value, REQ_PRIO); 30708c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 30718c2ecf20Sopenharmony_ci if (PTR_ERR(bh) == -ENOMEM) { 30728c2ecf20Sopenharmony_ci mb_cache_entry_put(ea_block_cache, ce); 30738c2ecf20Sopenharmony_ci return NULL; 30748c2ecf20Sopenharmony_ci } 30758c2ecf20Sopenharmony_ci bh = NULL; 30768c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "block %lu read error", 30778c2ecf20Sopenharmony_ci (unsigned long)ce->e_value); 30788c2ecf20Sopenharmony_ci } else if (ext4_xattr_cmp(header, BHDR(bh)) == 0) { 30798c2ecf20Sopenharmony_ci *pce = ce; 30808c2ecf20Sopenharmony_ci return bh; 30818c2ecf20Sopenharmony_ci } 30828c2ecf20Sopenharmony_ci brelse(bh); 30838c2ecf20Sopenharmony_ci ce = mb_cache_entry_find_next(ea_block_cache, ce); 30848c2ecf20Sopenharmony_ci } 30858c2ecf20Sopenharmony_ci return NULL; 30868c2ecf20Sopenharmony_ci} 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_ci#define NAME_HASH_SHIFT 5 30898c2ecf20Sopenharmony_ci#define VALUE_HASH_SHIFT 16 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci/* 30928c2ecf20Sopenharmony_ci * ext4_xattr_hash_entry() 30938c2ecf20Sopenharmony_ci * 30948c2ecf20Sopenharmony_ci * Compute the hash of an extended attribute. 30958c2ecf20Sopenharmony_ci */ 30968c2ecf20Sopenharmony_cistatic __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value, 30978c2ecf20Sopenharmony_ci size_t value_count) 30988c2ecf20Sopenharmony_ci{ 30998c2ecf20Sopenharmony_ci __u32 hash = 0; 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci while (name_len--) { 31028c2ecf20Sopenharmony_ci hash = (hash << NAME_HASH_SHIFT) ^ 31038c2ecf20Sopenharmony_ci (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ 31048c2ecf20Sopenharmony_ci *name++; 31058c2ecf20Sopenharmony_ci } 31068c2ecf20Sopenharmony_ci while (value_count--) { 31078c2ecf20Sopenharmony_ci hash = (hash << VALUE_HASH_SHIFT) ^ 31088c2ecf20Sopenharmony_ci (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ 31098c2ecf20Sopenharmony_ci le32_to_cpu(*value++); 31108c2ecf20Sopenharmony_ci } 31118c2ecf20Sopenharmony_ci return cpu_to_le32(hash); 31128c2ecf20Sopenharmony_ci} 31138c2ecf20Sopenharmony_ci 31148c2ecf20Sopenharmony_ci#undef NAME_HASH_SHIFT 31158c2ecf20Sopenharmony_ci#undef VALUE_HASH_SHIFT 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_ci#define BLOCK_HASH_SHIFT 16 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_ci/* 31208c2ecf20Sopenharmony_ci * ext4_xattr_rehash() 31218c2ecf20Sopenharmony_ci * 31228c2ecf20Sopenharmony_ci * Re-compute the extended attribute hash value after an entry has changed. 31238c2ecf20Sopenharmony_ci */ 31248c2ecf20Sopenharmony_cistatic void ext4_xattr_rehash(struct ext4_xattr_header *header) 31258c2ecf20Sopenharmony_ci{ 31268c2ecf20Sopenharmony_ci struct ext4_xattr_entry *here; 31278c2ecf20Sopenharmony_ci __u32 hash = 0; 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci here = ENTRY(header+1); 31308c2ecf20Sopenharmony_ci while (!IS_LAST_ENTRY(here)) { 31318c2ecf20Sopenharmony_ci if (!here->e_hash) { 31328c2ecf20Sopenharmony_ci /* Block is not shared if an entry's hash value == 0 */ 31338c2ecf20Sopenharmony_ci hash = 0; 31348c2ecf20Sopenharmony_ci break; 31358c2ecf20Sopenharmony_ci } 31368c2ecf20Sopenharmony_ci hash = (hash << BLOCK_HASH_SHIFT) ^ 31378c2ecf20Sopenharmony_ci (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^ 31388c2ecf20Sopenharmony_ci le32_to_cpu(here->e_hash); 31398c2ecf20Sopenharmony_ci here = EXT4_XATTR_NEXT(here); 31408c2ecf20Sopenharmony_ci } 31418c2ecf20Sopenharmony_ci header->h_hash = cpu_to_le32(hash); 31428c2ecf20Sopenharmony_ci} 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci#undef BLOCK_HASH_SHIFT 31458c2ecf20Sopenharmony_ci 31468c2ecf20Sopenharmony_ci#define HASH_BUCKET_BITS 10 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_cistruct mb_cache * 31498c2ecf20Sopenharmony_ciext4_xattr_create_cache(void) 31508c2ecf20Sopenharmony_ci{ 31518c2ecf20Sopenharmony_ci return mb_cache_create(HASH_BUCKET_BITS); 31528c2ecf20Sopenharmony_ci} 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_civoid ext4_xattr_destroy_cache(struct mb_cache *cache) 31558c2ecf20Sopenharmony_ci{ 31568c2ecf20Sopenharmony_ci if (cache) 31578c2ecf20Sopenharmony_ci mb_cache_destroy(cache); 31588c2ecf20Sopenharmony_ci} 31598c2ecf20Sopenharmony_ci 3160