fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / jfs / xattr.c
index 9c0e3a5..b05b369 100644 (file)
@@ -4,22 +4,26 @@
  *
  *   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 <linux/capability.h>
 #include <linux/fs.h>
 #include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 #include <linux/quotaops.h>
+#include <linux/security.h>
+#include <linux/vs_dlimit.h>
 #include "jfs_incore.h"
 #include "jfs_superblock.h"
 #include "jfs_dmap.h"
@@ -54,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  |                   |                    |
  *   +------------+-------------------+--------------------+-----
  *
@@ -81,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
@@ -103,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
         */
@@ -117,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)
@@ -148,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
  *
@@ -209,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
@@ -256,9 +264,16 @@ static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size,
        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) {
+               /*Rollback dlimit allocation. */
+               DLIMIT_FREE_BLOCK(ip, nblocks);
                /*Rollback quota allocation. */
                DQUOT_FREE_BLOCK(ip, nblocks);
                return rc;
@@ -324,6 +339,8 @@ 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);
 
@@ -333,9 +350,9 @@ static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size,
 
 /*
  * 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
@@ -365,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
@@ -399,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.
         */
@@ -436,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
@@ -461,6 +478,7 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
        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)
@@ -536,6 +554,12 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
 
                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)
@@ -592,6 +616,9 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
        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);
@@ -613,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);
@@ -644,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) {
@@ -671,15 +695,14 @@ static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size)
        }
 
        /* If old blocks exist, they must be removed from quota allocation. */
-       if (old_blocks)
+       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;
 }
 
 /*
@@ -699,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);
@@ -731,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);
@@ -755,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;
@@ -796,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);
@@ -910,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:
@@ -918,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;
 }
@@ -927,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,
@@ -952,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);
@@ -1005,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;
 }
@@ -1021,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;
@@ -1045,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;
@@ -1059,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:
@@ -1072,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