aboutsummaryrefslogtreecommitdiff
path: root/locale/programs/locarchive.c
diff options
context:
space:
mode:
Diffstat (limited to 'locale/programs/locarchive.c')
-rw-r--r--locale/programs/locarchive.c232
1 files changed, 195 insertions, 37 deletions
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
index 267d7ba..057fab8 100644
--- a/locale/programs/locarchive.c
+++ b/locale/programs/locarchive.c
@@ -167,6 +167,46 @@ create_archive (const char *archivefname, struct locarhandle *ah)
ah->len = total;
}
+
+struct oldlocrecent
+{
+ unsigned int cnt;
+ struct locrecent *locrec;
+};
+
+static int
+oldlocrecentcmp (const void *a, const void *b)
+{
+ struct locrecent *la = ((const struct oldlocrecent *) a)->locrec;
+ struct locrecent *lb = ((const struct oldlocrecent *) b)->locrec;
+
+ if (la->record[LC_ALL].offset < lb->record[LC_ALL].offset)
+ return -1;
+ if (la->record[LC_ALL].offset > lb->record[LC_ALL].offset)
+ return 1;
+
+ if (la->record[LC_CTYPE].offset < lb->record[LC_CTYPE].offset)
+ return -1;
+ if (la->record[LC_CTYPE].offset > lb->record[LC_CTYPE].offset)
+ return 1;
+
+ if (la->record[LC_COLLATE].offset < lb->record[LC_COLLATE].offset)
+ return -1;
+ if (la->record[LC_COLLATE].offset > lb->record[LC_COLLATE].offset)
+ return 1;
+
+ if (((const struct oldlocrecent *) a)->cnt
+ < ((const struct oldlocrecent *) b)->cnt)
+ return -1;
+
+ if (((const struct oldlocrecent *) a)->cnt
+ > ((const struct oldlocrecent *) b)->cnt)
+ return 1;
+
+ return 0;
+}
+
+
/* forward decl for below */
static uint32_t add_locale (struct locarhandle *ah, const char *name,
locale_data_t data, bool replace);
@@ -179,13 +219,14 @@ enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
struct locarhead newhead;
size_t total;
void *p;
- unsigned int cnt;
+ 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);
@@ -282,34 +323,46 @@ enlarge_archive (struct locarhandle *ah, const struct locarhead *head)
+ head->namehash_offset);
oldlocrectab = (struct locrecent *) ((char *) ah->addr
+ head->locrectab_offset);
- for (cnt = 0; cnt < head->namehash_size; ++cnt)
+ 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)
{
- /* Insert this entry in the new hash table. */
- locale_data_t old_data;
- unsigned int idx;
- struct locrecent *oldlocrec;
+ oldlocrecarray[loccnt].cnt = cnt;
+ oldlocrecarray[loccnt++].locrec
+ = (struct locrecent *) ((char *) ah->addr
+ + oldnamehashtab[cnt].locrec_offset);
+ }
- oldlocrec = (struct locrecent *) ((char *) ah->addr
- + oldnamehashtab[cnt].locrec_offset);
+ qsort (oldlocrecarray, loccnt, sizeof (struct oldlocrecent),
+ oldlocrecentcmp);
- for (idx = 0; idx < __LC_LAST; ++idx)
- if (idx != LC_ALL)
- {
- old_data[idx].size = oldlocrec->record[idx].len;
- old_data[idx].addr
- = ((char *) ah->addr + oldlocrec->record[idx].offset);
+ for (cnt = 0; cnt < loccnt; ++cnt)
+ {
+ /* Insert this entry in the new hash table. */
+ locale_data_t old_data;
+ unsigned int idx;
+ struct locrecent *oldlocrec = oldlocrecarray[cnt].locrec;
- __md5_buffer (old_data[idx].addr, old_data[idx].size,
- old_data[idx].sum);
- }
+ for (idx = 0; idx < __LC_LAST; ++idx)
+ if (idx != LC_ALL)
+ {
+ old_data[idx].size = oldlocrec->record[idx].len;
+ old_data[idx].addr
+ = ((char *) ah->addr + oldlocrec->record[idx].offset);
- if (add_locale (&new_ah,
- ((char *) ah->addr + oldnamehashtab[cnt].name_offset),
- old_data, 0) == 0)
- error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
- }
+ __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)
+ error (EXIT_FAILURE, 0, _("cannot extend locale archive file"));
+ }
/* Make the file globally readable. */
if (fchmod (fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
@@ -487,6 +540,13 @@ insert_name (struct locarhandle *ah,
break;
}
+ if (namehashtab[idx].hashval == hval)
+ {
+ error (0, 0, "hash collision (%u) %s, %s",
+ hval, name, (char *) ah->addr + namehashtab[idx].name_offset);
+ }
+
+
/* Remember the first place we can insert the new entry. */
if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1)
insert_idx = idx;
@@ -560,6 +620,16 @@ add_alias (struct locarhandle *ah, const char *alias, bool replace,
namehashent->locrec_offset = locrec_offset;
}
+static int /* qsort comparator used below */
+cmpcategorysize (const void *a, const void *b)
+{
+ if (*(const void **) a == NULL)
+ return 1;
+ if (*(const void **) b == NULL)
+ return -1;
+ return ((*(const struct locale_category_data **) a)->size
+ - (*(const struct locale_category_data **) b)->size);
+}
/* Check the content of the archive for duplicates. Add the content
of the files if necessary. Returns the locrec_offset. */
@@ -575,25 +645,70 @@ add_locale (struct locarhandle *ah,
unsigned int num_new_offsets = 0;
struct sumhashent *sumhashtab;
uint32_t hval;
- unsigned int cnt;
- unsigned int idx;
+ unsigned int cnt, idx;
struct locarhead *head;
struct namehashent *namehashent;
unsigned int incr;
struct locrecent *locrecent;
+ off64_t lastoffset;
+ char *ptr;
+ struct locale_category_data *size_order[__LC_LAST];
+ const size_t pagesz = getpagesize ();
+ int small_mask;
head = ah->addr;
sumhashtab = (struct sumhashent *) ((char *) ah->addr
+ head->sumhash_offset);
+ memset (file_offsets, 0, sizeof (file_offsets));
+
+ size_order[LC_ALL] = NULL;
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (cnt != LC_ALL)
+ size_order[cnt] = &data[cnt];
+
+ /* Sort the array in ascending order of data size. */
+ qsort (size_order, __LC_LAST, sizeof size_order[0], cmpcategorysize);
+
+ small_mask = 0;
+ data[LC_ALL].size = 0;
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (size_order[cnt] != NULL)
+ {
+ const size_t rounded_size = (size_order[cnt]->size + 15) & -16;
+ if (data[LC_ALL].size + rounded_size > 2 * pagesz)
+ {
+ /* This category makes the small-categories block
+ stop being small, so this is the end of the road. */
+ do
+ size_order[cnt++] = NULL;
+ while (cnt < __LC_LAST);
+ break;
+ }
+ data[LC_ALL].size += rounded_size;
+ small_mask |= 1 << (size_order[cnt] - data);
+ }
+
+ /* Copy the data for all the small categories into the LC_ALL
+ pseudo-category. */
+
+ data[LC_ALL].addr = alloca (data[LC_ALL].size);
+ memset (data[LC_ALL].addr, 0, data[LC_ALL].size);
+
+ ptr = data[LC_ALL].addr;
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (small_mask & (1 << cnt))
+ {
+ memcpy (ptr, data[cnt].addr, data[cnt].size);
+ ptr += (data[cnt].size + 15) & -16;
+ }
+ __md5_buffer (data[LC_ALL].addr, data[LC_ALL].size, data[LC_ALL].sum);
/* For each locale category data set determine whether the same data
is already somewhere in the archive. */
for (cnt = 0; cnt < __LC_LAST; ++cnt)
- if (cnt != LC_ALL)
+ if (small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
{
- /* By default signal that we have no data. */
- file_offsets[cnt] = 0;
++num_new_offsets;
/* Compute the hash value of the checksum to determine a
@@ -638,8 +753,9 @@ add_locale (struct locarhandle *ah,
}
/* Add the locale data which is not yet in the archive. */
- for (cnt = 0; cnt < __LC_LAST; ++cnt)
- if (cnt != LC_ALL && file_offsets[cnt] == 0)
+ for (cnt = 0, lastoffset = 0; cnt < __LC_LAST; ++cnt)
+ if ((small_mask == 0 ? cnt != LC_ALL : !(small_mask & (1 << cnt)))
+ && file_offsets[cnt] == 0)
{
/* The data for this section is not yet available in the
archive. Append it. */
@@ -650,6 +766,24 @@ add_locale (struct locarhandle *ah,
if (lastpos == (off64_t) -1)
error (EXIT_FAILURE, errno, _("cannot add to locale archive"));
+ /* If block of small categories would cross page boundary,
+ align it unless it immediately follows a large category. */
+ if (cnt == LC_ALL && lastoffset != lastpos
+ && ((((lastpos & (pagesz - 1)) + data[cnt].size + pagesz - 1)
+ & -pagesz)
+ > ((data[cnt].size + pagesz - 1) & -pagesz)))
+ {
+ size_t sz = pagesz - (lastpos & (pagesz - 1));
+ char *zeros = alloca (sz);
+
+ memset (zeros, 0, sz);
+ if (TEMP_FAILURE_RETRY (write (ah->fd, zeros, sz) != sz))
+ error (EXIT_FAILURE, errno,
+ _("cannot add to locale archive"));
+
+ lastpos += sz;
+ }
+
/* Align all data to a 16 byte boundary. */
if ((lastpos & 15) != 0)
{
@@ -664,6 +798,7 @@ add_locale (struct locarhandle *ah,
/* Remember the position. */
file_offsets[cnt] = lastpos;
+ lastoffset = lastpos + data[cnt].size;
/* Write the data. */
if (TEMP_FAILURE_RETRY (write (ah->fd, data[cnt].addr, data[cnt].size))
@@ -689,6 +824,14 @@ add_locale (struct locarhandle *ah,
++head->sumhash_used;
}
+ lastoffset = file_offsets[LC_ALL];
+ for (cnt = 0; cnt < __LC_LAST; ++cnt)
+ if (small_mask & (1 << cnt))
+ {
+ file_offsets[cnt] = lastoffset;
+ lastoffset += (data[cnt].size + 15) & -16;
+ }
+
if (namehashent->name_offset == 0)
{
/* Add the name string. */
@@ -730,11 +873,10 @@ add_locale (struct locarhandle *ah,
/* Fill in the table with the locations of the locale data. */
for (cnt = 0; cnt < __LC_LAST; ++cnt)
- if (cnt != LC_ALL)
- {
- locrecent->record[cnt].offset = file_offsets[cnt];
- locrecent->record[cnt].len = data[cnt].size;
- }
+ {
+ locrecent->record[cnt].offset = file_offsets[cnt];
+ locrecent->record[cnt].len = data[cnt].size;
+ }
return namehashent->locrec_offset;
}
@@ -1292,7 +1434,14 @@ show_archive_content (int verbose)
locrec = (struct locrecent *) ((char *) ah.addr
+ names[cnt].locrec_offset);
for (idx = 0; idx < __LC_LAST; ++idx)
- if (idx != LC_ALL)
+ if (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)))
+ : idx != LC_ALL)
{
struct dataent *data, dataent;
@@ -1319,12 +1468,21 @@ show_archive_content (int verbose)
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;
+
data = (struct dataent *) bsearch (&dataent, files, sumused,
sizeof (struct dataent),
dataentcmp);
- printf ("%6d %7x %3d ",
+ printf ("%6d %7x %3d%c ",
locrec->record[idx].len, locrec->record[idx].offset,
- data->nlink);
+ data->nlink,
+ dataent.file_offset == locrec->record[LC_ALL].offset
+ ? '+' : ' ');
for (i = 0; i < 16; i += 4)
printf ("%02x%02x%02x%02x",
data->sum[i], data->sum[i + 1],