blob: d9d8599e8e63024b3d823419d273e912332daf78 [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Implementation of the SID table type.
4 *
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +01005 * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
6 * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
7 *
8 * Copyright (C) 2018 Red Hat, Inc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010010#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/kernel.h>
12#include <linux/slab.h>
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010013#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/spinlock.h>
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +020015#include <asm/barrier.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include "flask.h"
17#include "security.h"
18#include "sidtab.h"
19
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010020#define index_to_sid(index) (index + SECINITSID_NUM + 1)
21#define sid_to_index(sid) (sid - (SECINITSID_NUM + 1))
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023int sidtab_init(struct sidtab *s)
24{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010025 u32 i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010027 memset(s->roots, 0, sizeof(s->roots));
28
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010029 for (i = 0; i < SECINITSID_NUM; i++)
30 s->isids[i].set = 0;
31
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +020032 s->count = 0;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +010033 s->convert = NULL;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010034 hash_init(s->context_to_sid);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010035
James Morrisbdd581c2008-06-06 18:50:12 +100036 spin_lock_init(&s->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 return 0;
38}
39
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010040static u32 context_to_sid(struct sidtab *s, struct context *context)
41{
42 struct sidtab_entry_leaf *entry;
43 u32 sid = 0;
44
45 rcu_read_lock();
46 hash_for_each_possible_rcu(s->context_to_sid, entry, list,
47 context->hash) {
48 if (context_cmp(&entry->context, context)) {
49 sid = entry->sid;
50 break;
51 }
52 }
53 rcu_read_unlock();
54 return sid;
55}
56
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010057int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
58{
59 struct sidtab_isid_entry *entry;
60 int rc;
61
62 if (sid == 0 || sid > SECINITSID_NUM)
63 return -EINVAL;
64
65 entry = &s->isids[sid - 1];
66
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010067 rc = context_cpy(&entry->leaf.context, context);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010068 if (rc)
69 return rc;
70
71 entry->set = 1;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010072
73 /*
74 * Multiple initial sids may map to the same context. Check that this
75 * context is not already represented in the context_to_sid hashtable
76 * to avoid duplicate entries and long linked lists upon hash
77 * collision.
78 */
79 if (!context_to_sid(s, context)) {
80 entry->leaf.sid = sid;
81 hash_add(s->context_to_sid, &entry->leaf.list, context->hash);
82 }
83
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +010084 return 0;
85}
86
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +010087int sidtab_hash_stats(struct sidtab *sidtab, char *page)
88{
89 int i;
90 int chain_len = 0;
91 int slots_used = 0;
92 int entries = 0;
93 int max_chain_len = 0;
94 int cur_bucket = 0;
95 struct sidtab_entry_leaf *entry;
96
97 rcu_read_lock();
98 hash_for_each_rcu(sidtab->context_to_sid, i, entry, list) {
99 entries++;
100 if (i == cur_bucket) {
101 chain_len++;
102 if (chain_len == 1)
103 slots_used++;
104 } else {
105 cur_bucket = i;
106 if (chain_len > max_chain_len)
107 max_chain_len = chain_len;
108 chain_len = 0;
109 }
110 }
111 rcu_read_unlock();
112
113 if (chain_len > max_chain_len)
114 max_chain_len = chain_len;
115
116 return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
117 "longest chain: %d\n", entries,
118 slots_used, SIDTAB_HASH_BUCKETS, max_chain_len);
119}
120
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100121static u32 sidtab_level_from_count(u32 count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100123 u32 capacity = SIDTAB_LEAF_ENTRIES;
124 u32 level = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100126 while (count > capacity) {
127 capacity <<= SIDTAB_INNER_SHIFT;
128 ++level;
129 }
130 return level;
131}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100133static int sidtab_alloc_roots(struct sidtab *s, u32 level)
134{
135 u32 l;
136
137 if (!s->roots[0].ptr_leaf) {
138 s->roots[0].ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
139 GFP_ATOMIC);
140 if (!s->roots[0].ptr_leaf)
141 return -ENOMEM;
142 }
143 for (l = 1; l <= level; ++l)
144 if (!s->roots[l].ptr_inner) {
145 s->roots[l].ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
146 GFP_ATOMIC);
147 if (!s->roots[l].ptr_inner)
148 return -ENOMEM;
149 s->roots[l].ptr_inner->entries[0] = s->roots[l - 1];
150 }
151 return 0;
152}
153
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100154static struct sidtab_entry_leaf *sidtab_do_lookup(struct sidtab *s, u32 index,
155 int alloc)
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100156{
157 union sidtab_entry_inner *entry;
158 u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES;
159
160 /* find the level of the subtree we need */
161 level = sidtab_level_from_count(index + 1);
162 capacity_shift = level * SIDTAB_INNER_SHIFT;
163
164 /* allocate roots if needed */
165 if (alloc && sidtab_alloc_roots(s, level) != 0)
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100166 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100168 /* lookup inside the subtree */
169 entry = &s->roots[level];
170 while (level != 0) {
171 capacity_shift -= SIDTAB_INNER_SHIFT;
172 --level;
173
174 entry = &entry->ptr_inner->entries[leaf_index >> capacity_shift];
175 leaf_index &= ((u32)1 << capacity_shift) - 1;
176
177 if (!entry->ptr_inner) {
178 if (alloc)
179 entry->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
180 GFP_ATOMIC);
181 if (!entry->ptr_inner)
182 return NULL;
183 }
184 }
185 if (!entry->ptr_leaf) {
186 if (alloc)
187 entry->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
188 GFP_ATOMIC);
189 if (!entry->ptr_leaf)
190 return NULL;
191 }
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100192 return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES];
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100193}
194
195static struct context *sidtab_lookup(struct sidtab *s, u32 index)
196{
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200197 /* read entries only after reading count */
198 u32 count = smp_load_acquire(&s->count);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100199
200 if (index >= count)
201 return NULL;
202
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100203 return &sidtab_do_lookup(s, index, 0)->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204}
205
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100206static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid)
207{
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100208 return s->isids[sid - 1].set ? &s->isids[sid - 1].leaf.context : NULL;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100209}
210
211static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
212{
213 struct context *context;
214
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100215 if (sid != 0) {
216 if (sid > SECINITSID_NUM)
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100217 context = sidtab_lookup(s, sid_to_index(sid));
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100218 else
219 context = sidtab_lookup_initial(s, sid);
220 if (context && (!context->len || force))
221 return context;
222 }
223
224 return sidtab_lookup_initial(s, SECINITSID_UNLABELED);
225}
226
Stephen Smalley12b29f32008-05-07 13:03:20 -0400227struct context *sidtab_search(struct sidtab *s, u32 sid)
228{
229 return sidtab_search_core(s, sid, 0);
230}
231
232struct context *sidtab_search_force(struct sidtab *s, u32 sid)
233{
234 return sidtab_search_core(s, sid, 1);
235}
236
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100237int sidtab_context_to_sid(struct sidtab *s, struct context *context,
238 u32 *sid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 unsigned long flags;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100241 u32 count;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100242 struct sidtab_convert_params *convert;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100243 struct sidtab_entry_leaf *dst, *dst_convert;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100244 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100246 *sid = context_to_sid(s, context);
247 if (*sid)
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100248 return 0;
249
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100250 /* lock-free search failed: lock, re-search, and insert if not found */
251 spin_lock_irqsave(&s->lock, flags);
252
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100253 rc = 0;
254 *sid = context_to_sid(s, context);
255 if (*sid)
256 goto out_unlock;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100257
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100258 /* read entries only after reading count */
259 count = smp_load_acquire(&s->count);
260 convert = s->convert;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100261
Ondrej Mosnacekacbc3722019-07-23 08:50:59 +0200262 /* bail out if we already reached max entries */
263 rc = -EOVERFLOW;
264 if (count >= SIDTAB_MAX)
265 goto out_unlock;
266
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100267 /* insert context into new entry */
268 rc = -ENOMEM;
269 dst = sidtab_do_lookup(s, count, 1);
270 if (!dst)
271 goto out_unlock;
272
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100273 dst->sid = index_to_sid(count);
274
275 rc = context_cpy(&dst->context, context);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100276 if (rc)
277 goto out_unlock;
278
279 /*
280 * if we are building a new sidtab, we need to convert the context
281 * and insert it there as well
282 */
283 if (convert) {
284 rc = -ENOMEM;
285 dst_convert = sidtab_do_lookup(convert->target, count, 1);
286 if (!dst_convert) {
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100287 context_destroy(&dst->context);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100288 goto out_unlock;
289 }
290
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100291 rc = convert->func(context, &dst_convert->context,
292 convert->args);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100293 if (rc) {
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100294 context_destroy(&dst->context);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100295 goto out_unlock;
296 }
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100297 dst_convert->sid = index_to_sid(count);
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200298 convert->target->count = count + 1;
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100299
300 hash_add_rcu(convert->target->context_to_sid,
301 &dst_convert->list, dst_convert->context.hash);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100302 }
303
304 if (context->len)
305 pr_info("SELinux: Context %s is not valid (left unmapped).\n",
306 context->str);
307
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100308 *sid = index_to_sid(count);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100309
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100310 /* write entries before updating count */
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200311 smp_store_release(&s->count, count + 1);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100312 hash_add_rcu(s->context_to_sid, &dst->list, dst->context.hash);
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100313
314 rc = 0;
315out_unlock:
316 spin_unlock_irqrestore(&s->lock, flags);
317 return rc;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100318}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100320static void sidtab_convert_hashtable(struct sidtab *s, u32 count)
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100321{
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100322 struct sidtab_entry_leaf *entry;
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100323 u32 i;
324
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100325 for (i = 0; i < count; i++) {
326 entry = sidtab_do_lookup(s, i, 0);
327 entry->sid = index_to_sid(i);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100328
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100329 hash_add_rcu(s->context_to_sid, &entry->list,
330 entry->context.hash);
331
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100332 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100335static int sidtab_convert_tree(union sidtab_entry_inner *edst,
336 union sidtab_entry_inner *esrc,
337 u32 *pos, u32 count, u32 level,
338 struct sidtab_convert_params *convert)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100340 int rc;
341 u32 i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100343 if (level != 0) {
344 if (!edst->ptr_inner) {
345 edst->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
346 GFP_KERNEL);
347 if (!edst->ptr_inner)
348 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 }
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100350 i = 0;
351 while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
352 rc = sidtab_convert_tree(&edst->ptr_inner->entries[i],
353 &esrc->ptr_inner->entries[i],
354 pos, count, level - 1,
355 convert);
356 if (rc)
357 return rc;
358 i++;
359 }
360 } else {
361 if (!edst->ptr_leaf) {
362 edst->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
363 GFP_KERNEL);
364 if (!edst->ptr_leaf)
365 return -ENOMEM;
366 }
367 i = 0;
368 while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
369 rc = convert->func(&esrc->ptr_leaf->entries[i].context,
370 &edst->ptr_leaf->entries[i].context,
371 convert->args);
372 if (rc)
373 return rc;
374 (*pos)++;
375 i++;
376 }
377 cond_resched();
378 }
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100379
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100380 return 0;
381}
382
383int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
384{
385 unsigned long flags;
386 u32 count, level, pos;
387 int rc;
388
389 spin_lock_irqsave(&s->lock, flags);
390
391 /* concurrent policy loads are not allowed */
392 if (s->convert) {
393 spin_unlock_irqrestore(&s->lock, flags);
394 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
396
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200397 count = s->count;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100398 level = sidtab_level_from_count(count);
399
400 /* allocate last leaf in the new sidtab (to avoid race with
401 * live convert)
402 */
403 rc = sidtab_do_lookup(params->target, count - 1, 1) ? 0 : -ENOMEM;
404 if (rc) {
405 spin_unlock_irqrestore(&s->lock, flags);
406 return rc;
407 }
408
409 /* set count in case no new entries are added during conversion */
Ondrej Mosnacek116f21b2019-08-14 15:33:20 +0200410 params->target->count = count;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100411
412 /* enable live convert of new entries */
413 s->convert = params;
414
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100415 /* we can safely convert the tree outside the lock */
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100416 spin_unlock_irqrestore(&s->lock, flags);
417
418 pr_info("SELinux: Converting %u SID table entries...\n", count);
419
420 /* convert all entries not covered by live convert */
421 pos = 0;
422 rc = sidtab_convert_tree(&params->target->roots[level],
423 &s->roots[level], &pos, count, level, params);
424 if (rc) {
425 /* we need to keep the old table - disable live convert */
426 spin_lock_irqsave(&s->lock, flags);
427 s->convert = NULL;
428 spin_unlock_irqrestore(&s->lock, flags);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100429 return rc;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100430 }
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100431 /*
432 * The hashtable can also be modified in sidtab_context_to_sid()
433 * so we must re-acquire the lock here.
434 */
435 spin_lock_irqsave(&s->lock, flags);
436 sidtab_convert_hashtable(params->target, count);
437 spin_unlock_irqrestore(&s->lock, flags);
438
439 return 0;
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100440}
441
442static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
443{
444 u32 i;
445
446 if (level != 0) {
447 struct sidtab_node_inner *node = entry.ptr_inner;
448
449 if (!node)
450 return;
451
452 for (i = 0; i < SIDTAB_INNER_ENTRIES; i++)
453 sidtab_destroy_tree(node->entries[i], level - 1);
454 kfree(node);
455 } else {
456 struct sidtab_node_leaf *node = entry.ptr_leaf;
457
458 if (!node)
459 return;
460
461 for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++)
462 context_destroy(&node->entries[i].context);
463 kfree(node);
464 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
467void sidtab_destroy(struct sidtab *s)
468{
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100469 u32 i, level;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100471 for (i = 0; i < SECINITSID_NUM; i++)
472 if (s->isids[i].set)
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100473 context_destroy(&s->isids[i].leaf.context);
Ondrej Mosnacek24ed7fd2018-11-30 16:24:07 +0100474
Ondrej Mosnacekee1a84f2018-11-30 16:24:08 +0100475 level = SIDTAB_MAX_LEVEL;
476 while (level && !s->roots[level].ptr_inner)
477 --level;
478
479 sidtab_destroy_tree(s->roots[level], level);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +0100480 /*
481 * The context_to_sid hashtable's objects are all shared
482 * with the isids array and context tree, and so don't need
483 * to be cleaned up here.
484 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485}