iconv.c 13 KB

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