1/* Functions to read locale data files.
2 Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <assert.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <locale.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#ifdef _POSIX_MAPPED_FILES
27# include <sys/mman.h>
28#endif
29#include <sys/stat.h>
30
31#include <not-cancel.h>
32#include "localeinfo.h"
33
34
35static const size_t _nl_category_num_items[] =
36{
37#define DEFINE_CATEGORY(category, category_name, items, a) \
38 [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
39#include "categories.def"
40#undef DEFINE_CATEGORY
41};
42
43
44#define NO_PAREN(arg, rest...) arg, ##rest
45
46/* The size of the array must be specified explicitly because some of
47 the 'items' may be subarrays, which will cause the compiler to deduce
48 an incorrect size from the initializer. */
49#define DEFINE_CATEGORY(category, category_name, items, a) \
50static const enum value_type _nl_value_type_##category \
51 [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items };
52#define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
53 [_NL_ITEM_INDEX (element)] = type,
54#include "categories.def"
55#undef DEFINE_CATEGORY
56
57static const enum value_type *const _nl_value_types[] =
58{
59#define DEFINE_CATEGORY(category, category_name, items, a) \
60 [category] = _nl_value_type_##category,
61#include "categories.def"
62#undef DEFINE_CATEGORY
63};
64
65
66struct __locale_data *
67_nl_intern_locale_data (int category, const void *data, size_t datasize)
68{
69 const struct
70 {
71 unsigned int magic;
72 unsigned int nstrings;
73 unsigned int strindex[0];
74 } *const filedata = data;
75 struct __locale_data *newdata;
76 size_t cnt;
77
78 if (__builtin_expect (datasize < sizeof *filedata, 0)
79 || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
80 {
81 /* Bad data file. */
82 __set_errno (EINVAL);
83 return NULL;
84 }
85
86 if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
87 0)
88 || (__builtin_expect (sizeof *filedata
89 + filedata->nstrings * sizeof (unsigned int)
90 >= datasize, 0)))
91 {
92 /* Insufficient data. */
93 __set_errno (EINVAL);
94 return NULL;
95 }
96
97 newdata = malloc (size: sizeof *newdata
98 + filedata->nstrings * sizeof (union locale_data_value));
99 if (newdata == NULL)
100 return NULL;
101
102 newdata->filedata = (void *) filedata;
103 newdata->filesize = datasize;
104 newdata->private.data = NULL;
105 newdata->private.cleanup = NULL;
106 newdata->usage_count = 0;
107 newdata->use_translit = 0;
108 newdata->nstrings = filedata->nstrings;
109 for (cnt = 0; cnt < newdata->nstrings; ++cnt)
110 {
111 size_t idx = filedata->strindex[cnt];
112 if (__glibc_unlikely (idx > (size_t) newdata->filesize))
113 {
114 puntdata:
115 free (ptr: newdata);
116 __set_errno (EINVAL);
117 return NULL;
118 }
119
120 /* Determine the type. There is one special case: the LC_CTYPE
121 category can have more elements than there are in the
122 _nl_value_type_LC_XYZ array. There are all pointers. */
123 switch (category)
124 {
125#define CATTEST(cat) \
126 case LC_##cat: \
127 if (cnt >= (sizeof (_nl_value_type_LC_##cat) \
128 / sizeof (_nl_value_type_LC_##cat[0]))) \
129 goto puntdata; \
130 break
131 CATTEST (NUMERIC);
132 CATTEST (TIME);
133 CATTEST (COLLATE);
134 CATTEST (MONETARY);
135 CATTEST (MESSAGES);
136 CATTEST (PAPER);
137 CATTEST (NAME);
138 CATTEST (ADDRESS);
139 CATTEST (TELEPHONE);
140 CATTEST (MEASUREMENT);
141 CATTEST (IDENTIFICATION);
142 default:
143 assert (category == LC_CTYPE);
144 break;
145 }
146
147 if ((category == LC_CTYPE
148 && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
149 / sizeof (_nl_value_type_LC_CTYPE[0])))
150 || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
151 newdata->values[cnt].string = newdata->filedata + idx;
152 else
153 {
154 if (!LOCFILE_ALIGNED_P (idx))
155 goto puntdata;
156 newdata->values[cnt].word =
157 *((const uint32_t *) (newdata->filedata + idx));
158 }
159 }
160
161 return newdata;
162}
163
164void
165_nl_load_locale (struct loaded_l10nfile *file, int category)
166{
167 int fd;
168 void *filedata;
169 struct __stat64_t64 st;
170 struct __locale_data *newdata;
171 int save_err;
172 int alloc = ld_mapped;
173
174 file->decided = 1;
175 file->data = NULL;
176
177 fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
178 if (__builtin_expect (fd, 0) < 0)
179 /* Cannot open the file. */
180 return;
181
182 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
183 {
184 puntfd:
185 __close_nocancel_nostatus (fd);
186 return;
187 }
188 if (__glibc_unlikely (S_ISDIR (st.st_mode)))
189 {
190 /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
191 instead. */
192 char *newp;
193 size_t filenamelen;
194
195 __close_nocancel_nostatus (fd);
196
197 filenamelen = strlen (file->filename);
198 newp = (char *) alloca (filenamelen
199 + 5 + _nl_category_name_sizes[category] + 1);
200 __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
201 "/SYS_", 5), _nl_category_names_get (category),
202 _nl_category_name_sizes[category] + 1);
203
204 fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
205 if (__builtin_expect (fd, 0) < 0)
206 return;
207
208 if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 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 (size: 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_nocancel (fd, p, to_read);
242 if (__builtin_expect (nread, 1) <= 0)
243 {
244 free (ptr: 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_nocancel_nostatus (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, data: filedata, datasize: 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
284void
285_nl_unload_locale (struct __locale_data *locale)
286{
287 if (locale->private.cleanup)
288 (*locale->private.cleanup) (locale);
289
290 switch (__builtin_expect (locale->alloc, ld_mapped))
291 {
292 case ld_malloced:
293 free (ptr: (void *) locale->filedata);
294 break;
295 case ld_mapped:
296#ifdef _POSIX_MAPPED_FILES
297 __munmap ((caddr_t) locale->filedata, locale->filesize);
298 break;
299#endif
300 case ld_archive: /* Nothing to do. */
301 break;
302 }
303
304 if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
305 free (ptr: (char *) locale->name);
306
307 free (ptr: locale);
308}
309

source code of glibc/locale/loadlocale.c