gconv_cache.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. /* Cache handling for iconv modules.
  2. Copyright (C) 2001-2026 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <https://www.gnu.org/licenses/>. */
  15. #include <dlfcn.h>
  16. #include <errno.h>
  17. #include <fcntl.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <unistd.h>
  21. #include <sys/mman.h>
  22. #include <sys/stat.h>
  23. #include <gconv_int.h>
  24. #include <iconvconfig.h>
  25. #include <not-cancel.h>
  26. #include <pointer_guard.h>
  27. #include "../intl/hash-string.h"
  28. static void *gconv_cache;
  29. static size_t cache_size;
  30. static int cache_malloced;
  31. void *
  32. __gconv_get_cache (void)
  33. {
  34. return gconv_cache;
  35. }
  36. int
  37. __gconv_load_cache (void)
  38. {
  39. int fd;
  40. struct __stat64_t64 st;
  41. struct gconvcache_header *header;
  42. /* We cannot use the cache if the GCONV_PATH environment variable is
  43. set. */
  44. __gconv_path_envvar = getenv ("GCONV_PATH");
  45. if (__gconv_path_envvar != NULL)
  46. return -1;
  47. /* See whether the cache file exists. */
  48. fd = __open_nocancel (GCONV_MODULES_CACHE, O_RDONLY | O_CLOEXEC, 0);
  49. if (__builtin_expect (fd, 0) == -1)
  50. /* Not available. */
  51. return -1;
  52. /* Get information about the file. */
  53. if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0)
  54. /* We do not have to start looking at the file if it cannot contain
  55. at least the cache header. */
  56. || (size_t) st.st_size < sizeof (struct gconvcache_header))
  57. {
  58. close_and_exit:
  59. __close_nocancel_nostatus (fd);
  60. return -1;
  61. }
  62. /* Make the file content available. */
  63. cache_size = st.st_size;
  64. #ifdef _POSIX_MAPPED_FILES
  65. gconv_cache = __mmap (NULL, cache_size, PROT_READ, MAP_SHARED, fd, 0);
  66. if (__glibc_unlikely (gconv_cache == MAP_FAILED))
  67. #endif
  68. {
  69. size_t already_read;
  70. gconv_cache = malloc (cache_size);
  71. if (gconv_cache == NULL)
  72. goto close_and_exit;
  73. already_read = 0;
  74. do
  75. {
  76. ssize_t n = __read (fd, (char *) gconv_cache + already_read,
  77. cache_size - already_read);
  78. if (__builtin_expect (n, 0) == -1)
  79. {
  80. free (gconv_cache);
  81. gconv_cache = NULL;
  82. goto close_and_exit;
  83. }
  84. already_read += n;
  85. }
  86. while (already_read < cache_size);
  87. cache_malloced = 1;
  88. }
  89. /* We don't need the file descriptor anymore. */
  90. __close_nocancel_nostatus (fd);
  91. /* Check the consistency. */
  92. header = (struct gconvcache_header *) gconv_cache;
  93. if (__builtin_expect (header->magic, GCONVCACHE_MAGIC) != GCONVCACHE_MAGIC
  94. || __builtin_expect (header->string_offset >= cache_size, 0)
  95. || __builtin_expect (header->hash_offset >= cache_size, 0)
  96. || __builtin_expect (header->hash_size == 0, 0)
  97. || __builtin_expect ((header->hash_offset
  98. + header->hash_size * sizeof (struct hash_entry))
  99. > cache_size, 0)
  100. || __builtin_expect (header->module_offset >= cache_size, 0)
  101. || __builtin_expect (header->otherconv_offset > cache_size, 0))
  102. {
  103. if (cache_malloced)
  104. {
  105. free (gconv_cache);
  106. cache_malloced = 0;
  107. }
  108. #ifdef _POSIX_MAPPED_FILES
  109. else
  110. __munmap (gconv_cache, cache_size);
  111. #endif
  112. gconv_cache = NULL;
  113. return -1;
  114. }
  115. /* That worked. */
  116. return 0;
  117. }
  118. static int
  119. find_module_idx (const char *str, size_t *idxp)
  120. {
  121. unsigned int idx;
  122. unsigned int hval;
  123. unsigned int hval2;
  124. const struct gconvcache_header *header;
  125. const char *strtab;
  126. const struct hash_entry *hashtab;
  127. unsigned int limit;
  128. header = (const struct gconvcache_header *) gconv_cache;
  129. strtab = (char *) gconv_cache + header->string_offset;
  130. hashtab = (struct hash_entry *) ((char *) gconv_cache
  131. + header->hash_offset);
  132. hval = __hash_string (str);
  133. idx = hval % header->hash_size;
  134. hval2 = 1 + hval % (header->hash_size - 2);
  135. limit = cache_size - header->string_offset;
  136. while (hashtab[idx].string_offset != 0)
  137. if (hashtab[idx].string_offset < limit
  138. && strcmp (str, strtab + hashtab[idx].string_offset) == 0)
  139. {
  140. *idxp = hashtab[idx].module_idx;
  141. return 0;
  142. }
  143. else
  144. if ((idx += hval2) >= header->hash_size)
  145. idx -= header->hash_size;
  146. /* Nothing found. */
  147. return -1;
  148. }
  149. #ifndef STATIC_GCONV
  150. static int
  151. find_module (const char *directory, const char *filename,
  152. struct __gconv_step *result)
  153. {
  154. size_t dirlen = strlen (directory);
  155. size_t fnamelen = strlen (filename) + 1;
  156. char fullname[dirlen + fnamelen];
  157. int status = __GCONV_NOCONV;
  158. memcpy (__mempcpy (fullname, directory, dirlen), filename, fnamelen);
  159. result->__shlib_handle = __gconv_find_shlib (fullname);
  160. if (result->__shlib_handle != NULL)
  161. {
  162. status = __GCONV_OK;
  163. result->__modname = NULL;
  164. result->__fct = result->__shlib_handle->fct;
  165. result->__init_fct = result->__shlib_handle->init_fct;
  166. result->__end_fct = result->__shlib_handle->end_fct;
  167. /* These settings can be overridden by the init function. */
  168. result->__btowc_fct = NULL;
  169. result->__data = NULL;
  170. /* Call the init function. */
  171. __gconv_init_fct init_fct = result->__init_fct;
  172. PTR_DEMANGLE (init_fct);
  173. if (init_fct != NULL)
  174. {
  175. status = DL_CALL_FCT (init_fct, (result));
  176. PTR_MANGLE (result->__btowc_fct);
  177. }
  178. }
  179. return status;
  180. }
  181. #endif
  182. int
  183. __gconv_compare_alias_cache (const char *name1, const char *name2, int *result)
  184. {
  185. size_t name1_idx;
  186. size_t name2_idx;
  187. if (gconv_cache == NULL)
  188. return -1;
  189. if (find_module_idx (name1, &name1_idx) != 0
  190. || find_module_idx (name2, &name2_idx) != 0)
  191. *result = strcmp (name1, name2);
  192. else
  193. *result = (int) (name1_idx - name2_idx);
  194. return 0;
  195. }
  196. int
  197. __gconv_lookup_cache (const char *toset, const char *fromset,
  198. struct __gconv_step **handle, size_t *nsteps, int flags)
  199. {
  200. const struct gconvcache_header *header;
  201. const char *strtab;
  202. size_t fromidx;
  203. size_t toidx;
  204. const struct module_entry *modtab;
  205. const struct module_entry *from_module;
  206. const struct module_entry *to_module;
  207. struct __gconv_step *result;
  208. if (gconv_cache == NULL)
  209. /* We have no cache available. */
  210. return __GCONV_NODB;
  211. header = (const struct gconvcache_header *) gconv_cache;
  212. strtab = (char *) gconv_cache + header->string_offset;
  213. modtab = (const struct module_entry *) ((char *) gconv_cache
  214. + header->module_offset);
  215. if (find_module_idx (fromset, &fromidx) != 0
  216. || (header->module_offset + (fromidx + 1) * sizeof (struct module_entry)
  217. > cache_size))
  218. return __GCONV_NOCONV;
  219. from_module = &modtab[fromidx];
  220. if (find_module_idx (toset, &toidx) != 0
  221. || (header->module_offset + (toidx + 1) * sizeof (struct module_entry)
  222. > cache_size))
  223. return __GCONV_NOCONV;
  224. to_module = &modtab[toidx];
  225. /* Avoid copy-only transformations if the user requests. */
  226. if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0) && fromidx == toidx)
  227. return __GCONV_NULCONV;
  228. /* If there are special conversions available examine them first. */
  229. if (fromidx != 0 && toidx != 0
  230. && __builtin_expect (from_module->extra_offset, 0) != 0)
  231. {
  232. /* Search through the list to see whether there is a module
  233. matching the destination character set. */
  234. const struct extra_entry *extra;
  235. /* Note the -1. This is due to the offset added in iconvconfig.
  236. See there for more explanations. */
  237. extra = (const struct extra_entry *) ((char *) gconv_cache
  238. + header->otherconv_offset
  239. + from_module->extra_offset - 1);
  240. while (extra->module_cnt != 0
  241. && extra->module[extra->module_cnt - 1].outname_offset != toidx)
  242. extra = (const struct extra_entry *) ((char *) extra
  243. + sizeof (struct extra_entry)
  244. + (extra->module_cnt
  245. * sizeof (struct extra_entry_module)));
  246. if (extra->module_cnt != 0)
  247. {
  248. /* Use the extra module. First determine how many steps. */
  249. char *fromname;
  250. int idx;
  251. *nsteps = extra->module_cnt;
  252. *handle = result =
  253. (struct __gconv_step *) malloc (extra->module_cnt
  254. * sizeof (struct __gconv_step));
  255. if (result == NULL)
  256. return __GCONV_NOMEM;
  257. fromname = (char *) strtab + from_module->canonname_offset;
  258. idx = 0;
  259. do
  260. {
  261. result[idx].__from_name = fromname;
  262. fromname = result[idx].__to_name =
  263. (char *) strtab + modtab[extra->module[idx].outname_offset].canonname_offset;
  264. result[idx].__counter = 1;
  265. result[idx].__data = NULL;
  266. #ifndef STATIC_GCONV
  267. if (strtab[extra->module[idx].dir_offset] != '\0')
  268. {
  269. /* Load the module, return handle for it. */
  270. int res;
  271. res = find_module (strtab + extra->module[idx].dir_offset,
  272. strtab + extra->module[idx].name_offset,
  273. &result[idx]);
  274. if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
  275. {
  276. /* Something went wrong. */
  277. free (result);
  278. goto try_internal;
  279. }
  280. }
  281. else
  282. #endif
  283. /* It's a builtin transformation. */
  284. __gconv_get_builtin_trans (strtab
  285. + extra->module[idx].name_offset,
  286. &result[idx]);
  287. }
  288. while (++idx < extra->module_cnt);
  289. return __GCONV_OK;
  290. }
  291. }
  292. try_internal:
  293. /* See whether we can convert via the INTERNAL charset. */
  294. if ((fromidx != 0 && __builtin_expect (from_module->fromname_offset, 1) == 0)
  295. || (toidx != 0 && __builtin_expect (to_module->toname_offset, 1) == 0)
  296. || (fromidx == 0 && toidx == 0))
  297. /* Not possible. Nothing we can do. */
  298. return __GCONV_NOCONV;
  299. /* We will use up to two modules. Always allocate room for two. */
  300. result = (struct __gconv_step *) malloc (2 * sizeof (struct __gconv_step));
  301. if (result == NULL)
  302. return __GCONV_NOMEM;
  303. *handle = result;
  304. *nsteps = 0;
  305. /* Generate data structure for conversion to INTERNAL. */
  306. if (fromidx != 0)
  307. {
  308. result[0].__from_name = (char *) strtab + from_module->canonname_offset;
  309. result[0].__to_name = (char *) "INTERNAL";
  310. result[0].__counter = 1;
  311. result[0].__data = NULL;
  312. #ifndef STATIC_GCONV
  313. if (strtab[from_module->todir_offset] != '\0')
  314. {
  315. /* Load the module, return handle for it. */
  316. int res = find_module (strtab + from_module->todir_offset,
  317. strtab + from_module->toname_offset,
  318. &result[0]);
  319. if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
  320. {
  321. /* Something went wrong. */
  322. free (result);
  323. return res;
  324. }
  325. }
  326. else
  327. #endif
  328. /* It's a builtin transformation. */
  329. __gconv_get_builtin_trans (strtab + from_module->toname_offset,
  330. &result[0]);
  331. ++*nsteps;
  332. }
  333. /* Generate data structure for conversion from INTERNAL. */
  334. if (toidx != 0)
  335. {
  336. int idx = *nsteps;
  337. result[idx].__from_name = (char *) "INTERNAL";
  338. result[idx].__to_name = (char *) strtab + to_module->canonname_offset;
  339. result[idx].__counter = 1;
  340. result[idx].__data = NULL;
  341. #ifndef STATIC_GCONV
  342. if (strtab[to_module->fromdir_offset] != '\0')
  343. {
  344. /* Load the module, return handle for it. */
  345. int res = find_module (strtab + to_module->fromdir_offset,
  346. strtab + to_module->fromname_offset,
  347. &result[idx]);
  348. if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
  349. {
  350. /* Something went wrong. */
  351. if (idx != 0)
  352. __gconv_release_step (&result[0]);
  353. free (result);
  354. return res;
  355. }
  356. }
  357. else
  358. #endif
  359. /* It's a builtin transformation. */
  360. __gconv_get_builtin_trans (strtab + to_module->fromname_offset,
  361. &result[idx]);
  362. ++*nsteps;
  363. }
  364. return __GCONV_OK;
  365. }
  366. /* Free memory allocated for the transformation record. */
  367. void
  368. __gconv_release_cache (struct __gconv_step *steps, size_t nsteps)
  369. {
  370. if (gconv_cache != NULL)
  371. /* The only thing we have to deallocate is the record with the
  372. steps. */
  373. free (steps);
  374. }
  375. /* Free all resources if necessary. */
  376. void
  377. __gconv_cache_freemem (void)
  378. {
  379. if (cache_malloced)
  380. free (gconv_cache);
  381. #ifdef _POSIX_MAPPED_FILES
  382. else if (gconv_cache != NULL)
  383. __munmap (gconv_cache, cache_size);
  384. #endif
  385. }