/*
- * 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
#include <linux/fs.h>
#include <linux/xattr.h>
+#include <linux/quotaops.h>
#include "jfs_incore.h"
#include "jfs_superblock.h"
#include "jfs_dmap.h"
#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,
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
*/
/* 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.
return 0;
failed:
+ /* Rollback quota allocation. */
+ DQUOT_FREE_BLOCK(ip, nblocks);
+
dbFree(ip, blkno, nblocks);
return rc;
}
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)
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);
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) &
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);
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)
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;
}
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);
}
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
*/
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,
*/
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;
(!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,
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,
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;
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;
/* 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:
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);
}