1 /*        $NetBSD: citrus_mapper_std.c,v 1.13 2025/02/26 04:49:46 andvar Exp $  */
2 
3 /*-
4  * Copyright (c)2003, 2006 Citrus Project,
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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #if defined(LIBC_SCCS) && !defined(lint)
31 __RCSID("$NetBSD: citrus_mapper_std.c,v 1.13 2025/02/26 04:49:46 andvar Exp $");
32 #endif /* LIBC_SCCS and not lint */
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stdint.h>
40 #include <string.h>
41 #include <machine/endian.h>
42 #include <sys/queue.h>
43 
44 #include "citrus_namespace.h"
45 #include "citrus_types.h"
46 #include "citrus_bcs.h"
47 #include "citrus_region.h"
48 #include "citrus_mmap.h"
49 #include "citrus_module.h"
50 #include "citrus_hash.h"
51 #include "citrus_mapper.h"
52 #include "citrus_db.h"
53 #include "citrus_db_hash.h"
54 
55 #include "citrus_mapper_std.h"
56 #include "citrus_mapper_std_file.h"
57 
58 /* ---------------------------------------------------------------------- */
59 
60 _CITRUS_MAPPER_DECLS(mapper_std);
61 _CITRUS_MAPPER_DEF_OPS(mapper_std);
62 
63 
64 /* ---------------------------------------------------------------------- */
65 
66 int
_citrus_mapper_std_mapper_getops(struct _citrus_mapper_ops * ops,size_t lenops,u_int32_t expected_version)67 _citrus_mapper_std_mapper_getops(struct _citrus_mapper_ops *ops, size_t lenops,
68                                          u_int32_t expected_version)
69 {
70           if (expected_version<_CITRUS_MAPPER_ABI_VERSION || lenops<sizeof(*ops))
71                     return (EINVAL);
72 
73           memcpy(ops, &_citrus_mapper_std_mapper_ops,
74                  sizeof(_citrus_mapper_std_mapper_ops));
75 
76           return (0);
77 }
78 
79 /* ---------------------------------------------------------------------- */
80 
81 static int
82 /*ARGSUSED*/
rowcol_convert(struct _citrus_mapper_std * __restrict ms,_index_t * __restrict dst,_index_t src,void * __restrict ps)83 rowcol_convert(struct _citrus_mapper_std * __restrict ms,
84                  _index_t * __restrict dst, _index_t src,
85                  void * __restrict ps)
86 {
87           struct _citrus_mapper_std_rowcol *rc;
88           size_t i;
89           struct _citrus_mapper_std_linear_zone *lz;
90           _index_t n, idx = 0;
91           u_int32_t conv;
92 
93           _DIAGASSERT(ms != NULL);
94           _DIAGASSERT(dst != NULL);
95           /* ps may be unused */
96           rc = &ms->ms_rowcol;
97 
98           for (i = rc->rc_src_rowcol_len * rc->rc_src_rowcol_bits,
99                lz = &rc->rc_src_rowcol[0]; i > 0; ++lz) {
100                     i -= rc->rc_src_rowcol_bits;
101                     n = (src >> i) & rc->rc_src_rowcol_mask;
102                     if (n < lz->begin || n > lz->end) {
103                               switch (rc->rc_oob_mode) {
104                               case _CITRUS_MAPPER_STD_OOB_NONIDENTICAL:
105                                         *dst = rc->rc_dst_invalid;
106                                         return _MAPPER_CONVERT_NONIDENTICAL;
107                               case _CITRUS_MAPPER_STD_OOB_ILSEQ:
108                                         return _MAPPER_CONVERT_ILSEQ;
109                               default:
110                                         return _MAPPER_CONVERT_FATAL;
111                               }
112                     }
113                     idx = idx * lz->width + n - lz->begin;
114           }
115           switch (rc->rc_dst_unit_bits) {
116           case 8:
117                     conv = _region_peek8(&rc->rc_table, idx);
118                     break;
119           case 16:
120                     conv = be16toh(_region_peek16(&rc->rc_table, idx*2));
121                     break;
122           case 32:
123                     conv = be32toh(_region_peek32(&rc->rc_table, idx*4));
124                     break;
125           default:
126                     return _MAPPER_CONVERT_FATAL;
127           }
128 
129           if (conv == rc->rc_dst_invalid) {
130                     *dst = rc->rc_dst_invalid;
131                     return _MAPPER_CONVERT_NONIDENTICAL;
132           }
133           if (conv == rc->rc_dst_ilseq)
134                     return _MAPPER_CONVERT_ILSEQ;
135 
136           *dst = conv;
137 
138           return _MAPPER_CONVERT_SUCCESS;
139 }
140 
141 static __inline int
set_linear_zone(struct _citrus_mapper_std_linear_zone * lz,u_int32_t begin,u_int32_t end)142 set_linear_zone(struct _citrus_mapper_std_linear_zone *lz,
143           u_int32_t begin, u_int32_t end)
144 {
145           _DIAGASSERT(lz != NULL);
146 
147           if (begin > end)
148                     return EFTYPE;
149 
150           lz->begin = begin;
151           lz->end = end;
152           lz->width= end - begin + 1;
153 
154           return 0;
155 }
156 
157 static __inline int
rowcol_parse_variable_compat(struct _citrus_mapper_std_rowcol * rc,struct _region * r)158 rowcol_parse_variable_compat(struct _citrus_mapper_std_rowcol *rc,
159           struct _region *r)
160 {
161           const struct _citrus_mapper_std_rowcol_info_compat_x *rcx;
162           struct _citrus_mapper_std_linear_zone *lz;
163           u_int32_t m, n;
164           int ret;
165 
166           _DIAGASSERT(rc != NULL);
167           _DIAGASSERT(r != NULL && _region_size(r) == sizeof(*rcx));
168           rcx = _region_head(r);
169 
170           rc->rc_dst_invalid = be32toh(rcx->rcx_dst_invalid);
171           rc->rc_dst_unit_bits = be32toh(rcx->rcx_dst_unit_bits);
172           m = be32toh(rcx->rcx_src_col_bits);
173           n = 1U << (m - 1);
174           n |= n - 1;
175           rc->rc_src_rowcol_bits = m;
176           rc->rc_src_rowcol_mask = n;
177 
178           rc->rc_src_rowcol = malloc(2 *
179               sizeof(*rc->rc_src_rowcol));
180           if (rc->rc_src_rowcol == NULL)
181                     return ENOMEM;
182           lz = rc->rc_src_rowcol;
183           rc->rc_src_rowcol_len = 1;
184           m = be32toh(rcx->rcx_src_row_begin);
185           n = be32toh(rcx->rcx_src_row_end);
186           if (m + n > 0) {
187                     ret = set_linear_zone(lz, m, n);
188                     if (ret != 0) {
189                               free(rc->rc_src_rowcol);
190                               rc->rc_src_rowcol = NULL;
191                               return ret;
192                     }
193                     ++rc->rc_src_rowcol_len, ++lz;
194           }
195           m = be32toh(rcx->rcx_src_col_begin);
196           n = be32toh(rcx->rcx_src_col_end);
197 
198           return set_linear_zone(lz, m, n);
199 }
200 
201 static __inline int
rowcol_parse_variable(struct _citrus_mapper_std_rowcol * rc,struct _region * r)202 rowcol_parse_variable(struct _citrus_mapper_std_rowcol *rc,
203           struct _region *r)
204 {
205           const struct _citrus_mapper_std_rowcol_info_x *rcx;
206           struct _citrus_mapper_std_linear_zone *lz;
207           u_int32_t m, n;
208           size_t i;
209           int ret;
210 
211           _DIAGASSERT(rc != NULL);
212           _DIAGASSERT(r != NULL && _region_size(r) == sizeof(*rcx));
213           rcx = _region_head(r);
214 
215           rc->rc_dst_invalid = be32toh(rcx->rcx_dst_invalid);
216           rc->rc_dst_unit_bits = be32toh(rcx->rcx_dst_unit_bits);
217 
218           m = be32toh(rcx->rcx_src_rowcol_bits);
219           n = 1U << (m - 1);
220           n |= n - 1;
221           rc->rc_src_rowcol_bits = m;
222           rc->rc_src_rowcol_mask = n;
223 
224           rc->rc_src_rowcol_len = be32toh(rcx->rcx_src_rowcol_len);
225           if (rc->rc_src_rowcol_len > _CITRUS_MAPPER_STD_ROWCOL_MAX)
226                     return EFTYPE;
227           rc->rc_src_rowcol = malloc(rc->rc_src_rowcol_len *
228               sizeof(*rc->rc_src_rowcol));
229           if (rc->rc_src_rowcol == NULL)
230                     return ENOMEM;
231           for (i = 0, lz = rc->rc_src_rowcol;
232                i < rc->rc_src_rowcol_len; ++i, ++lz) {
233                     m = be32toh(rcx->rcx_src_rowcol[i].begin),
234                     n = be32toh(rcx->rcx_src_rowcol[i].end);
235                     ret = set_linear_zone(lz, m, n);
236                     if (ret != 0) {
237                               free(rc->rc_src_rowcol);
238                               rc->rc_src_rowcol = NULL;
239                               return ret;
240                     }
241           }
242           return 0;
243 }
244 
245 static void
rowcol_uninit(struct _citrus_mapper_std * ms)246 rowcol_uninit(struct _citrus_mapper_std *ms)
247 {
248           struct _citrus_mapper_std_rowcol *rc;
249           _DIAGASSERT(ms != NULL);
250 
251           rc = &ms->ms_rowcol;
252           free(rc->rc_src_rowcol);
253 }
254 
255 static int
rowcol_init(struct _citrus_mapper_std * ms)256 rowcol_init(struct _citrus_mapper_std *ms)
257 {
258           int ret;
259           struct _citrus_mapper_std_rowcol *rc;
260           const struct _citrus_mapper_std_rowcol_ext_ilseq_info_x *eix;
261           struct _region r;
262           u_int64_t table_size;
263           size_t i;
264           struct _citrus_mapper_std_linear_zone *lz;
265 
266           _DIAGASSERT(ms != NULL);
267           ms->ms_convert = &rowcol_convert;
268           ms->ms_uninit = &rowcol_uninit;
269           rc = &ms->ms_rowcol;
270 
271           /* get table region */
272           ret = _db_lookup_by_s(ms->ms_db, _CITRUS_MAPPER_STD_SYM_TABLE,
273                                     &rc->rc_table, NULL);
274           if (ret) {
275                     if (ret==ENOENT)
276                               ret = EFTYPE;
277                     return ret;
278           }
279 
280           /* get table information */
281           ret = _db_lookup_by_s(ms->ms_db, _CITRUS_MAPPER_STD_SYM_INFO, &r, NULL);
282           if (ret) {
283                     if (ret==ENOENT)
284                               ret = EFTYPE;
285                     return ret;
286           }
287           switch (_region_size(&r)) {
288           case _CITRUS_MAPPER_STD_ROWCOL_INFO_COMPAT_SIZE:
289                     ret = rowcol_parse_variable_compat(rc, &r);
290                     break;
291           case _CITRUS_MAPPER_STD_ROWCOL_INFO_SIZE:
292                     ret = rowcol_parse_variable(rc, &r);
293                     break;
294           default:
295                     return EFTYPE;
296           }
297           if (ret != 0)
298                     return ret;
299           /* sanity check */
300           switch (rc->rc_src_rowcol_bits) {
301           case 8: case 16: case 32:
302                     if (rc->rc_src_rowcol_len <= 32 / rc->rc_src_rowcol_bits)
303                               break;
304           /*FALLTHROUGH*/
305           default:
306                     return EFTYPE;
307           }
308 
309           /* ilseq extension */
310           rc->rc_oob_mode = _CITRUS_MAPPER_STD_OOB_NONIDENTICAL;
311           rc->rc_dst_ilseq = rc->rc_dst_invalid;
312           ret = _db_lookup_by_s(ms->ms_db,
313                                     _CITRUS_MAPPER_STD_SYM_ROWCOL_EXT_ILSEQ,
314                                     &r, NULL);
315           if (ret && ret != ENOENT)
316                     return ret;
317           if (_region_size(&r) < sizeof(*eix))
318                     return EFTYPE;
319           if (ret == 0) {
320                     eix = _region_head(&r);
321                     rc->rc_oob_mode = be32toh(eix->eix_oob_mode);
322                     rc->rc_dst_ilseq = be32toh(eix->eix_dst_ilseq);
323           }
324 
325           /* calculate expected table size */
326           i = rc->rc_src_rowcol_len;
327           lz = &rc->rc_src_rowcol[--i];
328           table_size = lz->width;
329           while (i > 0) {
330                     lz = &rc->rc_src_rowcol[--i];
331                     table_size *= lz->width;
332           }
333           table_size *= rc->rc_dst_unit_bits/8;
334 
335           if (table_size > UINT32_MAX ||
336               _region_size(&rc->rc_table) < table_size)
337                     return EFTYPE;
338 
339           return 0;
340 }
341 
342 typedef int (*initfunc_t)(struct _citrus_mapper_std *);
343 static const struct {
344           const char                              *t_name;
345           initfunc_t                              t_init;
346 } types[] = {
347           { _CITRUS_MAPPER_STD_TYPE_ROWCOL, &rowcol_init },
348 };
349 #define NUM_OF_TYPES ((int)(sizeof(types)/sizeof(types[0])))
350 
351 static int
352 /*ARGSUSED*/
_citrus_mapper_std_mapper_init(struct _citrus_mapper_area * __restrict ma,struct _citrus_mapper * __restrict cm,const char * __restrict curdir,const void * __restrict var,size_t lenvar,struct _citrus_mapper_traits * __restrict mt,size_t lenmt)353 _citrus_mapper_std_mapper_init(struct _citrus_mapper_area *__restrict ma,
354                                      struct _citrus_mapper * __restrict cm,
355                                      const char * __restrict curdir,
356                                      const void * __restrict var, size_t lenvar,
357                                      struct _citrus_mapper_traits * __restrict mt,
358                                      size_t lenmt)
359 {
360           char path[PATH_MAX];
361           const char *type;
362           int ret, id;
363           struct _citrus_mapper_std *ms;
364 
365           /* set traits */
366           if (lenmt<sizeof(*mt)) {
367                     ret = EINVAL;
368                     goto err0;
369           }
370           mt->mt_src_max = mt->mt_dst_max = 1;    /* 1:1 converter */
371           mt->mt_state_size = 0;                            /* stateless */
372 
373           /* alloc mapper std structure */
374           ms = malloc(sizeof(*ms));
375           if (ms==NULL) {
376                     ret = errno;
377                     goto err0;
378           }
379 
380           /* open mapper file */
381           snprintf(path, sizeof(path),
382                      "%s/%.*s", curdir, (int)lenvar, (const char *)var);
383           ret = _map_file(&ms->ms_file, path);
384           if (ret)
385                     goto err1;
386 
387           ret = _db_open(&ms->ms_db, &ms->ms_file, _CITRUS_MAPPER_STD_MAGIC,
388                            &_db_hash_std, NULL);
389           if (ret)
390                     goto err2;
391 
392           /* get mapper type */
393           ret = _db_lookupstr_by_s(ms->ms_db, _CITRUS_MAPPER_STD_SYM_TYPE,
394                                          &type, NULL);
395           if (ret) {
396                     if (ret==ENOENT)
397                               ret = EFTYPE;
398                     goto err3;
399           }
400           for (id=0; id<NUM_OF_TYPES; id++)
401                     if (_bcs_strcasecmp(type, types[id].t_name) == 0)
402                               break;
403 
404           if (id == NUM_OF_TYPES)
405                     goto err3;
406 
407           /* init the per-type structure */
408           ret = (*types[id].t_init)(ms);
409           if (ret)
410                     goto err3;
411 
412           cm->cm_closure = ms;
413 
414           return 0;
415 
416 err3:
417           _db_close(ms->ms_db);
418 err2:
419           _unmap_file(&ms->ms_file);
420 err1:
421           free(ms);
422 err0:
423           return ret;
424 }
425 
426 static void
427 /*ARGSUSED*/
_citrus_mapper_std_mapper_uninit(struct _citrus_mapper * cm)428 _citrus_mapper_std_mapper_uninit(struct _citrus_mapper *cm)
429 {
430           struct _citrus_mapper_std *ms;
431 
432           _DIAGASSERT(cm!=NULL && cm->cm_closure!=NULL);
433 
434           ms = cm->cm_closure;
435           if (ms->ms_uninit)
436                     (*ms->ms_uninit)(ms);
437           _db_close(ms->ms_db);
438           _unmap_file(&ms->ms_file);
439           free(ms);
440 }
441 
442 static void
443 /*ARGSUSED*/
_citrus_mapper_std_mapper_init_state(struct _citrus_mapper * __restrict cm,void * __restrict ps)444 _citrus_mapper_std_mapper_init_state(struct _citrus_mapper * __restrict cm,
445                                              void * __restrict ps)
446 {
447 }
448 
449 static int
450 /*ARGSUSED*/
_citrus_mapper_std_mapper_convert(struct _citrus_mapper * __restrict cm,_index_t * __restrict dst,_index_t src,void * __restrict ps)451 _citrus_mapper_std_mapper_convert(struct _citrus_mapper * __restrict cm,
452                                           _index_t * __restrict dst, _index_t src,
453                                           void * __restrict ps)
454 {
455           struct _citrus_mapper_std *ms;
456 
457           _DIAGASSERT(cm!=NULL && cm->cm_closure!=NULL);
458 
459           ms = cm->cm_closure;
460 
461           _DIAGASSERT(ms->ms_convert != NULL);
462 
463           return (*ms->ms_convert)(ms, dst, src, ps);
464 }
465