]> git.ipfire.org Git - thirdparty/glibc.git/blame - locale/loadlocale.c
Remove internal usage of extensible stat functions
[thirdparty/glibc.git] / locale / loadlocale.c
CommitLineData
ce4d8b66 1/* Functions to read locale data files.
d614a753 2 Copyright (C) 1996-2020 Free Software Foundation, Inc.
c84142e8 3 This file is part of the GNU C Library.
4b10dd6c 4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
933e73fa 5
c84142e8 6 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
933e73fa 10
c84142e8
UD
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
41bdb6e2 14 Lesser General Public License for more details.
933e73fa 15
41bdb6e2 16 You should have received a copy of the GNU Lesser General Public
59ba27a6 17 License along with the GNU C Library; if not, see
5a82c748 18 <https://www.gnu.org/licenses/>. */
933e73fa 19
e43e0dd6 20#include <assert.h>
933e73fa
RM
21#include <errno.h>
22#include <fcntl.h>
3e154a6f 23#include <locale.h>
933e73fa
RM
24#include <stdlib.h>
25#include <string.h>
7a12c6bb 26#include <unistd.h>
b6aa34eb
UD
27#ifdef _POSIX_MAPPED_FILES
28# include <sys/mman.h>
29#endif
7a12c6bb
RM
30#include <sys/stat.h>
31
72ef277e 32#include <not-cancel.h>
933e73fa
RM
33#include "localeinfo.h"
34
7a12c6bb
RM
35
36static const size_t _nl_category_num_items[] =
19bc17a9 37{
4b10dd6c 38#define DEFINE_CATEGORY(category, category_name, items, a) \
19bc17a9 39 [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
933e73fa
RM
40#include "categories.def"
41#undef DEFINE_CATEGORY
19bc17a9
RM
42};
43
44
45#define NO_PAREN(arg, rest...) arg, ##rest
46
f301e533
RL
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. */
4b10dd6c 50#define DEFINE_CATEGORY(category, category_name, items, a) \
f301e533
RL
51static const enum value_type _nl_value_type_##category \
52 [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items };
19bc17a9
RM
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
aa87e915 58static const enum value_type *const _nl_value_types[] =
19bc17a9 59{
4b10dd6c 60#define DEFINE_CATEGORY(category, category_name, items, a) \
19bc17a9
RM
61 [category] = _nl_value_type_##category,
62#include "categories.def"
63#undef DEFINE_CATEGORY
64};
65
933e73fa 66
f095bb72 67struct __locale_data *
cb09a2cd 68_nl_intern_locale_data (int category, const void *data, size_t datasize)
933e73fa 69{
cb09a2cd 70 const struct
933e73fa
RM
71 {
72 unsigned int magic;
73 unsigned int nstrings;
74 unsigned int strindex[0];
cb09a2cd 75 } *const filedata = data;
f095bb72 76 struct __locale_data *newdata;
cb09a2cd
RM
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;
df9f41c9
RM
105 newdata->private.data = NULL;
106 newdata->private.cleanup = NULL;
cb09a2cd
RM
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];
a1ffb40e 113 if (__glibc_unlikely (idx > (size_t) newdata->filesize))
cb09a2cd
RM
114 {
115 puntdata:
116 free (newdata);
117 __set_errno (EINVAL);
118 return NULL;
119 }
fe6ce170
UD
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) \
0062ace2
LC
127 case LC_##cat: \
128 if (cnt >= (sizeof (_nl_value_type_LC_##cat) \
129 / sizeof (_nl_value_type_LC_##cat[0]))) \
130 goto puntdata; \
fe6ce170
UD
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
cb09a2cd 154 {
7602d070 155 if (!LOCFILE_ALIGNED_P (idx))
cb09a2cd
RM
156 goto puntdata;
157 newdata->values[cnt].word =
d9fee042 158 *((const uint32_t *) (newdata->filedata + idx));
cb09a2cd 159 }
cb09a2cd
RM
160 }
161
162 return newdata;
163}
164
165void
cb09a2cd
RM
166_nl_load_locale (struct loaded_l10nfile *file, int category)
167{
168 int fd;
169 void *filedata;
8edf6e0d 170 struct stat64 st;
f095bb72 171 struct __locale_data *newdata;
7a12c6bb 172 int save_err;
cb09a2cd 173 int alloc = ld_mapped;
933e73fa 174
7a12c6bb
RM
175 file->decided = 1;
176 file->data = NULL;
177
c2284574 178 fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
3a31f6f4 179 if (__builtin_expect (fd, 0) < 0)
7a12c6bb
RM
180 /* Cannot open the file. */
181 return;
182
04986243 183 if (__builtin_expect (__fstat64 (fd, &st), 0) < 0)
cb09a2cd
RM
184 {
185 puntfd:
c181840c 186 __close_nocancel_nostatus (fd);
cb09a2cd
RM
187 return;
188 }
a1ffb40e 189 if (__glibc_unlikely (S_ISDIR (st.st_mode)))
933e73fa 190 {
7a12c6bb 191 /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
f095bb72 192 instead. */
7a12c6bb 193 char *newp;
b6aa34eb 194 size_t filenamelen;
036cc82f 195
c181840c 196 __close_nocancel_nostatus (fd);
7a12c6bb 197
b6aa34eb
UD
198 filenamelen = strlen (file->filename);
199 newp = (char *) alloca (filenamelen
7a12c6bb 200 + 5 + _nl_category_name_sizes[category] + 1);
b6aa34eb 201 __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
de18a706 202 "/SYS_", 5), _nl_category_names_get (category),
b6aa34eb 203 _nl_category_name_sizes[category] + 1);
7a12c6bb 204
c2284574 205 fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
3a31f6f4 206 if (__builtin_expect (fd, 0) < 0)
7a12c6bb
RM
207 return;
208
04986243 209 if (__builtin_expect (__fstat64 (fd, &st), 0) < 0)
7a12c6bb 210 goto puntfd;
933e73fa
RM
211 }
212
7a12c6bb
RM
213 /* Map in the file's data. */
214 save_err = errno;
b6aa34eb
UD
215#ifdef _POSIX_MAPPED_FILES
216# ifndef MAP_COPY
7a12c6bb 217 /* Linux seems to lack read-only copy-on-write. */
b6aa34eb
UD
218# define MAP_COPY MAP_PRIVATE
219# endif
220# ifndef MAP_FILE
7a12c6bb 221 /* Some systems do not have this flag; it is superfluous. */
b6aa34eb
UD
222# define MAP_FILE 0
223# endif
cb09a2cd
RM
224 filedata = __mmap ((caddr_t) 0, st.st_size,
225 PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
a1ffb40e 226 if (__glibc_unlikely (filedata == MAP_FAILED))
7a12c6bb 227 {
8b2f25c2 228 filedata = NULL;
3a31f6f4 229 if (__builtin_expect (errno, ENOSYS) == ENOSYS)
7a12c6bb 230 {
b6aa34eb 231#endif /* _POSIX_MAPPED_FILES */
7a12c6bb 232 /* No mmap; allocate a buffer and read from the file. */
cb09a2cd 233 alloc = ld_malloced;
7a12c6bb
RM
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 {
a748eb31 242 nread = __read_nocancel (fd, p, to_read);
3a31f6f4 243 if (__builtin_expect (nread, 1) <= 0)
7a12c6bb
RM
244 {
245 free (filedata);
246 if (nread == 0)
c4029823 247 __set_errno (EINVAL); /* Bizarreness going on. */
7a12c6bb
RM
248 goto puntfd;
249 }
250 p += nread;
251 to_read -= nread;
252 }
cb09a2cd 253 __set_errno (save_err);
7a12c6bb 254 }
b6aa34eb 255#ifdef _POSIX_MAPPED_FILES
7a12c6bb 256 }
7a12c6bb 257 }
b6aa34eb 258#endif /* _POSIX_MAPPED_FILES */
933e73fa 259
cb09a2cd 260 /* We have mapped the data, so we no longer need the descriptor. */
c181840c 261 __close_nocancel_nostatus (fd);
cb09a2cd 262
a1ffb40e 263 if (__glibc_unlikely (filedata == NULL))
cb09a2cd
RM
264 /* We failed to map or read the data. */
265 return;
266
267 newdata = _nl_intern_locale_data (category, filedata, st.st_size);
a1ffb40e 268 if (__glibc_unlikely (newdata == NULL))
cb09a2cd 269 /* Bad data. */
933e73fa 270 {
b6aa34eb 271#ifdef _POSIX_MAPPED_FILES
cb09a2cd
RM
272 if (alloc == ld_mapped)
273 __munmap ((caddr_t) filedata, st.st_size);
b6aa34eb 274#endif
4a33c2f5 275 return;
933e73fa
RM
276 }
277
cb09a2cd 278 /* _nl_intern_locale_data leaves us these fields to initialize. */
7a12c6bb 279 newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
cb09a2cd 280 newdata->alloc = alloc;
933e73fa 281
7a12c6bb 282 file->data = newdata;
933e73fa 283}
a5a0310d
UD
284
285void
f095bb72 286_nl_unload_locale (struct __locale_data *locale)
a5a0310d 287{
df9f41c9
RM
288 if (locale->private.cleanup)
289 (*locale->private.cleanup) (locale);
290
cb09a2cd
RM
291 switch (__builtin_expect (locale->alloc, ld_mapped))
292 {
293 case ld_malloced:
294 free ((void *) locale->filedata);
295 break;
296 case ld_mapped:
b6aa34eb 297#ifdef _POSIX_MAPPED_FILES
cb09a2cd
RM
298 __munmap ((caddr_t) locale->filedata, locale->filesize);
299 break;
b6aa34eb 300#endif
cb09a2cd
RM
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);
a5a0310d
UD
307
308 free (locale);
309}