Strictly check whether catalog file is larger enough for the data.
[platform/upstream/glibc.git] / catgets / open_catalog.c
1 /* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <byteswap.h>
21 #include <endian.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/mman.h>
28 #include <sys/stat.h>
29
30 #include "catgetsinfo.h"
31
32
33 #define SWAPU32(w) bswap_32 (w)
34
35
36 void
37 __open_catalog (__nl_catd catalog)
38 {
39   int fd = -1;
40   struct stat st;
41   int swapping;
42   size_t cnt;
43   size_t max_offset;
44   size_t tab_size;
45   const char *lastp;
46
47   /* Make sure we are alone.  */
48   __libc_lock_lock (catalog->lock);
49
50   /* Check whether there was no other thread faster.  */
51   if (catalog->status != closed)
52     /* While we waited some other thread tried to open the catalog.  */
53     goto unlock_return;
54
55   if (strchr (catalog->cat_name, '/') != NULL || catalog->nlspath == NULL)
56     fd = __open (catalog->cat_name, O_RDONLY);
57   else
58     {
59       const char *run_nlspath = catalog->nlspath;
60 #define ENOUGH(n)                                                             \
61   if (bufact + (n) >=bufmax)                                                  \
62     {                                                                         \
63       char *old_buf = buf;                                                    \
64       bufmax += 256 + (n);                                                    \
65       buf = (char *) alloca (bufmax);                                         \
66       memcpy (buf, old_buf, bufact);                                          \
67     }
68
69       /* The RUN_NLSPATH variable contains a colon separated list of
70          descriptions where we expect to find catalogs.  We have to
71          recognize certain % substitutions and stop when we found the
72          first existing file.  */
73       char *buf;
74       size_t bufact;
75       size_t bufmax;
76       size_t len;
77
78       buf = NULL;
79       bufmax = 0;
80
81       fd = -1;
82       while (*run_nlspath != '\0')
83         {
84           bufact = 0;
85           while (*run_nlspath != ':' && *run_nlspath != '\0')
86             if (*run_nlspath == '%')
87               {
88                 const char *tmp;
89
90                 ++run_nlspath;  /* We have seen the `%'.  */
91                 switch (*run_nlspath++)
92                   {
93                   case 'N':
94                     /* Use the catalog name.  */
95                     len = strlen (catalog->cat_name);
96                     ENOUGH (len);
97                     memcpy (&buf[bufact], catalog->cat_name, len);
98                     bufact += len;
99                     break;
100                   case 'L':
101                     /* Use the current locale category value.  */
102                     len = strlen (catalog->env_var);
103                     ENOUGH (len);
104                     memcpy (&buf[bufact], catalog->env_var, len);
105                     bufact += len;
106                     break;
107                   case 'l':
108                     /* Use language element of locale category value.  */
109                     tmp = catalog->env_var;
110                     do
111                       {
112                         ENOUGH (1);
113                         buf[bufact++] = *tmp++;
114                       }
115                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
116                     break;
117                   case 't':
118                     /* Use territory element of locale category value.  */
119                     tmp = catalog->env_var;
120                     do
121                       ++tmp;
122                     while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
123                     if (*tmp == '_')
124                       {
125                         ++tmp;
126                         do
127                           {
128                             ENOUGH (1);
129                             buf[bufact++] = *tmp;
130                           }
131                         while (*tmp != '\0' && *tmp != '.');
132                       }
133                     break;
134                   case 'c':
135                     /* Use code set element of locale category value.  */
136                     tmp = catalog->env_var;
137                     do
138                       ++tmp;
139                     while (*tmp != '\0' && *tmp != '.');
140                     if (*tmp == '.')
141                       {
142                         ++tmp;
143                         do
144                           {
145                             ENOUGH (1);
146                             buf[bufact++] = *tmp;
147                           }
148                         while (*tmp != '\0');
149                       }
150                     break;
151                   case '%':
152                     ENOUGH (1);
153                     buf[bufact++] = '%';
154                     break;
155                   default:
156                     /* Unknown variable: ignore this path element.  */
157                     bufact = 0;
158                     while (*run_nlspath != '\0' && *run_nlspath != ':')
159                       ++run_nlspath;
160                     break;
161                   }
162               }
163             else
164               {
165                 ENOUGH (1);
166                 buf[bufact++] = *run_nlspath++;
167               }
168           ENOUGH (1);
169           buf[bufact] = '\0';
170
171           if (bufact != 0)
172             {
173               fd = __open (buf, O_RDONLY);
174               if (fd >= 0)
175                 break;
176             }
177
178           ++run_nlspath;
179         }
180     }
181
182   /* Avoid dealing with directories and block devices */
183   if (fd < 0 || __fstat (fd, &st) < 0)
184     {
185       catalog->status = nonexisting;
186       goto unlock_return;
187     }
188   if (!S_ISREG (st.st_mode) || st.st_size < sizeof (struct catalog_obj))
189     {
190       /* `errno' is not set correctly but the file is not usable.
191          Use an reasonable error value.  */
192       __set_errno (EINVAL);
193       catalog->status = nonexisting;
194       goto unlock_return;
195     }
196
197 #ifndef MAP_COPY
198     /* Linux seems to lack read-only copy-on-write.  */
199 # define MAP_COPY MAP_PRIVATE
200 #endif
201 #ifndef MAP_FILE
202     /* Some systems do not have this flag; it is superfluous.  */
203 # define MAP_FILE 0
204 #endif
205 #ifndef MAP_INHERIT
206     /* Some systems might lack this; they lose.  */
207 # define MAP_INHERIT 0
208 #endif
209   catalog->file_size = st.st_size;
210   catalog->file_ptr =
211     (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
212                                    MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0);
213   if (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED)
214     /* Tell the world we managed to mmap the file.  */
215     catalog->status = mmapped;
216   else
217     {
218       /* mmap failed perhaps because the system call is not
219          implemented.  Try to load the file.  */
220       size_t todo;
221       catalog->file_ptr = malloc (st.st_size);
222       if (catalog->file_ptr == NULL)
223         {
224           catalog->status = nonexisting;
225           goto unlock_return;
226         }
227       todo = st.st_size;
228       /* Save read, handle partial reads.  */
229       do
230         {
231           size_t now = __read (fd, (((char *) &catalog->file_ptr)
232                                     + (st.st_size - todo)), todo);
233           if (now == 0)
234             {
235               free ((void *) catalog->file_ptr);
236               catalog->status = nonexisting;
237               goto unlock_return;
238             }
239           todo -= now;
240         }
241       while (todo > 0);
242       catalog->status = malloced;
243     }
244
245   /* We don't need the file anymore.  */
246   __close (fd);
247   fd = -1;
248
249   /* Determine whether the file is a catalog file and if yes whether
250      it is written using the correct byte order.  Else we have to swap
251      the values.  */
252   if (catalog->file_ptr->magic == CATGETS_MAGIC)
253     swapping = 0;
254   else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
255     swapping = 1;
256   else
257     {
258     invalid_file:
259       /* Invalid file.  Free the resources and mark catalog as not
260          usable.  */
261       if (catalog->status == mmapped)
262         __munmap ((void *) catalog->file_ptr, catalog->file_size);
263       else
264         free (catalog->file_ptr);
265       catalog->status = nonexisting;
266       goto unlock_return;
267     }
268
269 #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
270
271   /* Get dimensions of the used hashing table.  */
272   catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
273   catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
274
275   /* The file contains two versions of the pointer tables.  Pick the
276      right one for the local byte order.  */
277 #if __BYTE_ORDER == __LITTLE_ENDIAN
278   catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
279 #elif __BYTE_ORDER == __BIG_ENDIAN
280   catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
281                                                   * catalog->plane_depth
282                                                   * 3];
283 #else
284 # error Cannot handle __BYTE_ORDER byte order
285 #endif
286
287   /* The rest of the file contains all the strings.  They are
288      addressed relative to the position of the first string.  */
289   catalog->strings =
290     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
291                                                * catalog->plane_depth * 3 * 2];
292
293   /* Determine the largest string offset mentioned in the table.  */
294   max_offset = 0;
295   tab_size = 3 * catalog->plane_size * catalog->plane_depth;
296   for (cnt = 2; cnt < tab_size; cnt += 3)
297     if (catalog->name_ptr[cnt] > max_offset)
298       max_offset = catalog->name_ptr[cnt];
299
300   /* Now we can check whether the file is large enough to contain the
301      tables it says it contains.  */
302   if (st.st_size <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
303     /* The last string is not contained in the file.  */
304     goto invalid_file;
305
306   lastp = catalog->strings + max_offset;
307   max_offset = (st.st_size
308                 - sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
309   while (*lastp != '\0')
310     {
311       if (--max_offset == 0)
312         goto invalid_file;
313       ++lastp;
314     }
315
316   /* Release the lock again.  */
317  unlock_return:
318   if (fd != -1)
319     __close (fd);
320   __libc_lock_unlock (catalog->lock);
321 }