summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--locale/loadarchive.c78
1 files changed, 42 insertions, 36 deletions
diff --git a/locale/loadarchive.c b/locale/loadarchive.c
index 59ac916e29..845b9b0774 100644
--- a/locale/loadarchive.c
+++ b/locale/loadarchive.c
@@ -41,6 +41,10 @@
 /* Name of the locale archive file.  */
 static const char archfname[] = LOCALEDIR "/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)
+
 
 /* Record of contiguous pages already mapped from the locale archive.  */
 struct archmapped
@@ -174,6 +178,9 @@ _nl_load_locale_from_archive (int category, const char **namep)
   /* 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.  */
@@ -193,49 +200,48 @@ _nl_load_locale_from_archive (int category, const char **namep)
 	  return NULL;
 	}
 
-      if (sizeof (void *) > 4)
-	{
-	  /* We will just map the whole file, what the hell.  */
-	  void *result = __mmap64 (NULL, archive_stat.st_size,
-				   PROT_READ, MAP_SHARED, fd, 0);
-	  if (result == MAP_FAILED)
-	    goto close_and_out;
-	  /* Check whether the file is large enough for the sizes given in the
-	     header.  */
-	  if (calculate_head_size ((const struct locarhead *) result)
-	      > archive_stat.st_size)
-	    {
-	      (void) __munmap (result, archive_stat.st_size);
-	      goto close_and_out;
-	    }
-	  __close (fd);
-	  fd = -1;
 
-	  headmap.ptr = result;
-	  /* headmap.from already initialized to zero.  */
-	  headmap.len = archive_stat.st_size;
-	}
-      else
-	{
-	  struct locarhead head;
-	  off_t head_size;
-	  void *result;
+      /* 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.  */
 
-	  if (TEMP_FAILURE_RETRY (__read (fd, &head, sizeof (head)))
-	      != sizeof (head))
-	    goto close_and_out;
-	  head_size = calculate_head_size (&head);
-	  if (head_size > archive_stat.st_size)
+      mapsize = (sizeof (void *) > 4 ? archive_stat.st_size
+		 : MAX (archive_stat.st_size, ARCHIVE_MAPPING_WINDOW));
+
+      result = __mmap64 (NULL, mapsize, PROT_READ, MAP_SHARED, 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;
-	  result = __mmap64 (NULL, head_size, PROT_READ, MAP_SHARED, fd, 0);
+
+	  /* Freakishly long header.  */
+	  /* XXX could use mremap when available */
+	  mapsize = (headsize + ps - 1) & ~(ps - 1);
+	  result = __mmap64 (NULL, mapsize, PROT_READ, MAP_SHARED, fd, 0);
 	  if (result == MAP_FAILED)
 	    goto close_and_out;
+	}
 
-	  /* Record that we have mapped the initial pages of the file.  */
-	  headmap.ptr = result;
-	  headmap.len = MIN ((head_size + ps - 1) & ~(ps - 1),
-			     archive_stat.st_size);
+      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 (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.  */