-------------------------------------------------------------------- This patch adds extended attribute support for JFS 1.1.2. This patch was built against linux-2.4.20 + ea+acl+nfsacl-2.4.20-0.8.57 + JFS 1.1.2 (remove fs/jfs/xattr.c & fs/jfs/jfs_xattr.h before applying patch) ea+acl+nfsacl-2.4.20-08.57 is available from http://acl.bestbits.at/ -------------------------------------------------------------------- diff -Nur linux-2.4.20/fs/jfs/Makefile linux-ea/fs/jfs/Makefile --- linux-2.4.20/fs/jfs/Makefile 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/Makefile 2003-05-08 14:13:00.000000000 -0500 @@ -12,7 +12,7 @@ jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \ jfs_unicode.o jfs_dtree.o jfs_inode.o \ jfs_extent.o symlink.o jfs_metapage.o \ - jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o resize.o + jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o resize.o xattr.o obj-m := $(O_TARGET) EXTRA_CFLAGS += -D_JFS_4K diff -Nur linux-2.4.20/fs/jfs/file.c linux-ea/fs/jfs/file.c --- linux-2.4.20/fs/jfs/file.c 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/file.c 2003-05-08 14:13:00.000000000 -0500 @@ -22,6 +22,7 @@ #include "jfs_incore.h" #include "jfs_dmap.h" #include "jfs_txnmgr.h" +#include "jfs_xattr.h" #include "jfs_debug.h" @@ -91,6 +92,10 @@ struct inode_operations jfs_file_inode_operations = { .truncate = jfs_truncate, + .setxattr = jfs_setxattr, + .getxattr = jfs_getxattr, + .listxattr = jfs_listxattr, + .removexattr = jfs_removexattr, }; struct file_operations jfs_file_operations = { @@ -102,3 +107,11 @@ .fsync = jfs_fsync, .release = jfs_release, }; + +struct inode_operations jfs_special_inode_operations = { + .setxattr = jfs_setxattr, + .getxattr = jfs_getxattr, + .listxattr = jfs_listxattr, + .removexattr = jfs_removexattr, +}; + diff -Nur linux-2.4.20/fs/jfs/inode.c linux-ea/fs/jfs/inode.c --- linux-2.4.20/fs/jfs/inode.c 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/inode.c 2003-05-08 14:13:00.000000000 -0500 @@ -31,6 +31,7 @@ extern struct inode_operations jfs_dir_inode_operations; extern struct inode_operations jfs_file_inode_operations; extern struct inode_operations jfs_symlink_inode_operations; +extern struct inode_operations jfs_special_inode_operations; extern struct file_operations jfs_dir_operations; extern struct file_operations jfs_file_operations; struct address_space_operations jfs_aops; @@ -96,6 +97,7 @@ } else inode->i_op = &jfs_symlink_inode_operations; } else { + inode->i_op = &jfs_special_inode_operations; init_special_inode(inode, inode->i_mode, kdev_t_to_nr(inode->i_rdev)); } diff -Nur linux-2.4.20/fs/jfs/jfs_imap.c linux-ea/fs/jfs/jfs_imap.c --- linux-2.4.20/fs/jfs/jfs_imap.c 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/jfs_imap.c 2003-05-08 14:13:00.000000000 -0500 @@ -806,21 +806,19 @@ memcpy(&dp->di_fastsymlink, jfs_ip->i_inline, IDATASIZE); dilinelock->index++; } -#ifdef _STILL_TO_PORT /* * copy inline data from in-memory inode to on-disk inode: * 128 byte slot granularity */ - if (test_cflag(COMMIT_Inlineea, ip)) + if (test_cflag(COMMIT_Inlineea, ip)) { lv = & dilinelock->lv[dilinelock->index]; lv->offset = (dioffset + 3 * 128) >> L2INODESLOTSIZE; lv->length = 1; - memcpy(&dp->di_inlineea, &ip->i_inlineea, INODESLOTSIZE); + memcpy(&dp->di_inlineea, &jfs_ip->i_inline_ea, INODESLOTSIZE); dilinelock->index++; clear_cflag(COMMIT_Inlineea, ip); } -#endif /* _STILL_TO_PORT */ /* * lock/copy inode base: 128 byte slot granularity @@ -3046,16 +3044,17 @@ jfs_ip->next_index = le32_to_cpu(dip->di_next_index); jfs_ip->otime = le32_to_cpu(dip->di_otime.tv_sec); jfs_ip->acltype = le32_to_cpu(dip->di_acltype); - /* - * We may only need to do this for "special" inodes (dmap, imap) - */ + if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode)) ip->i_rdev = to_kdev_t(le32_to_cpu(dip->di_rdev)); - else if (S_ISDIR(ip->i_mode)) { + + if (S_ISDIR(ip->i_mode)) { memcpy(&jfs_ip->i_dirtable, &dip->di_dirtable, 384); - } else if (!S_ISFIFO(ip->i_mode)) { + } else if (S_ISREG(ip->i_mode) || S_ISLNK(ip->i_mode)) { memcpy(&jfs_ip->i_xtroot, &dip->di_xtroot, 288); - } + } else + memcpy(&jfs_ip->i_inline_ea, &dip->di_inlineea, 128); + /* Zero the in-memory-only stuff */ jfs_ip->cflag = 0; jfs_ip->btindex = 0; diff -Nur linux-2.4.20/fs/jfs/jfs_incore.h linux-ea/fs/jfs/jfs_incore.h --- linux-2.4.20/fs/jfs/jfs_incore.h 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/jfs_incore.h 2003-05-08 14:13:01.000000000 -0500 @@ -82,6 +82,10 @@ unchar _unused[16]; /* 16: */ dxd_t _dxd; /* 16: */ unchar _inline[128]; /* 128: inline symlink */ + /* _inline_ea may overlay the last part of + * file._xtroot if maxentry = XTROOTINITSLOT + */ + unchar _inline_ea[128]; /* 128: inline extended attr */ } link; } u; }; @@ -90,6 +94,7 @@ #define i_dirtable u.dir._table #define i_dtroot u.dir._dtroot #define i_inline u.link._inline +#define i_inline_ea u.link._inline_ea #define IREAD_LOCK(ip) down_read(&JFS_IP(ip)->rdwrlock) diff -Nur linux-2.4.20/fs/jfs/jfs_metapage.h linux-ea/fs/jfs/jfs_metapage.h --- linux-2.4.20/fs/jfs/jfs_metapage.h 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/jfs_metapage.h 2003-05-08 14:13:01.000000000 -0500 @@ -106,7 +106,7 @@ } /* - * This routines invalidate all pages for an extent. + * These routines invalidate all pages for an extent. */ extern void __invalidate_metapages(struct inode *, s64, int); #define invalidate_pxd_metapages(ip, pxd) \ diff -Nur linux-2.4.20/fs/jfs/jfs_types.h linux-ea/fs/jfs/jfs_types.h --- linux-2.4.20/fs/jfs/jfs_types.h 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/jfs_types.h 2003-05-08 14:13:01.000000000 -0500 @@ -144,6 +144,8 @@ #define DXDaddress PXDaddress #define lengthDXD lengthPXD #define addressDXD addressPXD +#define DXDsize(dxd, size32) ((dxd)->size = cpu_to_le32(size32)) +#define sizeDXD(dxd) le32_to_cpu((dxd)->size) /* * directory entry argument diff -Nur linux-2.4.20/fs/jfs/jfs_xattr.h linux-ea/fs/jfs/jfs_xattr.h --- linux-2.4.20/fs/jfs/jfs_xattr.h 1969-12-31 18:00:00.000000000 -0600 +++ linux-ea/fs/jfs/jfs_xattr.h 2003-05-08 14:28:35.000000000 -0500 @@ -0,0 +1,64 @@ +/* + * Copyright (c) International Business Machines Corp., 2000-2002 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef H_JFS_XATTR +#define H_JFS_XATTR + +/* + * jfs_ea_list describe the on-disk format of the extended attributes. + * I know the null-terminator is redundant since namelen is stored, but + * I am maintaining compatibility with OS/2 where possible. + */ +struct jfs_ea { + u8 flag; /* Unused? */ + u8 namelen; /* Length of name */ + u16 valuelen; /* Length of value */ + char name[0]; /* Attribute name (includes null-terminator) */ +}; /* Value immediately follows name */ + +struct jfs_ea_list { + u32 size; /* overall size */ + struct jfs_ea ea[0]; /* Variable length list */ +}; + +/* Macros for defining maxiumum number of bytes supported for EAs */ +#define MAXEASIZE 65535 +#define MAXEALISTSIZE MAXEASIZE + +/* + * some macros for dealing with variable length EA lists. + */ +#define EA_SIZE(ea) \ + (sizeof (struct jfs_ea) + (ea)->namelen + 1 + \ + le16_to_cpu((ea)->valuelen)) +#define NEXT_EA(ea) ((struct jfs_ea *) (((char *) (ea)) + (EA_SIZE (ea)))) +#define FIRST_EA(ealist) ((ealist)->ea) +#define EALIST_SIZE(ealist) le32_to_cpu((ealist)->size) +#define END_EALIST(ealist) \ + ((struct jfs_ea *) (((char *) (ealist)) + EALIST_SIZE(ealist))) + +extern int __jfs_setxattr(struct inode *, const char *, const void *, size_t, + int); +extern int jfs_setxattr(struct dentry *, const char *, const void *, size_t, + int); +extern ssize_t __jfs_getxattr(struct inode *, const char *, void *, size_t); +extern ssize_t jfs_getxattr(struct dentry *, const char *, void *, size_t); +extern ssize_t jfs_listxattr(struct dentry *, char *, size_t); +extern int jfs_removexattr(struct dentry *, const char *); + +#endif /* H_JFS_XATTR */ diff -Nur linux-2.4.20/fs/jfs/namei.c linux-ea/fs/jfs/namei.c --- linux-2.4.20/fs/jfs/namei.c 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/namei.c 2003-05-08 14:13:01.000000000 -0500 @@ -24,10 +24,12 @@ #include "jfs_dmap.h" #include "jfs_unicode.h" #include "jfs_metapage.h" +#include "jfs_xattr.h" #include "jfs_debug.h" extern struct inode_operations jfs_file_inode_operations; extern struct inode_operations jfs_symlink_inode_operations; +extern struct inode_operations jfs_special_inode_operations; extern struct file_operations jfs_file_operations; extern struct address_space_operations jfs_aops; @@ -1316,6 +1318,7 @@ if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack))) goto out3; + ip->i_op = &jfs_special_inode_operations; init_special_inode(ip, ip->i_mode, rdev); insert_inode_hash(ip); @@ -1400,6 +1403,10 @@ .rmdir = jfs_rmdir, .mknod = jfs_mknod, .rename = jfs_rename, + .setxattr = jfs_setxattr, + .getxattr = jfs_getxattr, + .listxattr = jfs_listxattr, + .removexattr = jfs_removexattr, }; struct file_operations jfs_dir_operations = { diff -Nur linux-2.4.20/fs/jfs/symlink.c linux-ea/fs/jfs/symlink.c --- linux-2.4.20/fs/jfs/symlink.c 2003-05-08 14:10:45.000000000 -0500 +++ linux-ea/fs/jfs/symlink.c 2003-05-08 14:13:01.000000000 -0500 @@ -18,6 +18,7 @@ #include #include "jfs_incore.h" +#include "jfs_xattr.h" static int jfs_follow_link(struct dentry *dentry, struct nameidata *nd) { @@ -34,5 +35,9 @@ struct inode_operations jfs_symlink_inode_operations = { .readlink = jfs_readlink, .follow_link = jfs_follow_link, + .setxattr = jfs_setxattr, + .getxattr = jfs_getxattr, + .listxattr = jfs_listxattr, + .removexattr = jfs_removexattr, }; diff -Nur linux-2.4.20/fs/jfs/xattr.c linux-ea/fs/jfs/xattr.c --- linux-2.4.20/fs/jfs/xattr.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-ea/fs/jfs/xattr.c 2003-05-08 14:30:04.000000000 -0500 @@ -0,0 +1,938 @@ +/* + * Copyright (c) International Business Machines Corp., 2000-2002 + * Copyright (c) Christoph Hellwig, 2002 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "jfs_incore.h" +#include "jfs_dmap.h" +#include "jfs_debug.h" +#include "jfs_dinode.h" +#include "jfs_extent.h" +#include "jfs_metapage.h" +#include "jfs_xattr.h" + +/* + * jfs_xattr.c: extended attribute service + * + * Overall design -- + * + * Format: + * + * Extended attribute lists (jfs_ea_list) consist of an overall size (32 bit + * value) and a variable (0 or more) number of extended attribute + * entries. Each extended attribute entry (jfs_ea) is a double + * where is constructed from a null-terminated ascii string + * (1 ... 255 bytes in the name) and is arbitrary 8 bit data + * (1 ... 65535 bytes). The in-memory format is + * + * 0 1 2 4 4 + namelen + 1 + * +-------+--------+--------+----------------+-------------------+ + * | Flags | Name | Value | Name String \0 | Data . . . . | + * | | Length | Length | | | + * +-------+--------+--------+----------------+-------------------+ + * + * A jfs_ea_list then is structured as + * + * 0 4 4 + EA_SIZE(ea1) + * +------------+-------------------+--------------------+----- + * | Overall EA | First FEA Element | Second FEA Element | ..... + * | List Size | | | + * +------------+-------------------+--------------------+----- + * + * On-disk: + * + * FEALISTs are stored on disk using blocks allocated by dbAlloc() and + * written directly. An EA list may be in-lined in the inode if there is + * sufficient room available. + */ + +struct ea_buffer { + int flag; /* Indicates what storage xattr points to */ + int max_size; /* largest xattr that fits in current buffer */ + dxd_t new_ea; /* dxd to replace ea when modifying xattr */ + struct metapage *mp; /* metapage containing ea list */ + struct jfs_ea_list *xattr; /* buffer containing ea list */ +}; + +/* + * ea_buffer.flag values + */ +#define EA_INLINE 0x0001 +#define EA_EXTENT 0x0002 +#define EA_NEW 0x0004 +#define EA_MALLOC 0x0008 + +/* Namespaces */ +#define XATTR_SYSTEM_PREFIX "system." +#define XATTR_SYSTEM_PREFIX_LEN (sizeof (XATTR_SYSTEM_PREFIX) - 1) + +#define XATTR_USER_PREFIX "user." +#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) + +#define XATTR_OS2_PREFIX "os2." +#define XATTR_OS2_PREFIX_LEN (sizeof (XATTR_OS2_PREFIX) - 1) + +/* + * These three routines are used to recognize on-disk extended attributes + * that are in a recognized namespace. If the attribute is not recognized, + * "os2." is prepended to the name + */ +static inline int is_os2_xattr(struct jfs_ea *ea) +{ + /* + * Check for "system." + */ + if ((ea->namelen >= XATTR_SYSTEM_PREFIX_LEN) && + !strncmp(ea->name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) + return FALSE; + /* + * Check for "user." + */ + if ((ea->namelen >= XATTR_USER_PREFIX_LEN) && + !strncmp(ea->name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + return FALSE; + /* + * Add any other valid namespace prefixes here + */ + + /* + * We assume it's OS/2's flat namespace + */ + return TRUE; +} + +static inline int name_size(struct jfs_ea *ea) +{ + if (is_os2_xattr(ea)) + return ea->namelen + XATTR_OS2_PREFIX_LEN; + else + return ea->namelen; +} + +static inline int copy_name(char *buffer, struct jfs_ea *ea) +{ + int len = ea->namelen; + + if (is_os2_xattr(ea)) { + memcpy(buffer, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN); + buffer += XATTR_OS2_PREFIX_LEN; + len += XATTR_OS2_PREFIX_LEN; + } + memcpy(buffer, ea->name, ea->namelen); + buffer[ea->namelen] = 0; + + return len; +} + +/* Forward references */ +static void ea_release(struct inode *inode, struct ea_buffer *ea_buf); + +/* + * NAME: ea_write_inline + * + * FUNCTION: Attempt to write an EA inline if area is available + * + * PRE CONDITIONS: + * Already verified that the specified EA is small enough to fit inline + * + * PARAMETERS: + * ip - Inode pointer + * ealist - EA list pointer + * size - size of ealist in bytes + * ea - dxd_t structure to be filled in with necessary EA information + * if we successfully copy the EA inline + * + * NOTES: + * Checks if the inode's inline area is available. If so, copies EA inline + * and sets fields appropriately. Otherwise, returns failure, EA will + * have to be put into an extent. + * + * RETURNS: 0 for successful copy to inline area; -1 if area not available + */ +static int ea_write_inline(struct inode *ip, struct jfs_ea_list *ealist, + int size, dxd_t * ea) +{ + struct jfs_inode_info *ji = JFS_IP(ip); + + /* + * Make sure we have an EA -- the NULL EA list is valid, but you + * can't copy it! + */ + if (ealist && size > sizeof (struct jfs_ea_list)) { + assert(size <= sizeof (ji->i_inline_ea)); + + /* + * See if the space is available or if it is already being + * used for an inline EA. + */ + if (!(ji->mode2 & INLINEEA) && !(ji->ea.flag & DXD_INLINE)) + return -1; + + DXDsize(ea, size); + DXDlength(ea, 0); + DXDaddress(ea, 0); + memcpy(ji->i_inline_ea, ealist, size); + ea->flag = DXD_INLINE; + ji->mode2 &= ~INLINEEA; + } else { + ea->flag = 0; + DXDsize(ea, 0); + DXDlength(ea, 0); + DXDaddress(ea, 0); + + /* Free up INLINE area */ + if (ji->ea.flag & DXD_INLINE) + ji->mode2 |= INLINEEA; + } + + mark_inode_dirty(ip); + return 0; +} + +/* + * NAME: ea_write + * + * FUNCTION: Write an EA for an inode + * + * PRE CONDITIONS: EA has been verified + * + * PARAMETERS: + * ip - Inode pointer + * ealist - EA list pointer + * size - size of ealist in bytes + * ea - dxd_t structure to be filled in appropriately with where the + * EA was copied + * + * NOTES: Will write EA inline if able to, otherwise allocates blocks for an + * extent and synchronously writes it to those blocks. + * + * RETURNS: 0 for success; Anything else indicates failure + */ +static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size, + dxd_t * ea) +{ + struct super_block *sb = ip->i_sb; + struct jfs_inode_info *ji = JFS_IP(ip); + struct jfs_sb_info *sbi = JFS_SBI(sb); + int nblocks; + s64 blkno; + int rc = 0, i; + char *cp; + s32 nbytes, nb; + s32 bytes_to_write; + struct metapage *mp; + + /* + * Quick check to see if this is an in-linable EA. Short EAs + * and empty EAs are all in-linable, provided the space exists. + */ + if (!ealist || size <= sizeof (ji->i_inline_ea)) { + if (!ea_write_inline(ip, ealist, size, ea)) + return 0; + } + + /* figure out how many blocks we need */ + nblocks = (size + (sb->s_blocksize - 1)) >> sb->s_blocksize_bits; + + rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno); + if (rc) + return -rc; + + /* + * Now have nblocks worth of storage to stuff into the FEALIST. + * loop over the FEALIST copying data into the buffer one page at + * a time. + */ + cp = (char *) ealist; + nbytes = size; + for (i = 0; i < nblocks; i += sbi->nbperpage) { + /* + * Determine how many bytes for this request, and round up to + * the nearest aggregate block size + */ + nb = min(PSIZE, nbytes); + bytes_to_write = + ((((nb + sb->s_blocksize - 1)) >> sb->s_blocksize_bits)) + << sb->s_blocksize_bits; + + if (!(mp = get_metapage(ip, blkno + i, bytes_to_write, 1))) { + rc = -EIO; + goto failed; + } + + memcpy(mp->data, cp, nb); + + /* + * We really need a way to propagate errors for + * forced writes like this one. --hch + * + * (__write_metapage => release_metapage => flush_metapage) + */ +#ifdef _JFS_FIXME + if ((rc = flush_metapage(mp))) { + /* + * the write failed -- this means that the buffer + * is still assigned and the blocks are not being + * used. this seems like the best error recovery + * we can get ... + */ + goto failed; + } +#else + flush_metapage(mp); +#endif + + cp += PSIZE; + nbytes -= nb; + } + + ea->flag = DXD_EXTENT; + DXDsize(ea, le32_to_cpu(ealist->size)); + DXDlength(ea, nblocks); + DXDaddress(ea, blkno); + + /* Free up INLINE area */ + if (ji->ea.flag & DXD_INLINE) + ji->mode2 |= INLINEEA; + + return 0; + + failed: + dbFree(ip, blkno, nblocks); + return rc; +} + +/* + * NAME: ea_read_inline + * + * FUNCTION: Read an inlined EA into user's buffer + * + * PARAMETERS: + * ip - Inode pointer + * ealist - Pointer to buffer to fill in with EA + * + * RETURNS: 0 + */ +static int ea_read_inline(struct inode *ip, struct jfs_ea_list *ealist) +{ + struct jfs_inode_info *ji = JFS_IP(ip); + int ea_size = sizeDXD(&ji->ea); + + if (ea_size == 0) { + ealist->size = 0; + return 0; + } + + /* Sanity Check */ + if ((sizeDXD(&ji->ea) > sizeof (ji->i_inline_ea))) + return -EIO; + if (le32_to_cpu(((struct jfs_ea_list *) &ji->i_inline_ea)->size) + != ea_size) + return -EIO; + + memcpy(ealist, ji->i_inline_ea, ea_size); + return 0; +} + +/* + * NAME: ea_read + * + * FUNCTION: copy EA data into user's buffer + * + * PARAMETERS: + * ip - Inode pointer + * ealist - Pointer to buffer to fill in with EA + * + * NOTES: If EA is inline calls ea_read_inline() to copy EA. + * + * RETURNS: 0 for success; other indicates failure + */ +static int ea_read(struct inode *ip, struct jfs_ea_list *ealist) +{ + struct super_block *sb = ip->i_sb; + struct jfs_inode_info *ji = JFS_IP(ip); + struct jfs_sb_info *sbi = JFS_SBI(sb); + int nblocks; + s64 blkno; + char *cp = (char *) ealist; + int i; + int nbytes, nb; + s32 bytes_to_read; + struct metapage *mp; + + /* quick check for in-line EA */ + if (ji->ea.flag & DXD_INLINE) + return ea_read_inline(ip, ealist); + + nbytes = sizeDXD(&ji->ea); + assert(nbytes); + + /* + * Figure out how many blocks were allocated when this EA list was + * originally written to disk. + */ + nblocks = lengthDXD(&ji->ea) << sbi->l2nbperpage; + blkno = addressDXD(&ji->ea) << sbi->l2nbperpage; + + /* + * I have found the disk blocks which were originally used to store + * the FEALIST. now i loop over each contiguous block copying the + * data into the buffer. + */ + for (i = 0; i < nblocks; i += sbi->nbperpage) { + /* + * Determine how many bytes for this request, and round up to + * the nearest aggregate block size + */ + nb = min(PSIZE, nbytes); + bytes_to_read = + ((((nb + sb->s_blocksize - 1)) >> sb->s_blocksize_bits)) + << sb->s_blocksize_bits; + + if (!(mp = read_metapage(ip, blkno + i, bytes_to_read, 1))) + return -EIO; + + memcpy(cp, mp->data, nb); + release_metapage(mp); + + cp += PSIZE; + nbytes -= nb; + } + + return 0; +} + +/* + * NAME: ea_get + * + * FUNCTION: Returns buffer containing existing extended attributes. + * The size of the buffer will be the larger of the existing + * attributes size, or min_size. + * + * The buffer, which may be inlined in the inode or in the + * page cache must be release by calling ea_release or ea_put + * + * PARAMETERS: + * inode - Inode pointer + * ea_buf - Structure to be populated with ealist and its metadata + * min_size- minimum size of buffer to be returned + * + * RETURNS: 0 for success; Other indicates failure + */ +static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) +{ + struct jfs_inode_info *ji = JFS_IP(inode); + struct super_block *sb = inode->i_sb; + int size; + int ea_size = sizeDXD(&ji->ea); + int blocks_needed, current_blocks; + s64 blkno; + int rc; + + /* When fsck.jfs clears a bad ea, it doesn't clear the size */ + if (ji->ea.flag == 0) + ea_size = 0; + + if (ea_size == 0) { + if (min_size == 0) { + ea_buf->flag = 0; + ea_buf->max_size = 0; + ea_buf->xattr = NULL; + return 0; + } + if ((min_size <= sizeof (ji->i_inline_ea)) && + (ji->mode2 & INLINEEA)) { + ea_buf->flag = EA_INLINE | EA_NEW; + ea_buf->max_size = sizeof (ji->i_inline_ea); + ea_buf->xattr = (struct jfs_ea_list *) ji->i_inline_ea; + DXDlength(&ea_buf->new_ea, 0); + DXDaddress(&ea_buf->new_ea, 0); + ea_buf->new_ea.flag = DXD_INLINE; + DXDsize(&ea_buf->new_ea, min_size); + return 0; + } + current_blocks = 0; + } else if (ji->ea.flag & DXD_INLINE) { + if (min_size <= sizeof (ji->i_inline_ea)) { + ea_buf->flag = EA_INLINE; + ea_buf->max_size = sizeof (ji->i_inline_ea); + ea_buf->xattr = (struct jfs_ea_list *) ji->i_inline_ea; + goto size_check; + } + current_blocks = 0; + } else { + assert(ji->ea.flag & DXD_EXTENT); + current_blocks = (ea_size + sb->s_blocksize - 1) >> + sb->s_blocksize_bits; + } + size = max(min_size, ea_size); + + if (size > PSIZE) { + /* + * To keep the rest of the code simple. Allocate a + * contiguous buffer to work with + */ + ea_buf->xattr = kmalloc(size, GFP_KERNEL); + if (ea_buf->xattr == NULL) + return -ENOMEM; + + ea_buf->flag |= EA_MALLOC; + ea_buf->max_size = (size + sb->s_blocksize - 1) & + ~(sb->s_blocksize - 1); + + if (ea_size == 0) + return 0; + + if ((rc = ea_read(inode, ea_buf->xattr))) { + kfree(ea_buf->xattr); + ea_buf->xattr = NULL; + return rc; + } + goto size_check; + } + blocks_needed = (min_size + sb->s_blocksize - 1) >> + sb->s_blocksize_bits; + + if (blocks_needed > current_blocks) { + rc = dbAlloc(inode, INOHINT(inode), (s64) blocks_needed, + &blkno); + if (rc) + return -rc; + + DXDlength(&ea_buf->new_ea, blocks_needed); + DXDaddress(&ea_buf->new_ea, blkno); + ea_buf->new_ea.flag = DXD_EXTENT; + DXDsize(&ea_buf->new_ea, min_size); + + ea_buf->flag = EA_EXTENT | EA_NEW; + + ea_buf->mp = get_metapage(inode, blkno, + blocks_needed << sb->s_blocksize_bits, + 1); + if (ea_buf->mp == NULL) { + dbFree(inode, blkno, (s64) blocks_needed); + return -EIO; + } + ea_buf->xattr = ea_buf->mp->data; + ea_buf->max_size = (min_size + sb->s_blocksize - 1) & + ~(sb->s_blocksize - 1); + if (ea_size == 0) + return 0; + if ((rc = ea_read(inode, ea_buf->xattr))) { + discard_metapage(ea_buf->mp); + dbFree(inode, blkno, (s64) blocks_needed); + return rc; + } + goto size_check; + } + ea_buf->flag = EA_EXTENT; + ea_buf->mp = read_metapage(inode, addressDXD(&ji->ea), + lengthDXD(&ji->ea), 1); + if (ea_buf->mp == NULL) + return -EIO; + ea_buf->xattr = ea_buf->mp->data; + ea_buf->max_size = (ea_size + sb->s_blocksize - 1) & + ~(sb->s_blocksize - 1); + + size_check: + if (EALIST_SIZE(ea_buf->xattr) != ea_size) { + printk(KERN_ERR "ea_get: invalid extended attribute\n"); + dump_mem("xattr", ea_buf->xattr, ea_size); + ea_release(inode, ea_buf); + return -EIO; + } + + return ea_size; +} + +static void ea_release(struct inode *inode, struct ea_buffer *ea_buf) +{ + if (ea_buf->flag & EA_MALLOC) + kfree(ea_buf->xattr); + else if (ea_buf->flag & EA_EXTENT) { + assert(ea_buf->mp); + release_metapage(ea_buf->mp); + + if (ea_buf->flag & EA_NEW) + dbFree(inode, addressDXD(&ea_buf->new_ea), + lengthDXD(&ea_buf->new_ea)); + } +} + +static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size) +{ + struct jfs_inode_info *ji = JFS_IP(inode); + unsigned long old_blocks, new_blocks; + int rc = 0; + tid_t tid; + + if (new_size == 0) { + ea_release(inode, ea_buf); + ea_buf = 0; + } else if (ea_buf->flag & EA_INLINE) { + assert(new_size <= sizeof (ji->i_inline_ea)); + ji->mode2 &= ~INLINEEA; + ea_buf->new_ea.flag = DXD_INLINE; + DXDsize(&ea_buf->new_ea, new_size); + DXDaddress(&ea_buf->new_ea, 0); + DXDlength(&ea_buf->new_ea, 0); + } else if (ea_buf->flag & EA_MALLOC) { + rc = ea_write(inode, ea_buf->xattr, new_size, &ea_buf->new_ea); + kfree(ea_buf->xattr); + } else if (ea_buf->flag & EA_NEW) { + /* We have already allocated a new dxd */ + flush_metapage(ea_buf->mp); + } else { + /* ->xattr must point to original ea's metapage */ + rc = ea_write(inode, ea_buf->xattr, new_size, &ea_buf->new_ea); + discard_metapage(ea_buf->mp); + } + if (rc) + return rc; + + tid = txBegin(inode->i_sb, 0); + down(&ji->commit_sem); + + old_blocks = new_blocks = 0; + + if (ji->ea.flag & DXD_EXTENT) { + invalidate_dxd_metapages(inode, ji->ea); + old_blocks = lengthDXD(&ji->ea); + } + + if (ea_buf) { + txEA(tid, inode, &ji->ea, &ea_buf->new_ea); + if (ea_buf->new_ea.flag & DXD_EXTENT) { + new_blocks = lengthDXD(&ea_buf->new_ea); + if (ji->ea.flag & DXD_INLINE) + ji->mode2 |= INLINEEA; + } + ji->ea = ea_buf->new_ea; + } else { + txEA(tid, inode, &ji->ea, 0); + if (ji->ea.flag & DXD_INLINE) + ji->mode2 |= INLINEEA; + ji->ea.flag = 0; + ji->ea.size = 0; + } + + inode->i_blocks += LBLK2PBLK(inode->i_sb, new_blocks - old_blocks); + rc = txCommit(tid, 1, &inode, 0); + txEnd(tid); + up(&ji->commit_sem); + + return rc; +} + +static int can_set_xattr(struct inode *inode, const char *name, + const void *value, size_t value_len) +{ + if (IS_RDONLY(inode)) + return -EROFS; + + if (IS_IMMUTABLE(inode) || IS_APPEND(inode) || S_ISLNK(inode->i_mode)) + return -EPERM; + + if((strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) != 0) && + (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) != 0)) + return -EOPNOTSUPP; + + if (!S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode &S_ISVTX)) + return -EPERM; + + return permission(inode, MAY_WRITE); +} + +int __jfs_setxattr(struct inode *inode, const char *name, const void *value, + size_t value_len, int flags) +{ + struct jfs_ea_list *ealist; + struct jfs_ea *ea, *old_ea = NULL, *next_ea = NULL; + struct ea_buffer ea_buf; + int old_ea_size = 0; + int xattr_size; + int new_size; + int namelen = strlen(name); + char *os2name = NULL; + int found = 0; + int rc; + int length; + + if ((rc = can_set_xattr(inode, name, value, value_len))) + return rc; + + if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) { + os2name = kmalloc(namelen - XATTR_OS2_PREFIX_LEN + 1, + GFP_KERNEL); + if (!os2name) + return -ENOMEM; + strcpy(os2name, name + XATTR_OS2_PREFIX_LEN); + name = os2name; + namelen -= XATTR_OS2_PREFIX_LEN; + } + + xattr_size = ea_get(inode, &ea_buf, 0); + if (xattr_size < 0) { + rc = xattr_size; + goto out; + } + + again: + ealist = (struct jfs_ea_list *) ea_buf.xattr; + new_size = sizeof (struct jfs_ea_list); + + if (xattr_size) { + for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); + ea = NEXT_EA(ea)) { + if ((namelen == ea->namelen) && + (memcmp(name, ea->name, namelen) == 0)) { + found = 1; + if (flags & XATTR_CREATE) { + rc = -EEXIST; + goto release; + } + old_ea = ea; + old_ea_size = EA_SIZE(ea); + next_ea = NEXT_EA(ea); + } else + new_size += EA_SIZE(ea); + } + } + + if (!found) { + if (flags & XATTR_REPLACE) { + rc = -ENODATA; + goto release; + } + if (value == NULL) { + rc = 0; + goto release; + } + } + if (value) + new_size += sizeof (struct jfs_ea) + namelen + 1 + value_len; + + if (new_size > ea_buf.max_size) { + /* + * We need to allocate more space for merged ea list. + * We should only have loop to again: once. + */ + ea_release(inode, &ea_buf); + xattr_size = ea_get(inode, &ea_buf, new_size); + if (xattr_size < 0) { + rc = xattr_size; + goto out; + } + goto again; + } + + /* Remove old ea of the same name */ + if (found) { + /* number of bytes following target EA */ + length = (char *) END_EALIST(ealist) - (char *) next_ea; + if (length > 0) + memmove(old_ea, next_ea, length); + xattr_size -= old_ea_size; + } + + /* Add new entry to the end */ + if (value) { + if (xattr_size == 0) + /* Completely new ea list */ + xattr_size = sizeof (struct jfs_ea_list); + + ea = (struct jfs_ea *) ((char *) ealist + xattr_size); + ea->flag = 0; + ea->namelen = namelen; + ea->valuelen = (cpu_to_le16(value_len)); + memcpy(ea->name, name, namelen); + ea->name[namelen] = 0; + if (value_len) + memcpy(&ea->name[namelen + 1], value, value_len); + xattr_size += EA_SIZE(ea); + } + + /* DEBUG - If we did this right, these number match */ + if (xattr_size != new_size) { + printk(KERN_ERR + "jfs_xsetattr: xattr_size = %d, new_size = %d\n", + xattr_size, new_size); + + rc = -EINVAL; + goto release; + } + + /* + * If we're left with an empty list, there's no ea + */ + if (new_size == sizeof (struct jfs_ea_list)) + new_size = 0; + + ealist->size = cpu_to_le32(new_size); + + rc = ea_put(inode, &ea_buf, new_size); + + goto out; + release: + ea_release(inode, &ea_buf); + out: + if (os2name) + kfree(os2name); + + return rc; +} + +int jfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t value_len, int flags) +{ + if (value == NULL) { /* empty EA, do not remove */ + value = ""; + value_len = 0; + } + + return __jfs_setxattr(dentry->d_inode, name, value, value_len, flags); +} + +static inline int can_get_xattr(struct inode *inode, const char *name) +{ + return permission(inode, MAY_READ); +} + +ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, + size_t buf_size) +{ + struct jfs_ea_list *ealist; + struct jfs_ea *ea; + struct ea_buffer ea_buf; + int xattr_size; + ssize_t size; + int namelen = strlen(name); + char *os2name = NULL; + int rc; + char *value; + + if ((rc = can_get_xattr(inode, name))) + return rc; + + if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) { + os2name = kmalloc(namelen - XATTR_OS2_PREFIX_LEN + 1, + GFP_KERNEL); + if (!os2name) + return -ENOMEM; + strcpy(os2name, name + XATTR_OS2_PREFIX_LEN); + name = os2name; + namelen -= XATTR_OS2_PREFIX_LEN; + } + + xattr_size = ea_get(inode, &ea_buf, 0); + if (xattr_size < 0) { + size = xattr_size; + goto out; + } + + if (xattr_size == 0) + goto not_found; + + ealist = (struct jfs_ea_list *) ea_buf.xattr; + + /* Find the named attribute */ + for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) + if ((namelen == ea->namelen) && + memcmp(name, ea->name, namelen) == 0) { + /* Found it */ + size = le16_to_cpu(ea->valuelen); + if (!data) + goto release; + else if (size > buf_size) { + size = -ERANGE; + goto release; + } + value = ((char *) &ea->name) + ea->namelen + 1; + memcpy(data, value, size); + goto release; + } + not_found: + size = -ENODATA; + release: + ea_release(inode, &ea_buf); + out: + if (os2name) + kfree(os2name); + + return size; +} + +ssize_t jfs_getxattr(struct dentry *dentry, const char *name, void *data, + size_t buf_size) +{ + return __jfs_getxattr(dentry->d_inode, name, data, buf_size); +} + +ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) +{ + struct inode *inode = dentry->d_inode; + char *buffer; + ssize_t size = 0; + int xattr_size; + struct jfs_ea_list *ealist; + struct jfs_ea *ea; + struct ea_buffer ea_buf; + + xattr_size = ea_get(inode, &ea_buf, 0); + if (xattr_size < 0) { + size = xattr_size; + goto out; + } + + if (xattr_size == 0) + goto release; + + ealist = (struct jfs_ea_list *) ea_buf.xattr; + + /* compute required size of list */ + for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) + size += name_size(ea) + 1; + + if (!data) + goto release; + + if (size > buf_size) { + size = -ERANGE; + goto release; + } + + /* Copy attribute names to buffer */ + buffer = data; + for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) { + int namelen = copy_name(buffer, ea); + buffer += namelen + 1; + } + + release: + ea_release(inode, &ea_buf); + out: + return size; +} + +int jfs_removexattr(struct dentry *dentry, const char *name) +{ + return __jfs_setxattr(dentry->d_inode, name, 0, 0, XATTR_REPLACE); +}