]> git.ipfire.org Git - thirdparty/glibc.git/blob - locale/loadarchive.c
* sunrpc/svc_authux.c (_svcauth_unix): Remove spurious printf (ugh!).
[thirdparty/glibc.git] / locale / loadarchive.c
1 /* Code to load locale data from the locale archive file.
2 Copyright (C) 2002 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
19
20 #include <locale.h>
21 #include <stddef.h>
22 #include <stdbool.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30 #include <sys/param.h>
31
32 #include "localeinfo.h"
33 #include "locarchive.h"
34
35 /* Define the hash function. We define the function as static inline. */
36 #define compute_hashval static inline compute_hashval
37 #include "hashval.h"
38 #undef compute_hashval
39
40
41 /* Name of the locale archive file. */
42 static const char archfname[] = LOCALEDIR "locale-archive";
43
44
45 /* Record of contiguous pages already mapped from the locale archive. */
46 struct archmapped
47 {
48 void *ptr;
49 uint32_t from;
50 uint32_t len;
51 struct archmapped *next;
52 };
53 static struct archmapped *archmapped;
54
55 /* This describes the mapping at the beginning of the file that contains
56 the header data. There could be data in the following partial page,
57 so this is searched like any other. Once the archive has been used,
58 ARCHMAPPED points to this; if mapping the archive header failed,
59 then headmap.ptr is null. */
60 static struct archmapped headmap;
61 static struct stat64 archive_stat; /* stat of archive when header mapped. */
62
63 /* Record of locales that we have already loaded from the archive. */
64 struct locale_in_archive
65 {
66 struct locale_in_archive *next;
67 const char *name;
68 struct locale_data *data[__LC_LAST];
69 };
70 static struct locale_in_archive *archloaded;
71
72
73 /* Local structure and subroutine of _nl_load_archive, see below. */
74 struct range
75 {
76 uint32_t from;
77 uint32_t len;
78 int category;
79 void *result;
80 };
81
82 static int
83 rangecmp (const void *p1, const void *p2)
84 {
85 return ((struct range *) p1)->from - ((struct range *) p2)->from;
86 }
87
88
89 /* Calculate the amount of space needed for all the tables described
90 by the given header. Note we do not include the empty table space
91 that has been preallocated in the file, so our mapping may not be
92 large enough if localedef adds data to the file in place. However,
93 doing that would permute the header fields while we are accessing
94 them and thus not be safe anyway, so we don't allow for that. */
95 static inline off_t
96 calculate_head_size (const struct locarhead *h)
97 {
98 off_t namehash_end = (h->namehash_offset
99 + h->namehash_size * sizeof (struct namehashent));
100 off_t string_end = h->string_offset + h->string_used;
101 off_t locrectab_end = (h->locrectab_offset
102 + h->locrectab_used * sizeof (struct locrecent));
103 return MAX (namehash_end, MAX (string_end, locrectab_end));
104 }
105
106
107 /* Find the locale *NAMEP in the locale archive, and return the
108 internalized data structure for its CATEGORY data. If this locale has
109 already been loaded from the archive, just returns the existing data
110 structure. If successful, sets *NAMEP to point directly into the mapped
111 archive string table; that way, the next call can short-circuit strcmp. */
112 struct locale_data *
113 internal_function
114 _nl_load_locale_from_archive (int category, const char **namep)
115 {
116 const char *name = *namep;
117 struct
118 {
119 void *addr;
120 size_t len;
121 } results[__LC_LAST];
122 struct locale_in_archive *lia;
123 struct locarhead *head;
124 struct namehashent *namehashtab;
125 struct locrecent *locrec;
126 struct archmapped *mapped;
127 struct archmapped *last;
128 unsigned long int hval;
129 size_t idx;
130 size_t incr;
131 struct range ranges[__LC_LAST - 1];
132 int nranges;
133 int cnt;
134 size_t ps = __sysconf (_SC_PAGE_SIZE);
135 int fd = -1;
136
137 /* Check if we have already loaded this locale from the archive.
138 If we previously loaded the locale but found bogons in the data,
139 then we will have stored a null pointer to return here. */
140 for (lia = archloaded; lia != NULL; lia = lia->next)
141 if (name == lia->name || !strcmp (name, lia->name))
142 {
143 *namep = lia->name;
144 return lia->data[category];
145 }
146
147 {
148 /* If the name contains a codeset, then we normalize the name before
149 doing the lookup. */
150 const char *p = strchr (name, '.');
151 if (p != NULL && p[1] != '@' && p[1] != '\0')
152 {
153 const char *rest = __strchrnul (++p, '@');
154 const char *normalized_codeset = _nl_normalize_codeset (p, rest - p);
155 if (normalized_codeset == NULL) /* malloc failure */
156 return NULL;
157 if (strncmp (normalized_codeset, p, rest - p) != 0
158 || normalized_codeset[rest - p] != '\0')
159 {
160 /* There is a normalized codeset name that is different from
161 what was specified; reconstruct a new locale name using it. */
162 size_t normlen = strlen (normalized_codeset);
163 size_t restlen = strlen (rest) + 1;
164 char *newname = alloca (p - name + normlen + restlen);
165 memcpy (__mempcpy (__mempcpy (newname, name, p - name),
166 normalized_codeset, normlen),
167 rest, restlen);
168 free ((char *) normalized_codeset);
169 name = newname;
170 }
171 }
172 }
173
174 /* Make sure the archive is loaded. */
175 if (archmapped == NULL)
176 {
177 /* We do this early as a sign that we have tried to open the archive.
178 If headmap.ptr remains null, that's an indication that we tried
179 and failed, so we won't try again. */
180 archmapped = &headmap;
181
182 /* The archive has never been opened. */
183 fd = __open64 (archfname, O_RDONLY);
184 if (fd < 0)
185 /* Cannot open the archive, for whatever reason. */
186 return NULL;
187
188 if (__fxstat64 (_STAT_VER, fd, &archive_stat) == -1)
189 {
190 /* stat failed, very strange. */
191 close_and_out:
192 __close (fd);
193 return NULL;
194 }
195
196 if (sizeof (void *) > 4)
197 {
198 /* We will just map the whole file, what the hell. */
199 void *result = __mmap64 (NULL, archive_stat.st_size,
200 PROT_READ, MAP_SHARED, fd, 0);
201 if (result == MAP_FAILED)
202 goto close_and_out;
203 /* Check whether the file is large enough for the sizes given in the
204 header. */
205 if (calculate_head_size ((const struct locarhead *) result)
206 > archive_stat.st_size)
207 {
208 (void) __munmap (result, archive_stat.st_size);
209 goto close_and_out;
210 }
211 __close (fd);
212 fd = -1;
213
214 headmap.ptr = result;
215 /* headmap.from already initialized to zero. */
216 headmap.len = archive_stat.st_size;
217 }
218 else
219 {
220 struct locarhead head;
221 off_t head_size;
222 void *result;
223
224 if (TEMP_FAILURE_RETRY (__read (fd, &head, sizeof (head)))
225 != sizeof (head))
226 goto close_and_out;
227 head_size = calculate_head_size (&head);
228 if (head_size > archive_stat.st_size)
229 goto close_and_out;
230 result = __mmap64 (NULL, head_size, PROT_READ, MAP_SHARED, fd, 0);
231 if (result == MAP_FAILED)
232 goto close_and_out;
233
234 /* Record that we have mapped the initial pages of the file. */
235 headmap.ptr = result;
236 headmap.len = MIN ((head_size + ps - 1) & ~(ps - 1),
237 archive_stat.st_size);
238 }
239 }
240
241 /* If there is no archive or it cannot be loaded for some reason fail. */
242 if (__builtin_expect (headmap.ptr == NULL, 0))
243 return NULL;
244
245 /* We have the archive available. To find the name we first have to
246 determine its hash value. */
247 hval = compute_hashval (name, strlen (name));
248
249 head = headmap.ptr;
250 namehashtab = (struct namehashent *) ((char *) head
251 + head->namehash_offset);
252
253 idx = hval % head->namehash_size;
254 incr = 1 + hval % (head->namehash_size - 2);
255
256 /* If the name_offset field is zero this means this is a
257 deleted entry and therefore no entry can be found. */
258 while (1)
259 {
260 if (namehashtab[idx].name_offset == 0)
261 /* Not found. */
262 return NULL;
263
264 if (namehashtab[idx].hashval == hval
265 && strcmp (name, headmap.ptr + namehashtab[idx].name_offset) == 0)
266 /* Found the entry. */
267 break;
268
269 idx += incr;
270 if (idx >= head->namehash_size)
271 idx -= head->namehash_size;
272 }
273
274 /* We found an entry. It might be a placeholder for a removed one. */
275 if (namehashtab[idx].locrec_offset == 0)
276 return NULL;
277
278 locrec = (struct locrecent *) (headmap.ptr + namehashtab[idx].locrec_offset);
279
280 if (sizeof (void *) > 4 /* || headmap.len == archive_stat.st_size */)
281 {
282 /* We already have the whole locale archive mapped in. */
283 assert (headmap.len == archive_stat.st_size);
284 for (cnt = 0; cnt < __LC_LAST; ++cnt)
285 if (cnt != LC_ALL)
286 {
287 if (locrec->record[cnt].offset + locrec->record[cnt].len
288 > headmap.len)
289 /* The archive locrectab contains bogus offsets. */
290 return NULL;
291 results[cnt].addr = headmap.ptr + locrec->record[cnt].offset;
292 results[cnt].len = locrec->record[cnt].len;
293 }
294 }
295 else
296 {
297 /* Get the offsets of the data files and sort them. */
298 for (cnt = nranges = 0; cnt < __LC_LAST; ++cnt)
299 if (cnt != LC_ALL)
300 {
301 ranges[nranges].from = locrec->record[cnt].offset;
302 ranges[nranges].len = locrec->record[cnt].len;
303 ranges[nranges].category = cnt;
304 ranges[nranges].result = NULL;
305
306 ++nranges;
307 }
308
309 qsort (ranges, nranges, sizeof (ranges[0]), rangecmp);
310
311 /* The information about mmap'd blocks is kept in a list.
312 Skip over the blocks which are before the data we need. */
313 last = mapped = archmapped;
314 for (cnt = 0; cnt < nranges; ++cnt)
315 {
316 int upper;
317 size_t from;
318 size_t to;
319 void *addr;
320 struct archmapped *newp;
321
322 /* Determine whether the appropriate page is already mapped. */
323 while (mapped != NULL
324 && mapped->from + mapped->len <= ranges[cnt].from)
325 {
326 last = mapped;
327 mapped = mapped->next;
328 }
329
330 /* Do we have a match? */
331 if (mapped != NULL
332 && mapped->from <= ranges[cnt].from
333 && ((char *) ranges[cnt].from + ranges[cnt].len
334 <= (char *) mapped->from + mapped->len))
335 {
336 /* Yep, already loaded. */
337 results[ranges[cnt].category].addr = ((char *) mapped->ptr
338 + ranges[cnt].from
339 - mapped->from);
340 results[ranges[cnt].category].len = ranges[cnt].len;
341 continue;
342 }
343
344 /* Map the range with the locale data from the file. We will
345 try to cover as much of the locale as possible. I.e., if the
346 next category (next as in "next offset") is on the current or
347 immediately following page we use it as well. */
348 assert (powerof2 (ps));
349 from = ranges[cnt].from & ~(ps - 1);
350 upper = cnt;
351 do
352 {
353 to = ((ranges[upper].from + ranges[upper].len + ps - 1)
354 & ~(ps - 1));
355 ++upper;
356 }
357 /* Loop while still in contiguous pages. */
358 while (upper < nranges && ranges[upper].from < to + ps);
359
360 if (to > archive_stat.st_size)
361 /* The archive locrectab contains bogus offsets. */
362 return NULL;
363
364 /* Open the file if it hasn't happened yet. */
365 if (fd == -1)
366 {
367 struct stat64 st;
368 fd = __open64 (archfname, O_RDONLY);
369 if (fd == -1)
370 /* Cannot open the archive, for whatever reason. */
371 return NULL;
372 /* Now verify we think this is really the same archive file
373 we opened before. If it has been changed we cannot trust
374 the header we read previously. */
375 if (__fxstat64 (_STAT_VER, fd, &st) < 0
376 || st.st_size != archive_stat.st_size
377 || st.st_mtime != archive_stat.st_mtime
378 || st.st_dev != archive_stat.st_dev
379 || st.st_ino != archive_stat.st_ino)
380 return NULL;
381 }
382
383 /* Map the range from the archive. */
384 addr = __mmap64 (NULL, to - from, PROT_READ, MAP_SHARED, fd, from);
385 if (addr == MAP_FAILED)
386 return NULL;
387
388 /* Allocate a record for this mapping. */
389 newp = (struct archmapped *) malloc (sizeof (struct archmapped));
390 if (newp == NULL)
391 {
392 (void) __munmap (addr, to - from);
393 return NULL;
394 }
395
396 /* And queue it. */
397 newp->ptr = addr;
398 newp->from = from;
399 newp->len = to - from;
400 assert (last->next == mapped);
401 newp->next = mapped;
402 last->next = newp;
403 last = newp;
404
405 /* Determine the load addresses for the category data. */
406 do
407 {
408 assert (ranges[cnt].from >= from);
409 results[ranges[cnt].category].addr = ((char *) addr
410 + ranges[cnt].from - from);
411 results[ranges[cnt].category].len = ranges[cnt].len;
412 }
413 while (++cnt < upper);
414 --cnt; /* The 'for' will increase 'cnt' again. */
415 }
416 }
417
418 /* We succeeded in mapping all the necessary regions of the archive.
419 Now we need the expected data structures to point into the data. */
420
421 lia = malloc (sizeof *lia);
422 if (__builtin_expect (lia == NULL, 0))
423 return NULL;
424
425 lia->name = headmap.ptr + namehashtab[idx].name_offset;
426 lia->next = archloaded;
427 archloaded = lia;
428
429 for (cnt = 0; cnt < __LC_LAST; ++cnt)
430 if (cnt != LC_ALL)
431 {
432 lia->data[cnt] = _nl_intern_locale_data (cnt,
433 results[cnt].addr,
434 results[cnt].len);
435 if (__builtin_expect (lia->data[cnt] != NULL, 1))
436 {
437 /* _nl_intern_locale_data leaves us these fields to initialize. */
438 lia->data[cnt]->alloc = ld_archive;
439 lia->data[cnt]->name = lia->name;
440 }
441 }
442
443 *namep = lia->name;
444 return lia->data[category];
445 }
446
447 void
448 _nl_archive_subfreeres (void)
449 {
450 struct locale_in_archive *lia;
451 struct archmapped *am;
452
453 /* Toss out our cached locales. */
454 lia = archloaded;
455 while (lia != NULL)
456 {
457 int category;
458 struct locale_in_archive *dead = lia;
459 lia = lia->next;
460
461 for (category = 0; category < __LC_LAST; ++category)
462 if (category != LC_ALL)
463 /* _nl_unload_locale just does this free for the archive case. */
464 free (dead->data[category]);
465 free (dead);
466 }
467 archloaded = NULL;
468
469 if (archmapped != NULL)
470 {
471 /* Now toss all the mapping windows, which we know nothing is using any
472 more because we just tossed all the locales that point into them. */
473
474 assert (archmapped == &headmap);
475 archmapped = NULL;
476 (void) __munmap (headmap.ptr, headmap.len);
477 am = headmap.next;
478 while (am != NULL)
479 {
480 struct archmapped *dead = am;
481 am = am->next;
482 (void) __munmap (dead->ptr, dead->len);
483 free (dead);
484 }
485 }
486 }