Blame locale/loadlocale.c

Packit 6c4009
/* Functions to read locale data files.
Packit 6c4009
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
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 <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
# include <sys/mman.h>
Packit 6c4009
#endif
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
Packit 6c4009
#include <not-cancel.h>
Packit 6c4009
#include "localeinfo.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
static const size_t _nl_category_num_items[] =
Packit 6c4009
{
Packit 6c4009
#define DEFINE_CATEGORY(category, category_name, items, a) \
Packit 6c4009
  [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
Packit 6c4009
#include "categories.def"
Packit 6c4009
#undef	DEFINE_CATEGORY
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
#define NO_PAREN(arg, rest...) arg, ##rest
Packit 6c4009
Packit 6c4009
/* The size of the array must be specified explicitly because some of
Packit 6c4009
   the 'items' may be subarrays, which will cause the compiler to deduce
Packit 6c4009
   an incorrect size from the initializer.  */
Packit 6c4009
#define DEFINE_CATEGORY(category, category_name, items, a) \
Packit 6c4009
static const enum value_type _nl_value_type_##category     \
Packit 6c4009
  [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items };
Packit 6c4009
#define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
Packit 6c4009
  [_NL_ITEM_INDEX (element)] = type,
Packit 6c4009
#include "categories.def"
Packit 6c4009
#undef DEFINE_CATEGORY
Packit 6c4009
Packit 6c4009
static const enum value_type *const _nl_value_types[] =
Packit 6c4009
{
Packit 6c4009
#define DEFINE_CATEGORY(category, category_name, items, a) \
Packit 6c4009
  [category] = _nl_value_type_##category,
Packit 6c4009
#include "categories.def"
Packit 6c4009
#undef DEFINE_CATEGORY
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct __locale_data *
Packit 6c4009
_nl_intern_locale_data (int category, const void *data, size_t datasize)
Packit 6c4009
{
Packit 6c4009
  const struct
Packit 6c4009
    {
Packit 6c4009
      unsigned int magic;
Packit 6c4009
      unsigned int nstrings;
Packit 6c4009
      unsigned int strindex[0];
Packit 6c4009
    } *const filedata = data;
Packit 6c4009
  struct __locale_data *newdata;
Packit 6c4009
  size_t cnt;
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (datasize < sizeof *filedata, 0)
Packit 6c4009
      || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
Packit 6c4009
    {
Packit 6c4009
      /* Bad data file.  */
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
Packit 6c4009
			0)
Packit 6c4009
      || (__builtin_expect (sizeof *filedata
Packit 6c4009
			    + filedata->nstrings * sizeof (unsigned int)
Packit 6c4009
			    >= datasize, 0)))
Packit 6c4009
    {
Packit 6c4009
      /* Insufficient data.  */
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  newdata = malloc (sizeof *newdata
Packit 6c4009
		    + filedata->nstrings * sizeof (union locale_data_value));
Packit 6c4009
  if (newdata == NULL)
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  newdata->filedata = (void *) filedata;
Packit 6c4009
  newdata->filesize = datasize;
Packit 6c4009
  newdata->private.data = NULL;
Packit 6c4009
  newdata->private.cleanup = NULL;
Packit 6c4009
  newdata->usage_count = 0;
Packit 6c4009
  newdata->use_translit = 0;
Packit 6c4009
  newdata->nstrings = filedata->nstrings;
Packit 6c4009
  for (cnt = 0; cnt < newdata->nstrings; ++cnt)
Packit 6c4009
    {
Packit 6c4009
      size_t idx = filedata->strindex[cnt];
Packit 6c4009
      if (__glibc_unlikely (idx > (size_t) newdata->filesize))
Packit 6c4009
	{
Packit 6c4009
	puntdata:
Packit 6c4009
	  free (newdata);
Packit 6c4009
	  __set_errno (EINVAL);
Packit 6c4009
	  return NULL;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Determine the type.  There is one special case: the LC_CTYPE
Packit 6c4009
	 category can have more elements than there are in the
Packit 6c4009
	 _nl_value_type_LC_XYZ array.  There are all pointers.  */
Packit 6c4009
      switch (category)
Packit 6c4009
	{
Packit 6c4009
#define CATTEST(cat) \
Packit 6c4009
	case LC_##cat:						\
Packit 6c4009
	  if (cnt >= (sizeof (_nl_value_type_LC_##cat)		\
Packit 6c4009
		      / sizeof (_nl_value_type_LC_##cat[0])))	\
Packit 6c4009
	    goto puntdata;					\
Packit 6c4009
	  break
Packit 6c4009
	  CATTEST (NUMERIC);
Packit 6c4009
	  CATTEST (TIME);
Packit 6c4009
	  CATTEST (COLLATE);
Packit 6c4009
	  CATTEST (MONETARY);
Packit 6c4009
	  CATTEST (MESSAGES);
Packit 6c4009
	  CATTEST (PAPER);
Packit 6c4009
	  CATTEST (NAME);
Packit 6c4009
	  CATTEST (ADDRESS);
Packit 6c4009
	  CATTEST (TELEPHONE);
Packit 6c4009
	  CATTEST (MEASUREMENT);
Packit 6c4009
	  CATTEST (IDENTIFICATION);
Packit 6c4009
	default:
Packit 6c4009
	  assert (category == LC_CTYPE);
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if ((category == LC_CTYPE
Packit 6c4009
	   && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
Packit 6c4009
		      / sizeof (_nl_value_type_LC_CTYPE[0])))
Packit 6c4009
	  || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
Packit 6c4009
	newdata->values[cnt].string = newdata->filedata + idx;
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  if (!LOCFILE_ALIGNED_P (idx))
Packit 6c4009
	    goto puntdata;
Packit 6c4009
	  newdata->values[cnt].word =
Packit 6c4009
	    *((const uint32_t *) (newdata->filedata + idx));
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return newdata;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_nl_load_locale (struct loaded_l10nfile *file, int category)
Packit 6c4009
{
Packit 6c4009
  int fd;
Packit 6c4009
  void *filedata;
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  struct __locale_data *newdata;
Packit 6c4009
  int save_err;
Packit 6c4009
  int alloc = ld_mapped;
Packit 6c4009
Packit 6c4009
  file->data = NULL;
Packit 6c4009
Packit 6c4009
  fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
Packit 6c4009
  if (__builtin_expect (fd, 0) < 0)
Packit 6c4009
    /* Cannot open the file.  */
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
Packit 6c4009
    {
Packit 6c4009
    puntfd:
Packit 6c4009
      __close_nocancel_nostatus (fd);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
  if (__glibc_unlikely (S_ISDIR (st.st_mode)))
Packit 6c4009
    {
Packit 6c4009
      /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
Packit 6c4009
	   instead.  */
Packit 6c4009
      char *newp;
Packit 6c4009
      size_t filenamelen;
Packit 6c4009
Packit 6c4009
      __close_nocancel_nostatus (fd);
Packit 6c4009
Packit 6c4009
      filenamelen = strlen (file->filename);
Packit 6c4009
      newp = (char *) alloca (filenamelen
Packit 6c4009
			      + 5 + _nl_category_name_sizes[category] + 1);
Packit 6c4009
      __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
Packit 6c4009
			    "/SYS_", 5),
Packit 6c4009
		 _nl_category_names.str + _nl_category_name_idxs[category],
Packit 6c4009
		 _nl_category_name_sizes[category] + 1);
Packit 6c4009
Packit 6c4009
      fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
Packit 6c4009
      if (__builtin_expect (fd, 0) < 0)
Packit 6c4009
	return;
Packit 6c4009
Packit 6c4009
      if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
Packit 6c4009
	goto puntfd;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Map in the file's data.  */
Packit 6c4009
  save_err = errno;
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
# ifndef MAP_COPY
Packit 6c4009
  /* Linux seems to lack read-only copy-on-write.  */
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
  filedata = __mmap ((caddr_t) 0, st.st_size,
Packit 6c4009
		     PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
Packit 6c4009
  if (__glibc_unlikely (filedata == MAP_FAILED))
Packit 6c4009
    {
Packit 6c4009
      filedata = NULL;
Packit 6c4009
      if (__builtin_expect (errno, ENOSYS) == ENOSYS)
Packit 6c4009
	{
Packit 6c4009
#endif	/* _POSIX_MAPPED_FILES */
Packit 6c4009
	  /* No mmap; allocate a buffer and read from the file.  */
Packit 6c4009
	  alloc = ld_malloced;
Packit 6c4009
	  filedata = malloc (st.st_size);
Packit 6c4009
	  if (filedata != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      off_t to_read = st.st_size;
Packit 6c4009
	      ssize_t nread;
Packit 6c4009
	      char *p = (char *) filedata;
Packit 6c4009
	      while (to_read > 0)
Packit 6c4009
		{
Packit 6c4009
		  nread = __read_nocancel (fd, p, to_read);
Packit 6c4009
		  if (__builtin_expect (nread, 1) <= 0)
Packit 6c4009
		    {
Packit 6c4009
		      free (filedata);
Packit 6c4009
		      if (nread == 0)
Packit 6c4009
			__set_errno (EINVAL); /* Bizarreness going on.  */
Packit 6c4009
		      goto puntfd;
Packit 6c4009
		    }
Packit 6c4009
		  p += nread;
Packit 6c4009
		  to_read -= nread;
Packit 6c4009
		}
Packit 6c4009
	      __set_errno (save_err);
Packit 6c4009
	    }
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
#endif	/* _POSIX_MAPPED_FILES */
Packit 6c4009
Packit 6c4009
  /* We have mapped the data, so we no longer need the descriptor.  */
Packit 6c4009
  __close_nocancel_nostatus (fd);
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (filedata == NULL))
Packit 6c4009
    /* We failed to map or read the data.  */
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  newdata = _nl_intern_locale_data (category, filedata, st.st_size);
Packit 6c4009
  if (__glibc_unlikely (newdata == NULL))
Packit 6c4009
    /* Bad data.  */
Packit 6c4009
    {
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
      if (alloc == ld_mapped)
Packit 6c4009
	__munmap ((caddr_t) filedata, st.st_size);
Packit 6c4009
#endif
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* _nl_intern_locale_data leaves us these fields to initialize.  */
Packit 6c4009
  newdata->name = NULL;	/* This will be filled if necessary in findlocale.c. */
Packit 6c4009
  newdata->alloc = alloc;
Packit 6c4009
Packit 6c4009
  file->data = newdata;
Packit Service c89dc5
  file->decided = 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_nl_unload_locale (struct __locale_data *locale)
Packit 6c4009
{
Packit 6c4009
  if (locale->private.cleanup)
Packit 6c4009
    (*locale->private.cleanup) (locale);
Packit 6c4009
Packit 6c4009
  switch (__builtin_expect (locale->alloc, ld_mapped))
Packit 6c4009
    {
Packit 6c4009
    case ld_malloced:
Packit 6c4009
      free ((void *) locale->filedata);
Packit 6c4009
      break;
Packit 6c4009
    case ld_mapped:
Packit 6c4009
#ifdef _POSIX_MAPPED_FILES
Packit 6c4009
      __munmap ((caddr_t) locale->filedata, locale->filesize);
Packit 6c4009
      break;
Packit 6c4009
#endif
Packit 6c4009
    case ld_archive:		/* Nothing to do.  */
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
Packit 6c4009
    free ((char *) locale->name);
Packit 6c4009
Packit 6c4009
  free (locale);
Packit 6c4009
}