Blame locale/loadarchive.c

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 605bd9
  if (__glibc_unlikely (head->namehash_size <= 2))
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
}