mm: more likely reclaim MADV_SEQUENTIAL mappings
[linux-2.6.git] / kernel / sys.c
index 558b035..d356d79 100644 (file)
@@ -112,12 +112,17 @@ EXPORT_SYMBOL(cad_pid);
 
 void (*pm_power_off_prepare)(void);
 
+/*
+ * set the priority of a task
+ * - the caller must hold the RCU read lock
+ */
 static int set_one_prio(struct task_struct *p, int niceval, int error)
 {
+       const struct cred *cred = current_cred(), *pcred = __task_cred(p);
        int no_nice;
 
-       if (p->uid != current->euid &&
-               p->euid != current->euid && !capable(CAP_SYS_NICE)) {
+       if (pcred->uid  != cred->euid &&
+           pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) {
                error = -EPERM;
                goto out;
        }
@@ -141,6 +146,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
 {
        struct task_struct *g, *p;
        struct user_struct *user;
+       const struct cred *cred = current_cred();
        int error = -EINVAL;
        struct pid *pgrp;
 
@@ -174,18 +180,18 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
                        } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
                        break;
                case PRIO_USER:
-                       user = current->user;
+                       user = (struct user_struct *) cred->user;
                        if (!who)
-                               who = current->uid;
-                       else
-                               if ((who != current->uid) && !(user = find_user(who)))
-                                       goto out_unlock;        /* No processes for this user */
+                               who = cred->uid;
+                       else if ((who != cred->uid) &&
+                                !(user = find_user(who)))
+                               goto out_unlock;        /* No processes for this user */
 
                        do_each_thread(g, p)
-                               if (p->uid == who)
+                               if (__task_cred(p)->uid == who)
                                        error = set_one_prio(p, niceval, error);
                        while_each_thread(g, p);
-                       if (who != current->uid)
+                       if (who != cred->uid)
                                free_uid(user);         /* For find_user() */
                        break;
        }
@@ -205,6 +211,7 @@ asmlinkage long sys_getpriority(int which, int who)
 {
        struct task_struct *g, *p;
        struct user_struct *user;
+       const struct cred *cred = current_cred();
        long niceval, retval = -ESRCH;
        struct pid *pgrp;
 
@@ -236,21 +243,21 @@ asmlinkage long sys_getpriority(int which, int who)
                        } while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
                        break;
                case PRIO_USER:
-                       user = current->user;
+                       user = (struct user_struct *) cred->user;
                        if (!who)
-                               who = current->uid;
-                       else
-                               if ((who != current->uid) && !(user = find_user(who)))
-                                       goto out_unlock;        /* No processes for this user */
+                               who = cred->uid;
+                       else if ((who != cred->uid) &&
+                                !(user = find_user(who)))
+                               goto out_unlock;        /* No processes for this user */
 
                        do_each_thread(g, p)
-                               if (p->uid == who) {
+                               if (__task_cred(p)->uid == who) {
                                        niceval = 20 - task_nice(p);
                                        if (niceval > retval)
                                                retval = niceval;
                                }
                        while_each_thread(g, p);
-                       if (who != current->uid)
+                       if (who != cred->uid)
                                free_uid(user);         /* for find_user() */
                        break;
        }
@@ -472,46 +479,48 @@ void ctrl_alt_del(void)
  */
 asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
 {
-       int old_rgid = current->gid;
-       int old_egid = current->egid;
-       int new_rgid = old_rgid;
-       int new_egid = old_egid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
        if (retval)
-               return retval;
+               goto error;
 
+       retval = -EPERM;
        if (rgid != (gid_t) -1) {
-               if ((old_rgid == rgid) ||
-                   (current->egid==rgid) ||
+               if (old->gid == rgid ||
+                   old->egid == rgid ||
                    capable(CAP_SETGID))
-                       new_rgid = rgid;
+                       new->gid = rgid;
                else
-                       return -EPERM;
+                       goto error;
        }
        if (egid != (gid_t) -1) {
-               if ((old_rgid == egid) ||
-                   (current->egid == egid) ||
-                   (current->sgid == egid) ||
+               if (old->gid == egid ||
+                   old->egid == egid ||
+                   old->sgid == egid ||
                    capable(CAP_SETGID))
-                       new_egid = egid;
+                       new->egid = egid;
                else
-                       return -EPERM;
-       }
-       if (new_egid != old_egid) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
+                       goto error;
        }
+
        if (rgid != (gid_t) -1 ||
-           (egid != (gid_t) -1 && egid != old_rgid))
-               current->sgid = new_egid;
-       current->fsgid = new_egid;
-       current->egid = new_egid;
-       current->gid = new_rgid;
-       key_fsgid_changed(current);
-       proc_id_connector(current, PROC_EVENT_GID);
-       return 0;
+           (egid != (gid_t) -1 && egid != old->gid))
+               new->sgid = new->egid;
+       new->fsgid = new->egid;
+
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
 
 /*
@@ -521,56 +530,54 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
  */
 asmlinkage long sys_setgid(gid_t gid)
 {
-       int old_egid = current->egid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
        if (retval)
-               return retval;
+               goto error;
 
-       if (capable(CAP_SETGID)) {
-               if (old_egid != gid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->gid = current->egid = current->sgid = current->fsgid = gid;
-       } else if ((gid == current->gid) || (gid == current->sgid)) {
-               if (old_egid != gid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->egid = current->fsgid = gid;
-       }
+       retval = -EPERM;
+       if (capable(CAP_SETGID))
+               new->gid = new->egid = new->sgid = new->fsgid = gid;
+       else if (gid == old->gid || gid == old->sgid)
+               new->egid = new->fsgid = gid;
        else
-               return -EPERM;
+               goto error;
 
-       key_fsgid_changed(current);
-       proc_id_connector(current, PROC_EVENT_GID);
-       return 0;
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
   
-static int set_user(uid_t new_ruid, int dumpclear)
+/*
+ * change the user struct in a credentials set to match the new UID
+ */
+static int set_user(struct cred *new)
 {
        struct user_struct *new_user;
 
-       new_user = alloc_uid(current->nsproxy->user_ns, new_ruid);
+       new_user = alloc_uid(current_user_ns(), new->uid);
        if (!new_user)
                return -EAGAIN;
 
        if (atomic_read(&new_user->processes) >=
                                current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
-                       new_user != current->nsproxy->user_ns->root_user) {
+                       new_user != INIT_USER) {
                free_uid(new_user);
                return -EAGAIN;
        }
 
-       switch_uid(new_user);
-
-       if (dumpclear) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
-       }
-       current->uid = new_ruid;
+       free_uid(new->user);
+       new->user = new_user;
        return 0;
 }
 
@@ -591,54 +598,56 @@ static int set_user(uid_t new_ruid, int dumpclear)
  */
 asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
 {
-       int old_ruid, old_euid, old_suid, new_ruid, new_euid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
        if (retval)
-               return retval;
-
-       new_ruid = old_ruid = current->uid;
-       new_euid = old_euid = current->euid;
-       old_suid = current->suid;
+               goto error;
 
+       retval = -EPERM;
        if (ruid != (uid_t) -1) {
-               new_ruid = ruid;
-               if ((old_ruid != ruid) &&
-                   (current->euid != ruid) &&
+               new->uid = ruid;
+               if (old->uid != ruid &&
+                   old->euid != ruid &&
                    !capable(CAP_SETUID))
-                       return -EPERM;
+                       goto error;
        }
 
        if (euid != (uid_t) -1) {
-               new_euid = euid;
-               if ((old_ruid != euid) &&
-                   (current->euid != euid) &&
-                   (current->suid != euid) &&
+               new->euid = euid;
+               if (old->uid != euid &&
+                   old->euid != euid &&
+                   old->suid != euid &&
                    !capable(CAP_SETUID))
-                       return -EPERM;
+                       goto error;
        }
 
-       if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
-               return -EAGAIN;
+       retval = -EAGAIN;
+       if (new->uid != old->uid && set_user(new) < 0)
+               goto error;
 
-       if (new_euid != old_euid) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
-       }
-       current->fsuid = current->euid = new_euid;
        if (ruid != (uid_t) -1 ||
-           (euid != (uid_t) -1 && euid != old_ruid))
-               current->suid = current->euid;
-       current->fsuid = current->euid;
+           (euid != (uid_t) -1 && euid != old->uid))
+               new->suid = new->euid;
+       new->fsuid = new->euid;
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
-
-       return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
-}
+       retval = security_task_fix_setuid(new, old, LSM_SETID_RE);
+       if (retval < 0)
+               goto error;
 
+       return commit_creds(new);
 
+error:
+       abort_creds(new);
+       return retval;
+}
                
 /*
  * setuid() is implemented like SysV with SAVED_IDS 
@@ -653,36 +662,41 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
  */
 asmlinkage long sys_setuid(uid_t uid)
 {
-       int old_euid = current->euid;
-       int old_ruid, old_suid, new_suid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
        if (retval)
-               return retval;
+               goto error;
 
-       old_ruid = current->uid;
-       old_suid = current->suid;
-       new_suid = old_suid;
-       
+       retval = -EPERM;
        if (capable(CAP_SETUID)) {
-               if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
-                       return -EAGAIN;
-               new_suid = uid;
-       } else if ((uid != current->uid) && (uid != new_suid))
-               return -EPERM;
-
-       if (old_euid != uid) {
-               set_dumpable(current->mm, suid_dumpable);
-               smp_wmb();
+               new->suid = new->uid = uid;
+               if (uid != old->uid && set_user(new) < 0) {
+                       retval = -EAGAIN;
+                       goto error;
+               }
+       } else if (uid != old->uid && uid != new->suid) {
+               goto error;
        }
-       current->fsuid = current->euid = uid;
-       current->suid = new_suid;
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
+       new->fsuid = new->euid = uid;
+
+       retval = security_task_fix_setuid(new, old, LSM_SETID_ID);
+       if (retval < 0)
+               goto error;
+
+       return commit_creds(new);
 
-       return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
+error:
+       abort_creds(new);
+       return retval;
 }
 
 
@@ -692,54 +706,63 @@ asmlinkage long sys_setuid(uid_t uid)
  */
 asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
 {
-       int old_ruid = current->uid;
-       int old_euid = current->euid;
-       int old_suid = current->suid;
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
        retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
        if (retval)
-               return retval;
+               goto error;
+       old = current_cred();
 
+       retval = -EPERM;
        if (!capable(CAP_SETUID)) {
-               if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
-                   (ruid != current->euid) && (ruid != current->suid))
-                       return -EPERM;
-               if ((euid != (uid_t) -1) && (euid != current->uid) &&
-                   (euid != current->euid) && (euid != current->suid))
-                       return -EPERM;
-               if ((suid != (uid_t) -1) && (suid != current->uid) &&
-                   (suid != current->euid) && (suid != current->suid))
-                       return -EPERM;
+               if (ruid != (uid_t) -1 && ruid != old->uid &&
+                   ruid != old->euid  && ruid != old->suid)
+                       goto error;
+               if (euid != (uid_t) -1 && euid != old->uid &&
+                   euid != old->euid  && euid != old->suid)
+                       goto error;
+               if (suid != (uid_t) -1 && suid != old->uid &&
+                   suid != old->euid  && suid != old->suid)
+                       goto error;
        }
+
+       retval = -EAGAIN;
        if (ruid != (uid_t) -1) {
-               if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
-                       return -EAGAIN;
-       }
-       if (euid != (uid_t) -1) {
-               if (euid != current->euid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->euid = euid;
+               new->uid = ruid;
+               if (ruid != old->uid && set_user(new) < 0)
+                       goto error;
        }
-       current->fsuid = current->euid;
+       if (euid != (uid_t) -1)
+               new->euid = euid;
        if (suid != (uid_t) -1)
-               current->suid = suid;
+               new->suid = suid;
+       new->fsuid = new->euid;
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
+       retval = security_task_fix_setuid(new, old, LSM_SETID_RES);
+       if (retval < 0)
+               goto error;
 
-       return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
 
 asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid)
 {
+       const struct cred *cred = current_cred();
        int retval;
 
-       if (!(retval = put_user(current->uid, ruid)) &&
-           !(retval = put_user(current->euid, euid)))
-               retval = put_user(current->suid, suid);
+       if (!(retval   = put_user(cred->uid,  ruid)) &&
+           !(retval   = put_user(cred->euid, euid)))
+               retval = put_user(cred->suid, suid);
 
        return retval;
 }
@@ -749,48 +772,55 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us
  */
 asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
 {
+       const struct cred *old;
+       struct cred *new;
        int retval;
 
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+       old = current_cred();
+
        retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
        if (retval)
-               return retval;
+               goto error;
 
+       retval = -EPERM;
        if (!capable(CAP_SETGID)) {
-               if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
-                   (rgid != current->egid) && (rgid != current->sgid))
-                       return -EPERM;
-               if ((egid != (gid_t) -1) && (egid != current->gid) &&
-                   (egid != current->egid) && (egid != current->sgid))
-                       return -EPERM;
-               if ((sgid != (gid_t) -1) && (sgid != current->gid) &&
-                   (sgid != current->egid) && (sgid != current->sgid))
-                       return -EPERM;
+               if (rgid != (gid_t) -1 && rgid != old->gid &&
+                   rgid != old->egid  && rgid != old->sgid)
+                       goto error;
+               if (egid != (gid_t) -1 && egid != old->gid &&
+                   egid != old->egid  && egid != old->sgid)
+                       goto error;
+               if (sgid != (gid_t) -1 && sgid != old->gid &&
+                   sgid != old->egid  && sgid != old->sgid)
+                       goto error;
        }
-       if (egid != (gid_t) -1) {
-               if (egid != current->egid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
-               }
-               current->egid = egid;
-       }
-       current->fsgid = current->egid;
+
        if (rgid != (gid_t) -1)
-               current->gid = rgid;
+               new->gid = rgid;
+       if (egid != (gid_t) -1)
+               new->egid = egid;
        if (sgid != (gid_t) -1)
-               current->sgid = sgid;
+               new->sgid = sgid;
+       new->fsgid = new->egid;
 
-       key_fsgid_changed(current);
-       proc_id_connector(current, PROC_EVENT_GID);
-       return 0;
+       return commit_creds(new);
+
+error:
+       abort_creds(new);
+       return retval;
 }
 
 asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid)
 {
+       const struct cred *cred = current_cred();
        int retval;
 
-       if (!(retval = put_user(current->gid, rgid)) &&
-           !(retval = put_user(current->egid, egid)))
-               retval = put_user(current->sgid, sgid);
+       if (!(retval   = put_user(cred->gid,  rgid)) &&
+           !(retval   = put_user(cred->egid, egid)))
+               retval = put_user(cred->sgid, sgid);
 
        return retval;
 }
@@ -804,27 +834,35 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us
  */
 asmlinkage long sys_setfsuid(uid_t uid)
 {
-       int old_fsuid;
+       const struct cred *old;
+       struct cred *new;
+       uid_t old_fsuid;
+
+       new = prepare_creds();
+       if (!new)
+               return current_fsuid();
+       old = current_cred();
+       old_fsuid = old->fsuid;
 
-       old_fsuid = current->fsuid;
-       if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS))
-               return old_fsuid;
+       if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0)
+               goto error;
 
-       if (uid == current->uid || uid == current->euid ||
-           uid == current->suid || uid == current->fsuid || 
+       if (uid == old->uid  || uid == old->euid  ||
+           uid == old->suid || uid == old->fsuid ||
            capable(CAP_SETUID)) {
                if (uid != old_fsuid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
+                       new->fsuid = uid;
+                       if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
+                               goto change_okay;
                }
-               current->fsuid = uid;
        }
 
-       key_fsuid_changed(current);
-       proc_id_connector(current, PROC_EVENT_UID);
-
-       security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
+error:
+       abort_creds(new);
+       return old_fsuid;
 
+change_okay:
+       commit_creds(new);
        return old_fsuid;
 }
 
@@ -833,58 +871,59 @@ asmlinkage long sys_setfsuid(uid_t uid)
  */
 asmlinkage long sys_setfsgid(gid_t gid)
 {
-       int old_fsgid;
+       const struct cred *old;
+       struct cred *new;
+       gid_t old_fsgid;
+
+       new = prepare_creds();
+       if (!new)
+               return current_fsgid();
+       old = current_cred();
+       old_fsgid = old->fsgid;
 
-       old_fsgid = current->fsgid;
        if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS))
-               return old_fsgid;
+               goto error;
 
-       if (gid == current->gid || gid == current->egid ||
-           gid == current->sgid || gid == current->fsgid || 
+       if (gid == old->gid  || gid == old->egid  ||
+           gid == old->sgid || gid == old->fsgid ||
            capable(CAP_SETGID)) {
                if (gid != old_fsgid) {
-                       set_dumpable(current->mm, suid_dumpable);
-                       smp_wmb();
+                       new->fsgid = gid;
+                       goto change_okay;
                }
-               current->fsgid = gid;
-               key_fsgid_changed(current);
-               proc_id_connector(current, PROC_EVENT_GID);
        }
+
+error:
+       abort_creds(new);
+       return old_fsgid;
+
+change_okay:
+       commit_creds(new);
        return old_fsgid;
 }
 
+void do_sys_times(struct tms *tms)
+{
+       struct task_cputime cputime;
+       cputime_t cutime, cstime;
+
+       thread_group_cputime(current, &cputime);
+       spin_lock_irq(&current->sighand->siglock);
+       cutime = current->signal->cutime;
+       cstime = current->signal->cstime;
+       spin_unlock_irq(&current->sighand->siglock);
+       tms->tms_utime = cputime_to_clock_t(cputime.utime);
+       tms->tms_stime = cputime_to_clock_t(cputime.stime);
+       tms->tms_cutime = cputime_to_clock_t(cutime);
+       tms->tms_cstime = cputime_to_clock_t(cstime);
+}
+
 asmlinkage long sys_times(struct tms __user * tbuf)
 {
-       /*
-        *      In the SMP world we might just be unlucky and have one of
-        *      the times increment as we use it. Since the value is an
-        *      atomically safe type this is just fine. Conceptually its
-        *      as if the syscall took an instant longer to occur.
-        */
        if (tbuf) {
                struct tms tmp;
-               struct task_struct *tsk = current;
-               struct task_struct *t;
-               cputime_t utime, stime, cutime, cstime;
-
-               spin_lock_irq(&tsk->sighand->siglock);
-               utime = tsk->signal->utime;
-               stime = tsk->signal->stime;
-               t = tsk;
-               do {
-                       utime = cputime_add(utime, t->utime);
-                       stime = cputime_add(stime, t->stime);
-                       t = next_thread(t);
-               } while (t != tsk);
-
-               cutime = tsk->signal->cutime;
-               cstime = tsk->signal->cstime;
-               spin_unlock_irq(&tsk->sighand->siglock);
-
-               tmp.tms_utime = cputime_to_clock_t(utime);
-               tmp.tms_stime = cputime_to_clock_t(stime);
-               tmp.tms_cutime = cputime_to_clock_t(cutime);
-               tmp.tms_cstime = cputime_to_clock_t(cstime);
+
+               do_sys_times(&tmp);
                if (copy_to_user(tbuf, &tmp, sizeof(struct tms)))
                        return -EFAULT;
        }
@@ -1128,7 +1167,7 @@ EXPORT_SYMBOL(groups_free);
 
 /* export the group_info to a user-space array */
 static int groups_to_user(gid_t __user *grouplist,
-    struct group_info *group_info)
+                         const struct group_info *group_info)
 {
        int i;
        unsigned int count = group_info->ngroups;
@@ -1196,7 +1235,7 @@ static void groups_sort(struct group_info *group_info)
 }
 
 /* a simple bsearch */
-int groups_search(struct group_info *group_info, gid_t grp)
+int groups_search(const struct group_info *group_info, gid_t grp)
 {
        unsigned int left, right;
 
@@ -1218,51 +1257,74 @@ int groups_search(struct group_info *group_info, gid_t grp)
        return 0;
 }
 
-/* validate and set current->group_info */
-int set_current_groups(struct group_info *group_info)
+/**
+ * set_groups - Change a group subscription in a set of credentials
+ * @new: The newly prepared set of credentials to alter
+ * @group_info: The group list to install
+ *
+ * Validate a group subscription and, if valid, insert it into a set
+ * of credentials.
+ */
+int set_groups(struct cred *new, struct group_info *group_info)
 {
        int retval;
-       struct group_info *old_info;
 
        retval = security_task_setgroups(group_info);
        if (retval)
                return retval;
 
+       put_group_info(new->group_info);
        groups_sort(group_info);
        get_group_info(group_info);
+       new->group_info = group_info;
+       return 0;
+}
 
-       task_lock(current);
-       old_info = current->group_info;
-       current->group_info = group_info;
-       task_unlock(current);
+EXPORT_SYMBOL(set_groups);
 
-       put_group_info(old_info);
+/**
+ * set_current_groups - Change current's group subscription
+ * @group_info: The group list to impose
+ *
+ * Validate a group subscription and, if valid, impose it upon current's task
+ * security record.
+ */
+int set_current_groups(struct group_info *group_info)
+{
+       struct cred *new;
+       int ret;
 
-       return 0;
+       new = prepare_creds();
+       if (!new)
+               return -ENOMEM;
+
+       ret = set_groups(new, group_info);
+       if (ret < 0) {
+               abort_creds(new);
+               return ret;
+       }
+
+       return commit_creds(new);
 }
 
 EXPORT_SYMBOL(set_current_groups);
 
 asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist)
 {
-       int i = 0;
-
-       /*
-        *      SMP: Nobody else can change our grouplist. Thus we are
-        *      safe.
-        */
+       const struct cred *cred = current_cred();
+       int i;
 
        if (gidsetsize < 0)
                return -EINVAL;
 
        /* no need to grab task_lock here; it cannot change */
-       i = current->group_info->ngroups;
+       i = cred->group_info->ngroups;
        if (gidsetsize) {
                if (i > gidsetsize) {
                        i = -EINVAL;
                        goto out;
                }
-               if (groups_to_user(grouplist, current->group_info)) {
+               if (groups_to_user(grouplist, cred->group_info)) {
                        i = -EFAULT;
                        goto out;
                }
@@ -1306,9 +1368,11 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
  */
 int in_group_p(gid_t grp)
 {
+       const struct cred *cred = current_cred();
        int retval = 1;
-       if (grp != current->fsgid)
-               retval = groups_search(current->group_info, grp);
+
+       if (grp != cred->fsgid)
+               retval = groups_search(cred->group_info, grp);
        return retval;
 }
 
@@ -1316,9 +1380,11 @@ EXPORT_SYMBOL(in_group_p);
 
 int in_egroup_p(gid_t grp)
 {
+       const struct cred *cred = current_cred();
        int retval = 1;
-       if (grp != current->egid)
-               retval = groups_search(current->group_info, grp);
+
+       if (grp != cred->egid)
+               retval = groups_search(cred->group_info, grp);
        return retval;
 }
 
@@ -1349,9 +1415,10 @@ asmlinkage long sys_sethostname(char __user *name, int len)
        down_write(&uts_sem);
        errno = -EFAULT;
        if (!copy_from_user(tmp, name, len)) {
-               memcpy(utsname()->nodename, tmp, len);
-               memset(utsname()->nodename + len, 0,
-                       sizeof(utsname()->nodename) - len);
+               struct new_utsname *u = utsname();
+
+               memcpy(u->nodename, tmp, len);
+               memset(u->nodename + len, 0, sizeof(u->nodename) - len);
                errno = 0;
        }
        up_write(&uts_sem);
@@ -1363,15 +1430,17 @@ asmlinkage long sys_sethostname(char __user *name, int len)
 asmlinkage long sys_gethostname(char __user *name, int len)
 {
        int i, errno;
+       struct new_utsname *u;
 
        if (len < 0)
                return -EINVAL;
        down_read(&uts_sem);
-       i = 1 + strlen(utsname()->nodename);
+       u = utsname();
+       i = 1 + strlen(u->nodename);
        if (i > len)
                i = len;
        errno = 0;
-       if (copy_to_user(name, utsname()->nodename, i))
+       if (copy_to_user(name, u->nodename, i))
                errno = -EFAULT;
        up_read(&uts_sem);
        return errno;
@@ -1396,9 +1465,10 @@ asmlinkage long sys_setdomainname(char __user *name, int len)
        down_write(&uts_sem);
        errno = -EFAULT;
        if (!copy_from_user(tmp, name, len)) {
-               memcpy(utsname()->domainname, tmp, len);
-               memset(utsname()->domainname + len, 0,
-                       sizeof(utsname()->domainname) - len);
+               struct new_utsname *u = utsname();
+
+               memcpy(u->domainname, tmp, len);
+               memset(u->domainname + len, 0, sizeof(u->domainname) - len);
                errno = 0;
        }
        up_write(&uts_sem);
@@ -1445,7 +1515,6 @@ asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit __user *r
 asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim)
 {
        struct rlimit new_rlim, *old_rlim;
-       unsigned long it_prof_secs;
        int retval;
 
        if (resource >= RLIM_NLIMITS)
@@ -1499,18 +1568,7 @@ asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit __user *rlim)
        if (new_rlim.rlim_cur == RLIM_INFINITY)
                goto out;
 
-       it_prof_secs = cputime_to_secs(current->signal->it_prof_expires);
-       if (it_prof_secs == 0 || new_rlim.rlim_cur <= it_prof_secs) {
-               unsigned long rlim_cur = new_rlim.rlim_cur;
-               cputime_t cputime;
-
-               cputime = secs_to_cputime(rlim_cur);
-               read_lock(&tasklist_lock);
-               spin_lock_irq(&current->sighand->siglock);
-               set_process_cpu_timer(current, CPUCLOCK_PROF, &cputime, NULL);
-               spin_unlock_irq(&current->sighand->siglock);
-               read_unlock(&tasklist_lock);
-       }
+       update_rlimit_cpu(new_rlim.rlim_cur);
 out:
        return 0;
 }
@@ -1548,11 +1606,8 @@ out:
  *
  */
 
-static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r,
-                                    cputime_t *utimep, cputime_t *stimep)
+static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r)
 {
-       *utimep = cputime_add(*utimep, t->utime);
-       *stimep = cputime_add(*stimep, t->stime);
        r->ru_nvcsw += t->nvcsw;
        r->ru_nivcsw += t->nivcsw;
        r->ru_minflt += t->min_flt;
@@ -1566,12 +1621,13 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
        struct task_struct *t;
        unsigned long flags;
        cputime_t utime, stime;
+       struct task_cputime cputime;
 
        memset((char *) r, 0, sizeof *r);
        utime = stime = cputime_zero;
 
        if (who == RUSAGE_THREAD) {
-               accumulate_thread_rusage(p, r, &utime, &stime);
+               accumulate_thread_rusage(p, r);
                goto out;
        }
 
@@ -1594,8 +1650,9 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
                                break;
 
                case RUSAGE_SELF:
-                       utime = cputime_add(utime, p->signal->utime);
-                       stime = cputime_add(stime, p->signal->stime);
+                       thread_group_cputime(p, &cputime);
+                       utime = cputime_add(utime, cputime.utime);
+                       stime = cputime_add(stime, cputime.stime);
                        r->ru_nvcsw += p->signal->nvcsw;
                        r->ru_nivcsw += p->signal->nivcsw;
                        r->ru_minflt += p->signal->min_flt;
@@ -1604,7 +1661,7 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
                        r->ru_oublock += p->signal->oublock;
                        t = p;
                        do {
-                               accumulate_thread_rusage(t, r, &utime, &stime);
+                               accumulate_thread_rusage(t, r);
                                t = next_thread(t);
                        } while (t != p);
                        break;
@@ -1643,50 +1700,56 @@ asmlinkage long sys_umask(int mask)
 asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
                          unsigned long arg4, unsigned long arg5)
 {
-       long error = 0;
+       struct task_struct *me = current;
+       unsigned char comm[sizeof(me->comm)];
+       long error;
 
-       if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error))
+       error = security_task_prctl(option, arg2, arg3, arg4, arg5);
+       if (error != -ENOSYS)
                return error;
 
+       error = 0;
        switch (option) {
                case PR_SET_PDEATHSIG:
                        if (!valid_signal(arg2)) {
                                error = -EINVAL;
                                break;
                        }
-                       current->pdeath_signal = arg2;
+                       me->pdeath_signal = arg2;
+                       error = 0;
                        break;
                case PR_GET_PDEATHSIG:
-                       error = put_user(current->pdeath_signal, (int __user *)arg2);
+                       error = put_user(me->pdeath_signal, (int __user *)arg2);
                        break;
                case PR_GET_DUMPABLE:
-                       error = get_dumpable(current->mm);
+                       error = get_dumpable(me->mm);
                        break;
                case PR_SET_DUMPABLE:
                        if (arg2 < 0 || arg2 > 1) {
                                error = -EINVAL;
                                break;
                        }
-                       set_dumpable(current->mm, arg2);
+                       set_dumpable(me->mm, arg2);
+                       error = 0;
                        break;
 
                case PR_SET_UNALIGN:
-                       error = SET_UNALIGN_CTL(current, arg2);
+                       error = SET_UNALIGN_CTL(me, arg2);
                        break;
                case PR_GET_UNALIGN:
-                       error = GET_UNALIGN_CTL(current, arg2);
+                       error = GET_UNALIGN_CTL(me, arg2);
                        break;
                case PR_SET_FPEMU:
-                       error = SET_FPEMU_CTL(current, arg2);
+                       error = SET_FPEMU_CTL(me, arg2);
                        break;
                case PR_GET_FPEMU:
-                       error = GET_FPEMU_CTL(current, arg2);
+                       error = GET_FPEMU_CTL(me, arg2);
                        break;
                case PR_SET_FPEXC:
-                       error = SET_FPEXC_CTL(current, arg2);
+                       error = SET_FPEXC_CTL(me, arg2);
                        break;
                case PR_GET_FPEXC:
-                       error = GET_FPEXC_CTL(current, arg2);
+                       error = GET_FPEXC_CTL(me, arg2);
                        break;
                case PR_GET_TIMING:
                        error = PR_TIMING_STATISTICAL;
@@ -1694,33 +1757,28 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
                case PR_SET_TIMING:
                        if (arg2 != PR_TIMING_STATISTICAL)
                                error = -EINVAL;
+                       else
+                               error = 0;
                        break;
 
-               case PR_SET_NAME: {
-                       struct task_struct *me = current;
-                       unsigned char ncomm[sizeof(me->comm)];
-
-                       ncomm[sizeof(me->comm)-1] = 0;
-                       if (strncpy_from_user(ncomm, (char __user *)arg2,
-                                               sizeof(me->comm)-1) < 0)
+               case PR_SET_NAME:
+                       comm[sizeof(me->comm)-1] = 0;
+                       if (strncpy_from_user(comm, (char __user *)arg2,
+                                             sizeof(me->comm) - 1) < 0)
                                return -EFAULT;
-                       set_task_comm(me, ncomm);
+                       set_task_comm(me, comm);
                        return 0;
-               }
-               case PR_GET_NAME: {
-                       struct task_struct *me = current;
-                       unsigned char tcomm[sizeof(me->comm)];
-
-                       get_task_comm(tcomm, me);
-                       if (copy_to_user((char __user *)arg2, tcomm, sizeof(tcomm)))
+               case PR_GET_NAME:
+                       get_task_comm(comm, me);
+                       if (copy_to_user((char __user *)arg2, comm,
+                                        sizeof(comm)))
                                return -EFAULT;
                        return 0;
-               }
                case PR_GET_ENDIAN:
-                       error = GET_ENDIAN(current, arg2);
+                       error = GET_ENDIAN(me, arg2);
                        break;
                case PR_SET_ENDIAN:
-                       error = SET_ENDIAN(current, arg2);
+                       error = SET_ENDIAN(me, arg2);
                        break;
 
                case PR_GET_SECCOMP:
@@ -1735,6 +1793,17 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
                case PR_SET_TSC:
                        error = SET_TSC_CTL(arg2);
                        break;
+               case PR_GET_TIMERSLACK:
+                       error = current->timer_slack_ns;
+                       break;
+               case PR_SET_TIMERSLACK:
+                       if (arg2 <= 0)
+                               current->timer_slack_ns =
+                                       current->default_timer_slack_ns;
+                       else
+                               current->timer_slack_ns = arg2;
+                       error = 0;
+                       break;
                default:
                        error = -EINVAL;
                        break;