Linux: Use atomics for credential reference counts
[openafs.git] / src / afs / LINUX / osi_cred.c
index 266929c..f402a57 100644 (file)
 #include "afs/sysincludes.h"
 #include "afsincludes.h"
 
+/* Copy one credential structure to another, being careful about references */
+static inline void
+afs_copy_creds(cred_t *to_cred, const cred_t *from_cred) {
+    afs_set_cr_uid(to_cred, afs_cr_uid(from_cred));
+    afs_set_cr_gid(to_cred, afs_cr_gid(from_cred));
+    afs_set_cr_ruid(to_cred, afs_cr_ruid(from_cred));
+    afs_set_cr_rgid(to_cred, afs_cr_rgid(from_cred));
+    get_group_info(afs_cr_group_info(from_cred));
+    afs_set_cr_group_info(to_cred, afs_cr_group_info(from_cred));
+}
+
 cred_t *
 crget(void)
 {
@@ -27,24 +38,29 @@ crget(void)
 #define GFP_NOFS GFP_KERNEL
 #endif
     tmp = kmalloc(sizeof(cred_t), GFP_NOFS);
+    memset(tmp, 0, sizeof(cred_t));
     if (!tmp)
-       osi_Panic("crget: No more memory for creds!\n");
-    
-    tmp->cr_ref = 1;
+        osi_Panic("crget: No more memory for creds!\n");
+
+#if defined(STRUCT_TASK_HAS_CRED)
+    get_cred(tmp);
+#else
+    atomic_set(&tmp->cr_ref, 1);
+#endif
     return tmp;
 }
 
 void
 crfree(cred_t * cr)
 {
-    if (cr->cr_ref > 1) {
-       cr->cr_ref--;
-       return;
+#if defined(STRUCT_TASK_HAS_CRED)
+    put_cred(cr);
+#else
+    if (atomic_dec_and_test(&cr->cr_ref)) {
+        put_group_info(afs_cr_group_info(cr));
+        kfree(cr);
     }
-
-    put_group_info(cr->cr_group_info);
-
-    kfree(cr);
+#endif
 }
 
 
@@ -53,41 +69,46 @@ cred_t *
 crdup(cred_t * cr)
 {
     cred_t *tmp = crget();
+#if defined(STRUCT_TASK_HAS_CRED)
+    afs_copy_creds(tmp, cr);
+#else
+    afs_set_cr_uid(tmp, afs_cr_uid(cr));
+    afs_set_cr_ruid(tmp, afs_cr_ruid(cr));
+    afs_set_cr_gid(tmp, afs_cr_gid(cr));
+    afs_set_cr_rgid(tmp, afs_cr_rgid(cr));
 
-    tmp->cr_uid = cr->cr_uid;
-    tmp->cr_ruid = cr->cr_ruid;
-    tmp->cr_gid = cr->cr_gid;
-    tmp->cr_rgid = cr->cr_rgid;
-
-    get_group_info(cr->cr_group_info);
-    tmp->cr_group_info = cr->cr_group_info;
-
+    get_group_info(afs_cr_group_info(cr));
+    afs_set_cr_group_info(tmp, afs_cr_group_info(cr));
+#endif
     return tmp;
 }
 
 cred_t *
 crref(void)
 {
+#if defined(STRUCT_TASK_HAS_CRED)
+    return (cred_t *)get_current_cred();
+#else
     cred_t *cr = crget();
 
-    cr->cr_uid = current_fsuid();
-    cr->cr_ruid = current_uid();
-    cr->cr_gid = current_fsgid();
-    cr->cr_rgid = current_gid();
+    afs_set_cr_uid(cr, current_fsuid());
+    afs_set_cr_ruid(cr, current_uid());
+    afs_set_cr_gid(cr, current_fsgid());
+    afs_set_cr_rgid(cr, current_gid());
 
     task_lock(current);
     get_group_info(current_group_info());
-    cr->cr_group_info = current_group_info();
+    afs_set_cr_group_info(cr, current_group_info());
     task_unlock(current);
 
     return cr;
+#endif
 }
 
 /* Set the cred info into the current task */
 void
 crset(cred_t * cr)
 {
-    struct group_info *old_info;
 #if defined(STRUCT_TASK_HAS_CRED)
     struct cred *new_creds;
 
@@ -98,30 +119,24 @@ crset(cred_t * cr)
     if (current->cred != current->real_cred)
         return;
     new_creds = prepare_creds();
-    new_creds->fsuid = cr->cr_uid;
-    new_creds->uid = cr->cr_ruid;
-    new_creds->fsgid = cr->cr_gid;
-    new_creds->gid = cr->cr_rgid;
+    /* Drop the reference to group_info - we'll overwrite it in afs_copy_creds */
+    put_group_info(new_creds->group_info);
+    afs_copy_creds(new_creds, current_cred());
+
+    commit_creds(new_creds);
 #else
-    current->fsuid = cr->cr_uid;
-    current->uid = cr->cr_ruid;
-    current->fsgid = cr->cr_gid;
-    current->gid = cr->cr_rgid;
-#endif
+    struct group_info *old_info;
 
-    /* using set_current_groups() will sort the groups */
-    get_group_info(cr->cr_group_info);
+    current->fsuid = afs_cr_uid(cr);
+    current->uid = afs_cr_ruid(cr);
+    current->fsgid = afs_cr_gid(cr);
+    current->gid = afs_cr_rgid(cr);
 
+    get_group_info(afs_cr_group_info(cr));
     task_lock(current);
-#if defined(STRUCT_TASK_HAS_CRED)
-    old_info = current->cred->group_info;
-    new_creds->group_info = cr->cr_group_info;
-    commit_creds(new_creds);
-#else
     old_info = current->group_info;
-    current->group_info = cr->cr_group_info;
-#endif
+    current->group_info = afs_cr_group_info(cr);
     task_unlock(current);
-
     put_group_info(old_info);
+#endif
 }