| 1 | /* Functions to read locale data files. | 
|---|
| 2 | Copyright (C) 1996-2020 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 | <https://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 | /* The size of the array must be specified explicitly because some of | 
|---|
| 48 | the 'items' may be subarrays, which will cause the compiler to deduce | 
|---|
| 49 | an incorrect size from the initializer.  */ | 
|---|
| 50 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
|---|
| 51 | static const enum value_type _nl_value_type_##category     \ | 
|---|
| 52 | [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items }; | 
|---|
| 53 | #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \ | 
|---|
| 54 | [_NL_ITEM_INDEX (element)] = type, | 
|---|
| 55 | #include "categories.def" | 
|---|
| 56 | #undef DEFINE_CATEGORY | 
|---|
| 57 |  | 
|---|
| 58 | static const enum value_type *const _nl_value_types[] = | 
|---|
| 59 | { | 
|---|
| 60 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
|---|
| 61 | [category] = _nl_value_type_##category, | 
|---|
| 62 | #include "categories.def" | 
|---|
| 63 | #undef DEFINE_CATEGORY | 
|---|
| 64 | }; | 
|---|
| 65 |  | 
|---|
| 66 |  | 
|---|
| 67 | struct __locale_data * | 
|---|
| 68 | _nl_intern_locale_data (int category, const void *data, size_t datasize) | 
|---|
| 69 | { | 
|---|
| 70 | const struct | 
|---|
| 71 | { | 
|---|
| 72 | unsigned int magic; | 
|---|
| 73 | unsigned int nstrings; | 
|---|
| 74 | unsigned int strindex[0]; | 
|---|
| 75 | } *const filedata = data; | 
|---|
| 76 | struct __locale_data *newdata; | 
|---|
| 77 | size_t cnt; | 
|---|
| 78 |  | 
|---|
| 79 | if (__builtin_expect (datasize < sizeof *filedata, 0) | 
|---|
| 80 | || __builtin_expect (filedata->magic != LIMAGIC (category), 0)) | 
|---|
| 81 | { | 
|---|
| 82 | /* Bad data file.  */ | 
|---|
| 83 | __set_errno (EINVAL); | 
|---|
| 84 | return NULL; | 
|---|
| 85 | } | 
|---|
| 86 |  | 
|---|
| 87 | if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category], | 
|---|
| 88 | 0) | 
|---|
| 89 | || (__builtin_expect (sizeof *filedata | 
|---|
| 90 | + filedata->nstrings * sizeof (unsigned int) | 
|---|
| 91 | >= datasize, 0))) | 
|---|
| 92 | { | 
|---|
| 93 | /* Insufficient data.  */ | 
|---|
| 94 | __set_errno (EINVAL); | 
|---|
| 95 | return NULL; | 
|---|
| 96 | } | 
|---|
| 97 |  | 
|---|
| 98 | newdata = malloc (sizeof *newdata | 
|---|
| 99 | + filedata->nstrings * sizeof (union locale_data_value)); | 
|---|
| 100 | if (newdata == NULL) | 
|---|
| 101 | return NULL; | 
|---|
| 102 |  | 
|---|
| 103 | newdata->filedata = (void *) filedata; | 
|---|
| 104 | newdata->filesize = datasize; | 
|---|
| 105 | newdata->private.data = NULL; | 
|---|
| 106 | newdata->private.cleanup = NULL; | 
|---|
| 107 | newdata->usage_count = 0; | 
|---|
| 108 | newdata->use_translit = 0; | 
|---|
| 109 | newdata->nstrings = filedata->nstrings; | 
|---|
| 110 | for (cnt = 0; cnt < newdata->nstrings; ++cnt) | 
|---|
| 111 | { | 
|---|
| 112 | size_t idx = filedata->strindex[cnt]; | 
|---|
| 113 | if (__glibc_unlikely (idx > (size_t) newdata->filesize)) | 
|---|
| 114 | { | 
|---|
| 115 | puntdata: | 
|---|
| 116 | free (newdata); | 
|---|
| 117 | __set_errno (EINVAL); | 
|---|
| 118 | return NULL; | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|
| 121 | /* Determine the type.  There is one special case: the LC_CTYPE | 
|---|
| 122 | category can have more elements than there are in the | 
|---|
| 123 | _nl_value_type_LC_XYZ array.  There are all pointers.  */ | 
|---|
| 124 | switch (category) | 
|---|
| 125 | { | 
|---|
| 126 | #define CATTEST(cat) \ | 
|---|
| 127 | case LC_##cat:						\ | 
|---|
| 128 | if (cnt >= (sizeof (_nl_value_type_LC_##cat)		\ | 
|---|
| 129 | / sizeof (_nl_value_type_LC_##cat[0])))	\ | 
|---|
| 130 | goto puntdata;					\ | 
|---|
| 131 | break | 
|---|
| 132 | CATTEST (NUMERIC); | 
|---|
| 133 | CATTEST (TIME); | 
|---|
| 134 | CATTEST (COLLATE); | 
|---|
| 135 | CATTEST (MONETARY); | 
|---|
| 136 | CATTEST (MESSAGES); | 
|---|
| 137 | CATTEST (PAPER); | 
|---|
| 138 | CATTEST (NAME); | 
|---|
| 139 | CATTEST (ADDRESS); | 
|---|
| 140 | CATTEST (TELEPHONE); | 
|---|
| 141 | CATTEST (MEASUREMENT); | 
|---|
| 142 | CATTEST (IDENTIFICATION); | 
|---|
| 143 | default: | 
|---|
| 144 | assert (category == LC_CTYPE); | 
|---|
| 145 | break; | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | if ((category == LC_CTYPE | 
|---|
| 149 | && cnt >= (sizeof (_nl_value_type_LC_CTYPE) | 
|---|
| 150 | / sizeof (_nl_value_type_LC_CTYPE[0]))) | 
|---|
| 151 | || __builtin_expect (_nl_value_types[category][cnt] != word, 1)) | 
|---|
| 152 | newdata->values[cnt].string = newdata->filedata + idx; | 
|---|
| 153 | else | 
|---|
| 154 | { | 
|---|
| 155 | if (!LOCFILE_ALIGNED_P (idx)) | 
|---|
| 156 | goto puntdata; | 
|---|
| 157 | newdata->values[cnt].word = | 
|---|
| 158 | *((const uint32_t *) (newdata->filedata + idx)); | 
|---|
| 159 | } | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|
| 162 | return newdata; | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | void | 
|---|
| 166 | _nl_load_locale (struct loaded_l10nfile *file, int category) | 
|---|
| 167 | { | 
|---|
| 168 | int fd; | 
|---|
| 169 | void *filedata; | 
|---|
| 170 | struct stat64 st; | 
|---|
| 171 | struct __locale_data *newdata; | 
|---|
| 172 | int save_err; | 
|---|
| 173 | int alloc = ld_mapped; | 
|---|
| 174 |  | 
|---|
| 175 | file->decided = 1; | 
|---|
| 176 | file->data = NULL; | 
|---|
| 177 |  | 
|---|
| 178 | fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC); | 
|---|
| 179 | if (__builtin_expect (fd, 0) < 0) | 
|---|
| 180 | /* Cannot open the file.  */ | 
|---|
| 181 | return; | 
|---|
| 182 |  | 
|---|
| 183 | if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0) | 
|---|
| 184 | { | 
|---|
| 185 | puntfd: | 
|---|
| 186 | __close_nocancel_nostatus (fd); | 
|---|
| 187 | return; | 
|---|
| 188 | } | 
|---|
| 189 | if (__glibc_unlikely (S_ISDIR (st.st_mode))) | 
|---|
| 190 | { | 
|---|
| 191 | /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo | 
|---|
| 192 | instead.  */ | 
|---|
| 193 | char *newp; | 
|---|
| 194 | size_t filenamelen; | 
|---|
| 195 |  | 
|---|
| 196 | __close_nocancel_nostatus (fd); | 
|---|
| 197 |  | 
|---|
| 198 | filenamelen = strlen (file->filename); | 
|---|
| 199 | newp = (char *) alloca (filenamelen | 
|---|
| 200 | + 5 + _nl_category_name_sizes[category] + 1); | 
|---|
| 201 | __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen), | 
|---|
| 202 | "/SYS_", 5), _nl_category_names_get (category), | 
|---|
| 203 | _nl_category_name_sizes[category] + 1); | 
|---|
| 204 |  | 
|---|
| 205 | fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC); | 
|---|
| 206 | if (__builtin_expect (fd, 0) < 0) | 
|---|
| 207 | return; | 
|---|
| 208 |  | 
|---|
| 209 | if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0) | 
|---|
| 210 | goto puntfd; | 
|---|
| 211 | } | 
|---|
| 212 |  | 
|---|
| 213 | /* Map in the file's data.  */ | 
|---|
| 214 | save_err = errno; | 
|---|
| 215 | #ifdef _POSIX_MAPPED_FILES | 
|---|
| 216 | # ifndef MAP_COPY | 
|---|
| 217 | /* Linux seems to lack read-only copy-on-write.  */ | 
|---|
| 218 | #  define MAP_COPY MAP_PRIVATE | 
|---|
| 219 | # endif | 
|---|
| 220 | # ifndef MAP_FILE | 
|---|
| 221 | /* Some systems do not have this flag; it is superfluous.  */ | 
|---|
| 222 | #  define MAP_FILE 0 | 
|---|
| 223 | # endif | 
|---|
| 224 | filedata = __mmap ((caddr_t) 0, st.st_size, | 
|---|
| 225 | PROT_READ, MAP_FILE|MAP_COPY, fd, 0); | 
|---|
| 226 | if (__glibc_unlikely (filedata == MAP_FAILED)) | 
|---|
| 227 | { | 
|---|
| 228 | filedata = NULL; | 
|---|
| 229 | if (__builtin_expect (errno, ENOSYS) == ENOSYS) | 
|---|
| 230 | { | 
|---|
| 231 | #endif	/* _POSIX_MAPPED_FILES */ | 
|---|
| 232 | /* No mmap; allocate a buffer and read from the file.  */ | 
|---|
| 233 | alloc = ld_malloced; | 
|---|
| 234 | filedata = malloc (st.st_size); | 
|---|
| 235 | if (filedata != NULL) | 
|---|
| 236 | { | 
|---|
| 237 | off_t to_read = st.st_size; | 
|---|
| 238 | ssize_t nread; | 
|---|
| 239 | char *p = (char *) filedata; | 
|---|
| 240 | while (to_read > 0) | 
|---|
| 241 | { | 
|---|
| 242 | nread = __read_nocancel (fd, p, to_read); | 
|---|
| 243 | if (__builtin_expect (nread, 1) <= 0) | 
|---|
| 244 | { | 
|---|
| 245 | free (filedata); | 
|---|
| 246 | if (nread == 0) | 
|---|
| 247 | __set_errno (EINVAL); /* Bizarreness going on.  */ | 
|---|
| 248 | goto puntfd; | 
|---|
| 249 | } | 
|---|
| 250 | p += nread; | 
|---|
| 251 | to_read -= nread; | 
|---|
| 252 | } | 
|---|
| 253 | __set_errno (save_err); | 
|---|
| 254 | } | 
|---|
| 255 | #ifdef _POSIX_MAPPED_FILES | 
|---|
| 256 | } | 
|---|
| 257 | } | 
|---|
| 258 | #endif	/* _POSIX_MAPPED_FILES */ | 
|---|
| 259 |  | 
|---|
| 260 | /* We have mapped the data, so we no longer need the descriptor.  */ | 
|---|
| 261 | __close_nocancel_nostatus (fd); | 
|---|
| 262 |  | 
|---|
| 263 | if (__glibc_unlikely (filedata == NULL)) | 
|---|
| 264 | /* We failed to map or read the data.  */ | 
|---|
| 265 | return; | 
|---|
| 266 |  | 
|---|
| 267 | newdata = _nl_intern_locale_data (category, filedata, st.st_size); | 
|---|
| 268 | if (__glibc_unlikely (newdata == NULL)) | 
|---|
| 269 | /* Bad data.  */ | 
|---|
| 270 | { | 
|---|
| 271 | #ifdef _POSIX_MAPPED_FILES | 
|---|
| 272 | if (alloc == ld_mapped) | 
|---|
| 273 | __munmap ((caddr_t) filedata, st.st_size); | 
|---|
| 274 | #endif | 
|---|
| 275 | return; | 
|---|
| 276 | } | 
|---|
| 277 |  | 
|---|
| 278 | /* _nl_intern_locale_data leaves us these fields to initialize.  */ | 
|---|
| 279 | newdata->name = NULL;	/* This will be filled if necessary in findlocale.c. */ | 
|---|
| 280 | newdata->alloc = alloc; | 
|---|
| 281 |  | 
|---|
| 282 | file->data = newdata; | 
|---|
| 283 | } | 
|---|
| 284 |  | 
|---|
| 285 | void | 
|---|
| 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 | } | 
|---|
| 310 |  | 
|---|