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