]>
Commit | Line | Data |
---|---|---|
d4697bc9 | 1 | /* Copyright (C) 1996-2014 Free Software Foundation, Inc. |
2c6fe0bd | 2 | This file is part of the GNU C Library. |
390500b1 | 3 | Contributed by Ulrich Drepper, <drepper@gnu.org>. |
a641835a | 4 | |
2c6fe0bd | 5 | The GNU C Library is free software; you can redistribute it and/or |
41bdb6e2 AJ |
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. | |
a641835a | 9 | |
2c6fe0bd UD |
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 | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
a641835a | 14 | |
41bdb6e2 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
a641835a | 18 | |
d553bbf2 | 19 | #include <byteswap.h> |
a641835a | 20 | #include <endian.h> |
b0610668 | 21 | #include <errno.h> |
a641835a RM |
22 | #include <fcntl.h> |
23 | #include <string.h> | |
24 | #include <stdlib.h> | |
25 | #include <unistd.h> | |
7cabd57c UD |
26 | #ifdef _POSIX_MAPPED_FILES |
27 | # include <sys/mman.h> | |
28 | #endif | |
a641835a RM |
29 | #include <sys/stat.h> |
30 | ||
31 | #include "catgetsinfo.h" | |
73299943 | 32 | #include <not-cancel.h> |
a641835a RM |
33 | |
34 | ||
d553bbf2 | 35 | #define SWAPU32(w) bswap_32 (w) |
a641835a RM |
36 | |
37 | ||
ca130fe4 UD |
38 | int |
39 | __open_catalog (const char *cat_name, const char *nlspath, const char *env_var, | |
40 | __nl_catd catalog) | |
a641835a | 41 | { |
310b3460 | 42 | int fd = -1; |
417bafec | 43 | struct stat64 st; |
a641835a | 44 | int swapping; |
b0610668 UD |
45 | size_t cnt; |
46 | size_t max_offset; | |
47 | size_t tab_size; | |
48 | const char *lastp; | |
ca130fe4 | 49 | int result = -1; |
a641835a | 50 | |
ca130fe4 | 51 | if (strchr (cat_name, '/') != NULL || nlspath == NULL) |
73299943 | 52 | fd = open_not_cancel_2 (cat_name, O_RDONLY); |
a641835a RM |
53 | else |
54 | { | |
ca130fe4 | 55 | const char *run_nlspath = nlspath; |
a641835a | 56 | #define ENOUGH(n) \ |
a1ffb40e | 57 | if (__glibc_unlikely (bufact + (n) >= bufmax)) \ |
a641835a RM |
58 | { \ |
59 | char *old_buf = buf; \ | |
60 | bufmax += 256 + (n); \ | |
61 | buf = (char *) alloca (bufmax); \ | |
62 | memcpy (buf, old_buf, bufact); \ | |
63 | } | |
64 | ||
65 | /* The RUN_NLSPATH variable contains a colon separated list of | |
66 | descriptions where we expect to find catalogs. We have to | |
67 | recognize certain % substitutions and stop when we found the | |
68 | first existing file. */ | |
69 | char *buf; | |
70 | size_t bufact; | |
71 | size_t bufmax; | |
d553bbf2 | 72 | size_t len; |
a641835a RM |
73 | |
74 | buf = NULL; | |
75 | bufmax = 0; | |
76 | ||
77 | fd = -1; | |
78 | while (*run_nlspath != '\0') | |
79 | { | |
80 | bufact = 0; | |
b6aa34eb UD |
81 | |
82 | if (*run_nlspath == ':') | |
83 | { | |
84 | /* Leading colon or adjacent colons - treat same as %N. */ | |
ca130fe4 | 85 | len = strlen (cat_name); |
b6aa34eb | 86 | ENOUGH (len); |
ca130fe4 | 87 | memcpy (&buf[bufact], cat_name, len); |
b6aa34eb UD |
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. */ | |
ca130fe4 | 101 | len = strlen (cat_name); |
b6aa34eb | 102 | ENOUGH (len); |
ca130fe4 | 103 | memcpy (&buf[bufact], cat_name, len); |
b6aa34eb UD |
104 | bufact += len; |
105 | break; | |
106 | case 'L': | |
107 | /* Use the current locale category value. */ | |
ca130fe4 | 108 | len = strlen (env_var); |
b6aa34eb | 109 | ENOUGH (len); |
ca130fe4 | 110 | memcpy (&buf[bufact], env_var, len); |
b6aa34eb UD |
111 | bufact += len; |
112 | break; | |
113 | case 'l': | |
114 | /* Use language element of locale category value. */ | |
ca130fe4 | 115 | tmp = env_var; |
b6aa34eb UD |
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. */ | |
ca130fe4 | 125 | tmp = env_var; |
b6aa34eb | 126 | do |
a641835a | 127 | ++tmp; |
b6aa34eb UD |
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. */ | |
ca130fe4 | 142 | tmp = env_var; |
b6aa34eb | 143 | do |
a641835a | 144 | ++tmp; |
b6aa34eb UD |
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 | ||
a641835a RM |
175 | ENOUGH (1); |
176 | buf[bufact] = '\0'; | |
177 | ||
178 | if (bufact != 0) | |
179 | { | |
73299943 | 180 | fd = open_not_cancel_2 (buf, O_RDONLY); |
a641835a RM |
181 | if (fd >= 0) |
182 | break; | |
183 | } | |
184 | ||
185 | ++run_nlspath; | |
186 | } | |
187 | } | |
188 | ||
310b3460 | 189 | /* Avoid dealing with directories and block devices */ |
e84e339f | 190 | if (__builtin_expect (fd, 0) < 0) |
ca130fe4 | 191 | return -1; |
390500b1 | 192 | |
417bafec | 193 | if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0) |
ca130fe4 UD |
194 | goto close_unlock_return; |
195 | ||
e84e339f | 196 | if (__builtin_expect (!S_ISREG (st.st_mode), 0) |
6dd67bd5 | 197 | || (size_t) st.st_size < sizeof (struct catalog_obj)) |
b0610668 UD |
198 | { |
199 | /* `errno' is not set correctly but the file is not usable. | |
200 | Use an reasonable error value. */ | |
201 | __set_errno (EINVAL); | |
390500b1 | 202 | goto close_unlock_return; |
b0610668 | 203 | } |
a641835a | 204 | |
7cabd57c UD |
205 | catalog->file_size = st.st_size; |
206 | #ifdef _POSIX_MAPPED_FILES | |
207 | # ifndef MAP_COPY | |
a641835a | 208 | /* Linux seems to lack read-only copy-on-write. */ |
7cabd57c UD |
209 | # define MAP_COPY MAP_PRIVATE |
210 | # endif | |
211 | # ifndef MAP_FILE | |
a641835a | 212 | /* Some systems do not have this flag; it is superfluous. */ |
7cabd57c | 213 | # define MAP_FILE 0 |
7cabd57c | 214 | # endif |
a641835a | 215 | catalog->file_ptr = |
a5113b14 | 216 | (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ, |
946860b1 | 217 | MAP_FILE|MAP_COPY, fd, 0); |
e84e339f UD |
218 | if (__builtin_expect (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED, |
219 | 1)) | |
a641835a | 220 | /* Tell the world we managed to mmap the file. */ |
6d52618b | 221 | catalog->status = mmapped; |
a641835a | 222 | else |
7cabd57c | 223 | #endif /* _POSIX_MAPPED_FILES */ |
a641835a RM |
224 | { |
225 | /* mmap failed perhaps because the system call is not | |
226 | implemented. Try to load the file. */ | |
227 | size_t todo; | |
228 | catalog->file_ptr = malloc (st.st_size); | |
229 | if (catalog->file_ptr == NULL) | |
ca130fe4 UD |
230 | goto close_unlock_return; |
231 | ||
a641835a RM |
232 | todo = st.st_size; |
233 | /* Save read, handle partial reads. */ | |
234 | do | |
235 | { | |
73299943 UD |
236 | size_t now = read_not_cancel (fd, (((char *) catalog->file_ptr) |
237 | + (st.st_size - todo)), todo); | |
17c389fc | 238 | if (now == 0 || now == (size_t) -1) |
a641835a | 239 | { |
17c389fc UD |
240 | #ifdef EINTR |
241 | if (now == (size_t) -1 && errno == EINTR) | |
242 | continue; | |
243 | #endif | |
a641835a | 244 | free ((void *) catalog->file_ptr); |
390500b1 | 245 | goto close_unlock_return; |
a641835a RM |
246 | } |
247 | todo -= now; | |
248 | } | |
249 | while (todo > 0); | |
250 | catalog->status = malloced; | |
251 | } | |
252 | ||
a641835a RM |
253 | /* Determine whether the file is a catalog file and if yes whether |
254 | it is written using the correct byte order. Else we have to swap | |
255 | the values. */ | |
a1ffb40e | 256 | if (__glibc_likely (catalog->file_ptr->magic == CATGETS_MAGIC)) |
a641835a RM |
257 | swapping = 0; |
258 | else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC)) | |
259 | swapping = 1; | |
260 | else | |
261 | { | |
b0610668 | 262 | invalid_file: |
d553bbf2 | 263 | /* Invalid file. Free the resources and mark catalog as not |
a641835a | 264 | usable. */ |
7cabd57c | 265 | #ifdef _POSIX_MAPPED_FILES |
6d52618b | 266 | if (catalog->status == mmapped) |
a5113b14 | 267 | __munmap ((void *) catalog->file_ptr, catalog->file_size); |
a641835a | 268 | else |
7cabd57c | 269 | #endif /* _POSIX_MAPPED_FILES */ |
a641835a | 270 | free (catalog->file_ptr); |
390500b1 | 271 | goto close_unlock_return; |
a641835a RM |
272 | } |
273 | ||
274 | #define SWAP(x) (swapping ? SWAPU32 (x) : (x)) | |
275 | ||
276 | /* Get dimensions of the used hashing table. */ | |
277 | catalog->plane_size = SWAP (catalog->file_ptr->plane_size); | |
278 | catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth); | |
279 | ||
280 | /* The file contains two versions of the pointer tables. Pick the | |
281 | right one for the local byte order. */ | |
282 | #if __BYTE_ORDER == __LITTLE_ENDIAN | |
283 | catalog->name_ptr = &catalog->file_ptr->name_ptr[0]; | |
284 | #elif __BYTE_ORDER == __BIG_ENDIAN | |
285 | catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size | |
286 | * catalog->plane_depth | |
287 | * 3]; | |
288 | #else | |
289 | # error Cannot handle __BYTE_ORDER byte order | |
290 | #endif | |
291 | ||
292 | /* The rest of the file contains all the strings. They are | |
293 | addressed relative to the position of the first string. */ | |
294 | catalog->strings = | |
295 | (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size | |
296 | * catalog->plane_depth * 3 * 2]; | |
39e16978 | 297 | |
b0610668 UD |
298 | /* Determine the largest string offset mentioned in the table. */ |
299 | max_offset = 0; | |
300 | tab_size = 3 * catalog->plane_size * catalog->plane_depth; | |
301 | for (cnt = 2; cnt < tab_size; cnt += 3) | |
302 | if (catalog->name_ptr[cnt] > max_offset) | |
303 | max_offset = catalog->name_ptr[cnt]; | |
304 | ||
305 | /* Now we can check whether the file is large enough to contain the | |
306 | tables it says it contains. */ | |
6dd67bd5 UD |
307 | if ((size_t) st.st_size |
308 | <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset)) | |
b0610668 UD |
309 | /* The last string is not contained in the file. */ |
310 | goto invalid_file; | |
311 | ||
312 | lastp = catalog->strings + max_offset; | |
313 | max_offset = (st.st_size | |
314 | - sizeof (struct catalog_obj) + 2 * tab_size + max_offset); | |
315 | while (*lastp != '\0') | |
316 | { | |
317 | if (--max_offset == 0) | |
318 | goto invalid_file; | |
319 | ++lastp; | |
320 | } | |
321 | ||
ca130fe4 UD |
322 | /* We succeeded. */ |
323 | result = 0; | |
324 | ||
39e16978 | 325 | /* Release the lock again. */ |
390500b1 | 326 | close_unlock_return: |
73299943 | 327 | close_not_cancel_no_status (fd); |
ca130fe4 UD |
328 | |
329 | return result; | |
a641835a | 330 | } |
37ba7d66 | 331 | libc_hidden_def (__open_catalog) |