1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21: #include <dlfcn.h>
22: #include <errno.h>
23: #include <fcntl.h>
24: #include <stdlib.h>
25: #include <string.h>
26: #include <unistd.h>
27: #include <sys/mman.h>
28: #include <sys/stat.h>
29:
30: #include <gconv_int.h>
31: #include <iconvconfig.h>
32: #include <not-cancel.h>
33:
34: #include "../intl/hash-string.h"
35:
36: static void *gconv_cache;
37: static size_t cache_size;
38: static int cache_malloced;
39:
40:
41: void *
42: __gconv_get_cache (void)
43: {
44: return gconv_cache;
45: }
46:
47:
48: int
49: internal_function
50: __gconv_load_cache (void)
51: {
52: int fd;
53: struct stat64 st;
54: struct gconvcache_header *header;
55:
56:
57:
58: __gconv_path_envvar = getenv ("GCONV_PATH");
59: if (__gconv_path_envvar != NULL)
60: return -1;
61:
62:
63: fd = open_not_cancel (GCONV_MODULES_CACHE, O_RDONLY, 0);
64: if (__builtin_expect (fd, 0) == -1)
65:
66: return -1;
67:
68:
69: if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0
70:
71:
72: || (size_t) st.st_size < sizeof (struct gconvcache_header))
73: {
74: close_and_exit:
75: close_not_cancel_no_status (fd);
76: return -1;
77: }
78:
79:
80: cache_size = st.st_size;
81: #ifdef _POSIX_MAPPED_FILES
82: gconv_cache = __mmap (NULL, cache_size, PROT_READ, MAP_SHARED, fd, 0);
83: if (__builtin_expect (gconv_cache == MAP_FAILED, 0))
84: #endif
85: {
86: size_t already_read;
87:
88: gconv_cache = malloc (cache_size);
89: if (gconv_cache == NULL)
90: goto close_and_exit;
91:
92: already_read = 0;
93: do
94: {
95: ssize_t n = __read (fd, (char *) gconv_cache + already_read,
96: cache_size - already_read);
97: if (__builtin_expect (n, 0) == -1)
98: {
99: free (gconv_cache);
100: gconv_cache = NULL;
101: goto close_and_exit;
102: }
103:
104: already_read += n;
105: }
106: while (already_read < cache_size);
107:
108: cache_malloced = 1;
109: }
110:
111:
112: close_not_cancel_no_status (fd);
113:
114:
115: header = (struct gconvcache_header *) gconv_cache;
116: if (__builtin_expect (header->magic, GCONVCACHE_MAGIC) != GCONVCACHE_MAGIC
117: || __builtin_expect (header->string_offset >= cache_size, 0)
118: || __builtin_expect (header->hash_offset >= cache_size, 0)
119: || __builtin_expect (header->hash_size == 0, 0)
120: || __builtin_expect ((header->hash_offset
121: + header->hash_size * sizeof (struct hash_entry))
122: > cache_size, 0)
123: || __builtin_expect (header->module_offset >= cache_size, 0)
124: || __builtin_expect (header->otherconv_offset > cache_size, 0))
125: {
126: if (cache_malloced)
127: {
128: free (gconv_cache);
129: cache_malloced = 0;
130: }
131: #ifdef _POSIX_MAPPED_FILES
132: else
133: __munmap (gconv_cache, cache_size);
134: #endif
135: gconv_cache = NULL;
136:
137: return -1;
138: }
139:
140:
141: return 0;
142: }
143:
144:
145: static int
146: internal_function
147: find_module_idx (const char *str, size_t *idxp)
148: {
149: unsigned int idx;
150: unsigned int hval;
151: unsigned int hval2;
152: const struct gconvcache_header *header;
153: const char *strtab;
154: const struct hash_entry *hashtab;
155: unsigned int limit;
156:
157: header = (const struct gconvcache_header *) gconv_cache;
158: strtab = (char *) gconv_cache + header->string_offset;
159: hashtab = (struct hash_entry *) ((char *) gconv_cache
160: + header->hash_offset);
161:
162: hval = __hash_string (str);
163: idx = hval % header->hash_size;
164: hval2 = 1 + hval % (header->hash_size - 2);
165:
166: limit = cache_size - header->string_offset;
167: while (hashtab[idx].string_offset != 0)
168: if (hashtab[idx].string_offset < limit
169: && strcmp (str, strtab + hashtab[idx].string_offset) == 0)
170: {
171: *idxp = hashtab[idx].module_idx;
172: return 0;
173: }
174: else
175: if ((idx += hval2) >= header->hash_size)
176: idx -= header->hash_size;
177:
178:
179: return -1;
180: }
181:
182:
183: #ifndef STATIC_GCONV
184: static int
185: internal_function
186: find_module (const char *directory, const char *filename,
187: struct __gconv_step *result)
188: {
189: size_t dirlen = strlen (directory);
190: size_t fnamelen = strlen (filename) + 1;
191: char fullname[dirlen + fnamelen];
192: int status = __GCONV_NOCONV;
193:
194: memcpy (__mempcpy (fullname, directory, dirlen), filename, fnamelen);
195:
196: result->__shlib_handle = __gconv_find_shlib (fullname);
197: if (result->__shlib_handle != NULL)
198: {
199: status = __GCONV_OK;
200:
201: result->__modname = NULL;
202: result->__fct = result->__shlib_handle->fct;
203: result->__init_fct = result->__shlib_handle->init_fct;
204: result->__end_fct = result->__shlib_handle->end_fct;
205:
206:
207: result->__btowc_fct = NULL;
208: result->__data = NULL;
209:
210:
211: if (result->__init_fct != NULL)
212: {
213: __gconv_init_fct init_fct = result->__init_fct;
214: #ifdef PTR_DEMANGLE
215: PTR_DEMANGLE (init_fct);
216: #endif
217: status = DL_CALL_FCT (init_fct, (result));
218:
219: #ifdef PTR_MANGLE
220: if (result->__btowc_fct != NULL)
221: PTR_MANGLE (result->__btowc_fct);
222: #endif
223: }
224: }
225:
226: return status;
227: }
228: #endif
229:
230:
231: int
232: internal_function
233: __gconv_compare_alias_cache (const char *name1, const char *name2, int *result)
234: {
235: size_t name1_idx;
236: size_t name2_idx;
237:
238: if (gconv_cache == NULL)
239: return -1;
240:
241: if (find_module_idx (name1, &name1_idx) != 0
242: || find_module_idx (name2, &name2_idx) != 0)
243: *result = strcmp (name1, name2);
244: else
245: *result = (int) (name1_idx - name2_idx);
246:
247: return 0;
248: }
249:
250:
251: int
252: internal_function
253: __gconv_lookup_cache (const char *toset, const char *fromset,
254: struct __gconv_step **handle, size_t *nsteps, int flags)
255: {
256: const struct gconvcache_header *header;
257: const char *strtab;
258: size_t fromidx;
259: size_t toidx;
260: const struct module_entry *modtab;
261: const struct module_entry *from_module;
262: const struct module_entry *to_module;
263: struct __gconv_step *result;
264:
265: if (gconv_cache == NULL)
266:
267: return __GCONV_NODB;
268:
269: header = (const struct gconvcache_header *) gconv_cache;
270: strtab = (char *) gconv_cache + header->string_offset;
271: modtab = (const struct module_entry *) ((char *) gconv_cache
272: + header->module_offset);
273:
274: if (find_module_idx (fromset, &fromidx) != 0
275: || (header->module_offset + (fromidx + 1) * sizeof (struct module_entry)
276: > cache_size))
277: return __GCONV_NOCONV;
278: from_module = &modtab[fromidx];
279:
280: if (find_module_idx (toset, &toidx) != 0
281: || (header->module_offset + (toidx + 1) * sizeof (struct module_entry)
282: > cache_size))
283: return __GCONV_NOCONV;
284: to_module = &modtab[toidx];
285:
286:
287: if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0) && fromidx == toidx)
288: return __GCONV_NULCONV;
289:
290:
291: if (fromidx != 0 && toidx != 0
292: && __builtin_expect (from_module->extra_offset, 0) != 0)
293: {
294:
295:
296: const struct extra_entry *extra;
297:
298:
299:
300: extra = (const struct extra_entry *) ((char *) gconv_cache
301: + header->otherconv_offset
302: + from_module->extra_offset - 1);
303: while (extra->module_cnt != 0
304: && extra->module[extra->module_cnt - 1].outname_offset != toidx)
305: extra = (const struct extra_entry *) ((char *) extra
306: + sizeof (struct extra_entry)
307: + (extra->module_cnt
308: * sizeof (struct extra_entry_module)));
309:
310: if (extra->module_cnt != 0)
311: {
312:
313: char *fromname;
314: int idx;
315:
316: *nsteps = extra->module_cnt;
317: *handle = result =
318: (struct __gconv_step *) malloc (extra->module_cnt
319: * sizeof (struct __gconv_step));
320: if (result == NULL)
321: return __GCONV_NOMEM;
322:
323: fromname = (char *) strtab + from_module->canonname_offset;
324: idx = 0;
325: do
326: {
327: result[idx].__from_name = fromname;
328: fromname = result[idx].__to_name =
329: (char *) strtab + modtab[extra->module[idx].outname_offset].canonname_offset;
330:
331: result[idx].__counter = 1;
332: result[idx].__data = NULL;
333:
334: #ifndef STATIC_GCONV
335: if (strtab[extra->module[idx].dir_offset] != '\0')
336: {
337:
338: int res;
339:
340: res = find_module (strtab + extra->module[idx].dir_offset,
341: strtab + extra->module[idx].name_offset,
342: &result[idx]);
343: if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
344: {
345:
346: free (result);
347: goto try_internal;
348: }
349: }
350: else
351: #endif
352:
353: __gconv_get_builtin_trans (strtab
354: + extra->module[idx].name_offset,
355: &result[idx]);
356:
357: }
358: while (++idx < extra->module_cnt);
359:
360: return __GCONV_OK;
361: }
362: }
363:
364: try_internal:
365:
366: if ((fromidx != 0 && __builtin_expect (from_module->fromname_offset, 1) == 0)
367: || (toidx != 0 && __builtin_expect (to_module->toname_offset, 1) == 0)
368: || (fromidx == 0 && toidx == 0))
369:
370: return __GCONV_NOCONV;
371:
372:
373: result = (struct __gconv_step *) malloc (2 * sizeof (struct __gconv_step));
374: if (result == NULL)
375: return __GCONV_NOMEM;
376:
377: *handle = result;
378: *nsteps = 0;
379:
380:
381: if (fromidx != 0)
382: {
383: result[0].__from_name = (char *) strtab + from_module->canonname_offset;
384: result[0].__to_name = (char *) "INTERNAL";
385:
386: result[0].__counter = 1;
387: result[0].__data = NULL;
388:
389: #ifndef STATIC_GCONV
390: if (strtab[from_module->todir_offset] != '\0')
391: {
392:
393: int res = find_module (strtab + from_module->todir_offset,
394: strtab + from_module->toname_offset,
395: &result[0]);
396: if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
397: {
398:
399: free (result);
400: return res;
401: }
402: }
403: else
404: #endif
405:
406: __gconv_get_builtin_trans (strtab + from_module->toname_offset,
407: &result[0]);
408:
409: ++*nsteps;
410: }
411:
412:
413: if (toidx != 0)
414: {
415: int idx = *nsteps;
416:
417: result[idx].__from_name = (char *) "INTERNAL";
418: result[idx].__to_name = (char *) strtab + to_module->canonname_offset;
419:
420: result[idx].__counter = 1;
421: result[idx].__data = NULL;
422:
423: #ifndef STATIC_GCONV
424: if (strtab[to_module->fromdir_offset] != '\0')
425: {
426:
427: int res = find_module (strtab + to_module->fromdir_offset,
428: strtab + to_module->fromname_offset,
429: &result[idx]);
430: if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK)
431: {
432:
433: if (idx != 0)
434: __gconv_release_step (&result[0]);
435: free (result);
436: return res;
437: }
438: }
439: else
440: #endif
441:
442: __gconv_get_builtin_trans (strtab + to_module->fromname_offset,
443: &result[idx]);
444:
445: ++*nsteps;
446: }
447:
448: return __GCONV_OK;
449: }
450:
451:
452:
453: void
454: internal_function
455: __gconv_release_cache (struct __gconv_step *steps, size_t nsteps)
456: {
457: if (gconv_cache != NULL)
458:
459:
460: free (steps);
461: }
462:
463:
464:
465: libc_freeres_fn (free_mem)
466: {
467: if (cache_malloced)
468: free (gconv_cache);
469: #ifdef _POSIX_MAPPED_FILES
470: else if (gconv_cache != NULL)
471: __munmap (gconv_cache, cache_size);
472: #endif
473: }