diff options
Diffstat (limited to 'locale')
-rw-r--r-- | locale/locarchive.h | 2 | ||||
-rw-r--r-- | locale/programs/locarchive.c | 232 |
2 files changed, 196 insertions, 38 deletions
diff --git a/locale/locarchive.h b/locale/locarchive.h index f43a6b6734..e94bc2b480 100644 --- a/locale/locarchive.h +++ b/locale/locarchive.h @@ -88,7 +88,7 @@ struct locarhandle /* In memory data for the locales with their checksums. */ -typedef struct +typedef struct locale_category_data { off64_t size; void *addr; diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c index 267d7baaa4..057fab8d2e 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], |