cregit-Linux how code gets into the kernel

Release 4.10 fs/ext4/xattr.c

Directory: fs/ext4
/*
 * linux/fs/ext4/xattr.c
 *
 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
 *
 * Fix by Harrison Xing <harrison@mountainviewdata.com>.
 * Ext4 code with a lot of help from Eric Jarman <ejarman@acm.org>.
 * Extended attributes for symlinks and special files added per
 *  suggestion of Luka Renko <luka.renko@hermes.si>.
 * xattr consolidation Copyright (c) 2004 James Morris <jmorris@redhat.com>,
 *  Red Hat Inc.
 * ea-in-inode support by Alex Tomas <alex@clusterfs.com> aka bzzz
 *  and Andreas Gruenbacher <agruen@suse.de>.
 */

/*
 * Extended attributes are stored directly in inodes (on file systems with
 * inodes bigger than 128 bytes) and on additional disk blocks. The i_file_acl
 * field contains the block number if an inode uses an additional block. All
 * attributes must fit in the inode and one additional block. Blocks that
 * contain the identical set of attributes may be shared among several inodes.
 * Identical blocks are detected by keeping a cache of blocks that have
 * recently been accessed.
 *
 * The attributes in inodes and on blocks have a different header; the entries
 * are stored in the same format:
 *
 *   +------------------+
 *   | header           |
 *   | entry 1          | |
 *   | entry 2          | | growing downwards
 *   | entry 3          | v
 *   | four null bytes  |
 *   | . . .            |
 *   | value 1          | ^
 *   | value 3          | | growing upwards
 *   | value 2          | |
 *   +------------------+
 *
 * The header is followed by multiple entry descriptors. In disk blocks, the
 * entry descriptors are kept sorted. In inodes, they are unsorted. The
 * attribute values are aligned to the end of the block in no specific order.
 *
 * Locking strategy
 * ----------------
 * EXT4_I(inode)->i_file_acl is protected by EXT4_I(inode)->xattr_sem.
 * EA blocks are only changed if they are exclusive to an inode, so
 * holding xattr_sem also means that nothing but the EA block's reference
 * count can change. Multiple writers to the same block are synchronized
 * by the buffer lock.
 */

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/mbcache.h>
#include <linux/quotaops.h>
#include "ext4_jbd2.h"
#include "ext4.h"
#include "xattr.h"
#include "acl.h"

#ifdef EXT4_XATTR_DEBUG

# define ea_idebug(inode, fmt, ...)					\
	printk(KERN_DEBUG "inode %s:%lu: " fmt "\n",                    \
               inode->i_sb->s_id, inode->i_ino, ##__VA_ARGS__)

# define ea_bdebug(bh, fmt, ...)					\
	printk(KERN_DEBUG "block %pg:%lu: " fmt "\n",                   \
               bh->b_bdev, (unsigned long)bh->b_blocknr, ##__VA_ARGS__)
#else

# define ea_idebug(inode, fmt, ...)	no_printk(fmt, ##__VA_ARGS__)

# define ea_bdebug(bh, fmt, ...)	no_printk(fmt, ##__VA_ARGS__)
#endif

static void ext4_xattr_cache_insert(struct mb_cache *, struct buffer_head *);
static struct buffer_head *ext4_xattr_cache_find(struct inode *,
						 struct ext4_xattr_header *,
						 struct mb_cache_entry **);
static void ext4_xattr_rehash(struct ext4_xattr_header *,
			      struct ext4_xattr_entry *);
static int ext4_xattr_list(struct dentry *dentry, char *buffer,
			   size_t buffer_size);


static const struct xattr_handler *ext4_xattr_handler_map[] = {
	[EXT4_XATTR_INDEX_USER]		     = &ext4_xattr_user_handler,
#ifdef CONFIG_EXT4_FS_POSIX_ACL
	[EXT4_XATTR_INDEX_POSIX_ACL_ACCESS]  = &posix_acl_access_xattr_handler,
	[EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT] = &posix_acl_default_xattr_handler,
#endif
	[EXT4_XATTR_INDEX_TRUSTED]	     = &ext4_xattr_trusted_handler,
#ifdef CONFIG_EXT4_FS_SECURITY
	[EXT4_XATTR_INDEX_SECURITY]	     = &ext4_xattr_security_handler,
#endif
};


const struct xattr_handler *ext4_xattr_handlers[] = {
	&ext4_xattr_user_handler,
	&ext4_xattr_trusted_handler,
#ifdef CONFIG_EXT4_FS_POSIX_ACL
	&posix_acl_access_xattr_handler,
	&posix_acl_default_xattr_handler,
#endif
#ifdef CONFIG_EXT4_FS_SECURITY
	&ext4_xattr_security_handler,
#endif
	NULL
};


#define EXT4_GET_MB_CACHE(inode)	(((struct ext4_sb_info *) \
                                inode->i_sb->s_fs_info)->s_mb_cache)


static __le32 ext4_xattr_block_csum(struct inode *inode, sector_t block_nr, struct ext4_xattr_header *hdr) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); __u32 csum; __le64 dsk_block_nr = cpu_to_le64(block_nr); __u32 dummy_csum = 0; int offset = offsetof(struct ext4_xattr_header, h_checksum); csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&dsk_block_nr, sizeof(dsk_block_nr)); csum = ext4_chksum(sbi, csum, (__u8 *)hdr, offset); csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum)); offset += sizeof(dummy_csum); csum = ext4_chksum(sbi, csum, (__u8 *)hdr + offset, EXT4_BLOCK_SIZE(inode->i_sb) - offset); return cpu_to_le32(csum); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong8453.16%133.33%
daeho jeongdaeho jeong6440.51%133.33%
theodore tsotheodore tso106.33%133.33%
Total158100.00%3100.00%


static int ext4_xattr_block_csum_verify(struct inode *inode, sector_t block_nr, struct ext4_xattr_header *hdr) { if (ext4_has_metadata_csum(inode->i_sb) && (hdr->h_checksum != ext4_xattr_block_csum(inode, block_nr, hdr))) return 0; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong4897.96%150.00%
dmitriy monakhovdmitriy monakhov12.04%150.00%
Total49100.00%2100.00%


static void ext4_xattr_block_csum_set(struct inode *inode, sector_t block_nr, struct ext4_xattr_header *hdr) { if (!ext4_has_metadata_csum(inode->i_sb)) return; hdr->h_checksum = ext4_xattr_block_csum(inode, block_nr, hdr); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong4297.67%150.00%
dmitriy monakhovdmitriy monakhov12.33%150.00%
Total43100.00%2100.00%


static inline int ext4_handle_dirty_xattr_block(handle_t *handle, struct inode *inode, struct buffer_head *bh) { ext4_xattr_block_csum_set(inode, bh->b_blocknr, BHDR(bh)); return ext4_handle_dirty_metadata(handle, inode, bh); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong45100.00%1100.00%
Total45100.00%1100.00%


static inline const struct xattr_handler * ext4_xattr_handler(int name_index) { const struct xattr_handler *handler = NULL; if (name_index > 0 && name_index < ARRAY_SIZE(ext4_xattr_handler_map)) handler = ext4_xattr_handler_map[name_index]; return handler; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp3988.64%133.33%
mingming caomingming cao36.82%133.33%
stephen hemmingerstephen hemminger24.55%133.33%
Total44100.00%3100.00%

/* * Inode operation listxattr() * * d_inode(dentry)->i_mutex: don't care */
ssize_t ext4_listxattr(struct dentry *dentry, char *buffer, size_t size) { return ext4_xattr_list(dentry, buffer, size); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp2592.59%150.00%
mingming caomingming cao27.41%150.00%
Total27100.00%2100.00%


static int ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end, void *value_start) { struct ext4_xattr_entry *e = entry; /* Find the end of the names list */ while (!IS_LAST_ENTRY(e)) { struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e); if ((void *)next >= end) return -EFSCORRUPTED; e = next; } /* Check the values */ while (!IS_LAST_ENTRY(entry)) { if (entry->e_value_block != 0) return -EFSCORRUPTED; if (entry->e_value_size != 0) { u16 offs = le16_to_cpu(entry->e_value_offs); u32 size = le32_to_cpu(entry->e_value_size); void *value; /* * The value cannot overlap the names, and the value * with padding cannot extend beyond 'end'. Check both * the padded and unpadded sizes, since the size may * overflow to 0 when adding padding. */ if (offs > end - value_start) return -EFSCORRUPTED; value = value_start + offs; if (value < (void *)e + sizeof(u32) || size > end - value || EXT4_XATTR_SIZE(size) > end - value) return -EFSCORRUPTED; } entry = EXT4_XATTR_NEXT(entry); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong6535.14%233.33%
eric biggerseric biggers5630.27%116.67%
dave kleikampdave kleikamp4825.95%116.67%
jan karajan kara126.49%116.67%
mingming caomingming cao42.16%116.67%
Total185100.00%6100.00%


static inline int ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh) { int error; if (buffer_verified(bh)) return 0; if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) || BHDR(bh)->h_blocks != cpu_to_le32(1)) return -EFSCORRUPTED; if (!ext4_xattr_block_csum_verify(inode, bh->b_blocknr, BHDR(bh))) return -EFSBADCRC; error = ext4_xattr_check_names(BFIRST(bh), bh->b_data + bh->b_size, bh->b_data); if (!error) set_buffer_verified(bh); return error; }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong5850.00%350.00%
dave kleikampdave kleikamp5446.55%116.67%
mingming caomingming cao32.59%116.67%
zheng liuzheng liu10.86%116.67%
Total116100.00%6100.00%


static int __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header, void *end, const char *function, unsigned int line) { int error = -EFSCORRUPTED; if (end - (void *)header < sizeof(*header) + sizeof(u32) || (header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC))) goto errout; error = ext4_xattr_check_names(IFIRST(header), end, IFIRST(header)); errout: if (error) __ext4_error_inode(inode, function, line, 0, "corrupted in-inode xattr"); return error; }

Contributors

PersonTokensPropCommitsCommitProp
theodore tsotheodore tso8678.90%125.00%
eric biggerseric biggers2220.18%250.00%
dave kleikampdave kleikamp10.92%125.00%
Total109100.00%4100.00%

#define xattr_check_inode(inode, header, end) \ __xattr_check_inode((inode), (header), (end), __func__, __LINE__)
static inline int ext4_xattr_check_entry(struct ext4_xattr_entry *entry, size_t size) { size_t value_size = le32_to_cpu(entry->e_value_size); if (entry->e_value_block != 0 || value_size > size || le16_to_cpu(entry->e_value_offs) + value_size > size) return -EFSCORRUPTED; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp5192.73%125.00%
mingming caomingming cao23.64%125.00%
theodore tsotheodore tso11.82%125.00%
darrick j. wongdarrick j. wong11.82%125.00%
Total55100.00%4100.00%


static int ext4_xattr_find_entry(struct ext4_xattr_entry **pentry, int name_index, const char *name, size_t size, int sorted) { struct ext4_xattr_entry *entry; size_t name_len; int cmp = 1; if (name == NULL) return -EINVAL; name_len = strlen(name); entry = *pentry; for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { cmp = name_index - entry->e_name_index; if (!cmp) cmp = name_len - entry->e_name_len; if (!cmp) cmp = memcmp(name, entry->e_name, name_len); if (cmp <= 0 && (sorted || cmp == 0)) break; } *pentry = entry; if (!cmp && ext4_xattr_check_entry(entry, size)) return -EFSCORRUPTED; return cmp ? -ENODATA : 0; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp15696.30%133.33%
mingming caomingming cao53.09%133.33%
darrick j. wongdarrick j. wong10.62%133.33%
Total162100.00%3100.00%


static int ext4_xattr_block_get(struct inode *inode, int name_index, const char *name, void *buffer, size_t buffer_size) { struct buffer_head *bh = NULL; struct ext4_xattr_entry *entry; size_t size; int error; struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", name_index, name, buffer, (long)buffer_size); error = -ENODATA; if (!EXT4_I(inode)->i_file_acl) goto cleanup; ea_idebug(inode, "reading block %llu", (unsigned long long)EXT4_I(inode)->i_file_acl); bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); if (!bh) goto cleanup; ea_bdebug(bh, "b_count=%d, refcount=%d", atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); if (ext4_xattr_check_block(inode, bh)) { bad_block: EXT4_ERROR_INODE(inode, "bad block %llu", EXT4_I(inode)->i_file_acl); error = -EFSCORRUPTED; goto cleanup; } ext4_xattr_cache_insert(ext4_mb_cache, bh); entry = BFIRST(bh); error = ext4_xattr_find_entry(&entry, name_index, name, bh->b_size, 1); if (error == -EFSCORRUPTED) goto bad_block; if (error) goto cleanup; size = le32_to_cpu(entry->e_value_size); if (buffer) { error = -ERANGE; if (size > buffer_size) goto cleanup; memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs), size); } error = size; cleanup: brelse(bh); return error; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp27289.18%112.50%
t makphaibulchoket makphaibulchoke113.61%112.50%
mingming caomingming cao92.95%112.50%
joe perchesjoe perches61.97%112.50%
darrick j. wongdarrick j. wong41.31%225.00%
theodore tsotheodore tso20.66%112.50%
jan karajan kara10.33%112.50%
Total305100.00%8100.00%


int ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name, void *buffer, size_t buffer_size) { struct ext4_xattr_ibody_header *header; struct ext4_xattr_entry *entry; struct ext4_inode *raw_inode; struct ext4_iloc iloc; size_t size; void *end; int error; if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR)) return -ENODATA; error = ext4_get_inode_loc(inode, &iloc); if (error) return error; raw_inode = ext4_raw_inode(&iloc); header = IHDR(inode, raw_inode); entry = IFIRST(header); end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; error = xattr_check_inode(inode, header, end); if (error) goto cleanup; error = ext4_xattr_find_entry(&entry, name_index, name, end - (void *)entry, 0); if (error) goto cleanup; size = le32_to_cpu(entry->e_value_size); if (buffer) { error = -ERANGE; if (size > buffer_size) goto cleanup; memcpy(buffer, (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), size); } error = size; cleanup: brelse(iloc.bh); return error; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp22392.53%120.00%
mingming caomingming cao104.15%120.00%
theodore tsotheodore tso72.90%240.00%
darrick j. wongdarrick j. wong10.41%120.00%
Total241100.00%5100.00%

/* * ext4_xattr_get() * * Copy an extended attribute into the buffer * provided, or compute the buffer size required. * Buffer is NULL to compute the size of the buffer required. * * Returns a negative error number on failure, or the number of bytes * used / required on success. */
int ext4_xattr_get(struct inode *inode, int name_index, const char *name, void *buffer, size_t buffer_size) { int error; if (strlen(name) > 255) return -ERANGE; down_read(&EXT4_I(inode)->xattr_sem); error = ext4_xattr_ibody_get(inode, name_index, name, buffer, buffer_size); if (error == -ENODATA) error = ext4_xattr_block_get(inode, name_index, name, buffer, buffer_size); up_read(&EXT4_I(inode)->xattr_sem); return error; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp8582.52%133.33%
zhang zhenzhang zhen1312.62%133.33%
mingming caomingming cao54.85%133.33%
Total103100.00%3100.00%


static int ext4_xattr_list_entries(struct dentry *dentry, struct ext4_xattr_entry *entry, char *buffer, size_t buffer_size) { size_t rest = buffer_size; for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { const struct xattr_handler *handler = ext4_xattr_handler(entry->e_name_index); if (handler && (!handler->list || handler->list(dentry))) { const char *prefix = handler->prefix ?: handler->name; size_t prefix_len = strlen(prefix); size_t size = prefix_len + entry->e_name_len + 1; if (buffer) { if (size > rest) return -ERANGE; memcpy(buffer, prefix, prefix_len); buffer += prefix_len; memcpy(buffer, entry->e_name, entry->e_name_len); buffer += entry->e_name_len; *buffer++ = 0; } rest -= size; } } return buffer_size - rest; /* total size */ }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp9453.41%116.67%
andreas gruenbacherandreas gruenbacher7542.61%233.33%
mingming caomingming cao42.27%116.67%
christoph hellwigchristoph hellwig21.14%116.67%
stephen hemmingerstephen hemminger10.57%116.67%
Total176100.00%6100.00%


static int ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size) { struct inode *inode = d_inode(dentry); struct buffer_head *bh = NULL; int error; struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); ea_idebug(inode, "buffer=%p, buffer_size=%ld", buffer, (long)buffer_size); error = 0; if (!EXT4_I(inode)->i_file_acl) goto cleanup; ea_idebug(inode, "reading block %llu", (unsigned long long)EXT4_I(inode)->i_file_acl); bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); error = -EIO; if (!bh) goto cleanup; ea_bdebug(bh, "b_count=%d, refcount=%d", atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); if (ext4_xattr_check_block(inode, bh)) { EXT4_ERROR_INODE(inode, "bad block %llu", EXT4_I(inode)->i_file_acl); error = -EFSCORRUPTED; goto cleanup; } ext4_xattr_cache_insert(ext4_mb_cache, bh); error = ext4_xattr_list_entries(dentry, BFIRST(bh), buffer, buffer_size); cleanup: brelse(bh); return error; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp17680.00%110.00%
t makphaibulchoket makphaibulchoke115.00%110.00%
christoph hellwigchristoph hellwig104.55%110.00%
mingming caomingming cao83.64%110.00%
joe perchesjoe perches62.73%110.00%
david howellsdavid howells31.36%110.00%
darrick j. wongdarrick j. wong31.36%220.00%
theodore tsotheodore tso20.91%110.00%
jan karajan kara10.45%110.00%
Total220100.00%10100.00%


static int ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size) { struct inode *inode = d_inode(dentry); struct ext4_xattr_ibody_header *header; struct ext4_inode *raw_inode; struct ext4_iloc iloc; void *end; int error; if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR)) return 0; error = ext4_get_inode_loc(inode, &iloc); if (error) return error; raw_inode = ext4_raw_inode(&iloc); header = IHDR(inode, raw_inode); end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; error = xattr_check_inode(inode, header, end); if (error) goto cleanup; error = ext4_xattr_list_entries(dentry, IFIRST(header), buffer, buffer_size); cleanup: brelse(iloc.bh); return error; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp13182.39%116.67%
christoph hellwigchristoph hellwig106.29%116.67%
mingming caomingming cao95.66%116.67%
theodore tsotheodore tso63.77%233.33%
david howellsdavid howells31.89%116.67%
Total159100.00%6100.00%

/* * ext4_xattr_list() * * Copy a list of attribute names into the buffer * provided, or compute the buffer size required. * Buffer is NULL to compute the size of the buffer required. * * Returns a negative error number on failure, or the number of bytes * used / required on success. */
static int ext4_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) { int ret, ret2; down_read(&EXT4_I(d_inode(dentry))->xattr_sem); ret = ret2 = ext4_xattr_ibody_list(dentry, buffer, buffer_size); if (ret < 0) goto errout; if (buffer) { buffer += ret; buffer_size -= ret; } ret = ext4_xattr_block_list(dentry, buffer, buffer_size); if (ret < 0) goto errout; ret += ret2; errout: up_read(&EXT4_I(d_inode(dentry))->xattr_sem); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp7665.52%116.67%
theodore tsotheodore tso2218.97%116.67%
david howellsdavid howells65.17%116.67%
christoph hellwigchristoph hellwig65.17%116.67%
mingming caomingming cao65.17%233.33%
Total116100.00%6100.00%

/* * If the EXT4_FEATURE_COMPAT_EXT_ATTR feature of this file system is * not set, set it. */
static void ext4_xattr_update_super_block(handle_t *handle, struct super_block *sb) { if (ext4_has_feature_xattr(sb)) return; BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access"); if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) { ext4_set_feature_xattr(sb); ext4_handle_dirty_super(handle, sb); } }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp4772.31%120.00%
liang xieliang xie1218.46%120.00%
mingming caomingming cao34.62%120.00%
darrick j. wongdarrick j. wong23.08%120.00%
theodore tsotheodore tso11.54%120.00%
Total65100.00%5100.00%

/* * Release the xattr block BH: If the reference count is > 1, decrement it; * otherwise free the block. */
static void ext4_xattr_release_block(handle_t *handle, struct inode *inode, struct buffer_head *bh) { struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode); u32 hash, ref; int error = 0; BUFFER_TRACE(bh, "get_write_access"); error = ext4_journal_get_write_access(handle, bh); if (error) goto out; lock_buffer(bh); hash = le32_to_cpu(BHDR(bh)->h_hash); ref = le32_to_cpu(BHDR(bh)->h_refcount); if (ref == 1) { ea_bdebug(bh, "refcount now=0; freeing"); /* * This must happen under buffer lock for * ext4_xattr_block_set() to reliably detect freed block */ mb_cache_entry_delete_block(ext4_mb_cache, hash, bh->b_blocknr); get_bh(bh); unlock_buffer(bh); ext4_free_blocks(handle, inode, bh, 0, 1, EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET); } else { ref--; BHDR(bh)->h_refcount = cpu_to_le32(ref); if (ref == EXT4_XATTR_REFCOUNT_MAX - 1) { struct mb_cache_entry *ce; ce = mb_cache_entry_get(ext4_mb_cache, hash, bh->b_blocknr); if (ce) { ce->e_reusable = 1; mb_cache_entry_put(ext4_mb_cache, ce); } } /* * Beware of this ugliness: Releasing of xattr block references * from different inodes can race and so we have to protect * from a race where someone else frees the block (and releases * its journal_head) before we are done dirtying the buffer. In * nojournal mode this race is harmless and we actually cannot * call ext4_handle_dirty_xattr_block() with locked buffer as * that function can call sync_dirty_buffer() so for that case * we handle the dirtying after unlocking the buffer. */ if (ext4_handle_valid(handle)) error