1 | /* Copyright (C) 1996-2023 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 |
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 | O_CLOEXEC); |
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 | O_CLOEXEC); |
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 | |