tools headers UAPI: Sync linux/kvm.h with the kernel sources
[linux-2.6-microblaze.git] / kernel / cred.c
index 421b114..e6fd2b3 100644 (file)
@@ -33,7 +33,7 @@ do {                                                                  \
 static struct kmem_cache *cred_jar;
 
 /* init to 2 - one for init_task, one to ensure it is never freed */
-struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
+static struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
 
 /*
  * The initial credentials for the initial task
@@ -60,6 +60,7 @@ struct cred init_cred = {
        .user                   = INIT_USER,
        .user_ns                = &init_user_ns,
        .group_info             = &init_groups,
+       .ucounts                = &init_ucounts,
 };
 
 static inline void set_cred_subscribers(struct cred *cred, int n)
@@ -119,6 +120,8 @@ static void put_cred_rcu(struct rcu_head *rcu)
        if (cred->group_info)
                put_group_info(cred->group_info);
        free_uid(cred->user);
+       if (cred->ucounts)
+               put_ucounts(cred->ucounts);
        put_user_ns(cred->user_ns);
        kmem_cache_free(cred_jar, cred);
 }
@@ -222,6 +225,7 @@ struct cred *cred_alloc_blank(void)
 #ifdef CONFIG_DEBUG_CREDENTIALS
        new->magic = CRED_MAGIC;
 #endif
+       new->ucounts = get_ucounts(&init_ucounts);
 
        if (security_cred_alloc_blank(new, GFP_KERNEL_ACCOUNT) < 0)
                goto error;
@@ -284,6 +288,11 @@ struct cred *prepare_creds(void)
 
        if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
                goto error;
+
+       new->ucounts = get_ucounts(new->ucounts);
+       if (!new->ucounts)
+               goto error;
+
        validate_creds(new);
        return new;
 
@@ -351,7 +360,7 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
                kdebug("share_creds(%p{%d,%d})",
                       p->cred, atomic_read(&p->cred->usage),
                       read_cred_subscribers(p->cred));
-               atomic_inc(&p->cred->user->processes);
+               inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1);
                return 0;
        }
 
@@ -363,6 +372,9 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
                ret = create_user_ns(new);
                if (ret < 0)
                        goto error_put;
+               ret = set_cred_ucounts(new);
+               if (ret < 0)
+                       goto error_put;
        }
 
 #ifdef CONFIG_KEYS
@@ -384,8 +396,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
        }
 #endif
 
-       atomic_inc(&new->user->processes);
        p->cred = p->real_cred = get_cred(new);
+       inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1);
        alter_cred_subscribers(new, 2);
        validate_creds(new);
        return 0;
@@ -485,12 +497,12 @@ int commit_creds(struct cred *new)
         * in set_user().
         */
        alter_cred_subscribers(new, 2);
-       if (new->user != old->user)
-               atomic_inc(&new->user->processes);
+       if (new->user != old->user || new->user_ns != old->user_ns)
+               inc_rlimit_ucounts(new->ucounts, UCOUNT_RLIMIT_NPROC, 1);
        rcu_assign_pointer(task->real_cred, new);
        rcu_assign_pointer(task->cred, new);
        if (new->user != old->user)
-               atomic_dec(&old->user->processes);
+               dec_rlimit_ucounts(old->ucounts, UCOUNT_RLIMIT_NPROC, 1);
        alter_cred_subscribers(old, -2);
 
        /* send notifications */
@@ -653,6 +665,31 @@ int cred_fscmp(const struct cred *a, const struct cred *b)
 }
 EXPORT_SYMBOL(cred_fscmp);
 
+int set_cred_ucounts(struct cred *new)
+{
+       struct task_struct *task = current;
+       const struct cred *old = task->real_cred;
+       struct ucounts *old_ucounts = new->ucounts;
+
+       if (new->user == old->user && new->user_ns == old->user_ns)
+               return 0;
+
+       /*
+        * This optimization is needed because alloc_ucounts() uses locks
+        * for table lookups.
+        */
+       if (old_ucounts && old_ucounts->ns == new->user_ns && uid_eq(old_ucounts->uid, new->euid))
+               return 0;
+
+       if (!(new->ucounts = alloc_ucounts(new->user_ns, new->euid)))
+               return -EAGAIN;
+
+       if (old_ucounts)
+               put_ucounts(old_ucounts);
+
+       return 0;
+}
+
 /*
  * initialise the credentials stuff
  */
@@ -719,6 +756,10 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
        if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
                goto error;
 
+       new->ucounts = get_ucounts(new->ucounts);
+       if (!new->ucounts)
+               goto error;
+
        put_cred(old);
        validate_creds(new);
        return new;