1/* Functions to read locale data files.
2 Copyright (C) 1996-2018 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
36static 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) \
51static 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
58static 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
67struct __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
165void
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),
203 _nl_category_names.str + _nl_category_name_idxs[category],
204 _nl_category_name_sizes[category] + 1);
205
206 fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
207 if (__builtin_expect (fd, 0) < 0)
208 return;
209
210 if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
211 goto puntfd;
212 }
213
214 /* Map in the file's data. */
215 save_err = errno;
216#ifdef _POSIX_MAPPED_FILES
217# ifndef MAP_COPY
218 /* Linux seems to lack read-only copy-on-write. */
219# define MAP_COPY MAP_PRIVATE
220# endif
221# ifndef MAP_FILE
222 /* Some systems do not have this flag; it is superfluous. */
223# define MAP_FILE 0
224# endif
225 filedata = __mmap ((caddr_t) 0, st.st_size,
226 PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
227 if (__glibc_unlikely (filedata == MAP_FAILED))
228 {
229 filedata = NULL;
230 if (__builtin_expect (errno, ENOSYS) == ENOSYS)
231 {
232#endif /* _POSIX_MAPPED_FILES */
233 /* No mmap; allocate a buffer and read from the file. */
234 alloc = ld_malloced;
235 filedata = malloc (st.st_size);
236 if (filedata != NULL)
237 {
238 off_t to_read = st.st_size;
239 ssize_t nread;
240 char *p = (char *) filedata;
241 while (to_read > 0)
242 {
243 nread = __read_nocancel (fd, p, to_read);
244 if (__builtin_expect (nread, 1) <= 0)
245 {
246 free (filedata);
247 if (nread == 0)
248 __set_errno (EINVAL); /* Bizarreness going on. */
249 goto puntfd;
250 }
251 p += nread;
252 to_read -= nread;
253 }
254 __set_errno (save_err);
255 }
256#ifdef _POSIX_MAPPED_FILES
257 }
258 }
259#endif /* _POSIX_MAPPED_FILES */
260
261 /* We have mapped the data, so we no longer need the descriptor. */
262 __close_nocancel_nostatus (fd);
263
264 if (__glibc_unlikely (filedata == NULL))
265 /* We failed to map or read the data. */
266 return;
267
268 newdata = _nl_intern_locale_data (category, filedata, st.st_size);
269 if (__glibc_unlikely (newdata == NULL))
270 /* Bad data. */
271 {
272#ifdef _POSIX_MAPPED_FILES
273 if (alloc == ld_mapped)
274 __munmap ((caddr_t) filedata, st.st_size);
275#endif
276 return;
277 }
278
279 /* _nl_intern_locale_data leaves us these fields to initialize. */
280 newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
281 newdata->alloc = alloc;
282
283 file->data = newdata;
284}
285
286void
287_nl_unload_locale (struct __locale_data *locale)
288{
289 if (locale->private.cleanup)
290 (*locale->private.cleanup) (locale);
291
292 switch (__builtin_expect (locale->alloc, ld_mapped))
293 {
294 case ld_malloced:
295 free ((void *) locale->filedata);
296 break;
297 case ld_mapped:
298#ifdef _POSIX_MAPPED_FILES
299 __munmap ((caddr_t) locale->filedata, locale->filesize);
300 break;
301#endif
302 case ld_archive: /* Nothing to do. */
303 break;
304 }
305
306 if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
307 free ((char *) locale->name);
308
309 free (locale);
310}
311