diff options
Diffstat (limited to 'locale/loadarchive.c')
-rw-r--r-- | locale/loadarchive.c | 547 |
1 files changed, 0 insertions, 547 deletions
diff --git a/locale/loadarchive.c b/locale/loadarchive.c deleted file mode 100644 index e6e1a05d2e..0000000000 --- a/locale/loadarchive.c +++ /dev/null @@ -1,547 +0,0 @@ -/* Code to load locale data from the locale archive file. - Copyright (C) 2002-2017 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - 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. - - The GNU C Library 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. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <http://www.gnu.org/licenses/>. */ - -#include <locale.h> -#include <stddef.h> -#include <stdlib.h> -#include <stdbool.h> -#include <errno.h> -#include <assert.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdint.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/param.h> - -#include "localeinfo.h" -#include "locarchive.h" -#include <not-cancel.h> - -/* Define the hash function. We define the function as static inline. */ -#define compute_hashval static inline compute_hashval -#define hashval_t uint32_t -#include "hashval.h" -#undef compute_hashval - - -/* Name of the locale archive file. */ -static const char archfname[] = COMPLOCALEDIR "/locale-archive"; - -/* Size of initial mapping window, optimal if large enough to - cover the header plus the initial locale. */ -#define ARCHIVE_MAPPING_WINDOW (2 * 1024 * 1024) - -#ifndef MAP_COPY -/* This is not quite as good as MAP_COPY since unexamined pages - can change out from under us and give us inconsistent data. - But we rely on the user not to diddle the system's live archive. - Even though we only ever use PROT_READ, using MAP_SHARED would - not give the system sufficient freedom to e.g. let the on disk - file go away because it doesn't know we won't call mprotect later. */ -# define MAP_COPY MAP_PRIVATE -#endif -#ifndef MAP_FILE - /* Some systems do not have this flag; it is superfluous. */ -# define MAP_FILE 0 -#endif - -/* Record of contiguous pages already mapped from the locale archive. */ -struct archmapped -{ - void *ptr; - uint32_t from; - uint32_t len; - struct archmapped *next; -}; -static struct archmapped *archmapped; - -/* This describes the mapping at the beginning of the file that contains - the header data. There could be data in the following partial page, - so this is searched like any other. Once the archive has been used, - ARCHMAPPED points to this; if mapping the archive header failed, - then headmap.ptr is null. */ -static struct archmapped headmap; -static struct stat64 archive_stat; /* stat of archive when header mapped. */ - -/* Record of locales that we have already loaded from the archive. */ -struct locale_in_archive -{ - struct locale_in_archive *next; - char *name; - struct __locale_data *data[__LC_LAST]; -}; -static struct locale_in_archive *archloaded; - - -/* Local structure and subroutine of _nl_load_archive, see below. */ -struct range -{ - uint32_t from; - uint32_t len; - int category; - void *result; -}; - -static int -rangecmp (const void *p1, const void *p2) -{ - return ((struct range *) p1)->from - ((struct range *) p2)->from; -} - - -/* Calculate the amount of space needed for all the tables described - by the given header. Note we do not include the empty table space - that has been preallocated in the file, so our mapping may not be - large enough if localedef adds data to the file in place. However, - doing that would permute the header fields while we are accessing - them and thus not be safe anyway, so we don't allow for that. */ -static inline off_t -calculate_head_size (const struct locarhead *h) -{ - off_t namehash_end = (h->namehash_offset - + h->namehash_size * sizeof (struct namehashent)); - off_t string_end = h->string_offset + h->string_used; - off_t locrectab_end = (h->locrectab_offset - + h->locrectab_used * sizeof (struct locrecent)); - return MAX (namehash_end, MAX (string_end, locrectab_end)); -} - - -/* Find the locale *NAMEP in the locale archive, and return the - internalized data structure for its CATEGORY data. If this locale has - already been loaded from the archive, just returns the existing data - structure. If successful, sets *NAMEP to point directly into the mapped - archive string table; that way, the next call can short-circuit strcmp. */ -struct __locale_data * -internal_function -_nl_load_locale_from_archive (int category, const char **namep) -{ - const char *name = *namep; - struct - { - void *addr; - size_t len; - } results[__LC_LAST]; - struct locale_in_archive *lia; - struct locarhead *head; - struct namehashent *namehashtab; - struct locrecent *locrec; - struct archmapped *mapped; - struct archmapped *last; - unsigned long int hval; - size_t idx; - size_t incr; - struct range ranges[__LC_LAST - 1]; - int nranges; - int cnt; - size_t ps = __sysconf (_SC_PAGE_SIZE); - int fd = -1; - - /* Check if we have already loaded this locale from the archive. - If we previously loaded the locale but found bogons in the data, - then we will have stored a null pointer to return here. */ - for (lia = archloaded; lia != NULL; lia = lia->next) - if (name == lia->name || !strcmp (name, lia->name)) - { - *namep = lia->name; - return lia->data[category]; - } - - { - /* If the name contains a codeset, then we normalize the name before - doing the lookup. */ - const char *p = strchr (name, '.'); - if (p != NULL && p[1] != '@' && p[1] != '\0') - { - const char *rest = __strchrnul (++p, '@'); - const char *normalized_codeset = _nl_normalize_codeset (p, rest - p); - if (normalized_codeset == NULL) /* malloc failure */ - return NULL; - if (strncmp (normalized_codeset, p, rest - p) != 0 - || normalized_codeset[rest - p] != '\0') - { - /* There is a normalized codeset name that is different from - what was specified; reconstruct a new locale name using it. */ - size_t normlen = strlen (normalized_codeset); - size_t restlen = strlen (rest) + 1; - char *newname = alloca (p - name + normlen + restlen); - memcpy (__mempcpy (__mempcpy (newname, name, p - name), - normalized_codeset, normlen), - rest, restlen); - name = newname; - } - free ((char *) normalized_codeset); - } - } - - /* Make sure the archive is loaded. */ - if (archmapped == NULL) - { - void *result; - size_t headsize, mapsize; - - /* We do this early as a sign that we have tried to open the archive. - If headmap.ptr remains null, that's an indication that we tried - and failed, so we won't try again. */ - archmapped = &headmap; - - /* The archive has never been opened. */ - fd = open_not_cancel_2 (archfname, O_RDONLY|O_LARGEFILE|O_CLOEXEC); - if (fd < 0) - /* Cannot open the archive, for whatever reason. */ - return NULL; - - if (__fxstat64 (_STAT_VER, fd, &archive_stat) == -1) - { - /* stat failed, very strange. */ - close_and_out: - if (fd >= 0) - close_not_cancel_no_status (fd); - return NULL; - } - - - /* Map an initial window probably large enough to cover the header - and the first locale's data. With a large address space, we can - just map the whole file and be sure everything is covered. */ - - mapsize = (sizeof (void *) > 4 ? archive_stat.st_size - : MIN (archive_stat.st_size, ARCHIVE_MAPPING_WINDOW)); - - result = __mmap64 (NULL, mapsize, PROT_READ, MAP_FILE|MAP_COPY, fd, 0); - if (result == MAP_FAILED) - goto close_and_out; - - /* Check whether the file is large enough for the sizes given in - the header. Theoretically an archive could be so large that - just the header fails to fit in our initial mapping window. */ - headsize = calculate_head_size ((const struct locarhead *) result); - if (headsize > mapsize) - { - (void) __munmap (result, mapsize); - if (sizeof (void *) > 4 || headsize > archive_stat.st_size) - /* The file is not big enough for the header. Bogus. */ - goto close_and_out; - - /* Freakishly long header. */ - /* XXX could use mremap when available */ - mapsize = (headsize + ps - 1) & ~(ps - 1); - result = __mmap64 (NULL, mapsize, PROT_READ, MAP_FILE|MAP_COPY, - fd, 0); - if (result == MAP_FAILED) - goto close_and_out; - } - - if (sizeof (void *) > 4 || mapsize >= archive_stat.st_size) - { - /* We've mapped the whole file already, so we can be - sure we won't need this file descriptor later. */ - close_not_cancel_no_status (fd); - fd = -1; - } - - headmap.ptr = result; - /* headmap.from already initialized to zero. */ - headmap.len = mapsize; - } - - /* If there is no archive or it cannot be loaded for some reason fail. */ - if (__glibc_unlikely (headmap.ptr == NULL)) - goto close_and_out; - - /* We have the archive available. To find the name we first have to - determine its hash value. */ - hval = compute_hashval (name, strlen (name)); - - head = headmap.ptr; - namehashtab = (struct namehashent *) ((char *) head - + head->namehash_offset); - - /* Avoid division by 0 if the file is corrupted. */ - if (__glibc_unlikely (head->namehash_size == 0)) - goto close_and_out; - - idx = hval % head->namehash_size; - incr = 1 + hval % (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 (1) - { - if (namehashtab[idx].name_offset == 0) - /* Not found. */ - goto close_and_out; - - if (namehashtab[idx].hashval == hval - && strcmp (name, headmap.ptr + namehashtab[idx].name_offset) == 0) - /* Found the entry. */ - break; - - idx += incr; - if (idx >= head->namehash_size) - idx -= head->namehash_size; - } - - /* We found an entry. It might be a placeholder for a removed one. */ - if (namehashtab[idx].locrec_offset == 0) - goto close_and_out; - - locrec = (struct locrecent *) (headmap.ptr + namehashtab[idx].locrec_offset); - - if (sizeof (void *) > 4 /* || headmap.len == archive_stat.st_size */) - { - /* We already have the whole locale archive mapped in. */ - assert (headmap.len == archive_stat.st_size); - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - { - if (locrec->record[cnt].offset + locrec->record[cnt].len - > headmap.len) - /* The archive locrectab contains bogus offsets. */ - goto close_and_out; - results[cnt].addr = headmap.ptr + locrec->record[cnt].offset; - results[cnt].len = locrec->record[cnt].len; - } - } - else - { - /* Get the offsets of the data files and sort them. */ - for (cnt = nranges = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - { - ranges[nranges].from = locrec->record[cnt].offset; - ranges[nranges].len = locrec->record[cnt].len; - ranges[nranges].category = cnt; - ranges[nranges].result = NULL; - - ++nranges; - } - - qsort (ranges, nranges, sizeof (ranges[0]), rangecmp); - - /* The information about mmap'd blocks is kept in a list. - Skip over the blocks which are before the data we need. */ - last = mapped = archmapped; - for (cnt = 0; cnt < nranges; ++cnt) - { - int upper; - size_t from; - size_t to; - void *addr; - struct archmapped *newp; - - /* Determine whether the appropriate page is already mapped. */ - while (mapped != NULL - && (mapped->from + mapped->len - <= ranges[cnt].from + ranges[cnt].len)) - { - last = mapped; - mapped = mapped->next; - } - - /* Do we have a match? */ - if (mapped != NULL - && mapped->from <= ranges[cnt].from - && (ranges[cnt].from + ranges[cnt].len - <= mapped->from + mapped->len)) - { - /* Yep, already loaded. */ - results[ranges[cnt].category].addr = ((char *) mapped->ptr - + ranges[cnt].from - - mapped->from); - results[ranges[cnt].category].len = ranges[cnt].len; - continue; - } - - /* Map the range with the locale data from the file. We will - try to cover as much of the locale as possible. I.e., if the - next category (next as in "next offset") is on the current or - immediately following page we use it as well. */ - assert (powerof2 (ps)); - from = ranges[cnt].from & ~(ps - 1); - upper = cnt; - do - { - to = ranges[upper].from + ranges[upper].len; - if (to > (size_t) archive_stat.st_size) - /* The archive locrectab contains bogus offsets. */ - goto close_and_out; - to = (to + ps - 1) & ~(ps - 1); - - /* If a range is already mmaped in, stop. */ - if (mapped != NULL && ranges[upper].from >= mapped->from) - break; - - ++upper; - } - /* Loop while still in contiguous pages. */ - while (upper < nranges && ranges[upper].from < to + ps); - - /* Open the file if it hasn't happened yet. */ - if (fd == -1) - { - struct stat64 st; - fd = open_not_cancel_2 (archfname, - O_RDONLY|O_LARGEFILE|O_CLOEXEC); - if (fd == -1) - /* Cannot open the archive, for whatever reason. */ - return NULL; - /* Now verify we think this is really the same archive file - we opened before. If it has been changed we cannot trust - the header we read previously. */ - if (__fxstat64 (_STAT_VER, fd, &st) < 0 - || st.st_size != archive_stat.st_size - || st.st_mtime != archive_stat.st_mtime - || st.st_dev != archive_stat.st_dev - || st.st_ino != archive_stat.st_ino) - goto close_and_out; - } - - /* Map the range from the archive. */ - addr = __mmap64 (NULL, to - from, PROT_READ, MAP_FILE|MAP_COPY, - fd, from); - if (addr == MAP_FAILED) - goto close_and_out; - - /* Allocate a record for this mapping. */ - newp = (struct archmapped *) malloc (sizeof (struct archmapped)); - if (newp == NULL) - { - (void) __munmap (addr, to - from); - goto close_and_out; - } - - /* And queue it. */ - newp->ptr = addr; - newp->from = from; - newp->len = to - from; - assert (last->next == mapped); - newp->next = mapped; - last->next = newp; - last = newp; - - /* Determine the load addresses for the category data. */ - do - { - assert (ranges[cnt].from >= from); - results[ranges[cnt].category].addr = ((char *) addr - + ranges[cnt].from - from); - results[ranges[cnt].category].len = ranges[cnt].len; - } - while (++cnt < upper); - --cnt; /* The 'for' will increase 'cnt' again. */ - } - } - - /* We don't need the file descriptor any longer. */ - if (fd >= 0) - close_not_cancel_no_status (fd); - fd = -1; - - /* We succeeded in mapping all the necessary regions of the archive. - Now we need the expected data structures to point into the data. */ - - lia = malloc (sizeof *lia); - if (__glibc_unlikely (lia == NULL)) - return NULL; - - lia->name = __strdup (*namep); - if (__glibc_unlikely (lia->name == NULL)) - { - free (lia); - return NULL; - } - - lia->next = archloaded; - archloaded = lia; - - for (cnt = 0; cnt < __LC_LAST; ++cnt) - if (cnt != LC_ALL) - { - lia->data[cnt] = _nl_intern_locale_data (cnt, - results[cnt].addr, - results[cnt].len); - if (__glibc_likely (lia->data[cnt] != NULL)) - { - /* _nl_intern_locale_data leaves us these fields to initialize. */ - lia->data[cnt]->alloc = ld_archive; - lia->data[cnt]->name = lia->name; - - /* We do this instead of bumping the count each time we return - this data because the mappings stay around forever anyway - and we might as well hold on to a little more memory and not - have to rebuild it on the next lookup of the same thing. - If we were to maintain the usage_count normally and let the - structures be freed, we would have to remove the elements - from archloaded too. */ - lia->data[cnt]->usage_count = UNDELETABLE; - } - } - - *namep = lia->name; - return lia->data[category]; -} - -void __libc_freeres_fn_section -_nl_archive_subfreeres (void) -{ - struct locale_in_archive *lia; - struct archmapped *am; - - /* Toss out our cached locales. */ - lia = archloaded; - while (lia != NULL) - { - int category; - struct locale_in_archive *dead = lia; - lia = lia->next; - - free (dead->name); - for (category = 0; category < __LC_LAST; ++category) - if (category != LC_ALL && dead->data[category] != NULL) - { - /* _nl_unload_locale just does this free for the archive case. */ - if (dead->data[category]->private.cleanup) - (*dead->data[category]->private.cleanup) (dead->data[category]); - - free (dead->data[category]); - } - free (dead); - } - archloaded = NULL; - - if (archmapped != NULL) - { - /* Now toss all the mapping windows, which we know nothing is using any - more because we just tossed all the locales that point into them. */ - - assert (archmapped == &headmap); - archmapped = NULL; - (void) __munmap (headmap.ptr, headmap.len); - am = headmap.next; - while (am != NULL) - { - struct archmapped *dead = am; - am = am->next; - (void) __munmap (dead->ptr, dead->len); - free (dead); - } - } -} |