1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <locale.h>
21: #include <stddef.h>
22: #include <stdlib.h>
23: #include <stdbool.h>
24: #include <errno.h>
25: #include <assert.h>
26: #include <string.h>
27: #include <fcntl.h>
28: #include <unistd.h>
29: #include <sys/mman.h>
30: #include <sys/stat.h>
31: #include <sys/param.h>
32:
33: #include "localeinfo.h"
34: #include "locarchive.h"
35: #include <not-cancel.h>
36:
37:
38: #define compute_hashval static inline compute_hashval
39: #define hashval_t uint32_t
40: #include "hashval.h"
41: #undef compute_hashval
42:
43:
44:
45: static const char archfname[] = LOCALEDIR "/locale-archive";
46:
47:
48:
49: #define ARCHIVE_MAPPING_WINDOW (2 * 1024 * 1024)
50:
51: #ifndef MAP_COPY
52:
53:
54:
55:
56:
57:
58: # define MAP_COPY MAP_PRIVATE
59: #endif
60: #ifndef MAP_FILE
61:
62: # define MAP_FILE 0
63: #endif
64:
65:
66: struct archmapped
67: {
68: void *ptr;
69: uint32_t from;
70: uint32_t len;
71: struct archmapped *next;
72: };
73: static struct archmapped *archmapped;
74:
75:
76:
77:
78:
79:
80: static struct archmapped headmap;
81: static struct stat64 archive_stat;
82:
83:
84: struct locale_in_archive
85: {
86: struct locale_in_archive *next;
87: char *name;
88: struct locale_data *data[__LC_LAST];
89: };
90: static struct locale_in_archive *archloaded;
91:
92:
93:
94: struct range
95: {
96: uint32_t from;
97: uint32_t len;
98: int category;
99: void *result;
100: };
101:
102: static int
103: rangecmp (const void *p1, const void *p2)
104: {
105: return ((struct range *) p1)->from - ((struct range *) p2)->from;
106: }
107:
108:
109:
110:
111:
112:
113:
114:
115: static inline off_t
116: calculate_head_size (const struct locarhead *h)
117: {
118: off_t namehash_end = (h->namehash_offset
119: + h->namehash_size * sizeof (struct namehashent));
120: off_t string_end = h->string_offset + h->string_used;
121: off_t locrectab_end = (h->locrectab_offset
122: + h->locrectab_used * sizeof (struct locrecent));
123: return MAX (namehash_end, MAX (string_end, locrectab_end));
124: }
125:
126:
127:
128:
129:
130:
131:
132: struct locale_data *
133: internal_function
134: _nl_load_locale_from_archive (int category, const char **namep)
135: {
136: const char *name = *namep;
137: struct
138: {
139: void *addr;
140: size_t len;
141: } results[__LC_LAST];
142: struct locale_in_archive *lia;
143: struct locarhead *head;
144: struct namehashent *namehashtab;
145: struct locrecent *locrec;
146: struct archmapped *mapped;
147: struct archmapped *last;
148: unsigned long int hval;
149: size_t idx;
150: size_t incr;
151: struct range ranges[__LC_LAST - 1];
152: int nranges;
153: int cnt;
154: size_t ps = __sysconf (_SC_PAGE_SIZE);
155: int fd = -1;
156:
157:
158:
159:
160: for (lia = archloaded; lia != NULL; lia = lia->next)
161: if (name == lia->name || !strcmp (name, lia->name))
162: {
163: *namep = lia->name;
164: return lia->data[category];
165: }
166:
167: {
168:
169:
170: const char *p = strchr (name, '.');
171: if (p != NULL && p[1] != '@' && p[1] != '\0')
172: {
173: const char *rest = __strchrnul (++p, '@');
174: const char *normalized_codeset = _nl_normalize_codeset (p, rest - p);
175: if (normalized_codeset == NULL)
176: return NULL;
177: if (strncmp (normalized_codeset, p, rest - p) != 0
178: || normalized_codeset[rest - p] != '\0')
179: {
180:
181:
182: size_t normlen = strlen (normalized_codeset);
183: size_t restlen = strlen (rest) + 1;
184: char *newname = alloca (p - name + normlen + restlen);
185: memcpy (__mempcpy (__mempcpy (newname, name, p - name),
186: normalized_codeset, normlen),
187: rest, restlen);
188: name = newname;
189: }
190: free ((char *) normalized_codeset);
191: }
192: }
193:
194:
195: if (archmapped == NULL)
196: {
197: void *result;
198: size_t headsize, mapsize;
199:
200:
201:
202:
203: archmapped = &headmap;
204:
205:
206: fd = open_not_cancel_2 (archfname, O_RDONLY|O_LARGEFILE);
207: if (fd < 0)
208:
209: return NULL;
210:
211: if (__fxstat64 (_STAT_VER, fd, &archive_stat) == -1)
212: {
213:
214: close_and_out:
215: if (fd >= 0)
216: close_not_cancel_no_status (fd);
217: return NULL;
218: }
219:
220:
221:
222:
223:
224:
225: mapsize = (sizeof (void *) > 4 ? archive_stat.st_size
226: : MIN (archive_stat.st_size, ARCHIVE_MAPPING_WINDOW));
227:
228: result = __mmap64 (NULL, mapsize, PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
229: if (result == MAP_FAILED)
230: goto close_and_out;
231:
232:
233:
234:
235: headsize = calculate_head_size ((const struct locarhead *) result);
236: if (headsize > mapsize)
237: {
238: (void) __munmap (result, mapsize);
239: if (sizeof (void *) > 4 || headsize > archive_stat.st_size)
240:
241: goto close_and_out;
242:
243:
244:
245: mapsize = (headsize + ps - 1) & ~(ps - 1);
246: result = __mmap64 (NULL, mapsize, PROT_READ, MAP_FILE|MAP_COPY,
247: fd, 0);
248: if (result == MAP_FAILED)
249: goto close_and_out;
250: }
251:
252: if (sizeof (void *) > 4 || mapsize >= archive_stat.st_size)
253: {
254:
255:
256: close_not_cancel_no_status (fd);
257: fd = -1;
258: }
259:
260: headmap.ptr = result;
261:
262: headmap.len = mapsize;
263: }
264:
265:
266: if (__builtin_expect (headmap.ptr == NULL, 0))
267: goto close_and_out;
268:
269:
270:
271: hval = compute_hashval (name, strlen (name));
272:
273: head = headmap.ptr;
274: namehashtab = (struct namehashent *) ((char *) head
275: + head->namehash_offset);
276:
277: idx = hval % head->namehash_size;
278: incr = 1 + hval % (head->namehash_size - 2);
279:
280:
281:
282: while (1)
283: {
284: if (namehashtab[idx].name_offset == 0)
285:
286: goto close_and_out;
287:
288: if (namehashtab[idx].hashval == hval
289: && strcmp (name, headmap.ptr + namehashtab[idx].name_offset) == 0)
290:
291: break;
292:
293: idx += incr;
294: if (idx >= head->namehash_size)
295: idx -= head->namehash_size;
296: }
297:
298:
299: if (namehashtab[idx].locrec_offset == 0)
300: goto close_and_out;
301:
302: locrec = (struct locrecent *) (headmap.ptr + namehashtab[idx].locrec_offset);
303:
304: if (sizeof (void *) > 4 )
305: {
306:
307: assert (headmap.len == archive_stat.st_size);
308: for (cnt = 0; cnt < __LC_LAST; ++cnt)
309: if (cnt != LC_ALL)
310: {
311: if (locrec->record[cnt].offset + locrec->record[cnt].len
312: > headmap.len)
313:
314: goto close_and_out;
315: results[cnt].addr = headmap.ptr + locrec->record[cnt].offset;
316: results[cnt].len = locrec->record[cnt].len;
317: }
318: }
319: else
320: {
321:
322: for (cnt = nranges = 0; cnt < __LC_LAST; ++cnt)
323: if (cnt != LC_ALL)
324: {
325: ranges[nranges].from = locrec->record[cnt].offset;
326: ranges[nranges].len = locrec->record[cnt].len;
327: ranges[nranges].category = cnt;
328: ranges[nranges].result = NULL;
329:
330: ++nranges;
331: }
332:
333: qsort (ranges, nranges, sizeof (ranges[0]), rangecmp);
334:
335:
336:
337: last = mapped = archmapped;
338: for (cnt = 0; cnt < nranges; ++cnt)
339: {
340: int upper;
341: size_t from;
342: size_t to;
343: void *addr;
344: struct archmapped *newp;
345:
346:
347: while (mapped != NULL
348: && (mapped->from + mapped->len
349: <= ranges[cnt].from + ranges[cnt].len))
350: {
351: last = mapped;
352: mapped = mapped->next;
353: }
354:
355:
356: if (mapped != NULL
357: && mapped->from <= ranges[cnt].from
358: && (ranges[cnt].from + ranges[cnt].len
359: <= mapped->from + mapped->len))
360: {
361:
362: results[ranges[cnt].category].addr = ((char *) mapped->ptr
363: + ranges[cnt].from
364: - mapped->from);
365: results[ranges[cnt].category].len = ranges[cnt].len;
366: continue;
367: }
368:
369:
370:
371:
372:
373: assert (powerof2 (ps));
374: from = ranges[cnt].from & ~(ps - 1);
375: upper = cnt;
376: do
377: {
378: to = ranges[upper].from + ranges[upper].len;
379: if (to > (size_t) archive_stat.st_size)
380:
381: goto close_and_out;
382: to = (to + ps - 1) & ~(ps - 1);
383:
384:
385: if (mapped != NULL && ranges[upper].from >= mapped->from)
386: break;
387:
388: ++upper;
389: }
390:
391: while (upper < nranges && ranges[upper].from < to + ps);
392:
393:
394: if (fd == -1)
395: {
396: struct stat64 st;
397: fd = open_not_cancel_2 (archfname, O_RDONLY|O_LARGEFILE);
398: if (fd == -1)
399:
400: return NULL;
401:
402:
403:
404: if (__fxstat64 (_STAT_VER, fd, &st) < 0
405: || st.st_size != archive_stat.st_size
406: || st.st_mtime != archive_stat.st_mtime
407: || st.st_dev != archive_stat.st_dev
408: || st.st_ino != archive_stat.st_ino)
409: goto close_and_out;
410: }
411:
412:
413: addr = __mmap64 (NULL, to - from, PROT_READ, MAP_FILE|MAP_COPY,
414: fd, from);
415: if (addr == MAP_FAILED)
416: goto close_and_out;
417:
418:
419: newp = (struct archmapped *) malloc (sizeof (struct archmapped));
420: if (newp == NULL)
421: {
422: (void) __munmap (addr, to - from);
423: goto close_and_out;
424: }
425:
426:
427: newp->ptr = addr;
428: newp->from = from;
429: newp->len = to - from;
430: assert (last->next == mapped);
431: newp->next = mapped;
432: last->next = newp;
433: last = newp;
434:
435:
436: do
437: {
438: assert (ranges[cnt].from >= from);
439: results[ranges[cnt].category].addr = ((char *) addr
440: + ranges[cnt].from - from);
441: results[ranges[cnt].category].len = ranges[cnt].len;
442: }
443: while (++cnt < upper);
444: --cnt;
445: }
446: }
447:
448:
449: if (fd >= 0)
450: close_not_cancel_no_status (fd);
451: fd = -1;
452:
453:
454:
455:
456: lia = malloc (sizeof *lia);
457: if (__builtin_expect (lia == NULL, 0))
458: return NULL;
459:
460: lia->name = strdup (*namep);
461: if (__builtin_expect (lia->name == NULL, 0))
462: {
463: free (lia);
464: return NULL;
465: }
466:
467: lia->next = archloaded;
468: archloaded = lia;
469:
470: for (cnt = 0; cnt < __LC_LAST; ++cnt)
471: if (cnt != LC_ALL)
472: {
473: lia->data[cnt] = _nl_intern_locale_data (cnt,
474: results[cnt].addr,
475: results[cnt].len);
476: if (__builtin_expect (lia->data[cnt] != NULL, 1))
477: {
478:
479: lia->data[cnt]->alloc = ld_archive;
480: lia->data[cnt]->name = lia->name;
481:
482:
483:
484:
485:
486:
487:
488:
489: lia->data[cnt]->usage_count = UNDELETABLE;
490: }
491: }
492:
493: *namep = lia->name;
494: return lia->data[category];
495: }
496:
497: void __libc_freeres_fn_section
498: _nl_archive_subfreeres (void)
499: {
500: struct locale_in_archive *lia;
501: struct archmapped *am;
502:
503:
504: lia = archloaded;
505: while (lia != NULL)
506: {
507: int category;
508: struct locale_in_archive *dead = lia;
509: lia = lia->next;
510:
511: free (dead->name);
512: for (category = 0; category < __LC_LAST; ++category)
513: if (category != LC_ALL)
514: {
515:
516: if (dead->data[category]->private.cleanup)
517: (*dead->data[category]->private.cleanup) (dead->data[category]);
518:
519: free (dead->data[category]);
520: }
521: free (dead);
522: }
523: archloaded = NULL;
524:
525: if (archmapped != NULL)
526: {
527:
528:
529:
530: assert (archmapped == &headmap);
531: archmapped = NULL;
532: (void) __munmap (headmap.ptr, headmap.len);
533: am = headmap.next;
534: while (am != NULL)
535: {
536: struct archmapped *dead = am;
537: am = am->next;
538: (void) __munmap (dead->ptr, dead->len);
539: free (dead);
540: }
541: }
542: }