X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fjfs%2Fxattr.c;h=b05b369975b0acc33a54a943de10d47550108df3;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=cf3fba6e5d781124b160ccf299c7887ffb185a1d;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index cf3fba6e5..b05b36997 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -1,24 +1,29 @@ /* - * 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 * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * 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 + * 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 +#include +#include +#include #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_dmap.h" @@ -53,7 +58,7 @@ * * 0 4 4 + EA_SIZE(ea1) * +------------+-------------------+--------------------+----- - * | Overall EA | First FEA Element | Second FEA Element | ..... + * | Overall EA | First FEA Element | Second FEA Element | ..... * | List Size | | | * +------------+-------------------+--------------------+----- * @@ -80,15 +85,6 @@ struct ea_buffer { #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 @@ -102,13 +98,26 @@ static inline int is_os2_xattr(struct jfs_ea *ea) */ if ((ea->namelen >= XATTR_SYSTEM_PREFIX_LEN) && !strncmp(ea->name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) - return FALSE; + return false; /* * Check for "user." */ if ((ea->namelen >= XATTR_USER_PREFIX_LEN) && !strncmp(ea->name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - return FALSE; + 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 */ @@ -116,7 +125,7 @@ static inline int is_os2_xattr(struct jfs_ea *ea) /* * We assume it's OS/2's flat namespace */ - return TRUE; + return true; } static inline int name_size(struct jfs_ea *ea) @@ -147,9 +156,9 @@ 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 * @@ -208,10 +217,10 @@ static int ea_write_inline(struct inode *ip, struct jfs_ea_list *ealist, /* * NAME: ea_write - * + * * FUNCTION: Write an EA for an inode - * - * PRE CONDITIONS: EA has been verified + * + * PRE CONDITIONS: EA has been verified * * PARAMETERS: * ip - Inode pointer @@ -251,9 +260,24 @@ 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; + } + /* Allocate new blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, nblocks)) { + DQUOT_FREE_BLOCK(ip, nblocks); + return -ENOSPC; + } + rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno); - if (rc) + if (rc) { + /*Rollback dlimit allocation. */ + DLIMIT_FREE_BLOCK(ip, nblocks); + /*Rollback quota allocation. */ + DQUOT_FREE_BLOCK(ip, nblocks); return rc; + } /* * Now have nblocks worth of storage to stuff into the FEALIST. @@ -315,15 +339,20 @@ static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size, return 0; failed: + /* Rollback quota allocation. */ + DLIMIT_FREE_BLOCK(ip, nblocks); + /* Rollback quota allocation. */ + DQUOT_FREE_BLOCK(ip, nblocks); + 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 @@ -353,9 +382,9 @@ static int ea_read_inline(struct inode *ip, struct jfs_ea_list *ealist) /* * NAME: ea_read - * + * * FUNCTION: copy EA data into user's buffer - * + * * PARAMETERS: * ip - Inode pointer * ealist - Pointer to buffer to fill in with EA @@ -387,7 +416,7 @@ static int ea_read(struct inode *ip, struct jfs_ea_list *ealist) return -EIO; } - /* + /* * Figure out how many blocks were allocated when this EA list was * originally written to disk. */ @@ -424,14 +453,14 @@ static int ea_read(struct inode *ip, struct jfs_ea_list *ealist) /* * 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 - * + * 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 @@ -448,6 +477,8 @@ 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; + int dlimit_allocation = 0; /* When fsck.jfs clears a bad ea, it doesn't clear the size */ if (ji->ea.flag == 0) @@ -517,10 +548,22 @@ 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; + + /* Allocate new blocks to dlimit. */ + rc = -ENOSPC; + if (DLIMIT_ALLOC_BLOCK(inode, blocks_needed)) + goto clean_up; + dlimit_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 +577,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,7 +588,7 @@ 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; } @@ -552,8 +596,10 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) ea_buf->mp = read_metapage(inode, addressDXD(&ji->ea), lengthDXD(&ji->ea) << sb->s_blocksize_bits, 1); - if (ea_buf->mp == NULL) - return -EIO; + 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); @@ -563,10 +609,21 @@ 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 dlimit allocation */ + if (dlimit_allocation) + DLIMIT_FREE_BLOCK(inode, dlimit_allocation); + /* 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) @@ -583,12 +640,12 @@ static void ea_release(struct inode *inode, struct ea_buffer *ea_buf) } } -static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size) +static int ea_put(tid_t tid, 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); @@ -614,9 +671,6 @@ static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size) 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) { @@ -640,13 +694,15 @@ static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size) 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) { + DLIMIT_FREE_BLOCK(inode, old_blocks); + DQUOT_FREE_BLOCK(inode, old_blocks); + } + inode->i_ctime = CURRENT_TIME; - rc = txCommit(tid, 1, &inode, 0); - txEnd(tid); - up(&ji->commit_sem); - return rc; + return 0; } /* @@ -666,9 +722,9 @@ static int can_set_system_xattr(struct inode *inode, const char *name, return -EPERM; /* - * XATTR_NAME_ACL_ACCESS is tied to i_mode + * POSIX_ACL_XATTR_ACCESS is tied to i_mode */ - if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) { + if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) { acl = posix_acl_from_xattr(value, value_len); if (IS_ERR(acl)) { rc = PTR_ERR(acl); @@ -698,7 +754,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name, JFS_IP(inode)->i_acl = JFS_ACL_NOT_CACHED; return 0; - } else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) { + } else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) { acl = posix_acl_from_xattr(value, value_len); if (IS_ERR(acl)) { rc = PTR_ERR(acl); @@ -722,34 +778,31 @@ static int can_set_system_xattr(struct inode *inode, const char *name, return -EOPNOTSUPP; } +/* + * Most of the permission checking is done by xattr_permission in the vfs. + * The local file system is responsible for handling the system.* namespace. + * We also need to verify that this is a namespace that we recognize. + */ 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_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) == 0) - /* - * "system.*" - */ + if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) return can_set_system_xattr(inode, name, value, value_len); - if((strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) != 0) && - (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) != 0)) + /* + * Don't allow setting an attribute in an unknown namespace. + */ + if (strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) && + strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && + strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)) 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, NULL); + return 0; } -int __jfs_setxattr(struct inode *inode, const char *name, const void *value, - size_t value_len, int flags) +int __jfs_setxattr(tid_t tid, 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; @@ -763,9 +816,6 @@ int __jfs_setxattr(struct inode *inode, const char *name, const void *value, 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); @@ -877,7 +927,7 @@ int __jfs_setxattr(struct inode *inode, const char *name, const void *value, ealist->size = cpu_to_le32(new_size); - rc = ea_put(inode, &ea_buf, new_size); + rc = ea_put(tid, inode, &ea_buf, new_size); goto out; release: @@ -885,8 +935,7 @@ int __jfs_setxattr(struct inode *inode, const char *name, const void *value, out: up_write(&JFS_IP(inode)->xattr_sem); - if (os2name) - kfree(os2name); + kfree(os2name); return rc; } @@ -894,19 +943,29 @@ int __jfs_setxattr(struct inode *inode, const char *name, const void *value, int jfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t value_len, int flags) { + struct inode *inode = dentry->d_inode; + struct jfs_inode_info *ji = JFS_IP(inode); + int rc; + tid_t tid; + + if ((rc = can_set_xattr(inode, name, value, value_len))) + return rc; + if (value == NULL) { /* empty EA, do not remove */ value = ""; value_len = 0; } - return __jfs_setxattr(dentry->d_inode, name, value, value_len, flags); -} + tid = txBegin(inode->i_sb, 0); + mutex_lock(&ji->commit_mutex); + rc = __jfs_setxattr(tid, dentry->d_inode, name, value, value_len, + flags); + if (!rc) + rc = txCommit(tid, 1, &inode, 0); + txEnd(tid); + mutex_unlock(&ji->commit_mutex); -static int can_get_xattr(struct inode *inode, const char *name) -{ - if(strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) == 0) - return 0; - return permission(inode, MAY_READ, NULL); + return rc; } ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, @@ -919,12 +978,8 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, 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); @@ -972,8 +1027,7 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, out: up_read(&JFS_IP(inode)->xattr_sem); - if (os2name) - kfree(os2name); + kfree(os2name); return size; } @@ -988,6 +1042,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 +1076,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 +1092,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 +1107,56 @@ 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, NULL, 0, XATTR_REPLACE); + struct inode *inode = dentry->d_inode; + struct jfs_inode_info *ji = JFS_IP(inode); + int rc; + tid_t tid; + + if ((rc = can_set_xattr(inode, name, NULL, 0))) + return rc; + + tid = txBegin(inode->i_sb, 0); + mutex_lock(&ji->commit_mutex); + rc = __jfs_setxattr(tid, dentry->d_inode, name, NULL, 0, XATTR_REPLACE); + if (!rc) + rc = txCommit(tid, 1, &inode, 0); + txEnd(tid); + mutex_unlock(&ji->commit_mutex); + + return rc; } + +#ifdef CONFIG_JFS_SECURITY +int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir) +{ + int rc; + size_t len; + void *value; + char *suffix; + char *name; + + rc = security_inode_init_security(inode, dir, &suffix, &value, &len); + if (rc) { + if (rc == -EOPNOTSUPP) + return 0; + return rc; + } + name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 1 + strlen(suffix), + GFP_NOFS); + if (!name) { + rc = -ENOMEM; + goto kmalloc_failed; + } + strcpy(name, XATTR_SECURITY_PREFIX); + strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix); + + rc = __jfs_setxattr(tid, inode, name, value, len, 0); + + kfree(name); +kmalloc_failed: + kfree(suffix); + kfree(value); + + return rc; +} +#endif