]>
git.ipfire.org Git - thirdparty/glibc.git/blob - 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.
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.
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.
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
30 #include <sys/param.h>
32 #include "localeinfo.h"
33 #include "locarchive.h"
35 /* Define the hash function. We define the function as static inline. */
36 #define compute_hashval static inline compute_hashval
38 #undef compute_hashval
41 /* Name of the locale archive file. */
42 static const char archfname
[] = LOCALEDIR
"locale-archive";
45 /* Record of contiguous pages already mapped from the locale archive. */
51 struct archmapped
*next
;
53 static struct archmapped
*archmapped
;
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. */
63 /* Record of locales that we have already loaded from the archive. */
64 struct locale_in_archive
66 struct locale_in_archive
*next
;
68 struct locale_data
*data
[__LC_LAST
];
70 static struct locale_in_archive
*archloaded
;
73 /* Local structure and subroutine of _nl_load_archive, see below. */
83 rangecmp (const void *p1
, const void *p2
)
85 return ((struct range
*) p1
)->from
- ((struct range
*) p2
)->from
;
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. */
96 calculate_head_size (const struct locarhead
*h
)
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
));
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. */
114 _nl_load_locale_from_archive (int category
, const char **namep
)
116 const char *name
= *namep
;
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
;
131 struct range ranges
[__LC_LAST
- 1];
134 size_t ps
= __sysconf (_SC_PAGE_SIZE
);
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
))
144 return lia
->data
[category
];
148 /* If the name contains a codeset, then we normalize the name before
150 const char *p
= strchr (name
, '.');
151 if (p
!= NULL
&& p
[1] != '@' && p
[1] != '\0')
153 const char *rest
= __strchrnul (++p
, '@');
154 const char *normalized_codeset
= _nl_normalize_codeset (p
, rest
- p
);
155 if (normalized_codeset
== NULL
) /* malloc failure */
157 if (strncmp (normalized_codeset
, p
, rest
- p
) != 0
158 || normalized_codeset
[rest
- p
] != '\0')
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
),
168 free ((char *) normalized_codeset
);
174 /* Make sure the archive is loaded. */
175 if (archmapped
== NULL
)
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
;
182 /* The archive has never been opened. */
183 fd
= __open64 (archfname
, O_RDONLY
);
185 /* Cannot open the archive, for whatever reason. */
188 if (__fxstat64 (_STAT_VER
, fd
, &archive_stat
) == -1)
190 /* stat failed, very strange. */
196 if (sizeof (void *) > 4)
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
)
203 /* Check whether the file is large enough for the sizes given in the
205 if (calculate_head_size ((const struct locarhead
*) result
)
206 > archive_stat
.st_size
)
208 (void) __munmap (result
, archive_stat
.st_size
);
214 headmap
.ptr
= result
;
215 /* headmap.from already initialized to zero. */
216 headmap
.len
= archive_stat
.st_size
;
220 struct locarhead head
;
224 if (TEMP_FAILURE_RETRY (__read (fd
, &head
, sizeof (head
)))
227 head_size
= calculate_head_size (&head
);
228 if (head_size
> archive_stat
.st_size
)
230 result
= __mmap64 (NULL
, head_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
231 if (result
== MAP_FAILED
)
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
);
241 /* If there is no archive or it cannot be loaded for some reason fail. */
242 if (__builtin_expect (headmap
.ptr
== NULL
, 0))
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
));
250 namehashtab
= (struct namehashent
*) ((char *) head
251 + head
->namehash_offset
);
253 idx
= hval
% head
->namehash_size
;
254 incr
= 1 + hval
% (head
->namehash_size
- 2);
256 /* If the name_offset field is zero this means this is a
257 deleted entry and therefore no entry can be found. */
260 if (namehashtab
[idx
].name_offset
== 0)
264 if (namehashtab
[idx
].hashval
== hval
265 && strcmp (name
, headmap
.ptr
+ namehashtab
[idx
].name_offset
) == 0)
266 /* Found the entry. */
270 if (idx
>= head
->namehash_size
)
271 idx
-= head
->namehash_size
;
274 /* We found an entry. It might be a placeholder for a removed one. */
275 if (namehashtab
[idx
].locrec_offset
== 0)
278 locrec
= (struct locrecent
*) (headmap
.ptr
+ namehashtab
[idx
].locrec_offset
);
280 if (sizeof (void *) > 4 /* || headmap.len == archive_stat.st_size */)
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
)
287 if (locrec
->record
[cnt
].offset
+ locrec
->record
[cnt
].len
289 /* The archive locrectab contains bogus offsets. */
291 results
[cnt
].addr
= headmap
.ptr
+ locrec
->record
[cnt
].offset
;
292 results
[cnt
].len
= locrec
->record
[cnt
].len
;
297 /* Get the offsets of the data files and sort them. */
298 for (cnt
= nranges
= 0; cnt
< __LC_LAST
; ++cnt
)
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
;
309 qsort (ranges
, nranges
, sizeof (ranges
[0]), rangecmp
);
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
)
320 struct archmapped
*newp
;
322 /* Determine whether the appropriate page is already mapped. */
323 while (mapped
!= NULL
324 && mapped
->from
+ mapped
->len
<= ranges
[cnt
].from
)
327 mapped
= mapped
->next
;
330 /* Do we have a match? */
332 && mapped
->from
<= ranges
[cnt
].from
333 && ((char *) ranges
[cnt
].from
+ ranges
[cnt
].len
334 <= (char *) mapped
->from
+ mapped
->len
))
336 /* Yep, already loaded. */
337 results
[ranges
[cnt
].category
].addr
= ((char *) mapped
->ptr
340 results
[ranges
[cnt
].category
].len
= ranges
[cnt
].len
;
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);
353 to
= ((ranges
[upper
].from
+ ranges
[upper
].len
+ ps
- 1)
357 /* Loop while still in contiguous pages. */
358 while (upper
< nranges
&& ranges
[upper
].from
< to
+ ps
);
360 if (to
> archive_stat
.st_size
)
361 /* The archive locrectab contains bogus offsets. */
364 /* Open the file if it hasn't happened yet. */
368 fd
= __open64 (archfname
, O_RDONLY
);
370 /* Cannot open the archive, for whatever reason. */
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
)
383 /* Map the range from the archive. */
384 addr
= __mmap64 (NULL
, to
- from
, PROT_READ
, MAP_SHARED
, fd
, from
);
385 if (addr
== MAP_FAILED
)
388 /* Allocate a record for this mapping. */
389 newp
= (struct archmapped
*) malloc (sizeof (struct archmapped
));
392 (void) __munmap (addr
, to
- from
);
399 newp
->len
= to
- from
;
400 assert (last
->next
== mapped
);
405 /* Determine the load addresses for the category data. */
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
;
413 while (++cnt
< upper
);
414 --cnt
; /* The 'for' will increase 'cnt' again. */
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. */
421 lia
= malloc (sizeof *lia
);
422 if (__builtin_expect (lia
== NULL
, 0))
425 lia
->name
= headmap
.ptr
+ namehashtab
[idx
].name_offset
;
426 lia
->next
= archloaded
;
429 for (cnt
= 0; cnt
< __LC_LAST
; ++cnt
)
432 lia
->data
[cnt
] = _nl_intern_locale_data (cnt
,
435 if (__builtin_expect (lia
->data
[cnt
] != NULL
, 1))
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
;
444 return lia
->data
[category
];
448 _nl_archive_subfreeres (void)
450 struct locale_in_archive
*lia
;
451 struct archmapped
*am
;
453 /* Toss out our cached locales. */
458 struct locale_in_archive
*dead
= lia
;
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
]);
469 if (archmapped
!= NULL
)
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. */
474 assert (archmapped
== &headmap
);
476 (void) __munmap (headmap
.ptr
, headmap
.len
);
480 struct archmapped
*dead
= am
;
482 (void) __munmap (dead
->ptr
, dead
->len
);