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