kern_fileassoc.c revision 1.7
1/* $NetBSD: kern_fileassoc.c,v 1.7 2006/08/13 06:21:10 xtraeme 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.7 2006/08/13 06:21:10 xtraeme 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 */ 53struct 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. */ 59struct 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 65LIST_HEAD(fileassoc_hashhead, fileassoc_hash_entry); 66 67struct 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 76struct fileassoc_hook fileassoc_hooks[FILEASSOC_NHOOKS]; 77int fileassoc_nhooks; 78 79/* Global list of hash tables, one per device. */ 80LIST_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 */ 93void 94fileassoc_init(void) 95{ 96 memset(fileassoc_hooks, 0, sizeof(fileassoc_hooks)); 97 fileassoc_nhooks = 0; 98} 99 100/* 101 * Register a new hook. 102 */ 103fileassoc_t 104fileassoc_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 */ 126int 127fileassoc_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 */ 143static struct fileassoc_table * 144fileassoc_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. 158 */ 159static struct fileassoc_hash_entry * 160fileassoc_file_lookup(struct vnode *vp) 161{ 162 struct fileassoc_table *tbl; 163 struct fileassoc_hashhead *tble; 164 struct fileassoc_hash_entry *e; 165 struct vattr va; 166 size_t indx; 167 int error; 168 169 error = VOP_GETATTR(vp, &va, curlwp->l_cred, curlwp); 170 if (error) 171 return (NULL); 172 173 tbl = fileassoc_table_lookup(vp->v_mount); 174 if (tbl == NULL) 175 return (NULL); 176 177 /* 178 * XXX: We should NOT rely on fileid here! 179 */ 180 indx = FILEASSOC_HASH(tbl, va.va_fileid); 181 tble = &(tbl->hash_tbl[indx & ((tbl)->hash_mask)]); 182 183 LIST_FOREACH(e, tble, entries) { 184 if ((e != NULL) && (e->fileid == va.va_fileid)) 185 return (e); 186 } 187 188 return (NULL); 189} 190 191/* 192 * Return hook data associated with a vnode. 193 */ 194void * 195fileassoc_lookup(struct vnode *vp, fileassoc_t id) 196{ 197 struct fileassoc_hash_entry *mhe; 198 199 mhe = fileassoc_file_lookup(vp); 200 if (mhe == NULL) 201 return (NULL); 202 203 return (mhe->hooks[id]); 204} 205 206/* 207 * Create a new fileassoc table. 208 */ 209int 210fileassoc_table_add(struct mount *mp, size_t size) 211{ 212 struct fileassoc_table *tbl; 213 214 /* Check for existing table for device. */ 215 if (fileassoc_table_lookup(mp) != NULL) 216 return (EEXIST); 217 218 /* Allocate and initialize a Veriexec hash table. */ 219 tbl = malloc(sizeof(*tbl), M_TEMP, M_WAITOK | M_ZERO); 220 tbl->hash_size = size; 221 tbl->tbl_mntpt = mp; 222 tbl->hash_tbl = hashinit(size, HASH_LIST, M_TEMP, 223 M_WAITOK | M_ZERO, &tbl->hash_mask); 224 225 LIST_INSERT_HEAD(&fileassoc_tables, tbl, hash_list); 226 227 return (0); 228} 229 230/* 231 * Delete a table. 232 */ 233int 234fileassoc_table_delete(struct mount *mp) 235{ 236 struct fileassoc_table *tbl; 237 struct fileassoc_hashhead *hh; 238 u_long i; 239 int j; 240 241 tbl = fileassoc_table_lookup(mp); 242 if (tbl == NULL) 243 return (EEXIST); 244 245 /* Remove all entries from the table and lists */ 246 hh = tbl->hash_tbl; 247 for (i = 0; i < tbl->hash_size; i++) { 248 struct fileassoc_hash_entry *mhe; 249 250 while (LIST_FIRST(&hh[i]) != NULL) { 251 mhe = LIST_FIRST(&hh[i]); 252 LIST_REMOVE(mhe, entries); 253 254 for (j = 0; j < fileassoc_nhooks; j++) 255 if (fileassoc_hooks[j].hook_cleanup_cb != NULL) 256 (fileassoc_hooks[j].hook_cleanup_cb) 257 (mhe->hooks[j], 258 FILEASSOC_CLEANUP_FILE); 259 260 free(mhe, M_TEMP); 261 } 262 } 263 264 for (j = 0; j < fileassoc_nhooks; j++) 265 if (fileassoc_hooks[j].hook_cleanup_cb != NULL) 266 (fileassoc_hooks[j].hook_cleanup_cb)(tbl->tables[j], 267 FILEASSOC_CLEANUP_TABLE); 268 269 /* Remove hash table and sysctl node */ 270 hashdone(tbl->hash_tbl, M_TEMP); 271 LIST_REMOVE(tbl, hash_list); 272 273 return (0); 274} 275 276/* 277 * Run a callback for each hook entry in a table. 278 */ 279int 280fileassoc_table_run(struct mount *mp, fileassoc_t id, fileassoc_cb_t cb) 281{ 282 struct fileassoc_table *tbl; 283 struct fileassoc_hashhead *hh; 284 u_long i; 285 286 tbl = fileassoc_table_lookup(mp); 287 if (tbl == NULL) 288 return (EEXIST); 289 290 hh = tbl->hash_tbl; 291 for (i = 0; i < tbl->hash_size; i++) { 292 struct fileassoc_hash_entry *mhe; 293 294 LIST_FOREACH(mhe, &hh[i], entries) { 295 if (mhe->hooks[id] != NULL) 296 cb(mhe->hooks[id]); 297 } 298 } 299 300 return (0); 301} 302 303/* 304 * Clear a table for a given hook. 305 */ 306int 307fileassoc_table_clear(struct mount *mp, fileassoc_t id) 308{ 309 struct fileassoc_table *tbl; 310 struct fileassoc_hashhead *hh; 311 fileassoc_cleanup_cb_t cleanup_cb; 312 u_long i; 313 314 tbl = fileassoc_table_lookup(mp); 315 if (tbl == NULL) 316 return (EEXIST); 317 318 cleanup_cb = fileassoc_hooks[id].hook_cleanup_cb; 319 320 hh = tbl->hash_tbl; 321 for (i = 0; i < tbl->hash_size; i++) { 322 struct fileassoc_hash_entry *mhe; 323 324 LIST_FOREACH(mhe, &hh[i], entries) { 325 if ((mhe->hooks[id] != NULL) && cleanup_cb != NULL) 326 cleanup_cb(mhe->hooks[id], 327 FILEASSOC_CLEANUP_FILE); 328 329 mhe->hooks[id] = NULL; 330 } 331 } 332 333 if ((tbl->tables[id] != NULL) && cleanup_cb != NULL) 334 cleanup_cb(tbl->tables[id], FILEASSOC_CLEANUP_TABLE); 335 336 tbl->tables[id] = NULL; 337 338 return (0); 339} 340 341/* 342 * Add hook-specific data on a fileassoc table. 343 */ 344int 345fileassoc_tabledata_add(struct mount *mp, fileassoc_t id, void *data) 346{ 347 struct fileassoc_table *tbl; 348 349 tbl = fileassoc_table_lookup(mp); 350 if (tbl == NULL) 351 return (EFAULT); 352 353 tbl->tables[id] = data; 354 355 return (0); 356} 357 358/* 359 * Clear hook-specific data on a fileassoc table. 360 */ 361int 362fileassoc_tabledata_clear(struct mount *mp, fileassoc_t id) 363{ 364 struct fileassoc_table *tbl; 365 366 tbl = fileassoc_table_lookup(mp); 367 if (tbl == NULL) 368 return (EFAULT); 369 370 tbl->tables[id] = NULL; 371 372 return (0); 373} 374 375/* 376 * Retrieve hook-specific data from a fileassoc table. 377 */ 378void * 379fileassoc_tabledata_lookup(struct mount *mp, fileassoc_t id) 380{ 381 struct fileassoc_table *tbl; 382 383 tbl = fileassoc_table_lookup(mp); 384 if (tbl == NULL) 385 return (NULL); 386 387 return (tbl->tables[id]); 388} 389 390/* 391 * Add a file entry to a table. 392 */ 393static struct fileassoc_hash_entry * 394fileassoc_file_add(struct vnode *vp) 395{ 396 struct fileassoc_table *tbl; 397 struct fileassoc_hashhead *vhh; 398 struct fileassoc_hash_entry *e; 399 struct vattr va; 400 size_t indx; 401 int error; 402 403 error = VOP_GETATTR(vp, &va, curlwp->l_cred, curlwp); 404 if (error) 405 return (NULL); 406 407 e = fileassoc_file_lookup(vp); 408 if (e != NULL) 409 return (e); 410 411 tbl = fileassoc_table_lookup(vp->v_mount); 412 if (tbl == NULL) 413 return (NULL); 414 415 /* 416 * XXX: We should NOT rely on fileid here! 417 */ 418 indx = FILEASSOC_HASH(tbl, va.va_fileid); 419 vhh = &(tbl->hash_tbl[indx & ((tbl)->hash_mask)]); 420 421 e = malloc(sizeof(*e), M_TEMP, M_WAITOK | M_ZERO); 422 e->fileid = va.va_fileid; 423 LIST_INSERT_HEAD(vhh, e, entries); 424 425 return (e); 426} 427 428/* 429 * Delete a file entry from a table. 430 */ 431int 432fileassoc_file_delete(struct vnode *vp) 433{ 434 struct fileassoc_hash_entry *mhe; 435 int i; 436 437 mhe = fileassoc_file_lookup(vp); 438 if (mhe == NULL) 439 return (ENOENT); 440 441 LIST_REMOVE(mhe, entries); 442 443 for (i = 0; i < fileassoc_nhooks; i++) 444 if (fileassoc_hooks[i].hook_cleanup_cb != NULL) 445 (fileassoc_hooks[i].hook_cleanup_cb)(mhe->hooks[i], 446 FILEASSOC_CLEANUP_FILE); 447 448 free(mhe, M_TEMP); 449 450 return (0); 451} 452 453/* 454 * Add a hook to a vnode. 455 */ 456int 457fileassoc_add(struct vnode *vp, fileassoc_t id, void *data) 458{ 459 struct fileassoc_hash_entry *e; 460 461 e = fileassoc_file_lookup(vp); 462 if (e == NULL) { 463 e = fileassoc_file_add(vp); 464 if (e == NULL) 465 return (ENOTDIR); 466 } 467 468 if (e->hooks[id] != NULL) 469 return (EEXIST); 470 471 e->hooks[id] = data; 472 473 return (0); 474} 475 476/* 477 * Clear a hook from a vnode. 478 */ 479int 480fileassoc_clear(struct vnode *vp, fileassoc_t id) 481{ 482 struct fileassoc_hash_entry *mhe; 483 fileassoc_cleanup_cb_t cleanup_cb; 484 485 mhe = fileassoc_file_lookup(vp); 486 if (mhe == NULL) 487 return (ENOENT); 488 489 cleanup_cb = fileassoc_hooks[id].hook_cleanup_cb; 490 if ((mhe->hooks[id] != NULL) && cleanup_cb != NULL) 491 cleanup_cb(mhe->hooks[id], FILEASSOC_CLEANUP_FILE); 492 493 mhe->hooks[id] = NULL; 494 495 return (0); 496} 497