1 /* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <byteswap.h>
19 #include <endian.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #ifdef _POSIX_MAPPED_FILES
26 # include <sys/mman.h>
27 #endif
28 #include <sys/stat.h>
29 
30 #include "catgetsinfo.h"
31 #include <not-cancel.h>
32 
33 
34 #define SWAPU32(w) bswap_32 (w)
35 
36 
37 int
__open_catalog(const char * cat_name,const char * nlspath,const char * env_var,__nl_catd catalog)38 __open_catalog (const char *cat_name, const char *nlspath, const char *env_var,
39 		__nl_catd catalog)
40 {
41   int fd = -1;
42   struct __stat64_t64 st;
43   int swapping;
44   size_t cnt;
45   size_t max_offset;
46   size_t tab_size;
47   const char *lastp;
48   int result = -1;
49   char *buf = NULL;
50 
51   if (strchr (cat_name, '/') != NULL || nlspath == NULL)
52     fd = __open_nocancel (cat_name, O_RDONLY);
53   else
54     {
55       const char *run_nlspath = nlspath;
56 #define ENOUGH(n)							      \
57   if (__glibc_unlikely (bufact + (n) >= bufmax))			      \
58     {									      \
59       char *old_buf = buf;						      \
60       bufmax += (bufmax < 256 + (n)) ? 256 + (n) : bufmax;		      \
61       buf = realloc (buf, bufmax);					      \
62       if (__glibc_unlikely (buf == NULL))				      \
63 	{								      \
64 	  free (old_buf);						      \
65 	  return -1;							      \
66 	}								      \
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       size_t bufact;
74       size_t bufmax = 0;
75       size_t len;
76 
77       fd = -1;
78       while (*run_nlspath != '\0')
79 	{
80 	  bufact = 0;
81 
82 	  if (*run_nlspath == ':')
83 	    {
84 	      /* Leading colon or adjacent colons - treat same as %N.  */
85 	      len = strlen (cat_name);
86 	      ENOUGH (len);
87 	      memcpy (&buf[bufact], cat_name, len);
88 	      bufact += len;
89 	    }
90 	  else
91 	    while (*run_nlspath != ':' && *run_nlspath != '\0')
92 	      if (*run_nlspath == '%')
93 		{
94 		  const char *tmp;
95 
96 		  ++run_nlspath;	/* We have seen the `%'.  */
97 		  switch (*run_nlspath++)
98 		    {
99 		    case 'N':
100 		      /* Use the catalog name.  */
101 		      len = strlen (cat_name);
102 		      ENOUGH (len);
103 		      memcpy (&buf[bufact], cat_name, len);
104 		      bufact += len;
105 		      break;
106 		    case 'L':
107 		      /* Use the current locale category value.  */
108 		      len = strlen (env_var);
109 		      ENOUGH (len);
110 		      memcpy (&buf[bufact], env_var, len);
111 		      bufact += len;
112 		      break;
113 		    case 'l':
114 		      /* Use language element of locale category value.  */
115 		      tmp = env_var;
116 		      do
117 			{
118 			  ENOUGH (1);
119 			  buf[bufact++] = *tmp++;
120 			}
121 		      while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
122 		      break;
123 		    case 't':
124 		      /* Use territory element of locale category value.  */
125 		      tmp = env_var;
126 		      do
127 			++tmp;
128 		      while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
129 		      if (*tmp == '_')
130 			{
131 			  ++tmp;
132 			  do
133 			    {
134 			      ENOUGH (1);
135 			      buf[bufact++] = *tmp++;
136 			    }
137 			  while (*tmp != '\0' && *tmp != '.');
138 			}
139 		      break;
140 		    case 'c':
141 		      /* Use code set element of locale category value.  */
142 		      tmp = env_var;
143 		      do
144 			++tmp;
145 		      while (*tmp != '\0' && *tmp != '.');
146 		      if (*tmp == '.')
147 			{
148 			  ++tmp;
149 			  do
150 			    {
151 			      ENOUGH (1);
152 			      buf[bufact++] = *tmp++;
153 			    }
154 			  while (*tmp != '\0');
155 			}
156 		      break;
157 		    case '%':
158 		      ENOUGH (1);
159 		      buf[bufact++] = '%';
160 		      break;
161 		    default:
162 		      /* Unknown variable: ignore this path element.  */
163 		      bufact = 0;
164 		      while (*run_nlspath != '\0' && *run_nlspath != ':')
165 			++run_nlspath;
166 		      break;
167 		    }
168 		}
169 	      else
170 		{
171 		  ENOUGH (1);
172 		  buf[bufact++] = *run_nlspath++;
173 		}
174 
175 	  ENOUGH (1);
176 	  buf[bufact] = '\0';
177 
178 	  if (bufact != 0)
179 	    {
180 	      fd = __open_nocancel (buf, O_RDONLY);
181 	      if (fd >= 0)
182 		break;
183 	    }
184 
185 	  ++run_nlspath;
186 	}
187     }
188 
189   /* Avoid dealing with directories and block devices */
190   if (__builtin_expect (fd, 0) < 0)
191     {
192       free (buf);
193       return -1;
194     }
195 
196   if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0))
197     goto close_unlock_return;
198 
199   if (__builtin_expect (!S_ISREG (st.st_mode), 0)
200       || (size_t) st.st_size < sizeof (struct catalog_obj))
201     {
202       /* `errno' is not set correctly but the file is not usable.
203 	 Use an reasonable error value.  */
204       __set_errno (EINVAL);
205       goto close_unlock_return;
206     }
207 
208   catalog->file_size = st.st_size;
209 #ifdef _POSIX_MAPPED_FILES
210 # ifndef MAP_COPY
211     /* Linux seems to lack read-only copy-on-write.  */
212 #  define MAP_COPY MAP_PRIVATE
213 # endif
214 # ifndef MAP_FILE
215     /* Some systems do not have this flag; it is superfluous.  */
216 #  define MAP_FILE 0
217 # endif
218   catalog->file_ptr =
219     (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
220 				   MAP_FILE|MAP_COPY, fd, 0);
221   if (__builtin_expect (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED,
222 			1))
223     /* Tell the world we managed to mmap the file.  */
224     catalog->status = mmapped;
225   else
226 #endif /* _POSIX_MAPPED_FILES */
227     {
228       /* mmap failed perhaps because the system call is not
229 	 implemented.  Try to load the file.  */
230       size_t todo;
231       catalog->file_ptr = malloc (st.st_size);
232       if (catalog->file_ptr == NULL)
233 	goto close_unlock_return;
234 
235       todo = st.st_size;
236       /* Save read, handle partial reads.  */
237       do
238 	{
239 	  size_t now = __read_nocancel (fd, (((char *) catalog->file_ptr)
240 					     + (st.st_size - todo)), todo);
241 	  if (now == 0 || now == (size_t) -1)
242 	    {
243 #ifdef EINTR
244 	      if (now == (size_t) -1 && errno == EINTR)
245 		continue;
246 #endif
247 	      free ((void *) catalog->file_ptr);
248 	      goto close_unlock_return;
249 	    }
250 	  todo -= now;
251 	}
252       while (todo > 0);
253       catalog->status = malloced;
254     }
255 
256   /* Determine whether the file is a catalog file and if yes whether
257      it is written using the correct byte order.  Else we have to swap
258      the values.  */
259   if (__glibc_likely (catalog->file_ptr->magic == CATGETS_MAGIC))
260     swapping = 0;
261   else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
262     swapping = 1;
263   else
264     {
265     invalid_file:
266       /* Invalid file.  Free the resources and mark catalog as not
267 	 usable.  */
268 #ifdef _POSIX_MAPPED_FILES
269       if (catalog->status == mmapped)
270 	__munmap ((void *) catalog->file_ptr, catalog->file_size);
271       else
272 #endif	/* _POSIX_MAPPED_FILES */
273 	free (catalog->file_ptr);
274       goto close_unlock_return;
275     }
276 
277 #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
278 
279   /* Get dimensions of the used hashing table.  */
280   catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
281   catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
282 
283   /* The file contains two versions of the pointer tables.  Pick the
284      right one for the local byte order.  */
285 #if __BYTE_ORDER == __LITTLE_ENDIAN
286   catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
287 #elif __BYTE_ORDER == __BIG_ENDIAN
288   catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
289 						  * catalog->plane_depth
290 						  * 3];
291 #else
292 # error Cannot handle __BYTE_ORDER byte order
293 #endif
294 
295   /* The rest of the file contains all the strings.  They are
296      addressed relative to the position of the first string.  */
297   catalog->strings =
298     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
299 					       * catalog->plane_depth * 3 * 2];
300 
301   /* Determine the largest string offset mentioned in the table.  */
302   max_offset = 0;
303   tab_size = 3 * catalog->plane_size * catalog->plane_depth;
304   for (cnt = 2; cnt < tab_size; cnt += 3)
305     if (catalog->name_ptr[cnt] > max_offset)
306       max_offset = catalog->name_ptr[cnt];
307 
308   /* Now we can check whether the file is large enough to contain the
309      tables it says it contains.  */
310   if ((size_t) st.st_size
311       <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
312     /* The last string is not contained in the file.  */
313     goto invalid_file;
314 
315   lastp = catalog->strings + max_offset;
316   max_offset = (st.st_size
317 		- sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
318   while (*lastp != '\0')
319     {
320       if (--max_offset == 0)
321 	goto invalid_file;
322       ++lastp;
323     }
324 
325   /* We succeeded.  */
326   result = 0;
327 
328   /* Release the lock again.  */
329  close_unlock_return:
330   __close_nocancel_nostatus (fd);
331   free (buf);
332 
333   return result;
334 }
335 libc_hidden_def (__open_catalog)
336