KEYS: Do preallocation for __key_link()

Do preallocation for __key_link() so that the various callers in request_key.c
can deal with any errors from this source before attempting to construct a key.
This allows them to assume that the actual linkage step is guaranteed to be
successful.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index ac49c8a..f656e9c 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -299,6 +299,7 @@
 			       struct key_user *user,
 			       struct key **_key)
 {
+	struct keyring_list *prealloc;
 	const struct cred *cred = current_cred();
 	struct key *key;
 	key_ref_t key_ref;
@@ -306,6 +307,7 @@
 
 	kenter("%s,%s,,,", type->name, description);
 
+	*_key = NULL;
 	mutex_lock(&user->cons_lock);
 
 	key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
@@ -315,8 +317,12 @@
 
 	set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
 
-	if (dest_keyring)
-		down_write(&dest_keyring->sem);
+	if (dest_keyring) {
+		ret = __key_link_begin(dest_keyring, type, description,
+				       &prealloc);
+		if (ret < 0)
+			goto link_prealloc_failed;
+	}
 
 	/* attach the key to the destination keyring under lock, but we do need
 	 * to do another check just in case someone beat us to it whilst we
@@ -328,11 +334,11 @@
 		goto key_already_present;
 
 	if (dest_keyring)
-		__key_link(dest_keyring, key);
+		__key_link(dest_keyring, key, &prealloc);
 
 	mutex_unlock(&key_construction_mutex);
 	if (dest_keyring)
-		up_write(&dest_keyring->sem);
+		__key_link_end(dest_keyring, type, prealloc);
 	mutex_unlock(&user->cons_lock);
 	*_key = key;
 	kleave(" = 0 [%d]", key_serial(key));
@@ -341,27 +347,36 @@
 	/* the key is now present - we tell the caller that we found it by
 	 * returning -EINPROGRESS  */
 key_already_present:
+	key_put(key);
 	mutex_unlock(&key_construction_mutex);
-	ret = 0;
+	key = key_ref_to_ptr(key_ref);
 	if (dest_keyring) {
-		ret = __key_link(dest_keyring, key_ref_to_ptr(key_ref));
-		up_write(&dest_keyring->sem);
+		ret = __key_link_check_live_key(dest_keyring, key);
+		if (ret == 0)
+			__key_link(dest_keyring, key, &prealloc);
+		__key_link_end(dest_keyring, type, prealloc);
+		if (ret < 0)
+			goto link_check_failed;
 	}
 	mutex_unlock(&user->cons_lock);
-	key_put(key);
-	if (ret < 0) {
-		key_ref_put(key_ref);
-		*_key = NULL;
-		kleave(" = %d [link]", ret);
-		return ret;
-	}
-	*_key = key = key_ref_to_ptr(key_ref);
+	*_key = key;
 	kleave(" = -EINPROGRESS [%d]", key_serial(key));
 	return -EINPROGRESS;
 
+link_check_failed:
+	mutex_unlock(&user->cons_lock);
+	key_put(key);
+	kleave(" = %d [linkcheck]", ret);
+	return ret;
+
+link_prealloc_failed:
+	up_write(&dest_keyring->sem);
+	mutex_unlock(&user->cons_lock);
+	kleave(" = %d [prelink]", ret);
+	return ret;
+
 alloc_failed:
 	mutex_unlock(&user->cons_lock);
-	*_key = NULL;
 	kleave(" = %ld", PTR_ERR(key));
 	return PTR_ERR(key);
 }