1 1.10 martin /* $NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $ */ 2 1.1 tshiozak 3 1.1 tshiozak /*- 4 1.1 tshiozak * Copyright (c)2003 Citrus Project, 5 1.1 tshiozak * All rights reserved. 6 1.1 tshiozak * 7 1.1 tshiozak * Redistribution and use in source and binary forms, with or without 8 1.1 tshiozak * modification, are permitted provided that the following conditions 9 1.1 tshiozak * are met: 10 1.1 tshiozak * 1. Redistributions of source code must retain the above copyright 11 1.1 tshiozak * notice, this list of conditions and the following disclaimer. 12 1.1 tshiozak * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 tshiozak * notice, this list of conditions and the following disclaimer in the 14 1.1 tshiozak * documentation and/or other materials provided with the distribution. 15 1.1 tshiozak * 16 1.1 tshiozak * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 tshiozak * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 tshiozak * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 tshiozak * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 tshiozak * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 tshiozak * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 tshiozak * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 tshiozak * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 tshiozak * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 tshiozak * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 tshiozak * SUCH DAMAGE. 27 1.1 tshiozak */ 28 1.1 tshiozak 29 1.1 tshiozak #include <sys/cdefs.h> 30 1.1 tshiozak #if defined(LIBC_SCCS) && !defined(lint) 31 1.10 martin __RCSID("$NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $"); 32 1.1 tshiozak #endif /* LIBC_SCCS and not lint */ 33 1.1 tshiozak 34 1.1 tshiozak #include "namespace.h" 35 1.1 tshiozak #include "reentrant.h" 36 1.1 tshiozak #include <assert.h> 37 1.1 tshiozak #include <stdio.h> 38 1.1 tshiozak #include <stdlib.h> 39 1.1 tshiozak #include <string.h> 40 1.1 tshiozak #include <errno.h> 41 1.1 tshiozak #include <limits.h> 42 1.1 tshiozak #include <sys/types.h> 43 1.1 tshiozak #include <sys/stat.h> 44 1.2 tshiozak #include <sys/queue.h> 45 1.1 tshiozak 46 1.1 tshiozak #include "citrus_namespace.h" 47 1.1 tshiozak #include "citrus_types.h" 48 1.1 tshiozak #include "citrus_region.h" 49 1.1 tshiozak #include "citrus_memstream.h" 50 1.1 tshiozak #include "citrus_bcs.h" 51 1.1 tshiozak #include "citrus_mmap.h" 52 1.1 tshiozak #include "citrus_module.h" 53 1.1 tshiozak #include "citrus_hash.h" 54 1.1 tshiozak #include "citrus_mapper.h" 55 1.1 tshiozak 56 1.1 tshiozak #define _CITRUS_MAPPER_DIR "mapper.dir" 57 1.1 tshiozak 58 1.1 tshiozak #define CM_HASH_SIZE 101 59 1.1 tshiozak #define REFCOUNT_PERSISTENT -1 60 1.1 tshiozak 61 1.1 tshiozak #ifdef _REENTRANT 62 1.1 tshiozak static rwlock_t lock = RWLOCK_INITIALIZER; 63 1.1 tshiozak #endif 64 1.1 tshiozak 65 1.1 tshiozak struct _citrus_mapper_area { 66 1.1 tshiozak _CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE) ma_cache; 67 1.1 tshiozak char *ma_dir; 68 1.1 tshiozak }; 69 1.1 tshiozak 70 1.1 tshiozak /* 71 1.1 tshiozak * _citrus_mapper_create_area: 72 1.1 tshiozak * create mapper area 73 1.1 tshiozak */ 74 1.1 tshiozak 75 1.1 tshiozak int 76 1.1 tshiozak _citrus_mapper_create_area( 77 1.1 tshiozak struct _citrus_mapper_area *__restrict *__restrict rma, 78 1.1 tshiozak const char *__restrict area) 79 1.1 tshiozak { 80 1.1 tshiozak struct stat st; 81 1.1 tshiozak int ret; 82 1.1 tshiozak char path[PATH_MAX]; 83 1.1 tshiozak struct _citrus_mapper_area *ma; 84 1.1 tshiozak 85 1.1 tshiozak rwlock_wrlock(&lock); 86 1.1 tshiozak 87 1.1 tshiozak if (*rma != NULL) { 88 1.1 tshiozak ret = 0; 89 1.1 tshiozak goto quit; 90 1.1 tshiozak } 91 1.1 tshiozak 92 1.6 christos snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR); 93 1.1 tshiozak 94 1.1 tshiozak ret = stat(path, &st); 95 1.1 tshiozak if (ret) 96 1.1 tshiozak goto quit; 97 1.1 tshiozak 98 1.1 tshiozak ma = malloc(sizeof(*ma)); 99 1.1 tshiozak if (ma == NULL) { 100 1.1 tshiozak ret = errno; 101 1.1 tshiozak goto quit; 102 1.1 tshiozak } 103 1.1 tshiozak ma->ma_dir = strdup(area); 104 1.1 tshiozak if (ma->ma_dir == NULL) { 105 1.10 martin free(ma); 106 1.1 tshiozak ret = errno; 107 1.1 tshiozak goto quit; 108 1.1 tshiozak } 109 1.1 tshiozak _CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE); 110 1.1 tshiozak 111 1.1 tshiozak *rma = ma; 112 1.1 tshiozak ret = 0; 113 1.1 tshiozak quit: 114 1.1 tshiozak rwlock_unlock(&lock); 115 1.1 tshiozak 116 1.1 tshiozak return ret; 117 1.1 tshiozak } 118 1.1 tshiozak 119 1.1 tshiozak 120 1.1 tshiozak /* 121 1.1 tshiozak * lookup_mapper_entry: 122 1.1 tshiozak * lookup mapper.dir entry in the specified directory. 123 1.1 tshiozak * 124 1.1 tshiozak * line format of iconv.dir file: 125 1.1 tshiozak * mapper module arg 126 1.1 tshiozak * mapper : mapper name. 127 1.1 tshiozak * module : mapper module name. 128 1.1 tshiozak * arg : argument for the module (generally, description file name) 129 1.1 tshiozak */ 130 1.1 tshiozak 131 1.1 tshiozak static int 132 1.1 tshiozak lookup_mapper_entry(const char *dir, const char *mapname, 133 1.1 tshiozak void *linebuf, size_t linebufsize, 134 1.1 tshiozak const char **module, const char **variable) 135 1.1 tshiozak { 136 1.1 tshiozak struct _region r; 137 1.1 tshiozak struct _memstream ms; 138 1.1 tshiozak int ret; 139 1.1 tshiozak const char *cp, *cq; 140 1.1 tshiozak char *p; 141 1.1 tshiozak size_t len; 142 1.1 tshiozak char path[PATH_MAX]; 143 1.1 tshiozak 144 1.1 tshiozak /* create mapper.dir path */ 145 1.6 christos snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR); 146 1.1 tshiozak 147 1.1 tshiozak /* open read stream */ 148 1.1 tshiozak ret = _map_file(&r, path); 149 1.1 tshiozak if (ret) 150 1.1 tshiozak return ret; 151 1.1 tshiozak 152 1.1 tshiozak _memstream_bind(&ms, &r); 153 1.1 tshiozak 154 1.1 tshiozak /* search the line matching to the map name */ 155 1.1 tshiozak cp = _memstream_matchline(&ms, mapname, &len, 0); 156 1.1 tshiozak if (!cp) { 157 1.1 tshiozak ret = ENOENT; 158 1.1 tshiozak goto quit; 159 1.1 tshiozak } 160 1.1 tshiozak if (!len || len>linebufsize-1) { 161 1.1 tshiozak ret = EINVAL; 162 1.1 tshiozak goto quit; 163 1.1 tshiozak } 164 1.1 tshiozak 165 1.1 tshiozak p = linebuf; 166 1.1 tshiozak /* get module name */ 167 1.1 tshiozak *module = p; 168 1.1 tshiozak cq = _bcs_skip_nonws_len(cp, &len); 169 1.1 tshiozak strlcpy(p, cp, (size_t)(cq-cp+1)); 170 1.1 tshiozak p += cq-cp+1; 171 1.1 tshiozak 172 1.1 tshiozak /* get variable */ 173 1.1 tshiozak *variable = p; 174 1.1 tshiozak cp = _bcs_skip_ws_len(cq, &len); 175 1.1 tshiozak strlcpy(p, cp, len+1); 176 1.1 tshiozak 177 1.1 tshiozak ret = 0; 178 1.1 tshiozak 179 1.1 tshiozak quit: 180 1.1 tshiozak _unmap_file(&r); 181 1.1 tshiozak return ret; 182 1.1 tshiozak } 183 1.1 tshiozak 184 1.1 tshiozak /* 185 1.4 tshiozak * mapper_close: 186 1.4 tshiozak * simply close a mapper. (without handling hash) 187 1.4 tshiozak */ 188 1.4 tshiozak static void 189 1.4 tshiozak mapper_close(struct _citrus_mapper *cm) 190 1.4 tshiozak { 191 1.4 tshiozak if (cm->cm_module) { 192 1.4 tshiozak if (cm->cm_ops) { 193 1.4 tshiozak if (cm->cm_closure) 194 1.4 tshiozak (*cm->cm_ops->mo_uninit)(cm); 195 1.4 tshiozak free(cm->cm_ops); 196 1.4 tshiozak } 197 1.4 tshiozak _citrus_unload_module(cm->cm_module); 198 1.4 tshiozak } 199 1.4 tshiozak free(cm->cm_traits); 200 1.4 tshiozak free(cm); 201 1.4 tshiozak } 202 1.4 tshiozak 203 1.4 tshiozak /* 204 1.1 tshiozak * mapper_open: 205 1.4 tshiozak * simply open a mapper. (without handling hash) 206 1.1 tshiozak */ 207 1.1 tshiozak static int 208 1.1 tshiozak mapper_open(struct _citrus_mapper_area *__restrict ma, 209 1.1 tshiozak struct _citrus_mapper * __restrict * __restrict rcm, 210 1.1 tshiozak const char * __restrict module, 211 1.1 tshiozak const char * __restrict variable) 212 1.1 tshiozak { 213 1.1 tshiozak int ret; 214 1.1 tshiozak struct _citrus_mapper *cm; 215 1.1 tshiozak _citrus_mapper_getops_t getops; 216 1.1 tshiozak 217 1.1 tshiozak /* initialize mapper handle */ 218 1.1 tshiozak cm = malloc(sizeof(*cm)); 219 1.1 tshiozak if (!cm) 220 1.1 tshiozak return errno; 221 1.1 tshiozak 222 1.1 tshiozak cm->cm_module = NULL; 223 1.1 tshiozak cm->cm_ops = NULL; 224 1.1 tshiozak cm->cm_closure = NULL; 225 1.1 tshiozak cm->cm_traits = NULL; 226 1.1 tshiozak cm->cm_refcount = 0; 227 1.1 tshiozak cm->cm_key = NULL; 228 1.1 tshiozak 229 1.1 tshiozak /* load module */ 230 1.1 tshiozak ret = _citrus_load_module(&cm->cm_module, module); 231 1.1 tshiozak if (ret) 232 1.1 tshiozak goto err; 233 1.1 tshiozak 234 1.1 tshiozak /* get operators */ 235 1.1 tshiozak getops = (_citrus_mapper_getops_t) 236 1.1 tshiozak _citrus_find_getops(cm->cm_module, module, "mapper"); 237 1.1 tshiozak if (!getops) { 238 1.1 tshiozak ret = EOPNOTSUPP; 239 1.1 tshiozak goto err; 240 1.1 tshiozak } 241 1.1 tshiozak cm->cm_ops = malloc(sizeof(*cm->cm_ops)); 242 1.1 tshiozak if (!cm->cm_ops) { 243 1.1 tshiozak ret = errno; 244 1.1 tshiozak goto err; 245 1.1 tshiozak } 246 1.1 tshiozak ret = (*getops)(cm->cm_ops, sizeof(*cm->cm_ops), 247 1.1 tshiozak _CITRUS_MAPPER_ABI_VERSION); 248 1.1 tshiozak if (ret) 249 1.1 tshiozak goto err; 250 1.1 tshiozak 251 1.1 tshiozak if (!cm->cm_ops->mo_init || 252 1.1 tshiozak !cm->cm_ops->mo_uninit || 253 1.1 tshiozak !cm->cm_ops->mo_convert || 254 1.8 tnozaki !cm->cm_ops->mo_init_state) { 255 1.8 tnozaki ret = EINVAL; 256 1.1 tshiozak goto err; 257 1.8 tnozaki } 258 1.1 tshiozak 259 1.1 tshiozak /* allocate traits structure */ 260 1.1 tshiozak cm->cm_traits = malloc(sizeof(*cm->cm_traits)); 261 1.1 tshiozak if (cm->cm_traits == NULL) { 262 1.1 tshiozak ret = errno; 263 1.1 tshiozak goto err; 264 1.1 tshiozak } 265 1.1 tshiozak /* initialize the mapper */ 266 1.1 tshiozak ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir, 267 1.1 tshiozak (const void *)variable, 268 1.1 tshiozak strlen(variable)+1, 269 1.1 tshiozak cm->cm_traits, sizeof(*cm->cm_traits)); 270 1.1 tshiozak if (ret) 271 1.1 tshiozak goto err; 272 1.3 tshiozak 273 1.1 tshiozak *rcm = cm; 274 1.1 tshiozak 275 1.1 tshiozak return 0; 276 1.4 tshiozak 277 1.1 tshiozak err: 278 1.4 tshiozak mapper_close(cm); 279 1.1 tshiozak return ret; 280 1.1 tshiozak } 281 1.1 tshiozak 282 1.1 tshiozak /* 283 1.1 tshiozak * _citrus_mapper_open_direct: 284 1.1 tshiozak * open a mapper. 285 1.1 tshiozak */ 286 1.1 tshiozak int 287 1.1 tshiozak _citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma, 288 1.1 tshiozak struct _citrus_mapper * __restrict * __restrict rcm, 289 1.1 tshiozak const char * __restrict module, 290 1.1 tshiozak const char * __restrict variable) 291 1.1 tshiozak { 292 1.1 tshiozak return mapper_open(ma, rcm, module, variable); 293 1.1 tshiozak } 294 1.1 tshiozak 295 1.1 tshiozak /* 296 1.1 tshiozak * hash_func 297 1.1 tshiozak */ 298 1.1 tshiozak static __inline int 299 1.1 tshiozak hash_func(const char *key) 300 1.1 tshiozak { 301 1.1 tshiozak return _string_hash_func(key, CM_HASH_SIZE); 302 1.1 tshiozak } 303 1.1 tshiozak 304 1.1 tshiozak /* 305 1.1 tshiozak * match_func 306 1.1 tshiozak */ 307 1.1 tshiozak static __inline int 308 1.1 tshiozak match_func(struct _citrus_mapper *cm, const char *key) 309 1.1 tshiozak { 310 1.1 tshiozak return strcmp(cm->cm_key, key); 311 1.1 tshiozak } 312 1.1 tshiozak 313 1.1 tshiozak /* 314 1.1 tshiozak * _citrus_mapper_open: 315 1.1 tshiozak * open a mapper with looking up "mapper.dir". 316 1.1 tshiozak */ 317 1.1 tshiozak int 318 1.1 tshiozak _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma, 319 1.1 tshiozak struct _citrus_mapper * __restrict * __restrict rcm, 320 1.1 tshiozak const char * __restrict mapname) 321 1.1 tshiozak { 322 1.1 tshiozak int ret; 323 1.1 tshiozak char linebuf[PATH_MAX]; 324 1.7 christos const char *module, *variable = NULL; 325 1.1 tshiozak struct _citrus_mapper *cm; 326 1.1 tshiozak int hashval; 327 1.1 tshiozak 328 1.1 tshiozak rwlock_wrlock(&lock); 329 1.1 tshiozak 330 1.1 tshiozak /* search in the cache */ 331 1.1 tshiozak hashval = hash_func(mapname); 332 1.1 tshiozak _CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname, 333 1.1 tshiozak hashval); 334 1.1 tshiozak if (cm) { 335 1.1 tshiozak /* found */ 336 1.1 tshiozak cm->cm_refcount++; 337 1.1 tshiozak *rcm = cm; 338 1.1 tshiozak ret = 0; 339 1.1 tshiozak goto quit; 340 1.1 tshiozak } 341 1.3 tshiozak 342 1.1 tshiozak /* search mapper entry */ 343 1.6 christos ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf, 344 1.6 christos (size_t)PATH_MAX, &module, &variable); 345 1.1 tshiozak if (ret) 346 1.1 tshiozak goto quit; 347 1.1 tshiozak 348 1.1 tshiozak /* open mapper */ 349 1.1 tshiozak ret = mapper_open(ma, &cm, module, variable); 350 1.1 tshiozak if (ret) 351 1.1 tshiozak goto quit; 352 1.1 tshiozak cm->cm_key = strdup(mapname); 353 1.1 tshiozak if (cm->cm_key == NULL) { 354 1.1 tshiozak ret = errno; 355 1.1 tshiozak rwlock_unlock(&lock); 356 1.1 tshiozak _mapper_close(cm); 357 1.1 tshiozak return ret; 358 1.1 tshiozak } 359 1.1 tshiozak 360 1.1 tshiozak /* insert to the cache */ 361 1.1 tshiozak cm->cm_refcount = 1; 362 1.1 tshiozak _CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval); 363 1.1 tshiozak 364 1.1 tshiozak *rcm = cm; 365 1.1 tshiozak ret = 0; 366 1.1 tshiozak quit: 367 1.1 tshiozak rwlock_unlock(&lock); 368 1.1 tshiozak return ret; 369 1.1 tshiozak } 370 1.1 tshiozak 371 1.1 tshiozak /* 372 1.1 tshiozak * _citrus_mapper_close: 373 1.1 tshiozak * close the specified mapper. 374 1.1 tshiozak */ 375 1.1 tshiozak void 376 1.1 tshiozak _citrus_mapper_close(struct _citrus_mapper *cm) 377 1.1 tshiozak { 378 1.1 tshiozak if (cm) { 379 1.1 tshiozak rwlock_wrlock(&lock); 380 1.4 tshiozak if (cm->cm_refcount == REFCOUNT_PERSISTENT) 381 1.4 tshiozak goto quit; 382 1.1 tshiozak if (cm->cm_refcount > 0) { 383 1.4 tshiozak if (--cm->cm_refcount > 0) 384 1.4 tshiozak goto quit; 385 1.4 tshiozak _CITRUS_HASH_REMOVE(cm, cm_entry); 386 1.4 tshiozak free(cm->cm_key); 387 1.1 tshiozak } 388 1.5 christos rwlock_unlock(&lock); 389 1.4 tshiozak mapper_close(cm); 390 1.5 christos return; 391 1.4 tshiozak quit: 392 1.1 tshiozak rwlock_unlock(&lock); 393 1.1 tshiozak } 394 1.1 tshiozak } 395 1.1 tshiozak 396 1.1 tshiozak /* 397 1.1 tshiozak * _citrus_mapper_set_persistent: 398 1.1 tshiozak * set persistent count. 399 1.1 tshiozak */ 400 1.1 tshiozak void 401 1.1 tshiozak _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm) 402 1.1 tshiozak { 403 1.1 tshiozak rwlock_wrlock(&lock); 404 1.1 tshiozak cm->cm_refcount = REFCOUNT_PERSISTENT; 405 1.1 tshiozak rwlock_unlock(&lock); 406 1.1 tshiozak } 407