Blame locale/loadlocale.c

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