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