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
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), _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
285void
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