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