1 /*
2 * Implementation of the access vector table type.
3 *
4 * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
5 */
6
7 /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
8 *
9 * Added conditional policy language extensions
10 *
11 * Copyright (C) 2003 Tresys Technology, LLC
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, version 2.
15 *
16 * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
17 * Tuned number of hash slots for avtab to reduce memory usage
18 */
19
20 #include <linux/kernel.h>
21 #include <linux/slab.h>
22 #include <linux/errno.h>
23 #include "avtab.h"
24 #include "policydb.h"
25
26 static struct kmem_cache *avtab_node_cachep;
27 static struct kmem_cache *avtab_xperms_cachep;
28
avtab_hash(struct avtab_key * keyp,u16 mask)29 static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
30 {
31 return ((keyp->target_class + (keyp->target_type << 2) +
32 (keyp->source_type << 9)) & mask);
33 }
34
35 static struct avtab_node*
avtab_insert_node(struct avtab * h,int hvalue,struct avtab_node * prev,struct avtab_node * cur,struct avtab_key * key,struct avtab_datum * datum)36 avtab_insert_node(struct avtab *h, int hvalue,
37 struct avtab_node *prev, struct avtab_node *cur,
38 struct avtab_key *key, struct avtab_datum *datum)
39 {
40 struct avtab_node *newnode;
41 struct avtab_extended_perms *xperms;
42 newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
43 if (newnode == NULL)
44 return NULL;
45 newnode->key = *key;
46
47 if (key->specified & AVTAB_XPERMS) {
48 xperms = kmem_cache_zalloc(avtab_xperms_cachep, GFP_KERNEL);
49 if (xperms == NULL) {
50 kmem_cache_free(avtab_node_cachep, newnode);
51 return NULL;
52 }
53 *xperms = *(datum->u.xperms);
54 newnode->datum.u.xperms = xperms;
55 } else {
56 newnode->datum.u.data = datum->u.data;
57 }
58
59 if (prev) {
60 newnode->next = prev->next;
61 prev->next = newnode;
62 } else {
63 newnode->next = h->htable[hvalue];
64 h->htable[hvalue] = newnode;
65 }
66
67 h->nel++;
68 return newnode;
69 }
70
avtab_insert(struct avtab * h,struct avtab_key * key,struct avtab_datum * datum)71 static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
72 {
73 int hvalue;
74 struct avtab_node *prev, *cur, *newnode;
75 u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
76
77 if (!h || !h->htable)
78 return -EINVAL;
79
80 hvalue = avtab_hash(key, h->mask);
81 for (prev = NULL, cur = h->htable[hvalue];
82 cur;
83 prev = cur, cur = cur->next) {
84 if (key->source_type == cur->key.source_type &&
85 key->target_type == cur->key.target_type &&
86 key->target_class == cur->key.target_class &&
87 (specified & cur->key.specified)) {
88 /* extended perms may not be unique */
89 if (specified & AVTAB_XPERMS)
90 break;
91 return -EEXIST;
92 }
93 if (key->source_type < cur->key.source_type)
94 break;
95 if (key->source_type == cur->key.source_type &&
96 key->target_type < cur->key.target_type)
97 break;
98 if (key->source_type == cur->key.source_type &&
99 key->target_type == cur->key.target_type &&
100 key->target_class < cur->key.target_class)
101 break;
102 }
103
104 newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
105 if (!newnode)
106 return -ENOMEM;
107
108 return 0;
109 }
110
111 /* Unlike avtab_insert(), this function allow multiple insertions of the same
112 * key/specified mask into the table, as needed by the conditional avtab.
113 * It also returns a pointer to the node inserted.
114 */
115 struct avtab_node *
avtab_insert_nonunique(struct avtab * h,struct avtab_key * key,struct avtab_datum * datum)116 avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
117 {
118 int hvalue;
119 struct avtab_node *prev, *cur;
120 u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
121
122 if (!h || !h->htable)
123 return NULL;
124 hvalue = avtab_hash(key, h->mask);
125 for (prev = NULL, cur = h->htable[hvalue];
126 cur;
127 prev = cur, cur = cur->next) {
128 if (key->source_type == cur->key.source_type &&
129 key->target_type == cur->key.target_type &&
130 key->target_class == cur->key.target_class &&
131 (specified & cur->key.specified))
132 break;
133 if (key->source_type < cur->key.source_type)
134 break;
135 if (key->source_type == cur->key.source_type &&
136 key->target_type < cur->key.target_type)
137 break;
138 if (key->source_type == cur->key.source_type &&
139 key->target_type == cur->key.target_type &&
140 key->target_class < cur->key.target_class)
141 break;
142 }
143 return avtab_insert_node(h, hvalue, prev, cur, key, datum);
144 }
145
avtab_search(struct avtab * h,struct avtab_key * key)146 struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
147 {
148 int hvalue;
149 struct avtab_node *cur;
150 u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
151
152 if (!h || !h->htable)
153 return NULL;
154
155 hvalue = avtab_hash(key, h->mask);
156 for (cur = h->htable[hvalue]; cur; cur = cur->next) {
157 if (key->source_type == cur->key.source_type &&
158 key->target_type == cur->key.target_type &&
159 key->target_class == cur->key.target_class &&
160 (specified & cur->key.specified))
161 return &cur->datum;
162
163 if (key->source_type < cur->key.source_type)
164 break;
165 if (key->source_type == cur->key.source_type &&
166 key->target_type < cur->key.target_type)
167 break;
168 if (key->source_type == cur->key.source_type &&
169 key->target_type == cur->key.target_type &&
170 key->target_class < cur->key.target_class)
171 break;
172 }
173
174 return NULL;
175 }
176
177 /* This search function returns a node pointer, and can be used in
178 * conjunction with avtab_search_next_node()
179 */
180 struct avtab_node*
avtab_search_node(struct avtab * h,struct avtab_key * key)181 avtab_search_node(struct avtab *h, struct avtab_key *key)
182 {
183 int hvalue;
184 struct avtab_node *cur;
185 u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
186
187 if (!h || !h->htable)
188 return NULL;
189
190 hvalue = avtab_hash(key, h->mask);
191 for (cur = h->htable[hvalue]; cur; cur = cur->next) {
192 if (key->source_type == cur->key.source_type &&
193 key->target_type == cur->key.target_type &&
194 key->target_class == cur->key.target_class &&
195 (specified & cur->key.specified))
196 return cur;
197
198 if (key->source_type < cur->key.source_type)
199 break;
200 if (key->source_type == cur->key.source_type &&
201 key->target_type < cur->key.target_type)
202 break;
203 if (key->source_type == cur->key.source_type &&
204 key->target_type == cur->key.target_type &&
205 key->target_class < cur->key.target_class)
206 break;
207 }
208 return NULL;
209 }
210
211 struct avtab_node*
avtab_search_node_next(struct avtab_node * node,int specified)212 avtab_search_node_next(struct avtab_node *node, int specified)
213 {
214 struct avtab_node *cur;
215
216 if (!node)
217 return NULL;
218
219 specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
220 for (cur = node->next; cur; cur = cur->next) {
221 if (node->key.source_type == cur->key.source_type &&
222 node->key.target_type == cur->key.target_type &&
223 node->key.target_class == cur->key.target_class &&
224 (specified & cur->key.specified))
225 return cur;
226
227 if (node->key.source_type < cur->key.source_type)
228 break;
229 if (node->key.source_type == cur->key.source_type &&
230 node->key.target_type < cur->key.target_type)
231 break;
232 if (node->key.source_type == cur->key.source_type &&
233 node->key.target_type == cur->key.target_type &&
234 node->key.target_class < cur->key.target_class)
235 break;
236 }
237 return NULL;
238 }
239
avtab_destroy(struct avtab * h)240 void avtab_destroy(struct avtab *h)
241 {
242 int i;
243 struct avtab_node *cur, *temp;
244
245 if (!h || !h->htable)
246 return;
247
248 for (i = 0; i < h->nslot; i++) {
249 cur = h->htable[i];
250 while (cur) {
251 temp = cur;
252 cur = cur->next;
253 if (temp->key.specified & AVTAB_XPERMS)
254 kmem_cache_free(avtab_xperms_cachep,
255 temp->datum.u.xperms);
256 kmem_cache_free(avtab_node_cachep, temp);
257 }
258 h->htable[i] = NULL;
259 }
260 kfree(h->htable);
261 h->htable = NULL;
262 h->nslot = 0;
263 h->mask = 0;
264 }
265
avtab_init(struct avtab * h)266 int avtab_init(struct avtab *h)
267 {
268 h->htable = NULL;
269 h->nel = 0;
270 return 0;
271 }
272
avtab_alloc(struct avtab * h,u32 nrules)273 int avtab_alloc(struct avtab *h, u32 nrules)
274 {
275 u16 mask = 0;
276 u32 shift = 0;
277 u32 work = nrules;
278 u32 nslot = 0;
279
280 if (nrules == 0)
281 goto avtab_alloc_out;
282
283 while (work) {
284 work = work >> 1;
285 shift++;
286 }
287 if (shift > 2)
288 shift = shift - 2;
289 nslot = 1 << shift;
290 if (nslot > MAX_AVTAB_HASH_BUCKETS)
291 nslot = MAX_AVTAB_HASH_BUCKETS;
292 mask = nslot - 1;
293
294 h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL);
295 if (!h->htable)
296 return -ENOMEM;
297
298 avtab_alloc_out:
299 h->nel = 0;
300 h->nslot = nslot;
301 h->mask = mask;
302 printk(KERN_DEBUG "SELinux: %d avtab hash slots, %d rules.\n",
303 h->nslot, nrules);
304 return 0;
305 }
306
avtab_hash_eval(struct avtab * h,char * tag)307 void avtab_hash_eval(struct avtab *h, char *tag)
308 {
309 int i, chain_len, slots_used, max_chain_len;
310 unsigned long long chain2_len_sum;
311 struct avtab_node *cur;
312
313 slots_used = 0;
314 max_chain_len = 0;
315 chain2_len_sum = 0;
316 for (i = 0; i < h->nslot; i++) {
317 cur = h->htable[i];
318 if (cur) {
319 slots_used++;
320 chain_len = 0;
321 while (cur) {
322 chain_len++;
323 cur = cur->next;
324 }
325
326 if (chain_len > max_chain_len)
327 max_chain_len = chain_len;
328 chain2_len_sum += chain_len * chain_len;
329 }
330 }
331
332 printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, "
333 "longest chain length %d sum of chain length^2 %llu\n",
334 tag, h->nel, slots_used, h->nslot, max_chain_len,
335 chain2_len_sum);
336 }
337
338 /*
339 * extended permissions compatibility. Make ToT Android kernels compatible
340 * with Android M releases
341 */
342 #define AVTAB_OPTYPE_ALLOWED 0x1000
343 #define AVTAB_OPTYPE_AUDITALLOW 0x2000
344 #define AVTAB_OPTYPE_DONTAUDIT 0x4000
345 #define AVTAB_OPTYPE (AVTAB_OPTYPE_ALLOWED | \
346 AVTAB_OPTYPE_AUDITALLOW | \
347 AVTAB_OPTYPE_DONTAUDIT)
348 #define AVTAB_XPERMS_OPTYPE 4
349
350 #define avtab_xperms_to_optype(x) (x << AVTAB_XPERMS_OPTYPE)
351 #define avtab_optype_to_xperms(x) (x >> AVTAB_XPERMS_OPTYPE)
352
353 static unsigned int avtab_android_m_compat;
354
avtab_android_m_compat_set(void)355 static void avtab_android_m_compat_set(void)
356 {
357 if (!avtab_android_m_compat) {
358 pr_info("SELinux: Android master kernel running Android"
359 " M policy in compatibility mode.\n");
360 avtab_android_m_compat = 1;
361 }
362 }
363
364 static uint16_t spec_order[] = {
365 AVTAB_ALLOWED,
366 AVTAB_AUDITDENY,
367 AVTAB_AUDITALLOW,
368 AVTAB_TRANSITION,
369 AVTAB_CHANGE,
370 AVTAB_MEMBER,
371 AVTAB_XPERMS_ALLOWED,
372 AVTAB_XPERMS_AUDITALLOW,
373 AVTAB_XPERMS_DONTAUDIT
374 };
375
avtab_read_item(struct avtab * a,void * fp,struct policydb * pol,int (* insertf)(struct avtab * a,struct avtab_key * k,struct avtab_datum * d,void * p),void * p)376 int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
377 int (*insertf)(struct avtab *a, struct avtab_key *k,
378 struct avtab_datum *d, void *p),
379 void *p)
380 {
381 __le16 buf16[4];
382 u16 enabled;
383 u32 items, items2, val, vers = pol->policyvers;
384 struct avtab_key key;
385 struct avtab_datum datum;
386 struct avtab_extended_perms xperms;
387 __le32 buf32[ARRAY_SIZE(xperms.perms.p)];
388 unsigned int android_m_compat_optype = 0;
389 int i, rc;
390 unsigned set;
391
392 memset(&key, 0, sizeof(struct avtab_key));
393 memset(&datum, 0, sizeof(struct avtab_datum));
394
395 if (vers < POLICYDB_VERSION_AVTAB) {
396 rc = next_entry(buf32, fp, sizeof(u32));
397 if (rc) {
398 printk(KERN_ERR "SELinux: avtab: truncated entry\n");
399 return rc;
400 }
401 items2 = le32_to_cpu(buf32[0]);
402 if (items2 > ARRAY_SIZE(buf32)) {
403 printk(KERN_ERR "SELinux: avtab: entry overflow\n");
404 return -EINVAL;
405
406 }
407 rc = next_entry(buf32, fp, sizeof(u32)*items2);
408 if (rc) {
409 printk(KERN_ERR "SELinux: avtab: truncated entry\n");
410 return rc;
411 }
412 items = 0;
413
414 val = le32_to_cpu(buf32[items++]);
415 key.source_type = (u16)val;
416 if (key.source_type != val) {
417 printk(KERN_ERR "SELinux: avtab: truncated source type\n");
418 return -EINVAL;
419 }
420 val = le32_to_cpu(buf32[items++]);
421 key.target_type = (u16)val;
422 if (key.target_type != val) {
423 printk(KERN_ERR "SELinux: avtab: truncated target type\n");
424 return -EINVAL;
425 }
426 val = le32_to_cpu(buf32[items++]);
427 key.target_class = (u16)val;
428 if (key.target_class != val) {
429 printk(KERN_ERR "SELinux: avtab: truncated target class\n");
430 return -EINVAL;
431 }
432
433 val = le32_to_cpu(buf32[items++]);
434 enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
435
436 if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
437 printk(KERN_ERR "SELinux: avtab: null entry\n");
438 return -EINVAL;
439 }
440 if ((val & AVTAB_AV) &&
441 (val & AVTAB_TYPE)) {
442 printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
443 return -EINVAL;
444 }
445 if (val & AVTAB_XPERMS) {
446 printk(KERN_ERR "SELinux: avtab: entry has extended permissions\n");
447 return -EINVAL;
448 }
449
450 for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
451 if (val & spec_order[i]) {
452 key.specified = spec_order[i] | enabled;
453 datum.u.data = le32_to_cpu(buf32[items++]);
454 rc = insertf(a, &key, &datum, p);
455 if (rc)
456 return rc;
457 }
458 }
459
460 if (items != items2) {
461 printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items);
462 return -EINVAL;
463 }
464 return 0;
465 }
466
467 rc = next_entry(buf16, fp, sizeof(u16)*4);
468 if (rc) {
469 printk(KERN_ERR "SELinux: avtab: truncated entry\n");
470 return rc;
471 }
472
473 items = 0;
474 key.source_type = le16_to_cpu(buf16[items++]);
475 key.target_type = le16_to_cpu(buf16[items++]);
476 key.target_class = le16_to_cpu(buf16[items++]);
477 key.specified = le16_to_cpu(buf16[items++]);
478
479 if ((key.specified & AVTAB_OPTYPE) &&
480 (vers == POLICYDB_VERSION_XPERMS_IOCTL)) {
481 key.specified = avtab_optype_to_xperms(key.specified);
482 android_m_compat_optype = 1;
483 avtab_android_m_compat_set();
484 }
485
486 if (!policydb_type_isvalid(pol, key.source_type) ||
487 !policydb_type_isvalid(pol, key.target_type) ||
488 !policydb_class_isvalid(pol, key.target_class)) {
489 printk(KERN_ERR "SELinux: avtab: invalid type or class\n");
490 return -EINVAL;
491 }
492
493 set = 0;
494 for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
495 if (key.specified & spec_order[i])
496 set++;
497 }
498 if (!set || set > 1) {
499 printk(KERN_ERR "SELinux: avtab: more than one specifier\n");
500 return -EINVAL;
501 }
502
503 if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) &&
504 (key.specified & AVTAB_XPERMS)) {
505 printk(KERN_ERR "SELinux: avtab: policy version %u does not "
506 "support extended permissions rules and one "
507 "was specified\n", vers);
508 return -EINVAL;
509 } else if (key.specified & AVTAB_XPERMS) {
510 memset(&xperms, 0, sizeof(struct avtab_extended_perms));
511 rc = next_entry(&xperms.specified, fp, sizeof(u8));
512 if (rc) {
513 printk(KERN_ERR "SELinux: avtab: truncated entry\n");
514 return rc;
515 }
516 if (avtab_android_m_compat ||
517 ((xperms.specified != AVTAB_XPERMS_IOCTLFUNCTION) &&
518 (xperms.specified != AVTAB_XPERMS_IOCTLDRIVER) &&
519 (vers == POLICYDB_VERSION_XPERMS_IOCTL))) {
520 xperms.driver = xperms.specified;
521 if (android_m_compat_optype)
522 xperms.specified = AVTAB_XPERMS_IOCTLDRIVER;
523 else
524 xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION;
525 avtab_android_m_compat_set();
526 } else {
527 rc = next_entry(&xperms.driver, fp, sizeof(u8));
528 if (rc) {
529 printk(KERN_ERR "SELinux: avtab: truncated entry\n");
530 return rc;
531 }
532 }
533 rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p));
534 if (rc) {
535 printk(KERN_ERR "SELinux: avtab: truncated entry\n");
536 return rc;
537 }
538 for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++)
539 xperms.perms.p[i] = le32_to_cpu(buf32[i]);
540 datum.u.xperms = &xperms;
541 } else {
542 rc = next_entry(buf32, fp, sizeof(u32));
543 if (rc) {
544 printk(KERN_ERR "SELinux: avtab: truncated entry\n");
545 return rc;
546 }
547 datum.u.data = le32_to_cpu(*buf32);
548 }
549 if ((key.specified & AVTAB_TYPE) &&
550 !policydb_type_isvalid(pol, datum.u.data)) {
551 printk(KERN_ERR "SELinux: avtab: invalid type\n");
552 return -EINVAL;
553 }
554 return insertf(a, &key, &datum, p);
555 }
556
avtab_insertf(struct avtab * a,struct avtab_key * k,struct avtab_datum * d,void * p)557 static int avtab_insertf(struct avtab *a, struct avtab_key *k,
558 struct avtab_datum *d, void *p)
559 {
560 return avtab_insert(a, k, d);
561 }
562
avtab_read(struct avtab * a,void * fp,struct policydb * pol)563 int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
564 {
565 int rc;
566 __le32 buf[1];
567 u32 nel, i;
568
569
570 rc = next_entry(buf, fp, sizeof(u32));
571 if (rc < 0) {
572 printk(KERN_ERR "SELinux: avtab: truncated table\n");
573 goto bad;
574 }
575 nel = le32_to_cpu(buf[0]);
576 if (!nel) {
577 printk(KERN_ERR "SELinux: avtab: table is empty\n");
578 rc = -EINVAL;
579 goto bad;
580 }
581
582 rc = avtab_alloc(a, nel);
583 if (rc)
584 goto bad;
585
586 for (i = 0; i < nel; i++) {
587 rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
588 if (rc) {
589 if (rc == -ENOMEM)
590 printk(KERN_ERR "SELinux: avtab: out of memory\n");
591 else if (rc == -EEXIST)
592 printk(KERN_ERR "SELinux: avtab: duplicate entry\n");
593
594 goto bad;
595 }
596 }
597
598 rc = 0;
599 out:
600 return rc;
601
602 bad:
603 avtab_destroy(a);
604 goto out;
605 }
606
avtab_write_item(struct policydb * p,struct avtab_node * cur,void * fp)607 int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
608 {
609 __le16 buf16[4];
610 __le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)];
611 int rc;
612 unsigned int i;
613
614 buf16[0] = cpu_to_le16(cur->key.source_type);
615 buf16[1] = cpu_to_le16(cur->key.target_type);
616 buf16[2] = cpu_to_le16(cur->key.target_class);
617 if (avtab_android_m_compat && (cur->key.specified & AVTAB_XPERMS) &&
618 (cur->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER))
619 buf16[3] = cpu_to_le16(avtab_xperms_to_optype(cur->key.specified));
620 else
621 buf16[3] = cpu_to_le16(cur->key.specified);
622 rc = put_entry(buf16, sizeof(u16), 4, fp);
623 if (rc)
624 return rc;
625
626 if (cur->key.specified & AVTAB_XPERMS) {
627 if (avtab_android_m_compat == 0) {
628 rc = put_entry(&cur->datum.u.xperms->specified,
629 sizeof(u8), 1, fp);
630 if (rc)
631 return rc;
632 }
633 rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp);
634 if (rc)
635 return rc;
636 for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++)
637 buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]);
638 rc = put_entry(buf32, sizeof(u32),
639 ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
640 } else {
641 buf32[0] = cpu_to_le32(cur->datum.u.data);
642 rc = put_entry(buf32, sizeof(u32), 1, fp);
643 }
644 if (rc)
645 return rc;
646 return 0;
647 }
648
avtab_write(struct policydb * p,struct avtab * a,void * fp)649 int avtab_write(struct policydb *p, struct avtab *a, void *fp)
650 {
651 unsigned int i;
652 int rc = 0;
653 struct avtab_node *cur;
654 __le32 buf[1];
655
656 buf[0] = cpu_to_le32(a->nel);
657 rc = put_entry(buf, sizeof(u32), 1, fp);
658 if (rc)
659 return rc;
660
661 for (i = 0; i < a->nslot; i++) {
662 for (cur = a->htable[i]; cur; cur = cur->next) {
663 rc = avtab_write_item(p, cur, fp);
664 if (rc)
665 return rc;
666 }
667 }
668
669 return rc;
670 }
avtab_cache_init(void)671 void avtab_cache_init(void)
672 {
673 avtab_node_cachep = kmem_cache_create("avtab_node",
674 sizeof(struct avtab_node),
675 0, SLAB_PANIC, NULL);
676 avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms",
677 sizeof(struct avtab_extended_perms),
678 0, SLAB_PANIC, NULL);
679 }
680
avtab_cache_destroy(void)681 void avtab_cache_destroy(void)
682 {
683 kmem_cache_destroy(avtab_node_cachep);
684 kmem_cache_destroy(avtab_xperms_cachep);
685 }
686