Back to index

glibc  2.9
Classes | Defines | Functions | Variables
loadarchive.c File Reference
#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 <sys/mman.h>
#include <sys/stat.h>
#include <sys/param.h>
#include "localeinfo.h"
#include "locarchive.h"
#include <not-cancel.h>
#include "hashval.h"

Go to the source code of this file.

Classes

struct  archmapped
struct  locale_in_archive
struct  range

Defines

#define compute_hashval   static inline compute_hashval
#define hashval_t   uint32_t
#define ARCHIVE_MAPPING_WINDOW   (2 * 1024 * 1024)
#define MAP_COPY   MAP_PRIVATE
#define MAP_FILE   0

Functions

static int rangecmp (const void *p1, const void *p2)
static off_t calculate_head_size (const struct locarhead *h)
struct locale_data
*internal_function 
_nl_load_locale_from_archive (int category, const char **namep)
void __libc_freeres_fn_section _nl_archive_subfreeres (void)

Variables

static const char archfname [] = "/locale-archive"
static struct archmappedarchmapped
static struct stat64
static struct locale_in_archivearchloaded

Class Documentation

struct archmapped

Definition at line 66 of file loadarchive.c.

Collaboration diagram for archmapped:
Class Members
uint32_t from
uint32_t len
struct archmapped * next
void * ptr
struct locale_in_archive

Definition at line 84 of file loadarchive.c.

Collaboration diagram for locale_in_archive:
Class Members
struct locale_data * data
char * name
struct locale_in_archive * next
struct range

Definition at line 94 of file loadarchive.c.

Class Members
int category
uint32_t from
uint32_t len
void * result

Define Documentation

#define ARCHIVE_MAPPING_WINDOW   (2 * 1024 * 1024)

Definition at line 49 of file loadarchive.c.

#define compute_hashval   static inline compute_hashval

Definition at line 38 of file loadarchive.c.

#define hashval_t   uint32_t

Definition at line 39 of file loadarchive.c.

#define MAP_COPY   MAP_PRIVATE

Definition at line 58 of file loadarchive.c.

#define MAP_FILE   0

Definition at line 62 of file loadarchive.c.


Function Documentation

Definition at line 498 of file loadarchive.c.

{
  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)
         {
           /* _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);
       }
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:

struct locale_data* internal_function _nl_load_locale_from_archive ( int  category,
const char **  namep 
) [read]

Definition at line 134 of file loadarchive.c.

{
  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);
      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 (__builtin_expect (headmap.ptr == NULL, 0))
    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);

  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);
             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 (__builtin_expect (lia == NULL, 0))
    return NULL;

  lia->name = strdup (*namep);
  if (__builtin_expect (lia->name == NULL, 0))
    {
      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 (__builtin_expect (lia->data[cnt] != NULL, 1))
         {
           /* _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];
}

Here is the call graph for this function:

Here is the caller graph for this function:

static off_t calculate_head_size ( const struct locarhead h) [inline, static]

Definition at line 116 of file loadarchive.c.

{
  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));
}

Here is the caller graph for this function:

static int rangecmp ( const void *  p1,
const void *  p2 
) [static]

Definition at line 103 of file loadarchive.c.

{
  return ((struct range *) p1)->from - ((struct range *) p2)->from;
}

Here is the caller graph for this function:


Variable Documentation

const char archfname[] = "/locale-archive" [static]

Definition at line 45 of file loadarchive.c.

struct locale_in_archive* archloaded [static]

Definition at line 90 of file loadarchive.c.

static struct archmapped [static]

Definition at line 73 of file loadarchive.c.

struct stat64 [static]

Definition at line 81 of file loadarchive.c.