]> git.ipfire.org Git - thirdparty/glibc.git/blob - locale/programs/locarchive.c
[BZ #10283] localedef: align fixed maps to SHMLBA
[thirdparty/glibc.git] / locale / programs / locarchive.c
1 /* Copyright (C) 2002-2013 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
9
10 This program 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
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, see <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <assert.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <error.h>
26 #include <fcntl.h>
27 #include <inttypes.h>
28 #include <libintl.h>
29 #include <locale.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdio_ext.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <stdint.h>
38 #include <sys/mman.h>
39 #include <sys/param.h>
40 #include <sys/shm.h>
41 #include <sys/stat.h>
42
43 #include <libc-mmap.h>
44 #include "../../crypt/md5.h"
45 #include "../localeinfo.h"
46 #include "../locarchive.h"
47 #include "localedef.h"
48
49 /* Define the hash function. We define the function as static inline.
50 We must change the name so as not to conflict with simple-hash.h. */
51 #define compute_hashval static archive_hashval
52 #define hashval_t uint32_t
53 #include "hashval.h"
54 #undef compute_hashval
55
56 extern const char *output_prefix;
57
58 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
59
60 static const char *locnames[] =
61 {
62 #define DEFINE_CATEGORY(category, category_name, items, a) \
63 [category] = category_name,
64 #include "categories.def"
65 #undef DEFINE_CATEGORY
66 };
67
68
69 /* Size of the initial archive header. */
70 #define INITIAL_NUM_NAMES 900
71 #define INITIAL_SIZE_STRINGS 7500
72 #define INITIAL_NUM_LOCREC 420
73 #define INITIAL_NUM_SUMS 2000
74
75
76 /* Size of the reserved address space area. */
77 #define RESERVE_MMAP_SIZE 512 * 1024 * 1024
78
79 /* To prepare for enlargements of the mmaped area reserve some address
80 space. On some machines, being a file mapping rather than an anonymous
81 mapping affects the address selection. So do this mapping from the
82 actual file, even though it's only a dummy to reserve address space. */
83 static void *
84 prepare_address_space (int fd, size_t total, size_t *reserved, int *xflags,
85 void **mmap_base, size_t *mmap_len)
86 {
87 if (total < RESERVE_MMAP_SIZE)
88 {
89 void *p = mmap64 (NULL, RESERVE_MMAP_SIZE, PROT_NONE, MAP_SHARED, fd, 0);
90 if (p != MAP_FAILED)
91 {
92 void *aligned_p = PTR_ALIGN_UP (p, MAP_FIXED_ALIGNMENT);
93 size_t align_adjust = aligned_p - p;
94 *mmap_base = p;
95 *mmap_len = RESERVE_MMAP_SIZE;
96 assert (align_adjust < RESERVE_MMAP_SIZE);
97 *reserved = RESERVE_MMAP_SIZE - align_adjust;
98 *xflags = MAP_FIXED;
99 return aligned_p;
100 }
101 }
102
103 *reserved = total;
104 *xflags = 0;
105 *mmap_base = NULL;
106 *mmap_len = 0;
107 return NULL;
108 }
109
110
111 static void
112 create_archive (const char *archivefname, struct locarhandle *ah)
113 {
114 int fd;
115 char fname[strlen (archivefname) + sizeof (".XXXXXX")];
116 struct locarhead head;
117 size_t total;
118
119 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
120
121 /* Create a temporary file in the correct directory. */
122 fd = mkstemp (fname);
123 if (fd == -1)
124 error (EXIT_FAILURE, errno, _("cannot create temporary file: %s"), fname);
125
126 /* Create the initial content of the archive. */
127 head.magic = AR_MAGIC;
128 head.serial = 0;
129 head.namehash_offset = sizeof (struct locarhead);
130 head.namehash_used = 0;
131 head.namehash_size = next_prime (INITIAL_NUM_NAMES);
132
133 head.string_offset = (head.namehash_offset
134 + head.namehash_size * sizeof (struct namehashent));
135 head.string_used = 0;
136 head.string_size = INITIAL_SIZE_STRINGS;
137
138 head.locrectab_offset = head.string_offset + head.string_size;
139 head.locrectab_used = 0;
140 head.locrectab_size = INITIAL_NUM_LOCREC;
141
142 head.sumhash_offset = (head.locrectab_offset
143 + head.locrectab_size * sizeof (struct locrecent));
144 head.sumhash_used = 0;
145 head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
146
147 total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
148
149 /* Write out the header and create room for the other data structures. */
150 if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
151 {
152 int errval = errno;
153 unlink (fname);
154 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
155 }
156
157 if (ftruncate64 (fd, total) != 0)
158 {
159 int errval = errno;
160 unlink (fname);
161 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
162 }
163
164 size_t reserved, mmap_len;
165 int xflags;
166 void *mmap_base;
167 void *p = prepare_address_space (fd, total, &reserved, &xflags, &mmap_base,
168 &mmap_len);
169
170 /* Map the header and all the administration data structures. */
171 p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0);
172 if (p == MAP_FAILED)
173 {
174 int errval = errno;
175 unlink (fname);
176 error (EXIT_FAILURE, errval, _("cannot map archive header"));
177 }
178
179 /* Now try to rename it. We don't use the rename function since
180 this would overwrite a file which has been created in
181 parallel. */
182 if (link (fname, archivefname) == -1)
183 {
184 int errval = errno;
185
186 /* We cannot use the just created file. */
187 close (fd);
188 unlink (fname);
189
190 if (errval == EEXIST)
191 {
192 /* There is already an archive. Must have been a localedef run
193 which happened in parallel. Simply open this file then. */
194 open_archive (ah, false);
195 return;
196 }
197
198 error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
199 }
200
201 /* Remove the temporary name. */
202 unlink (fname);
203
204 /* Make the file globally readable. */
205 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
206 {
207 int errval = errno;
208 unlink (archivefname);
209 error (EXIT_FAILURE, errval,
210 _("cannot change mode of new locale archive"));
211 }
212
213 ah->fd = fd;
214 ah->mmap_base = mmap_base;
215 ah->mmap_len = mmap_len;
216 ah->addr = p;
217 ah->mmaped = total;
218 ah->reserved = reserved;
219 }
220
221
222 /* This structure and qsort comparator function are used below to sort an
223 old archive's locrec table in order of data position in the file. */
224 struct oldlocrecent
225 {
226 unsigned int cnt;
227 struct locrecent *locrec;
228 };
229
230 static int
231 oldlocrecentcmp (const void *a, const void *b)
232 {
233 struct locrecent *la = ((const struct oldlocrecent *) a)->locrec;
234 struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec;
235 uint32_t start_a = -1, end_a = 0;
236 uint32_t start_b = -1, end_b = 0;
237 int cnt;
238
239 for (cnt = 0; cnt < __LC_LAST; ++cnt)
240 if (cnt != LC_ALL)
241 {
242 if (la->record[cnt].offset < start_a)
243 start_a = la->record[cnt].offset;
244 if (la->record[cnt].offset + la->record[cnt].len > end_a)
245 end_a = la->record[cnt].offset + la->record[cnt].len;
246 }
247 assert (start_a != (uint32_t)-1);
248 assert (end_a != 0);
249
250 for (cnt = 0; cnt < __LC_LAST; ++cnt)
251 if (cnt != LC_ALL)
252 {
253 if (lb->record[cnt].offset < start_b)
254 start_b = lb->record[cnt].offset;
255 if (lb->record[cnt].offset + lb->record[cnt].len > end_b)
256 end_b = lb->record[cnt].offset + lb->record[cnt].len;
257 }
258 assert (start_b != (uint32_t)-1);
259 assert (end_b != 0);
260
261 if (start_a != start_b)
262 return (int)start_a - (int)start_b;
263 return (int)end_a - (int)end_b;
264 }
265
266
267 /* forward decls for below */
268 static uint32_t add_locale (struct locarhandle *ah, const char *name,
269 locale_data_t data, bool replace);
270 static void add_alias (struct locarhandle *ah, const char *alias,
271 bool replace, const char *oldname,
272 uint32_t *locrec_offset_p);
273
274
275 static bool
276 file_data_available_p (struct locarhandle *ah, uint32_t offset, uint32_t size)
277 {
278 if (offset < ah->mmaped && offset + size <= ah->mmaped)
279 return true;
280
281 struct stat64 st;
282 if (fstat64 (ah->fd, &st) != 0)
283 return false;
284
285 if (st.st_size > ah->reserved)
286 return false;
287
288 size_t start = ALIGN_DOWN (ah->mmaped, MAP_FIXED_ALIGNMENT);
289 void *p = mmap64 (ah->addr + start, st.st_size - start,
290 PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
291 ah->fd, start);
292 if (p == MAP_FAILED)
293 {
294 ah->mmaped = start;
295 return false;
296 }
297
298 ah->mmaped = st.st_size;
299 return true;
300 }
301
302
303 static int
304 compare_from_file (struct locarhandle *ah, void *p1, uint32_t offset2,
305 uint32_t size)
306 {
307 void *p2 = xmalloc (size);
308 if (pread (ah->fd, p2, size, offset2) != size)
309 WITH_CUR_LOCALE (error (4, errno,
310 _("cannot read data from locale archive")));
311
312 int res = memcmp (p1, p2, size);
313 free (p2);
314 return res;
315 }
316
317
318 static void
319 enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
320 {
321 struct stat64 st;
322 int fd;
323 struct locarhead newhead;
324 size_t total;
325 unsigned int cnt, loccnt;
326 struct namehashent *oldnamehashtab;
327 struct locarhandle new_ah;
328 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
329 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
330 char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
331
332 if (output_prefix)
333 memcpy (archivefname, output_prefix, prefix_len);
334 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
335 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
336
337 /* Not all of the old file has to be mapped. Change this now this
338 we will have to access the whole content. */
339 if (fstat64 (ah->fd, &st) != 0)
340 enomap:
341 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
342
343 if (st.st_size < ah->reserved)
344 ah->addr = mmap64 (ah->addr, st.st_size, PROT_READ | PROT_WRITE,
345 MAP_SHARED | MAP_FIXED, ah->fd, 0);
346 else
347 {
348 if (ah->mmap_base)
349 munmap (ah->mmap_base, ah->mmap_len);
350 else
351 munmap (ah->addr, ah->reserved);
352 ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
353 MAP_SHARED, ah->fd, 0);
354 ah->reserved = st.st_size;
355 ah->mmap_base = NULL;
356 ah->mmap_len = 0;
357 head = ah->addr;
358 }
359 if (ah->addr == MAP_FAILED)
360 goto enomap;
361 ah->mmaped = st.st_size;
362
363 /* Create a temporary file in the correct directory. */
364 fd = mkstemp (fname);
365 if (fd == -1)
366 error (EXIT_FAILURE, errno, _("cannot create temporary file: %s"), fname);
367
368 /* Copy the existing head information. */
369 newhead = *head;
370
371 /* Create the new archive header. The sizes of the various tables
372 should be double from what is currently used. */
373 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
374 newhead.namehash_size);
375 if (verbose)
376 printf ("name: size: %u, used: %d, new: size: %u\n",
377 head->namehash_size, head->namehash_used, newhead.namehash_size);
378
379 newhead.string_offset = (newhead.namehash_offset
380 + (newhead.namehash_size
381 * sizeof (struct namehashent)));
382 /* Keep the string table size aligned to 4 bytes, so that
383 all the struct { uint32_t } types following are happy. */
384 newhead.string_size = MAX ((2 * newhead.string_used + 3) & -4,
385 newhead.string_size);
386
387 newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
388 newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
389 newhead.locrectab_size);
390
391 newhead.sumhash_offset = (newhead.locrectab_offset
392 + (newhead.locrectab_size
393 * sizeof (struct locrecent)));
394 newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
395 newhead.sumhash_size);
396
397 total = (newhead.sumhash_offset
398 + newhead.sumhash_size * sizeof (struct sumhashent));
399
400 /* The new file is empty now. */
401 newhead.namehash_used = 0;
402 newhead.string_used = 0;
403 newhead.locrectab_used = 0;
404 newhead.sumhash_used = 0;
405
406 /* Write out the header and create room for the other data structures. */
407 if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
408 != sizeof (newhead))
409 {
410 int errval = errno;
411 unlink (fname);
412 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
413 }
414
415 if (ftruncate64 (fd, total) != 0)
416 {
417 int errval = errno;
418 unlink (fname);
419 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
420 }
421
422 size_t reserved, mmap_len;
423 int xflags;
424 void *mmap_base;
425 void *p = prepare_address_space (fd, total, &reserved, &xflags, &mmap_base,
426 &mmap_len);
427
428 /* Map the header and all the administration data structures. */
429 p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0);
430 if (p == MAP_FAILED)
431 {
432 int errval = errno;
433 unlink (fname);
434 error (EXIT_FAILURE, errval, _("cannot map archive header"));
435 }
436
437 /* Lock the new file. */
438 if (lockf64 (fd, F_LOCK, total) != 0)
439 {
440 int errval = errno;
441 unlink (fname);
442 error (EXIT_FAILURE, errval, _("cannot lock new archive"));
443 }
444
445 new_ah.mmaped = total;
446 new_ah.mmap_base = mmap_base;
447 new_ah.mmap_len = mmap_len;
448 new_ah.addr = p;
449 new_ah.fd = fd;
450 new_ah.reserved = reserved;
451
452 /* Walk through the hash name hash table to find out what data is
453 still referenced and transfer it into the new file. */
454 oldnamehashtab = (struct namehashent *) ((char *) ah->addr
455 + head->namehash_offset);
456
457 /* Sort the old locrec table in order of data position. */
458 struct oldlocrecent oldlocrecarray[head->namehash_size];
459 for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
460 if (oldnamehashtab[cnt].locrec_offset != 0)
461 {
462 oldlocrecarray[loccnt].cnt = cnt;
463 oldlocrecarray[loccnt++].locrec
464 = (struct locrecent *) ((char *) ah->addr
465 + oldnamehashtab[cnt].locrec_offset);
466 }
467 qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
468 oldlocrecentcmp);
469
470 uint32_t last_locrec_offset = 0;
471 for (cnt = 0; cnt < loccnt; ++cnt)
472 {
473 /* Insert this entry in the new hash table. */
474 locale_data_t old_data;
475 unsigned int idx;
476 struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
477
478 for (idx = 0; idx < __LC_LAST; ++idx)
479 if (idx != LC_ALL)
480 {
481 old_data[idx].size = oldlocrec->record[idx].len;
482 old_data[idx].addr
483 = ((char *) ah->addr + oldlocrec->record[idx].offset);
484
485 __md5_buffer (old_data[idx].addr, old_data[idx].size,
486 old_data[idx].sum);
487 }
488
489 if (cnt > 0 && oldlocrecarray[cnt - 1].locrec == oldlocrec)
490 {
491 const char *oldname
492 = ((char *) ah->addr
493 + oldnamehashtab[oldlocrecarray[cnt - 1].cnt].name_offset);
494
495 add_alias (&new_ah,
496 ((char *) ah->addr
497 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
498 0, oldname, &last_locrec_offset);
499 continue;
500 }
501
502 last_locrec_offset =
503 add_locale (&new_ah,
504 ((char *) ah->addr
505 + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
506 old_data, 0);
507 if (last_locrec_offset == 0)
508 error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
509 }
510
511 /* Make the file globally readable. */
512 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
513 {
514 int errval = errno;
515 unlink (fname);
516 error (EXIT_FAILURE, errval,
517 _("cannot change mode of resized locale archive"));
518 }
519
520 /* Rename the new file. */
521 if (rename (fname, archivefname) != 0)
522 {
523 int errval = errno;
524 unlink (fname);
525 error (EXIT_FAILURE, errval, _("cannot rename new archive"));
526 }
527
528 /* Close the old file. */
529 close_archive (ah);
530
531 /* Add the information for the new one. */
532 *ah = new_ah;
533 }
534
535
536 void
537 open_archive (struct locarhandle *ah, bool readonly)
538 {
539 struct stat64 st;
540 struct stat64 st2;
541 int fd;
542 struct locarhead head;
543 int retry = 0;
544 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
545 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
546
547 if (output_prefix)
548 memcpy (archivefname, output_prefix, prefix_len);
549 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
550
551 while (1)
552 {
553 /* Open the archive. We must have exclusive write access. */
554 fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
555 if (fd == -1)
556 {
557 /* Maybe the file does not yet exist. */
558 if (errno == ENOENT)
559 {
560 if (readonly)
561 {
562 static const struct locarhead nullhead =
563 {
564 .namehash_used = 0,
565 .namehash_offset = 0,
566 .namehash_size = 0
567 };
568
569 ah->addr = (void *) &nullhead;
570 ah->fd = -1;
571 }
572 else
573 create_archive (archivefname, ah);
574
575 return;
576 }
577 else
578 error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
579 archivefname);
580 }
581
582 if (fstat64 (fd, &st) < 0)
583 error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
584 archivefname);
585
586 if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1)
587 {
588 close (fd);
589
590 if (retry++ < max_locarchive_open_retry)
591 {
592 struct timespec req;
593
594 /* Wait for a bit. */
595 req.tv_sec = 0;
596 req.tv_nsec = 1000000 * (random () % 500 + 1);
597 (void) nanosleep (&req, NULL);
598
599 continue;
600 }
601
602 error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
603 archivefname);
604 }
605
606 /* One more check. Maybe another process replaced the archive file
607 with a new, larger one since we opened the file. */
608 if (stat64 (archivefname, &st2) == -1
609 || st.st_dev != st2.st_dev
610 || st.st_ino != st2.st_ino)
611 {
612 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
613 close (fd);
614 continue;
615 }
616
617 /* Leave the loop. */
618 break;
619 }
620
621 /* Read the header. */
622 if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
623 {
624 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
625 error (EXIT_FAILURE, errno, _("cannot read archive header"));
626 }
627
628 ah->fd = fd;
629 ah->mmaped = st.st_size;
630
631 size_t reserved, mmap_len;
632 int xflags;
633 void *mmap_base;
634 void *p = prepare_address_space (fd, st.st_size, &reserved, &xflags,
635 &mmap_base, &mmap_len);
636
637 /* Map the entire file. We might need to compare the category data
638 in the file with the newly added data. */
639 ah->addr = mmap64 (p, st.st_size, PROT_READ | (readonly ? 0 : PROT_WRITE),
640 MAP_SHARED | xflags, fd, 0);
641 if (ah->addr == MAP_FAILED)
642 {
643 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
644 error (EXIT_FAILURE, errno, _("cannot map archive header"));
645 }
646 ah->reserved = reserved;
647 ah->mmap_base = mmap_base;
648 ah->mmap_len = mmap_len;
649 }
650
651
652 void
653 close_archive (struct locarhandle *ah)
654 {
655 if (ah->fd != -1)
656 {
657 if (ah->mmap_base)
658 munmap (ah->mmap_base, ah->mmap_len);
659 else
660 munmap (ah->addr, ah->reserved);
661 close (ah->fd);
662 }
663 }
664
665 #include "../../intl/explodename.c"
666 #include "../../intl/l10nflist.c"
667
668 static struct namehashent *
669 insert_name (struct locarhandle *ah,
670 const char *name, size_t name_len, bool replace)
671 {
672 const struct locarhead *const head = ah->addr;
673 struct namehashent *namehashtab
674 = (struct namehashent *) ((char *) ah->addr + head->namehash_offset);
675 unsigned int insert_idx, idx, incr;
676
677 /* Hash value of the locale name. */
678 uint32_t hval = archive_hashval (name, name_len);
679
680 insert_idx = -1;
681 idx = hval % head->namehash_size;
682 incr = 1 + hval % (head->namehash_size - 2);
683
684 /* If the name_offset field is zero this means this is a
685 deleted entry and therefore no entry can be found. */
686 while (namehashtab[idx].name_offset != 0)
687 {
688 if (namehashtab[idx].hashval == hval
689 && strcmp (name,
690 (char *) ah->addr + namehashtab[idx].name_offset) == 0)
691 {
692 /* Found the entry. */
693 if (namehashtab[idx].locrec_offset != 0 && ! replace)
694 {
695 if (! be_quiet)
696 error (0, 0, _("locale '%s' already exists"), name);
697 return NULL;
698 }
699
700 break;
701 }
702
703 if (namehashtab[idx].hashval == hval && ! be_quiet)
704 {
705 error (0, 0, "hash collision (%u) %s, %s",
706 hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
707 }
708
709 /* Remember the first place we can insert the new entry. */
710 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
711 insert_idx = idx;
712
713 idx += incr;
714 if (idx >= head->namehash_size)
715 idx -= head->namehash_size;
716 }
717
718 /* Add as early as possible. */
719 if (insert_idx != -1)
720 idx = insert_idx;
721
722 namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */
723 return &namehashtab[idx];
724 }
725
726 static void
727 add_alias (struct locarhandle *ah, const char *alias, bool replace,
728 const char *oldname, uint32_t *locrec_offset_p)
729 {
730 uint32_t locrec_offset = *locrec_offset_p;
731 struct locarhead *head = ah->addr;
732 const size_t name_len = strlen (alias);
733 struct namehashent *namehashent = insert_name (ah, alias, strlen (alias),
734 replace);
735 if (namehashent == NULL && ! replace)
736 return;
737
738 if (namehashent->name_offset == 0)
739 {
740 /* We are adding a new hash entry for this alias.
741 Determine whether we have to resize the file. */
742 if (head->string_used + name_len + 1 > head->string_size
743 || 100 * head->namehash_used > 75 * head->namehash_size)
744 {
745 /* The current archive is not large enough. */
746 enlarge_archive (ah, head);
747
748 /* The locrecent might have moved, so we have to look up
749 the old name afresh. */
750 namehashent = insert_name (ah, oldname, strlen (oldname), true);
751 assert (namehashent->name_offset != 0);
752 assert (namehashent->locrec_offset != 0);
753 *locrec_offset_p = namehashent->locrec_offset;
754
755 /* Tail call to try the whole thing again. */
756 add_alias (ah, alias, replace, oldname, locrec_offset_p);
757 return;
758 }
759
760 /* Add the name string. */
761 memcpy (ah->addr + head->string_offset + head->string_used,
762 alias, name_len + 1);
763 namehashent->name_offset = head->string_offset + head->string_used;
764 head->string_used += name_len + 1;
765
766 ++head->namehash_used;
767 }
768
769 if (namehashent->locrec_offset != 0)
770 {
771 /* Replacing an existing entry.
772 Mark that we are no longer using the old locrecent. */
773 struct locrecent *locrecent
774 = (struct locrecent *) ((char *) ah->addr
775 + namehashent->locrec_offset);
776 --locrecent->refs;
777 }
778
779 /* Point this entry at the locrecent installed for the main name. */
780 namehashent->locrec_offset = locrec_offset;
781 }
782
783 static int /* qsort comparator used below */
784 cmpcategorysize (const void *a, const void *b)
785 {
786 if (*(const void **) a == NULL)
787 return 1;
788 if (*(const void **) b == NULL)
789 return -1;
790 return ((*(const struct locale_category_data **) a)->size
791 - (*(const struct locale_category_data **) b)->size);
792 }
793
794 /* Check the content of the archive for duplicates. Add the content
795 of the files if necessary. Returns the locrec_offset. */
796 static uint32_t
797 add_locale (struct locarhandle *ah,
798 const char *name, locale_data_t data, bool replace)
799 {
800 /* First look for the name. If it already exists and we are not
801 supposed to replace it don't do anything. If it does not exist
802 we have to allocate a new locale record. */
803 size_t name_len = strlen (name);
804 uint32_t file_offsets[__LC_LAST];
805 unsigned int num_new_offsets = 0;
806 struct sumhashent *sumhashtab;
807 uint32_t hval;
808 unsigned int cnt, idx;
809 struct locarhead *head;
810 struct namehashent *namehashent;
811 unsigned int incr;
812 struct locrecent *locrecent;
813 off64_t lastoffset;
814 char *ptr;
815 struct locale_category_data *size_order[__LC_LAST];
816 const size_t pagesz = getpagesize ();
817 int small_mask;
818
819 head = ah->addr;
820 sumhashtab = (struct sumhashent *) ((char *) ah->addr
821 + head->sumhash_offset);
822
823 memset (file_offsets, 0, sizeof (file_offsets));
824
825 size_order[LC_ALL] = NULL;
826 for (cnt = 0; cnt < __LC_LAST; ++cnt)
827 if (cnt != LC_ALL)
828 size_order[cnt] = &data[cnt];
829
830 /* Sort the array in ascending order of data size. */
831 qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
832
833 small_mask = 0;
834 data[LC_ALL].size = 0;
835 for (cnt = 0; cnt < __LC_LAST; ++cnt)
836 if (size_order[cnt] != NULL)
837 {
838 const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
839 if (data[LC_ALL].size + rounded_size > 2 * pagesz)
840 {
841 /* This category makes the small-categories block
842 stop being small, so this is the end of the road. */
843 do
844 size_order[cnt++] = NULL;
845 while (cnt < __LC_LAST);
846 break;
847 }
848 data[LC_ALL].size += rounded_size;
849 small_mask |= 1 << (size_order[cnt] - data);
850 }
851
852 /* Copy the data for all the small categories into the LC_ALL
853 pseudo-category. */
854
855 data[LC_ALL].addr = alloca (data[LC_ALL].size);
856 memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
857
858 ptr = data[LC_ALL].addr;
859 for (cnt = 0; cnt < __LC_LAST; ++cnt)
860 if (small_mask & (1 << cnt))
861 {
862 memcpy (ptr, data[cnt].addr, data[cnt].size);
863 ptr += (data[cnt].size + 15) & -16;
864 }
865 __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
866
867 /* For each locale category data set determine whether the same data
868 is already somewhere in the archive. */
869 for (cnt = 0; cnt < __LC_LAST; ++cnt)
870 if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
871 {
872 ++num_new_offsets;
873
874 /* Compute the hash value of the checksum to determine a
875 starting point for the search in the MD5 hash value
876 table. */
877 hval = archive_hashval (data[cnt].sum, 16);
878
879 idx = hval % head->sumhash_size;
880 incr = 1 + hval % (head->sumhash_size - 2);
881
882 while (sumhashtab[idx].file_offset != 0)
883 {
884 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
885 {
886 /* Check the content, there could be a collision of
887 the hash sum.
888
889 Unfortunately the sumhashent record does not include
890 the size of the stored data. So we have to search for
891 it. */
892 locrecent = (struct locrecent *) ((char *) ah->addr
893 + head->locrectab_offset);
894 size_t iloc;
895 for (iloc = 0; iloc < head->locrectab_used; ++iloc)
896 if (locrecent[iloc].refs != 0
897 && (locrecent[iloc].record[cnt].offset
898 == sumhashtab[idx].file_offset))
899 break;
900
901 if (iloc != head->locrectab_used
902 && data[cnt].size == locrecent[iloc].record[cnt].len
903 /* We have to compare the content. Either we can
904 have the data mmaped or we have to read from
905 the file. */
906 && (file_data_available_p (ah, sumhashtab[idx].file_offset,
907 data[cnt].size)
908 ? memcmp (data[cnt].addr,
909 (char *) ah->addr
910 + sumhashtab[idx].file_offset,
911 data[cnt].size) == 0
912 : compare_from_file (ah, data[cnt].addr,
913 sumhashtab[idx].file_offset,
914 data[cnt].size) == 0))
915 {
916 /* Found it. */
917 file_offsets[cnt] = sumhashtab[idx].file_offset;
918 --num_new_offsets;
919 break;
920 }
921 }
922
923 idx += incr;
924 if (idx >= head->sumhash_size)
925 idx -= head->sumhash_size;
926 }
927 }
928
929 /* Find a slot for the locale name in the hash table. */
930 namehashent = insert_name (ah, name, name_len, replace);
931 if (namehashent == NULL) /* Already exists and !REPLACE. */
932 return 0;
933
934 /* Determine whether we have to resize the file. */
935 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
936 || (namehashent->locrec_offset == 0
937 && (head->locrectab_used == head->locrectab_size
938 || head->string_used + name_len + 1 > head->string_size
939 || 100 * head->namehash_used > 75 * head->namehash_size)))
940 {
941 /* The current archive is not large enough. */
942 enlarge_archive (ah, head);
943 return add_locale (ah, name, data, replace);
944 }
945
946 /* Add the locale data which is not yet in the archive. */
947 for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
948 if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
949 && file_offsets[cnt] == 0)
950 {
951 /* The data for this section is not yet available in the
952 archive. Append it. */
953 off64_t lastpos;
954 uint32_t md5hval;
955
956 lastpos = lseek64 (ah->fd, 0, SEEK_END);
957 if (lastpos == (off64_t) -1)
958 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
959
960 /* If block of small categories would cross page boundary,
961 align it unless it immediately follows a large category. */
962 if (cnt == LC_ALL && lastoffset != lastpos
963 && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
964 & -pagesz)
965 > ((data[cnt].size + pagesz - 1) & -pagesz)))
966 {
967 size_t sz = pagesz - (lastpos & (pagesz - 1));
968 char *zeros = alloca (sz);
969
970 memset (zeros, 0, sz);
971 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
972 error (EXIT_FAILURE, errno,
973 _("cannot add to locale archive"));
974
975 lastpos += sz;
976 }
977
978 /* Align all data to a 16 byte boundary. */
979 if ((lastpos & 15) != 0)
980 {
981 static const char zeros[15] = { 0, };
982
983 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
984 != 16 - (lastpos & 15))
985 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
986
987 lastpos += 16 - (lastpos & 15);
988 }
989
990 /* Remember the position. */
991 file_offsets[cnt] = lastpos;
992 lastoffset = lastpos + data[cnt].size;
993
994 /* Write the data. */
995 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
996 != data[cnt].size)
997 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
998
999 /* Add the hash value to the hash table. */
1000 md5hval = archive_hashval (data[cnt].sum, 16);
1001
1002 idx = md5hval % head->sumhash_size;
1003 incr = 1 + md5hval % (head->sumhash_size - 2);
1004
1005 while (sumhashtab[idx].file_offset != 0)
1006 {
1007 idx += incr;
1008 if (idx >= head->sumhash_size)
1009 idx -= head->sumhash_size;
1010 }
1011
1012 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
1013 sumhashtab[idx].file_offset = file_offsets[cnt];
1014
1015 ++head->sumhash_used;
1016 }
1017
1018 lastoffset = file_offsets[LC_ALL];
1019 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1020 if (small_mask & (1 << cnt))
1021 {
1022 file_offsets[cnt] = lastoffset;
1023 lastoffset += (data[cnt].size + 15) & -16;
1024 }
1025
1026 if (namehashent->name_offset == 0)
1027 {
1028 /* Add the name string. */
1029 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
1030 name, name_len + 1);
1031 namehashent->name_offset = head->string_offset + head->string_used;
1032 head->string_used += name_len + 1;
1033 ++head->namehash_used;
1034 }
1035
1036 if (namehashent->locrec_offset == 0)
1037 {
1038 /* Allocate a name location record. */
1039 namehashent->locrec_offset = (head->locrectab_offset
1040 + (head->locrectab_used++
1041 * sizeof (struct locrecent)));
1042 locrecent = (struct locrecent *) ((char *) ah->addr
1043 + namehashent->locrec_offset);
1044 locrecent->refs = 1;
1045 }
1046 else
1047 {
1048 /* If there are other aliases pointing to this locrecent,
1049 we still need a new one. If not, reuse the old one. */
1050
1051 locrecent = (struct locrecent *) ((char *) ah->addr
1052 + namehashent->locrec_offset);
1053 if (locrecent->refs > 1)
1054 {
1055 --locrecent->refs;
1056 namehashent->locrec_offset = (head->locrectab_offset
1057 + (head->locrectab_used++
1058 * sizeof (struct locrecent)));
1059 locrecent = (struct locrecent *) ((char *) ah->addr
1060 + namehashent->locrec_offset);
1061 locrecent->refs = 1;
1062 }
1063 }
1064
1065 /* Fill in the table with the locations of the locale data. */
1066 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1067 {
1068 locrecent->record[cnt].offset = file_offsets[cnt];
1069 locrecent->record[cnt].len = data[cnt].size;
1070 }
1071
1072 return namehashent->locrec_offset;
1073 }
1074
1075
1076 /* Check the content of the archive for duplicates. Add the content
1077 of the files if necessary. Add all the names, possibly overwriting
1078 old files. */
1079 int
1080 add_locale_to_archive (ah, name, data, replace)
1081 struct locarhandle *ah;
1082 const char *name;
1083 locale_data_t data;
1084 bool replace;
1085 {
1086 char *normalized_name = NULL;
1087 uint32_t locrec_offset;
1088
1089 /* First analyze the name to decide how to archive it. */
1090 const char *language;
1091 const char *modifier;
1092 const char *territory;
1093 const char *codeset;
1094 const char *normalized_codeset;
1095 int mask = _nl_explode_name (strdupa (name),
1096 &language, &modifier, &territory,
1097 &codeset, &normalized_codeset);
1098 if (mask == -1)
1099 return -1;
1100
1101 if (mask & XPG_NORM_CODESET)
1102 /* This name contains a codeset in unnormalized form.
1103 We will store it in the archive with a normalized name. */
1104 asprintf (&normalized_name, "%s%s%s.%s%s%s",
1105 language, territory == NULL ? "" : "_", territory ?: "",
1106 (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset,
1107 modifier == NULL ? "" : "@", modifier ?: "");
1108
1109 /* This call does the main work. */
1110 locrec_offset = add_locale (ah, normalized_name ?: name, data, replace);
1111 if (locrec_offset == 0)
1112 {
1113 free (normalized_name);
1114 if (mask & XPG_NORM_CODESET)
1115 free ((char *) normalized_codeset);
1116 return -1;
1117 }
1118
1119 if ((mask & XPG_CODESET) == 0)
1120 {
1121 /* This name lacks a codeset, so determine the locale's codeset and
1122 add an alias for its name with normalized codeset appended. */
1123
1124 const struct
1125 {
1126 unsigned int magic;
1127 unsigned int nstrings;
1128 unsigned int strindex[0];
1129 } *filedata = data[LC_CTYPE].addr;
1130 codeset = (char *) filedata
1131 + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
1132 char *normalized_codeset_name = NULL;
1133
1134 normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
1135 mask |= XPG_NORM_CODESET;
1136
1137 asprintf (&normalized_codeset_name, "%s%s%s.%s%s%s",
1138 language, territory == NULL ? "" : "_", territory ?: "",
1139 normalized_codeset,
1140 modifier == NULL ? "" : "@", modifier ?: "");
1141
1142 add_alias (ah, normalized_codeset_name, replace,
1143 normalized_name ?: name, &locrec_offset);
1144 free (normalized_codeset_name);
1145 }
1146
1147 /* Now read the locale.alias files looking for lines whose
1148 right hand side matches our name after normalization. */
1149 int result = 0;
1150 if (alias_file != NULL)
1151 {
1152 FILE *fp;
1153 fp = fopen (alias_file, "rm");
1154 if (fp == NULL)
1155 error (1, errno, _("locale alias file `%s' not found"),
1156 alias_file);
1157
1158 /* No threads present. */
1159 __fsetlocking (fp, FSETLOCKING_BYCALLER);
1160
1161 while (! feof_unlocked (fp))
1162 {
1163 /* It is a reasonable approach to use a fix buffer here
1164 because
1165 a) we are only interested in the first two fields
1166 b) these fields must be usable as file names and so must
1167 not be that long */
1168 char buf[BUFSIZ];
1169 char *alias;
1170 char *value;
1171 char *cp;
1172
1173 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1174 /* EOF reached. */
1175 break;
1176
1177 cp = buf;
1178 /* Ignore leading white space. */
1179 while (isspace (cp[0]) && cp[0] != '\n')
1180 ++cp;
1181
1182 /* A leading '#' signals a comment line. */
1183 if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
1184 {
1185 alias = cp++;
1186 while (cp[0] != '\0' && !isspace (cp[0]))
1187 ++cp;
1188 /* Terminate alias name. */
1189 if (cp[0] != '\0')
1190 *cp++ = '\0';
1191
1192 /* Now look for the beginning of the value. */
1193 while (isspace (cp[0]))
1194 ++cp;
1195
1196 if (cp[0] != '\0')
1197 {
1198 value = cp++;
1199 while (cp[0] != '\0' && !isspace (cp[0]))
1200 ++cp;
1201 /* Terminate value. */
1202 if (cp[0] == '\n')
1203 {
1204 /* This has to be done to make the following
1205 test for the end of line possible. We are
1206 looking for the terminating '\n' which do not
1207 overwrite here. */
1208 *cp++ = '\0';
1209 *cp = '\n';
1210 }
1211 else if (cp[0] != '\0')
1212 *cp++ = '\0';
1213
1214 /* Does this alias refer to our locale? We will
1215 normalize the right hand side and compare the
1216 elements of the normalized form. */
1217 {
1218 const char *rhs_language;
1219 const char *rhs_modifier;
1220 const char *rhs_territory;
1221 const char *rhs_codeset;
1222 const char *rhs_normalized_codeset;
1223 int rhs_mask = _nl_explode_name (value,
1224 &rhs_language,
1225 &rhs_modifier,
1226 &rhs_territory,
1227 &rhs_codeset,
1228 &rhs_normalized_codeset);
1229 if (rhs_mask == -1)
1230 {
1231 result = -1;
1232 goto out;
1233 }
1234 if (!strcmp (language, rhs_language)
1235 && ((rhs_mask & XPG_CODESET)
1236 /* He has a codeset, it must match normalized. */
1237 ? !strcmp ((mask & XPG_NORM_CODESET)
1238 ? normalized_codeset : codeset,
1239 (rhs_mask & XPG_NORM_CODESET)
1240 ? rhs_normalized_codeset : rhs_codeset)
1241 /* He has no codeset, we must also have none. */
1242 : (mask & XPG_CODESET) == 0)
1243 /* Codeset (or lack thereof) matches. */
1244 && !strcmp (territory ?: "", rhs_territory ?: "")
1245 && !strcmp (modifier ?: "", rhs_modifier ?: ""))
1246 /* We have a winner. */
1247 add_alias (ah, alias, replace,
1248 normalized_name ?: name, &locrec_offset);
1249 if (rhs_mask & XPG_NORM_CODESET)
1250 free ((char *) rhs_normalized_codeset);
1251 }
1252 }
1253 }
1254
1255 /* Possibly not the whole line fits into the buffer.
1256 Ignore the rest of the line. */
1257 while (strchr (cp, '\n') == NULL)
1258 {
1259 cp = buf;
1260 if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
1261 /* Make sure the inner loop will be left. The outer
1262 loop will exit at the `feof' test. */
1263 *cp = '\n';
1264 }
1265 }
1266
1267 out:
1268 fclose (fp);
1269 }
1270
1271 free (normalized_name);
1272
1273 if (mask & XPG_NORM_CODESET)
1274 free ((char *) normalized_codeset);
1275
1276 return result;
1277 }
1278
1279
1280 int
1281 add_locales_to_archive (nlist, list, replace)
1282 size_t nlist;
1283 char *list[];
1284 bool replace;
1285 {
1286 struct locarhandle ah;
1287 int result = 0;
1288
1289 /* Open the archive. This call never returns if we cannot
1290 successfully open the archive. */
1291 open_archive (&ah, false);
1292
1293 while (nlist-- > 0)
1294 {
1295 const char *fname = *list++;
1296 size_t fnamelen = strlen (fname);
1297 struct stat64 st;
1298 DIR *dirp;
1299 struct dirent64 *d;
1300 int seen;
1301 locale_data_t data;
1302 int cnt;
1303
1304 if (! be_quiet)
1305 printf (_("Adding %s\n"), fname);
1306
1307 /* First see whether this really is a directory and whether it
1308 contains all the require locale category files. */
1309 if (stat64 (fname, &st) < 0)
1310 {
1311 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
1312 strerror (errno));
1313 continue;
1314 }
1315 if (!S_ISDIR (st.st_mode))
1316 {
1317 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
1318 continue;
1319 }
1320
1321 dirp = opendir (fname);
1322 if (dirp == NULL)
1323 {
1324 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
1325 fname, strerror (errno));
1326 continue;
1327 }
1328
1329 seen = 0;
1330 while ((d = readdir64 (dirp)) != NULL)
1331 {
1332 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1333 if (cnt != LC_ALL)
1334 if (strcmp (d->d_name, locnames[cnt]) == 0)
1335 {
1336 unsigned char d_type;
1337
1338 /* We have an object of the required name. If it's
1339 a directory we have to look at a file with the
1340 prefix "SYS_". Otherwise we have found what we
1341 are looking for. */
1342 #ifdef _DIRENT_HAVE_D_TYPE
1343 d_type = d->d_type;
1344
1345 if (d_type != DT_REG)
1346 #endif
1347 {
1348 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
1349
1350 #ifdef _DIRENT_HAVE_D_TYPE
1351 if (d_type == DT_UNKNOWN)
1352 #endif
1353 {
1354 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
1355 d->d_name);
1356
1357 if (stat64 (fullname, &st) == -1)
1358 /* We cannot stat the file, ignore it. */
1359 break;
1360
1361 d_type = IFTODT (st.st_mode);
1362 }
1363
1364 if (d_type == DT_DIR)
1365 {
1366 /* We have to do more tests. The file is a
1367 directory and it therefore must contain a
1368 regular file with the same name except a
1369 "SYS_" prefix. */
1370 char *t = stpcpy (stpcpy (fullname, fname), "/");
1371 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
1372 d->d_name);
1373
1374 if (stat64 (fullname, &st) == -1)
1375 /* There is no SYS_* file or we cannot
1376 access it. */
1377 break;
1378
1379 d_type = IFTODT (st.st_mode);
1380 }
1381 }
1382
1383 /* If we found a regular file (eventually after
1384 following a symlink) we are successful. */
1385 if (d_type == DT_REG)
1386 ++seen;
1387 break;
1388 }
1389 }
1390
1391 closedir (dirp);
1392
1393 if (seen != __LC_LAST - 1)
1394 {
1395 /* We don't have all locale category files. Ignore the name. */
1396 error (0, 0, _("incomplete set of locale files in \"%s\""),
1397 fname);
1398 continue;
1399 }
1400
1401 /* Add the files to the archive. To do this we first compute
1402 sizes and the MD5 sums of all the files. */
1403 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1404 if (cnt != LC_ALL)
1405 {
1406 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
1407 int fd;
1408
1409 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
1410 fd = open64 (fullname, O_RDONLY);
1411 if (fd == -1 || fstat64 (fd, &st) == -1)
1412 {
1413 /* Cannot read the file. */
1414 if (fd != -1)
1415 close (fd);
1416 break;
1417 }
1418
1419 if (S_ISDIR (st.st_mode))
1420 {
1421 char *t;
1422 close (fd);
1423 t = stpcpy (stpcpy (fullname, fname), "/");
1424 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
1425 locnames[cnt]);
1426
1427 fd = open64 (fullname, O_RDONLY);
1428 if (fd == -1 || fstat64 (fd, &st) == -1
1429 || !S_ISREG (st.st_mode))
1430 {
1431 if (fd != -1)
1432 close (fd);
1433 break;
1434 }
1435 }
1436
1437 /* Map the file. */
1438 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
1439 fd, 0);
1440 if (data[cnt].addr == MAP_FAILED)
1441 {
1442 /* Cannot map it. */
1443 close (fd);
1444 break;
1445 }
1446
1447 data[cnt].size = st.st_size;
1448 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
1449
1450 /* We don't need the file descriptor anymore. */
1451 close (fd);
1452 }
1453
1454 if (cnt != __LC_LAST)
1455 {
1456 while (cnt-- > 0)
1457 if (cnt != LC_ALL)
1458 munmap (data[cnt].addr, data[cnt].size);
1459
1460 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
1461
1462 continue;
1463 }
1464
1465 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
1466
1467 for (cnt = 0; cnt < __LC_LAST; ++cnt)
1468 if (cnt != LC_ALL)
1469 munmap (data[cnt].addr, data[cnt].size);
1470 }
1471
1472 /* We are done. */
1473 close_archive (&ah);
1474
1475 return result;
1476 }
1477
1478
1479 int
1480 delete_locales_from_archive (nlist, list)
1481 size_t nlist;
1482 char *list[];
1483 {
1484 struct locarhandle ah;
1485 struct locarhead *head;
1486 struct namehashent *namehashtab;
1487
1488 /* Open the archive. This call never returns if we cannot
1489 successfully open the archive. */
1490 open_archive (&ah, false);
1491
1492 head = ah.addr;
1493 namehashtab = (struct namehashent *) ((char *) ah.addr
1494 + head->namehash_offset);
1495
1496 while (nlist-- > 0)
1497 {
1498 const char *locname = *list++;
1499 uint32_t hval;
1500 unsigned int idx;
1501 unsigned int incr;
1502
1503 /* Search for this locale in the archive. */
1504 hval = archive_hashval (locname, strlen (locname));
1505
1506 idx = hval % head->namehash_size;
1507 incr = 1 + hval % (head->namehash_size - 2);
1508
1509 /* If the name_offset field is zero this means this is no
1510 deleted entry and therefore no entry can be found. */
1511 while (namehashtab[idx].name_offset != 0)
1512 {
1513 if (namehashtab[idx].hashval == hval
1514 && (strcmp (locname,
1515 (char *) ah.addr + namehashtab[idx].name_offset)
1516 == 0))
1517 {
1518 /* Found the entry. Now mark it as removed by zero-ing
1519 the reference to the locale record. */
1520 namehashtab[idx].locrec_offset = 0;
1521 break;
1522 }
1523
1524 idx += incr;
1525 if (idx >= head->namehash_size)
1526 idx -= head->namehash_size;
1527 }
1528
1529 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
1530 error (0, 0, _("locale \"%s\" not in archive"), locname);
1531 }
1532
1533 close_archive (&ah);
1534
1535 return 0;
1536 }
1537
1538
1539 struct nameent
1540 {
1541 char *name;
1542 uint32_t locrec_offset;
1543 };
1544
1545
1546 struct dataent
1547 {
1548 const unsigned char *sum;
1549 uint32_t file_offset;
1550 uint32_t nlink;
1551 };
1552
1553
1554 static int
1555 nameentcmp (const void *a, const void *b)
1556 {
1557 return strcmp (((const struct nameent *) a)->name,
1558 ((const struct nameent *) b)->name);
1559 }
1560
1561
1562 static int
1563 dataentcmp (const void *a, const void *b)
1564 {
1565 if (((const struct dataent *) a)->file_offset
1566 < ((const struct dataent *) b)->file_offset)
1567 return -1;
1568
1569 if (((const struct dataent *) a)->file_offset
1570 > ((const struct dataent *) b)->file_offset)
1571 return 1;
1572
1573 return 0;
1574 }
1575
1576
1577 void
1578 show_archive_content (int verbose)
1579 {
1580 struct locarhandle ah;
1581 struct locarhead *head;
1582 struct namehashent *namehashtab;
1583 struct nameent *names;
1584 size_t cnt, used;
1585
1586 /* Open the archive. This call never returns if we cannot
1587 successfully open the archive. */
1588 open_archive (&ah, true);
1589
1590 head = ah.addr;
1591
1592 names = (struct nameent *) xmalloc (head->namehash_used
1593 * sizeof (struct nameent));
1594
1595 namehashtab = (struct namehashent *) ((char *) ah.addr
1596 + head->namehash_offset);
1597 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
1598 if (namehashtab[cnt].locrec_offset != 0)
1599 {
1600 assert (used < head->namehash_used);
1601 names[used].name = ah.addr + namehashtab[cnt].name_offset;
1602 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
1603 }
1604
1605 /* Sort the names. */
1606 qsort (names, used, sizeof (struct nameent), nameentcmp);
1607
1608 if (verbose)
1609 {
1610 struct dataent *files;
1611 struct sumhashent *sumhashtab;
1612 int sumused;
1613
1614 files = (struct dataent *) xmalloc (head->sumhash_used
1615 * sizeof (struct dataent));
1616
1617 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1618 + head->sumhash_offset);
1619 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1620 if (sumhashtab[cnt].file_offset != 0)
1621 {
1622 assert (sumused < head->sumhash_used);
1623 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1624 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1625 files[sumused++].nlink = 0;
1626 }
1627
1628 /* Sort by file locations. */
1629 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1630
1631 /* Compute nlink fields. */
1632 for (cnt = 0; cnt < used; ++cnt)
1633 {
1634 struct locrecent *locrec;
1635 int idx;
1636
1637 locrec = (struct locrecent *) ((char *) ah.addr
1638 + names[cnt].locrec_offset);
1639 for (idx = 0; idx < __LC_LAST; ++idx)
1640 if (locrec->record[LC_ALL].offset != 0
1641 ? (idx == LC_ALL
1642 || (locrec->record[idx].offset
1643 < locrec->record[LC_ALL].offset)
1644 || (locrec->record[idx].offset + locrec->record[idx].len
1645 > (locrec->record[LC_ALL].offset
1646 + locrec->record[LC_ALL].len)))
1647 : idx != LC_ALL)
1648 {
1649 struct dataent *data, dataent;
1650
1651 dataent.file_offset = locrec->record[idx].offset;
1652 data = (struct dataent *) bsearch (&dataent, files, sumused,
1653 sizeof (struct dataent),
1654 dataentcmp);
1655 assert (data != NULL);
1656 ++data->nlink;
1657 }
1658 }
1659
1660 /* Print it. */
1661 for (cnt = 0; cnt < used; ++cnt)
1662 {
1663 struct locrecent *locrec;
1664 int idx, i;
1665
1666 locrec = (struct locrecent *) ((char *) ah.addr
1667 + names[cnt].locrec_offset);
1668 for (idx = 0; idx < __LC_LAST; ++idx)
1669 if (idx != LC_ALL)
1670 {
1671 struct dataent *data, dataent;
1672
1673 dataent.file_offset = locrec->record[idx].offset;
1674 if (locrec->record[LC_ALL].offset != 0
1675 && dataent.file_offset >= locrec->record[LC_ALL].offset
1676 && (dataent.file_offset + locrec->record[idx].len
1677 <= (locrec->record[LC_ALL].offset
1678 + locrec->record[LC_ALL].len)))
1679 dataent.file_offset = locrec->record[LC_ALL].offset;
1680
1681 data = (struct dataent *) bsearch (&dataent, files, sumused,
1682 sizeof (struct dataent),
1683 dataentcmp);
1684 printf ("%6d %7x %3d%c ",
1685 locrec->record[idx].len, locrec->record[idx].offset,
1686 data->nlink,
1687 dataent.file_offset == locrec->record[LC_ALL].offset
1688 ? '+' : ' ');
1689 for (i = 0; i < 16; i += 4)
1690 printf ("%02x%02x%02x%02x",
1691 data->sum[i], data->sum[i + 1],
1692 data->sum[i + 2], data->sum[i + 3]);
1693 printf (" %s/%s\n", names[cnt].name,
1694 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1695 : locnames[idx]);
1696 }
1697 }
1698 }
1699 else
1700 for (cnt = 0; cnt < used; ++cnt)
1701 puts (names[cnt].name);
1702
1703 close_archive (&ah);
1704
1705 exit (EXIT_SUCCESS);
1706 }