about summary refs log tree commit diff
path: root/locale/loadlocale.c
diff options
context:
space:
mode:
Diffstat (limited to 'locale/loadlocale.c')
-rw-r--r--locale/loadlocale.c188
1 files changed, 107 insertions, 81 deletions
diff --git a/locale/loadlocale.c b/locale/loadlocale.c
index 53962babac..14f3773c47 100644
--- a/locale/loadlocale.c
+++ b/locale/loadlocale.c
@@ -60,21 +60,82 @@ static const enum value_type *_nl_value_types[] =
 };
 
 
-void
-_nl_load_locale (struct loaded_l10nfile *file, int category)
+struct locale_data *
+internal_function
+_nl_intern_locale_data (int category, const void *data, size_t datasize)
 {
-  int fd;
-  struct
+  const struct
     {
       unsigned int magic;
       unsigned int nstrings;
       unsigned int strindex[0];
-    } *filedata;
+    } *const filedata = data;
+  struct locale_data *newdata;
+  size_t cnt;
+
+  if (__builtin_expect (datasize < sizeof *filedata, 0)
+      || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
+    {
+      /* Bad data file.  */
+      __set_errno (EINVAL);
+      return NULL;
+    }
+
+  if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
+			0)
+      || (__builtin_expect (sizeof *filedata
+			    + filedata->nstrings * sizeof (unsigned int)
+			    >= datasize, 0)))
+    {
+      /* Insufficient data.  */
+      __set_errno (EINVAL);
+      return NULL;
+    }
+
+  newdata = malloc (sizeof *newdata
+		    + filedata->nstrings * sizeof (union locale_data_value));
+  if (newdata == NULL)
+    return NULL;
+
+  newdata->filedata = (void *) filedata;
+  newdata->filesize = datasize;
+  newdata->usage_count = 0;
+  newdata->use_translit = 0;
+  newdata->nstrings = filedata->nstrings;
+  for (cnt = 0; cnt < newdata->nstrings; ++cnt)
+    {
+      size_t idx = filedata->strindex[cnt];
+      if (__builtin_expect (idx > newdata->filesize, 0))
+	{
+	puntdata:
+	  free (newdata);
+	  __set_errno (EINVAL);
+	  return NULL;
+	}
+      if (__builtin_expect (_nl_value_types[category][cnt] == word, 0))
+	{
+	  if (idx % __alignof__ (u_int32_t) != 0)
+	    goto puntdata;
+	  newdata->values[cnt].word =
+	    *((const u_int32_t *) (newdata->filedata + idx));
+	}
+      else
+	newdata->values[cnt].string = newdata->filedata + idx;
+    }
+
+  return newdata;
+}
+
+void
+internal_function
+_nl_load_locale (struct loaded_l10nfile *file, int category)
+{
+  int fd;
+  void *filedata;
   struct stat64 st;
   struct locale_data *newdata;
   int save_err;
-  int mmaped = 1;
-  size_t cnt;
+  int alloc = ld_mapped;
 
   file->decided = 1;
   file->data = NULL;
@@ -85,7 +146,11 @@ _nl_load_locale (struct loaded_l10nfile *file, int category)
     return;
 
   if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
-    goto puntfd;
+    {
+    puntfd:
+      __close (fd);
+      return;
+    }
   if (__builtin_expect (S_ISDIR (st.st_mode), 0))
     {
       /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
@@ -122,25 +187,15 @@ _nl_load_locale (struct loaded_l10nfile *file, int category)
   /* Some systems do not have this flag; it is superfluous.  */
 #  define MAP_FILE 0
 # endif
-# ifndef MAP_INHERIT
-  /* Some systems might lack this; they lose.  */
-#  define MAP_INHERIT 0
-# endif
-  filedata = (void *) __mmap ((caddr_t) 0, st.st_size, PROT_READ,
-			      MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0);
-  if (__builtin_expect ((void *) filedata != MAP_FAILED, 1))
-    {
-      if (__builtin_expect (st.st_size < sizeof (*filedata), 0))
-	/* This cannot be a locale data file since it's too small.  */
-	goto puntfd;
-    }
-  else
+  filedata = __mmap ((caddr_t) 0, st.st_size,
+		     PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
+  if (__builtin_expect (filedata == MAP_FAILED, 0))
     {
       if (__builtin_expect (errno, ENOSYS) == ENOSYS)
 	{
 #endif	/* _POSIX_MAPPED_FILES */
 	  /* No mmap; allocate a buffer and read from the file.  */
-	  mmaped = 0;
+	  alloc = ld_malloced;
 	  filedata = malloc (st.st_size);
 	  if (filedata != NULL)
 	    {
@@ -160,87 +215,58 @@ _nl_load_locale (struct loaded_l10nfile *file, int category)
 		  p += nread;
 		  to_read -= nread;
 		}
+	      __set_errno (save_err);
 	    }
-	  else
-	    goto puntfd;
-	  __set_errno (save_err);
 #ifdef _POSIX_MAPPED_FILES
 	}
-      else
-	goto puntfd;
     }
 #endif	/* _POSIX_MAPPED_FILES */
 
-  if (__builtin_expect (filedata->magic != LIMAGIC (category), 0))
-    /* Bad data file in either byte order.  */
+  /* We have mapped the data, so we no longer need the descriptor.  */
+  __close (fd);
+
+  if (__builtin_expect (filedata == NULL, 0))
+    /* We failed to map or read the data.  */
+    return;
+
+  newdata = _nl_intern_locale_data (category, filedata, st.st_size);
+  if (__builtin_expect (newdata == NULL, 0))
+    /* Bad data.  */
     {
-    puntmap:
 #ifdef _POSIX_MAPPED_FILES
-      __munmap ((caddr_t) filedata, st.st_size);
+      if (alloc == ld_mapped)
+	__munmap ((caddr_t) filedata, st.st_size);
 #endif
-    puntfd:
-      __close (fd);
       return;
     }
 
-  if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
-			0)
-      || (__builtin_expect (sizeof *filedata
-			    + filedata->nstrings * sizeof (unsigned int)
-			    >= (size_t) st.st_size, 0)))
-    {
-      /* Insufficient data.  */
-      __set_errno (EINVAL);
-      goto puntmap;
-    }
-
-  newdata = malloc (sizeof *newdata
-		    + filedata->nstrings * sizeof (union locale_data_value));
-  if (newdata == NULL)
-    goto puntmap;
-
+  /* _nl_intern_locale_data leaves us these fields to initialize.  */
   newdata->name = NULL;	/* This will be filled if necessary in findlocale.c. */
-  newdata->filedata = (void *) filedata;
-  newdata->filesize = st.st_size;
-  newdata->mmaped = mmaped;
-  newdata->usage_count = 0;
-  newdata->use_translit = 0;
-  newdata->options = NULL;
-  newdata->nstrings = filedata->nstrings;
-  for (cnt = 0; cnt < newdata->nstrings; ++cnt)
-    {
-      off_t idx = filedata->strindex[cnt];
-      if (__builtin_expect (idx > newdata->filesize, 0))
-	{
-	  free (newdata);
-	  __set_errno (EINVAL);
-	  goto puntmap;
-	}
-      if (__builtin_expect (_nl_value_types[category][cnt] == word, 0))
-	{
-	  assert (idx % __alignof__ (u_int32_t) == 0);
-	  newdata->values[cnt].word =
-	    *((u_int32_t *) (newdata->filedata + idx));
-	}
-      else
-	newdata->values[cnt].string = newdata->filedata + idx;
-    }
+  newdata->alloc = alloc;
 
-  __close (fd);
   file->data = newdata;
 }
 
 void
+internal_function
 _nl_unload_locale (struct locale_data *locale)
 {
+  switch (__builtin_expect (locale->alloc, ld_mapped))
+    {
+    case ld_malloced:
+      free ((void *) locale->filedata);
+      break;
+    case ld_mapped:
 #ifdef _POSIX_MAPPED_FILES
-  if (__builtin_expect (locale->mmaped, 1))
-    __munmap ((caddr_t) locale->filedata, locale->filesize);
-  else
+      __munmap ((caddr_t) locale->filedata, locale->filesize);
+      break;
 #endif
-    free ((void *) locale->filedata);
+    case ld_archive:		/* Nothing to do.  */
+      break;
+    }
+
+  if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
+    free ((char *) locale->name);
 
-  free ((char *) locale->options);
-  free ((char *) locale->name);
   free (locale);
 }