]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/programs/locarchive.c
Update.
[thirdparty/glibc.git] / locale / programs / locarchive.c
CommitLineData
a7b65cdc
UD
1/* Copyright (C) 2002 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 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#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <assert.h>
25#include <dirent.h>
26#include <errno.h>
27#include <error.h>
28#include <fcntl.h>
29#include <inttypes.h>
30#include <libintl.h>
31#include <locale.h>
32#include <stdbool.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <time.h>
37#include <unistd.h>
38#include <sys/mman.h>
39#include <sys/param.h>
40#include <sys/stat.h>
41
42#include "../../crypt/md5.h"
43#include "../localeinfo.h"
44#include "../locarchive.h"
45#include "simple-hash.h"
46#include "localedef.h"
47
531bafd8 48extern const char *output_prefix;
a7b65cdc 49
531bafd8 50#define ARCHIVE_NAME LOCALEDIR "/locale-archive"
a7b65cdc
UD
51
52static const char *locnames[] =
53 {
54#define DEFINE_CATEGORY(category, category_name, items, a) \
55 [category] = category_name,
56#include "categories.def"
57#undef DEFINE_CATEGORY
58 };
59
60
61/* Size of the initial archive header. */
62#define INITIAL_NUM_NANES 450
63#define INITIAL_SIZE_STRINGS 3500
64#define INITIAL_NUM_LOCREC 350
65#define INITIAL_NUM_SUMS 2000
66
67
68static void
531bafd8 69create_archive (const char *archivefname, struct locarhandle *ah)
a7b65cdc
UD
70{
71 int fd;
531bafd8 72 char fname[strlen (archivefname) + sizeof (".XXXXXX")];
a7b65cdc
UD
73 struct locarhead head;
74 void *p;
75 size_t total;
76
531bafd8
UD
77 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
78
a7b65cdc
UD
79 /* Create a temporary file in the correct directory. */
80 fd = mkstemp (fname);
81 if (fd == -1)
82 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
83
84 /* Create the initial content of the archive. */
85 head.magic = AR_MAGIC;
86 head.namehash_offset = sizeof (struct locarhead);
87 head.namehash_used = 0;
88 head.namehash_size = next_prime (INITIAL_NUM_NANES);
89
90 head.string_offset = (head.namehash_offset
91 + head.namehash_size * sizeof (struct namehashent));
92 head.string_used = 0;
93 head.string_size = INITIAL_SIZE_STRINGS;
94
95 head.locrectab_offset = head.string_offset + head.string_size;
96 head.locrectab_used = 0;
97 head.locrectab_size = INITIAL_NUM_LOCREC;
98
99 head.sumhash_offset = (head.locrectab_offset
100 + head.locrectab_size * sizeof (struct locrecent));
101 head.sumhash_used = 0;
102 head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
103
104 total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
105
106 /* Write out the header and create room for the other data structures. */
107 if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
108 {
109 int errval = errno;
110 unlink (fname);
111 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
112 }
113
114 if (ftruncate64 (fd, total) != 0)
115 {
116 int errval = errno;
117 unlink (fname);
118 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
119 }
120
121 /* Map the header and all the administration data structures. */
122 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
123 if (p == MAP_FAILED)
124 {
125 int errval = errno;
126 unlink (fname);
127 error (EXIT_FAILURE, errval, _("cannot map archive header"));
128 }
129
130 /* Now try to rename it. We don't use the rename function since
131 this would overwrite a file which has been created in
132 parallel. */
133 if (link (fname, archivefname) == -1)
134 {
135 int errval = errno;
136
137 /* We cannot use the just created file. */
138 close (fd);
139 unlink (fname);
140
141 if (errval == EEXIST)
142 {
143 /* There is already an archive. Must have been a localedef run
144 which happened in parallel. Simply open this file then. */
b2bffca2 145 open_archive (ah, false);
a7b65cdc
UD
146 return;
147 }
148
149 error (EXIT_FAILURE, errval, _("failed to create new locale archive"));
150 }
151
152 /* Remove the temporary name. */
153 unlink (fname);
154
155 /* Make the file globally readable. */
156 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
157 {
158 int errval = errno;
159 unlink (archivefname);
160 error (EXIT_FAILURE, errval,
161 _("cannot change mode of new locale archive"));
162 }
163
164 ah->fd = fd;
165 ah->addr = p;
166 ah->len = total;
167}
168
169
170static void
171enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
172{
173 struct stat64 st;
174 int fd;
a7b65cdc
UD
175 struct locarhead newhead;
176 size_t total;
177 void *p;
178 unsigned int cnt;
179 struct namehashent *oldnamehashtab;
180 struct locrecent *oldlocrectab;
181 struct locarhandle new_ah;
531bafd8
UD
182 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
183 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
184 char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
185
186 if (output_prefix)
187 memcpy (archivefname, output_prefix, prefix_len);
188 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
189 strcpy (stpcpy (fname, archivefname), ".XXXXXX");
a7b65cdc
UD
190
191 /* Not all of the old file has to be mapped. Change this now this
192 we will have to access the whole content. */
193 if (fstat64 (ah->fd, &st) != 0
194 || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
195 MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
196 error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
197 ah->len = st.st_size;
198
199 /* Create a temporary file in the correct directory. */
200 fd = mkstemp (fname);
201 if (fd == -1)
202 error (EXIT_FAILURE, errno, _("cannot create temporary file"));
203
204 /* Copy the existing head information. */
205 newhead = *head;
206
207 /* Create the new archive header. The sizes of the various tables
208 should be double from what is currently used. */
209 newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
210 newhead.namehash_size);
211 printf ("name: size: %u, used: %d, new: size: %u\n",
212 head->namehash_size, head->namehash_used, newhead.namehash_size);
213
214 newhead.string_offset = (newhead.namehash_offset
215 + (newhead.namehash_size
216 * sizeof (struct namehashent)));
217 newhead.string_size = MAX (2 * newhead.string_used, newhead.string_size);
218
219 newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
220 newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
221 newhead.locrectab_size);
222
223 newhead.sumhash_offset = (newhead.locrectab_offset
224 + (newhead.locrectab_size
225 * sizeof (struct locrecent)));
226 newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
227 newhead.sumhash_size);
228
229 total = (newhead.sumhash_offset
230 + newhead.sumhash_size * sizeof (struct sumhashent));
231
232 /* The new file is empty now. */
233 newhead.namehash_used = 0;
234 newhead.string_used = 0;
235 newhead.locrectab_used = 0;
236 newhead.sumhash_used = 0;
237
238 /* Write out the header and create room for the other data structures. */
239 if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
240 != sizeof (newhead))
241 {
242 int errval = errno;
243 unlink (fname);
244 error (EXIT_FAILURE, errval, _("cannot initialize archive file"));
245 }
246
247 if (ftruncate64 (fd, total) != 0)
248 {
249 int errval = errno;
250 unlink (fname);
251 error (EXIT_FAILURE, errval, _("cannot resize archive file"));
252 }
253
254 /* Map the header and all the administration data structures. */
255 p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
256 if (p == MAP_FAILED)
257 {
258 int errval = errno;
259 unlink (fname);
260 error (EXIT_FAILURE, errval, _("cannot map archive header"));
261 }
262
263 /* Lock the new file. */
264 if (lockf64 (fd, F_LOCK, total) != 0)
265 {
266 int errval = errno;
267 unlink (fname);
268 error (EXIT_FAILURE, errval, _("cannot lock new archive"));
269 }
270
271 new_ah.len = total;
272 new_ah.addr = p;
273 new_ah.fd = fd;
274
275 /* Walk through the hash name hash table to find out what data is
276 still referenced and transfer it into the new file. */
277 oldnamehashtab = (struct namehashent *) ((char *) ah->addr
278 + head->namehash_offset);
279 oldlocrectab = (struct locrecent *) ((char *) ah->addr
280 + head->locrectab_offset);
281 for (cnt = 0; cnt < head->namehash_size; ++cnt)
282 if (oldnamehashtab[cnt].locrec_offset != 0)
283 {
284 /* Insert this entry in the new hash table. */
285 locale_data_t old_data;
286 unsigned int idx;
287 struct locrecent *oldlocrec;
288
289 oldlocrec = (struct locrecent *) ((char *) ah->addr
290 + oldnamehashtab[cnt].locrec_offset);
291
292 for (idx = 0; idx < __LC_LAST; ++idx)
293 if (idx != LC_ALL)
294 {
295 old_data[idx].size = oldlocrec->record[idx].len;
296 old_data[idx].addr
297 = ((char *) ah->addr + oldlocrec->record[idx].offset);
298
299 __md5_buffer (old_data[idx].addr, old_data[idx].size,
300 old_data[idx].sum);
301 }
302
303 if (add_locale_to_archive (&new_ah,
304 ((char *) ah->addr
305 + oldnamehashtab[cnt].name_offset),
306 old_data, 0) != 0)
307 error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
308 }
309
310
311 /* Make the file globally readable. */
312 if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
313 {
314 int errval = errno;
315 unlink (fname);
316 error (EXIT_FAILURE, errval,
317 _("cannot change mode of resized locale archive"));
318 }
319
320 /* Rename the new file. */
321 if (rename (fname, archivefname) != 0)
322 {
323 int errval = errno;
324 unlink (fname);
325 error (EXIT_FAILURE, errval, _("cannot rename new archive"));
326 }
327
328 /* Close the old file. */
329 close_archive (ah);
330
331 /* Add the information for the new one. */
332 *ah = new_ah;
333}
334
335
336void
b2bffca2 337open_archive (struct locarhandle *ah, bool readonly)
a7b65cdc
UD
338{
339 struct stat64 st;
340 struct stat64 st2;
341 int fd;
342 struct locarhead head;
343 int retry = 0;
531bafd8
UD
344 size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
345 char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
346
347 if (output_prefix)
348 memcpy (archivefname, output_prefix, prefix_len);
349 strcpy (archivefname + prefix_len, ARCHIVE_NAME);
a7b65cdc
UD
350
351 again:
352 /* Open the archive. We must have exclusive write access. */
b2bffca2 353 fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
a7b65cdc
UD
354 if (fd == -1)
355 {
356 /* Maybe the file does not yet exist. */
357 if (errno == ENOENT)
358 {
b2bffca2
UD
359 if (readonly)
360 {
361 static const struct locarhead nullhead =
362 {
363 .namehash_used = 0,
364 .namehash_offset = 0,
365 .namehash_size = 0
366 };
367
368 ah->addr = (void *) &nullhead;
369 ah->fd = -1;
370 }
371 else
372 create_archive (archivefname, ah);
373
a7b65cdc
UD
374 return;
375 }
376 else
377 error (EXIT_FAILURE, errno, _("cannot open locale archive \"%s\""),
378 archivefname);
379 }
380
381 if (fstat64 (fd, &st) < 0)
382 error (EXIT_FAILURE, errno, _("cannot stat locale archive \"%s\""),
383 archivefname);
384
b2bffca2 385 if (!readonly && lockf64 (fd, F_LOCK, sizeof (struct locarhead)) == -1)
a7b65cdc
UD
386 {
387 close (fd);
388
389 if (retry++ < max_locarchive_open_retry)
390 {
391 struct timespec req;
392
393 /* Wait for a bit. */
394 req.tv_sec = 0;
395 req.tv_nsec = 1000000 * (random () % 500 + 1);
396 (void) nanosleep (&req, NULL);
397
398 goto again;
399 }
400
401 error (EXIT_FAILURE, errno, _("cannot lock locale archive \"%s\""),
402 archivefname);
403 }
404
405 /* One more check. Maybe another process replaced the archive file
406 with a new, larger one since we opened the file. */
407 if (stat64 (archivefname, &st2) == -1
408 || st.st_dev != st2.st_dev
409 || st.st_ino != st2.st_ino)
410 {
b2bffca2 411 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
a7b65cdc
UD
412 close (fd);
413 goto again;
414 }
415
416 /* Read the header. */
417 if (TEMP_FAILURE_RETRY (read (fd, &head, sizeof (head))) != sizeof (head))
b2bffca2
UD
418 {
419 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
420 error (EXIT_FAILURE, errno, _("cannot read archive header"));
421 }
a7b65cdc
UD
422
423 ah->fd = fd;
424 ah->len = (head.sumhash_offset
425 + head.sumhash_size * sizeof (struct sumhashent));
426
427 /* Now we know how large the administrative information part is.
428 Map all of it. */
b2bffca2
UD
429 ah->addr = mmap64 (NULL, ah->len, PROT_READ | (readonly ? 0 : PROT_WRITE),
430 MAP_SHARED, fd, 0);
a7b65cdc 431 if (ah->addr == MAP_FAILED)
b2bffca2
UD
432 {
433 (void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
434 error (EXIT_FAILURE, errno, _("cannot map archive header"));
435 }
a7b65cdc
UD
436}
437
438
439void
440close_archive (struct locarhandle *ah)
441{
b2bffca2
UD
442 if (ah->fd != -1)
443 {
444 munmap (ah->addr, ah->len);
445 close (ah->fd);
446 }
a7b65cdc
UD
447}
448
449
450/* Check the content of the archive for duplicates. Add the content
451 of the files if necessary. Add all the names, possibly overwriting
452 old files. */
453int
454add_locale_to_archive (ah, name, data, replace)
455 struct locarhandle *ah;
456 const char *name;
457 locale_data_t data;
458 bool replace;
459{
460 /* First look for the name. If it already exists and we are not
461 supposed to replace it don't do anything. If it does not exist
462 we have to allocate a new locale record. */
463 size_t name_len = strlen (name);
464 uint32_t file_offsets[__LC_LAST];
465 unsigned int num_new_offsets = 0;
466 struct sumhashent *sumhashtab;
467 uint32_t hval;
468 unsigned int cnt;
469 unsigned int idx;
470 unsigned int insert_idx;
471 struct locarhead *head;
472 struct namehashent *namehashtab;
473 struct namehashent *namehashent;
474 unsigned int incr;
475 struct locrecent *locrecent;
476
477 head = ah->addr;
478 sumhashtab = (struct sumhashent *) ((char *) ah->addr
479 + head->sumhash_offset);
480 namehashtab = (struct namehashent *) ((char *) ah->addr
481 + head->namehash_offset);
482
483
484 /* For each locale category data set determine whether the same data
485 is already somewhere in the archive. */
486 for (cnt = 0; cnt < __LC_LAST; ++cnt)
487 if (cnt != LC_ALL)
488 {
489 /* By default signal that we have no data. */
490 file_offsets[cnt] = 0;
491 ++num_new_offsets;
492
493 /* Compute the hash value of the checksum to determine a
494 starting point for the search in the MD5 hash value
495 table. */
496 hval = compute_hashval (data[cnt].sum, 16);
497
498 idx = hval % head->sumhash_size;
499 incr = 1 + hval % (head->sumhash_size - 2);
500
501 while (sumhashtab[idx].file_offset != 0)
502 {
503 if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
504 {
505 /* Found it. */
506 file_offsets[cnt] = sumhashtab[idx].file_offset;
507 --num_new_offsets;
508 break;
509 }
510
511 idx += incr;
512 if (idx >= head->sumhash_size)
513 idx -= head->sumhash_size;
514 }
515 }
516
517
518 /* Hash value of the locale name. */
519 hval = compute_hashval (name, name_len);
520
521 insert_idx = -1;
522 idx = hval % head->namehash_size;
523 incr = 1 + hval % (head->namehash_size - 2);
524
525 /* If the name_offset field is zero this means this is no
526 deleted entry and therefore no entry can be found. */
527 while (namehashtab[idx].name_offset != 0)
528 {
529 if (namehashtab[idx].hashval == hval
530 && strcmp (name,
531 (char *) ah->addr + namehashtab[idx].name_offset) == 0)
532 {
533 /* Found the entry. */
405550bf 534 if (namehashtab[idx].locrec_offset != 0 && ! replace)
a7b65cdc
UD
535 {
536 if (! be_quiet)
537 error (0, 0, _("locale '%s' already exists"), name);
538 return 1;
539 }
540
541 break;
542 }
543
544 /* Remember the first place we can insert the new entry. */
545 if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
546 insert_idx = idx;
547
548 idx += incr;
549 if (idx >= head->namehash_size)
550 idx -= head->namehash_size;
551 }
552
553 /* Add as early as possible. */
554 if (insert_idx != -1)
555 idx = insert_idx;
556
557 namehashent = &namehashtab[idx];
558
559 /* Determine whether we have to resize the file. */
560 if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
561 || (namehashent->locrec_offset == 0
562 && (head->locrectab_used == head->locrectab_size
563 || head->string_used + name_len + 1 > head->string_size
564 || 100 * head->namehash_used > 75 * head->namehash_size)))
565 {
566 /* The current archive is not large enough. */
567 enlarge_archive (ah, head);
568 return add_locale_to_archive (ah, name, data, replace);
569 }
570
571 /* Add the locale data which is not yet in the archive. */
572 for (cnt = 0; cnt < __LC_LAST; ++cnt)
573 if (cnt != LC_ALL && file_offsets[cnt] == 0)
574 {
575 /* The data for this section is not yet available in the
576 archive. Append it. */
577 off64_t lastpos;
578 uint32_t md5hval;
579
580 lastpos = lseek64 (ah->fd, 0, SEEK_END);
581 if (lastpos == (off64_t) -1)
582 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
583
584 /* Align all data to a 16 byte boundary. */
585 if ((lastpos & 15) != 0)
586 {
587 static const char zeros[15] = { 0, };
588
589 if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, 16 - (lastpos & 15)))
590 != 16 - (lastpos & 15))
591 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
592
593 lastpos += 16 - (lastpos & 15);
594 }
595
596 /* Remember the position. */
597 file_offsets[cnt] = lastpos;
598
599 /* Write the data. */
600 if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
601 != data[cnt].size)
602 error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
603
604 /* Add the hash value to the hash table. */
605 md5hval = compute_hashval (data[cnt].sum, 16);
606
607 idx = md5hval % head->sumhash_size;
608 incr = 1 + md5hval % (head->sumhash_size - 2);
609
610 while (sumhashtab[idx].file_offset != 0)
611 {
612 idx += incr;
613 if (idx >= head->sumhash_size)
614 idx -= head->sumhash_size;
615 }
616
617 memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
618 sumhashtab[idx].file_offset = file_offsets[cnt];
619
620 ++head->sumhash_used;
621 }
622
623
624 if (namehashent->locrec_offset == 0)
625 {
626 /* Add the name string. */
627 memcpy ((char *) ah->addr + head->string_offset + head->string_used,
628 name, name_len + 1);
629 namehashent->name_offset = head->string_offset + head->string_used;
630 head->string_used += name_len + 1;
631
632 /* Allocate a name location record. */
633 namehashent->locrec_offset = (head->locrectab_offset
634 + (head->locrectab_used++
635 * sizeof (struct locrecent)));
636
637 namehashent->hashval = hval;
638
639 ++head->namehash_used;
640 }
641
642
643 /* Fill in the table with the locations of the locale data. */
644 locrecent = (struct locrecent *) ((char *) ah->addr
645 + namehashent->locrec_offset);
646 for (cnt = 0; cnt < __LC_LAST; ++cnt)
647 if (cnt != LC_ALL)
648 {
649 locrecent->record[cnt].offset = file_offsets[cnt];
650 locrecent->record[cnt].len = data[cnt].size;
651 }
652
653
654 /* Read the locale.alias file to see whether any matching record is
655 found. If an entry is available check whether it is already in
656 the archive. If this is the case check whether the new locale's
657 name is more specific than the one currently referred to by the
658 alias. */
659
660
661 return 0;
662}
663
664
665int
666add_locales_to_archive (nlist, list, replace)
667 size_t nlist;
668 char *list[];
669 bool replace;
670{
671 struct locarhandle ah;
672 int result = 0;
673
674 /* Open the archive. This call never returns if we cannot
675 successfully open the archive. */
b2bffca2 676 open_archive (&ah, false);
a7b65cdc
UD
677
678 while (nlist-- > 0)
679 {
680 const char *fname = *list++;
681 size_t fnamelen = strlen (fname);
682 struct stat64 st;
683 DIR *dirp;
684 struct dirent64 *d;
685 int seen;
686 locale_data_t data;
687 int cnt;
688
689 if (! be_quiet)
690 printf (_("Adding %s\n"), fname);
691
692 /* First see whether this really is a directory and whether it
693 contains all the require locale category files. */
694 if (stat64 (fname, &st) < 0)
695 {
696 error (0, 0, _("stat of \"%s\" failed: %s: ignored"), fname,
697 strerror (errno));
698 continue;
699 }
700 if (!S_ISDIR (st.st_mode))
701 {
702 error (0, 0, _("\"%s\" is no directory; ignored"), fname);
703 continue;
704 }
705
706 dirp = opendir (fname);
707 if (dirp == NULL)
708 {
709 error (0, 0, _("cannot open directory \"%s\": %s: ignored"),
710 fname, strerror (errno));
711 continue;
712 }
713
714 seen = 0;
715 while ((d = readdir64 (dirp)) != NULL)
716 {
717 for (cnt = 0; cnt < __LC_LAST; ++cnt)
718 if (cnt != LC_ALL)
719 if (strcmp (d->d_name, locnames[cnt]) == 0)
720 {
721 unsigned char d_type;
722
723 /* We have an object of the required name. If it's
724 a directory we have to look at a file with the
725 prefix "SYS_". Otherwise we have found what we
726 are looking for. */
727#ifdef _DIRENT_HAVE_D_TYPE
728 d_type = d->d_type;
729
730 if (d_type != DT_REG)
731#endif
732 {
733 char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
734
735#ifdef _DIRENT_HAVE_D_TYPE
736 if (d_type == DT_UNKNOWN)
737#endif
738 {
739 strcpy (stpcpy (stpcpy (fullname, fname), "/"),
740 d->d_name);
741
742 if (stat64 (fullname, &st) == -1)
743 /* We cannot stat the file, ignore it. */
744 break;
745
746 d_type = IFTODT (st.st_mode);
747 }
748
749 if (d_type == DT_DIR)
750 {
751 /* We have to do more tests. The file is a
752 directory and it therefore must contain a
753 regular file with the same name except a
754 "SYS_" prefix. */
531bafd8
UD
755 char *t = stpcpy (stpcpy (fullname, fname), "/");
756 strcpy (stpcpy (stpcpy (t, d->d_name), "/SYS_"),
a7b65cdc
UD
757 d->d_name);
758
759 if (stat64 (fullname, &st) == -1)
760 /* There is no SYS_* file or we cannot
761 access it. */
762 break;
763
764 d_type = IFTODT (st.st_mode);
765 }
766 }
767
768 /* If we found a regular file (eventually after
769 following a symlink) we are successful. */
770 if (d_type == DT_REG)
771 ++seen;
772 break;
773 }
774 }
775
776 closedir (dirp);
777
778 if (seen != __LC_LAST - 1)
779 {
780 /* We don't have all locale category files. Ignore the name. */
781 error (0, 0, _("incomplete set of locale files in \"%s\""),
782 fname);
783 continue;
784 }
785
786 /* Add the files to the archive. To do this we first compute
787 sizes and the MD5 sums of all the files. */
788 for (cnt = 0; cnt < __LC_LAST; ++cnt)
789 if (cnt != LC_ALL)
790 {
791 char fullname[fnamelen + 2 * strlen (locnames[cnt]) + 7];
792 int fd;
793
794 strcpy (stpcpy (stpcpy (fullname, fname), "/"), locnames[cnt]);
795 fd = open64 (fullname, O_RDONLY);
796 if (fd == -1 || fstat64 (fd, &st) == -1)
797 {
798 /* Cannot read the file. */
799 if (fd != -1)
800 close (fd);
801 break;
802 }
803
804 if (S_ISDIR (st.st_mode))
805 {
531bafd8 806 char *t;
a7b65cdc 807 close (fd);
531bafd8
UD
808 t = stpcpy (stpcpy (fullname, fname), "/");
809 strcpy (stpcpy (stpcpy (t, locnames[cnt]), "/SYS_"),
a7b65cdc
UD
810 locnames[cnt]);
811
812 fd = open64 (fullname, O_RDONLY);
813 if (fd == -1 || fstat64 (fd, &st) == -1
814 || !S_ISREG (st.st_mode))
815 {
816 if (fd != -1)
817 close (fd);
818 break;
819 }
820 }
821
822 /* Map the file. */
823 data[cnt].addr = mmap64 (NULL, st.st_size, PROT_READ, MAP_SHARED,
824 fd, 0);
825 if (data[cnt].addr == MAP_FAILED)
826 {
827 /* Cannot map it. */
828 close (fd);
829 break;
830 }
831
832 data[cnt].size = st.st_size;
833 __md5_buffer (data[cnt].addr, st.st_size, data[cnt].sum);
834
835 /* We don't need the file descriptor anymore. */
836 close (fd);
837 }
838
839 if (cnt != __LC_LAST)
840 {
841 while (cnt-- > 0)
842 if (cnt != LC_ALL)
843 munmap (data[cnt].addr, data[cnt].size);
844
845 error (0, 0, _("cannot read all files in \"%s\": ignored"), fname);
846
847 continue;
848 }
849
850 result |= add_locale_to_archive (&ah, basename (fname), data, replace);
851
852 for (cnt = 0; cnt < __LC_LAST; ++cnt)
853 if (cnt != LC_ALL)
854 munmap (data[cnt].addr, data[cnt].size);
855 }
856
857 /* We are done. */
858 close_archive (&ah);
859
860 return result;
861}
862
863
864int
865delete_locales_from_archive (nlist, list)
866 size_t nlist;
867 char *list[];
868{
869 struct locarhandle ah;
870 struct locarhead *head;
871 struct namehashent *namehashtab;
872
873 /* Open the archive. This call never returns if we cannot
874 successfully open the archive. */
b2bffca2 875 open_archive (&ah, false);
a7b65cdc
UD
876
877 head = ah.addr;
878 namehashtab = (struct namehashent *) ((char *) ah.addr
879 + head->namehash_offset);
880
881 while (nlist-- > 0)
882 {
883 const char *locname = *list++;
884 uint32_t hval;
885 unsigned int idx;
886 unsigned int incr;
887
888 /* Search for this locale in the archive. */
889 hval = compute_hashval (locname, strlen (locname));
890
891 idx = hval % head->namehash_size;
892 incr = 1 + hval % (head->namehash_size - 2);
893
894 /* If the name_offset field is zero this means this is no
895 deleted entry and therefore no entry can be found. */
896 while (namehashtab[idx].name_offset != 0)
897 {
898 if (namehashtab[idx].hashval == hval
899 && (strcmp (locname,
900 (char *) ah.addr + namehashtab[idx].name_offset)
901 == 0))
902 {
903 /* Found the entry. Now mark it as removed by zero-ing
904 the reference to the locale record. */
905 namehashtab[idx].locrec_offset = 0;
906 --head->namehash_used;
907 break;
908 }
909
910 idx += incr;
911 if (idx >= head->namehash_size)
912 idx -= head->namehash_size;
913 }
914
915 if (namehashtab[idx].name_offset == 0 && ! be_quiet)
916 error (0, 0, _("locale \"%s\" not in archive"), locname);
917 }
918
919 close_archive (&ah);
920
921 return 0;
922}
923
924
b2bffca2
UD
925struct nameent
926{
927 char *name;
928 uint32_t locrec_offset;
929};
930
931
932struct dataent
933{
934 const unsigned char *sum;
935 uint32_t file_offset;
936 uint32_t nlink;
937};
938
939
940static int
941nameentcmp (const void *a, const void *b)
942{
943 return strcmp (((const struct nameent *) a)->name,
944 ((const struct nameent *) b)->name);
945}
946
947
a7b65cdc 948static int
b2bffca2 949dataentcmp (const void *a, const void *b)
a7b65cdc 950{
b2bffca2
UD
951 if (((const struct dataent *) a)->file_offset
952 < ((const struct dataent *) b)->file_offset)
953 return -1;
954
955 if (((const struct dataent *) a)->file_offset
956 > ((const struct dataent *) b)->file_offset)
957 return 1;
958
959 return 0;
a7b65cdc
UD
960}
961
962
963void
b2bffca2 964show_archive_content (int verbose)
a7b65cdc
UD
965{
966 struct locarhandle ah;
967 struct locarhead *head;
968 struct namehashent *namehashtab;
b2bffca2 969 struct nameent *names;
a7b65cdc 970 int cnt;
a7b65cdc
UD
971 int used;
972
973 /* Open the archive. This call never returns if we cannot
974 successfully open the archive. */
b2bffca2 975 open_archive (&ah, true);
a7b65cdc
UD
976
977 head = ah.addr;
978
b2bffca2
UD
979 names = (struct nameent *) xmalloc (head->namehash_used
980 * sizeof (struct nameent));
a7b65cdc
UD
981
982 namehashtab = (struct namehashent *) ((char *) ah.addr
983 + head->namehash_offset);
984 for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
985 if (namehashtab[cnt].locrec_offset != 0)
986 {
987 assert (used < head->namehash_used);
b2bffca2
UD
988 names[used].name = ah.addr + namehashtab[cnt].name_offset;
989 names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
a7b65cdc
UD
990 }
991
992 /* Sort the names. */
b2bffca2
UD
993 qsort (names, used, sizeof (struct nameent), nameentcmp);
994
995 if (verbose)
996 {
997 struct dataent *files;
998 struct sumhashent *sumhashtab;
999 int sumused;
1000
1001 files = (struct dataent *) xmalloc (head->sumhash_used
1002 * sizeof (struct sumhashent));
1003
1004 sumhashtab = (struct sumhashent *) ((char *) ah.addr
1005 + head->sumhash_offset);
1006 for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
1007 if (sumhashtab[cnt].file_offset != 0)
1008 {
1009 assert (sumused < head->sumhash_used);
1010 files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
1011 files[sumused].file_offset = sumhashtab[cnt].file_offset;
1012 files[sumused++].nlink = 0;
1013 }
1014
1015 /* Sort by file locations. */
1016 qsort (files, sumused, sizeof (struct dataent), dataentcmp);
1017
1018 /* Compute nlink fields. */
1019 for (cnt = 0; cnt < used; ++cnt)
1020 {
1021 struct locrecent *locrec;
1022 int idx;
1023
1024 locrec = (struct locrecent *) ((char *) ah.addr
1025 + names[cnt].locrec_offset);
1026 for (idx = 0; idx < __LC_LAST; ++idx)
1027 if (idx != LC_ALL)
1028 {
1029 struct dataent *data, dataent;
1030
1031 dataent.file_offset = locrec->record[idx].offset;
1032 data = (struct dataent *) bsearch (&dataent, files, sumused,
1033 sizeof (struct dataent),
1034 dataentcmp);
1035 assert (data != NULL);
1036 ++data->nlink;
1037 }
1038 }
a7b65cdc 1039
b2bffca2
UD
1040 /* Print it. */
1041 for (cnt = 0; cnt < used; ++cnt)
1042 {
1043 struct locrecent *locrec;
1044 int idx, i;
1045
1046 locrec = (struct locrecent *) ((char *) ah.addr
1047 + names[cnt].locrec_offset);
1048 for (idx = 0; idx < __LC_LAST; ++idx)
1049 if (idx != LC_ALL)
1050 {
1051 struct dataent *data, dataent;
1052
1053 dataent.file_offset = locrec->record[idx].offset;
1054 data = (struct dataent *) bsearch (&dataent, files, sumused,
1055 sizeof (struct dataent),
1056 dataentcmp);
1057 printf ("%6d %7x %3d ",
1058 locrec->record[idx].len, locrec->record[idx].offset,
1059 data->nlink);
1060 for (i = 0; i < 16; i += 4)
1061 printf ("%02x%02x%02x%02x",
1062 data->sum[i], data->sum[i + 1],
1063 data->sum[i + 2], data->sum[i + 3]);
1064 printf (" %s/%s\n", names[cnt].name,
1065 idx == LC_MESSAGES ? "LC_MESSAGES/SYS_LC_MESSAGES"
1066 : locnames[idx]);
1067 }
1068 }
1069 }
1070 else
1071 for (cnt = 0; cnt < used; ++cnt)
1072 puts (names[cnt].name);
a7b65cdc
UD
1073
1074 close_archive (&ah);
1075
1076 exit (EXIT_SUCCESS);
1077}