attr->ia_atime = now;
        if (!(ia_valid & ATTR_MTIME_SET))
                attr->ia_mtime = now;
+       if (ia_valid & ATTR_KILL_PRIV) {
+               attr->ia_valid &= ~ATTR_KILL_PRIV;
+               ia_valid &= ~ATTR_KILL_PRIV;
+               error = security_inode_need_killpriv(dentry);
+               if (error > 0)
+                       error = security_inode_killpriv(dentry);
+               if (error)
+                       return error;
+       }
        if (ia_valid & ATTR_KILL_SUID) {
                attr->ia_valid &= ~ATTR_KILL_SUID;
                if (mode & S_ISUID) {
 
 
        /* Revoke setuid/setgid bit on chown/chgrp */
        if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid)
-               iap->ia_valid |= ATTR_KILL_SUID;
+               iap->ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV;
        if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)
                iap->ia_valid |= ATTR_KILL_SGID;
 
 static void kill_suid(struct dentry *dentry)
 {
        struct iattr    ia;
-       ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID;
+       ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
 
        mutex_lock(&dentry->d_inode->i_mutex);
        notify_change(dentry, &ia);
 
                newattrs.ia_gid = group;
        }
        if (!S_ISDIR(inode->i_mode))
-               newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID;
+               newattrs.ia_valid |=
+                       ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
        mutex_lock(&inode->i_mutex);
        error = notify_change(dentry, &newattrs);
        mutex_unlock(&inode->i_mutex);
 
 {
        struct address_space *mapping = out->f_mapping;
        struct inode *inode = mapping->host;
+       int killsuid, killpriv;
        ssize_t ret;
-       int err;
+       int err = 0;
 
-       err = should_remove_suid(out->f_path.dentry);
-       if (unlikely(err)) {
+       killpriv = security_inode_need_killpriv(out->f_path.dentry);
+       killsuid = should_remove_suid(out->f_path.dentry);
+       if (unlikely(killsuid || killpriv)) {
                mutex_lock(&inode->i_mutex);
-               err = __remove_suid(out->f_path.dentry, err);
+               if (killpriv)
+                       err = security_inode_killpriv(out->f_path.dentry);
+               if (!err && killsuid)
+                       err = __remove_suid(out->f_path.dentry, killsuid);
                mutex_unlock(&inode->i_mutex);
                if (err)
                        return err;
 
        int sh_bang;
        struct file * file;
        int e_uid, e_gid;
-       kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
+       kernel_cap_t cap_inheritable, cap_permitted;
+       bool cap_effective;
        void *security;
        int argc, envc;
        char * filename;        /* Name of binary as seen by procps */
 
 /*
  * This is <linux/capability.h>
  *
- * Andrew G. Morgan <morgan@transmeta.com>
+ * Andrew G. Morgan <morgan@kernel.org>
  * Alexander Kjeldaas <astor@guardian.no>
  * with help from Aleph1, Roland Buresund and Andrew Main.
  *
  * See here for the libcap library ("POSIX draft" compliance):
  *
- * ftp://linux.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.2/
- */ 
+ * ftp://linux.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.6/
+ */
 
 #ifndef _LINUX_CAPABILITY_H
 #define _LINUX_CAPABILITY_H
    following structure to such a composite is better handled in a user
    library since the draft standard requires the use of malloc/free
    etc.. */
- 
+
 #define _LINUX_CAPABILITY_VERSION  0x19980330
 
 typedef struct __user_cap_header_struct {
        __u32 version;
        int pid;
 } __user *cap_user_header_t;
- 
+
 typedef struct __user_cap_data_struct {
         __u32 effective;
         __u32 permitted;
         __u32 inheritable;
 } __user *cap_user_data_t;
-  
-#ifdef __KERNEL__
 
-#include <asm/current.h>
+#define XATTR_CAPS_SUFFIX "capability"
+#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
+
+#define XATTR_CAPS_SZ (3*sizeof(__le32))
+#define VFS_CAP_REVISION_MASK  0xFF000000
+#define VFS_CAP_REVISION_1     0x01000000
+
+#define VFS_CAP_REVISION       VFS_CAP_REVISION_1
+
+#define VFS_CAP_FLAGS_MASK     ~VFS_CAP_REVISION_MASK
+#define VFS_CAP_FLAGS_EFFECTIVE        0x000001
+
+struct vfs_cap_data {
+       __u32 magic_etc;  /* Little endian */
+       struct {
+               __u32 permitted;    /* Little endian */
+               __u32 inheritable;  /* Little endian */
+       } data[1];
+};
+
+#ifdef __KERNEL__
 
 /* #define STRICT_CAP_T_TYPECHECKS */
 
 typedef __u32 kernel_cap_t;
 
 #endif
-  
+
 #define _USER_CAP_HEADER_SIZE  (2*sizeof(__u32))
 #define _KERNEL_CAP_T_SIZE     (sizeof(kernel_cap_t))
 
 
 
 /**
- ** POSIX-draft defined capabilities. 
+ ** POSIX-draft defined capabilities.
  **/
 
 /* In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
    defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. */
 
 #define CAP_DAC_READ_SEARCH  2
-    
+
 /* Overrides all restrictions about allowed operations on files, where
    file owner ID must be equal to the user ID, except where CAP_FSETID
    is applicable. It doesn't override MAC and DAC restrictions. */
 /* Override reserved space on ext2 filesystem */
 /* Modify data journaling mode on ext3 filesystem (uses journaling
    resources) */
-/* NOTE: ext2 honors fsuid when checking for resource overrides, so 
+/* NOTE: ext2 honors fsuid when checking for resource overrides, so
    you can override using fsuid too */
 /* Override size restrictions on IPC message queues */
 /* Allow more than 64hz interrupts from the real-time clock */
 
 #define CAP_AUDIT_CONTROL    30
 
+#define CAP_SETFCAP         31
+
 #ifdef __KERNEL__
-/* 
+/*
  * Bounding set
  */
 extern kernel_cap_t cap_bset;
 /*
  * Internal kernel functions only
  */
- 
+
 #ifdef STRICT_CAP_T_TYPECHECKS
 
 #define to_cap_t(x) { x }
 
 #define ATTR_KILL_SUID 2048
 #define ATTR_KILL_SGID 4096
 #define ATTR_FILE      8192
+#define ATTR_KILL_PRIV 16384
 
 /*
  * This is the Inode Attributes structure, used for notify_change().  It
 
 extern int cap_bprm_secureexec(struct linux_binprm *bprm);
 extern int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags);
 extern int cap_inode_removexattr(struct dentry *dentry, char *name);
+extern int cap_inode_need_killpriv(struct dentry *dentry);
+extern int cap_inode_killpriv(struct dentry *dentry);
 extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
 extern void cap_task_reparent_to_init (struct task_struct *p);
+extern int cap_task_kill(struct task_struct *p, struct siginfo *info, int sig, u32 secid);
+extern int cap_task_setscheduler (struct task_struct *p, int policy, struct sched_param *lp);
+extern int cap_task_setioprio (struct task_struct *p, int ioprio);
+extern int cap_task_setnice (struct task_struct *p, int nice);
 extern int cap_syslog (int type);
 extern int cap_vm_enough_memory(struct mm_struct *mm, long pages);
 
  *     is specified by @buffer_size.  @buffer may be NULL to request
  *     the size of the buffer required.
  *     Returns number of bytes used/required on success.
+ * @inode_need_killpriv:
+ *     Called when an inode has been changed.
+ *     @dentry is the dentry being changed.
+ *     Return <0 on error to abort the inode change operation.
+ *     Return 0 if inode_killpriv does not need to be called.
+ *     Return >0 if inode_killpriv does need to be called.
+ * @inode_killpriv:
+ *     The setuid bit is being removed.  Remove similar security labels.
+ *     Called with the dentry->d_inode->i_mutex held.
+ *     @dentry is the dentry being changed.
+ *     Return 0 on success.  If error is returned, then the operation
+ *     causing setuid bit removal is failed.
  *
  * Security hooks for file operations
  *
        int (*inode_getxattr) (struct dentry *dentry, char *name);
        int (*inode_listxattr) (struct dentry *dentry);
        int (*inode_removexattr) (struct dentry *dentry, char *name);
+       int (*inode_need_killpriv) (struct dentry *dentry);
+       int (*inode_killpriv) (struct dentry *dentry);
        const char *(*inode_xattr_getsuffix) (void);
        int (*inode_getsecurity)(const struct inode *inode, const char *name, void *buffer, size_t size, int err);
        int (*inode_setsecurity)(struct inode *inode, const char *name, const void *value, size_t size, int flags);
 int security_inode_getxattr(struct dentry *dentry, char *name);
 int security_inode_listxattr(struct dentry *dentry);
 int security_inode_removexattr(struct dentry *dentry, char *name);
+int security_inode_need_killpriv(struct dentry *dentry);
+int security_inode_killpriv(struct dentry *dentry);
 const char *security_inode_xattr_getsuffix(void);
 int security_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err);
 int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags);
        return cap_inode_removexattr(dentry, name);
 }
 
+static inline int security_inode_need_killpriv(struct dentry *dentry)
+{
+       return cap_inode_need_killpriv(dentry);
+}
+
+static inline int security_inode_killpriv(struct dentry *dentry)
+{
+       return cap_inode_killpriv(dentry);
+}
+
 static inline const char *security_inode_xattr_getsuffix (void)
 {
        return NULL ;
 
 static inline int security_task_setnice (struct task_struct *p, int nice)
 {
-       return 0;
+       return cap_task_setnice(p, nice);
 }
 
 static inline int security_task_setioprio (struct task_struct *p, int ioprio)
 {
-       return 0;
+       return cap_task_setioprio(p, ioprio);
 }
 
 static inline int security_task_getioprio (struct task_struct *p)
                                              int policy,
                                              struct sched_param *lp)
 {
-       return 0;
+       return cap_task_setscheduler(p, policy, lp);
 }
 
 static inline int security_task_getscheduler (struct task_struct *p)
                                      struct siginfo *info, int sig,
                                      u32 secid)
 {
-       return 0;
+       return cap_task_kill(p, info, sig, secid);
 }
 
 static inline int security_task_wait (struct task_struct *p)
 
 
 int remove_suid(struct dentry *dentry)
 {
-       int kill = should_remove_suid(dentry);
+       int killsuid = should_remove_suid(dentry);
+       int killpriv = security_inode_need_killpriv(dentry);
+       int error = 0;
 
-       if (unlikely(kill))
-               return __remove_suid(dentry, kill);
+       if (killpriv < 0)
+               return killpriv;
+       if (killpriv)
+               error = security_inode_killpriv(dentry);
+       if (!error && killsuid)
+               error = __remove_suid(dentry, killsuid);
 
-       return 0;
+       return error;
 }
 EXPORT_SYMBOL(remove_suid);
 
 
          This enables the "default" Linux capabilities functionality.
          If you are unsure how to answer this question, answer Y.
 
+config SECURITY_FILE_CAPABILITIES
+       bool "File POSIX Capabilities (EXPERIMENTAL)"
+       depends on (SECURITY=n || SECURITY_CAPABILITIES!=n) && EXPERIMENTAL
+       default n
+       help
+         This enables filesystem capabilities, allowing you to give
+         binaries a subset of root's powers without using setuid 0.
+
+         If in doubt, answer N.
+
 config SECURITY_ROOTPLUG
        bool "Root Plug Support"
        depends on USB=y && SECURITY
 
 
        .inode_setxattr =               cap_inode_setxattr,
        .inode_removexattr =            cap_inode_removexattr,
+       .inode_need_killpriv =          cap_inode_need_killpriv,
+       .inode_killpriv =               cap_inode_killpriv,
 
+       .task_kill =                    cap_task_kill,
+       .task_setscheduler =            cap_task_setscheduler,
+       .task_setioprio =               cap_task_setioprio,
+       .task_setnice =                 cap_task_setnice,
        .task_post_setuid =             cap_task_post_setuid,
        .task_reparent_to_init =        cap_task_reparent_to_init,
 
 
 #include <linux/ptrace.h>
 #include <linux/xattr.h>
 #include <linux/hugetlb.h>
+#include <linux/mount.h>
 
 int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
        target->cap_permitted = *permitted;
 }
 
+static inline void bprm_clear_caps(struct linux_binprm *bprm)
+{
+       cap_clear(bprm->cap_inheritable);
+       cap_clear(bprm->cap_permitted);
+       bprm->cap_effective = false;
+}
+
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+
+int cap_inode_need_killpriv(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+       int error;
+
+       if (!inode->i_op || !inode->i_op->getxattr)
+              return 0;
+
+       error = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0);
+       if (error <= 0)
+               return 0;
+       return 1;
+}
+
+int cap_inode_killpriv(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (!inode->i_op || !inode->i_op->removexattr)
+              return 0;
+
+       return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
+}
+
+static inline int cap_from_disk(__le32 *caps, struct linux_binprm *bprm,
+                               int size)
+{
+       __u32 magic_etc;
+
+       if (size != XATTR_CAPS_SZ)
+               return -EINVAL;
+
+       magic_etc = le32_to_cpu(caps[0]);
+
+       switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
+       case VFS_CAP_REVISION:
+               if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
+                       bprm->cap_effective = true;
+               else
+                       bprm->cap_effective = false;
+               bprm->cap_permitted = to_cap_t( le32_to_cpu(caps[1]) );
+               bprm->cap_inheritable = to_cap_t( le32_to_cpu(caps[2]) );
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+/* Locate any VFS capabilities: */
+static int get_file_caps(struct linux_binprm *bprm)
+{
+       struct dentry *dentry;
+       int rc = 0;
+       __le32 v1caps[XATTR_CAPS_SZ];
+       struct inode *inode;
+
+       if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) {
+               bprm_clear_caps(bprm);
+               return 0;
+       }
+
+       dentry = dget(bprm->file->f_dentry);
+       inode = dentry->d_inode;
+       if (!inode->i_op || !inode->i_op->getxattr)
+               goto out;
+
+       rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &v1caps,
+                                                       XATTR_CAPS_SZ);
+       if (rc == -ENODATA || rc == -EOPNOTSUPP) {
+               /* no data, that's ok */
+               rc = 0;
+               goto out;
+       }
+       if (rc < 0)
+               goto out;
+
+       rc = cap_from_disk(v1caps, bprm, rc);
+       if (rc)
+               printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
+                       __FUNCTION__, rc, bprm->filename);
+
+out:
+       dput(dentry);
+       if (rc)
+               bprm_clear_caps(bprm);
+
+       return rc;
+}
+
+#else
+int cap_inode_need_killpriv(struct dentry *dentry)
+{
+       return 0;
+}
+
+int cap_inode_killpriv(struct dentry *dentry)
+{
+       return 0;
+}
+
+static inline int get_file_caps(struct linux_binprm *bprm)
+{
+       bprm_clear_caps(bprm);
+       return 0;
+}
+#endif
+
 int cap_bprm_set_security (struct linux_binprm *bprm)
 {
-       /* Copied from fs/exec.c:prepare_binprm. */
+       int ret;
 
-       /* We don't have VFS support for capabilities yet */
-       cap_clear (bprm->cap_inheritable);
-       cap_clear (bprm->cap_permitted);
-       cap_clear (bprm->cap_effective);
+       ret = get_file_caps(bprm);
+       if (ret)
+               printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n",
+                       __FUNCTION__, ret, bprm->filename);
 
        /*  To support inheritance of root-permissions and suid-root
         *  executables under compatibility mode, we raise all three
                        cap_set_full (bprm->cap_permitted);
                }
                if (bprm->e_uid == 0)
-                       cap_set_full (bprm->cap_effective);
+                       bprm->cap_effective = true;
        }
-       return 0;
+
+       return ret;
 }
 
 void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
        if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
            !cap_issubset (new_permitted, current->cap_permitted)) {
                set_dumpable(current->mm, suid_dumpable);
+               current->pdeath_signal = 0;
 
                if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
                        if (!capable(CAP_SETUID)) {
         * capability rules */
        if (!is_init(current)) {
                current->cap_permitted = new_permitted;
-               current->cap_effective =
-                   cap_intersect (new_permitted, bprm->cap_effective);
+               current->cap_effective = bprm->cap_effective ?
+                               new_permitted : 0;
        }
 
        /* AUD: Audit candidate if current->cap_effective is set */
 
 int cap_bprm_secureexec (struct linux_binprm *bprm)
 {
-       /* If/when this module is enhanced to incorporate capability
-          bits on files, the test below should be extended to also perform a 
-          test between the old and new capability sets.  For now,
-          it simply preserves the legacy decision algorithm used by
-          the old userland. */
+       if (current->uid != 0) {
+               if (bprm->cap_effective)
+                       return 1;
+               if (!cap_isclear(bprm->cap_permitted))
+                       return 1;
+               if (!cap_isclear(bprm->cap_inheritable))
+                       return 1;
+       }
+
        return (current->euid != current->uid ||
                current->egid != current->gid);
 }
 int cap_inode_setxattr(struct dentry *dentry, char *name, void *value,
                       size_t size, int flags)
 {
-       if (!strncmp(name, XATTR_SECURITY_PREFIX,
+       if (!strcmp(name, XATTR_NAME_CAPS)) {
+               if (!capable(CAP_SETFCAP))
+                       return -EPERM;
+               return 0;
+       } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
                     sizeof(XATTR_SECURITY_PREFIX) - 1)  &&
            !capable(CAP_SYS_ADMIN))
                return -EPERM;
 
 int cap_inode_removexattr(struct dentry *dentry, char *name)
 {
-       if (!strncmp(name, XATTR_SECURITY_PREFIX,
+       if (!strcmp(name, XATTR_NAME_CAPS)) {
+               if (!capable(CAP_SETFCAP))
+                       return -EPERM;
+               return 0;
+       } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
                     sizeof(XATTR_SECURITY_PREFIX) - 1)  &&
            !capable(CAP_SYS_ADMIN))
                return -EPERM;
        return 0;
 }
 
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+/*
+ * Rationale: code calling task_setscheduler, task_setioprio, and
+ * task_setnice, assumes that
+ *   . if capable(cap_sys_nice), then those actions should be allowed
+ *   . if not capable(cap_sys_nice), but acting on your own processes,
+ *     then those actions should be allowed
+ * This is insufficient now since you can call code without suid, but
+ * yet with increased caps.
+ * So we check for increased caps on the target process.
+ */
+static inline int cap_safe_nice(struct task_struct *p)
+{
+       if (!cap_issubset(p->cap_permitted, current->cap_permitted) &&
+           !__capable(current, CAP_SYS_NICE))
+               return -EPERM;
+       return 0;
+}
+
+int cap_task_setscheduler (struct task_struct *p, int policy,
+                          struct sched_param *lp)
+{
+       return cap_safe_nice(p);
+}
+
+int cap_task_setioprio (struct task_struct *p, int ioprio)
+{
+       return cap_safe_nice(p);
+}
+
+int cap_task_setnice (struct task_struct *p, int nice)
+{
+       return cap_safe_nice(p);
+}
+
+int cap_task_kill(struct task_struct *p, struct siginfo *info,
+                               int sig, u32 secid)
+{
+       if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)))
+               return 0;
+
+       if (secid)
+               /*
+                * Signal sent as a particular user.
+                * Capabilities are ignored.  May be wrong, but it's the
+                * only thing we can do at the moment.
+                * Used only by usb drivers?
+                */
+               return 0;
+       if (cap_issubset(p->cap_permitted, current->cap_permitted))
+               return 0;
+       if (capable(CAP_KILL))
+               return 0;
+
+       return -EPERM;
+}
+#else
+int cap_task_setscheduler (struct task_struct *p, int policy,
+                          struct sched_param *lp)
+{
+       return 0;
+}
+int cap_task_setioprio (struct task_struct *p, int ioprio)
+{
+       return 0;
+}
+int cap_task_setnice (struct task_struct *p, int nice)
+{
+       return 0;
+}
+int cap_task_kill(struct task_struct *p, struct siginfo *info,
+                               int sig, u32 secid)
+{
+       return 0;
+}
+#endif
+
 void cap_task_reparent_to_init (struct task_struct *p)
 {
        p->cap_effective = CAP_INIT_EFF_SET;
 EXPORT_SYMBOL(cap_inode_setxattr);
 EXPORT_SYMBOL(cap_inode_removexattr);
 EXPORT_SYMBOL(cap_task_post_setuid);
+EXPORT_SYMBOL(cap_task_kill);
+EXPORT_SYMBOL(cap_task_setscheduler);
+EXPORT_SYMBOL(cap_task_setioprio);
+EXPORT_SYMBOL(cap_task_setnice);
 EXPORT_SYMBOL(cap_task_reparent_to_init);
 EXPORT_SYMBOL(cap_syslog);
 EXPORT_SYMBOL(cap_vm_enough_memory);
 
        return 0;
 }
 
+static int dummy_inode_need_killpriv(struct dentry *dentry)
+{
+       return 0;
+}
+
+static int dummy_inode_killpriv(struct dentry *dentry)
+{
+       return 0;
+}
+
 static int dummy_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
 {
        return -EOPNOTSUPP;
        set_to_dummy_if_null(ops, inode_getxattr);
        set_to_dummy_if_null(ops, inode_listxattr);
        set_to_dummy_if_null(ops, inode_removexattr);
+       set_to_dummy_if_null(ops, inode_need_killpriv);
+       set_to_dummy_if_null(ops, inode_killpriv);
        set_to_dummy_if_null(ops, inode_xattr_getsuffix);
        set_to_dummy_if_null(ops, inode_getsecurity);
        set_to_dummy_if_null(ops, inode_setsecurity);
 
        return security_ops->inode_removexattr(dentry, name);
 }
 
+int security_inode_need_killpriv(struct dentry *dentry)
+{
+       return security_ops->inode_need_killpriv(dentry);
+}
+
+int security_inode_killpriv(struct dentry *dentry)
+{
+       return security_ops->inode_killpriv(dentry);
+}
+
 const char *security_inode_xattr_getsuffix(void)
 {
        return security_ops->inode_xattr_getsuffix();
 
        return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
 }
 
+static int selinux_inode_setotherxattr(struct dentry *dentry, char *name)
+{
+       if (!strncmp(name, XATTR_SECURITY_PREFIX,
+                    sizeof XATTR_SECURITY_PREFIX - 1)) {
+               if (!strcmp(name, XATTR_NAME_CAPS)) {
+                       if (!capable(CAP_SETFCAP))
+                               return -EPERM;
+               } else if (!capable(CAP_SYS_ADMIN)) {
+                       /* A different attribute in the security namespace.
+                          Restrict to administrator. */
+                       return -EPERM;
+               }
+       }
+
+       /* Not an attribute we recognize, so just check the
+          ordinary setattr permission. */
+       return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+}
+
 static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags)
 {
        struct task_security_struct *tsec = current->security;
        u32 newsid;
        int rc = 0;
 
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        sbsec = inode->i_sb->s_security;
        if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
 
 static int selinux_inode_removexattr (struct dentry *dentry, char *name)
 {
-       if (strcmp(name, XATTR_NAME_SELINUX)) {
-               if (!strncmp(name, XATTR_SECURITY_PREFIX,
-                            sizeof XATTR_SECURITY_PREFIX - 1) &&
-                   !capable(CAP_SYS_ADMIN)) {
-                       /* A different attribute in the security namespace.
-                          Restrict to administrator. */
-                       return -EPERM;
-               }
-
-               /* Not an attribute we recognize, so just check the
-                  ordinary setattr permission. Might want a separate
-                  permission for removexattr. */
-               return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
-       }
+       if (strcmp(name, XATTR_NAME_SELINUX))
+               return selinux_inode_setotherxattr(dentry, name);
 
        /* No one is allowed to remove a SELinux security label.
           You can change the label, but all data must be labeled. */
        return len;
 }
 
+static int selinux_inode_need_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_need_killpriv(dentry);
+}
+
+static int selinux_inode_killpriv(struct dentry *dentry)
+{
+       return secondary_ops->inode_killpriv(dentry);
+}
+
 /* file security operations */
 
 static int selinux_revalidate_file_permission(struct file *file, int mask)
 
 static int selinux_task_setioprio(struct task_struct *p, int ioprio)
 {
+       int rc;
+
+       rc = secondary_ops->task_setioprio(p, ioprio);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
 
 static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
 {
+       int rc;
+
+       rc = secondary_ops->task_setscheduler(p, policy, lp);
+       if (rc)
+               return rc;
+
        return task_has_perm(current, p, PROCESS__SETSCHED);
 }
 
        .inode_getsecurity =            selinux_inode_getsecurity,
        .inode_setsecurity =            selinux_inode_setsecurity,
        .inode_listsecurity =           selinux_inode_listsecurity,
+       .inode_need_killpriv =          selinux_inode_need_killpriv,
+       .inode_killpriv =               selinux_inode_killpriv,
 
        .file_permission =              selinux_file_permission,
        .file_alloc_security =          selinux_file_alloc_security,