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