]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - locale/loadarchive.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / locale / loadarchive.c
index d25334a7fd97f37c53d54b96cc83dd42e48caa2b..981f68d410fdfc1dcd51c7dda8822bd1e796efe5 100644 (file)
@@ -1,5 +1,5 @@
 /* Code to load locale data from the locale archive file.
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002-2019 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
    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, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://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[] = LOCALEDIR "/locale-archive";
+static const char archfname[] = COMPLOCALEDIR "/locale-archive";
 
 /* Size of initial mapping window, optimal if large enough to
    cover the header plus the initial locale.  */
@@ -82,7 +85,7 @@ struct locale_in_archive
 {
   struct locale_in_archive *next;
   char *name;
-  struct locale_data *data[__LC_LAST];
+  struct __locale_data *data[__LC_LAST];
 };
 static struct locale_in_archive *archloaded;
 
@@ -126,8 +129,7 @@ calculate_head_size (const struct locarhead *h)
    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
+struct __locale_data *
 _nl_load_locale_from_archive (int category, const char **namep)
 {
   const char *name = *namep;
@@ -182,9 +184,9 @@ _nl_load_locale_from_archive (int category, const char **namep)
            memcpy (__mempcpy (__mempcpy (newname, name, p - name),
                               normalized_codeset, normlen),
                    rest, restlen);
-           free ((char *) normalized_codeset);
            name = newname;
          }
+       free ((char *) normalized_codeset);
       }
   }
 
@@ -200,7 +202,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
       archmapped = &headmap;
 
       /* The archive has never been opened.  */
-      fd = __open64 (archfname, O_RDONLY);
+      fd = __open_nocancel (archfname, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
       if (fd < 0)
        /* Cannot open the archive, for whatever reason.  */
        return NULL;
@@ -209,7 +211,8 @@ _nl_load_locale_from_archive (int category, const char **namep)
        {
          /* stat failed, very strange.  */
        close_and_out:
-         __close (fd);
+         if (fd >= 0)
+           __close_nocancel_nostatus (fd);
          return NULL;
        }
 
@@ -249,7 +252,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
        {
          /* We've mapped the whole file already, so we can be
             sure we won't need this file descriptor later.  */
-         __close (fd);
+         __close_nocancel_nostatus (fd);
          fd = -1;
        }
 
@@ -259,8 +262,8 @@ _nl_load_locale_from_archive (int category, const char **namep)
     }
 
   /* If there is no archive or it cannot be loaded for some reason fail.  */
-  if (__builtin_expect (headmap.ptr == NULL, 0))
-    return NULL;
+  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.  */
@@ -270,6 +273,10 @@ _nl_load_locale_from_archive (int category, const char **namep)
   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);
 
@@ -279,7 +286,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
     {
       if (namehashtab[idx].name_offset == 0)
        /* Not found.  */
-       return NULL;
+       goto close_and_out;
 
       if (namehashtab[idx].hashval == hval
          && strcmp (name, headmap.ptr + namehashtab[idx].name_offset) == 0)
@@ -293,7 +300,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
 
   /* We found an entry.  It might be a placeholder for a removed one.  */
   if (namehashtab[idx].locrec_offset == 0)
-    return NULL;
+    goto close_and_out;
 
   locrec = (struct locrecent *) (headmap.ptr + namehashtab[idx].locrec_offset);
 
@@ -307,7 +314,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
            if (locrec->record[cnt].offset + locrec->record[cnt].len
                > headmap.len)
              /* The archive locrectab contains bogus offsets.  */
-             return NULL;
+             goto close_and_out;
            results[cnt].addr = headmap.ptr + locrec->record[cnt].offset;
            results[cnt].len = locrec->record[cnt].len;
          }
@@ -372,9 +379,9 @@ _nl_load_locale_from_archive (int category, const char **namep)
          do
            {
              to = ranges[upper].from + ranges[upper].len;
-             if (to > archive_stat.st_size)
+             if (to > (size_t) archive_stat.st_size)
                /* The archive locrectab contains bogus offsets.  */
-               return NULL;
+               goto close_and_out;
              to = (to + ps - 1) & ~(ps - 1);
 
              /* If a range is already mmaped in, stop.  */
@@ -390,7 +397,8 @@ _nl_load_locale_from_archive (int category, const char **namep)
          if (fd == -1)
            {
              struct stat64 st;
-             fd = __open64 (archfname, O_RDONLY);
+             fd = __open_nocancel (archfname,
+                                   O_RDONLY|O_LARGEFILE|O_CLOEXEC);
              if (fd == -1)
                /* Cannot open the archive, for whatever reason.  */
                return NULL;
@@ -402,21 +410,21 @@ _nl_load_locale_from_archive (int category, const char **namep)
                  || st.st_mtime != archive_stat.st_mtime
                  || st.st_dev != archive_stat.st_dev
                  || st.st_ino != archive_stat.st_ino)
-               return NULL;
+               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)
-           return NULL;
+           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);
-             return NULL;
+             goto close_and_out;
            }
 
          /* And queue it.  */
@@ -441,15 +449,20 @@ _nl_load_locale_from_archive (int category, const char **namep)
        }
     }
 
+  /* We don't need the file descriptor any longer.  */
+  if (fd >= 0)
+    __close_nocancel_nostatus (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))
+  if (__glibc_unlikely (lia == NULL))
     return NULL;
 
-  lia->name = strdup (*namep);
-  if (__builtin_expect (lia->name == NULL, 0))
+  lia->name = __strdup (*namep);
+  if (__glibc_unlikely (lia->name == NULL))
     {
       free (lia);
       return NULL;
@@ -464,11 +477,20 @@ _nl_load_locale_from_archive (int category, const char **namep)
        lia->data[cnt] = _nl_intern_locale_data (cnt,
                                                 results[cnt].addr,
                                                 results[cnt].len);
-       if (__builtin_expect (lia->data[cnt] != NULL, 1))
+       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;
          }
       }
 
@@ -476,7 +498,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
   return lia->data[category];
 }
 
-void
+void __libc_freeres_fn_section
 _nl_archive_subfreeres (void)
 {
   struct locale_in_archive *lia;
@@ -492,9 +514,14 @@ _nl_archive_subfreeres (void)
 
       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.  */
-         free (dead->data[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;