X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fjfs%2Fxattr.c;h=7a9ffd5d03dc66f025f6224f7de246263d54f695;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=967253c2f2c4e28b1534d2206e1ead10f8439dad;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 967253c2f..7a9ffd5d0 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) International Business Machines Corp., 2000-2003 + * Copyright (C) International Business Machines Corp., 2000-2004 * Copyright (C) Christoph Hellwig, 2002 * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include #include +#include #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_dmap.h" @@ -90,6 +91,12 @@ struct ea_buffer { #define XATTR_OS2_PREFIX "os2." #define XATTR_OS2_PREFIX_LEN (sizeof (XATTR_OS2_PREFIX) - 1) +/* XATTR_SECURITY_PREFIX is defined in include/linux/xattr.h */ +#define XATTR_SECURITY_PREFIX_LEN (sizeof (XATTR_SECURITY_PREFIX) - 1) + +#define XATTR_TRUSTED_PREFIX "trusted." +#define XATTR_TRUSTED_PREFIX_LEN (sizeof (XATTR_TRUSTED_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, @@ -109,6 +116,19 @@ static inline int is_os2_xattr(struct jfs_ea *ea) if ((ea->namelen >= XATTR_USER_PREFIX_LEN) && !strncmp(ea->name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) return FALSE; + /* + * Check for "security." + */ + if ((ea->namelen >= XATTR_SECURITY_PREFIX_LEN) && + !strncmp(ea->name, XATTR_SECURITY_PREFIX, + XATTR_SECURITY_PREFIX_LEN)) + return FALSE; + /* + * Check for "trusted." + */ + if ((ea->namelen >= XATTR_TRUSTED_PREFIX_LEN) && + !strncmp(ea->name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) + return FALSE; /* * Add any other valid namespace prefixes here */ @@ -251,9 +271,17 @@ static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size, /* figure out how many blocks we need */ nblocks = (size + (sb->s_blocksize - 1)) >> sb->s_blocksize_bits; + /* Allocate new blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, nblocks)) { + return -EDQUOT; + } + rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno); - if (rc) + if (rc) { + /*Rollback quota allocation. */ + DQUOT_FREE_BLOCK(ip, nblocks); return rc; + } /* * Now have nblocks worth of storage to stuff into the FEALIST. @@ -315,6 +343,9 @@ static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size, return 0; failed: + /* Rollback quota allocation. */ + DQUOT_FREE_BLOCK(ip, nblocks); + dbFree(ip, blkno, nblocks); return rc; } @@ -448,6 +479,7 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) int blocks_needed, current_blocks; s64 blkno; int rc; + int quota_allocation = 0; /* When fsck.jfs clears a bad ea, it doesn't clear the size */ if (ji->ea.flag == 0) @@ -517,10 +549,16 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) sb->s_blocksize_bits; if (blocks_needed > current_blocks) { + /* Allocate new blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(inode, blocks_needed)) + return -EDQUOT; + + quota_allocation = blocks_needed; + rc = dbAlloc(inode, INOHINT(inode), (s64) blocks_needed, &blkno); if (rc) - return rc; + goto clean_up; DXDlength(&ea_buf->new_ea, blocks_needed); DXDaddress(&ea_buf->new_ea, blkno); @@ -534,7 +572,8 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) 1); if (ea_buf->mp == NULL) { dbFree(inode, blkno, (s64) blocks_needed); - return -EIO; + rc = -EIO; + goto clean_up; } ea_buf->xattr = ea_buf->mp->data; ea_buf->max_size = (min_size + sb->s_blocksize - 1) & @@ -544,15 +583,18 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) if ((rc = ea_read(inode, ea_buf->xattr))) { discard_metapage(ea_buf->mp); dbFree(inode, blkno, (s64) blocks_needed); - return rc; + goto clean_up; } 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; + lengthDXD(&ji->ea) << sb->s_blocksize_bits, + 1); + if (ea_buf->mp == NULL) { + rc = -EIO; + goto clean_up; + } ea_buf->xattr = ea_buf->mp->data; ea_buf->max_size = (ea_size + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1); @@ -562,10 +604,18 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_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; + rc = -EIO; + goto clean_up; } return ea_size; + + clean_up: + /* Rollback quota allocation */ + if (quota_allocation) + DQUOT_FREE_BLOCK(inode, quota_allocation); + + return (rc); } static void ea_release(struct inode *inode, struct ea_buffer *ea_buf) @@ -591,7 +641,7 @@ static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size) if (new_size == 0) { ea_release(inode, ea_buf); - ea_buf = 0; + ea_buf = NULL; } else if (ea_buf->flag & EA_INLINE) { assert(new_size <= sizeof (ji->i_inline_ea)); ji->mode2 &= ~INLINEEA; @@ -632,14 +682,17 @@ static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size) } ji->ea = ea_buf->new_ea; } else { - txEA(tid, inode, &ji->ea, 0); + txEA(tid, inode, &ji->ea, NULL); 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); + /* If old blocks exist, they must be removed from quota allocation. */ + if (old_blocks) + DQUOT_FREE_BLOCK(inode, old_blocks); + inode->i_ctime = CURRENT_TIME; rc = txCommit(tid, 1, &inode, 0); txEnd(tid); @@ -687,17 +740,26 @@ static int can_set_system_xattr(struct inode *inode, const char *name, } inode->i_mode = mode; mark_inode_dirty(inode); - if (rc == 0) - value = NULL; } /* * We're changing the ACL. Get rid of the cached one */ acl =JFS_IP(inode)->i_acl; - if (acl && (acl != JFS_ACL_NOT_CACHED)) + if (acl != JFS_ACL_NOT_CACHED) posix_acl_release(acl); JFS_IP(inode)->i_acl = JFS_ACL_NOT_CACHED; + + return 0; } else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) { + acl = posix_acl_from_xattr(value, value_len); + if (IS_ERR(acl)) { + rc = PTR_ERR(acl); + printk(KERN_ERR "posix_acl_from_xattr returned %d\n", + rc); + return rc; + } + posix_acl_release(acl); + /* * We're changing the default ACL. Get rid of the cached one */ @@ -705,13 +767,11 @@ static int can_set_system_xattr(struct inode *inode, const char *name, if (acl && (acl != JFS_ACL_NOT_CACHED)) posix_acl_release(acl); JFS_IP(inode)->i_default_acl = JFS_ACL_NOT_CACHED; - } else - /* Invalid xattr name */ - return -EINVAL; - return 0; -#else /* CONFIG_JFS_POSIX_ACL */ - return -EOPNOTSUPP; + + return 0; + } #endif /* CONFIG_JFS_POSIX_ACL */ + return -EOPNOTSUPP; } static int can_set_xattr(struct inode *inode, const char *name, @@ -729,6 +789,15 @@ static int can_set_xattr(struct inode *inode, const char *name, */ return can_set_system_xattr(inode, name, value, value_len); + if(strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0) + return (capable(CAP_SYS_ADMIN) ? 0 : -EPERM); + +#ifdef CONFIG_JFS_SECURITY + if (strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) + != 0) + return 0; /* Leave it to the security module */ +#endif + if((strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) != 0) && (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) != 0)) return -EOPNOTSUPP; @@ -737,11 +806,7 @@ static int can_set_xattr(struct inode *inode, const char *name, (!S_ISDIR(inode->i_mode) || inode->i_mode &S_ISVTX)) return -EPERM; -#ifdef CONFIG_JFS_POSIX_ACL - return jfs_permission(inode, MAY_WRITE, NULL); -#else return permission(inode, MAY_WRITE, NULL); -#endif } int __jfs_setxattr(struct inode *inode, const char *name, const void *value, @@ -900,13 +965,18 @@ int jfs_setxattr(struct dentry *dentry, const char *name, const void *value, static int can_get_xattr(struct inode *inode, const char *name) { -#ifdef CONFIG_JFS_POSIX_ACL +#ifdef CONFIG_JFS_SECURITY + if(strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) + return 0; +#endif + + if(strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) + return (capable(CAP_SYS_ADMIN) ? 0 : -EPERM); + if(strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) == 0) return 0; - return jfs_permission(inode, MAY_READ, NULL); -#else + return permission(inode, MAY_READ, NULL); -#endif } ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, @@ -988,6 +1058,16 @@ ssize_t jfs_getxattr(struct dentry *dentry, const char *name, void *data, return err; } +/* + * No special permissions are needed to list attributes except for trusted.* + */ +static inline int can_list(struct jfs_ea *ea) +{ + return (strncmp(ea->name, XATTR_TRUSTED_PREFIX, + XATTR_TRUSTED_PREFIX_LEN) || + capable(CAP_SYS_ADMIN)); +} + ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) { struct inode *inode = dentry->d_inode; @@ -1012,8 +1092,10 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) 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; + for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) { + if (can_list(ea)) + size += name_size(ea) + 1; + } if (!data) goto release; @@ -1026,8 +1108,10 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) /* 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; + if (can_list(ea)) { + int namelen = copy_name(buffer, ea); + buffer += namelen + 1; + } } release: @@ -1039,5 +1123,5 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) int jfs_removexattr(struct dentry *dentry, const char *name) { - return __jfs_setxattr(dentry->d_inode, name, 0, 0, XATTR_REPLACE); + return __jfs_setxattr(dentry->d_inode, name, NULL, 0, XATTR_REPLACE); }