citrus_mapper.c revision 330897
1/* $FreeBSD: stable/11/lib/libc/iconv/citrus_mapper.c 330897 2018-03-14 03:19:51Z eadler $ */
2/*	$NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $	*/
3
4/*-
5 * SPDX-License-Identifier: BSD-2-Clause
6 *
7 * Copyright (c)2003 Citrus Project,
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <sys/queue.h>
36
37#include <assert.h>
38#include <errno.h>
39#include <limits.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43
44#include "citrus_namespace.h"
45#include "citrus_types.h"
46#include "citrus_region.h"
47#include "citrus_lock.h"
48#include "citrus_memstream.h"
49#include "citrus_bcs.h"
50#include "citrus_mmap.h"
51#include "citrus_module.h"
52#include "citrus_hash.h"
53#include "citrus_mapper.h"
54
55#define _CITRUS_MAPPER_DIR	"mapper.dir"
56
57#define CM_HASH_SIZE 101
58#define REFCOUNT_PERSISTENT	-1
59
60static pthread_rwlock_t		cm_lock = PTHREAD_RWLOCK_INITIALIZER;
61
62struct _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
72int
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;
110quit:
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
128static int
129lookup_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
175quit:
176	_unmap_file(&r);
177	return (ret);
178}
179
180/*
181 * mapper_close:
182 *	simply close a mapper. (without handling hash)
183 */
184static void
185mapper_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 */
203static int
204mapper_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
271err:
272	mapper_close(cm);
273	return (ret);
274}
275
276/*
277 * _citrus_mapper_open_direct:
278 *	open a mapper.
279 */
280int
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 */
292static __inline int
293hash_func(const char *key)
294{
295
296	return (_string_hash_func(key, CM_HASH_SIZE));
297}
298
299/*
300 * match_func
301 */
302static __inline int
303match_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 */
313int
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;
364quit:
365	UNLOCK(&cm_lock);
366
367	return (ret);
368}
369
370/*
371 * _citrus_mapper_close:
372 *	close the specified mapper.
373 */
374void
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;
391quit:
392		UNLOCK(&cm_lock);
393	}
394}
395
396/*
397 * _citrus_mapper_set_persistent:
398 *	set persistent count.
399 */
400void
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