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