packaging: add --disable-experimental-malloc
[platform/upstream/glibc.git] / locale / loadlocale.c
1 /* Functions to read locale data files.
2    Copyright (C) 1996-2023 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
35 static 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) \
50 static 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
57 static 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 /* Fill in LOCDATA->private for the LC_CTYPE category.  */
66 static void
67 _nl_intern_locale_data_fill_cache_ctype (struct __locale_data *locdata)
68 {
69   struct lc_ctype_data *data = locdata->private;
70
71   /* Default to no translation.  Assumes zero initialization of *data.  */
72   memset (data->outdigit_bytes, 1, sizeof (data->outdigit_bytes));
73
74   for (int i = 0; i <= 9; ++i)
75     {
76       const char *digit
77         = locdata->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB + i)].string;
78       unsigned char len;
79       if (digit[0] != '0' + i || digit[1] != '\0')
80          {
81            data->outdigit_translation_needed = true;
82            len = strlen (locdata->values[_NL_ITEM_INDEX
83                                          (_NL_CTYPE_OUTDIGIT0_MB + i)].string);
84          }
85       else
86         len = 1;
87       data->outdigit_bytes[i] = len;
88       if (i == 0)
89         data->outdigit_bytes_all_equal = len;
90       else if (data->outdigit_bytes_all_equal != len)
91         data->outdigit_bytes_all_equal = 0;
92     }
93 }
94
95 /* Updates data in LOCDATA->private for CATEGORY.  */
96 static void
97 _nl_intern_locale_data_fill_cache (int category, struct __locale_data *locdata)
98 {
99   switch (category)
100     {
101     case LC_CTYPE:
102       _nl_intern_locale_data_fill_cache_ctype (locdata);
103       break;
104     }
105 }
106
107 /* Returns the number of bytes allocated of struct __locale_data for
108    CATEGORY.  */
109 static size_t
110 _nl_intern_locale_data_extra_size (int category)
111 {
112   switch (category)
113     {
114     case LC_CTYPE:
115       return sizeof (struct lc_ctype_data);
116     default:
117       return 0;
118     }
119 }
120
121 struct __locale_data *
122 _nl_intern_locale_data (int category, const void *data, size_t datasize)
123 {
124   const struct
125     {
126       unsigned int magic;
127       unsigned int nstrings;
128       unsigned int strindex[0];
129     } *const filedata = data;
130   struct __locale_data *newdata;
131   size_t cnt;
132
133   if (__builtin_expect (datasize < sizeof *filedata, 0)
134       || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
135     {
136       /* Bad data file.  */
137       __set_errno (EINVAL);
138       return NULL;
139     }
140
141   if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
142                         0)
143       || (__builtin_expect (sizeof *filedata
144                             + filedata->nstrings * sizeof (unsigned int)
145                             >= datasize, 0)))
146     {
147       /* Insufficient data.  */
148       __set_errno (EINVAL);
149       return NULL;
150     }
151
152   size_t base_size = (sizeof *newdata
153                       + filedata->nstrings * sizeof (union locale_data_value));
154   size_t extra_size = _nl_intern_locale_data_extra_size (category);
155
156   newdata = malloc (base_size + extra_size);
157   if (newdata == NULL)
158     return NULL;
159
160   newdata->filedata = (void *) filedata;
161   newdata->filesize = datasize;
162   if (extra_size == 0)
163     newdata->private = NULL;
164   else
165     {
166       newdata->private = (char *) newdata + base_size;
167       memset (newdata->private, 0, extra_size);
168     }
169   newdata->usage_count = 0;
170   newdata->use_translit = 0;
171   newdata->nstrings = filedata->nstrings;
172   for (cnt = 0; cnt < newdata->nstrings; ++cnt)
173     {
174       size_t idx = filedata->strindex[cnt];
175       if (__glibc_unlikely (idx > (size_t) newdata->filesize))
176         {
177         puntdata:
178           free (newdata);
179           __set_errno (EINVAL);
180           return NULL;
181         }
182
183       /* Determine the type.  There is one special case: the LC_CTYPE
184          category can have more elements than there are in the
185          _nl_value_type_LC_XYZ array.  There are all pointers.  */
186       switch (category)
187         {
188 #define CATTEST(cat) \
189         case LC_##cat:                                          \
190           if (cnt >= (sizeof (_nl_value_type_LC_##cat)          \
191                       / sizeof (_nl_value_type_LC_##cat[0])))   \
192             goto puntdata;                                      \
193           break
194           CATTEST (NUMERIC);
195           CATTEST (TIME);
196           CATTEST (COLLATE);
197           CATTEST (MONETARY);
198           CATTEST (MESSAGES);
199           CATTEST (PAPER);
200           CATTEST (NAME);
201           CATTEST (ADDRESS);
202           CATTEST (TELEPHONE);
203           CATTEST (MEASUREMENT);
204           CATTEST (IDENTIFICATION);
205         default:
206           assert (category == LC_CTYPE);
207           break;
208         }
209
210       if ((category == LC_CTYPE
211            && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
212                       / sizeof (_nl_value_type_LC_CTYPE[0])))
213           || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
214         newdata->values[cnt].string = newdata->filedata + idx;
215       else
216         {
217           if (!LOCFILE_ALIGNED_P (idx))
218             goto puntdata;
219           newdata->values[cnt].word =
220             *((const uint32_t *) (newdata->filedata + idx));
221         }
222     }
223
224   if (extra_size > 0)
225     _nl_intern_locale_data_fill_cache (category, newdata);
226
227   return newdata;
228 }
229
230 void
231 _nl_load_locale (struct loaded_l10nfile *file, int category)
232 {
233   int fd;
234   void *filedata;
235   struct __stat64_t64 st;
236   struct __locale_data *newdata;
237   int save_err;
238   int alloc = ld_mapped;
239
240   file->decided = 1;
241   file->data = NULL;
242
243   fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
244   if (__builtin_expect (fd, 0) < 0)
245     /* Cannot open the file.  */
246     return;
247
248   if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
249     {
250     puntfd:
251       __close_nocancel_nostatus (fd);
252       return;
253     }
254   if (__glibc_unlikely (S_ISDIR (st.st_mode)))
255     {
256       /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
257            instead.  */
258       char *newp;
259       size_t filenamelen;
260
261       __close_nocancel_nostatus (fd);
262
263       filenamelen = strlen (file->filename);
264       newp = (char *) alloca (filenamelen
265                               + 5 + _nl_category_name_sizes[category] + 1);
266       __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
267                             "/SYS_", 5), _nl_category_names_get (category),
268                  _nl_category_name_sizes[category] + 1);
269
270       fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
271       if (__builtin_expect (fd, 0) < 0)
272         return;
273
274       if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
275         goto puntfd;
276     }
277
278   /* Map in the file's data.  */
279   save_err = errno;
280 #ifdef _POSIX_MAPPED_FILES
281 # ifndef MAP_COPY
282   /* Linux seems to lack read-only copy-on-write.  */
283 #  define MAP_COPY MAP_PRIVATE
284 # endif
285 # ifndef MAP_FILE
286   /* Some systems do not have this flag; it is superfluous.  */
287 #  define MAP_FILE 0
288 # endif
289   filedata = __mmap ((caddr_t) 0, st.st_size,
290                      PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
291   if (__glibc_unlikely (filedata == MAP_FAILED))
292     {
293       filedata = NULL;
294       if (__builtin_expect (errno, ENOSYS) == ENOSYS)
295         {
296 #endif  /* _POSIX_MAPPED_FILES */
297           /* No mmap; allocate a buffer and read from the file.  */
298           alloc = ld_malloced;
299           filedata = malloc (st.st_size);
300           if (filedata != NULL)
301             {
302               off_t to_read = st.st_size;
303               ssize_t nread;
304               char *p = (char *) filedata;
305               while (to_read > 0)
306                 {
307                   nread = __read_nocancel (fd, p, to_read);
308                   if (__builtin_expect (nread, 1) <= 0)
309                     {
310                       free (filedata);
311                       if (nread == 0)
312                         __set_errno (EINVAL); /* Bizarreness going on.  */
313                       goto puntfd;
314                     }
315                   p += nread;
316                   to_read -= nread;
317                 }
318               __set_errno (save_err);
319             }
320 #ifdef _POSIX_MAPPED_FILES
321         }
322     }
323 #endif  /* _POSIX_MAPPED_FILES */
324
325   /* We have mapped the data, so we no longer need the descriptor.  */
326   __close_nocancel_nostatus (fd);
327
328   if (__glibc_unlikely (filedata == NULL))
329     /* We failed to map or read the data.  */
330     return;
331
332   newdata = _nl_intern_locale_data (category, filedata, st.st_size);
333   if (__glibc_unlikely (newdata == NULL))
334     /* Bad data.  */
335     {
336 #ifdef _POSIX_MAPPED_FILES
337       if (alloc == ld_mapped)
338         __munmap ((caddr_t) filedata, st.st_size);
339 #endif
340       return;
341     }
342
343   /* _nl_intern_locale_data leaves us these fields to initialize.  */
344   newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
345   newdata->alloc = alloc;
346
347   file->data = newdata;
348 }
349
350 void
351 _nl_unload_locale (int category, struct __locale_data *locale)
352 {
353   /* Deallocate locale->private.  */
354   switch (category)
355     {
356     case LC_CTYPE:
357       _nl_cleanup_ctype (locale);
358       break;
359     case LC_TIME:
360       _nl_cleanup_time (locale);
361       break;
362     }
363
364   switch (__builtin_expect (locale->alloc, ld_mapped))
365     {
366     case ld_malloced:
367       free ((void *) locale->filedata);
368       break;
369     case ld_mapped:
370 #ifdef _POSIX_MAPPED_FILES
371       __munmap ((caddr_t) locale->filedata, locale->filesize);
372       break;
373 #endif
374     case ld_archive:            /* Nothing to do.  */
375       break;
376     }
377
378   if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
379     free ((char *) locale->name);
380
381   free (locale);
382 }