FreeBSD kernel libkern code
iconv.c
Go to the documentation of this file.
1 /*-
2  * Copyright (c) 2000-2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$BSDSUniX$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/iconv.h>
34 #include <sys/malloc.h>
35 #include <sys/mount.h>
36 #include <sys/sx.h>
37 #include <sys/syslog.h>
38 
39 #include "iconv_converter_if.h"
40 
41 SYSCTL_DECL(_kern_iconv);
42 
43 SYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW, NULL, "kernel iconv interface");
44 
45 MALLOC_DEFINE(M_ICONV, "iconv", "ICONV structures");
46 static MALLOC_DEFINE(M_ICONVDATA, "iconv_data", "ICONV data");
47 
48 MODULE_VERSION(libiconv, 2);
49 
50 static struct sx iconv_lock;
51 
52 #ifdef notnow
53 /*
54  * iconv converter instance
55  */
56 struct iconv_converter {
57  KOBJ_FIELDS;
58  void * c_data;
59 };
60 #endif
61 
62 struct sysctl_oid *iconv_oid_hook = &sysctl___kern_iconv;
63 
64 /*
65  * List of loaded converters
66  */
67 static TAILQ_HEAD(iconv_converter_list, iconv_converter_class)
68  iconv_converters = TAILQ_HEAD_INITIALIZER(iconv_converters);
69 
70 /*
71  * List of supported/loaded charsets pairs
72  */
73 static TAILQ_HEAD(, iconv_cspair)
74  iconv_cslist = TAILQ_HEAD_INITIALIZER(iconv_cslist);
75 static int iconv_csid = 1;
76 
77 static char iconv_unicode_string[] = "unicode"; /* save eight bytes when possible */
78 
79 static void iconv_unregister_cspair(struct iconv_cspair *csp);
80 
81 static int
82 iconv_mod_unload(void)
83 {
84  struct iconv_cspair *csp;
85 
86  sx_xlock(&iconv_lock);
87  TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
88  if (csp->cp_refcount) {
89  sx_xunlock(&iconv_lock);
90  return EBUSY;
91  }
92  }
93 
94  while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL)
96  sx_xunlock(&iconv_lock);
97  sx_destroy(&iconv_lock);
98  return 0;
99 }
100 
101 static int
102 iconv_mod_handler(module_t mod, int type, void *data)
103 {
104  int error;
105 
106  switch (type) {
107  case MOD_LOAD:
108  error = 0;
109  sx_init(&iconv_lock, "iconv");
110  break;
111  case MOD_UNLOAD:
112  error = iconv_mod_unload();
113  break;
114  default:
115  error = EINVAL;
116  }
117  return error;
118 }
119 
120 static moduledata_t iconv_mod = {
121  "iconv", iconv_mod_handler, NULL
122 };
123 
124 DECLARE_MODULE(iconv, iconv_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
125 
126 static int
127 iconv_register_converter(struct iconv_converter_class *dcp)
128 {
129  kobj_class_compile((struct kobj_class*)dcp);
130  dcp->refs++;
131  TAILQ_INSERT_TAIL(&iconv_converters, dcp, cc_link);
132  return 0;
133 }
134 
135 static int
136 iconv_unregister_converter(struct iconv_converter_class *dcp)
137 {
138  if (dcp->refs > 1) {
139  ICDEBUG("converter have %d referenses left\n", dcp->refs);
140  return EBUSY;
141  }
142  TAILQ_REMOVE(&iconv_converters, dcp, cc_link);
143  kobj_class_free((struct kobj_class*)dcp);
144  return 0;
145 }
146 
147 static int
148 iconv_lookupconv(const char *name, struct iconv_converter_class **dcpp)
149 {
150  struct iconv_converter_class *dcp;
151 
152  TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
153  if (name == NULL)
154  continue;
155  if (strcmp(name, ICONV_CONVERTER_NAME(dcp)) == 0) {
156  if (dcpp)
157  *dcpp = dcp;
158  return 0;
159  }
160  }
161  return ENOENT;
162 }
163 
164 static int
165 iconv_lookupcs(const char *to, const char *from, struct iconv_cspair **cspp)
166 {
167  struct iconv_cspair *csp;
168 
169  TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
170  if (strcasecmp(csp->cp_to, to) == 0 &&
171  strcasecmp(csp->cp_from, from) == 0) {
172  if (cspp)
173  *cspp = csp;
174  return 0;
175  }
176  }
177  return ENOENT;
178 }
179 
180 static int
181 iconv_register_cspair(const char *to, const char *from,
182  struct iconv_converter_class *dcp, void *data,
183  struct iconv_cspair **cspp)
184 {
185  struct iconv_cspair *csp;
186  char *cp;
187  int csize, ucsto, ucsfrom;
188 
189  if (iconv_lookupcs(to, from, NULL) == 0)
190  return EEXIST;
191  csize = sizeof(*csp);
192  ucsto = strcmp(to, iconv_unicode_string) == 0;
193  if (!ucsto)
194  csize += strlen(to) + 1;
195  ucsfrom = strcmp(from, iconv_unicode_string) == 0;
196  if (!ucsfrom)
197  csize += strlen(from) + 1;
198  csp = malloc(csize, M_ICONV, M_WAITOK);
199  bzero(csp, csize);
200  csp->cp_id = iconv_csid++;
201  csp->cp_dcp = dcp;
202  cp = (char*)(csp + 1);
203  if (!ucsto) {
204  strcpy(cp, to);
205  csp->cp_to = cp;
206  cp += strlen(cp) + 1;
207  } else
208  csp->cp_to = iconv_unicode_string;
209  if (!ucsfrom) {
210  strcpy(cp, from);
211  csp->cp_from = cp;
212  } else
213  csp->cp_from = iconv_unicode_string;
214  csp->cp_data = data;
215 
216  TAILQ_INSERT_TAIL(&iconv_cslist, csp, cp_link);
217  *cspp = csp;
218  return 0;
219 }
220 
221 static void
222 iconv_unregister_cspair(struct iconv_cspair *csp)
223 {
224  TAILQ_REMOVE(&iconv_cslist, csp, cp_link);
225  if (csp->cp_data)
226  free(csp->cp_data, M_ICONVDATA);
227  free(csp, M_ICONV);
228 }
229 
230 /*
231  * Lookup and create an instance of converter.
232  * Currently this layer didn't have associated 'instance' structure
233  * to avoid unnesessary memory allocation.
234  */
235 int
236 iconv_open(const char *to, const char *from, void **handle)
237 {
238  struct iconv_cspair *csp, *cspfrom, *cspto;
239  struct iconv_converter_class *dcp;
240  const char *cnvname;
241  int error;
242 
243  /*
244  * First, lookup fully qualified cspairs
245  */
246  error = iconv_lookupcs(to, from, &csp);
247  if (error == 0)
248  return ICONV_CONVERTER_OPEN(csp->cp_dcp, csp, NULL, handle);
249 
250  /*
251  * Well, nothing found. Now try to construct a composite conversion
252  * ToDo: add a 'capability' field to converter
253  */
254  TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
255  cnvname = ICONV_CONVERTER_NAME(dcp);
256  if (cnvname == NULL)
257  continue;
258  error = iconv_lookupcs(cnvname, from, &cspfrom);
259  if (error)
260  continue;
261  error = iconv_lookupcs(to, cnvname, &cspto);
262  if (error)
263  continue;
264  /*
265  * Fine, we're found a pair which can be combined together
266  */
267  return ICONV_CONVERTER_OPEN(dcp, cspto, cspfrom, handle);
268  }
269  return ENOENT;
270 }
271 
272 int
273 iconv_close(void *handle)
274 {
275  return ICONV_CONVERTER_CLOSE(handle);
276 }
277 
278 int
279 iconv_conv(void *handle, const char **inbuf,
280  size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
281 {
282  return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, 0);
283 }
284 
285 int
286 iconv_conv_case(void *handle, const char **inbuf,
287  size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
288 {
289  return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, casetype);
290 }
291 
292 int
293 iconv_convchr(void *handle, const char **inbuf,
294  size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
295 {
296  return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, 0);
297 }
298 
299 int
300 iconv_convchr_case(void *handle, const char **inbuf,
301  size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
302 {
303  return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, casetype);
304 }
305 
306 int
307 towlower(int c, void *handle)
308 {
309  return ICONV_CONVERTER_TOLOWER(handle, c);
310 }
311 
312 int
313 towupper(int c, void *handle)
314 {
315  return ICONV_CONVERTER_TOUPPER(handle, c);
316 }
317 
318 /*
319  * Give a list of loaded converters. Each name terminated with 0.
320  * An empty string terminates the list.
321  */
322 static int
323 iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS)
324 {
325  struct iconv_converter_class *dcp;
326  const char *name;
327  char spc;
328  int error;
329 
330  error = 0;
331  sx_slock(&iconv_lock);
332  TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
333  name = ICONV_CONVERTER_NAME(dcp);
334  if (name == NULL)
335  continue;
336  error = SYSCTL_OUT(req, name, strlen(name) + 1);
337  if (error)
338  break;
339  }
340  sx_sunlock(&iconv_lock);
341  if (error)
342  return error;
343  spc = 0;
344  error = SYSCTL_OUT(req, &spc, sizeof(spc));
345  return error;
346 }
347 
348 SYSCTL_PROC(_kern_iconv, OID_AUTO, drvlist, CTLFLAG_RD | CTLTYPE_OPAQUE,
349  NULL, 0, iconv_sysctl_drvlist, "S,xlat", "registered converters");
350 
351 /*
352  * List all available charset pairs.
353  */
354 static int
355 iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS)
356 {
357  struct iconv_cspair *csp;
358  struct iconv_cspair_info csi;
359  int error;
360 
361  error = 0;
362  bzero(&csi, sizeof(csi));
363  csi.cs_version = ICONV_CSPAIR_INFO_VER;
364  sx_slock(&iconv_lock);
365  TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
366  csi.cs_id = csp->cp_id;
367  csi.cs_refcount = csp->cp_refcount;
368  csi.cs_base = csp->cp_base ? csp->cp_base->cp_id : 0;
369  strcpy(csi.cs_to, csp->cp_to);
370  strcpy(csi.cs_from, csp->cp_from);
371  error = SYSCTL_OUT(req, &csi, sizeof(csi));
372  if (error)
373  break;
374  }
375  sx_sunlock(&iconv_lock);
376  return error;
377 }
378 
379 SYSCTL_PROC(_kern_iconv, OID_AUTO, cslist, CTLFLAG_RD | CTLTYPE_OPAQUE,
380  NULL, 0, iconv_sysctl_cslist, "S,xlat", "registered charset pairs");
381 
382 int
383 iconv_add(const char *converter, const char *to, const char *from)
384 {
385  struct iconv_converter_class *dcp;
386  struct iconv_cspair *csp;
387 
388  if (iconv_lookupconv(converter, &dcp) != 0)
389  return EINVAL;
390 
391  return iconv_register_cspair(to, from, dcp, NULL, &csp);
392 }
393 
394 /*
395  * Add new charset pair
396  */
397 static int
398 iconv_sysctl_add(SYSCTL_HANDLER_ARGS)
399 {
400  struct iconv_converter_class *dcp;
401  struct iconv_cspair *csp;
402  struct iconv_add_in din;
403  struct iconv_add_out dout;
404  int error;
405 
406  error = SYSCTL_IN(req, &din, sizeof(din));
407  if (error)
408  return error;
409  if (din.ia_version != ICONV_ADD_VER)
410  return EINVAL;
411  if (din.ia_datalen > ICONV_CSMAXDATALEN)
412  return EINVAL;
413  if (strlen(din.ia_from) >= ICONV_CSNMAXLEN)
414  return EINVAL;
415  if (strlen(din.ia_to) >= ICONV_CSNMAXLEN)
416  return EINVAL;
417  if (strlen(din.ia_converter) >= ICONV_CNVNMAXLEN)
418  return EINVAL;
419  if (iconv_lookupconv(din.ia_converter, &dcp) != 0)
420  return EINVAL;
421  sx_xlock(&iconv_lock);
422  error = iconv_register_cspair(din.ia_to, din.ia_from, dcp, NULL, &csp);
423  if (error) {
424  sx_xunlock(&iconv_lock);
425  return error;
426  }
427  if (din.ia_datalen) {
428  csp->cp_data = malloc(din.ia_datalen, M_ICONVDATA, M_WAITOK);
429  error = copyin(din.ia_data, csp->cp_data, din.ia_datalen);
430  if (error)
431  goto bad;
432  }
433  dout.ia_csid = csp->cp_id;
434  error = SYSCTL_OUT(req, &dout, sizeof(dout));
435  if (error)
436  goto bad;
437  sx_xunlock(&iconv_lock);
438  ICDEBUG("%s => %s, %d bytes\n",din.ia_from, din.ia_to, din.ia_datalen);
439  return 0;
440 bad:
442  sx_xunlock(&iconv_lock);
443  return error;
444 }
445 
446 SYSCTL_PROC(_kern_iconv, OID_AUTO, add, CTLFLAG_RW | CTLTYPE_OPAQUE,
447  NULL, 0, iconv_sysctl_add, "S,xlat", "register charset pair");
448 
449 /*
450  * Default stubs for converters
451  */
452 int
453 iconv_converter_initstub(struct iconv_converter_class *dp)
454 {
455  return 0;
456 }
457 
458 int
459 iconv_converter_donestub(struct iconv_converter_class *dp)
460 {
461  return 0;
462 }
463 
464 int
465 iconv_converter_tolowerstub(int c, void *handle)
466 {
467  return (c);
468 }
469 
470 int
471 iconv_converter_handler(module_t mod, int type, void *data)
472 {
473  struct iconv_converter_class *dcp = data;
474  int error;
475 
476  switch (type) {
477  case MOD_LOAD:
478  sx_xlock(&iconv_lock);
479  error = iconv_register_converter(dcp);
480  if (error) {
481  sx_xunlock(&iconv_lock);
482  break;
483  }
484  error = ICONV_CONVERTER_INIT(dcp);
485  if (error)
487  sx_xunlock(&iconv_lock);
488  break;
489  case MOD_UNLOAD:
490  sx_xlock(&iconv_lock);
491  ICONV_CONVERTER_DONE(dcp);
492  error = iconv_unregister_converter(dcp);
493  sx_xunlock(&iconv_lock);
494  break;
495  default:
496  error = EINVAL;
497  }
498  return error;
499 }
500 
501 /*
502  * Common used functions (don't use with unicode)
503  */
504 char *
505 iconv_convstr(void *handle, char *dst, const char *src)
506 {
507  char *p = dst;
508  size_t inlen, outlen;
509  int error;
510 
511  if (handle == NULL) {
512  strcpy(dst, src);
513  return dst;
514  }
515  inlen = outlen = strlen(src);
516  error = iconv_conv(handle, NULL, NULL, &p, &outlen);
517  if (error)
518  return NULL;
519  error = iconv_conv(handle, &src, &inlen, &p, &outlen);
520  if (error)
521  return NULL;
522  *p = 0;
523  return dst;
524 }
525 
526 void *
527 iconv_convmem(void *handle, void *dst, const void *src, int size)
528 {
529  const char *s = src;
530  char *d = dst;
531  size_t inlen, outlen;
532  int error;
533 
534  if (size == 0)
535  return dst;
536  if (handle == NULL) {
537  memcpy(dst, src, size);
538  return dst;
539  }
540  inlen = outlen = size;
541  error = iconv_conv(handle, NULL, NULL, &d, &outlen);
542  if (error)
543  return NULL;
544  error = iconv_conv(handle, &s, &inlen, &d, &outlen);
545  if (error)
546  return NULL;
547  return dst;
548 }
549 
550 int
551 iconv_lookupcp(char **cpp, const char *s)
552 {
553  if (cpp == NULL) {
554  ICDEBUG("warning a NULL list passed\n", ""); /* XXX ISO variadic macros cannot
555  leave out the
556  variadic args */
557  return ENOENT;
558  }
559  for (; *cpp; cpp++)
560  if (strcmp(*cpp, s) == 0)
561  return 0;
562  return ENOENT;
563 }
564 
565 /*
566  * Return if fsname is in use of not
567  */
568 int
569 iconv_vfs_refcount(const char *fsname)
570 {
571  struct vfsconf *vfsp;
572 
573  vfsp = vfs_byname(fsname);
574  if (vfsp != NULL && vfsp->vfc_refcount > 0)
575  return (EBUSY);
576  return (0);
577 }
const char ** inbuf
static int iconv_register_converter(struct iconv_converter_class *dcp)
Definition: iconv.c:127
static int iconv_lookupcs(const char *to, const char *from, struct iconv_cspair **cspp)
Definition: iconv.c:165
MODULE_VERSION(libiconv, 2)
static int iconv_register_cspair(const char *to, const char *from, struct iconv_converter_class *dcp, void *data, struct iconv_cspair **cspp)
Definition: iconv.c:181
size_t strlen(const char *str)
Definition: strlen.c:82
int iconv_lookupcp(char **cpp, const char *s)
Definition: iconv.c:551
static int iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS)
Definition: iconv.c:355
MALLOC_DEFINE(M_ICONV,"iconv","ICONV structures")
struct iconv_cspair * cspfrom
static void iconv_unregister_cspair(struct iconv_cspair *csp)
Definition: iconv.c:222
int iconv_convchr(void *handle, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
Definition: iconv.c:293
int c
SYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW, NULL,"kernel iconv interface")
SYSCTL_DECL(_kern_iconv)
SYSCTL_PROC(_kern_iconv, OID_AUTO, drvlist, CTLFLAG_RD|CTLTYPE_OPAQUE, NULL, 0, iconv_sysctl_drvlist,"S,xlat","registered converters")
int iconv_converter_handler(module_t mod, int type, void *data)
Definition: iconv.c:471
int iconv_close(void *handle)
Definition: iconv.c:273
int iconv_open(const char *to, const char *from, void **handle)
Definition: iconv.c:236
STATICMETHOD const char * name
static moduledata_t iconv_mod
Definition: iconv.c:120
__FBSDID("$BSDSUniX$")
int iconv_add(const char *converter, const char *to, const char *from)
Definition: iconv.c:383
int towlower(int c, void *handle)
Definition: iconv.c:307
int iconv_converter_initstub(struct iconv_converter_class *dp)
Definition: iconv.c:453
int iconv_vfs_refcount(const char *fsname)
Definition: iconv.c:569
static int iconv_unregister_converter(struct iconv_converter_class *dcp)
Definition: iconv.c:136
int iconv_converter_donestub(struct iconv_converter_class *dp)
Definition: iconv.c:459
static int iconv_sysctl_add(SYSCTL_HANDLER_ARGS)
Definition: iconv.c:398
int casetype
struct iconv_cspair * cspto
char * iconv_convstr(void *handle, char *dst, const char *src)
Definition: iconv.c:505
int iconv_conv(void *handle, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
Definition: iconv.c:279
static TAILQ_HEAD(iconv_converter_list, iconv_converter_class)
Definition: iconv.c:67
DECLARE_MODULE(iconv, iconv_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND)
int towupper(int c, void *handle)
Definition: iconv.c:313
struct sysctl_oid * iconv_oid_hook
Definition: iconv.c:62
char ** outbuf
int iconv_conv_case(void *handle, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
Definition: iconv.c:286
static int iconv_lookupconv(const char *name, struct iconv_converter_class **dcpp)
Definition: iconv.c:148
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:42
static int iconv_mod_handler(module_t mod, int type, void *data)
Definition: iconv.c:102
size_t * outbytesleft
int iconv_convchr_case(void *handle, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
Definition: iconv.c:300
static struct sx iconv_lock
Definition: iconv.c:50
char * strcpy(char *__restrict to, const char *__restrict from)
Definition: strcpy.c:36
static int iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS)
Definition: iconv.c:323
INTERFACE iconv_converter
int iconv_converter_tolowerstub(int c, void *handle)
Definition: iconv.c:465
void * iconv_convmem(void *handle, void *dst, const void *src, int size)
Definition: iconv.c:527
int strcmp(const char *s1, const char *s2)
Definition: strcmp.c:42
size_t * inbytesleft