| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Functions to read locale data files. | 
 | 2 |    Copyright (C) 1996-2016 Free Software Foundation, Inc. | 
 | 3 |    This file is part of the GNU C Library. | 
 | 4 |    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. | 
 | 5 |  | 
 | 6 |    The GNU C Library is free software; you can redistribute it and/or | 
 | 7 |    modify it under the terms of the GNU Lesser General Public | 
 | 8 |    License as published by the Free Software Foundation; either | 
 | 9 |    version 2.1 of the License, or (at your option) any later version. | 
 | 10 |  | 
 | 11 |    The GNU C Library is distributed in the hope that it will be useful, | 
 | 12 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 13 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | 14 |    Lesser General Public License for more details. | 
 | 15 |  | 
 | 16 |    You should have received a copy of the GNU Lesser General Public | 
 | 17 |    License along with the GNU C Library; if not, see | 
 | 18 |    <http://www.gnu.org/licenses/>.  */ | 
 | 19 |  | 
 | 20 | #include <assert.h> | 
 | 21 | #include <errno.h> | 
 | 22 | #include <fcntl.h> | 
 | 23 | #include <locale.h> | 
 | 24 | #include <stdlib.h> | 
 | 25 | #include <string.h> | 
 | 26 | #include <unistd.h> | 
 | 27 | #ifdef _POSIX_MAPPED_FILES | 
 | 28 | # include <sys/mman.h> | 
 | 29 | #endif | 
 | 30 | #include <sys/stat.h> | 
 | 31 |  | 
 | 32 | #include <not-cancel.h> | 
 | 33 | #include "localeinfo.h" | 
 | 34 |  | 
 | 35 |  | 
 | 36 | static const size_t _nl_category_num_items[] = | 
 | 37 | { | 
 | 38 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 | 39 |   [category] = _NL_ITEM_INDEX (_NL_NUM_##category), | 
 | 40 | #include "categories.def" | 
 | 41 | #undef	DEFINE_CATEGORY | 
 | 42 | }; | 
 | 43 |  | 
 | 44 |  | 
 | 45 | #define NO_PAREN(arg, rest...) arg, ##rest | 
 | 46 |  | 
 | 47 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 | 48 | static const enum value_type _nl_value_type_##category[] = { NO_PAREN items }; | 
 | 49 | #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \ | 
 | 50 |   [_NL_ITEM_INDEX (element)] = type, | 
 | 51 | #include "categories.def" | 
 | 52 | #undef DEFINE_CATEGORY | 
 | 53 |  | 
 | 54 | static const enum value_type *const _nl_value_types[] = | 
 | 55 | { | 
 | 56 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 | 57 |   [category] = _nl_value_type_##category, | 
 | 58 | #include "categories.def" | 
 | 59 | #undef DEFINE_CATEGORY | 
 | 60 | }; | 
 | 61 |  | 
 | 62 |  | 
 | 63 | struct __locale_data * | 
 | 64 | internal_function | 
 | 65 | _nl_intern_locale_data (int category, const void *data, size_t datasize) | 
 | 66 | { | 
 | 67 |   const struct | 
 | 68 |     { | 
 | 69 |       unsigned int magic; | 
 | 70 |       unsigned int nstrings; | 
 | 71 |       unsigned int strindex[0]; | 
 | 72 |     } *const filedata = data; | 
 | 73 |   struct __locale_data *newdata; | 
 | 74 |   size_t cnt; | 
 | 75 |  | 
 | 76 |   if (__builtin_expect (datasize < sizeof *filedata, 0) | 
 | 77 |       || __builtin_expect (filedata->magic != LIMAGIC (category), 0)) | 
 | 78 |     { | 
 | 79 |       /* Bad data file.  */ | 
 | 80 |       __set_errno (EINVAL); | 
 | 81 |       return NULL; | 
 | 82 |     } | 
 | 83 |  | 
 | 84 |   if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category], | 
 | 85 | 			0) | 
 | 86 |       || (__builtin_expect (sizeof *filedata | 
 | 87 | 			    + filedata->nstrings * sizeof (unsigned int) | 
 | 88 | 			    >= datasize, 0))) | 
 | 89 |     { | 
 | 90 |       /* Insufficient data.  */ | 
 | 91 |       __set_errno (EINVAL); | 
 | 92 |       return NULL; | 
 | 93 |     } | 
 | 94 |  | 
 | 95 |   newdata = malloc (sizeof *newdata | 
 | 96 | 		    + filedata->nstrings * sizeof (union locale_data_value)); | 
 | 97 |   if (newdata == NULL) | 
 | 98 |     return NULL; | 
 | 99 |  | 
 | 100 |   newdata->filedata = (void *) filedata; | 
 | 101 |   newdata->filesize = datasize; | 
 | 102 |   newdata->private.data = NULL; | 
 | 103 |   newdata->private.cleanup = NULL; | 
 | 104 |   newdata->usage_count = 0; | 
 | 105 |   newdata->use_translit = 0; | 
 | 106 |   newdata->nstrings = filedata->nstrings; | 
 | 107 |   for (cnt = 0; cnt < newdata->nstrings; ++cnt) | 
 | 108 |     { | 
 | 109 |       size_t idx = filedata->strindex[cnt]; | 
 | 110 |       if (__glibc_unlikely (idx > (size_t) newdata->filesize)) | 
 | 111 | 	{ | 
 | 112 | 	puntdata: | 
 | 113 | 	  free (newdata); | 
 | 114 | 	  __set_errno (EINVAL); | 
 | 115 | 	  return NULL; | 
 | 116 | 	} | 
 | 117 |  | 
 | 118 |       /* Determine the type.  There is one special case: the LC_CTYPE | 
 | 119 | 	 category can have more elements than there are in the | 
 | 120 | 	 _nl_value_type_LC_XYZ array.  There are all pointers.  */ | 
 | 121 |       switch (category) | 
 | 122 | 	{ | 
 | 123 | #define CATTEST(cat) \ | 
 | 124 | 	case LC_##cat:						\ | 
 | 125 | 	  if (cnt >= (sizeof (_nl_value_type_LC_##cat)		\ | 
 | 126 | 		      / sizeof (_nl_value_type_LC_##cat[0])))	\ | 
 | 127 | 	    goto puntdata;					\ | 
 | 128 | 	  break | 
 | 129 | 	  CATTEST (NUMERIC); | 
 | 130 | 	  CATTEST (TIME); | 
 | 131 | 	  CATTEST (COLLATE); | 
 | 132 | 	  CATTEST (MONETARY); | 
 | 133 | 	  CATTEST (MESSAGES); | 
 | 134 | 	  CATTEST (PAPER); | 
 | 135 | 	  CATTEST (NAME); | 
 | 136 | 	  CATTEST (ADDRESS); | 
 | 137 | 	  CATTEST (TELEPHONE); | 
 | 138 | 	  CATTEST (MEASUREMENT); | 
 | 139 | 	  CATTEST (IDENTIFICATION); | 
 | 140 | 	default: | 
 | 141 | 	  assert (category == LC_CTYPE); | 
 | 142 | 	  break; | 
 | 143 | 	} | 
 | 144 |  | 
 | 145 |       if ((category == LC_CTYPE | 
 | 146 | 	   && cnt >= (sizeof (_nl_value_type_LC_CTYPE) | 
 | 147 | 		      / sizeof (_nl_value_type_LC_CTYPE[0]))) | 
 | 148 | 	  || __builtin_expect (_nl_value_types[category][cnt] != word, 1)) | 
 | 149 | 	newdata->values[cnt].string = newdata->filedata + idx; | 
 | 150 |       else | 
 | 151 | 	{ | 
 | 152 | 	  if (!LOCFILE_ALIGNED_P (idx)) | 
 | 153 | 	    goto puntdata; | 
 | 154 | 	  newdata->values[cnt].word = | 
 | 155 | 	    *((const u_int32_t *) (newdata->filedata + idx)); | 
 | 156 | 	} | 
 | 157 |     } | 
 | 158 |  | 
 | 159 |   return newdata; | 
 | 160 | } | 
 | 161 |  | 
 | 162 | void | 
 | 163 | internal_function | 
 | 164 | _nl_load_locale (struct loaded_l10nfile *file, int category) | 
 | 165 | { | 
 | 166 |   int fd; | 
 | 167 |   void *filedata; | 
 | 168 |   struct stat64 st; | 
 | 169 |   struct __locale_data *newdata; | 
 | 170 |   int save_err; | 
 | 171 |   int alloc = ld_mapped; | 
 | 172 |  | 
 | 173 |   file->decided = 1; | 
 | 174 |   file->data = NULL; | 
 | 175 |  | 
 | 176 |   fd = open_not_cancel_2 (file->filename, O_RDONLY | O_CLOEXEC); | 
 | 177 |   if (__builtin_expect (fd, 0) < 0) | 
 | 178 |     /* Cannot open the file.  */ | 
 | 179 |     return; | 
 | 180 |  | 
 | 181 |   if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0) | 
 | 182 |     { | 
 | 183 |     puntfd: | 
 | 184 |       close_not_cancel_no_status (fd); | 
 | 185 |       return; | 
 | 186 |     } | 
 | 187 |   if (__glibc_unlikely (S_ISDIR (st.st_mode))) | 
 | 188 |     { | 
 | 189 |       /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo | 
 | 190 | 	   instead.  */ | 
 | 191 |       char *newp; | 
 | 192 |       size_t filenamelen; | 
 | 193 |  | 
 | 194 |       close_not_cancel_no_status (fd); | 
 | 195 |  | 
 | 196 |       filenamelen = strlen (file->filename); | 
 | 197 |       newp = (char *) alloca (filenamelen | 
 | 198 | 			      + 5 + _nl_category_name_sizes[category] + 1); | 
 | 199 |       __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen), | 
 | 200 | 			    "/SYS_", 5), | 
 | 201 | 		 _nl_category_names.str + _nl_category_name_idxs[category], | 
 | 202 | 		 _nl_category_name_sizes[category] + 1); | 
 | 203 |  | 
 | 204 |       fd = open_not_cancel_2 (newp, O_RDONLY | O_CLOEXEC); | 
 | 205 |       if (__builtin_expect (fd, 0) < 0) | 
 | 206 | 	return; | 
 | 207 |  | 
 | 208 |       if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0) | 
 | 209 | 	goto puntfd; | 
 | 210 |     } | 
 | 211 |  | 
 | 212 |   /* Map in the file's data.  */ | 
 | 213 |   save_err = errno; | 
 | 214 | #ifdef _POSIX_MAPPED_FILES | 
 | 215 | # ifndef MAP_COPY | 
 | 216 |   /* Linux seems to lack read-only copy-on-write.  */ | 
 | 217 | #  define MAP_COPY MAP_PRIVATE | 
 | 218 | # endif | 
 | 219 | # ifndef MAP_FILE | 
 | 220 |   /* Some systems do not have this flag; it is superfluous.  */ | 
 | 221 | #  define MAP_FILE 0 | 
 | 222 | # endif | 
 | 223 |   filedata = __mmap ((caddr_t) 0, st.st_size, | 
 | 224 | 		     PROT_READ, MAP_FILE|MAP_COPY, fd, 0); | 
 | 225 |   if (__glibc_unlikely (filedata == MAP_FAILED)) | 
 | 226 |     { | 
 | 227 |       filedata = NULL; | 
 | 228 |       if (__builtin_expect (errno, ENOSYS) == ENOSYS) | 
 | 229 | 	{ | 
 | 230 | #endif	/* _POSIX_MAPPED_FILES */ | 
 | 231 | 	  /* No mmap; allocate a buffer and read from the file.  */ | 
 | 232 | 	  alloc = ld_malloced; | 
 | 233 | 	  filedata = malloc (st.st_size); | 
 | 234 | 	  if (filedata != NULL) | 
 | 235 | 	    { | 
 | 236 | 	      off_t to_read = st.st_size; | 
 | 237 | 	      ssize_t nread; | 
 | 238 | 	      char *p = (char *) filedata; | 
 | 239 | 	      while (to_read > 0) | 
 | 240 | 		{ | 
 | 241 | 		  nread = read_not_cancel (fd, p, to_read); | 
 | 242 | 		  if (__builtin_expect (nread, 1) <= 0) | 
 | 243 | 		    { | 
 | 244 | 		      free (filedata); | 
 | 245 | 		      if (nread == 0) | 
 | 246 | 			__set_errno (EINVAL); /* Bizarreness going on.  */ | 
 | 247 | 		      goto puntfd; | 
 | 248 | 		    } | 
 | 249 | 		  p += nread; | 
 | 250 | 		  to_read -= nread; | 
 | 251 | 		} | 
 | 252 | 	      __set_errno (save_err); | 
 | 253 | 	    } | 
 | 254 | #ifdef _POSIX_MAPPED_FILES | 
 | 255 | 	} | 
 | 256 |     } | 
 | 257 | #endif	/* _POSIX_MAPPED_FILES */ | 
 | 258 |  | 
 | 259 |   /* We have mapped the data, so we no longer need the descriptor.  */ | 
 | 260 |   close_not_cancel_no_status (fd); | 
 | 261 |  | 
 | 262 |   if (__glibc_unlikely (filedata == NULL)) | 
 | 263 |     /* We failed to map or read the data.  */ | 
 | 264 |     return; | 
 | 265 |  | 
 | 266 |   newdata = _nl_intern_locale_data (category, filedata, st.st_size); | 
 | 267 |   if (__glibc_unlikely (newdata == NULL)) | 
 | 268 |     /* Bad data.  */ | 
 | 269 |     { | 
 | 270 | #ifdef _POSIX_MAPPED_FILES | 
 | 271 |       if (alloc == ld_mapped) | 
 | 272 | 	__munmap ((caddr_t) filedata, st.st_size); | 
 | 273 | #endif | 
 | 274 |       return; | 
 | 275 |     } | 
 | 276 |  | 
 | 277 |   /* _nl_intern_locale_data leaves us these fields to initialize.  */ | 
 | 278 |   newdata->name = NULL;	/* This will be filled if necessary in findlocale.c. */ | 
 | 279 |   newdata->alloc = alloc; | 
 | 280 |  | 
 | 281 |   file->data = newdata; | 
 | 282 | } | 
 | 283 |  | 
 | 284 | void | 
 | 285 | internal_function | 
 | 286 | _nl_unload_locale (struct __locale_data *locale) | 
 | 287 | { | 
 | 288 |   if (locale->private.cleanup) | 
 | 289 |     (*locale->private.cleanup) (locale); | 
 | 290 |  | 
 | 291 |   switch (__builtin_expect (locale->alloc, ld_mapped)) | 
 | 292 |     { | 
 | 293 |     case ld_malloced: | 
 | 294 |       free ((void *) locale->filedata); | 
 | 295 |       break; | 
 | 296 |     case ld_mapped: | 
 | 297 | #ifdef _POSIX_MAPPED_FILES | 
 | 298 |       __munmap ((caddr_t) locale->filedata, locale->filesize); | 
 | 299 |       break; | 
 | 300 | #endif | 
 | 301 |     case ld_archive:		/* Nothing to do.  */ | 
 | 302 |       break; | 
 | 303 |     } | 
 | 304 |  | 
 | 305 |   if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive) | 
 | 306 |     free ((char *) locale->name); | 
 | 307 |  | 
 | 308 |   free (locale); | 
 | 309 | } |