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