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