2006-10-11 12:20:50 +04:00
/*
2006-10-11 12:20:53 +04:00
* linux / fs / ext4 / xattr . c
2006-10-11 12:20:50 +04:00
*
* Copyright ( C ) 2001 - 2003 Andreas Gruenbacher , < agruen @ suse . de >
*
* Fix by Harrison Xing < harrison @ mountainviewdata . com > .
2006-10-11 12:20:53 +04:00
* Ext4 code with a lot of help from Eric Jarman < ejarman @ acm . org > .
2006-10-11 12:20:50 +04:00
* 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
* - - - - - - - - - - - - - - - -
2006-10-11 12:20:53 +04:00
* EXT4_I ( inode ) - > i_file_acl is protected by EXT4_I ( inode ) - > xattr_sem .
2006-10-11 12:20:50 +04:00
* 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>
2006-10-11 12:21:01 +04:00
# include <linux/ext4_jbd2.h>
2006-10-11 12:20:53 +04:00
# include <linux/ext4_fs.h>
2006-10-11 12:20:50 +04:00
# include <linux/mbcache.h>
# include <linux/quotaops.h>
# include <linux/rwsem.h>
# include "xattr.h"
# include "acl.h"
2006-10-11 12:20:53 +04:00
# define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data))
# define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
2006-10-11 12:20:50 +04:00
# define BFIRST(bh) ENTRY(BHDR(bh)+1)
# define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
# define IHDR(inode, raw_inode) \
2006-10-11 12:20:53 +04:00
( ( struct ext4_xattr_ibody_header * ) \
2006-10-11 12:20:50 +04:00
( ( void * ) raw_inode + \
2006-10-11 12:20:53 +04:00
EXT4_GOOD_OLD_INODE_SIZE + \
EXT4_I ( inode ) - > i_extra_isize ) )
# define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
# ifdef EXT4_XATTR_DEBUG
2006-10-11 12:20:50 +04:00
# define ea_idebug(inode, f...) do { \
printk ( KERN_DEBUG " inode %s:%lu: " , \
inode - > i_sb - > s_id , inode - > i_ino ) ; \
printk ( f ) ; \
printk ( " \n " ) ; \
} while ( 0 )
# define ea_bdebug(bh, f...) do { \
char b [ BDEVNAME_SIZE ] ; \
printk ( KERN_DEBUG " block %s:%lu: " , \
bdevname ( bh - > b_bdev , b ) , \
( unsigned long ) bh - > b_blocknr ) ; \
printk ( f ) ; \
printk ( " \n " ) ; \
} while ( 0 )
# else
# define ea_idebug(f...)
# define ea_bdebug(f...)
# endif
2006-10-11 12:20:53 +04:00
static void ext4_xattr_cache_insert ( struct buffer_head * ) ;
static struct buffer_head * ext4_xattr_cache_find ( struct inode * ,
struct ext4_xattr_header * ,
2006-10-11 12:20:50 +04:00
struct mb_cache_entry * * ) ;
2006-10-11 12:20:53 +04:00
static void ext4_xattr_rehash ( struct ext4_xattr_header * ,
struct ext4_xattr_entry * ) ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
static struct mb_cache * ext4_xattr_cache ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
static struct xattr_handler * ext4_xattr_handler_map [ ] = {
[ EXT4_XATTR_INDEX_USER ] = & ext4_xattr_user_handler ,
# ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
[ EXT4_XATTR_INDEX_POSIX_ACL_ACCESS ] = & ext4_xattr_acl_access_handler ,
[ EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT ] = & ext4_xattr_acl_default_handler ,
2006-10-11 12:20:50 +04:00
# endif
2006-10-11 12:20:53 +04:00
[ EXT4_XATTR_INDEX_TRUSTED ] = & ext4_xattr_trusted_handler ,
# ifdef CONFIG_EXT4DEV_FS_SECURITY
[ EXT4_XATTR_INDEX_SECURITY ] = & ext4_xattr_security_handler ,
2006-10-11 12:20:50 +04:00
# endif
} ;
2006-10-11 12:20:53 +04:00
struct xattr_handler * ext4_xattr_handlers [ ] = {
& ext4_xattr_user_handler ,
& ext4_xattr_trusted_handler ,
# ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
& ext4_xattr_acl_access_handler ,
& ext4_xattr_acl_default_handler ,
2006-10-11 12:20:50 +04:00
# endif
2006-10-11 12:20:53 +04:00
# ifdef CONFIG_EXT4DEV_FS_SECURITY
& ext4_xattr_security_handler ,
2006-10-11 12:20:50 +04:00
# endif
NULL
} ;
static inline struct xattr_handler *
2006-10-11 12:20:53 +04:00
ext4_xattr_handler ( int name_index )
2006-10-11 12:20:50 +04:00
{
struct xattr_handler * handler = NULL ;
2006-10-11 12:20:53 +04:00
if ( name_index > 0 & & name_index < ARRAY_SIZE ( ext4_xattr_handler_map ) )
handler = ext4_xattr_handler_map [ name_index ] ;
2006-10-11 12:20:50 +04:00
return handler ;
}
/*
* Inode operation listxattr ( )
*
* dentry - > d_inode - > i_mutex : don ' t care
*/
ssize_t
2006-10-11 12:20:53 +04:00
ext4_listxattr ( struct dentry * dentry , char * buffer , size_t size )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
return ext4_xattr_list ( dentry - > d_inode , buffer , size ) ;
2006-10-11 12:20:50 +04:00
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_check_names ( struct ext4_xattr_entry * entry , void * end )
2006-10-11 12:20:50 +04:00
{
while ( ! IS_LAST_ENTRY ( entry ) ) {
2006-10-11 12:20:53 +04:00
struct ext4_xattr_entry * next = EXT4_XATTR_NEXT ( entry ) ;
2006-10-11 12:20:50 +04:00
if ( ( void * ) next > = end )
return - EIO ;
entry = next ;
}
return 0 ;
}
static inline int
2006-10-11 12:20:53 +04:00
ext4_xattr_check_block ( struct buffer_head * bh )
2006-10-11 12:20:50 +04:00
{
int error ;
2006-10-11 12:20:53 +04:00
if ( BHDR ( bh ) - > h_magic ! = cpu_to_le32 ( EXT4_XATTR_MAGIC ) | |
2006-10-11 12:20:50 +04:00
BHDR ( bh ) - > h_blocks ! = cpu_to_le32 ( 1 ) )
return - EIO ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_check_names ( BFIRST ( bh ) , bh - > b_data + bh - > b_size ) ;
2006-10-11 12:20:50 +04:00
return error ;
}
static inline int
2006-10-11 12:20:53 +04:00
ext4_xattr_check_entry ( struct ext4_xattr_entry * entry , size_t size )
2006-10-11 12:20:50 +04:00
{
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 - EIO ;
return 0 ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_find_entry ( struct ext4_xattr_entry * * pentry , int name_index ,
2006-10-11 12:20:50 +04:00
const char * name , size_t size , int sorted )
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_entry * entry ;
2006-10-11 12:20:50 +04:00
size_t name_len ;
int cmp = 1 ;
if ( name = = NULL )
return - EINVAL ;
name_len = strlen ( name ) ;
entry = * pentry ;
2006-10-11 12:20:53 +04:00
for ( ; ! IS_LAST_ENTRY ( entry ) ; entry = EXT4_XATTR_NEXT ( entry ) ) {
2006-10-11 12:20:50 +04:00
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 ;
2006-10-11 12:20:53 +04:00
if ( ! cmp & & ext4_xattr_check_entry ( entry , size ) )
2006-10-11 12:20:50 +04:00
return - EIO ;
return cmp ? - ENODATA : 0 ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_block_get ( struct inode * inode , int name_index , const char * name ,
2006-10-11 12:20:50 +04:00
void * buffer , size_t buffer_size )
{
struct buffer_head * bh = NULL ;
2006-10-11 12:20:53 +04:00
struct ext4_xattr_entry * entry ;
2006-10-11 12:20:50 +04:00
size_t size ;
int error ;
ea_idebug ( inode , " name=%d.%s, buffer=%p, buffer_size=%ld " ,
name_index , name , buffer , ( long ) buffer_size ) ;
error = - ENODATA ;
2006-10-11 12:20:53 +04:00
if ( ! EXT4_I ( inode ) - > i_file_acl )
2006-10-11 12:20:50 +04:00
goto cleanup ;
2006-10-11 12:20:53 +04:00
ea_idebug ( inode , " reading block %u " , EXT4_I ( inode ) - > i_file_acl ) ;
bh = sb_bread ( inode - > i_sb , EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
if ( ! bh )
goto cleanup ;
ea_bdebug ( bh , " b_count=%d, refcount=%d " ,
atomic_read ( & ( bh - > b_count ) ) , le32_to_cpu ( BHDR ( bh ) - > h_refcount ) ) ;
2006-10-11 12:20:53 +04:00
if ( ext4_xattr_check_block ( bh ) ) {
bad_block : ext4_error ( inode - > i_sb , __FUNCTION__ ,
2006-10-11 12:20:50 +04:00
" inode %lu: bad block " E3FSBLK , inode - > i_ino ,
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
error = - EIO ;
goto cleanup ;
}
2006-10-11 12:20:53 +04:00
ext4_xattr_cache_insert ( bh ) ;
2006-10-11 12:20:50 +04:00
entry = BFIRST ( bh ) ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_find_entry ( & entry , name_index , name , bh - > b_size , 1 ) ;
2006-10-11 12:20:50 +04:00
if ( error = = - EIO )
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 ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_ibody_get ( struct inode * inode , int name_index , const char * name ,
2006-10-11 12:20:50 +04:00
void * buffer , size_t buffer_size )
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_ibody_header * header ;
struct ext4_xattr_entry * entry ;
struct ext4_inode * raw_inode ;
struct ext4_iloc iloc ;
2006-10-11 12:20:50 +04:00
size_t size ;
void * end ;
int error ;
2006-10-11 12:20:53 +04:00
if ( ! ( EXT4_I ( inode ) - > i_state & EXT4_STATE_XATTR ) )
2006-10-11 12:20:50 +04:00
return - ENODATA ;
2006-10-11 12:20:53 +04:00
error = ext4_get_inode_loc ( inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
if ( error )
return error ;
2006-10-11 12:20:53 +04:00
raw_inode = ext4_raw_inode ( & iloc ) ;
2006-10-11 12:20:50 +04:00
header = IHDR ( inode , raw_inode ) ;
entry = IFIRST ( header ) ;
2006-10-11 12:20:53 +04:00
end = ( void * ) raw_inode + EXT4_SB ( inode - > i_sb ) - > s_inode_size ;
error = ext4_xattr_check_names ( entry , end ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_find_entry ( & entry , name_index , name ,
2006-10-11 12:20:50 +04:00
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 ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_get ( )
2006-10-11 12:20:50 +04:00
*
* 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
2006-10-11 12:20:53 +04:00
ext4_xattr_get ( struct inode * inode , int name_index , const char * name ,
2006-10-11 12:20:50 +04:00
void * buffer , size_t buffer_size )
{
int error ;
2006-10-11 12:20:53 +04:00
down_read ( & EXT4_I ( inode ) - > xattr_sem ) ;
error = ext4_xattr_ibody_get ( inode , name_index , name , buffer ,
2006-10-11 12:20:50 +04:00
buffer_size ) ;
if ( error = = - ENODATA )
2006-10-11 12:20:53 +04:00
error = ext4_xattr_block_get ( inode , name_index , name , buffer ,
2006-10-11 12:20:50 +04:00
buffer_size ) ;
2006-10-11 12:20:53 +04:00
up_read ( & EXT4_I ( inode ) - > xattr_sem ) ;
2006-10-11 12:20:50 +04:00
return error ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_list_entries ( struct inode * inode , struct ext4_xattr_entry * entry ,
2006-10-11 12:20:50 +04:00
char * buffer , size_t buffer_size )
{
size_t rest = buffer_size ;
2006-10-11 12:20:53 +04:00
for ( ; ! IS_LAST_ENTRY ( entry ) ; entry = EXT4_XATTR_NEXT ( entry ) ) {
2006-10-11 12:20:50 +04:00
struct xattr_handler * handler =
2006-10-11 12:20:53 +04:00
ext4_xattr_handler ( entry - > e_name_index ) ;
2006-10-11 12:20:50 +04:00
if ( handler ) {
size_t size = handler - > list ( inode , buffer , rest ,
entry - > e_name ,
entry - > e_name_len ) ;
if ( buffer ) {
if ( size > rest )
return - ERANGE ;
buffer + = size ;
}
rest - = size ;
}
}
return buffer_size - rest ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_block_list ( struct inode * inode , char * buffer , size_t buffer_size )
2006-10-11 12:20:50 +04:00
{
struct buffer_head * bh = NULL ;
int error ;
ea_idebug ( inode , " buffer=%p, buffer_size=%ld " ,
buffer , ( long ) buffer_size ) ;
error = 0 ;
2006-10-11 12:20:53 +04:00
if ( ! EXT4_I ( inode ) - > i_file_acl )
2006-10-11 12:20:50 +04:00
goto cleanup ;
2006-10-11 12:20:53 +04:00
ea_idebug ( inode , " reading block %u " , EXT4_I ( inode ) - > i_file_acl ) ;
bh = sb_bread ( inode - > i_sb , EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
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 ) ) ;
2006-10-11 12:20:53 +04:00
if ( ext4_xattr_check_block ( bh ) ) {
ext4_error ( inode - > i_sb , __FUNCTION__ ,
2006-10-11 12:20:50 +04:00
" inode %lu: bad block " E3FSBLK , inode - > i_ino ,
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
error = - EIO ;
goto cleanup ;
}
2006-10-11 12:20:53 +04:00
ext4_xattr_cache_insert ( bh ) ;
error = ext4_xattr_list_entries ( inode , BFIRST ( bh ) , buffer , buffer_size ) ;
2006-10-11 12:20:50 +04:00
cleanup :
brelse ( bh ) ;
return error ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_ibody_list ( struct inode * inode , char * buffer , size_t buffer_size )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_ibody_header * header ;
struct ext4_inode * raw_inode ;
struct ext4_iloc iloc ;
2006-10-11 12:20:50 +04:00
void * end ;
int error ;
2006-10-11 12:20:53 +04:00
if ( ! ( EXT4_I ( inode ) - > i_state & EXT4_STATE_XATTR ) )
2006-10-11 12:20:50 +04:00
return 0 ;
2006-10-11 12:20:53 +04:00
error = ext4_get_inode_loc ( inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
if ( error )
return error ;
2006-10-11 12:20:53 +04:00
raw_inode = ext4_raw_inode ( & iloc ) ;
2006-10-11 12:20:50 +04:00
header = IHDR ( inode , raw_inode ) ;
2006-10-11 12:20:53 +04:00
end = ( void * ) raw_inode + EXT4_SB ( inode - > i_sb ) - > s_inode_size ;
error = ext4_xattr_check_names ( IFIRST ( header ) , end ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_list_entries ( inode , IFIRST ( header ) ,
2006-10-11 12:20:50 +04:00
buffer , buffer_size ) ;
cleanup :
brelse ( iloc . bh ) ;
return error ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_list ( )
2006-10-11 12:20:50 +04:00
*
* 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 .
*/
int
2006-10-11 12:20:53 +04:00
ext4_xattr_list ( struct inode * inode , char * buffer , size_t buffer_size )
2006-10-11 12:20:50 +04:00
{
int i_error , b_error ;
2006-10-11 12:20:53 +04:00
down_read ( & EXT4_I ( inode ) - > xattr_sem ) ;
i_error = ext4_xattr_ibody_list ( inode , buffer , buffer_size ) ;
2006-10-11 12:20:50 +04:00
if ( i_error < 0 ) {
b_error = 0 ;
} else {
if ( buffer ) {
buffer + = i_error ;
buffer_size - = i_error ;
}
2006-10-11 12:20:53 +04:00
b_error = ext4_xattr_block_list ( inode , buffer , buffer_size ) ;
2006-10-11 12:20:50 +04:00
if ( b_error < 0 )
i_error = 0 ;
}
2006-10-11 12:20:53 +04:00
up_read ( & EXT4_I ( inode ) - > xattr_sem ) ;
2006-10-11 12:20:50 +04:00
return i_error + b_error ;
}
/*
2006-10-11 12:20:53 +04:00
* If the EXT4_FEATURE_COMPAT_EXT_ATTR feature of this file system is
2006-10-11 12:20:50 +04:00
* not set , set it .
*/
2006-10-11 12:20:53 +04:00
static void ext4_xattr_update_super_block ( handle_t * handle ,
2006-10-11 12:20:50 +04:00
struct super_block * sb )
{
2006-10-11 12:20:53 +04:00
if ( EXT4_HAS_COMPAT_FEATURE ( sb , EXT4_FEATURE_COMPAT_EXT_ATTR ) )
2006-10-11 12:20:50 +04:00
return ;
lock_super ( sb ) ;
2006-10-11 12:20:53 +04:00
if ( ext4_journal_get_write_access ( handle , EXT4_SB ( sb ) - > s_sbh ) = = 0 ) {
EXT4_SB ( sb ) - > s_es - > s_feature_compat | =
cpu_to_le32 ( EXT4_FEATURE_COMPAT_EXT_ATTR ) ;
2006-10-11 12:20:50 +04:00
sb - > s_dirt = 1 ;
2006-10-11 12:20:53 +04:00
ext4_journal_dirty_metadata ( handle , EXT4_SB ( sb ) - > s_sbh ) ;
2006-10-11 12:20:50 +04:00
}
unlock_super ( sb ) ;
}
/*
* Release the xattr block BH : If the reference count is > 1 , decrement
* it ; otherwise free the block .
*/
static void
2006-10-11 12:20:53 +04:00
ext4_xattr_release_block ( handle_t * handle , struct inode * inode ,
2006-10-11 12:20:50 +04:00
struct buffer_head * bh )
{
struct mb_cache_entry * ce = NULL ;
2006-10-11 12:20:53 +04:00
ce = mb_cache_entry_get ( ext4_xattr_cache , bh - > b_bdev , bh - > b_blocknr ) ;
2006-10-11 12:20:50 +04:00
if ( BHDR ( bh ) - > h_refcount = = cpu_to_le32 ( 1 ) ) {
ea_bdebug ( bh , " refcount now=0; freeing " ) ;
if ( ce )
mb_cache_entry_free ( ce ) ;
2006-10-11 12:20:53 +04:00
ext4_free_blocks ( handle , inode , bh - > b_blocknr , 1 ) ;
2006-10-11 12:20:50 +04:00
get_bh ( bh ) ;
2006-10-11 12:20:53 +04:00
ext4_forget ( handle , 1 , inode , bh , bh - > b_blocknr ) ;
2006-10-11 12:20:50 +04:00
} else {
2006-10-11 12:20:53 +04:00
if ( ext4_journal_get_write_access ( handle , bh ) = = 0 ) {
2006-10-11 12:20:50 +04:00
lock_buffer ( bh ) ;
BHDR ( bh ) - > h_refcount = cpu_to_le32 (
le32_to_cpu ( BHDR ( bh ) - > h_refcount ) - 1 ) ;
2006-10-11 12:20:53 +04:00
ext4_journal_dirty_metadata ( handle , bh ) ;
2006-10-11 12:20:50 +04:00
if ( IS_SYNC ( inode ) )
handle - > h_sync = 1 ;
DQUOT_FREE_BLOCK ( inode , 1 ) ;
unlock_buffer ( bh ) ;
ea_bdebug ( bh , " refcount now=%d; releasing " ,
le32_to_cpu ( BHDR ( bh ) - > h_refcount ) ) ;
}
if ( ce )
mb_cache_entry_release ( ce ) ;
}
}
2006-10-11 12:20:53 +04:00
struct ext4_xattr_info {
2006-10-11 12:20:50 +04:00
int name_index ;
const char * name ;
const void * value ;
size_t value_len ;
} ;
2006-10-11 12:20:53 +04:00
struct ext4_xattr_search {
struct ext4_xattr_entry * first ;
2006-10-11 12:20:50 +04:00
void * base ;
void * end ;
2006-10-11 12:20:53 +04:00
struct ext4_xattr_entry * here ;
2006-10-11 12:20:50 +04:00
int not_found ;
} ;
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_set_entry ( struct ext4_xattr_info * i , struct ext4_xattr_search * s )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_entry * last ;
2006-10-11 12:20:50 +04:00
size_t free , min_offs = s - > end - s - > base , name_len = strlen ( i - > name ) ;
/* Compute min_offs and last. */
last = s - > first ;
2006-10-11 12:20:53 +04:00
for ( ; ! IS_LAST_ENTRY ( last ) ; last = EXT4_XATTR_NEXT ( last ) ) {
2006-10-11 12:20:50 +04:00
if ( ! last - > e_value_block & & last - > e_value_size ) {
size_t offs = le16_to_cpu ( last - > e_value_offs ) ;
if ( offs < min_offs )
min_offs = offs ;
}
}
free = min_offs - ( ( void * ) last - s - > base ) - sizeof ( __u32 ) ;
if ( ! s - > not_found ) {
if ( ! s - > here - > e_value_block & & s - > here - > e_value_size ) {
size_t size = le32_to_cpu ( s - > here - > e_value_size ) ;
2006-10-11 12:20:53 +04:00
free + = EXT4_XATTR_SIZE ( size ) ;
2006-10-11 12:20:50 +04:00
}
2006-10-11 12:20:53 +04:00
free + = EXT4_XATTR_LEN ( name_len ) ;
2006-10-11 12:20:50 +04:00
}
if ( i - > value ) {
2006-10-11 12:20:53 +04:00
if ( free < EXT4_XATTR_SIZE ( i - > value_len ) | |
free < EXT4_XATTR_LEN ( name_len ) +
EXT4_XATTR_SIZE ( i - > value_len ) )
2006-10-11 12:20:50 +04:00
return - ENOSPC ;
}
if ( i - > value & & s - > not_found ) {
/* Insert the new name. */
2006-10-11 12:20:53 +04:00
size_t size = EXT4_XATTR_LEN ( name_len ) ;
2006-10-11 12:20:50 +04:00
size_t rest = ( void * ) last - ( void * ) s - > here + sizeof ( __u32 ) ;
memmove ( ( void * ) s - > here + size , s - > here , rest ) ;
memset ( s - > here , 0 , size ) ;
s - > here - > e_name_index = i - > name_index ;
s - > here - > e_name_len = name_len ;
memcpy ( s - > here - > e_name , i - > name , name_len ) ;
} else {
if ( ! s - > here - > e_value_block & & s - > here - > e_value_size ) {
void * first_val = s - > base + min_offs ;
size_t offs = le16_to_cpu ( s - > here - > e_value_offs ) ;
void * val = s - > base + offs ;
2006-10-11 12:20:53 +04:00
size_t size = EXT4_XATTR_SIZE (
2006-10-11 12:20:50 +04:00
le32_to_cpu ( s - > here - > e_value_size ) ) ;
2006-10-11 12:20:53 +04:00
if ( i - > value & & size = = EXT4_XATTR_SIZE ( i - > value_len ) ) {
2006-10-11 12:20:50 +04:00
/* The old and the new value have the same
size . Just replace . */
s - > here - > e_value_size =
cpu_to_le32 ( i - > value_len ) ;
2006-10-11 12:20:53 +04:00
memset ( val + size - EXT4_XATTR_PAD , 0 ,
EXT4_XATTR_PAD ) ; /* Clear pad bytes. */
2006-10-11 12:20:50 +04:00
memcpy ( val , i - > value , i - > value_len ) ;
return 0 ;
}
/* Remove the old value. */
memmove ( first_val + size , first_val , val - first_val ) ;
memset ( first_val , 0 , size ) ;
s - > here - > e_value_size = 0 ;
s - > here - > e_value_offs = 0 ;
min_offs + = size ;
/* Adjust all value offsets. */
last = s - > first ;
while ( ! IS_LAST_ENTRY ( last ) ) {
size_t o = le16_to_cpu ( last - > e_value_offs ) ;
if ( ! last - > e_value_block & &
last - > e_value_size & & o < offs )
last - > e_value_offs =
cpu_to_le16 ( o + size ) ;
2006-10-11 12:20:53 +04:00
last = EXT4_XATTR_NEXT ( last ) ;
2006-10-11 12:20:50 +04:00
}
}
if ( ! i - > value ) {
/* Remove the old name. */
2006-10-11 12:20:53 +04:00
size_t size = EXT4_XATTR_LEN ( name_len ) ;
2006-10-11 12:20:50 +04:00
last = ENTRY ( ( void * ) last - size ) ;
memmove ( s - > here , ( void * ) s - > here + size ,
( void * ) last - ( void * ) s - > here + sizeof ( __u32 ) ) ;
memset ( last , 0 , size ) ;
}
}
if ( i - > value ) {
/* Insert the new value. */
s - > here - > e_value_size = cpu_to_le32 ( i - > value_len ) ;
if ( i - > value_len ) {
2006-10-11 12:20:53 +04:00
size_t size = EXT4_XATTR_SIZE ( i - > value_len ) ;
2006-10-11 12:20:50 +04:00
void * val = s - > base + min_offs - size ;
s - > here - > e_value_offs = cpu_to_le16 ( min_offs - size ) ;
2006-10-11 12:20:53 +04:00
memset ( val + size - EXT4_XATTR_PAD , 0 ,
EXT4_XATTR_PAD ) ; /* Clear the pad bytes. */
2006-10-11 12:20:50 +04:00
memcpy ( val , i - > value , i - > value_len ) ;
}
}
return 0 ;
}
2006-10-11 12:20:53 +04:00
struct ext4_xattr_block_find {
struct ext4_xattr_search s ;
2006-10-11 12:20:50 +04:00
struct buffer_head * bh ;
} ;
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_block_find ( struct inode * inode , struct ext4_xattr_info * i ,
struct ext4_xattr_block_find * bs )
2006-10-11 12:20:50 +04:00
{
struct super_block * sb = inode - > i_sb ;
int error ;
ea_idebug ( inode , " name=%d.%s, value=%p, value_len=%ld " ,
i - > name_index , i - > name , i - > value , ( long ) i - > value_len ) ;
2006-10-11 12:20:53 +04:00
if ( EXT4_I ( inode ) - > i_file_acl ) {
2006-10-11 12:20:50 +04:00
/* The inode already has an extended attribute block. */
2006-10-11 12:20:53 +04:00
bs - > bh = sb_bread ( sb , EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
error = - EIO ;
if ( ! bs - > bh )
goto cleanup ;
ea_bdebug ( bs - > bh , " b_count=%d, refcount=%d " ,
atomic_read ( & ( bs - > bh - > b_count ) ) ,
le32_to_cpu ( BHDR ( bs - > bh ) - > h_refcount ) ) ;
2006-10-11 12:20:53 +04:00
if ( ext4_xattr_check_block ( bs - > bh ) ) {
ext4_error ( sb , __FUNCTION__ ,
2006-10-11 12:20:50 +04:00
" inode %lu: bad block " E3FSBLK , inode - > i_ino ,
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
error = - EIO ;
goto cleanup ;
}
/* Find the named attribute. */
bs - > s . base = BHDR ( bs - > bh ) ;
bs - > s . first = BFIRST ( bs - > bh ) ;
bs - > s . end = bs - > bh - > b_data + bs - > bh - > b_size ;
bs - > s . here = bs - > s . first ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_find_entry ( & bs - > s . here , i - > name_index ,
2006-10-11 12:20:50 +04:00
i - > name , bs - > bh - > b_size , 1 ) ;
if ( error & & error ! = - ENODATA )
goto cleanup ;
bs - > s . not_found = error ;
}
error = 0 ;
cleanup :
return error ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_block_set ( handle_t * handle , struct inode * inode ,
struct ext4_xattr_info * i ,
struct ext4_xattr_block_find * bs )
2006-10-11 12:20:50 +04:00
{
struct super_block * sb = inode - > i_sb ;
struct buffer_head * new_bh = NULL ;
2006-10-11 12:20:53 +04:00
struct ext4_xattr_search * s = & bs - > s ;
2006-10-11 12:20:50 +04:00
struct mb_cache_entry * ce = NULL ;
int error ;
2006-10-11 12:20:53 +04:00
# define header(x) ((struct ext4_xattr_header *)(x))
2006-10-11 12:20:50 +04:00
if ( i - > value & & i - > value_len > sb - > s_blocksize )
return - ENOSPC ;
if ( s - > base ) {
2006-10-11 12:20:53 +04:00
ce = mb_cache_entry_get ( ext4_xattr_cache , bs - > bh - > b_bdev ,
2006-10-11 12:20:50 +04:00
bs - > bh - > b_blocknr ) ;
if ( header ( s - > base ) - > h_refcount = = cpu_to_le32 ( 1 ) ) {
if ( ce ) {
mb_cache_entry_free ( ce ) ;
ce = NULL ;
}
ea_bdebug ( bs - > bh , " modifying in-place " ) ;
2006-10-11 12:20:53 +04:00
error = ext4_journal_get_write_access ( handle , bs - > bh ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
lock_buffer ( bs - > bh ) ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_set_entry ( i , s ) ;
2006-10-11 12:20:50 +04:00
if ( ! error ) {
if ( ! IS_LAST_ENTRY ( s - > first ) )
2006-10-11 12:20:53 +04:00
ext4_xattr_rehash ( header ( s - > base ) ,
2006-10-11 12:20:50 +04:00
s - > here ) ;
2006-10-11 12:20:53 +04:00
ext4_xattr_cache_insert ( bs - > bh ) ;
2006-10-11 12:20:50 +04:00
}
unlock_buffer ( bs - > bh ) ;
if ( error = = - EIO )
goto bad_block ;
if ( ! error )
2006-10-11 12:20:53 +04:00
error = ext4_journal_dirty_metadata ( handle ,
2006-10-11 12:20:50 +04:00
bs - > bh ) ;
if ( error )
goto cleanup ;
goto inserted ;
} else {
int offset = ( char * ) s - > here - bs - > bh - > b_data ;
if ( ce ) {
mb_cache_entry_release ( ce ) ;
ce = NULL ;
}
ea_bdebug ( bs - > bh , " cloning " ) ;
s - > base = kmalloc ( bs - > bh - > b_size , GFP_KERNEL ) ;
error = - ENOMEM ;
if ( s - > base = = NULL )
goto cleanup ;
memcpy ( s - > base , BHDR ( bs - > bh ) , bs - > bh - > b_size ) ;
s - > first = ENTRY ( header ( s - > base ) + 1 ) ;
header ( s - > base ) - > h_refcount = cpu_to_le32 ( 1 ) ;
s - > here = ENTRY ( s - > base + offset ) ;
s - > end = s - > base + bs - > bh - > b_size ;
}
} else {
/* Allocate a buffer where we construct the new block. */
s - > base = kmalloc ( sb - > s_blocksize , GFP_KERNEL ) ;
/* assert(header == s->base) */
error = - ENOMEM ;
if ( s - > base = = NULL )
goto cleanup ;
memset ( s - > base , 0 , sb - > s_blocksize ) ;
2006-10-11 12:20:53 +04:00
header ( s - > base ) - > h_magic = cpu_to_le32 ( EXT4_XATTR_MAGIC ) ;
2006-10-11 12:20:50 +04:00
header ( s - > base ) - > h_blocks = cpu_to_le32 ( 1 ) ;
header ( s - > base ) - > h_refcount = cpu_to_le32 ( 1 ) ;
s - > first = ENTRY ( header ( s - > base ) + 1 ) ;
s - > here = ENTRY ( header ( s - > base ) + 1 ) ;
s - > end = s - > base + sb - > s_blocksize ;
}
2006-10-11 12:20:53 +04:00
error = ext4_xattr_set_entry ( i , s ) ;
2006-10-11 12:20:50 +04:00
if ( error = = - EIO )
goto bad_block ;
if ( error )
goto cleanup ;
if ( ! IS_LAST_ENTRY ( s - > first ) )
2006-10-11 12:20:53 +04:00
ext4_xattr_rehash ( header ( s - > base ) , s - > here ) ;
2006-10-11 12:20:50 +04:00
inserted :
if ( ! IS_LAST_ENTRY ( s - > first ) ) {
2006-10-11 12:20:53 +04:00
new_bh = ext4_xattr_cache_find ( inode , header ( s - > base ) , & ce ) ;
2006-10-11 12:20:50 +04:00
if ( new_bh ) {
/* We found an identical block in the cache. */
if ( new_bh = = bs - > bh )
ea_bdebug ( new_bh , " keeping " ) ;
else {
/* The old block is released after updating
the inode . */
error = - EDQUOT ;
if ( DQUOT_ALLOC_BLOCK ( inode , 1 ) )
goto cleanup ;
2006-10-11 12:20:53 +04:00
error = ext4_journal_get_write_access ( handle ,
2006-10-11 12:20:50 +04:00
new_bh ) ;
if ( error )
goto cleanup_dquot ;
lock_buffer ( new_bh ) ;
BHDR ( new_bh ) - > h_refcount = cpu_to_le32 ( 1 +
le32_to_cpu ( BHDR ( new_bh ) - > h_refcount ) ) ;
ea_bdebug ( new_bh , " reusing; refcount now=%d " ,
le32_to_cpu ( BHDR ( new_bh ) - > h_refcount ) ) ;
unlock_buffer ( new_bh ) ;
2006-10-11 12:20:53 +04:00
error = ext4_journal_dirty_metadata ( handle ,
2006-10-11 12:20:50 +04:00
new_bh ) ;
if ( error )
goto cleanup_dquot ;
}
mb_cache_entry_release ( ce ) ;
ce = NULL ;
} else if ( bs - > bh & & s - > base = = bs - > bh - > b_data ) {
/* We were modifying this block in-place. */
ea_bdebug ( bs - > bh , " keeping this block " ) ;
new_bh = bs - > bh ;
get_bh ( new_bh ) ;
} else {
/* We need to allocate a new block */
2006-10-11 12:20:53 +04:00
ext4_fsblk_t goal = le32_to_cpu (
EXT4_SB ( sb ) - > s_es - > s_first_data_block ) +
( ext4_fsblk_t ) EXT4_I ( inode ) - > i_block_group *
EXT4_BLOCKS_PER_GROUP ( sb ) ;
ext4_fsblk_t block = ext4_new_block ( handle , inode ,
2006-10-11 12:20:50 +04:00
goal , & error ) ;
if ( error )
goto cleanup ;
ea_idebug ( inode , " creating block %d " , block ) ;
new_bh = sb_getblk ( sb , block ) ;
if ( ! new_bh ) {
getblk_failed :
2006-10-11 12:20:53 +04:00
ext4_free_blocks ( handle , inode , block , 1 ) ;
2006-10-11 12:20:50 +04:00
error = - EIO ;
goto cleanup ;
}
lock_buffer ( new_bh ) ;
2006-10-11 12:20:53 +04:00
error = ext4_journal_get_create_access ( handle , new_bh ) ;
2006-10-11 12:20:50 +04:00
if ( error ) {
unlock_buffer ( new_bh ) ;
goto getblk_failed ;
}
memcpy ( new_bh - > b_data , s - > base , new_bh - > b_size ) ;
set_buffer_uptodate ( new_bh ) ;
unlock_buffer ( new_bh ) ;
2006-10-11 12:20:53 +04:00
ext4_xattr_cache_insert ( new_bh ) ;
error = ext4_journal_dirty_metadata ( handle , new_bh ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
}
}
/* Update the inode. */
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_file_acl = new_bh ? new_bh - > b_blocknr : 0 ;
2006-10-11 12:20:50 +04:00
/* Drop the previous xattr block. */
if ( bs - > bh & & bs - > bh ! = new_bh )
2006-10-11 12:20:53 +04:00
ext4_xattr_release_block ( handle , inode , bs - > bh ) ;
2006-10-11 12:20:50 +04:00
error = 0 ;
cleanup :
if ( ce )
mb_cache_entry_release ( ce ) ;
brelse ( new_bh ) ;
if ( ! ( bs - > bh & & s - > base = = bs - > bh - > b_data ) )
kfree ( s - > base ) ;
return error ;
cleanup_dquot :
DQUOT_FREE_BLOCK ( inode , 1 ) ;
goto cleanup ;
bad_block :
2006-10-11 12:20:53 +04:00
ext4_error ( inode - > i_sb , __FUNCTION__ ,
2006-10-11 12:20:50 +04:00
" inode %lu: bad block " E3FSBLK , inode - > i_ino ,
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
goto cleanup ;
# undef header
}
2006-10-11 12:20:53 +04:00
struct ext4_xattr_ibody_find {
struct ext4_xattr_search s ;
struct ext4_iloc iloc ;
2006-10-11 12:20:50 +04:00
} ;
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_ibody_find ( struct inode * inode , struct ext4_xattr_info * i ,
struct ext4_xattr_ibody_find * is )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_ibody_header * header ;
struct ext4_inode * raw_inode ;
2006-10-11 12:20:50 +04:00
int error ;
2006-10-11 12:20:53 +04:00
if ( EXT4_I ( inode ) - > i_extra_isize = = 0 )
2006-10-11 12:20:50 +04:00
return 0 ;
2006-10-11 12:20:53 +04:00
raw_inode = ext4_raw_inode ( & is - > iloc ) ;
2006-10-11 12:20:50 +04:00
header = IHDR ( inode , raw_inode ) ;
is - > s . base = is - > s . first = IFIRST ( header ) ;
is - > s . here = is - > s . first ;
2006-10-11 12:20:53 +04:00
is - > s . end = ( void * ) raw_inode + EXT4_SB ( inode - > i_sb ) - > s_inode_size ;
if ( EXT4_I ( inode ) - > i_state & EXT4_STATE_XATTR ) {
error = ext4_xattr_check_names ( IFIRST ( header ) , is - > s . end ) ;
2006-10-11 12:20:50 +04:00
if ( error )
return error ;
/* Find the named attribute. */
2006-10-11 12:20:53 +04:00
error = ext4_xattr_find_entry ( & is - > s . here , i - > name_index ,
2006-10-11 12:20:50 +04:00
i - > name , is - > s . end -
( void * ) is - > s . base , 0 ) ;
if ( error & & error ! = - ENODATA )
return error ;
is - > s . not_found = error ;
}
return 0 ;
}
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_ibody_set ( handle_t * handle , struct inode * inode ,
struct ext4_xattr_info * i ,
struct ext4_xattr_ibody_find * is )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_ibody_header * header ;
struct ext4_xattr_search * s = & is - > s ;
2006-10-11 12:20:50 +04:00
int error ;
2006-10-11 12:20:53 +04:00
if ( EXT4_I ( inode ) - > i_extra_isize = = 0 )
2006-10-11 12:20:50 +04:00
return - ENOSPC ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_set_entry ( i , s ) ;
2006-10-11 12:20:50 +04:00
if ( error )
return error ;
2006-10-11 12:20:53 +04:00
header = IHDR ( inode , ext4_raw_inode ( & is - > iloc ) ) ;
2006-10-11 12:20:50 +04:00
if ( ! IS_LAST_ENTRY ( s - > first ) ) {
2006-10-11 12:20:53 +04:00
header - > h_magic = cpu_to_le32 ( EXT4_XATTR_MAGIC ) ;
EXT4_I ( inode ) - > i_state | = EXT4_STATE_XATTR ;
2006-10-11 12:20:50 +04:00
} else {
header - > h_magic = cpu_to_le32 ( 0 ) ;
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_state & = ~ EXT4_STATE_XATTR ;
2006-10-11 12:20:50 +04:00
}
return 0 ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_set_handle ( )
2006-10-11 12:20:50 +04:00
*
* Create , replace or remove an extended attribute for this inode . Buffer
* is NULL to remove an existing extended attribute , and non - NULL to
* either replace an existing extended attribute , or create a new extended
* attribute . The flags XATTR_REPLACE and XATTR_CREATE
* specify that an extended attribute must exist and must not exist
* previous to the call , respectively .
*
* Returns 0 , or a negative error number on failure .
*/
int
2006-10-11 12:20:53 +04:00
ext4_xattr_set_handle ( handle_t * handle , struct inode * inode , int name_index ,
2006-10-11 12:20:50 +04:00
const char * name , const void * value , size_t value_len ,
int flags )
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_info i = {
2006-10-11 12:20:50 +04:00
. name_index = name_index ,
. name = name ,
. value = value ,
. value_len = value_len ,
} ;
2006-10-11 12:20:53 +04:00
struct ext4_xattr_ibody_find is = {
2006-10-11 12:20:50 +04:00
. s = { . not_found = - ENODATA , } ,
} ;
2006-10-11 12:20:53 +04:00
struct ext4_xattr_block_find bs = {
2006-10-11 12:20:50 +04:00
. s = { . not_found = - ENODATA , } ,
} ;
int error ;
if ( ! name )
return - EINVAL ;
if ( strlen ( name ) > 255 )
return - ERANGE ;
2006-10-11 12:20:53 +04:00
down_write ( & EXT4_I ( inode ) - > xattr_sem ) ;
error = ext4_get_inode_loc ( inode , & is . iloc ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
2006-10-11 12:20:53 +04:00
if ( EXT4_I ( inode ) - > i_state & EXT4_STATE_NEW ) {
struct ext4_inode * raw_inode = ext4_raw_inode ( & is . iloc ) ;
memset ( raw_inode , 0 , EXT4_SB ( inode - > i_sb ) - > s_inode_size ) ;
EXT4_I ( inode ) - > i_state & = ~ EXT4_STATE_NEW ;
2006-10-11 12:20:50 +04:00
}
2006-10-11 12:20:53 +04:00
error = ext4_xattr_ibody_find ( inode , & i , & is ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
if ( is . s . not_found )
2006-10-11 12:20:53 +04:00
error = ext4_xattr_block_find ( inode , & i , & bs ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
if ( is . s . not_found & & bs . s . not_found ) {
error = - ENODATA ;
if ( flags & XATTR_REPLACE )
goto cleanup ;
error = 0 ;
if ( ! value )
goto cleanup ;
} else {
error = - EEXIST ;
if ( flags & XATTR_CREATE )
goto cleanup ;
}
2006-10-11 12:20:53 +04:00
error = ext4_journal_get_write_access ( handle , is . iloc . bh ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
if ( ! value ) {
if ( ! is . s . not_found )
2006-10-11 12:20:53 +04:00
error = ext4_xattr_ibody_set ( handle , inode , & i , & is ) ;
2006-10-11 12:20:50 +04:00
else if ( ! bs . s . not_found )
2006-10-11 12:20:53 +04:00
error = ext4_xattr_block_set ( handle , inode , & i , & bs ) ;
2006-10-11 12:20:50 +04:00
} else {
2006-10-11 12:20:53 +04:00
error = ext4_xattr_ibody_set ( handle , inode , & i , & is ) ;
2006-10-11 12:20:50 +04:00
if ( ! error & & ! bs . s . not_found ) {
i . value = NULL ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_block_set ( handle , inode , & i , & bs ) ;
2006-10-11 12:20:50 +04:00
} else if ( error = = - ENOSPC ) {
2006-10-11 12:20:53 +04:00
error = ext4_xattr_block_set ( handle , inode , & i , & bs ) ;
2006-10-11 12:20:50 +04:00
if ( error )
goto cleanup ;
if ( ! is . s . not_found ) {
i . value = NULL ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_ibody_set ( handle , inode , & i ,
2006-10-11 12:20:50 +04:00
& is ) ;
}
}
}
if ( ! error ) {
2006-10-11 12:20:53 +04:00
ext4_xattr_update_super_block ( handle , inode - > i_sb ) ;
2006-10-11 12:20:50 +04:00
inode - > i_ctime = CURRENT_TIME_SEC ;
2006-10-11 12:20:53 +04:00
error = ext4_mark_iloc_dirty ( handle , inode , & is . iloc ) ;
2006-10-11 12:20:50 +04:00
/*
2006-10-11 12:20:53 +04:00
* The bh is consumed by ext4_mark_iloc_dirty , even with
2006-10-11 12:20:50 +04:00
* error ! = 0.
*/
is . iloc . bh = NULL ;
if ( IS_SYNC ( inode ) )
handle - > h_sync = 1 ;
}
cleanup :
brelse ( is . iloc . bh ) ;
brelse ( bs . bh ) ;
2006-10-11 12:20:53 +04:00
up_write ( & EXT4_I ( inode ) - > xattr_sem ) ;
2006-10-11 12:20:50 +04:00
return error ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_set ( )
2006-10-11 12:20:50 +04:00
*
2006-10-11 12:20:53 +04:00
* Like ext4_xattr_set_handle , but start from an inode . This extended
2006-10-11 12:20:50 +04:00
* attribute modification is a filesystem transaction by itself .
*
* Returns 0 , or a negative error number on failure .
*/
int
2006-10-11 12:20:53 +04:00
ext4_xattr_set ( struct inode * inode , int name_index , const char * name ,
2006-10-11 12:20:50 +04:00
const void * value , size_t value_len , int flags )
{
handle_t * handle ;
int error , retries = 0 ;
retry :
2006-10-11 12:20:53 +04:00
handle = ext4_journal_start ( inode , EXT4_DATA_TRANS_BLOCKS ( inode - > i_sb ) ) ;
2006-10-11 12:20:50 +04:00
if ( IS_ERR ( handle ) ) {
error = PTR_ERR ( handle ) ;
} else {
int error2 ;
2006-10-11 12:20:53 +04:00
error = ext4_xattr_set_handle ( handle , inode , name_index , name ,
2006-10-11 12:20:50 +04:00
value , value_len , flags ) ;
2006-10-11 12:20:53 +04:00
error2 = ext4_journal_stop ( handle ) ;
2006-10-11 12:20:50 +04:00
if ( error = = - ENOSPC & &
2006-10-11 12:20:53 +04:00
ext4_should_retry_alloc ( inode - > i_sb , & retries ) )
2006-10-11 12:20:50 +04:00
goto retry ;
if ( error = = 0 )
error = error2 ;
}
return error ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_delete_inode ( )
2006-10-11 12:20:50 +04:00
*
* Free extended attribute resources associated with this inode . This
* is called immediately before an inode is freed . We have exclusive
* access to the inode .
*/
void
2006-10-11 12:20:53 +04:00
ext4_xattr_delete_inode ( handle_t * handle , struct inode * inode )
2006-10-11 12:20:50 +04:00
{
struct buffer_head * bh = NULL ;
2006-10-11 12:20:53 +04:00
if ( ! EXT4_I ( inode ) - > i_file_acl )
2006-10-11 12:20:50 +04:00
goto cleanup ;
2006-10-11 12:20:53 +04:00
bh = sb_bread ( inode - > i_sb , EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
if ( ! bh ) {
2006-10-11 12:20:53 +04:00
ext4_error ( inode - > i_sb , __FUNCTION__ ,
2006-10-11 12:20:50 +04:00
" inode %lu: block " E3FSBLK " read error " , inode - > i_ino ,
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
goto cleanup ;
}
2006-10-11 12:20:53 +04:00
if ( BHDR ( bh ) - > h_magic ! = cpu_to_le32 ( EXT4_XATTR_MAGIC ) | |
2006-10-11 12:20:50 +04:00
BHDR ( bh ) - > h_blocks ! = cpu_to_le32 ( 1 ) ) {
2006-10-11 12:20:53 +04:00
ext4_error ( inode - > i_sb , __FUNCTION__ ,
2006-10-11 12:20:50 +04:00
" inode %lu: bad block " E3FSBLK , inode - > i_ino ,
2006-10-11 12:20:53 +04:00
EXT4_I ( inode ) - > i_file_acl ) ;
2006-10-11 12:20:50 +04:00
goto cleanup ;
}
2006-10-11 12:20:53 +04:00
ext4_xattr_release_block ( handle , inode , bh ) ;
EXT4_I ( inode ) - > i_file_acl = 0 ;
2006-10-11 12:20:50 +04:00
cleanup :
brelse ( bh ) ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_put_super ( )
2006-10-11 12:20:50 +04:00
*
* This is called when a file system is unmounted .
*/
void
2006-10-11 12:20:53 +04:00
ext4_xattr_put_super ( struct super_block * sb )
2006-10-11 12:20:50 +04:00
{
mb_cache_shrink ( sb - > s_bdev ) ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_cache_insert ( )
2006-10-11 12:20:50 +04:00
*
* Create a new entry in the extended attribute cache , and insert
* it unless such an entry is already in the cache .
*
* Returns 0 , or a negative error number on failure .
*/
static void
2006-10-11 12:20:53 +04:00
ext4_xattr_cache_insert ( struct buffer_head * bh )
2006-10-11 12:20:50 +04:00
{
__u32 hash = le32_to_cpu ( BHDR ( bh ) - > h_hash ) ;
struct mb_cache_entry * ce ;
int error ;
2006-10-11 12:20:53 +04:00
ce = mb_cache_entry_alloc ( ext4_xattr_cache ) ;
2006-10-11 12:20:50 +04:00
if ( ! ce ) {
ea_bdebug ( bh , " out of memory " ) ;
return ;
}
error = mb_cache_entry_insert ( ce , bh - > b_bdev , bh - > b_blocknr , & hash ) ;
if ( error ) {
mb_cache_entry_free ( ce ) ;
if ( error = = - EBUSY ) {
ea_bdebug ( bh , " already in cache " ) ;
error = 0 ;
}
} else {
ea_bdebug ( bh , " inserting [%x] " , ( int ) hash ) ;
mb_cache_entry_release ( ce ) ;
}
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_cmp ( )
2006-10-11 12:20:50 +04:00
*
* Compare two extended attribute blocks for equality .
*
* Returns 0 if the blocks are equal , 1 if they differ , and
* a negative error number on errors .
*/
static int
2006-10-11 12:20:53 +04:00
ext4_xattr_cmp ( struct ext4_xattr_header * header1 ,
struct ext4_xattr_header * header2 )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_entry * entry1 , * entry2 ;
2006-10-11 12:20:50 +04:00
entry1 = ENTRY ( header1 + 1 ) ;
entry2 = ENTRY ( header2 + 1 ) ;
while ( ! IS_LAST_ENTRY ( entry1 ) ) {
if ( IS_LAST_ENTRY ( entry2 ) )
return 1 ;
if ( entry1 - > e_hash ! = entry2 - > e_hash | |
entry1 - > e_name_index ! = entry2 - > e_name_index | |
entry1 - > e_name_len ! = entry2 - > e_name_len | |
entry1 - > e_value_size ! = entry2 - > e_value_size | |
memcmp ( entry1 - > e_name , entry2 - > e_name , entry1 - > e_name_len ) )
return 1 ;
if ( entry1 - > e_value_block ! = 0 | | entry2 - > e_value_block ! = 0 )
return - EIO ;
if ( memcmp ( ( char * ) header1 + le16_to_cpu ( entry1 - > e_value_offs ) ,
( char * ) header2 + le16_to_cpu ( entry2 - > e_value_offs ) ,
le32_to_cpu ( entry1 - > e_value_size ) ) )
return 1 ;
2006-10-11 12:20:53 +04:00
entry1 = EXT4_XATTR_NEXT ( entry1 ) ;
entry2 = EXT4_XATTR_NEXT ( entry2 ) ;
2006-10-11 12:20:50 +04:00
}
if ( ! IS_LAST_ENTRY ( entry2 ) )
return 1 ;
return 0 ;
}
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_cache_find ( )
2006-10-11 12:20:50 +04:00
*
* Find an identical extended attribute block .
*
* Returns a pointer to the block found , or NULL if such a block was
* not found or an error occurred .
*/
static struct buffer_head *
2006-10-11 12:20:53 +04:00
ext4_xattr_cache_find ( struct inode * inode , struct ext4_xattr_header * header ,
2006-10-11 12:20:50 +04:00
struct mb_cache_entry * * pce )
{
__u32 hash = le32_to_cpu ( header - > h_hash ) ;
struct mb_cache_entry * ce ;
if ( ! header - > h_hash )
return NULL ; /* never share */
ea_idebug ( inode , " looking for cached blocks [%x] " , ( int ) hash ) ;
again :
2006-10-11 12:20:53 +04:00
ce = mb_cache_entry_find_first ( ext4_xattr_cache , 0 ,
2006-10-11 12:20:50 +04:00
inode - > i_sb - > s_bdev , hash ) ;
while ( ce ) {
struct buffer_head * bh ;
if ( IS_ERR ( ce ) ) {
if ( PTR_ERR ( ce ) = = - EAGAIN )
goto again ;
break ;
}
bh = sb_bread ( inode - > i_sb , ce - > e_block ) ;
if ( ! bh ) {
2006-10-11 12:20:53 +04:00
ext4_error ( inode - > i_sb , __FUNCTION__ ,
2006-10-11 12:20:50 +04:00
" inode %lu: block %lu read error " ,
inode - > i_ino , ( unsigned long ) ce - > e_block ) ;
} else if ( le32_to_cpu ( BHDR ( bh ) - > h_refcount ) > =
2006-10-11 12:20:53 +04:00
EXT4_XATTR_REFCOUNT_MAX ) {
2006-10-11 12:20:50 +04:00
ea_idebug ( inode , " block %lu refcount %d>=%d " ,
( unsigned long ) ce - > e_block ,
le32_to_cpu ( BHDR ( bh ) - > h_refcount ) ,
2006-10-11 12:20:53 +04:00
EXT4_XATTR_REFCOUNT_MAX ) ;
} else if ( ext4_xattr_cmp ( header , BHDR ( bh ) ) = = 0 ) {
2006-10-11 12:20:50 +04:00
* pce = ce ;
return bh ;
}
brelse ( bh ) ;
ce = mb_cache_entry_find_next ( ce , 0 , inode - > i_sb - > s_bdev , hash ) ;
}
return NULL ;
}
# define NAME_HASH_SHIFT 5
# define VALUE_HASH_SHIFT 16
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_hash_entry ( )
2006-10-11 12:20:50 +04:00
*
* Compute the hash of an extended attribute .
*/
2006-10-11 12:20:53 +04:00
static inline void ext4_xattr_hash_entry ( struct ext4_xattr_header * header ,
struct ext4_xattr_entry * entry )
2006-10-11 12:20:50 +04:00
{
__u32 hash = 0 ;
char * name = entry - > e_name ;
int n ;
for ( n = 0 ; n < entry - > e_name_len ; n + + ) {
hash = ( hash < < NAME_HASH_SHIFT ) ^
( hash > > ( 8 * sizeof ( hash ) - NAME_HASH_SHIFT ) ) ^
* name + + ;
}
if ( entry - > e_value_block = = 0 & & entry - > e_value_size ! = 0 ) {
__le32 * value = ( __le32 * ) ( ( char * ) header +
le16_to_cpu ( entry - > e_value_offs ) ) ;
for ( n = ( le32_to_cpu ( entry - > e_value_size ) +
2006-10-11 12:20:53 +04:00
EXT4_XATTR_ROUND ) > > EXT4_XATTR_PAD_BITS ; n ; n - - ) {
2006-10-11 12:20:50 +04:00
hash = ( hash < < VALUE_HASH_SHIFT ) ^
( hash > > ( 8 * sizeof ( hash ) - VALUE_HASH_SHIFT ) ) ^
le32_to_cpu ( * value + + ) ;
}
}
entry - > e_hash = cpu_to_le32 ( hash ) ;
}
# undef NAME_HASH_SHIFT
# undef VALUE_HASH_SHIFT
# define BLOCK_HASH_SHIFT 16
/*
2006-10-11 12:20:53 +04:00
* ext4_xattr_rehash ( )
2006-10-11 12:20:50 +04:00
*
* Re - compute the extended attribute hash value after an entry has changed .
*/
2006-10-11 12:20:53 +04:00
static void ext4_xattr_rehash ( struct ext4_xattr_header * header ,
struct ext4_xattr_entry * entry )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_xattr_entry * here ;
2006-10-11 12:20:50 +04:00
__u32 hash = 0 ;
2006-10-11 12:20:53 +04:00
ext4_xattr_hash_entry ( header , entry ) ;
2006-10-11 12:20:50 +04:00
here = ENTRY ( header + 1 ) ;
while ( ! IS_LAST_ENTRY ( here ) ) {
if ( ! here - > e_hash ) {
/* Block is not shared if an entry's hash value == 0 */
hash = 0 ;
break ;
}
hash = ( hash < < BLOCK_HASH_SHIFT ) ^
( hash > > ( 8 * sizeof ( hash ) - BLOCK_HASH_SHIFT ) ) ^
le32_to_cpu ( here - > e_hash ) ;
2006-10-11 12:20:53 +04:00
here = EXT4_XATTR_NEXT ( here ) ;
2006-10-11 12:20:50 +04:00
}
header - > h_hash = cpu_to_le32 ( hash ) ;
}
# undef BLOCK_HASH_SHIFT
int __init
2006-10-11 12:20:53 +04:00
init_ext4_xattr ( void )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
ext4_xattr_cache = mb_cache_create ( " ext4_xattr " , NULL ,
2006-10-11 12:20:50 +04:00
sizeof ( struct mb_cache_entry ) +
sizeof ( ( ( struct mb_cache_entry * ) 0 ) - > e_indexes [ 0 ] ) , 1 , 6 ) ;
2006-10-11 12:20:53 +04:00
if ( ! ext4_xattr_cache )
2006-10-11 12:20:50 +04:00
return - ENOMEM ;
return 0 ;
}
void
2006-10-11 12:20:53 +04:00
exit_ext4_xattr ( void )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
if ( ext4_xattr_cache )
mb_cache_destroy ( ext4_xattr_cache ) ;
ext4_xattr_cache = NULL ;
2006-10-11 12:20:50 +04:00
}