diff options
author | James Morris <jmorris@namei.org> | 2009-09-11 08:04:49 +1000 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2009-09-11 08:04:49 +1000 |
commit | a3c8b97396ef42edfb845788ba6f53b2a93ce980 (patch) | |
tree | 530c5bdbc534618311dab3e0af245835af56db0f /security/keys/keyring.c | |
parent | 74fca6a42863ffacaf7ba6f1936a9f228950f657 (diff) | |
parent | 9f0ab4a3f0fdb1ff404d150618ace2fa069bb2e1 (diff) | |
download | linux-a3c8b97396ef42edfb845788ba6f53b2a93ce980.tar.bz2 |
Merge branch 'next' into for-linus
Diffstat (limited to 'security/keys/keyring.c')
-rw-r--r-- | security/keys/keyring.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 3dba81c2eba3..ac977f661a79 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1000,3 +1000,88 @@ static void keyring_revoke(struct key *keyring) } } /* end keyring_revoke() */ + +/* + * Determine whether a key is dead + */ +static bool key_is_dead(struct key *key, time_t limit) +{ + return test_bit(KEY_FLAG_DEAD, &key->flags) || + (key->expiry > 0 && key->expiry <= limit); +} + +/* + * Collect garbage from the contents of a keyring + */ +void keyring_gc(struct key *keyring, time_t limit) +{ + struct keyring_list *klist, *new; + struct key *key; + int loop, keep, max; + + kenter("%x", key_serial(keyring)); + + down_write(&keyring->sem); + + klist = keyring->payload.subscriptions; + if (!klist) + goto just_return; + + /* work out how many subscriptions we're keeping */ + keep = 0; + for (loop = klist->nkeys - 1; loop >= 0; loop--) + if (!key_is_dead(klist->keys[loop], limit)); + keep++; + + if (keep == klist->nkeys) + goto just_return; + + /* allocate a new keyring payload */ + max = roundup(keep, 4); + new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *), + GFP_KERNEL); + if (!new) + goto just_return; + new->maxkeys = max; + new->nkeys = 0; + new->delkey = 0; + + /* install the live keys + * - must take care as expired keys may be updated back to life + */ + keep = 0; + for (loop = klist->nkeys - 1; loop >= 0; loop--) { + key = klist->keys[loop]; + if (!key_is_dead(key, limit)) { + if (keep >= max) + goto discard_new; + new->keys[keep++] = key_get(key); + } + } + new->nkeys = keep; + + /* adjust the quota */ + key_payload_reserve(keyring, + sizeof(struct keyring_list) + + KEYQUOTA_LINK_BYTES * keep); + + if (keep == 0) { + rcu_assign_pointer(keyring->payload.subscriptions, NULL); + kfree(new); + } else { + rcu_assign_pointer(keyring->payload.subscriptions, new); + } + + up_write(&keyring->sem); + + call_rcu(&klist->rcu, keyring_clear_rcu_disposal); + kleave(" [yes]"); + return; + +discard_new: + new->nkeys = keep; + keyring_clear_rcu_disposal(&new->rcu); +just_return: + up_write(&keyring->sem); + kleave(" [no]"); +} |