kern_fileassoc.c revision 1.5.4.3 1 /* $NetBSD: kern_fileassoc.c,v 1.5.4.3 2006/09/03 15:25:22 yamt Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Elad Efrat <elad (at) NetBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Elad Efrat.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: kern_fileassoc.c,v 1.5.4.3 2006/09/03 15:25:22 yamt Exp $");
35
36 #include <sys/param.h>
37 #include <sys/mount.h>
38 #include <sys/queue.h>
39 #include <sys/malloc.h>
40 #include <sys/vnode.h>
41 #include <sys/namei.h>
42 #include <sys/exec.h>
43 #include <sys/proc.h>
44 #include <sys/inttypes.h>
45 #include <sys/errno.h>
46 #include <sys/fileassoc.h>
47 #include <sys/hash.h>
48
49 /*
50 * Hook entry.
51 * Includes the hook name for identification and private hook clear callback.
52 */
53 struct fileassoc_hook {
54 const char *hook_name; /* Hook name. */
55 fileassoc_cleanup_cb_t hook_cleanup_cb; /* Hook clear callback. */
56 };
57
58 /* An entry in the per-device hash table. */
59 struct fileassoc_hash_entry {
60 ino_t fileid; /* File id. */
61 void *hooks[FILEASSOC_NHOOKS]; /* Hooks. */
62 LIST_ENTRY(fileassoc_hash_entry) entries; /* List pointer. */
63 };
64
65 LIST_HEAD(fileassoc_hashhead, fileassoc_hash_entry);
66
67 struct fileassoc_table {
68 struct fileassoc_hashhead *hash_tbl;
69 size_t hash_size; /* Number of slots. */
70 struct mount *tbl_mntpt;
71 u_long hash_mask;
72 void *tables[FILEASSOC_NHOOKS];
73 LIST_ENTRY(fileassoc_table) hash_list; /* List pointer. */
74 };
75
76 struct fileassoc_hook fileassoc_hooks[FILEASSOC_NHOOKS];
77 int fileassoc_nhooks;
78
79 /* Global list of hash tables, one per device. */
80 LIST_HEAD(, fileassoc_table) fileassoc_tables;
81
82 /*
83 * Hashing function: Takes a number modulus the mask to give back
84 * an index into the hash table.
85 */
86 #define FILEASSOC_HASH(tbl, fileid) \
87 (hash32_buf(&(fileid), sizeof((fileid)), HASH32_BUF_INIT) \
88 & ((tbl)->hash_mask))
89
90 /*
91 * Initialize the fileassoc subsystem.
92 */
93 void
94 fileassoc_init(void)
95 {
96 memset(fileassoc_hooks, 0, sizeof(fileassoc_hooks));
97 fileassoc_nhooks = 0;
98 }
99
100 /*
101 * Register a new hook.
102 */
103 fileassoc_t
104 fileassoc_register(const char *name, fileassoc_cleanup_cb_t cleanup_cb)
105 {
106 int i;
107
108 if (fileassoc_nhooks >= FILEASSOC_NHOOKS)
109 return (-1);
110
111 for (i = 0; i < FILEASSOC_NHOOKS; i++)
112 if (fileassoc_hooks[i].hook_name == NULL)
113 break;
114
115 fileassoc_hooks[i].hook_name = name;
116 fileassoc_hooks[i].hook_cleanup_cb = cleanup_cb;
117
118 fileassoc_nhooks++;
119
120 return (i);
121 }
122
123 /*
124 * Deregister a hook.
125 */
126 int
127 fileassoc_deregister(fileassoc_t id)
128 {
129 if (id < 0 || id >= FILEASSOC_NHOOKS)
130 return (EINVAL);
131
132 fileassoc_hooks[id].hook_name = NULL;
133 fileassoc_hooks[id].hook_cleanup_cb = NULL;
134
135 fileassoc_nhooks--;
136
137 return (0);
138 }
139
140 /*
141 * Get the hash table for the specified device.
142 */
143 static struct fileassoc_table *
144 fileassoc_table_lookup(struct mount *mp)
145 {
146 struct fileassoc_table *tbl;
147
148 LIST_FOREACH(tbl, &fileassoc_tables, hash_list) {
149 if (tbl->tbl_mntpt == mp)
150 return (tbl);
151 }
152
153 return (NULL);
154 }
155
156 /*
157 * Perform a lookup on a hash table. If hint is non-zero then use the value
158 * of the hint as the identifier instead of performing a lookup for the
159 * fileid.
160 */
161 static struct fileassoc_hash_entry *
162 fileassoc_file_lookup(struct vnode *vp, uint64_t hint)
163 {
164 struct fileassoc_table *tbl;
165 struct fileassoc_hashhead *tble;
166 struct fileassoc_hash_entry *e;
167 struct vattr va;
168 size_t indx;
169 uint64_t th;
170 int error;
171
172 if (hint == 0) {
173 error = VOP_GETATTR(vp, &va, curlwp->l_cred, curlwp);
174 if (error)
175 return (NULL);
176 th = va.va_fileid;
177 } else
178 th = hint;
179
180 tbl = fileassoc_table_lookup(vp->v_mount);
181 if (tbl == NULL)
182 return (NULL);
183
184 /*
185 * XXX: We should NOT rely on fileid here!
186 */
187 indx = FILEASSOC_HASH(tbl, th);
188 tble = &(tbl->hash_tbl[indx & ((tbl)->hash_mask)]);
189
190 LIST_FOREACH(e, tble, entries) {
191 if ((e != NULL) && (e->fileid == th))
192 return (e);
193 }
194
195 return (NULL);
196 }
197
198 /*
199 * Return hook data associated with a vnode.
200 */
201 void *
202 fileassoc_lookup(struct vnode *vp, fileassoc_t id)
203 {
204 return fileassoc_lookup_hint(vp, id, 0);
205 }
206
207 /*
208 * Return hook data associated with a vnode, use hint to look for file
209 * instead of performing an implicit VOP_GETATTR() on the vnode.
210 */
211 void *
212 fileassoc_lookup_hint(struct vnode *vp, fileassoc_t id, uint64_t hint)
213 {
214 struct fileassoc_hash_entry *mhe;
215
216 mhe = fileassoc_file_lookup(vp, hint);
217
218 if (mhe == NULL)
219 return (NULL);
220
221 return (mhe->hooks[id]);
222 }
223
224 /*
225 * Create a new fileassoc table.
226 */
227 int
228 fileassoc_table_add(struct mount *mp, size_t size)
229 {
230 struct fileassoc_table *tbl;
231
232 /* Check for existing table for device. */
233 if (fileassoc_table_lookup(mp) != NULL)
234 return (EEXIST);
235
236 /* Allocate and initialize a Veriexec hash table. */
237 tbl = malloc(sizeof(*tbl), M_TEMP, M_WAITOK | M_ZERO);
238 tbl->hash_size = size;
239 tbl->tbl_mntpt = mp;
240 tbl->hash_tbl = hashinit(size, HASH_LIST, M_TEMP,
241 M_WAITOK | M_ZERO, &tbl->hash_mask);
242
243 LIST_INSERT_HEAD(&fileassoc_tables, tbl, hash_list);
244
245 return (0);
246 }
247
248 /*
249 * Delete a table.
250 */
251 int
252 fileassoc_table_delete(struct mount *mp)
253 {
254 struct fileassoc_table *tbl;
255 struct fileassoc_hashhead *hh;
256 u_long i;
257 int j;
258
259 tbl = fileassoc_table_lookup(mp);
260 if (tbl == NULL)
261 return (EEXIST);
262
263 /* Remove all entries from the table and lists */
264 hh = tbl->hash_tbl;
265 for (i = 0; i < tbl->hash_size; i++) {
266 struct fileassoc_hash_entry *mhe;
267
268 while (LIST_FIRST(&hh[i]) != NULL) {
269 mhe = LIST_FIRST(&hh[i]);
270 LIST_REMOVE(mhe, entries);
271
272 for (j = 0; j < fileassoc_nhooks; j++)
273 if (fileassoc_hooks[j].hook_cleanup_cb != NULL)
274 (fileassoc_hooks[j].hook_cleanup_cb)
275 (mhe->hooks[j],
276 FILEASSOC_CLEANUP_FILE);
277
278 free(mhe, M_TEMP);
279 }
280 }
281
282 for (j = 0; j < fileassoc_nhooks; j++)
283 if (fileassoc_hooks[j].hook_cleanup_cb != NULL)
284 (fileassoc_hooks[j].hook_cleanup_cb)(tbl->tables[j],
285 FILEASSOC_CLEANUP_TABLE);
286
287 /* Remove hash table and sysctl node */
288 hashdone(tbl->hash_tbl, M_TEMP);
289 LIST_REMOVE(tbl, hash_list);
290
291 return (0);
292 }
293
294 /*
295 * Run a callback for each hook entry in a table.
296 */
297 int
298 fileassoc_table_run(struct mount *mp, fileassoc_t id, fileassoc_cb_t cb)
299 {
300 struct fileassoc_table *tbl;
301 struct fileassoc_hashhead *hh;
302 u_long i;
303
304 tbl = fileassoc_table_lookup(mp);
305 if (tbl == NULL)
306 return (EEXIST);
307
308 hh = tbl->hash_tbl;
309 for (i = 0; i < tbl->hash_size; i++) {
310 struct fileassoc_hash_entry *mhe;
311
312 LIST_FOREACH(mhe, &hh[i], entries) {
313 if (mhe->hooks[id] != NULL)
314 cb(mhe->hooks[id]);
315 }
316 }
317
318 return (0);
319 }
320
321 /*
322 * Clear a table for a given hook.
323 */
324 int
325 fileassoc_table_clear(struct mount *mp, fileassoc_t id)
326 {
327 struct fileassoc_table *tbl;
328 struct fileassoc_hashhead *hh;
329 fileassoc_cleanup_cb_t cleanup_cb;
330 u_long i;
331
332 tbl = fileassoc_table_lookup(mp);
333 if (tbl == NULL)
334 return (EEXIST);
335
336 cleanup_cb = fileassoc_hooks[id].hook_cleanup_cb;
337
338 hh = tbl->hash_tbl;
339 for (i = 0; i < tbl->hash_size; i++) {
340 struct fileassoc_hash_entry *mhe;
341
342 LIST_FOREACH(mhe, &hh[i], entries) {
343 if ((mhe->hooks[id] != NULL) && cleanup_cb != NULL)
344 cleanup_cb(mhe->hooks[id],
345 FILEASSOC_CLEANUP_FILE);
346
347 mhe->hooks[id] = NULL;
348 }
349 }
350
351 if ((tbl->tables[id] != NULL) && cleanup_cb != NULL)
352 cleanup_cb(tbl->tables[id], FILEASSOC_CLEANUP_TABLE);
353
354 tbl->tables[id] = NULL;
355
356 return (0);
357 }
358
359 /*
360 * Add hook-specific data on a fileassoc table.
361 */
362 int
363 fileassoc_tabledata_add(struct mount *mp, fileassoc_t id, void *data)
364 {
365 struct fileassoc_table *tbl;
366
367 tbl = fileassoc_table_lookup(mp);
368 if (tbl == NULL)
369 return (EFAULT);
370
371 tbl->tables[id] = data;
372
373 return (0);
374 }
375
376 /*
377 * Clear hook-specific data on a fileassoc table.
378 */
379 int
380 fileassoc_tabledata_clear(struct mount *mp, fileassoc_t id)
381 {
382 struct fileassoc_table *tbl;
383
384 tbl = fileassoc_table_lookup(mp);
385 if (tbl == NULL)
386 return (EFAULT);
387
388 tbl->tables[id] = NULL;
389
390 return (0);
391 }
392
393 /*
394 * Retrieve hook-specific data from a fileassoc table.
395 */
396 void *
397 fileassoc_tabledata_lookup(struct mount *mp, fileassoc_t id)
398 {
399 struct fileassoc_table *tbl;
400
401 tbl = fileassoc_table_lookup(mp);
402 if (tbl == NULL)
403 return (NULL);
404
405 return (tbl->tables[id]);
406 }
407
408 /*
409 * Add a file entry to a table.
410 */
411 static struct fileassoc_hash_entry *
412 fileassoc_file_add(struct vnode *vp, uint64_t hint)
413 {
414 struct fileassoc_table *tbl;
415 struct fileassoc_hashhead *vhh;
416 struct fileassoc_hash_entry *e;
417 struct vattr va;
418 size_t indx;
419 uint64_t id;
420 int error;
421
422 if (hint == 0) {
423 error = VOP_GETATTR(vp, &va, curlwp->l_cred, curlwp);
424 if (error)
425 return (NULL);
426 /*
427 * XXX: We should NOT rely on fileid here!
428 */
429 id = va.va_fileid;
430 } else
431 id = hint;
432
433 e = fileassoc_file_lookup(vp, id);
434 if (e != NULL)
435 return (e);
436
437 tbl = fileassoc_table_lookup(vp->v_mount);
438 if (tbl == NULL)
439 return (NULL);
440
441 indx = FILEASSOC_HASH(tbl, id);
442 vhh = &(tbl->hash_tbl[indx & ((tbl)->hash_mask)]);
443
444 e = malloc(sizeof(*e), M_TEMP, M_WAITOK | M_ZERO);
445 e->fileid = id;
446 LIST_INSERT_HEAD(vhh, e, entries);
447
448 return (e);
449 }
450
451 /*
452 * Delete a file entry from a table.
453 */
454 int
455 fileassoc_file_delete(struct vnode *vp)
456 {
457 struct fileassoc_hash_entry *mhe;
458 int i;
459
460 mhe = fileassoc_file_lookup(vp, 0);
461 if (mhe == NULL)
462 return (ENOENT);
463
464 LIST_REMOVE(mhe, entries);
465
466 for (i = 0; i < fileassoc_nhooks; i++)
467 if (fileassoc_hooks[i].hook_cleanup_cb != NULL)
468 (fileassoc_hooks[i].hook_cleanup_cb)(mhe->hooks[i],
469 FILEASSOC_CLEANUP_FILE);
470
471 free(mhe, M_TEMP);
472
473 return (0);
474 }
475
476 /*
477 * Add a hook to a vnode using the given hint.
478 */
479 int
480 fileassoc_add_hint(struct vnode *vp, fileassoc_t id, void *data, uint64_t hint)
481 {
482 struct fileassoc_hash_entry *e;
483
484 e = fileassoc_file_lookup(vp, hint);
485 if (e == NULL) {
486 e = fileassoc_file_add(vp, hint);
487 if (e == NULL)
488 return (ENOTDIR);
489 }
490
491 if (e->hooks[id] != NULL)
492 return (EEXIST);
493
494 e->hooks[id] = data;
495
496 return (0);
497 }
498
499 /*
500 * Add a hook to a vnode.
501 */
502 int
503 fileassoc_add(struct vnode *vp, fileassoc_t id, void *data)
504 {
505 return fileassoc_add_hint(vp, id, data, 0);
506 }
507
508 /*
509 * Clear a hook from a vnode.
510 */
511 int
512 fileassoc_clear(struct vnode *vp, fileassoc_t id)
513 {
514 struct fileassoc_hash_entry *mhe;
515 fileassoc_cleanup_cb_t cleanup_cb;
516
517 mhe = fileassoc_file_lookup(vp, 0);
518 if (mhe == NULL)
519 return (ENOENT);
520
521 cleanup_cb = fileassoc_hooks[id].hook_cleanup_cb;
522 if ((mhe->hooks[id] != NULL) && cleanup_cb != NULL)
523 cleanup_cb(mhe->hooks[id], FILEASSOC_CLEANUP_FILE);
524
525 mhe->hooks[id] = NULL;
526
527 return (0);
528 }
529