Mention bug fix for BZ #17806
[platform/upstream/glibc.git] / locale / loadlocale.c
1 /* Functions to read locale data files.
2    Copyright (C) 1996-2015 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
36 static 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 #define DEFINE_CATEGORY(category, category_name, items, a) \
48 static const enum value_type _nl_value_type_##category[] = { NO_PAREN items };
49 #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
50   [_NL_ITEM_INDEX (element)] = type,
51 #include "categories.def"
52 #undef DEFINE_CATEGORY
53
54 static const enum value_type *const _nl_value_types[] =
55 {
56 #define DEFINE_CATEGORY(category, category_name, items, a) \
57   [category] = _nl_value_type_##category,
58 #include "categories.def"
59 #undef DEFINE_CATEGORY
60 };
61
62
63 struct __locale_data *
64 internal_function
65 _nl_intern_locale_data (int category, const void *data, size_t datasize)
66 {
67   const struct
68     {
69       unsigned int magic;
70       unsigned int nstrings;
71       unsigned int strindex[0];
72     } *const filedata = data;
73   struct __locale_data *newdata;
74   size_t cnt;
75
76   if (__builtin_expect (datasize < sizeof *filedata, 0)
77       || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
78     {
79       /* Bad data file.  */
80       __set_errno (EINVAL);
81       return NULL;
82     }
83
84   if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
85                         0)
86       || (__builtin_expect (sizeof *filedata
87                             + filedata->nstrings * sizeof (unsigned int)
88                             >= datasize, 0)))
89     {
90       /* Insufficient data.  */
91       __set_errno (EINVAL);
92       return NULL;
93     }
94
95   newdata = malloc (sizeof *newdata
96                     + filedata->nstrings * sizeof (union locale_data_value));
97   if (newdata == NULL)
98     return NULL;
99
100   newdata->filedata = (void *) filedata;
101   newdata->filesize = datasize;
102   newdata->private.data = NULL;
103   newdata->private.cleanup = NULL;
104   newdata->usage_count = 0;
105   newdata->use_translit = 0;
106   newdata->nstrings = filedata->nstrings;
107   for (cnt = 0; cnt < newdata->nstrings; ++cnt)
108     {
109       size_t idx = filedata->strindex[cnt];
110       if (__glibc_unlikely (idx > (size_t) newdata->filesize))
111         {
112         puntdata:
113           free (newdata);
114           __set_errno (EINVAL);
115           return NULL;
116         }
117
118       /* Determine the type.  There is one special case: the LC_CTYPE
119          category can have more elements than there are in the
120          _nl_value_type_LC_XYZ array.  There are all pointers.  */
121       switch (category)
122         {
123 #define CATTEST(cat) \
124         case LC_##cat:                                                        \
125           assert (cnt < (sizeof (_nl_value_type_LC_##cat)                     \
126                          / sizeof (_nl_value_type_LC_##cat[0])));             \
127           break
128           CATTEST (NUMERIC);
129           CATTEST (TIME);
130           CATTEST (COLLATE);
131           CATTEST (MONETARY);
132           CATTEST (MESSAGES);
133           CATTEST (PAPER);
134           CATTEST (NAME);
135           CATTEST (ADDRESS);
136           CATTEST (TELEPHONE);
137           CATTEST (MEASUREMENT);
138           CATTEST (IDENTIFICATION);
139         default:
140           assert (category == LC_CTYPE);
141           break;
142         }
143
144       if ((category == LC_CTYPE
145            && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
146                       / sizeof (_nl_value_type_LC_CTYPE[0])))
147           || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
148         newdata->values[cnt].string = newdata->filedata + idx;
149       else
150         {
151           if (!LOCFILE_ALIGNED_P (idx))
152             goto puntdata;
153           newdata->values[cnt].word =
154             *((const u_int32_t *) (newdata->filedata + idx));
155         }
156     }
157
158   return newdata;
159 }
160
161 void
162 internal_function
163 _nl_load_locale (struct loaded_l10nfile *file, int category)
164 {
165   int fd;
166   void *filedata;
167   struct stat64 st;
168   struct __locale_data *newdata;
169   int save_err;
170   int alloc = ld_mapped;
171
172   file->decided = 1;
173   file->data = NULL;
174
175   fd = open_not_cancel_2 (file->filename, O_RDONLY | O_CLOEXEC);
176   if (__builtin_expect (fd, 0) < 0)
177     /* Cannot open the file.  */
178     return;
179
180   if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
181     {
182     puntfd:
183       close_not_cancel_no_status (fd);
184       return;
185     }
186   if (__glibc_unlikely (S_ISDIR (st.st_mode)))
187     {
188       /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
189            instead.  */
190       char *newp;
191       size_t filenamelen;
192
193       close_not_cancel_no_status (fd);
194
195       filenamelen = strlen (file->filename);
196       newp = (char *) alloca (filenamelen
197                               + 5 + _nl_category_name_sizes[category] + 1);
198       __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
199                             "/SYS_", 5),
200                  _nl_category_names.str + _nl_category_name_idxs[category],
201                  _nl_category_name_sizes[category] + 1);
202
203       fd = open_not_cancel_2 (newp, O_RDONLY | O_CLOEXEC);
204       if (__builtin_expect (fd, 0) < 0)
205         return;
206
207       if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
208         goto puntfd;
209     }
210
211   /* Map in the file's data.  */
212   save_err = errno;
213 #ifdef _POSIX_MAPPED_FILES
214 # ifndef MAP_COPY
215   /* Linux seems to lack read-only copy-on-write.  */
216 #  define MAP_COPY MAP_PRIVATE
217 # endif
218 # ifndef MAP_FILE
219   /* Some systems do not have this flag; it is superfluous.  */
220 #  define MAP_FILE 0
221 # endif
222   filedata = __mmap ((caddr_t) 0, st.st_size,
223                      PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
224   if (__glibc_unlikely (filedata == MAP_FAILED))
225     {
226       filedata = NULL;
227       if (__builtin_expect (errno, ENOSYS) == ENOSYS)
228         {
229 #endif  /* _POSIX_MAPPED_FILES */
230           /* No mmap; allocate a buffer and read from the file.  */
231           alloc = ld_malloced;
232           filedata = malloc (st.st_size);
233           if (filedata != NULL)
234             {
235               off_t to_read = st.st_size;
236               ssize_t nread;
237               char *p = (char *) filedata;
238               while (to_read > 0)
239                 {
240                   nread = read_not_cancel (fd, p, to_read);
241                   if (__builtin_expect (nread, 1) <= 0)
242                     {
243                       free (filedata);
244                       if (nread == 0)
245                         __set_errno (EINVAL); /* Bizarreness going on.  */
246                       goto puntfd;
247                     }
248                   p += nread;
249                   to_read -= nread;
250                 }
251               __set_errno (save_err);
252             }
253 #ifdef _POSIX_MAPPED_FILES
254         }
255     }
256 #endif  /* _POSIX_MAPPED_FILES */
257
258   /* We have mapped the data, so we no longer need the descriptor.  */
259   close_not_cancel_no_status (fd);
260
261   if (__glibc_unlikely (filedata == NULL))
262     /* We failed to map or read the data.  */
263     return;
264
265   newdata = _nl_intern_locale_data (category, filedata, st.st_size);
266   if (__glibc_unlikely (newdata == NULL))
267     /* Bad data.  */
268     {
269 #ifdef _POSIX_MAPPED_FILES
270       if (alloc == ld_mapped)
271         __munmap ((caddr_t) filedata, st.st_size);
272 #endif
273       return;
274     }
275
276   /* _nl_intern_locale_data leaves us these fields to initialize.  */
277   newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
278   newdata->alloc = alloc;
279
280   file->data = newdata;
281 }
282
283 void
284 internal_function
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 ((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 ((char *) locale->name);
306
307   free (locale);
308 }