1 /* $NetBSD: kern_fileassoc.c,v 1.12 2006/11/20 21:50:51 elad Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Elad Efrat <elad@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.12 2006/11/20 21:50:51 elad 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 #include <sys/fstypes.h>
49
50 static struct fileassoc_hash_entry *
51 fileassoc_file_lookup(struct vnode *, fhandle_t *);
52 static struct fileassoc_hash_entry *
53 fileassoc_file_add(struct vnode *, fhandle_t *);
54
55 /*
56 * Hook entry.
57 * Includes the hook name for identification and private hook clear callback.
58 */
59 struct fileassoc_hook {
60 const char *hook_name; /* Hook name. */
61 fileassoc_cleanup_cb_t hook_cleanup_cb; /* Hook clear callback. */
62 };
63
64 /* An entry in the per-device hash table. */
65 struct fileassoc_hash_entry {
66 fhandle_t *handle; /* File handle */
67 void *hooks[FILEASSOC_NHOOKS]; /* Hooks. */
68 LIST_ENTRY(fileassoc_hash_entry) entries; /* List pointer. */
69 };
70
71 LIST_HEAD(fileassoc_hashhead, fileassoc_hash_entry);
72
73 struct fileassoc_table {
74 struct fileassoc_hashhead *hash_tbl;
75 size_t hash_size; /* Number of slots. */
76 struct mount *tbl_mntpt;
77 u_long hash_mask;
78 void *tables[FILEASSOC_NHOOKS];
79 LIST_ENTRY(fileassoc_table) hash_list; /* List pointer. */
80 };
81
82 struct fileassoc_hook fileassoc_hooks[FILEASSOC_NHOOKS];
83 int fileassoc_nhooks;
84
85 /* Global list of hash tables, one per device. */
86 LIST_HEAD(, fileassoc_table) fileassoc_tables;
87
88 /*
89 * Hashing function: Takes a number modulus the mask to give back an
90 * index into the hash table.
91 */
92 #define FILEASSOC_HASH(tbl, handle) \
93 (hash32_buf((handle), FHANDLE_SIZE(handle), HASH32_BUF_INIT) \
94 & ((tbl)->hash_mask))
95
96 /*
97 * Initialize the fileassoc subsystem.
98 */
99 void
100 fileassoc_init(void)
101 {
102 memset(fileassoc_hooks, 0, sizeof(fileassoc_hooks));
103 fileassoc_nhooks = 0;
104 }
105
106 /*
107 * Register a new hook.
108 */
109 fileassoc_t
110 fileassoc_register(const char *name, fileassoc_cleanup_cb_t cleanup_cb)
111 {
112 int i;
113
114 if (fileassoc_nhooks >= FILEASSOC_NHOOKS)
115 return (-1);
116
117 for (i = 0; i < FILEASSOC_NHOOKS; i++)
118 if (fileassoc_hooks[i].hook_name == NULL)
119 break;
120
121 fileassoc_hooks[i].hook_name = name;
122 fileassoc_hooks[i].hook_cleanup_cb = cleanup_cb;
123
124 fileassoc_nhooks++;
125
126 return (i);
127 }
128
129 /*
130 * Deregister a hook.
131 */
132 int
133 fileassoc_deregister(fileassoc_t id)
134 {
135 if (id < 0 || id >= FILEASSOC_NHOOKS)
136 return (EINVAL);
137
138 fileassoc_hooks[id].hook_name = NULL;
139 fileassoc_hooks[id].hook_cleanup_cb = NULL;
140
141 fileassoc_nhooks--;
142
143 return (0);
144 }
145
146 /*
147 * Get the hash table for the specified device.
148 */
149 static struct fileassoc_table *
150 fileassoc_table_lookup(struct mount *mp)
151 {
152 struct fileassoc_table *tbl;
153
154 LIST_FOREACH(tbl, &fileassoc_tables, hash_list) {
155 if (tbl->tbl_mntpt == mp)
156 return (tbl);
157 }
158
159 return (NULL);
160 }
161
162 /*
163 * Perform a lookup on a hash table. If hint is non-zero then use the value
164 * of the hint as the identifier instead of performing a lookup for the
165 * fileid.
166 */
167 static struct fileassoc_hash_entry *
168 fileassoc_file_lookup(struct vnode *vp, fhandle_t *hint)
169 {
170 struct fileassoc_table *tbl;
171 struct fileassoc_hashhead *tble;
172 struct fileassoc_hash_entry *e;
173 size_t indx;
174 fhandle_t *th;
175 int error;
176
177 if (hint == NULL) {
178 error = vfs_composefh_alloc(vp, &th);
179 if (error)
180 return (NULL);
181 } else
182 th = hint;
183
184 tbl = fileassoc_table_lookup(vp->v_mount);
185 if (tbl == NULL) {
186 if (hint == NULL)
187 vfs_composefh_free(th);
188
189 return (NULL);
190 }
191
192 indx = FILEASSOC_HASH(tbl, th);
193 tble = &(tbl->hash_tbl[indx]);
194
195 LIST_FOREACH(e, tble, entries) {
196 if ((e != NULL) &&
197 ((FHANDLE_FILEID(e->handle)->fid_len ==
198 FHANDLE_FILEID(th)->fid_len)) &&
199 (memcmp(FHANDLE_FILEID(e->handle), FHANDLE_FILEID(th),
200 (FHANDLE_FILEID(th))->fid_len) == 0)) {
201 if (hint == NULL)
202 vfs_composefh_free(th);
203
204 return (e);
205 }
206 }
207
208 if (hint == NULL)
209 vfs_composefh_free(th);
210
211 return (NULL);
212 }
213
214 /*
215 * Return hook data associated with a vnode.
216 */
217 void *
218 fileassoc_lookup(struct vnode *vp, fileassoc_t id)
219 {
220 struct fileassoc_hash_entry *mhe;
221
222 mhe = fileassoc_file_lookup(vp, NULL);
223 if (mhe == NULL)
224 return (NULL);
225
226 return (mhe->hooks[id]);
227 }
228
229 /*
230 * Create a new fileassoc table.
231 */
232 int
233 fileassoc_table_add(struct mount *mp, size_t size)
234 {
235 struct fileassoc_table *tbl;
236
237 /* Check for existing table for device. */
238 if (fileassoc_table_lookup(mp) != NULL)
239 return (EEXIST);
240
241 /* Allocate and initialize a Veriexec hash table. */
242 tbl = malloc(sizeof(*tbl), M_TEMP, M_WAITOK | M_ZERO);
243 tbl->hash_size = size;
244 tbl->tbl_mntpt = mp;
245 tbl->hash_tbl = hashinit(size, HASH_LIST, M_TEMP,
246 M_WAITOK | M_ZERO, &tbl->hash_mask);
247
248 LIST_INSERT_HEAD(&fileassoc_tables, tbl, hash_list);
249
250 return (0);
251 }
252
253 /*
254 * Delete a table.
255 */
256 int
257 fileassoc_table_delete(struct mount *mp)
258 {
259 struct fileassoc_table *tbl;
260 struct fileassoc_hashhead *hh;
261 u_long i;
262 int j;
263
264 tbl = fileassoc_table_lookup(mp);
265 if (tbl == NULL)
266 return (EEXIST);
267
268 /* Remove all entries from the table and lists */
269 hh = tbl->hash_tbl;
270 for (i = 0; i < tbl->hash_size; i++) {
271 struct fileassoc_hash_entry *mhe;
272
273 while (LIST_FIRST(&hh[i]) != NULL) {
274 mhe = LIST_FIRST(&hh[i]);
275 LIST_REMOVE(mhe, entries);
276
277 for (j = 0; j < fileassoc_nhooks; j++)
278 if (fileassoc_hooks[j].hook_cleanup_cb != NULL)
279 (fileassoc_hooks[j].hook_cleanup_cb)
280 (mhe->hooks[j],
281 FILEASSOC_CLEANUP_FILE);
282
283 vfs_composefh_free(mhe->handle);
284 free(mhe, M_TEMP);
285 }
286 }
287
288 for (j = 0; j < fileassoc_nhooks; j++)
289 if (fileassoc_hooks[j].hook_cleanup_cb != NULL)
290 (fileassoc_hooks[j].hook_cleanup_cb)(tbl->tables[j],
291 FILEASSOC_CLEANUP_TABLE);
292
293 /* Remove hash table and sysctl node */
294 hashdone(tbl->hash_tbl, M_TEMP);
295 LIST_REMOVE(tbl, hash_list);
296
297 return (0);
298 }
299
300 /*
301 * Run a callback for each hook entry in a table.
302 */
303 int
304 fileassoc_table_run(struct mount *mp, fileassoc_t id, fileassoc_cb_t cb)
305 {
306 struct fileassoc_table *tbl;
307 struct fileassoc_hashhead *hh;
308 u_long i;
309
310 tbl = fileassoc_table_lookup(mp);
311 if (tbl == NULL)
312 return (EEXIST);
313
314 hh = tbl->hash_tbl;
315 for (i = 0; i < tbl->hash_size; i++) {
316 struct fileassoc_hash_entry *mhe;
317
318 LIST_FOREACH(mhe, &hh[i], entries) {
319 if (mhe->hooks[id] != NULL)
320 cb(mhe->hooks[id]);
321 }
322 }
323
324 return (0);
325 }
326
327 /*
328 * Clear a table for a given hook.
329 */
330 int
331 fileassoc_table_clear(struct mount *mp, fileassoc_t id)
332 {
333 struct fileassoc_table *tbl;
334 struct fileassoc_hashhead *hh;
335 fileassoc_cleanup_cb_t cleanup_cb;
336 u_long i;
337
338 tbl = fileassoc_table_lookup(mp);
339 if (tbl == NULL)
340 return (EEXIST);
341
342 cleanup_cb = fileassoc_hooks[id].hook_cleanup_cb;
343
344 hh = tbl->hash_tbl;
345 for (i = 0; i < tbl->hash_size; i++) {
346 struct fileassoc_hash_entry *mhe;
347
348 LIST_FOREACH(mhe, &hh[i], entries) {
349 if ((mhe->hooks[id] != NULL) && cleanup_cb != NULL)
350 cleanup_cb(mhe->hooks[id],
351 FILEASSOC_CLEANUP_FILE);
352
353 mhe->hooks[id] = NULL;
354 }
355 }
356
357 if ((tbl->tables[id] != NULL) && cleanup_cb != NULL)
358 cleanup_cb(tbl->tables[id], FILEASSOC_CLEANUP_TABLE);
359
360 tbl->tables[id] = NULL;
361
362 return (0);
363 }
364
365 /*
366 * Add hook-specific data on a fileassoc table.
367 */
368 int
369 fileassoc_tabledata_add(struct mount *mp, fileassoc_t id, void *data)
370 {
371 struct fileassoc_table *tbl;
372
373 tbl = fileassoc_table_lookup(mp);
374 if (tbl == NULL)
375 return (EFAULT);
376
377 tbl->tables[id] = data;
378
379 return (0);
380 }
381
382 /*
383 * Clear hook-specific data on a fileassoc table.
384 */
385 int
386 fileassoc_tabledata_clear(struct mount *mp, fileassoc_t id)
387 {
388 struct fileassoc_table *tbl;
389
390 tbl = fileassoc_table_lookup(mp);
391 if (tbl == NULL)
392 return (EFAULT);
393
394 tbl->tables[id] = NULL;
395
396 return (0);
397 }
398
399 /*
400 * Retrieve hook-specific data from a fileassoc table.
401 */
402 void *
403 fileassoc_tabledata_lookup(struct mount *mp, fileassoc_t id)
404 {
405 struct fileassoc_table *tbl;
406
407 tbl = fileassoc_table_lookup(mp);
408 if (tbl == NULL)
409 return (NULL);
410
411 return (tbl->tables[id]);
412 }
413
414 /*
415 * Add a file entry to a table.
416 */
417 static struct fileassoc_hash_entry *
418 fileassoc_file_add(struct vnode *vp, fhandle_t *hint)
419 {
420 struct fileassoc_table *tbl;
421 struct fileassoc_hashhead *vhh;
422 struct fileassoc_hash_entry *e;
423 size_t indx;
424 fhandle_t *th;
425 int error;
426
427 if (hint == NULL) {
428 error = vfs_composefh_alloc(vp, &th);
429 if (error)
430 return (NULL);
431 } else
432 th = hint;
433
434 e = fileassoc_file_lookup(vp, th);
435 if (e != NULL) {
436 if (hint == NULL)
437 vfs_composefh_free(th);
438
439 return (e);
440 }
441
442 tbl = fileassoc_table_lookup(vp->v_mount);
443 if (tbl == NULL) {
444 if (hint == NULL)
445 vfs_composefh_free(th);
446
447 return (NULL);
448 }
449
450 indx = FILEASSOC_HASH(tbl, th);
451 vhh = &(tbl->hash_tbl[indx]);
452
453 e = malloc(sizeof(*e), M_TEMP, M_WAITOK | M_ZERO);
454 e->handle = th;
455 LIST_INSERT_HEAD(vhh, e, entries);
456
457 return (e);
458 }
459
460 /*
461 * Delete a file entry from a table.
462 */
463 int
464 fileassoc_file_delete(struct vnode *vp)
465 {
466 struct fileassoc_hash_entry *mhe;
467 int i;
468
469 mhe = fileassoc_file_lookup(vp, NULL);
470 if (mhe == NULL)
471 return (ENOENT);
472
473 LIST_REMOVE(mhe, entries);
474
475 for (i = 0; i < fileassoc_nhooks; i++)
476 if (fileassoc_hooks[i].hook_cleanup_cb != NULL)
477 (fileassoc_hooks[i].hook_cleanup_cb)(mhe->hooks[i],
478 FILEASSOC_CLEANUP_FILE);
479
480 free(mhe, M_TEMP);
481
482 return (0);
483 }
484
485 /*
486 * Add a hook to a vnode.
487 */
488 int
489 fileassoc_add(struct vnode *vp, fileassoc_t id, void *data)
490 {
491 struct fileassoc_hash_entry *e;
492
493 e = fileassoc_file_lookup(vp, NULL);
494 if (e == NULL) {
495 e = fileassoc_file_add(vp, NULL);
496 if (e == NULL)
497 return (ENOTDIR);
498 }
499
500 if (e->hooks[id] != NULL)
501 return (EEXIST);
502
503 e->hooks[id] = data;
504
505 return (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, NULL);
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 }
Cache object: 438742f89fde923c1b28aa2605632333
|