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