-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; version 2 of the License, or
+ (at your option) any later version.
- The GNU C Library is distributed in the hope that it will be useful,
+ This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <stdint.h>
#include <sys/mman.h>
#include <sys/param.h>
+#include <sys/shm.h>
#include <sys/stat.h>
+#include <libc-mmap.h>
+#include <libc-pointer-arith.h>
#include "../../crypt/md5.h"
#include "../localeinfo.h"
#include "../locarchive.h"
#include "localedef.h"
+#include "locfile.h"
/* Define the hash function. We define the function as static inline.
We must change the name so as not to conflict with simple-hash.h. */
-#define compute_hashval static inline archive_hashval
+#define compute_hashval static archive_hashval
#define hashval_t uint32_t
#include "hashval.h"
#undef compute_hashval
extern const char *output_prefix;
-#define ARCHIVE_NAME LOCALEDIR "/locale-archive"
+#define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
static const char *locnames[] =
{
/* Size of the initial archive header. */
-#define INITIAL_NUM_NAMES 450
-#define INITIAL_SIZE_STRINGS 3500
-#define INITIAL_NUM_LOCREC 350
+#define INITIAL_NUM_NAMES 900
+#define INITIAL_SIZE_STRINGS 7500
+#define INITIAL_NUM_LOCREC 420
#define INITIAL_NUM_SUMS 2000
+/* Get and set values (possibly endian-swapped) in structures mapped
+ from or written directly to locale archives. */
+#define GET(FIELD) maybe_swap_uint32 (FIELD)
+#define SET(FIELD, VALUE) ((FIELD) = maybe_swap_uint32 (VALUE))
+#define INC(FIELD, INCREMENT) SET (FIELD, GET (FIELD) + (INCREMENT))
+
+
+/* Size of the reserved address space area. */
+#define RESERVE_MMAP_SIZE 512 * 1024 * 1024
+
+/* To prepare for enlargements of the mmaped area reserve some address
+ space. On some machines, being a file mapping rather than an anonymous
+ mapping affects the address selection. So do this mapping from the
+ actual file, even though it's only a dummy to reserve address space. */
+static void *
+prepare_address_space (int fd, size_t total, size_t *reserved, int *xflags,
+ void **mmap_base, size_t *mmap_len)
+{
+ if (total < RESERVE_MMAP_SIZE)
+ {
+ void *p = mmap64 (NULL, RESERVE_MMAP_SIZE, PROT_NONE, MAP_SHARED, fd, 0);
+ if (p != MAP_FAILED)
+ {
+ void *aligned_p = PTR_ALIGN_UP (p, MAP_FIXED_ALIGNMENT);
+ size_t align_adjust = aligned_p - p;
+ *mmap_base = p;
+ *mmap_len = RESERVE_MMAP_SIZE;
+ assert (align_adjust < RESERVE_MMAP_SIZE);
+ *reserved = RESERVE_MMAP_SIZE - align_adjust;
+ *xflags = MAP_FIXED;
+ return aligned_p;
+ }
+ }
+
+ *reserved = total;
+ *xflags = 0;
+ *mmap_base = NULL;
+ *mmap_len = 0;
+ return NULL;
+}
+
+
static void
create_archive (const char *archivefname, struct locarhandle *ah)
{
int fd;
char fname[strlen (archivefname) + sizeof (".XXXXXX")];
struct locarhead head;
- void *p;
size_t total;
strcpy (stpcpy (fname, archivefname), ".XXXXXX");
/* Create a temporary file in the correct directory. */
fd = mkstemp (fname);
if (fd == -1)
- error (EXIT_FAILURE, errno, _("cannot create temporary file"));
+ error (EXIT_FAILURE, errno, _("cannot create temporary file: %s"), fname);
/* Create the initial content of the archive. */
- head.magic = AR_MAGIC;
- head.namehash_offset = sizeof (struct locarhead);
- head.namehash_used = 0;
- head.namehash_size = next_prime (INITIAL_NUM_NAMES);
-
- head.string_offset = (head.namehash_offset
- + head.namehash_size * sizeof (struct namehashent));
- head.string_used = 0;
- head.string_size = INITIAL_SIZE_STRINGS;
-
- head.locrectab_offset = head.string_offset + head.string_size;
- head.locrectab_used = 0;
- head.locrectab_size = INITIAL_NUM_LOCREC;
-
- head.sumhash_offset = (head.locrectab_offset
- + head.locrectab_size * sizeof (struct locrecent));
- head.sumhash_used = 0;
- head.sumhash_size = next_prime (INITIAL_NUM_SUMS);
-
- total = head.sumhash_offset + head.sumhash_size * sizeof (struct sumhashent);
+ SET (head.magic, AR_MAGIC);
+ SET (head.serial, 0);
+ SET (head.namehash_offset, sizeof (struct locarhead));
+ SET (head.namehash_used, 0);
+ SET (head.namehash_size, next_prime (INITIAL_NUM_NAMES));
+
+ SET (head.string_offset,
+ (GET (head.namehash_offset)
+ + GET (head.namehash_size) * sizeof (struct namehashent)));
+ SET (head.string_used, 0);
+ SET (head.string_size, INITIAL_SIZE_STRINGS);
+
+ SET (head.locrectab_offset,
+ GET (head.string_offset) + GET (head.string_size));
+ SET (head.locrectab_used, 0);
+ SET (head.locrectab_size, INITIAL_NUM_LOCREC);
+
+ SET (head.sumhash_offset,
+ (GET (head.locrectab_offset)
+ + GET (head.locrectab_size) * sizeof (struct locrecent)));
+ SET (head.sumhash_used, 0);
+ SET (head.sumhash_size, next_prime (INITIAL_NUM_SUMS));
+
+ total = (GET (head.sumhash_offset)
+ + GET (head.sumhash_size) * sizeof (struct sumhashent));
/* Write out the header and create room for the other data structures. */
if (TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head))) != sizeof (head))
error (EXIT_FAILURE, errval, _("cannot resize archive file"));
}
+ size_t reserved, mmap_len;
+ int xflags;
+ void *mmap_base;
+ void *p = prepare_address_space (fd, total, &reserved, &xflags, &mmap_base,
+ &mmap_len);
+
/* Map the header and all the administration data structures. */
- p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0);
if (p == MAP_FAILED)
{
int errval = errno;
_("cannot change mode of new locale archive"));
}
+ ah->fname = NULL;
ah->fd = fd;
+ ah->mmap_base = mmap_base;
+ ah->mmap_len = mmap_len;
ah->addr = p;
- ah->len = total;
+ ah->mmaped = total;
+ ah->reserved = reserved;
}
for (cnt = 0; cnt < __LC_LAST; ++cnt)
if (cnt != LC_ALL)
{
- if (la->record[cnt].offset < start_a)
- start_a = la->record[cnt].offset;
- if (la->record[cnt].offset + la->record[cnt].len > end_a)
- end_a = la->record[cnt].offset + la->record[cnt].len;
+ if (GET (la->record[cnt].offset) < start_a)
+ start_a = GET (la->record[cnt].offset);
+ if (GET (la->record[cnt].offset) + GET (la->record[cnt].len) > end_a)
+ end_a = GET (la->record[cnt].offset) + GET (la->record[cnt].len);
}
assert (start_a != (uint32_t)-1);
assert (end_a != 0);
for (cnt = 0; cnt < __LC_LAST; ++cnt)
if (cnt != LC_ALL)
{
- if (lb->record[cnt].offset < start_b)
- start_b = lb->record[cnt].offset;
- if (lb->record[cnt].offset + lb->record[cnt].len > end_b)
- end_b = lb->record[cnt].offset + lb->record[cnt].len;
+ if (GET (lb->record[cnt].offset) < start_b)
+ start_b = GET (lb->record[cnt].offset);
+ if (GET (lb->record[cnt].offset) + GET (lb->record[cnt].len) > end_b)
+ end_b = GET (lb->record[cnt].offset) + GET (lb->record[cnt].len);
}
assert (start_b != (uint32_t)-1);
assert (end_b != 0);
}
-/* forward decl for below */
+/* forward decls for below */
static uint32_t add_locale (struct locarhandle *ah, const char *name,
locale_data_t data, bool replace);
+static void add_alias (struct locarhandle *ah, const char *alias,
+ bool replace, const char *oldname,
+ uint32_t *locrec_offset_p);
+
+
+static bool
+file_data_available_p (struct locarhandle *ah, uint32_t offset, uint32_t size)
+{
+ if (offset < ah->mmaped && offset + size <= ah->mmaped)
+ return true;
+
+ struct stat64 st;
+ if (fstat64 (ah->fd, &st) != 0)
+ return false;
+
+ if (st.st_size > ah->reserved)
+ return false;
+
+ size_t start = ALIGN_DOWN (ah->mmaped, MAP_FIXED_ALIGNMENT);
+ void *p = mmap64 (ah->addr + start, st.st_size - start,
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
+ ah->fd, start);
+ if (p == MAP_FAILED)
+ {
+ ah->mmaped = start;
+ return false;
+ }
+
+ ah->mmaped = st.st_size;
+ return true;
+}
+
+
+static int
+compare_from_file (struct locarhandle *ah, void *p1, uint32_t offset2,
+ uint32_t size)
+{
+ void *p2 = xmalloc (size);
+ if (pread (ah->fd, p2, size, offset2) != size)
+ record_error (4, errno,
+ _("cannot read data from locale archive"));
+
+ int res = memcmp (p1, p2, size);
+ free (p2);
+ return res;
+}
+
static void
enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
int fd;
struct locarhead newhead;
size_t total;
- void *p;
unsigned int cnt, loccnt;
struct namehashent *oldnamehashtab;
- struct locrecent *oldlocrectab;
struct locarhandle new_ah;
size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
char fname[prefix_len + sizeof (ARCHIVE_NAME) + sizeof (".XXXXXX") - 1];
- struct oldlocrecent *oldlocrecarray;
if (output_prefix)
memcpy (archivefname, output_prefix, prefix_len);
/* Not all of the old file has to be mapped. Change this now this
we will have to access the whole content. */
- if (fstat64 (ah->fd, &st) != 0
- || (ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, ah->fd, 0)) == MAP_FAILED)
+ if (fstat64 (ah->fd, &st) != 0)
+ enomap:
error (EXIT_FAILURE, errno, _("cannot map locale archive file"));
- ah->len = st.st_size;
+
+ if (st.st_size < ah->reserved)
+ ah->addr = mmap64 (ah->addr, st.st_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_FIXED, ah->fd, 0);
+ else
+ {
+ if (ah->mmap_base)
+ munmap (ah->mmap_base, ah->mmap_len);
+ else
+ munmap (ah->addr, ah->reserved);
+ ah->addr = mmap64 (NULL, st.st_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, ah->fd, 0);
+ ah->reserved = st.st_size;
+ ah->mmap_base = NULL;
+ ah->mmap_len = 0;
+ head = ah->addr;
+ }
+ if (ah->addr == MAP_FAILED)
+ goto enomap;
+ ah->mmaped = st.st_size;
/* Create a temporary file in the correct directory. */
fd = mkstemp (fname);
if (fd == -1)
- error (EXIT_FAILURE, errno, _("cannot create temporary file"));
+ error (EXIT_FAILURE, errno, _("cannot create temporary file: %s"), fname);
/* Copy the existing head information. */
newhead = *head;
/* Create the new archive header. The sizes of the various tables
should be double from what is currently used. */
- newhead.namehash_size = MAX (next_prime (2 * newhead.namehash_used),
- newhead.namehash_size);
- if (! be_quiet)
+ SET (newhead.namehash_size,
+ MAX (next_prime (2 * GET (newhead.namehash_used)),
+ GET (newhead.namehash_size)));
+ if (verbose)
printf ("name: size: %u, used: %d, new: size: %u\n",
- head->namehash_size, head->namehash_used, newhead.namehash_size);
+ GET (head->namehash_size),
+ GET (head->namehash_used), GET (newhead.namehash_size));
- newhead.string_offset = (newhead.namehash_offset
- + (newhead.namehash_size
- * sizeof (struct namehashent)));
+ SET (newhead.string_offset, (GET (newhead.namehash_offset)
+ + (GET (newhead.namehash_size)
+ * sizeof (struct namehashent))));
/* Keep the string table size aligned to 4 bytes, so that
all the struct { uint32_t } types following are happy. */
- newhead.string_size = MAX ((2 * newhead.string_used + 3) & -4,
- newhead.string_size);
+ SET (newhead.string_size, MAX ((2 * GET (newhead.string_used) + 3) & -4,
+ GET (newhead.string_size)));
- newhead.locrectab_offset = newhead.string_offset + newhead.string_size;
- newhead.locrectab_size = MAX (2 * newhead.locrectab_used,
- newhead.locrectab_size);
+ SET (newhead.locrectab_offset,
+ GET (newhead.string_offset) + GET (newhead.string_size));
+ SET (newhead.locrectab_size, MAX (2 * GET (newhead.locrectab_used),
+ GET (newhead.locrectab_size)));
- newhead.sumhash_offset = (newhead.locrectab_offset
- + (newhead.locrectab_size
- * sizeof (struct locrecent)));
- newhead.sumhash_size = MAX (next_prime (2 * newhead.sumhash_used),
- newhead.sumhash_size);
+ SET (newhead.sumhash_offset, (GET (newhead.locrectab_offset)
+ + (GET (newhead.locrectab_size)
+ * sizeof (struct locrecent))));
+ SET (newhead.sumhash_size,
+ MAX (next_prime (2 * GET (newhead.sumhash_used)),
+ GET (newhead.sumhash_size)));
- total = (newhead.sumhash_offset
- + newhead.sumhash_size * sizeof (struct sumhashent));
+ total = (GET (newhead.sumhash_offset)
+ + GET (newhead.sumhash_size) * sizeof (struct sumhashent));
/* The new file is empty now. */
- newhead.namehash_used = 0;
- newhead.string_used = 0;
- newhead.locrectab_used = 0;
- newhead.sumhash_used = 0;
+ SET (newhead.namehash_used, 0);
+ SET (newhead.string_used, 0);
+ SET (newhead.locrectab_used, 0);
+ SET (newhead.sumhash_used, 0);
/* Write out the header and create room for the other data structures. */
if (TEMP_FAILURE_RETRY (write (fd, &newhead, sizeof (newhead)))
error (EXIT_FAILURE, errval, _("cannot resize archive file"));
}
+ size_t reserved, mmap_len;
+ int xflags;
+ void *mmap_base;
+ void *p = prepare_address_space (fd, total, &reserved, &xflags, &mmap_base,
+ &mmap_len);
+
/* Map the header and all the administration data structures. */
- p = mmap64 (NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ p = mmap64 (p, total, PROT_READ | PROT_WRITE, MAP_SHARED | xflags, fd, 0);
if (p == MAP_FAILED)
{
int errval = errno;
error (EXIT_FAILURE, errval, _("cannot lock new archive"));
}
- new_ah.len = total;
+ new_ah.mmaped = total;
+ new_ah.mmap_base = mmap_base;
+ new_ah.mmap_len = mmap_len;
new_ah.addr = p;
new_ah.fd = fd;
+ new_ah.reserved = reserved;
/* Walk through the hash name hash table to find out what data is
still referenced and transfer it into the new file. */
oldnamehashtab = (struct namehashent *) ((char *) ah->addr
- + head->namehash_offset);
- oldlocrectab = (struct locrecent *) ((char *) ah->addr
- + head->locrectab_offset);
+ + GET (head->namehash_offset));
/* Sort the old locrec table in order of data position. */
- oldlocrecarray = (struct oldlocrecent *)
- alloca (head->namehash_size
- * sizeof (struct oldlocrecent));
- for (cnt = 0, loccnt = 0; cnt < head->namehash_size; ++cnt)
- if (oldnamehashtab[cnt].locrec_offset != 0)
+ struct oldlocrecent oldlocrecarray[GET (head->namehash_size)];
+ for (cnt = 0, loccnt = 0; cnt < GET (head->namehash_size); ++cnt)
+ if (GET (oldnamehashtab[cnt].locrec_offset) != 0)
{
oldlocrecarray[loccnt].cnt = cnt;
oldlocrecarray[loccnt++].locrec
= (struct locrecent *) ((char *) ah->addr
- + oldnamehashtab[cnt].locrec_offset);
+ + GET (oldnamehashtab[cnt].locrec_offset));
}
qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
oldlocrecentcmp);
+ uint32_t last_locrec_offset = 0;
for (cnt = 0; cnt < loccnt; ++cnt)
{
/* Insert this entry in the new hash table. */
for (idx = 0; idx < __LC_LAST; ++idx)
if (idx != LC_ALL)
{
- old_data[idx].size = oldlocrec->record[idx].len;
+ old_data[idx].size = GET (oldlocrec->record[idx].len);
old_data[idx].addr
- = ((char *) ah->addr + oldlocrec->record[idx].offset);
+ = ((char *) ah->addr + GET (oldlocrec->record[idx].offset));
__md5_buffer (old_data[idx].addr, old_data[idx].size,
old_data[idx].sum);
}
- if (add_locale (&new_ah,
- ((char *) ah->addr
- + oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset),
- old_data, 0) == 0)
+ if (cnt > 0 && oldlocrecarray[cnt - 1].locrec == oldlocrec)
+ {
+ const char *oldname
+ = ((char *) ah->addr
+ + GET (oldnamehashtab[oldlocrecarray[cnt
+ - 1].cnt].name_offset));
+
+ add_alias
+ (&new_ah,
+ ((char *) ah->addr
+ + GET (oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset)),
+ 0, oldname, &last_locrec_offset);
+ continue;
+ }
+
+ last_locrec_offset =
+ add_locale
+ (&new_ah,
+ ((char *) ah->addr
+ + GET (oldnamehashtab[oldlocrecarray[cnt].cnt].name_offset)),
+ old_data, 0);
+ if (last_locrec_offset == 0)
error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
}
struct locarhead head;
int retry = 0;
size_t prefix_len = output_prefix ? strlen (output_prefix) : 0;
- char archivefname[prefix_len + sizeof (ARCHIVE_NAME)];
+ char default_fname[prefix_len + sizeof (ARCHIVE_NAME)];
+ const char *archivefname = ah->fname;
- if (output_prefix)
- memcpy (archivefname, output_prefix, prefix_len);
- strcpy (archivefname + prefix_len, ARCHIVE_NAME);
+ /* If ah has a non-NULL fname open that otherwise open the default. */
+ if (archivefname == NULL)
+ {
+ archivefname = default_fname;
+ if (output_prefix)
+ memcpy (default_fname, output_prefix, prefix_len);
+ strcpy (default_fname + prefix_len, ARCHIVE_NAME);
+ }
while (1)
{
fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
if (fd == -1)
{
- /* Maybe the file does not yet exist. */
- if (errno == ENOENT)
+ /* Maybe the file does not yet exist? If we are opening
+ the default locale archive we ignore the failure and
+ list an empty archive, otherwise we print an error
+ and exit. */
+ if (errno == ENOENT && archivefname == default_fname)
{
if (readonly)
{
}
ah->fd = fd;
- ah->len = (head.sumhash_offset
- + head.sumhash_size * sizeof (struct sumhashent));
-
- /* Now we know how large the administrative information part is.
- Map all of it. */
- ah->addr = mmap64 (NULL, ah->len, PROT_READ | (readonly ? 0 : PROT_WRITE),
- MAP_SHARED, fd, 0);
+ ah->mmaped = st.st_size;
+
+ size_t reserved, mmap_len;
+ int xflags;
+ void *mmap_base;
+ void *p = prepare_address_space (fd, st.st_size, &reserved, &xflags,
+ &mmap_base, &mmap_len);
+
+ /* Map the entire file. We might need to compare the category data
+ in the file with the newly added data. */
+ ah->addr = mmap64 (p, st.st_size, PROT_READ | (readonly ? 0 : PROT_WRITE),
+ MAP_SHARED | xflags, fd, 0);
if (ah->addr == MAP_FAILED)
{
(void) lockf64 (fd, F_ULOCK, sizeof (struct locarhead));
error (EXIT_FAILURE, errno, _("cannot map archive header"));
}
+ ah->reserved = reserved;
+ ah->mmap_base = mmap_base;
+ ah->mmap_len = mmap_len;
}
{
if (ah->fd != -1)
{
- munmap (ah->addr, ah->len);
+ if (ah->mmap_base)
+ munmap (ah->mmap_base, ah->mmap_len);
+ else
+ munmap (ah->addr, ah->reserved);
close (ah->fd);
}
}
{
const struct locarhead *const head = ah->addr;
struct namehashent *namehashtab
- = (struct namehashent *) ((char *) ah->addr + head->namehash_offset);
+ = (struct namehashent *) ((char *) ah->addr
+ + GET (head->namehash_offset));
unsigned int insert_idx, idx, incr;
/* Hash value of the locale name. */
uint32_t hval = archive_hashval (name, name_len);
insert_idx = -1;
- idx = hval % head->namehash_size;
- incr = 1 + hval % (head->namehash_size - 2);
+ idx = hval % GET (head->namehash_size);
+ incr = 1 + hval % (GET (head->namehash_size) - 2);
/* If the name_offset field is zero this means this is a
deleted entry and therefore no entry can be found. */
- while (namehashtab[idx].name_offset != 0)
+ while (GET (namehashtab[idx].name_offset) != 0)
{
- if (namehashtab[idx].hashval == hval
- && strcmp (name,
- (char *) ah->addr + namehashtab[idx].name_offset) == 0)
+ if (GET (namehashtab[idx].hashval) == hval
+ && (strcmp (name,
+ (char *) ah->addr + GET (namehashtab[idx].name_offset))
+ == 0))
{
/* Found the entry. */
- if (namehashtab[idx].locrec_offset != 0 && ! replace)
+ if (GET (namehashtab[idx].locrec_offset) != 0 && ! replace)
{
if (! be_quiet)
error (0, 0, _("locale '%s' already exists"), name);
break;
}
- if (namehashtab[idx].hashval == hval && ! be_quiet)
+ if (GET (namehashtab[idx].hashval) == hval && ! be_quiet)
{
error (0, 0, "hash collision (%u) %s, %s",
- hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
+ hval, name,
+ (char *) ah->addr + GET (namehashtab[idx].name_offset));
}
/* Remember the first place we can insert the new entry. */
- if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
+ if (GET (namehashtab[idx].locrec_offset) == 0 && insert_idx == -1)
insert_idx = idx;
idx += incr;
- if (idx >= head->namehash_size)
- idx -= head->namehash_size;
+ if (idx >= GET (head->namehash_size))
+ idx -= GET (head->namehash_size);
}
/* Add as early as possible. */
if (insert_idx != -1)
idx = insert_idx;
- namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */
+ SET (namehashtab[idx].hashval, hval); /* no-op if replacing an old entry. */
return &namehashtab[idx];
}
if (namehashent == NULL && ! replace)
return;
- if (namehashent->name_offset == 0)
+ if (GET (namehashent->name_offset) == 0)
{
/* We are adding a new hash entry for this alias.
Determine whether we have to resize the file. */
- if (head->string_used + name_len + 1 > head->string_size
- || 100 * head->namehash_used > 75 * head->namehash_size)
+ if (GET (head->string_used) + name_len + 1 > GET (head->string_size)
+ || (100 * GET (head->namehash_used)
+ > 75 * GET (head->namehash_size)))
{
/* The current archive is not large enough. */
enlarge_archive (ah, head);
/* The locrecent might have moved, so we have to look up
the old name afresh. */
namehashent = insert_name (ah, oldname, strlen (oldname), true);
- assert (namehashent->name_offset != 0);
- assert (namehashent->locrec_offset != 0);
- *locrec_offset_p = namehashent->locrec_offset;
+ assert (GET (namehashent->name_offset) != 0);
+ assert (GET (namehashent->locrec_offset) != 0);
+ *locrec_offset_p = GET (namehashent->locrec_offset);
/* Tail call to try the whole thing again. */
add_alias (ah, alias, replace, oldname, locrec_offset_p);
}
/* Add the name string. */
- memcpy (ah->addr + head->string_offset + head->string_used,
+ memcpy (ah->addr + GET (head->string_offset) + GET (head->string_used),
alias, name_len + 1);
- namehashent->name_offset = head->string_offset + head->string_used;
- head->string_used += name_len + 1;
+ SET (namehashent->name_offset,
+ GET (head->string_offset) + GET (head->string_used));
+ INC (head->string_used, name_len + 1);
- ++head->namehash_used;
+ INC (head->namehash_used, 1);
}
- if (namehashent->locrec_offset != 0)
+ if (GET (namehashent->locrec_offset) != 0)
{
/* Replacing an existing entry.
Mark that we are no longer using the old locrecent. */
struct locrecent *locrecent
= (struct locrecent *) ((char *) ah->addr
- + namehashent->locrec_offset);
- --locrecent->refs;
+ + GET (namehashent->locrec_offset));
+ INC (locrecent->refs, -1);
}
/* Point this entry at the locrecent installed for the main name. */
- namehashent->locrec_offset = locrec_offset;
+ SET (namehashent->locrec_offset, locrec_offset);
}
static int /* qsort comparator used below */
off64_t lastoffset;
char *ptr;
struct locale_category_data *size_order[__LC_LAST];
- const size_t pagesz = getpagesize ();
+ /* Page size alignment is a minor optimization for locality; use a
+ common value here rather than making the localedef output depend
+ on the page size of the system on which localedef is run. See
+ <https://sourceware.org/glibc/wiki/Development_Todo/Master#Locale_archive_alignment>
+ for more discussion. */
+ const size_t pagesz = 4096;
int small_mask;
head = ah->addr;
sumhashtab = (struct sumhashent *) ((char *) ah->addr
- + head->sumhash_offset);
+ + GET (head->sumhash_offset));
memset (file_offsets, 0, sizeof (file_offsets));
table. */
hval = archive_hashval (data[cnt].sum, 16);
- idx = hval % head->sumhash_size;
- incr = 1 + hval % (head->sumhash_size - 2);
+ idx = hval % GET (head->sumhash_size);
+ incr = 1 + hval % (GET (head->sumhash_size) - 2);
- while (sumhashtab[idx].file_offset != 0)
+ while (GET (sumhashtab[idx].file_offset) != 0)
{
if (memcmp (data[cnt].sum, sumhashtab[idx].sum, 16) == 0)
{
- /* Found it. */
- file_offsets[cnt] = sumhashtab[idx].file_offset;
- --num_new_offsets;
- break;
+ /* Check the content, there could be a collision of
+ the hash sum.
+
+ Unfortunately the sumhashent record does not include
+ the size of the stored data. So we have to search for
+ it. */
+ locrecent
+ = (struct locrecent *) ((char *) ah->addr
+ + GET (head->locrectab_offset));
+ size_t iloc;
+ for (iloc = 0; iloc < GET (head->locrectab_used); ++iloc)
+ if (GET (locrecent[iloc].refs) != 0
+ && (GET (locrecent[iloc].record[cnt].offset)
+ == GET (sumhashtab[idx].file_offset)))
+ break;
+
+ if (iloc != GET (head->locrectab_used)
+ && data[cnt].size == GET (locrecent[iloc].record[cnt].len)
+ /* We have to compare the content. Either we can
+ have the data mmaped or we have to read from
+ the file. */
+ && (file_data_available_p
+ (ah, GET (sumhashtab[idx].file_offset),
+ data[cnt].size)
+ ? memcmp (data[cnt].addr,
+ (char *) ah->addr
+ + GET (sumhashtab[idx].file_offset),
+ data[cnt].size) == 0
+ : compare_from_file (ah, data[cnt].addr,
+ GET (sumhashtab[idx].file_offset),
+ data[cnt].size) == 0))
+ {
+ /* Found it. */
+ file_offsets[cnt] = GET (sumhashtab[idx].file_offset);
+ --num_new_offsets;
+ break;
+ }
}
idx += incr;
- if (idx >= head->sumhash_size)
- idx -= head->sumhash_size;
+ if (idx >= GET (head->sumhash_size))
+ idx -= GET (head->sumhash_size);
}
}
return 0;
/* Determine whether we have to resize the file. */
- if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size
- || (namehashent->locrec_offset == 0
- && (head->locrectab_used == head->locrectab_size
- || head->string_used + name_len + 1 > head->string_size
- || 100 * head->namehash_used > 75 * head->namehash_size)))
+ if ((100 * (GET (head->sumhash_used) + num_new_offsets)
+ > 75 * GET (head->sumhash_size))
+ || (GET (namehashent->locrec_offset) == 0
+ && (GET (head->locrectab_used) == GET (head->locrectab_size)
+ || (GET (head->string_used) + name_len + 1
+ > GET (head->string_size))
+ || (100 * GET (head->namehash_used)
+ > 75 * GET (head->namehash_size)))))
{
/* The current archive is not large enough. */
enlarge_archive (ah, head);
/* Add the hash value to the hash table. */
md5hval = archive_hashval (data[cnt].sum, 16);
- idx = md5hval % head->sumhash_size;
- incr = 1 + md5hval % (head->sumhash_size - 2);
+ idx = md5hval % GET (head->sumhash_size);
+ incr = 1 + md5hval % (GET (head->sumhash_size) - 2);
- while (sumhashtab[idx].file_offset != 0)
+ while (GET (sumhashtab[idx].file_offset) != 0)
{
idx += incr;
- if (idx >= head->sumhash_size)
- idx -= head->sumhash_size;
+ if (idx >= GET (head->sumhash_size))
+ idx -= GET (head->sumhash_size);
}
memcpy (sumhashtab[idx].sum, data[cnt].sum, 16);
- sumhashtab[idx].file_offset = file_offsets[cnt];
+ SET (sumhashtab[idx].file_offset, file_offsets[cnt]);
- ++head->sumhash_used;
+ INC (head->sumhash_used, 1);
}
lastoffset = file_offsets[LC_ALL];
lastoffset += (data[cnt].size + 15) & -16;
}
- if (namehashent->name_offset == 0)
+ if (GET (namehashent->name_offset) == 0)
{
/* Add the name string. */
- memcpy ((char *) ah->addr + head->string_offset + head->string_used,
+ memcpy ((char *) ah->addr + GET (head->string_offset)
+ + GET (head->string_used),
name, name_len + 1);
- namehashent->name_offset = head->string_offset + head->string_used;
- head->string_used += name_len + 1;
- ++head->namehash_used;
+ SET (namehashent->name_offset,
+ GET (head->string_offset) + GET (head->string_used));
+ INC (head->string_used, name_len + 1);
+ INC (head->namehash_used, 1);
}
- if (namehashent->locrec_offset == 0)
+ if (GET (namehashent->locrec_offset == 0))
{
/* Allocate a name location record. */
- namehashent->locrec_offset = (head->locrectab_offset
- + (head->locrectab_used++
- * sizeof (struct locrecent)));
+ SET (namehashent->locrec_offset, (GET (head->locrectab_offset)
+ + (GET (head->locrectab_used)
+ * sizeof (struct locrecent))));
+ INC (head->locrectab_used, 1);
locrecent = (struct locrecent *) ((char *) ah->addr
- + namehashent->locrec_offset);
- locrecent->refs = 1;
+ + GET (namehashent->locrec_offset));
+ SET (locrecent->refs, 1);
}
else
{
we still need a new one. If not, reuse the old one. */
locrecent = (struct locrecent *) ((char *) ah->addr
- + namehashent->locrec_offset);
- if (locrecent->refs > 1)
+ + GET (namehashent->locrec_offset));
+ if (GET (locrecent->refs) > 1)
{
- --locrecent->refs;
- namehashent->locrec_offset = (head->locrectab_offset
- + (head->locrectab_used++
- * sizeof (struct locrecent)));
- locrecent = (struct locrecent *) ((char *) ah->addr
- + namehashent->locrec_offset);
- locrecent->refs = 1;
+ INC (locrecent->refs, -1);
+ SET (namehashent->locrec_offset, (GET (head->locrectab_offset)
+ + (GET (head->locrectab_used)
+ * sizeof (struct locrecent))));
+ INC (head->locrectab_used, 1);
+ locrecent
+ = (struct locrecent *) ((char *) ah->addr
+ + GET (namehashent->locrec_offset));
+ SET (locrecent->refs, 1);
}
}
/* Fill in the table with the locations of the locale data. */
for (cnt = 0; cnt < __LC_LAST; ++cnt)
{
- locrecent->record[cnt].offset = file_offsets[cnt];
- locrecent->record[cnt].len = data[cnt].size;
+ SET (locrecent->record[cnt].offset, file_offsets[cnt]);
+ SET (locrecent->record[cnt].len, data[cnt].size);
}
- return namehashent->locrec_offset;
+ return GET (namehashent->locrec_offset);
}
of the files if necessary. Add all the names, possibly overwriting
old files. */
int
-add_locale_to_archive (ah, name, data, replace)
- struct locarhandle *ah;
- const char *name;
- locale_data_t data;
- bool replace;
+add_locale_to_archive (struct locarhandle *ah, const char *name,
+ locale_data_t data, bool replace)
{
char *normalized_name = NULL;
uint32_t locrec_offset;
int mask = _nl_explode_name (strdupa (name),
&language, &modifier, &territory,
&codeset, &normalized_codeset);
+ if (mask == -1)
+ return -1;
if (mask & XPG_NORM_CODESET)
/* This name contains a codeset in unnormalized form.
unsigned int strindex[0];
} *filedata = data[LC_CTYPE].addr;
codeset = (char *) filedata
- + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)];
+ + maybe_swap_uint32 (filedata->strindex[_NL_ITEM_INDEX
+ (_NL_CTYPE_CODESET_NAME)]);
char *normalized_codeset_name = NULL;
normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset));
/* Now read the locale.alias files looking for lines whose
right hand side matches our name after normalization. */
+ int result = 0;
if (alias_file != NULL)
{
FILE *fp;
&rhs_territory,
&rhs_codeset,
&rhs_normalized_codeset);
+ if (rhs_mask == -1)
+ {
+ result = -1;
+ goto out;
+ }
if (!strcmp (language, rhs_language)
&& ((rhs_mask & XPG_CODESET)
/* He has a codeset, it must match normalized. */
}
}
+ out:
fclose (fp);
}
if (mask & XPG_NORM_CODESET)
free ((char *) normalized_codeset);
- return 0;
+ return result;
}
int
-add_locales_to_archive (nlist, list, replace)
- size_t nlist;
- char *list[];
- bool replace;
+add_locales_to_archive (size_t nlist, char *list[], bool replace)
{
struct locarhandle ah;
int result = 0;
/* Open the archive. This call never returns if we cannot
successfully open the archive. */
+ ah.fname = NULL;
open_archive (&ah, false);
while (nlist-- > 0)
a directory we have to look at a file with the
prefix "SYS_". Otherwise we have found what we
are looking for. */
-#ifdef _DIRENT_HAVE_D_TYPE
d_type = d->d_type;
if (d_type != DT_REG)
-#endif
{
char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
-#ifdef _DIRENT_HAVE_D_TYPE
if (d_type == DT_UNKNOWN)
-#endif
{
strcpy (stpcpy (stpcpy (fullname, fname), "/"),
d->d_name);
int
-delete_locales_from_archive (nlist, list)
- size_t nlist;
- char *list[];
+delete_locales_from_archive (size_t nlist, char *list[])
{
struct locarhandle ah;
struct locarhead *head;
/* Open the archive. This call never returns if we cannot
successfully open the archive. */
+ ah.fname = NULL;
open_archive (&ah, false);
head = ah.addr;
namehashtab = (struct namehashent *) ((char *) ah.addr
- + head->namehash_offset);
+ + GET (head->namehash_offset));
while (nlist-- > 0)
{
/* Search for this locale in the archive. */
hval = archive_hashval (locname, strlen (locname));
- idx = hval % head->namehash_size;
- incr = 1 + hval % (head->namehash_size - 2);
+ idx = hval % GET (head->namehash_size);
+ incr = 1 + hval % (GET (head->namehash_size) - 2);
/* If the name_offset field is zero this means this is no
deleted entry and therefore no entry can be found. */
- while (namehashtab[idx].name_offset != 0)
+ while (GET (namehashtab[idx].name_offset) != 0)
{
- if (namehashtab[idx].hashval == hval
+ if (GET (namehashtab[idx].hashval) == hval
&& (strcmp (locname,
- (char *) ah.addr + namehashtab[idx].name_offset)
+ ((char *) ah.addr
+ + GET (namehashtab[idx].name_offset)))
== 0))
{
/* Found the entry. Now mark it as removed by zero-ing
the reference to the locale record. */
- namehashtab[idx].locrec_offset = 0;
+ SET (namehashtab[idx].locrec_offset, 0);
break;
}
idx += incr;
- if (idx >= head->namehash_size)
- idx -= head->namehash_size;
+ if (idx >= GET (head->namehash_size))
+ idx -= GET (head->namehash_size);
}
- if (namehashtab[idx].name_offset == 0 && ! be_quiet)
+ if (GET (namehashtab[idx].name_offset) == 0 && ! be_quiet)
error (0, 0, _("locale \"%s\" not in archive"), locname);
}
void
-show_archive_content (int verbose)
+show_archive_content (const char *fname, int verbose)
{
struct locarhandle ah;
struct locarhead *head;
struct namehashent *namehashtab;
struct nameent *names;
- int cnt;
- int used;
+ size_t cnt, used;
/* Open the archive. This call never returns if we cannot
successfully open the archive. */
+ ah.fname = fname;
open_archive (&ah, true);
head = ah.addr;
- names = (struct nameent *) xmalloc (head->namehash_used
+ names = (struct nameent *) xmalloc (GET (head->namehash_used)
* sizeof (struct nameent));
namehashtab = (struct namehashent *) ((char *) ah.addr
- + head->namehash_offset);
- for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
- if (namehashtab[cnt].locrec_offset != 0)
+ + GET (head->namehash_offset));
+ for (cnt = used = 0; cnt < GET (head->namehash_size); ++cnt)
+ if (GET (namehashtab[cnt].locrec_offset) != 0)
{
- assert (used < head->namehash_used);
- names[used].name = ah.addr + namehashtab[cnt].name_offset;
- names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
+ assert (used < GET (head->namehash_used));
+ names[used].name = ah.addr + GET (namehashtab[cnt].name_offset);
+ names[used++].locrec_offset = GET (namehashtab[cnt].locrec_offset);
}
/* Sort the names. */
struct sumhashent *sumhashtab;
int sumused;
- files = (struct dataent *) xmalloc (head->sumhash_used
- * sizeof (struct sumhashent));
+ files = (struct dataent *) xmalloc (GET (head->sumhash_used)
+ * sizeof (struct dataent));
sumhashtab = (struct sumhashent *) ((char *) ah.addr
- + head->sumhash_offset);
- for (cnt = sumused = 0; cnt < head->sumhash_size; ++cnt)
- if (sumhashtab[cnt].file_offset != 0)
+ + GET (head->sumhash_offset));
+ for (cnt = sumused = 0; cnt < GET (head->sumhash_size); ++cnt)
+ if (GET (sumhashtab[cnt].file_offset) != 0)
{
- assert (sumused < head->sumhash_used);
+ assert (sumused < GET (head->sumhash_used));
files[sumused].sum = (const unsigned char *) sumhashtab[cnt].sum;
- files[sumused].file_offset = sumhashtab[cnt].file_offset;
+ files[sumused].file_offset = GET (sumhashtab[cnt].file_offset);
files[sumused++].nlink = 0;
}
locrec = (struct locrecent *) ((char *) ah.addr
+ names[cnt].locrec_offset);
for (idx = 0; idx < __LC_LAST; ++idx)
- if (locrec->record[LC_ALL].offset != 0
+ if (GET (locrec->record[LC_ALL].offset) != 0
? (idx == LC_ALL
- || (locrec->record[idx].offset
- < locrec->record[LC_ALL].offset)
- || (locrec->record[idx].offset + locrec->record[idx].len
- > (locrec->record[LC_ALL].offset
- + locrec->record[LC_ALL].len)))
+ || (GET (locrec->record[idx].offset)
+ < GET (locrec->record[LC_ALL].offset))
+ || ((GET (locrec->record[idx].offset)
+ + GET (locrec->record[idx].len))
+ > (GET (locrec->record[LC_ALL].offset)
+ + GET (locrec->record[LC_ALL].len))))
: idx != LC_ALL)
{
struct dataent *data, dataent;
- dataent.file_offset = locrec->record[idx].offset;
+ dataent.file_offset = GET (locrec->record[idx].offset);
data = (struct dataent *) bsearch (&dataent, files, sumused,
sizeof (struct dataent),
dataentcmp);
{
struct dataent *data, dataent;
- dataent.file_offset = locrec->record[idx].offset;
- if (locrec->record[LC_ALL].offset != 0
- && dataent.file_offset >= locrec->record[LC_ALL].offset
- && (dataent.file_offset + locrec->record[idx].len
- <= (locrec->record[LC_ALL].offset
- + locrec->record[LC_ALL].len)))
- dataent.file_offset = locrec->record[LC_ALL].offset;
+ dataent.file_offset = GET (locrec->record[idx].offset);
+ if (GET (locrec->record[LC_ALL].offset) != 0
+ && (dataent.file_offset
+ >= GET (locrec->record[LC_ALL].offset))
+ && (dataent.file_offset + GET (locrec->record[idx].len)
+ <= (GET (locrec->record[LC_ALL].offset)
+ + GET (locrec->record[LC_ALL].len))))
+ dataent.file_offset = GET (locrec->record[LC_ALL].offset);
data = (struct dataent *) bsearch (&dataent, files, sumused,
sizeof (struct dataent),
dataentcmp);
printf ("%6d %7x %3d%c ",
- locrec->record[idx].len, locrec->record[idx].offset,
+ GET (locrec->record[idx].len),
+ GET (locrec->record[idx].offset),
data->nlink,
- dataent.file_offset == locrec->record[LC_ALL].offset
+ (dataent.file_offset
+ == GET (locrec->record[LC_ALL].offset))
? '+' : ' ');
for (i = 0; i < 16; i += 4)
printf ("%02x%02x%02x%02x",