1 /* Functions to read locale data files.
2    Copyright (C) 1996-2021 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 
66 struct __locale_data *
_nl_intern_locale_data(int category,const void * data,size_t datasize)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 (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 (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 
164 void
_nl_load_locale(struct loaded_l10nfile * file,int category)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 (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 (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, filedata, 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 
284 void
_nl_unload_locale(struct __locale_data * locale)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 }
309