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/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/iconv.h> 33 #include <sys/malloc.h> 34 #include <sys/mount.h> 35 #include <sys/sx.h> 36 #include <sys/syslog.h> 37 38 #include "iconv_converter_if.h" 39 40 SYSCTL_DECL(_kern_iconv); 41 42 SYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 43 "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) 95 iconv_unregister_cspair(csp); 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 dcp->refs--; 139 if (dcp->refs > 1) { 140 ICDEBUG("converter has %d references left\n", dcp->refs); 141 return EBUSY; 142 } 143 TAILQ_REMOVE(&iconv_converters, dcp, cc_link); 144 kobj_class_free((struct kobj_class*)dcp); 145 return 0; 146 } 147 148 static int 149 iconv_lookupconv(const char *name, struct iconv_converter_class **dcpp) 150 { 151 struct iconv_converter_class *dcp; 152 153 TAILQ_FOREACH(dcp, &iconv_converters, cc_link) { 154 if (name == NULL) 155 continue; 156 if (strcmp(name, ICONV_CONVERTER_NAME(dcp)) == 0) { 157 if (dcpp) 158 *dcpp = dcp; 159 return 0; 160 } 161 } 162 return ENOENT; 163 } 164 165 static int 166 iconv_lookupcs(const char *to, const char *from, struct iconv_cspair **cspp) 167 { 168 struct iconv_cspair *csp; 169 170 TAILQ_FOREACH(csp, &iconv_cslist, cp_link) { 171 if (strcasecmp(csp->cp_to, to) == 0 && 172 strcasecmp(csp->cp_from, from) == 0) { 173 if (cspp) 174 *cspp = csp; 175 return 0; 176 } 177 } 178 return ENOENT; 179 } 180 181 static int 182 iconv_register_cspair(const char *to, const char *from, 183 struct iconv_converter_class *dcp, void *data, 184 struct iconv_cspair **cspp) 185 { 186 struct iconv_cspair *csp; 187 char *cp; 188 int csize, ucsto, ucsfrom; 189 190 if (iconv_lookupcs(to, from, NULL) == 0) 191 return EEXIST; 192 csize = sizeof(*csp); 193 ucsto = strcmp(to, iconv_unicode_string) == 0; 194 if (!ucsto) 195 csize += strlen(to) + 1; 196 ucsfrom = strcmp(from, iconv_unicode_string) == 0; 197 if (!ucsfrom) 198 csize += strlen(from) + 1; 199 csp = malloc(csize, M_ICONV, M_WAITOK); 200 bzero(csp, csize); 201 csp->cp_id = iconv_csid++; 202 csp->cp_dcp = dcp; 203 cp = (char*)(csp + 1); 204 if (!ucsto) { 205 strcpy(cp, to); 206 csp->cp_to = cp; 207 cp += strlen(cp) + 1; 208 } else 209 csp->cp_to = iconv_unicode_string; 210 if (!ucsfrom) { 211 strcpy(cp, from); 212 csp->cp_from = cp; 213 } else 214 csp->cp_from = iconv_unicode_string; 215 csp->cp_data = data; 216 217 TAILQ_INSERT_TAIL(&iconv_cslist, csp, cp_link); 218 *cspp = csp; 219 return 0; 220 } 221 222 static void 223 iconv_unregister_cspair(struct iconv_cspair *csp) 224 { 225 TAILQ_REMOVE(&iconv_cslist, csp, cp_link); 226 if (csp->cp_data) 227 free(csp->cp_data, M_ICONVDATA); 228 free(csp, M_ICONV); 229 } 230 231 /* 232 * Lookup and create an instance of converter. 233 * Currently this layer didn't have associated 'instance' structure 234 * to avoid unnesessary memory allocation. 235 */ 236 int 237 iconv_open(const char *to, const char *from, void **handle) 238 { 239 struct iconv_cspair *csp, *cspfrom, *cspto; 240 struct iconv_converter_class *dcp; 241 const char *cnvname; 242 int error; 243 244 /* 245 * First, lookup fully qualified cspairs 246 */ 247 error = iconv_lookupcs(to, from, &csp); 248 if (error == 0) 249 return ICONV_CONVERTER_OPEN(csp->cp_dcp, csp, NULL, handle); 250 251 /* 252 * Well, nothing found. Now try to construct a composite conversion 253 * ToDo: add a 'capability' field to converter 254 */ 255 TAILQ_FOREACH(dcp, &iconv_converters, cc_link) { 256 cnvname = ICONV_CONVERTER_NAME(dcp); 257 if (cnvname == NULL) 258 continue; 259 error = iconv_lookupcs(cnvname, from, &cspfrom); 260 if (error) 261 continue; 262 error = iconv_lookupcs(to, cnvname, &cspto); 263 if (error) 264 continue; 265 /* 266 * Fine, we're found a pair which can be combined together 267 */ 268 return ICONV_CONVERTER_OPEN(dcp, cspto, cspfrom, handle); 269 } 270 return ENOENT; 271 } 272 273 int 274 iconv_close(void *handle) 275 { 276 return ICONV_CONVERTER_CLOSE(handle); 277 } 278 279 int 280 iconv_conv(void *handle, const char **inbuf, 281 size_t *inbytesleft, char **outbuf, size_t *outbytesleft) 282 { 283 return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, 0); 284 } 285 286 int 287 iconv_conv_case(void *handle, const char **inbuf, 288 size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype) 289 { 290 return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, casetype); 291 } 292 293 int 294 iconv_convchr(void *handle, const char **inbuf, 295 size_t *inbytesleft, char **outbuf, size_t *outbytesleft) 296 { 297 return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, 0); 298 } 299 300 int 301 iconv_convchr_case(void *handle, const char **inbuf, 302 size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype) 303 { 304 return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, casetype); 305 } 306 307 int 308 towlower(int c, void *handle) 309 { 310 return ICONV_CONVERTER_TOLOWER(handle, c); 311 } 312 313 int 314 towupper(int c, void *handle) 315 { 316 return ICONV_CONVERTER_TOUPPER(handle, c); 317 } 318 319 /* 320 * Give a list of loaded converters. Each name terminated with 0. 321 * An empty string terminates the list. 322 */ 323 static int 324 iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS) 325 { 326 struct iconv_converter_class *dcp; 327 const char *name; 328 char spc; 329 int error; 330 331 error = 0; 332 sx_slock(&iconv_lock); 333 TAILQ_FOREACH(dcp, &iconv_converters, cc_link) { 334 name = ICONV_CONVERTER_NAME(dcp); 335 if (name == NULL) 336 continue; 337 error = SYSCTL_OUT(req, name, strlen(name) + 1); 338 if (error) 339 break; 340 } 341 sx_sunlock(&iconv_lock); 342 if (error) 343 return error; 344 spc = 0; 345 error = SYSCTL_OUT(req, &spc, sizeof(spc)); 346 return error; 347 } 348 349 SYSCTL_PROC(_kern_iconv, OID_AUTO, drvlist, 350 CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0, 351 iconv_sysctl_drvlist, "S,xlat", 352 "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, 383 CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0, 384 iconv_sysctl_cslist, "S,xlat", 385 "registered charset pairs"); 386 387 int 388 iconv_add(const char *converter, const char *to, const char *from) 389 { 390 struct iconv_converter_class *dcp; 391 struct iconv_cspair *csp; 392 393 if (iconv_lookupconv(converter, &dcp) != 0) 394 return EINVAL; 395 396 return iconv_register_cspair(to, from, dcp, NULL, &csp); 397 } 398 399 /* 400 * Add new charset pair 401 */ 402 static int 403 iconv_sysctl_add(SYSCTL_HANDLER_ARGS) 404 { 405 struct iconv_converter_class *dcp; 406 struct iconv_cspair *csp; 407 struct iconv_add_in din; 408 struct iconv_add_out dout; 409 int error; 410 411 error = SYSCTL_IN(req, &din, sizeof(din)); 412 if (error) 413 return error; 414 if (din.ia_version != ICONV_ADD_VER) 415 return EINVAL; 416 if (din.ia_datalen > ICONV_CSMAXDATALEN) 417 return EINVAL; 418 if (strnlen(din.ia_from, sizeof(din.ia_from)) >= ICONV_CSNMAXLEN) 419 return EINVAL; 420 if (strnlen(din.ia_to, sizeof(din.ia_to)) >= ICONV_CSNMAXLEN) 421 return EINVAL; 422 if (strnlen(din.ia_converter, sizeof(din.ia_converter)) >= ICONV_CNVNMAXLEN) 423 return EINVAL; 424 if (iconv_lookupconv(din.ia_converter, &dcp) != 0) 425 return EINVAL; 426 sx_xlock(&iconv_lock); 427 error = iconv_register_cspair(din.ia_to, din.ia_from, dcp, NULL, &csp); 428 if (error) { 429 sx_xunlock(&iconv_lock); 430 return error; 431 } 432 if (din.ia_datalen) { 433 csp->cp_data = malloc(din.ia_datalen, M_ICONVDATA, M_WAITOK); 434 error = copyin(din.ia_data, csp->cp_data, din.ia_datalen); 435 if (error) 436 goto bad; 437 } 438 dout.ia_csid = csp->cp_id; 439 error = SYSCTL_OUT(req, &dout, sizeof(dout)); 440 if (error) 441 goto bad; 442 sx_xunlock(&iconv_lock); 443 ICDEBUG("%s => %s, %d bytes\n",din.ia_from, din.ia_to, din.ia_datalen); 444 return 0; 445 bad: 446 iconv_unregister_cspair(csp); 447 sx_xunlock(&iconv_lock); 448 return error; 449 } 450 451 SYSCTL_PROC(_kern_iconv, OID_AUTO, add, 452 CTLFLAG_RW | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0, 453 iconv_sysctl_add, "S,xlat", 454 "register charset pair"); 455 456 /* 457 * Default stubs for converters 458 */ 459 int 460 iconv_converter_initstub(struct iconv_converter_class *dp) 461 { 462 return 0; 463 } 464 465 int 466 iconv_converter_donestub(struct iconv_converter_class *dp) 467 { 468 return 0; 469 } 470 471 int 472 iconv_converter_tolowerstub(int c, void *handle) 473 { 474 return (c); 475 } 476 477 int 478 iconv_converter_handler(module_t mod, int type, void *data) 479 { 480 struct iconv_converter_class *dcp = data; 481 int error; 482 483 switch (type) { 484 case MOD_LOAD: 485 sx_xlock(&iconv_lock); 486 error = iconv_register_converter(dcp); 487 if (error) { 488 sx_xunlock(&iconv_lock); 489 break; 490 } 491 error = ICONV_CONVERTER_INIT(dcp); 492 if (error) 493 iconv_unregister_converter(dcp); 494 sx_xunlock(&iconv_lock); 495 break; 496 case MOD_UNLOAD: 497 sx_xlock(&iconv_lock); 498 ICONV_CONVERTER_DONE(dcp); 499 error = iconv_unregister_converter(dcp); 500 sx_xunlock(&iconv_lock); 501 break; 502 default: 503 error = EINVAL; 504 } 505 return error; 506 } 507 508 /* 509 * Common used functions (don't use with unicode) 510 */ 511 char * 512 iconv_convstr(void *handle, char *dst, const char *src) 513 { 514 char *p = dst; 515 size_t inlen, outlen; 516 int error; 517 518 if (handle == NULL) { 519 strcpy(dst, src); 520 return dst; 521 } 522 inlen = outlen = strlen(src); 523 error = iconv_conv(handle, NULL, NULL, &p, &outlen); 524 if (error) 525 return NULL; 526 error = iconv_conv(handle, &src, &inlen, &p, &outlen); 527 if (error) 528 return NULL; 529 *p = 0; 530 return dst; 531 } 532 533 void * 534 iconv_convmem(void *handle, void *dst, const void *src, int size) 535 { 536 const char *s = src; 537 char *d = dst; 538 size_t inlen, outlen; 539 int error; 540 541 if (size == 0) 542 return dst; 543 if (handle == NULL) { 544 memcpy(dst, src, size); 545 return dst; 546 } 547 inlen = outlen = size; 548 error = iconv_conv(handle, NULL, NULL, &d, &outlen); 549 if (error) 550 return NULL; 551 error = iconv_conv(handle, &s, &inlen, &d, &outlen); 552 if (error) 553 return NULL; 554 return dst; 555 } 556 557 int 558 iconv_lookupcp(char **cpp, const char *s) 559 { 560 if (cpp == NULL) { 561 ICDEBUG("warning a NULL list passed\n", ""); 562 return ENOENT; 563 } 564 for (; *cpp; cpp++) 565 if (strcmp(*cpp, s) == 0) 566 return 0; 567 return ENOENT; 568 } 569 570 /* 571 * Return if fsname is in use of not 572 */ 573 int 574 iconv_vfs_refcount(const char *fsname) 575 { 576 struct vfsconf *vfsp; 577 578 vfsp = vfs_byname(fsname); 579 if (vfsp != NULL && vfsp->vfc_refcount > 0) 580 return (EBUSY); 581 return (0); 582 } 583