162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2000-2004 462306a36Sopenharmony_ci * Copyright (C) Christoph Hellwig, 2002 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/capability.h> 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/xattr.h> 1062306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/quotaops.h> 1362306a36Sopenharmony_ci#include <linux/security.h> 1462306a36Sopenharmony_ci#include "jfs_incore.h" 1562306a36Sopenharmony_ci#include "jfs_superblock.h" 1662306a36Sopenharmony_ci#include "jfs_dmap.h" 1762306a36Sopenharmony_ci#include "jfs_debug.h" 1862306a36Sopenharmony_ci#include "jfs_dinode.h" 1962306a36Sopenharmony_ci#include "jfs_extent.h" 2062306a36Sopenharmony_ci#include "jfs_metapage.h" 2162306a36Sopenharmony_ci#include "jfs_xattr.h" 2262306a36Sopenharmony_ci#include "jfs_acl.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * jfs_xattr.c: extended attribute service 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Overall design -- 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Format: 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Extended attribute lists (jfs_ea_list) consist of an overall size (32 bit 3262306a36Sopenharmony_ci * value) and a variable (0 or more) number of extended attribute 3362306a36Sopenharmony_ci * entries. Each extended attribute entry (jfs_ea) is a <name,value> double 3462306a36Sopenharmony_ci * where <name> is constructed from a null-terminated ascii string 3562306a36Sopenharmony_ci * (1 ... 255 bytes in the name) and <value> is arbitrary 8 bit data 3662306a36Sopenharmony_ci * (1 ... 65535 bytes). The in-memory format is 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * 0 1 2 4 4 + namelen + 1 3962306a36Sopenharmony_ci * +-------+--------+--------+----------------+-------------------+ 4062306a36Sopenharmony_ci * | Flags | Name | Value | Name String \0 | Data . . . . | 4162306a36Sopenharmony_ci * | | Length | Length | | | 4262306a36Sopenharmony_ci * +-------+--------+--------+----------------+-------------------+ 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * A jfs_ea_list then is structured as 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * 0 4 4 + EA_SIZE(ea1) 4762306a36Sopenharmony_ci * +------------+-------------------+--------------------+----- 4862306a36Sopenharmony_ci * | Overall EA | First FEA Element | Second FEA Element | ..... 4962306a36Sopenharmony_ci * | List Size | | | 5062306a36Sopenharmony_ci * +------------+-------------------+--------------------+----- 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * On-disk: 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * FEALISTs are stored on disk using blocks allocated by dbAlloc() and 5562306a36Sopenharmony_ci * written directly. An EA list may be in-lined in the inode if there is 5662306a36Sopenharmony_ci * sufficient room available. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct ea_buffer { 6062306a36Sopenharmony_ci int flag; /* Indicates what storage xattr points to */ 6162306a36Sopenharmony_ci int max_size; /* largest xattr that fits in current buffer */ 6262306a36Sopenharmony_ci dxd_t new_ea; /* dxd to replace ea when modifying xattr */ 6362306a36Sopenharmony_ci struct metapage *mp; /* metapage containing ea list */ 6462306a36Sopenharmony_ci struct jfs_ea_list *xattr; /* buffer containing ea list */ 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * ea_buffer.flag values 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define EA_INLINE 0x0001 7162306a36Sopenharmony_ci#define EA_EXTENT 0x0002 7262306a36Sopenharmony_ci#define EA_NEW 0x0004 7362306a36Sopenharmony_ci#define EA_MALLOC 0x0008 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * Mapping of on-disk attribute names: for on-disk attribute names with an 7862306a36Sopenharmony_ci * unknown prefix (not "system.", "user.", "security.", or "trusted."), the 7962306a36Sopenharmony_ci * prefix "os2." is prepended. On the way back to disk, "os2." prefixes are 8062306a36Sopenharmony_ci * stripped and we make sure that the remaining name does not start with one 8162306a36Sopenharmony_ci * of the know prefixes. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int is_known_namespace(const char *name) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) && 8762306a36Sopenharmony_ci strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && 8862306a36Sopenharmony_ci strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && 8962306a36Sopenharmony_ci strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) 9062306a36Sopenharmony_ci return false; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return true; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic inline int name_size(struct jfs_ea *ea) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci if (is_known_namespace(ea->name)) 9862306a36Sopenharmony_ci return ea->namelen; 9962306a36Sopenharmony_ci else 10062306a36Sopenharmony_ci return ea->namelen + XATTR_OS2_PREFIX_LEN; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic inline int copy_name(char *buffer, struct jfs_ea *ea) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int len = ea->namelen; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!is_known_namespace(ea->name)) { 10862306a36Sopenharmony_ci memcpy(buffer, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN); 10962306a36Sopenharmony_ci buffer += XATTR_OS2_PREFIX_LEN; 11062306a36Sopenharmony_ci len += XATTR_OS2_PREFIX_LEN; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci memcpy(buffer, ea->name, ea->namelen); 11362306a36Sopenharmony_ci buffer[ea->namelen] = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return len; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Forward references */ 11962306a36Sopenharmony_cistatic void ea_release(struct inode *inode, struct ea_buffer *ea_buf); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * NAME: ea_write_inline 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * FUNCTION: Attempt to write an EA inline if area is available 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * PRE CONDITIONS: 12762306a36Sopenharmony_ci * Already verified that the specified EA is small enough to fit inline 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * PARAMETERS: 13062306a36Sopenharmony_ci * ip - Inode pointer 13162306a36Sopenharmony_ci * ealist - EA list pointer 13262306a36Sopenharmony_ci * size - size of ealist in bytes 13362306a36Sopenharmony_ci * ea - dxd_t structure to be filled in with necessary EA information 13462306a36Sopenharmony_ci * if we successfully copy the EA inline 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * NOTES: 13762306a36Sopenharmony_ci * Checks if the inode's inline area is available. If so, copies EA inline 13862306a36Sopenharmony_ci * and sets <ea> fields appropriately. Otherwise, returns failure, EA will 13962306a36Sopenharmony_ci * have to be put into an extent. 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * RETURNS: 0 for successful copy to inline area; -1 if area not available 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_cistatic int ea_write_inline(struct inode *ip, struct jfs_ea_list *ealist, 14462306a36Sopenharmony_ci int size, dxd_t * ea) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct jfs_inode_info *ji = JFS_IP(ip); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * Make sure we have an EA -- the NULL EA list is valid, but you 15062306a36Sopenharmony_ci * can't copy it! 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci if (ealist && size > sizeof (struct jfs_ea_list)) { 15362306a36Sopenharmony_ci assert(size <= sizeof (ji->i_inline_ea)); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * See if the space is available or if it is already being 15762306a36Sopenharmony_ci * used for an inline EA. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci if (!(ji->mode2 & INLINEEA) && !(ji->ea.flag & DXD_INLINE)) 16062306a36Sopenharmony_ci return -EPERM; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci DXDsize(ea, size); 16362306a36Sopenharmony_ci DXDlength(ea, 0); 16462306a36Sopenharmony_ci DXDaddress(ea, 0); 16562306a36Sopenharmony_ci memcpy(ji->i_inline_ea, ealist, size); 16662306a36Sopenharmony_ci ea->flag = DXD_INLINE; 16762306a36Sopenharmony_ci ji->mode2 &= ~INLINEEA; 16862306a36Sopenharmony_ci } else { 16962306a36Sopenharmony_ci ea->flag = 0; 17062306a36Sopenharmony_ci DXDsize(ea, 0); 17162306a36Sopenharmony_ci DXDlength(ea, 0); 17262306a36Sopenharmony_ci DXDaddress(ea, 0); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Free up INLINE area */ 17562306a36Sopenharmony_ci if (ji->ea.flag & DXD_INLINE) 17662306a36Sopenharmony_ci ji->mode2 |= INLINEEA; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * NAME: ea_write 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * FUNCTION: Write an EA for an inode 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * PRE CONDITIONS: EA has been verified 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * PARAMETERS: 19062306a36Sopenharmony_ci * ip - Inode pointer 19162306a36Sopenharmony_ci * ealist - EA list pointer 19262306a36Sopenharmony_ci * size - size of ealist in bytes 19362306a36Sopenharmony_ci * ea - dxd_t structure to be filled in appropriately with where the 19462306a36Sopenharmony_ci * EA was copied 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci * NOTES: Will write EA inline if able to, otherwise allocates blocks for an 19762306a36Sopenharmony_ci * extent and synchronously writes it to those blocks. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * RETURNS: 0 for success; Anything else indicates failure 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cistatic int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size, 20262306a36Sopenharmony_ci dxd_t * ea) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct super_block *sb = ip->i_sb; 20562306a36Sopenharmony_ci struct jfs_inode_info *ji = JFS_IP(ip); 20662306a36Sopenharmony_ci struct jfs_sb_info *sbi = JFS_SBI(sb); 20762306a36Sopenharmony_ci int nblocks; 20862306a36Sopenharmony_ci s64 blkno; 20962306a36Sopenharmony_ci int rc = 0, i; 21062306a36Sopenharmony_ci char *cp; 21162306a36Sopenharmony_ci s32 nbytes, nb; 21262306a36Sopenharmony_ci s32 bytes_to_write; 21362306a36Sopenharmony_ci struct metapage *mp; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* 21662306a36Sopenharmony_ci * Quick check to see if this is an in-linable EA. Short EAs 21762306a36Sopenharmony_ci * and empty EAs are all in-linable, provided the space exists. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci if (!ealist || size <= sizeof (ji->i_inline_ea)) { 22062306a36Sopenharmony_ci if (!ea_write_inline(ip, ealist, size, ea)) 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* figure out how many blocks we need */ 22562306a36Sopenharmony_ci nblocks = (size + (sb->s_blocksize - 1)) >> sb->s_blocksize_bits; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Allocate new blocks to quota. */ 22862306a36Sopenharmony_ci rc = dquot_alloc_block(ip, nblocks); 22962306a36Sopenharmony_ci if (rc) 23062306a36Sopenharmony_ci return rc; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno); 23362306a36Sopenharmony_ci if (rc) { 23462306a36Sopenharmony_ci /*Rollback quota allocation. */ 23562306a36Sopenharmony_ci dquot_free_block(ip, nblocks); 23662306a36Sopenharmony_ci return rc; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * Now have nblocks worth of storage to stuff into the FEALIST. 24162306a36Sopenharmony_ci * loop over the FEALIST copying data into the buffer one page at 24262306a36Sopenharmony_ci * a time. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci cp = (char *) ealist; 24562306a36Sopenharmony_ci nbytes = size; 24662306a36Sopenharmony_ci for (i = 0; i < nblocks; i += sbi->nbperpage) { 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * Determine how many bytes for this request, and round up to 24962306a36Sopenharmony_ci * the nearest aggregate block size 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci nb = min(PSIZE, nbytes); 25262306a36Sopenharmony_ci bytes_to_write = 25362306a36Sopenharmony_ci ((((nb + sb->s_blocksize - 1)) >> sb->s_blocksize_bits)) 25462306a36Sopenharmony_ci << sb->s_blocksize_bits; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!(mp = get_metapage(ip, blkno + i, bytes_to_write, 1))) { 25762306a36Sopenharmony_ci rc = -EIO; 25862306a36Sopenharmony_ci goto failed; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci memcpy(mp->data, cp, nb); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * We really need a way to propagate errors for 26562306a36Sopenharmony_ci * forced writes like this one. --hch 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * (__write_metapage => release_metapage => flush_metapage) 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci#ifdef _JFS_FIXME 27062306a36Sopenharmony_ci if ((rc = flush_metapage(mp))) { 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * the write failed -- this means that the buffer 27362306a36Sopenharmony_ci * is still assigned and the blocks are not being 27462306a36Sopenharmony_ci * used. this seems like the best error recovery 27562306a36Sopenharmony_ci * we can get ... 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci goto failed; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci#else 28062306a36Sopenharmony_ci flush_metapage(mp); 28162306a36Sopenharmony_ci#endif 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci cp += PSIZE; 28462306a36Sopenharmony_ci nbytes -= nb; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ea->flag = DXD_EXTENT; 28862306a36Sopenharmony_ci DXDsize(ea, le32_to_cpu(ealist->size)); 28962306a36Sopenharmony_ci DXDlength(ea, nblocks); 29062306a36Sopenharmony_ci DXDaddress(ea, blkno); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Free up INLINE area */ 29362306a36Sopenharmony_ci if (ji->ea.flag & DXD_INLINE) 29462306a36Sopenharmony_ci ji->mode2 |= INLINEEA; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci failed: 29962306a36Sopenharmony_ci /* Rollback quota allocation. */ 30062306a36Sopenharmony_ci dquot_free_block(ip, nblocks); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dbFree(ip, blkno, nblocks); 30362306a36Sopenharmony_ci return rc; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* 30762306a36Sopenharmony_ci * NAME: ea_read_inline 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * FUNCTION: Read an inlined EA into user's buffer 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * PARAMETERS: 31262306a36Sopenharmony_ci * ip - Inode pointer 31362306a36Sopenharmony_ci * ealist - Pointer to buffer to fill in with EA 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * RETURNS: 0 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_cistatic int ea_read_inline(struct inode *ip, struct jfs_ea_list *ealist) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct jfs_inode_info *ji = JFS_IP(ip); 32062306a36Sopenharmony_ci int ea_size = sizeDXD(&ji->ea); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (ea_size == 0) { 32362306a36Sopenharmony_ci ealist->size = 0; 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Sanity Check */ 32862306a36Sopenharmony_ci if ((sizeDXD(&ji->ea) > sizeof (ji->i_inline_ea))) 32962306a36Sopenharmony_ci return -EIO; 33062306a36Sopenharmony_ci if (le32_to_cpu(((struct jfs_ea_list *) &ji->i_inline_ea)->size) 33162306a36Sopenharmony_ci != ea_size) 33262306a36Sopenharmony_ci return -EIO; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci memcpy(ealist, ji->i_inline_ea, ea_size); 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci/* 33962306a36Sopenharmony_ci * NAME: ea_read 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * FUNCTION: copy EA data into user's buffer 34262306a36Sopenharmony_ci * 34362306a36Sopenharmony_ci * PARAMETERS: 34462306a36Sopenharmony_ci * ip - Inode pointer 34562306a36Sopenharmony_ci * ealist - Pointer to buffer to fill in with EA 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * NOTES: If EA is inline calls ea_read_inline() to copy EA. 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * RETURNS: 0 for success; other indicates failure 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_cistatic int ea_read(struct inode *ip, struct jfs_ea_list *ealist) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct super_block *sb = ip->i_sb; 35462306a36Sopenharmony_ci struct jfs_inode_info *ji = JFS_IP(ip); 35562306a36Sopenharmony_ci struct jfs_sb_info *sbi = JFS_SBI(sb); 35662306a36Sopenharmony_ci int nblocks; 35762306a36Sopenharmony_ci s64 blkno; 35862306a36Sopenharmony_ci char *cp = (char *) ealist; 35962306a36Sopenharmony_ci int i; 36062306a36Sopenharmony_ci int nbytes, nb; 36162306a36Sopenharmony_ci s32 bytes_to_read; 36262306a36Sopenharmony_ci struct metapage *mp; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* quick check for in-line EA */ 36562306a36Sopenharmony_ci if (ji->ea.flag & DXD_INLINE) 36662306a36Sopenharmony_ci return ea_read_inline(ip, ealist); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci nbytes = sizeDXD(&ji->ea); 36962306a36Sopenharmony_ci if (!nbytes) { 37062306a36Sopenharmony_ci jfs_error(sb, "nbytes is 0\n"); 37162306a36Sopenharmony_ci return -EIO; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * Figure out how many blocks were allocated when this EA list was 37662306a36Sopenharmony_ci * originally written to disk. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci nblocks = lengthDXD(&ji->ea) << sbi->l2nbperpage; 37962306a36Sopenharmony_ci blkno = addressDXD(&ji->ea) << sbi->l2nbperpage; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * I have found the disk blocks which were originally used to store 38362306a36Sopenharmony_ci * the FEALIST. now i loop over each contiguous block copying the 38462306a36Sopenharmony_ci * data into the buffer. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci for (i = 0; i < nblocks; i += sbi->nbperpage) { 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * Determine how many bytes for this request, and round up to 38962306a36Sopenharmony_ci * the nearest aggregate block size 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci nb = min(PSIZE, nbytes); 39262306a36Sopenharmony_ci bytes_to_read = 39362306a36Sopenharmony_ci ((((nb + sb->s_blocksize - 1)) >> sb->s_blocksize_bits)) 39462306a36Sopenharmony_ci << sb->s_blocksize_bits; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (!(mp = read_metapage(ip, blkno + i, bytes_to_read, 1))) 39762306a36Sopenharmony_ci return -EIO; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci memcpy(cp, mp->data, nb); 40062306a36Sopenharmony_ci release_metapage(mp); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci cp += PSIZE; 40362306a36Sopenharmony_ci nbytes -= nb; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* 41062306a36Sopenharmony_ci * NAME: ea_get 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * FUNCTION: Returns buffer containing existing extended attributes. 41362306a36Sopenharmony_ci * The size of the buffer will be the larger of the existing 41462306a36Sopenharmony_ci * attributes size, or min_size. 41562306a36Sopenharmony_ci * 41662306a36Sopenharmony_ci * The buffer, which may be inlined in the inode or in the 41762306a36Sopenharmony_ci * page cache must be release by calling ea_release or ea_put 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * PARAMETERS: 42062306a36Sopenharmony_ci * inode - Inode pointer 42162306a36Sopenharmony_ci * ea_buf - Structure to be populated with ealist and its metadata 42262306a36Sopenharmony_ci * min_size- minimum size of buffer to be returned 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * RETURNS: 0 for success; Other indicates failure 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct jfs_inode_info *ji = JFS_IP(inode); 42962306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 43062306a36Sopenharmony_ci int size; 43162306a36Sopenharmony_ci int ea_size = sizeDXD(&ji->ea); 43262306a36Sopenharmony_ci int blocks_needed, current_blocks; 43362306a36Sopenharmony_ci s64 blkno; 43462306a36Sopenharmony_ci int rc; 43562306a36Sopenharmony_ci int quota_allocation = 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* When fsck.jfs clears a bad ea, it doesn't clear the size */ 43862306a36Sopenharmony_ci if (ji->ea.flag == 0) 43962306a36Sopenharmony_ci ea_size = 0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (ea_size == 0) { 44262306a36Sopenharmony_ci if (min_size == 0) { 44362306a36Sopenharmony_ci ea_buf->flag = 0; 44462306a36Sopenharmony_ci ea_buf->max_size = 0; 44562306a36Sopenharmony_ci ea_buf->xattr = NULL; 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci if ((min_size <= sizeof (ji->i_inline_ea)) && 44962306a36Sopenharmony_ci (ji->mode2 & INLINEEA)) { 45062306a36Sopenharmony_ci ea_buf->flag = EA_INLINE | EA_NEW; 45162306a36Sopenharmony_ci ea_buf->max_size = sizeof (ji->i_inline_ea); 45262306a36Sopenharmony_ci ea_buf->xattr = (struct jfs_ea_list *) ji->i_inline_ea; 45362306a36Sopenharmony_ci DXDlength(&ea_buf->new_ea, 0); 45462306a36Sopenharmony_ci DXDaddress(&ea_buf->new_ea, 0); 45562306a36Sopenharmony_ci ea_buf->new_ea.flag = DXD_INLINE; 45662306a36Sopenharmony_ci DXDsize(&ea_buf->new_ea, min_size); 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci current_blocks = 0; 46062306a36Sopenharmony_ci } else if (ji->ea.flag & DXD_INLINE) { 46162306a36Sopenharmony_ci if (min_size <= sizeof (ji->i_inline_ea)) { 46262306a36Sopenharmony_ci ea_buf->flag = EA_INLINE; 46362306a36Sopenharmony_ci ea_buf->max_size = sizeof (ji->i_inline_ea); 46462306a36Sopenharmony_ci ea_buf->xattr = (struct jfs_ea_list *) ji->i_inline_ea; 46562306a36Sopenharmony_ci goto size_check; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci current_blocks = 0; 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci if (!(ji->ea.flag & DXD_EXTENT)) { 47062306a36Sopenharmony_ci jfs_error(sb, "invalid ea.flag\n"); 47162306a36Sopenharmony_ci return -EIO; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci current_blocks = (ea_size + sb->s_blocksize - 1) >> 47462306a36Sopenharmony_ci sb->s_blocksize_bits; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci size = max(min_size, ea_size); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (size > PSIZE) { 47962306a36Sopenharmony_ci /* 48062306a36Sopenharmony_ci * To keep the rest of the code simple. Allocate a 48162306a36Sopenharmony_ci * contiguous buffer to work with. Make the buffer large 48262306a36Sopenharmony_ci * enough to make use of the whole extent. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci ea_buf->max_size = (size + sb->s_blocksize - 1) & 48562306a36Sopenharmony_ci ~(sb->s_blocksize - 1); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ea_buf->xattr = kmalloc(ea_buf->max_size, GFP_KERNEL); 48862306a36Sopenharmony_ci if (ea_buf->xattr == NULL) 48962306a36Sopenharmony_ci return -ENOMEM; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ea_buf->flag = EA_MALLOC; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (ea_size == 0) 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if ((rc = ea_read(inode, ea_buf->xattr))) { 49762306a36Sopenharmony_ci kfree(ea_buf->xattr); 49862306a36Sopenharmony_ci ea_buf->xattr = NULL; 49962306a36Sopenharmony_ci return rc; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci goto size_check; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci blocks_needed = (min_size + sb->s_blocksize - 1) >> 50462306a36Sopenharmony_ci sb->s_blocksize_bits; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (blocks_needed > current_blocks) { 50762306a36Sopenharmony_ci /* Allocate new blocks to quota. */ 50862306a36Sopenharmony_ci rc = dquot_alloc_block(inode, blocks_needed); 50962306a36Sopenharmony_ci if (rc) 51062306a36Sopenharmony_ci return -EDQUOT; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci quota_allocation = blocks_needed; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci rc = dbAlloc(inode, INOHINT(inode), (s64) blocks_needed, 51562306a36Sopenharmony_ci &blkno); 51662306a36Sopenharmony_ci if (rc) 51762306a36Sopenharmony_ci goto clean_up; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci DXDlength(&ea_buf->new_ea, blocks_needed); 52062306a36Sopenharmony_ci DXDaddress(&ea_buf->new_ea, blkno); 52162306a36Sopenharmony_ci ea_buf->new_ea.flag = DXD_EXTENT; 52262306a36Sopenharmony_ci DXDsize(&ea_buf->new_ea, min_size); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci ea_buf->flag = EA_EXTENT | EA_NEW; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ea_buf->mp = get_metapage(inode, blkno, 52762306a36Sopenharmony_ci blocks_needed << sb->s_blocksize_bits, 52862306a36Sopenharmony_ci 1); 52962306a36Sopenharmony_ci if (ea_buf->mp == NULL) { 53062306a36Sopenharmony_ci dbFree(inode, blkno, (s64) blocks_needed); 53162306a36Sopenharmony_ci rc = -EIO; 53262306a36Sopenharmony_ci goto clean_up; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci ea_buf->xattr = ea_buf->mp->data; 53562306a36Sopenharmony_ci ea_buf->max_size = (min_size + sb->s_blocksize - 1) & 53662306a36Sopenharmony_ci ~(sb->s_blocksize - 1); 53762306a36Sopenharmony_ci if (ea_size == 0) 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci if ((rc = ea_read(inode, ea_buf->xattr))) { 54062306a36Sopenharmony_ci discard_metapage(ea_buf->mp); 54162306a36Sopenharmony_ci dbFree(inode, blkno, (s64) blocks_needed); 54262306a36Sopenharmony_ci goto clean_up; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci goto size_check; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci ea_buf->flag = EA_EXTENT; 54762306a36Sopenharmony_ci ea_buf->mp = read_metapage(inode, addressDXD(&ji->ea), 54862306a36Sopenharmony_ci lengthDXD(&ji->ea) << sb->s_blocksize_bits, 54962306a36Sopenharmony_ci 1); 55062306a36Sopenharmony_ci if (ea_buf->mp == NULL) { 55162306a36Sopenharmony_ci rc = -EIO; 55262306a36Sopenharmony_ci goto clean_up; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci ea_buf->xattr = ea_buf->mp->data; 55562306a36Sopenharmony_ci ea_buf->max_size = (ea_size + sb->s_blocksize - 1) & 55662306a36Sopenharmony_ci ~(sb->s_blocksize - 1); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci size_check: 55962306a36Sopenharmony_ci if (EALIST_SIZE(ea_buf->xattr) != ea_size) { 56062306a36Sopenharmony_ci printk(KERN_ERR "ea_get: invalid extended attribute\n"); 56162306a36Sopenharmony_ci print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, 56262306a36Sopenharmony_ci ea_buf->xattr, ea_size, 1); 56362306a36Sopenharmony_ci ea_release(inode, ea_buf); 56462306a36Sopenharmony_ci rc = -EIO; 56562306a36Sopenharmony_ci goto clean_up; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return ea_size; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci clean_up: 57162306a36Sopenharmony_ci /* Rollback quota allocation */ 57262306a36Sopenharmony_ci if (quota_allocation) 57362306a36Sopenharmony_ci dquot_free_block(inode, quota_allocation); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return (rc); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void ea_release(struct inode *inode, struct ea_buffer *ea_buf) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci if (ea_buf->flag & EA_MALLOC) 58162306a36Sopenharmony_ci kfree(ea_buf->xattr); 58262306a36Sopenharmony_ci else if (ea_buf->flag & EA_EXTENT) { 58362306a36Sopenharmony_ci assert(ea_buf->mp); 58462306a36Sopenharmony_ci release_metapage(ea_buf->mp); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (ea_buf->flag & EA_NEW) 58762306a36Sopenharmony_ci dbFree(inode, addressDXD(&ea_buf->new_ea), 58862306a36Sopenharmony_ci lengthDXD(&ea_buf->new_ea)); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int ea_put(tid_t tid, struct inode *inode, struct ea_buffer *ea_buf, 59362306a36Sopenharmony_ci int new_size) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct jfs_inode_info *ji = JFS_IP(inode); 59662306a36Sopenharmony_ci unsigned long old_blocks, new_blocks; 59762306a36Sopenharmony_ci int rc = 0; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (new_size == 0) { 60062306a36Sopenharmony_ci ea_release(inode, ea_buf); 60162306a36Sopenharmony_ci ea_buf = NULL; 60262306a36Sopenharmony_ci } else if (ea_buf->flag & EA_INLINE) { 60362306a36Sopenharmony_ci assert(new_size <= sizeof (ji->i_inline_ea)); 60462306a36Sopenharmony_ci ji->mode2 &= ~INLINEEA; 60562306a36Sopenharmony_ci ea_buf->new_ea.flag = DXD_INLINE; 60662306a36Sopenharmony_ci DXDsize(&ea_buf->new_ea, new_size); 60762306a36Sopenharmony_ci DXDaddress(&ea_buf->new_ea, 0); 60862306a36Sopenharmony_ci DXDlength(&ea_buf->new_ea, 0); 60962306a36Sopenharmony_ci } else if (ea_buf->flag & EA_MALLOC) { 61062306a36Sopenharmony_ci rc = ea_write(inode, ea_buf->xattr, new_size, &ea_buf->new_ea); 61162306a36Sopenharmony_ci kfree(ea_buf->xattr); 61262306a36Sopenharmony_ci } else if (ea_buf->flag & EA_NEW) { 61362306a36Sopenharmony_ci /* We have already allocated a new dxd */ 61462306a36Sopenharmony_ci flush_metapage(ea_buf->mp); 61562306a36Sopenharmony_ci } else { 61662306a36Sopenharmony_ci /* ->xattr must point to original ea's metapage */ 61762306a36Sopenharmony_ci rc = ea_write(inode, ea_buf->xattr, new_size, &ea_buf->new_ea); 61862306a36Sopenharmony_ci discard_metapage(ea_buf->mp); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci if (rc) 62162306a36Sopenharmony_ci return rc; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci old_blocks = new_blocks = 0; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (ji->ea.flag & DXD_EXTENT) { 62662306a36Sopenharmony_ci invalidate_dxd_metapages(inode, ji->ea); 62762306a36Sopenharmony_ci old_blocks = lengthDXD(&ji->ea); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (ea_buf) { 63162306a36Sopenharmony_ci txEA(tid, inode, &ji->ea, &ea_buf->new_ea); 63262306a36Sopenharmony_ci if (ea_buf->new_ea.flag & DXD_EXTENT) { 63362306a36Sopenharmony_ci new_blocks = lengthDXD(&ea_buf->new_ea); 63462306a36Sopenharmony_ci if (ji->ea.flag & DXD_INLINE) 63562306a36Sopenharmony_ci ji->mode2 |= INLINEEA; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci ji->ea = ea_buf->new_ea; 63862306a36Sopenharmony_ci } else { 63962306a36Sopenharmony_ci txEA(tid, inode, &ji->ea, NULL); 64062306a36Sopenharmony_ci if (ji->ea.flag & DXD_INLINE) 64162306a36Sopenharmony_ci ji->mode2 |= INLINEEA; 64262306a36Sopenharmony_ci ji->ea.flag = 0; 64362306a36Sopenharmony_ci ji->ea.size = 0; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* If old blocks exist, they must be removed from quota allocation. */ 64762306a36Sopenharmony_ci if (old_blocks) 64862306a36Sopenharmony_ci dquot_free_block(inode, old_blocks); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci inode_set_ctime_current(inode); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ciint __jfs_setxattr(tid_t tid, struct inode *inode, const char *name, 65662306a36Sopenharmony_ci const void *value, size_t value_len, int flags) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct jfs_ea_list *ealist; 65962306a36Sopenharmony_ci struct jfs_ea *ea, *old_ea = NULL, *next_ea = NULL; 66062306a36Sopenharmony_ci struct ea_buffer ea_buf; 66162306a36Sopenharmony_ci int old_ea_size = 0; 66262306a36Sopenharmony_ci int xattr_size; 66362306a36Sopenharmony_ci int new_size; 66462306a36Sopenharmony_ci int namelen = strlen(name); 66562306a36Sopenharmony_ci int found = 0; 66662306a36Sopenharmony_ci int rc; 66762306a36Sopenharmony_ci int length; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci down_write(&JFS_IP(inode)->xattr_sem); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci xattr_size = ea_get(inode, &ea_buf, 0); 67262306a36Sopenharmony_ci if (xattr_size < 0) { 67362306a36Sopenharmony_ci rc = xattr_size; 67462306a36Sopenharmony_ci goto out; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci again: 67862306a36Sopenharmony_ci ealist = (struct jfs_ea_list *) ea_buf.xattr; 67962306a36Sopenharmony_ci new_size = sizeof (struct jfs_ea_list); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (xattr_size) { 68262306a36Sopenharmony_ci for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); 68362306a36Sopenharmony_ci ea = NEXT_EA(ea)) { 68462306a36Sopenharmony_ci if ((namelen == ea->namelen) && 68562306a36Sopenharmony_ci (memcmp(name, ea->name, namelen) == 0)) { 68662306a36Sopenharmony_ci found = 1; 68762306a36Sopenharmony_ci if (flags & XATTR_CREATE) { 68862306a36Sopenharmony_ci rc = -EEXIST; 68962306a36Sopenharmony_ci goto release; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci old_ea = ea; 69262306a36Sopenharmony_ci old_ea_size = EA_SIZE(ea); 69362306a36Sopenharmony_ci next_ea = NEXT_EA(ea); 69462306a36Sopenharmony_ci } else 69562306a36Sopenharmony_ci new_size += EA_SIZE(ea); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (!found) { 70062306a36Sopenharmony_ci if (flags & XATTR_REPLACE) { 70162306a36Sopenharmony_ci rc = -ENODATA; 70262306a36Sopenharmony_ci goto release; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci if (value == NULL) { 70562306a36Sopenharmony_ci rc = 0; 70662306a36Sopenharmony_ci goto release; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci if (value) 71062306a36Sopenharmony_ci new_size += sizeof (struct jfs_ea) + namelen + 1 + value_len; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (new_size > ea_buf.max_size) { 71362306a36Sopenharmony_ci /* 71462306a36Sopenharmony_ci * We need to allocate more space for merged ea list. 71562306a36Sopenharmony_ci * We should only have loop to again: once. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci ea_release(inode, &ea_buf); 71862306a36Sopenharmony_ci xattr_size = ea_get(inode, &ea_buf, new_size); 71962306a36Sopenharmony_ci if (xattr_size < 0) { 72062306a36Sopenharmony_ci rc = xattr_size; 72162306a36Sopenharmony_ci goto out; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci goto again; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Remove old ea of the same name */ 72762306a36Sopenharmony_ci if (found) { 72862306a36Sopenharmony_ci /* number of bytes following target EA */ 72962306a36Sopenharmony_ci length = (char *) END_EALIST(ealist) - (char *) next_ea; 73062306a36Sopenharmony_ci if (length > 0) 73162306a36Sopenharmony_ci memmove(old_ea, next_ea, length); 73262306a36Sopenharmony_ci xattr_size -= old_ea_size; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Add new entry to the end */ 73662306a36Sopenharmony_ci if (value) { 73762306a36Sopenharmony_ci if (xattr_size == 0) 73862306a36Sopenharmony_ci /* Completely new ea list */ 73962306a36Sopenharmony_ci xattr_size = sizeof (struct jfs_ea_list); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* 74262306a36Sopenharmony_ci * The size of EA value is limitted by on-disk format up to 74362306a36Sopenharmony_ci * __le16, there would be an overflow if the size is equal 74462306a36Sopenharmony_ci * to XATTR_SIZE_MAX (65536). In order to avoid this issue, 74562306a36Sopenharmony_ci * we can pre-checkup the value size against USHRT_MAX, and 74662306a36Sopenharmony_ci * return -E2BIG in this case, which is consistent with the 74762306a36Sopenharmony_ci * VFS setxattr interface. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci if (value_len >= USHRT_MAX) { 75062306a36Sopenharmony_ci rc = -E2BIG; 75162306a36Sopenharmony_ci goto release; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ea = (struct jfs_ea *) ((char *) ealist + xattr_size); 75562306a36Sopenharmony_ci ea->flag = 0; 75662306a36Sopenharmony_ci ea->namelen = namelen; 75762306a36Sopenharmony_ci ea->valuelen = (cpu_to_le16(value_len)); 75862306a36Sopenharmony_ci memcpy(ea->name, name, namelen); 75962306a36Sopenharmony_ci ea->name[namelen] = 0; 76062306a36Sopenharmony_ci if (value_len) 76162306a36Sopenharmony_ci memcpy(&ea->name[namelen + 1], value, value_len); 76262306a36Sopenharmony_ci xattr_size += EA_SIZE(ea); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* DEBUG - If we did this right, these number match */ 76662306a36Sopenharmony_ci if (xattr_size != new_size) { 76762306a36Sopenharmony_ci printk(KERN_ERR 76862306a36Sopenharmony_ci "__jfs_setxattr: xattr_size = %d, new_size = %d\n", 76962306a36Sopenharmony_ci xattr_size, new_size); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci rc = -EINVAL; 77262306a36Sopenharmony_ci goto release; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* 77662306a36Sopenharmony_ci * If we're left with an empty list, there's no ea 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci if (new_size == sizeof (struct jfs_ea_list)) 77962306a36Sopenharmony_ci new_size = 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci ealist->size = cpu_to_le32(new_size); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci rc = ea_put(tid, inode, &ea_buf, new_size); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci goto out; 78662306a36Sopenharmony_ci release: 78762306a36Sopenharmony_ci ea_release(inode, &ea_buf); 78862306a36Sopenharmony_ci out: 78962306a36Sopenharmony_ci up_write(&JFS_IP(inode)->xattr_sem); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return rc; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cissize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, 79562306a36Sopenharmony_ci size_t buf_size) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct jfs_ea_list *ealist; 79862306a36Sopenharmony_ci struct jfs_ea *ea; 79962306a36Sopenharmony_ci struct ea_buffer ea_buf; 80062306a36Sopenharmony_ci int xattr_size; 80162306a36Sopenharmony_ci ssize_t size; 80262306a36Sopenharmony_ci int namelen = strlen(name); 80362306a36Sopenharmony_ci char *value; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci down_read(&JFS_IP(inode)->xattr_sem); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci xattr_size = ea_get(inode, &ea_buf, 0); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (xattr_size < 0) { 81062306a36Sopenharmony_ci size = xattr_size; 81162306a36Sopenharmony_ci goto out; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (xattr_size == 0) 81562306a36Sopenharmony_ci goto not_found; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ealist = (struct jfs_ea_list *) ea_buf.xattr; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* Find the named attribute */ 82062306a36Sopenharmony_ci for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) 82162306a36Sopenharmony_ci if ((namelen == ea->namelen) && 82262306a36Sopenharmony_ci memcmp(name, ea->name, namelen) == 0) { 82362306a36Sopenharmony_ci /* Found it */ 82462306a36Sopenharmony_ci size = le16_to_cpu(ea->valuelen); 82562306a36Sopenharmony_ci if (!data) 82662306a36Sopenharmony_ci goto release; 82762306a36Sopenharmony_ci else if (size > buf_size) { 82862306a36Sopenharmony_ci size = -ERANGE; 82962306a36Sopenharmony_ci goto release; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci value = ((char *) &ea->name) + ea->namelen + 1; 83262306a36Sopenharmony_ci memcpy(data, value, size); 83362306a36Sopenharmony_ci goto release; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci not_found: 83662306a36Sopenharmony_ci size = -ENODATA; 83762306a36Sopenharmony_ci release: 83862306a36Sopenharmony_ci ea_release(inode, &ea_buf); 83962306a36Sopenharmony_ci out: 84062306a36Sopenharmony_ci up_read(&JFS_IP(inode)->xattr_sem); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci return size; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/* 84662306a36Sopenharmony_ci * No special permissions are needed to list attributes except for trusted.* 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_cistatic inline int can_list(struct jfs_ea *ea) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci return (strncmp(ea->name, XATTR_TRUSTED_PREFIX, 85162306a36Sopenharmony_ci XATTR_TRUSTED_PREFIX_LEN) || 85262306a36Sopenharmony_ci capable(CAP_SYS_ADMIN)); 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cissize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 85862306a36Sopenharmony_ci char *buffer; 85962306a36Sopenharmony_ci ssize_t size = 0; 86062306a36Sopenharmony_ci int xattr_size; 86162306a36Sopenharmony_ci struct jfs_ea_list *ealist; 86262306a36Sopenharmony_ci struct jfs_ea *ea; 86362306a36Sopenharmony_ci struct ea_buffer ea_buf; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci down_read(&JFS_IP(inode)->xattr_sem); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci xattr_size = ea_get(inode, &ea_buf, 0); 86862306a36Sopenharmony_ci if (xattr_size < 0) { 86962306a36Sopenharmony_ci size = xattr_size; 87062306a36Sopenharmony_ci goto out; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (xattr_size == 0) 87462306a36Sopenharmony_ci goto release; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci ealist = (struct jfs_ea_list *) ea_buf.xattr; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* compute required size of list */ 87962306a36Sopenharmony_ci for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) { 88062306a36Sopenharmony_ci if (can_list(ea)) 88162306a36Sopenharmony_ci size += name_size(ea) + 1; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (!data) 88562306a36Sopenharmony_ci goto release; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (size > buf_size) { 88862306a36Sopenharmony_ci size = -ERANGE; 88962306a36Sopenharmony_ci goto release; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Copy attribute names to buffer */ 89362306a36Sopenharmony_ci buffer = data; 89462306a36Sopenharmony_ci for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) { 89562306a36Sopenharmony_ci if (can_list(ea)) { 89662306a36Sopenharmony_ci int namelen = copy_name(buffer, ea); 89762306a36Sopenharmony_ci buffer += namelen + 1; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci release: 90262306a36Sopenharmony_ci ea_release(inode, &ea_buf); 90362306a36Sopenharmony_ci out: 90462306a36Sopenharmony_ci up_read(&JFS_IP(inode)->xattr_sem); 90562306a36Sopenharmony_ci return size; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int __jfs_xattr_set(struct inode *inode, const char *name, 90962306a36Sopenharmony_ci const void *value, size_t size, int flags) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct jfs_inode_info *ji = JFS_IP(inode); 91262306a36Sopenharmony_ci tid_t tid; 91362306a36Sopenharmony_ci int rc; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci tid = txBegin(inode->i_sb, 0); 91662306a36Sopenharmony_ci mutex_lock(&ji->commit_mutex); 91762306a36Sopenharmony_ci rc = __jfs_setxattr(tid, inode, name, value, size, flags); 91862306a36Sopenharmony_ci if (!rc) 91962306a36Sopenharmony_ci rc = txCommit(tid, 1, &inode, 0); 92062306a36Sopenharmony_ci txEnd(tid); 92162306a36Sopenharmony_ci mutex_unlock(&ji->commit_mutex); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci return rc; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic int jfs_xattr_get(const struct xattr_handler *handler, 92762306a36Sopenharmony_ci struct dentry *unused, struct inode *inode, 92862306a36Sopenharmony_ci const char *name, void *value, size_t size) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci name = xattr_full_name(handler, name); 93162306a36Sopenharmony_ci return __jfs_getxattr(inode, name, value, size); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int jfs_xattr_set(const struct xattr_handler *handler, 93562306a36Sopenharmony_ci struct mnt_idmap *idmap, 93662306a36Sopenharmony_ci struct dentry *unused, struct inode *inode, 93762306a36Sopenharmony_ci const char *name, const void *value, 93862306a36Sopenharmony_ci size_t size, int flags) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci name = xattr_full_name(handler, name); 94162306a36Sopenharmony_ci return __jfs_xattr_set(inode, name, value, size, flags); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic int jfs_xattr_get_os2(const struct xattr_handler *handler, 94562306a36Sopenharmony_ci struct dentry *unused, struct inode *inode, 94662306a36Sopenharmony_ci const char *name, void *value, size_t size) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci if (is_known_namespace(name)) 94962306a36Sopenharmony_ci return -EOPNOTSUPP; 95062306a36Sopenharmony_ci return __jfs_getxattr(inode, name, value, size); 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic int jfs_xattr_set_os2(const struct xattr_handler *handler, 95462306a36Sopenharmony_ci struct mnt_idmap *idmap, 95562306a36Sopenharmony_ci struct dentry *unused, struct inode *inode, 95662306a36Sopenharmony_ci const char *name, const void *value, 95762306a36Sopenharmony_ci size_t size, int flags) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci if (is_known_namespace(name)) 96062306a36Sopenharmony_ci return -EOPNOTSUPP; 96162306a36Sopenharmony_ci return __jfs_xattr_set(inode, name, value, size, flags); 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic const struct xattr_handler jfs_user_xattr_handler = { 96562306a36Sopenharmony_ci .prefix = XATTR_USER_PREFIX, 96662306a36Sopenharmony_ci .get = jfs_xattr_get, 96762306a36Sopenharmony_ci .set = jfs_xattr_set, 96862306a36Sopenharmony_ci}; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic const struct xattr_handler jfs_os2_xattr_handler = { 97162306a36Sopenharmony_ci .prefix = XATTR_OS2_PREFIX, 97262306a36Sopenharmony_ci .get = jfs_xattr_get_os2, 97362306a36Sopenharmony_ci .set = jfs_xattr_set_os2, 97462306a36Sopenharmony_ci}; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic const struct xattr_handler jfs_security_xattr_handler = { 97762306a36Sopenharmony_ci .prefix = XATTR_SECURITY_PREFIX, 97862306a36Sopenharmony_ci .get = jfs_xattr_get, 97962306a36Sopenharmony_ci .set = jfs_xattr_set, 98062306a36Sopenharmony_ci}; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic const struct xattr_handler jfs_trusted_xattr_handler = { 98362306a36Sopenharmony_ci .prefix = XATTR_TRUSTED_PREFIX, 98462306a36Sopenharmony_ci .get = jfs_xattr_get, 98562306a36Sopenharmony_ci .set = jfs_xattr_set, 98662306a36Sopenharmony_ci}; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ciconst struct xattr_handler *jfs_xattr_handlers[] = { 98962306a36Sopenharmony_ci &jfs_os2_xattr_handler, 99062306a36Sopenharmony_ci &jfs_user_xattr_handler, 99162306a36Sopenharmony_ci &jfs_security_xattr_handler, 99262306a36Sopenharmony_ci &jfs_trusted_xattr_handler, 99362306a36Sopenharmony_ci NULL, 99462306a36Sopenharmony_ci}; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci#ifdef CONFIG_JFS_SECURITY 99862306a36Sopenharmony_cistatic int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, 99962306a36Sopenharmony_ci void *fs_info) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci const struct xattr *xattr; 100262306a36Sopenharmony_ci tid_t *tid = fs_info; 100362306a36Sopenharmony_ci char *name; 100462306a36Sopenharmony_ci int err = 0; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci for (xattr = xattr_array; xattr->name != NULL; xattr++) { 100762306a36Sopenharmony_ci name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 100862306a36Sopenharmony_ci strlen(xattr->name) + 1, GFP_NOFS); 100962306a36Sopenharmony_ci if (!name) { 101062306a36Sopenharmony_ci err = -ENOMEM; 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci strcpy(name, XATTR_SECURITY_PREFIX); 101462306a36Sopenharmony_ci strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci err = __jfs_setxattr(*tid, inode, name, 101762306a36Sopenharmony_ci xattr->value, xattr->value_len, 0); 101862306a36Sopenharmony_ci kfree(name); 101962306a36Sopenharmony_ci if (err < 0) 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci return err; 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ciint jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir, 102662306a36Sopenharmony_ci const struct qstr *qstr) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci return security_inode_init_security(inode, dir, qstr, 102962306a36Sopenharmony_ci &jfs_initxattrs, &tid); 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci#endif 1032