Home | History | Annotate | Line # | Download | only in kern
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 (at) 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 
     51 static struct fileassoc_hash_entry *
     52 fileassoc_file_lookup(struct vnode *, fhandle_t *);
     53 static struct fileassoc_hash_entry *
     54 fileassoc_file_add(struct vnode *, fhandle_t *);
     55 
     56 /*
     57  * Hook entry.
     58  * Includes the hook name for identification and private hook clear callback.
     59  */
     60 struct 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. */
     66 struct 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 
     72 LIST_HEAD(fileassoc_hashhead, fileassoc_hash_entry);
     73 
     74 struct 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 
     83 struct fileassoc_hook fileassoc_hooks[FILEASSOC_NHOOKS];
     84 int fileassoc_nhooks;
     85 
     86 /* Global list of hash tables, one per device. */
     87 LIST_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  */
    101 void
    102 fileassoc_init(void)
    103 {
    104 	memset(fileassoc_hooks, 0, sizeof(fileassoc_hooks));
    105 	fileassoc_nhooks = 0;
    106 }
    107 
    108 /*
    109  * Register a new hook.
    110  */
    111 fileassoc_t
    112 fileassoc_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  */
    134 int
    135 fileassoc_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  */
    151 static struct fileassoc_table *
    152 fileassoc_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  */
    169 static struct fileassoc_hash_entry *
    170 fileassoc_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  */
    207 void *
    208 fileassoc_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  */
    222 int
    223 fileassoc_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  */
    246 int
    247 fileassoc_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  */
    293 int
    294 fileassoc_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  */
    320 int
    321 fileassoc_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  */
    358 int
    359 fileassoc_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  */
    375 int
    376 fileassoc_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  */
    392 void *
    393 fileassoc_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  */
    407 static struct fileassoc_hash_entry *
    408 fileassoc_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  */
    445 int
    446 fileassoc_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  */
    470 int
    471 fileassoc_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  */
    493 int
    494 fileassoc_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