/src/ghostpdl/base/gpmisc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2021 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Miscellaneous support for platform facilities */ |
18 | | |
19 | | #include "errno_.h" |
20 | | #include "stat_.h" |
21 | | #include "unistd_.h" |
22 | | #include "fcntl_.h" |
23 | | #include "stdio_.h" |
24 | | #include "memory_.h" |
25 | | #include "string_.h" |
26 | | #include "gp.h" |
27 | | #include "gpgetenv.h" |
28 | | #include "gpmisc.h" |
29 | | #include "gserrors.h" |
30 | | |
31 | | /* |
32 | | * Get the name of the directory for temporary files, if any. Currently |
33 | | * this checks the TMPDIR and TEMP environment variables, in that order. |
34 | | * The return value and the setting of *ptr and *plen are as for gp_getenv. |
35 | | */ |
36 | | int |
37 | | gp_gettmpdir(char *ptr, int *plen) |
38 | 669 | { |
39 | 669 | int max_len = *plen; |
40 | 669 | int code = gp_getenv("TMPDIR", ptr, plen); |
41 | | |
42 | 669 | if (code != 1) |
43 | 0 | return code; |
44 | 669 | *plen = max_len; |
45 | 669 | return gp_getenv("TEMP", ptr, plen); |
46 | 669 | } |
47 | | |
48 | | /* |
49 | | * Open a temporary file, using O_EXCL and S_I*USR to prevent race |
50 | | * conditions and symlink attacks. |
51 | | */ |
52 | | FILE * |
53 | | gp_fopentemp(const char *fname, const char *mode) |
54 | 0 | { |
55 | 0 | int flags = O_EXCL; |
56 | | /* Scan the mode to construct the flags. */ |
57 | 0 | const char *p = mode; |
58 | 0 | int fildes; |
59 | 0 | FILE *file; |
60 | |
|
61 | 0 | #if defined (O_LARGEFILE) |
62 | | /* It works for Linux/gcc. */ |
63 | 0 | flags |= O_LARGEFILE; |
64 | | #else |
65 | | /* fixme : Not sure what to do. Unimplemented. */ |
66 | | /* MSVC has no O_LARGEFILE, but MSVC build never calls this function. */ |
67 | | #endif |
68 | 0 | while (*p) |
69 | 0 | switch (*p++) { |
70 | 0 | case 'a': |
71 | 0 | flags |= O_CREAT | O_APPEND; |
72 | 0 | break; |
73 | 0 | case 'r': |
74 | 0 | flags |= O_RDONLY; |
75 | 0 | break; |
76 | 0 | case 'w': |
77 | 0 | flags |= O_CREAT | O_WRONLY | O_TRUNC; |
78 | 0 | break; |
79 | | #ifdef O_BINARY |
80 | | /* Watcom C insists on this non-ANSI flag being set. */ |
81 | | case 'b': |
82 | | flags |= O_BINARY; |
83 | | break; |
84 | | #endif |
85 | 0 | case '+': |
86 | 0 | flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; |
87 | 0 | break; |
88 | 0 | default: /* e.g., 'b' */ |
89 | 0 | break; |
90 | 0 | } |
91 | 0 | fildes = open(fname, flags, S_IRUSR | S_IWUSR); |
92 | 0 | if (fildes < 0) |
93 | 0 | return 0; |
94 | | /* |
95 | | * The DEC VMS C compiler incorrectly defines the second argument of |
96 | | * fdopen as (char *), rather than following the POSIX.1 standard, |
97 | | * which defines it as (const char *). Patch this here. |
98 | | */ |
99 | 0 | file = fdopen(fildes, (char *)mode); /* still really const */ |
100 | 0 | if (file == 0) |
101 | 0 | close(fildes); |
102 | 0 | return file; |
103 | 0 | } |
104 | | |
105 | | /* Append a string to buffer. */ |
106 | | static inline bool |
107 | | append(char **bp, const char *bpe, const char **ip, uint len) |
108 | 8.82M | { |
109 | 8.82M | if (bpe - *bp < len) |
110 | 0 | return false; |
111 | 8.82M | memcpy(*bp, *ip, len); |
112 | 8.82M | *bp += len; |
113 | 8.82M | *ip += len; |
114 | 8.82M | return true; |
115 | 8.82M | } |
116 | | |
117 | | /* Search a separator forward. */ |
118 | | static inline uint |
119 | | search_separator(const char **ip, const char *ipe, const char *item, int direction) |
120 | 4.45M | { uint slen = 0; |
121 | 45.2M | for (slen = 0; (*ip - ipe) * direction < 0; (*ip) += direction) |
122 | 43.4M | if((slen = gs_file_name_check_separator(*ip, ipe - *ip, item)) != 0) |
123 | 2.64M | break; |
124 | 4.45M | return slen; |
125 | 4.45M | } |
126 | | |
127 | | /* |
128 | | * Combine a file name with a prefix. |
129 | | * Concatenates two paths and reduce parent references and current |
130 | | * directory references from the concatenation when possible. |
131 | | * The trailing zero byte is being added. |
132 | | * |
133 | | * Returns "gp_combine_success" if OK and sets '*blen' to the length of |
134 | | * the combined string. If the combined string is too small for the buffer |
135 | | * length passed in (as defined by the initial value of *blen), then the |
136 | | * "gp_combine_small_buffer" code is returned. |
137 | | * |
138 | | * Also tolerates/skips leading IODevice specifiers such as %os% or %rom% |
139 | | * When there is a leading '%' in the 'fname' no other processing is done. |
140 | | * |
141 | | * Examples : |
142 | | * "/gs/lib" + "../Resource/CMap/H" --> "/gs/Resource/CMap/H" |
143 | | * "C:/gs/lib" + "../Resource/CMap/H" --> "C:/gs/Resource/CMap/H" |
144 | | * "hard disk:gs:lib" + "::Resource:CMap:H" --> |
145 | | * "hard disk:gs:Resource:CMap:H" |
146 | | * "DUA1:[GHOSTSCRIPT.LIB" + "-.RESOURCE.CMAP]H" --> |
147 | | * "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H" |
148 | | * "\\server\share/a/b///c/../d.e/./" + "../x.e/././/../../y.z/v.v" --> |
149 | | * "\\server\share/a/y.z/v.v" |
150 | | * "%rom%lib/" + "gs_init.ps" --> "%rom%lib/gs_init.ps |
151 | | * "" + "%rom%lib/gs_init.ps" --> "%rom%lib/gs_init.ps" |
152 | | */ |
153 | | gp_file_name_combine_result |
154 | | gp_file_name_combine_generic(const char *prefix, uint plen, const char *fname, uint flen, |
155 | | bool no_sibling, char *buffer, uint *blen) |
156 | 1.15M | { |
157 | | /* |
158 | | * THIS CODE IS SHARED FOR MULTIPLE PLATFORMS. |
159 | | * PLEASE DON'T CHANGE IT FOR A SPECIFIC PLATFORM. |
160 | | * Change gp_file_name_combine instead. |
161 | | */ |
162 | 1.15M | char *bp = buffer, *bpe = buffer + *blen; |
163 | 1.15M | const char *ip, *ipe; |
164 | 1.15M | uint slen; |
165 | 1.15M | uint infix_type = 0; /* 0=none, 1=current, 2=parent. */ |
166 | 1.15M | uint infix_len = 0; |
167 | 1.15M | uint rlen = gp_file_name_root(fname, flen); |
168 | | /* We need a special handling of infixes only immediately after a drive. */ |
169 | | |
170 | 1.15M | if ( flen > 0 && fname[0] == '%') { |
171 | | /* IoDevice -- just return the fname as-is since this */ |
172 | | /* function only handles the default file system */ |
173 | | /* NOTE: %os% will subvert the normal processing of prefix and fname */ |
174 | 0 | ip = fname; |
175 | 0 | *blen = flen; |
176 | 0 | if (!append(&bp, bpe, &ip, flen)) |
177 | 0 | return gp_combine_small_buffer; |
178 | 0 | return gp_combine_success; |
179 | 0 | } |
180 | 1.15M | if (rlen != 0) { |
181 | | /* 'fname' is absolute, ignore the prefix. */ |
182 | 0 | ip = fname; |
183 | 0 | ipe = fname + flen; |
184 | 1.15M | } else { |
185 | | /* Concatenate with prefix. */ |
186 | 1.15M | ip = prefix; |
187 | 1.15M | ipe = prefix + plen; |
188 | 1.15M | rlen = gp_file_name_root(prefix, plen); |
189 | 1.15M | } |
190 | 1.15M | if (!append(&bp, bpe, &ip, rlen)) |
191 | 0 | return gp_combine_small_buffer; |
192 | 1.15M | slen = gs_file_name_check_separator(bp, buffer - bp, bp); /* Backward search. */ |
193 | 1.15M | if (rlen != 0 && slen == 0) { |
194 | | /* Patch it against names like "c:dir" on Windows. */ |
195 | 0 | const char *sep = gp_file_name_directory_separator(); |
196 | |
|
197 | 0 | slen = strlen(sep); |
198 | 0 | if (!append(&bp, bpe, &sep, slen)) |
199 | 0 | return gp_combine_small_buffer; |
200 | 0 | rlen += slen; |
201 | 0 | } |
202 | 3.82M | for (;;) { |
203 | 3.82M | const char *item = ip; |
204 | 3.82M | uint ilen; |
205 | | |
206 | 3.82M | slen = search_separator(&ip, ipe, item, 1); |
207 | 3.82M | ilen = ip - item; |
208 | 3.82M | if (ilen == 0 && !gp_file_name_is_empty_item_meanful()) { |
209 | 683 | ip += slen; |
210 | 683 | slen = 0; |
211 | 3.82M | } else if (gp_file_name_is_current(item, ilen)) { |
212 | | /* Skip the reference to 'current', except the starting one. |
213 | | * We keep the starting 'current' for platforms, which |
214 | | * require relative paths to start with it. |
215 | | */ |
216 | 0 | if (bp == buffer) { |
217 | 0 | if (!append(&bp, bpe, &item, ilen)) |
218 | 0 | return gp_combine_small_buffer; |
219 | 0 | infix_type = 1; |
220 | 0 | infix_len = ilen; |
221 | 0 | } else { |
222 | 0 | ip += slen; |
223 | 0 | slen = 0; |
224 | 0 | } |
225 | 3.82M | } else if (!gp_file_name_is_parent(item, ilen)) { |
226 | 3.82M | if (!append(&bp, bpe, &item, ilen)) |
227 | 0 | return gp_combine_small_buffer; |
228 | | /* The 'item' pointer is now broken; it may be restored using 'ilen'. */ |
229 | 3.82M | } else if (bp == buffer + rlen + infix_len) { |
230 | | /* Input is a parent and the output only contains a root and an infix. */ |
231 | 0 | if (rlen != 0) |
232 | 0 | return gp_combine_cant_handle; |
233 | 0 | switch (infix_type) { |
234 | 0 | case 1: |
235 | | /* Replace the infix with parent. */ |
236 | 0 | bp = buffer + rlen; /* Drop the old infix, if any. */ |
237 | 0 | infix_len = 0; |
238 | | /* Falls through. */ |
239 | 0 | case 0: |
240 | | /* We have no infix, start with parent. */ |
241 | 0 | if ((no_sibling && ipe == fname + flen && flen != 0) || |
242 | 0 | !gp_file_name_is_parent_allowed()) |
243 | 0 | return gp_combine_cant_handle; |
244 | | /* Falls through. */ |
245 | 0 | case 2: |
246 | | /* Append one more parent - falls through. */ |
247 | 0 | DO_NOTHING; |
248 | 0 | } |
249 | 0 | if (!append(&bp, bpe, &item, ilen)) |
250 | 0 | return gp_combine_small_buffer; |
251 | 0 | infix_type = 2; |
252 | 0 | infix_len += ilen; |
253 | | /* Recompute the separator length. We cannot use the old slen on Mac OS. */ |
254 | 0 | slen = gs_file_name_check_separator(ip, ipe - ip, ip); |
255 | 0 | } else { |
256 | | /* Input is a parent and the output continues after infix. */ |
257 | | /* Unappend the last separator and the last item. */ |
258 | 0 | uint slen1 = gs_file_name_check_separator(bp, buffer + rlen - bp, bp); /* Backward search. */ |
259 | 0 | char *bie = bp - slen1; |
260 | |
|
261 | 0 | bp = bie; |
262 | 0 | DISCARD(search_separator((const char **)&bp, buffer + rlen, bp, -1)); |
263 | | /* The cast above quiets a gcc warning. We believe it's a bug in the compiler. */ |
264 | | /* Skip the input with separator. We cannot use slen on Mac OS. */ |
265 | 0 | ip += gs_file_name_check_separator(ip, ipe - ip, ip); |
266 | 0 | if (no_sibling) { |
267 | 0 | const char *p = ip; |
268 | |
|
269 | 0 | DISCARD(search_separator(&p, ipe, ip, 1)); |
270 | 0 | if (p - ip != bie - bp || memcmp(ip, bp, p - ip)) |
271 | 0 | return gp_combine_cant_handle; |
272 | 0 | } |
273 | 0 | slen = 0; |
274 | 0 | } |
275 | 3.82M | if (slen) { |
276 | 2.52M | if (bp == buffer + rlen + infix_len) |
277 | 0 | infix_len += slen; |
278 | 2.52M | if (!append(&bp, bpe, &ip, slen)) |
279 | 0 | return gp_combine_small_buffer; |
280 | 2.52M | } |
281 | 3.82M | if (ip == ipe) { |
282 | 1.33M | if (ipe == fname + flen) { |
283 | | /* All done. |
284 | | * Note that the case (prefix + plen == fname && flen == 0) |
285 | | * falls here without appending a separator. |
286 | | */ |
287 | 1.15M | const char *zero=""; |
288 | | |
289 | 1.15M | if (bp == buffer) { |
290 | | /* Must not return empty path. */ |
291 | 0 | const char *current = gp_file_name_current(); |
292 | 0 | int clen = strlen(current); |
293 | |
|
294 | 0 | if (!append(&bp, bpe, ¤t, clen)) |
295 | 0 | return gp_combine_small_buffer; |
296 | 0 | } |
297 | 1.15M | *blen = bp - buffer; |
298 | 1.15M | if (!append(&bp, bpe, &zero, 1)) |
299 | 0 | return gp_combine_small_buffer; |
300 | 1.15M | return gp_combine_success; |
301 | 1.15M | } else { |
302 | | /* ipe == prefix + plen */ |
303 | | /* Switch to fname. */ |
304 | 172k | ip = fname; |
305 | 172k | ipe = fname + flen; |
306 | 172k | if (slen == 0) { |
307 | | /* Insert a separator. */ |
308 | 156k | const char *sep; |
309 | | |
310 | 156k | slen = search_separator(&ip, ipe, fname, 1); |
311 | 156k | sep = (slen != 0 ? gp_file_name_directory_separator() |
312 | 156k | : gp_file_name_separator()); |
313 | 156k | slen = strlen(sep); |
314 | 156k | if (bp == buffer + rlen + infix_len) |
315 | 0 | infix_len += slen; |
316 | 156k | if (!append(&bp, bpe, &sep, slen)) |
317 | 0 | return gp_combine_small_buffer; |
318 | 156k | ip = fname; /* Switch to fname. */ |
319 | 156k | } |
320 | 172k | } |
321 | 1.33M | } |
322 | 3.82M | } |
323 | 1.15M | } |
324 | | |
325 | | /* |
326 | | * Reduces parent references and current directory references when possible. |
327 | | * The trailing zero byte is being added. |
328 | | * |
329 | | * Examples : |
330 | | * "/gs/lib/../Resource/CMap/H" --> "/gs/Resource/CMap/H" |
331 | | * "C:/gs/lib/../Resource/CMap/H" --> "C:/gs/Resource/CMap/H" |
332 | | * "hard disk:gs:lib::Resource:CMap:H" --> |
333 | | * "hard disk:gs:Resource:CMap:H" |
334 | | * "DUA1:[GHOSTSCRIPT.LIB.-.RESOURCE.CMAP]H" --> |
335 | | * "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H" |
336 | | * "\\server\share/a/b///c/../d.e/./../x.e/././/../../y.z/v.v" --> |
337 | | * "\\server\share/a/y.z/v.v" |
338 | | * |
339 | | */ |
340 | | gp_file_name_combine_result |
341 | | gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen) |
342 | 987k | { |
343 | 987k | return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen); |
344 | 987k | } |
345 | | |
346 | | /* |
347 | | * Answers whether a file name is absolute (starts from a root). |
348 | | */ |
349 | | bool |
350 | | gp_file_name_is_absolute(const char *fname, uint flen) |
351 | 345k | { |
352 | 345k | return (gp_file_name_root(fname, flen) > 0); |
353 | 345k | } |
354 | | |
355 | | /* |
356 | | * Returns length of all starting parent references. |
357 | | */ |
358 | | static uint |
359 | | gp_file_name_prefix(const char *fname, uint flen, |
360 | | bool (*test)(const char *fname, uint flen)) |
361 | 580k | { |
362 | 580k | uint plen = gp_file_name_root(fname, flen), slen; |
363 | 580k | const char *ip, *ipe; |
364 | 580k | const char *item = fname; /* plen == flen could cause an indeterminizm. */ |
365 | | |
366 | 580k | if (plen > 0) |
367 | 107k | return 0; |
368 | 473k | ip = fname + plen; |
369 | 473k | ipe = fname + flen; |
370 | 473k | for (; ip < ipe; ) { |
371 | 473k | item = ip; |
372 | 473k | slen = search_separator(&ip, ipe, item, 1); |
373 | 473k | if (!(*test)(item, ip - item)) |
374 | 473k | break; |
375 | 0 | ip += slen; |
376 | 0 | } |
377 | 473k | return item - fname; |
378 | 580k | } |
379 | | |
380 | | /* |
381 | | * Returns length of all starting parent references. |
382 | | */ |
383 | | uint |
384 | | gp_file_name_parents(const char *fname, uint flen) |
385 | 580k | { |
386 | 580k | return gp_file_name_prefix(fname, flen, gp_file_name_is_parent); |
387 | 580k | } |
388 | | |
389 | | /* |
390 | | * Returns length of all starting cwd references. |
391 | | */ |
392 | | uint |
393 | | gp_file_name_cwds(const char *fname, uint flen) |
394 | 0 | { |
395 | 0 | return gp_file_name_prefix(fname, flen, gp_file_name_is_current); |
396 | 0 | } |
397 | | |
398 | | static int |
399 | | generic_pread(gp_file *f, size_t count, gs_offset_t offset, void *buf) |
400 | 0 | { |
401 | 0 | int c; |
402 | 0 | int64_t os, curroff = gp_ftell(f); |
403 | 0 | if (curroff < 0) return curroff; |
404 | | |
405 | 0 | os = gp_fseek(f, offset, 0); |
406 | 0 | if (os < 0) return os; |
407 | | |
408 | 0 | c = gp_fread(buf, 1, count, f); |
409 | 0 | if (c < 0) return c; |
410 | | |
411 | 0 | os = gp_fseek(f, curroff, 0); |
412 | 0 | if (os < 0) return os; |
413 | | |
414 | 0 | return c; |
415 | 0 | } |
416 | | |
417 | | static int |
418 | | generic_pwrite(gp_file *f, size_t count, gs_offset_t offset, const void *buf) |
419 | 0 | { |
420 | 0 | int c; |
421 | 0 | int64_t os, curroff = gp_ftell(f); |
422 | 0 | if (curroff < 0) return curroff; |
423 | | |
424 | 0 | os = gp_fseek(f, offset, 0); |
425 | 0 | if (os < 0) return os; |
426 | | |
427 | 0 | c = gp_fwrite(buf, 1, count, f); |
428 | 0 | if (c < 0) return c; |
429 | | |
430 | 0 | os = gp_fseek(f, curroff, 0); |
431 | 0 | if (os < 0) return os; |
432 | | |
433 | 0 | return c; |
434 | 0 | } |
435 | | |
436 | | gp_file *gp_file_alloc(const gs_memory_t *mem, const gp_file_ops_t *prototype, size_t size, const char *cname) |
437 | 138k | { |
438 | 138k | gp_file *file = (gp_file *)gs_alloc_bytes(mem->thread_safe_memory, size, cname ? cname : "gp_file"); |
439 | 138k | if (file == NULL) |
440 | 0 | return NULL; |
441 | | |
442 | 138k | if (prototype) |
443 | 138k | file->ops = *prototype; |
444 | 138k | if (file->ops.pread == NULL) |
445 | 0 | file->ops.pread = generic_pread; |
446 | 138k | if (file->ops.pwrite == NULL) |
447 | 0 | file->ops.pwrite = generic_pwrite; |
448 | 138k | if (size > sizeof(*prototype)) |
449 | 138k | memset(((char *)file)+sizeof(*prototype), |
450 | 138k | 0, |
451 | 138k | size - sizeof(*prototype)); |
452 | 138k | file->memory = mem->thread_safe_memory; |
453 | | |
454 | 138k | return file; |
455 | 138k | } |
456 | | |
457 | | void gp_file_dealloc(gp_file *file) |
458 | 138k | { |
459 | 138k | if (file == NULL) |
460 | 0 | return; |
461 | | |
462 | 138k | if (file->buffer) |
463 | 0 | gs_free_object(file->memory, file->buffer, "gp_file"); |
464 | 138k | gs_free_object(file->memory, file, "gp_file"); |
465 | 138k | } |
466 | | |
467 | | int gp_fprintf(gp_file *f, const char *fmt, ...) |
468 | 0 | { |
469 | 0 | va_list args; |
470 | 0 | int n; |
471 | |
|
472 | 0 | if (f->buffer) |
473 | 0 | goto mid; |
474 | 0 | do { |
475 | 0 | n = f->buffer_size * 2; |
476 | 0 | if (n == 0) |
477 | 0 | n = 256; |
478 | 0 | gs_free_object(f->memory, f->buffer, "gp_file(buffer)"); |
479 | 0 | f->buffer = (char *)gs_alloc_bytes(f->memory, n, "gp_file(buffer)"); |
480 | 0 | if (f->buffer == NULL) |
481 | 0 | return -1; |
482 | 0 | f->buffer_size = n; |
483 | 0 | mid: |
484 | 0 | va_start(args, fmt); |
485 | 0 | n = vsnprintf(f->buffer, f->buffer_size, fmt, args); |
486 | 0 | va_end(args); |
487 | 0 | } while (n >= f->buffer_size); |
488 | 0 | return (f->ops.write)(f, 1, n, f->buffer); |
489 | 0 | } |
490 | | typedef struct { |
491 | | gp_file base; |
492 | | FILE *file; |
493 | | int (*close)(FILE *file); |
494 | | } gp_file_FILE; |
495 | | |
496 | | static int |
497 | | gp_file_FILE_close(gp_file *file_) |
498 | 1.99k | { |
499 | 1.99k | gp_file_FILE *file = (gp_file_FILE *)file_; |
500 | | |
501 | 1.99k | return (file->close)(file->file); |
502 | 1.99k | } |
503 | | |
504 | | static int |
505 | | gp_file_FILE_getc(gp_file *file_) |
506 | 0 | { |
507 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
508 | |
|
509 | 0 | return fgetc(file->file); |
510 | 0 | } |
511 | | |
512 | | static int |
513 | | gp_file_FILE_putc(gp_file *file_, int c) |
514 | 0 | { |
515 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
516 | |
|
517 | 0 | return fputc(c, file->file); |
518 | 0 | } |
519 | | |
520 | | static int |
521 | | gp_file_FILE_read(gp_file *file_, size_t size, unsigned int count, void *buf) |
522 | 366k | { |
523 | 366k | gp_file_FILE *file = (gp_file_FILE *)file_; |
524 | | |
525 | 366k | return fread(buf, size, count, file->file); |
526 | 366k | } |
527 | | |
528 | | static int |
529 | | gp_file_FILE_write(gp_file *file_, size_t size, unsigned int count, const void *buf) |
530 | 129k | { |
531 | 129k | gp_file_FILE *file = (gp_file_FILE *)file_; |
532 | | |
533 | 129k | return fwrite(buf, size, count, file->file); |
534 | 129k | } |
535 | | |
536 | | static int |
537 | | gp_file_FILE_seek(gp_file *file_, gs_offset_t offset, int whence) |
538 | 251k | { |
539 | 251k | gp_file_FILE *file = (gp_file_FILE *)file_; |
540 | | |
541 | 251k | return gp_fseek_impl(file->file, offset, whence); |
542 | 251k | } |
543 | | |
544 | | static gs_offset_t |
545 | | gp_file_FILE_tell(gp_file *file_) |
546 | 3.34k | { |
547 | 3.34k | gp_file_FILE *file = (gp_file_FILE *)file_; |
548 | | |
549 | 3.34k | return gp_ftell_impl(file->file); |
550 | 3.34k | } |
551 | | |
552 | | static int |
553 | | gp_file_FILE_eof(gp_file *file_) |
554 | 366k | { |
555 | 366k | gp_file_FILE *file = (gp_file_FILE *)file_; |
556 | | |
557 | 366k | return feof(file->file); |
558 | 366k | } |
559 | | |
560 | | static gp_file * |
561 | | gp_file_FILE_dup(gp_file *file_, const char *mode) |
562 | 0 | { |
563 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
564 | 0 | gp_file *file2 = gp_file_FILE_alloc(file->base.memory); |
565 | |
|
566 | 0 | if (gp_file_FILE_set(file2, gp_fdup_impl(file->file, mode), NULL)) |
567 | 0 | file2 = NULL; |
568 | |
|
569 | 0 | return file2; |
570 | 0 | } |
571 | | |
572 | | static int |
573 | | gp_file_FILE_seekable(gp_file *file_) |
574 | 0 | { |
575 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
576 | |
|
577 | 0 | return gp_fseekable_impl(file->file); |
578 | 0 | } |
579 | | |
580 | | static int |
581 | | gp_file_FILE_pread(gp_file *file_, size_t count, gs_offset_t offset, void *buf) |
582 | 0 | { |
583 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
584 | |
|
585 | 0 | return gp_pread_impl(buf, count, offset, file->file); |
586 | 0 | } |
587 | | |
588 | | static int |
589 | | gp_file_FILE_pwrite(gp_file *file_, size_t count, gs_offset_t offset, const void *buf) |
590 | 0 | { |
591 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
592 | |
|
593 | 0 | return gp_pwrite_impl(buf, count, offset, file->file); |
594 | 0 | } |
595 | | |
596 | | static int |
597 | | gp_file_FILE_is_char_buffered(gp_file *file_) |
598 | 0 | { |
599 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
600 | 0 | struct stat rstat; |
601 | |
|
602 | 0 | if (fstat(fileno(file->file), &rstat) != 0) |
603 | 0 | return ERRC; |
604 | 0 | return S_ISCHR(rstat.st_mode); |
605 | 0 | } |
606 | | |
607 | | static void |
608 | | gp_file_FILE_fflush(gp_file *file_) |
609 | 14.9k | { |
610 | 14.9k | gp_file_FILE *file = (gp_file_FILE *)file_; |
611 | | |
612 | 14.9k | fflush(file->file); |
613 | 14.9k | } |
614 | | |
615 | | static int |
616 | | gp_file_FILE_ferror(gp_file *file_) |
617 | 485k | { |
618 | 485k | gp_file_FILE *file = (gp_file_FILE *)file_; |
619 | | |
620 | 485k | return ferror(file->file); |
621 | 485k | } |
622 | | |
623 | | static FILE * |
624 | | gp_file_FILE_get_file(gp_file *file_) |
625 | 2.68k | { |
626 | 2.68k | gp_file_FILE *file = (gp_file_FILE *)file_; |
627 | | |
628 | 2.68k | return file->file; |
629 | 2.68k | } |
630 | | |
631 | | static void |
632 | | gp_file_FILE_clearerr(gp_file *file_) |
633 | 669 | { |
634 | 669 | gp_file_FILE *file = (gp_file_FILE *)file_; |
635 | | |
636 | 669 | clearerr(file->file); |
637 | 669 | } |
638 | | |
639 | | static gp_file * |
640 | | gp_file_FILE_reopen(gp_file *file_, const char *fname, const char *mode) |
641 | 0 | { |
642 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
643 | |
|
644 | 0 | file->file = freopen(fname, mode, file->file); |
645 | 0 | if (file->file == NULL) { |
646 | 0 | gp_file_dealloc(file_); |
647 | 0 | return NULL; |
648 | 0 | } |
649 | 0 | return file_; |
650 | 0 | } |
651 | | |
652 | | static const gp_file_ops_t gp_file_FILE_prototype = |
653 | | { |
654 | | gp_file_FILE_close, |
655 | | gp_file_FILE_getc, |
656 | | gp_file_FILE_putc, |
657 | | gp_file_FILE_read, |
658 | | gp_file_FILE_write, |
659 | | gp_file_FILE_seek, |
660 | | gp_file_FILE_tell, |
661 | | gp_file_FILE_eof, |
662 | | gp_file_FILE_dup, |
663 | | gp_file_FILE_seekable, |
664 | | gp_file_FILE_pread, |
665 | | gp_file_FILE_pwrite, |
666 | | gp_file_FILE_is_char_buffered, |
667 | | gp_file_FILE_fflush, |
668 | | gp_file_FILE_ferror, |
669 | | gp_file_FILE_get_file, |
670 | | gp_file_FILE_clearerr, |
671 | | gp_file_FILE_reopen |
672 | | }; |
673 | | |
674 | | gp_file *gp_file_FILE_alloc(const gs_memory_t *mem) |
675 | 138k | { |
676 | 138k | return gp_file_alloc(mem->non_gc_memory, |
677 | 138k | &gp_file_FILE_prototype, |
678 | 138k | sizeof(gp_file_FILE), |
679 | 138k | "gp_file_FILE"); |
680 | 138k | } |
681 | | |
682 | | int gp_file_FILE_set(gp_file *file_, FILE *f, int (*close)(FILE *)) |
683 | 138k | { |
684 | 138k | gp_file_FILE *file = (gp_file_FILE *)file_; |
685 | | |
686 | 138k | if (f == NULL) { |
687 | 136k | gp_file_dealloc(file_); |
688 | 136k | return 1; |
689 | 136k | } |
690 | | |
691 | 1.99k | file->file = f; |
692 | 1.99k | file->close = close ? close : fclose; |
693 | | |
694 | 1.99k | return 0; |
695 | 138k | } |
696 | | |
697 | | char *gp_fgets(char *buffer, size_t n, gp_file *f) |
698 | 0 | { |
699 | 0 | int c = EOF; |
700 | 0 | char *b = buffer; |
701 | 0 | while (n > 1) { |
702 | 0 | c = gp_fgetc(f); |
703 | 0 | if (c == 0) |
704 | 0 | break; |
705 | 0 | *b++ = c; |
706 | 0 | n--; |
707 | 0 | } |
708 | 0 | if (c == EOF && b == buffer) |
709 | 0 | return NULL; |
710 | 0 | if (gp_ferror(f)) |
711 | 0 | return NULL; |
712 | 0 | if (n > 0) |
713 | 0 | *b++ = 0; |
714 | 0 | return buffer; |
715 | 0 | } |
716 | | |
717 | | gp_file * |
718 | | gp_fopen(const gs_memory_t *mem, const char *fname, const char *mode) |
719 | 367k | { |
720 | 367k | gp_file *file = NULL; |
721 | 367k | gs_lib_ctx_t *ctx = mem->gs_lib_ctx; |
722 | 367k | gs_fs_list_t *fs = ctx->core->fs; |
723 | | |
724 | 367k | if (gp_validate_path(mem, fname, mode) != 0) |
725 | 230k | return NULL; |
726 | | |
727 | 137k | for (fs = ctx->core->fs; fs != NULL; fs = fs->next) |
728 | 137k | { |
729 | 137k | int code = 0; |
730 | 137k | if (fs->fs.open_file) |
731 | 137k | code = fs->fs.open_file(mem, fs->secret, fname, mode, &file); |
732 | 137k | if (code < 0) |
733 | 136k | return NULL; |
734 | 683 | if (file != NULL) |
735 | 683 | break; |
736 | 683 | } |
737 | | |
738 | 683 | return file; |
739 | 137k | } |
740 | | |
741 | | gp_file * |
742 | | gp_open_printer(const gs_memory_t *mem, |
743 | | char fname[gp_file_name_sizeof], |
744 | | int binary_mode) |
745 | 645 | { |
746 | 645 | gp_file *file = NULL; |
747 | 645 | gs_lib_ctx_t *ctx = mem->gs_lib_ctx; |
748 | 645 | gs_fs_list_t *fs = ctx->core->fs; |
749 | | |
750 | 645 | if (gp_validate_path(mem, fname, binary_mode ? "wb" : "w") != 0) |
751 | 0 | return NULL; |
752 | | |
753 | 645 | for (fs = ctx->core->fs; fs != NULL; fs = fs->next) |
754 | 645 | { |
755 | 645 | int code = 0; |
756 | 645 | if (fs->fs.open_printer) |
757 | 645 | code = fs->fs.open_printer(mem, fs->secret, fname, binary_mode, &file); |
758 | 645 | if (code < 0) |
759 | 0 | return NULL; |
760 | 645 | if (file != NULL) |
761 | 645 | break; |
762 | 645 | } |
763 | | |
764 | 645 | return file; |
765 | 645 | } |
766 | | |
767 | | static gp_file * |
768 | | do_open_scratch_file(const gs_memory_t *mem, |
769 | | const char *prefix, |
770 | | char *fname, |
771 | | const char *mode, |
772 | | int rm) |
773 | 669 | { |
774 | 669 | gp_file *file = NULL; |
775 | 669 | gs_lib_ctx_t *ctx = mem->gs_lib_ctx; |
776 | 669 | gs_fs_list_t *fs = ctx->core->fs; |
777 | 669 | int code = 0; |
778 | | |
779 | | /* If the prefix is absolute, then we must check it's a permissible |
780 | | * path. If not, we're OK. */ |
781 | 669 | if (gp_file_name_is_absolute(prefix, strlen(prefix)) && |
782 | 669 | gp_validate_path(mem, prefix, mode) != 0) |
783 | 0 | return NULL; |
784 | | |
785 | 669 | for (fs = ctx->core->fs; fs != NULL; fs = fs->next) |
786 | 669 | { |
787 | 669 | if (fs->fs.open_scratch) |
788 | 669 | code = fs->fs.open_scratch(mem, fs->secret, prefix, fname, mode, rm, &file); |
789 | 669 | if (code < 0) |
790 | 0 | return NULL; |
791 | 669 | if (file != NULL) |
792 | 669 | break; |
793 | 669 | } |
794 | | |
795 | 669 | if (file == NULL) { |
796 | | /* The file failed to open. Don't add it to the list. */ |
797 | 669 | } else if (rm) { |
798 | | /* This file has already been deleted by the underlying system. |
799 | | * We don't need to add it to the lists as it will never be |
800 | | * deleted manually, nor do we need to tidy it up on closedown. */ |
801 | 669 | } else { |
802 | | /* This file was not requested to be deleted. We add it to the |
803 | | * list so that it will either be deleted by any future call to |
804 | | * zdeletefile, OR on closedown. */ |
805 | | /* Add the scratch file name to the lists. We can't do this any |
806 | | * earlier as we didn't know the name until now! Unfortunately |
807 | | * that makes cleanup harder. */ |
808 | 669 | code = gs_add_control_path_flags(mem, gs_permit_file_control, fname, |
809 | 669 | gs_path_control_flag_is_scratch_file); |
810 | 669 | if (code >= 0) |
811 | 669 | code = gs_add_control_path_flags(mem, gs_permit_file_reading, fname, |
812 | 669 | gs_path_control_flag_is_scratch_file); |
813 | 669 | if (code >= 0) |
814 | 669 | code = gs_add_control_path_flags(mem, gs_permit_file_writing, fname, |
815 | 669 | gs_path_control_flag_is_scratch_file); |
816 | | |
817 | 669 | if (code < 0) { |
818 | 0 | gp_fclose(file); |
819 | 0 | file = NULL; |
820 | | /* Call directly through to the unlink implementation. We know |
821 | | * we're 'permitted' to do this, but we might not be on all the |
822 | | * required permit lists because of the failure. The only bad |
823 | | * thing here, is that we're deleting an fname that might not |
824 | | * have come from the filing system itself. */ |
825 | 0 | if (fname && fname[0]) |
826 | 0 | gp_unlink_impl(ctx->memory, fname); |
827 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_control, fname, |
828 | 0 | gs_path_control_flag_is_scratch_file); |
829 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, fname, |
830 | 0 | gs_path_control_flag_is_scratch_file); |
831 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, fname, |
832 | 0 | gs_path_control_flag_is_scratch_file); |
833 | 0 | } |
834 | 669 | } |
835 | | |
836 | 669 | return file; |
837 | 669 | } |
838 | | |
839 | | gp_file * |
840 | | gp_open_scratch_file(const gs_memory_t *mem, |
841 | | const char *prefix, |
842 | | char *fname, |
843 | | const char *mode) |
844 | 669 | { |
845 | 669 | return do_open_scratch_file(mem, prefix, fname, mode, 0); |
846 | 669 | } |
847 | | |
848 | | gp_file * |
849 | | gp_open_scratch_file_rm(const gs_memory_t *mem, |
850 | | const char *prefix, |
851 | | char *fname, |
852 | | const char *mode) |
853 | 0 | { |
854 | 0 | return do_open_scratch_file(mem, prefix, fname, mode, 1); |
855 | 0 | } |
856 | | |
857 | | int |
858 | | gp_stat(const gs_memory_t *mem, const char *path, struct stat *buf) |
859 | 0 | { |
860 | 0 | if (gp_validate_path(mem, path, "r") != 0) { |
861 | 0 | return -1; |
862 | 0 | } |
863 | | |
864 | 0 | return gp_stat_impl(mem, path, buf); |
865 | 0 | } |
866 | | |
867 | | file_enum * |
868 | | gp_enumerate_files_init(gs_memory_t *mem, const char *pat, uint patlen) |
869 | 7.51k | { |
870 | 7.51k | return gp_enumerate_files_init_impl(mem, pat, patlen); |
871 | 7.51k | } |
872 | | |
873 | | uint |
874 | | gp_enumerate_files_next(gs_memory_t *mem, file_enum * pfen, char *ptr, uint maxlen) |
875 | 7.51k | { |
876 | 7.51k | uint code = 0; |
877 | | |
878 | 7.51k | while (code == 0) { |
879 | 7.51k | code = gp_enumerate_files_next_impl(mem, pfen, ptr, maxlen); |
880 | 7.51k | if (code == ~0) break; |
881 | 0 | if (code > 0) { |
882 | 0 | if (gp_validate_path_len(mem, ptr, code, "r") != 0) |
883 | 0 | code = 0; |
884 | 0 | } |
885 | 0 | } |
886 | 7.51k | return code; |
887 | 7.51k | } |
888 | | void |
889 | | gp_enumerate_files_close(gs_memory_t *mem, file_enum * pfen) |
890 | 7.51k | { |
891 | 7.51k | gp_enumerate_files_close_impl(mem, pfen); |
892 | 7.51k | } |
893 | | |
894 | | /* Path validation: (FIXME: Move this somewhere better) |
895 | | * |
896 | | * The only wildcard we accept is '*'. |
897 | | * |
898 | | * A '*' at the end of the path means "in this directory, |
899 | | * or any subdirectory". Anywhere else it means "a sequence of |
900 | | * characters not including a director separator". |
901 | | * |
902 | | * A sequence of multiple '*'s is equivalent to a single one. |
903 | | * |
904 | | * Matching on '*' is simplistic; the matching sequence will end |
905 | | * as soon as we meet an instance of a character that follows |
906 | | * the '*' in a pattern. i.e. "foo*bar" will fail to match "fooabbar" |
907 | | * as the '*' will be held to match just 'a'. |
908 | | * |
909 | | * There is no way of specifying a literal '*'; if you find yourself |
910 | | * wanting to do this, slap yourself until you come to your senses. |
911 | | * |
912 | | * Due to the difficulties of using both * and / in writing C comments, |
913 | | * I shall use \ as the directory separator in the examples below, but |
914 | | * in practice it means "the directory separator for the current |
915 | | * platform". |
916 | | * |
917 | | * Pattern Match example |
918 | | * * any file, in any directory at all. |
919 | | * foo\bar a file, foo\bar. |
920 | | * foo\bar\ any file within foo\bar\, but no subdirectories. |
921 | | * foo\bar\* any file within foo\bar\ or any subdirectory thereof. |
922 | | * foo\*\bar any file 'bar' within any single subdirectory of foo |
923 | | * (i.e. foo\baz\bar, but not foo\baz\whoop\bar) |
924 | | * foo\out*.tif e.g. foo\out1.tif |
925 | | * foo\out*.*.tif* e.g. foo\out1.(Red).tif |
926 | | */ |
927 | | |
928 | | static int |
929 | | validate(const gs_memory_t *mem, |
930 | | const char *path, |
931 | | gs_path_control_t type) |
932 | 546k | { |
933 | 546k | gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core; |
934 | 546k | gs_path_control_set_t *control; |
935 | 546k | unsigned int i, n; |
936 | | |
937 | 546k | switch (type) { |
938 | 544k | case gs_permit_file_reading: |
939 | 544k | control = &core->permit_reading; |
940 | 544k | break; |
941 | 645 | case gs_permit_file_writing: |
942 | 645 | control = &core->permit_writing; |
943 | 645 | break; |
944 | 1.33k | case gs_permit_file_control: |
945 | 1.33k | control = &core->permit_control; |
946 | 1.33k | break; |
947 | 0 | default: |
948 | 0 | return gs_error_unknownerror; |
949 | 546k | } |
950 | | |
951 | 546k | n = control->num; |
952 | 17.3M | for (i = 0; i < n; i++) { |
953 | 16.8M | const char *a = path; |
954 | 16.8M | const char *b = control->entry[i].path; |
955 | 38.5M | while (1) { |
956 | 38.5M | if (*a == 0) { |
957 | 645 | if (*b == 0) |
958 | | /* PATH=abc pattern=abc */ |
959 | 645 | goto found; /* Bingo! */ |
960 | 0 | else |
961 | | /* PATH=abc pattern=abcd */ |
962 | 0 | break; /* No match */ |
963 | 38.5M | } else if (*b == '*') { |
964 | 85.5k | if (b[1] == '*') { |
965 | | /* Multiple '*'s are taken to mean the |
966 | | * output from a printf. */ |
967 | 0 | b++; |
968 | 0 | while (b[1] == '*') |
969 | 0 | b++; |
970 | | /* Skip over the permissible matching chars */ |
971 | 0 | while (*a && |
972 | 0 | ((*a == ' ' || *a == '-' || *a == '+' || |
973 | 0 | (*a >= '0' && *a <= '9') || |
974 | 0 | (*a >= 'a' && *a <= 'f') || |
975 | 0 | (*a >= 'A' && *a <= 'F')))) |
976 | 0 | a++; |
977 | 0 | if (b[1] == 0 && *a == 0) |
978 | | /* PATH=abc<%d> pattern=abc** */ |
979 | 0 | goto found; |
980 | 0 | if (*a == 0) |
981 | 0 | break; /* No match */ |
982 | 85.5k | } else { |
983 | 85.5k | if (b[1] == 0) |
984 | | /* PATH=abc???? pattern=abc* */ |
985 | 85.5k | goto found; |
986 | | /* Skip over anything except NUL, directory |
987 | | * separator, and the next char to match. */ |
988 | 0 | while (*a && !gs_file_name_check_separator(a, 1, a) && *a != b[1]) |
989 | 0 | a++; |
990 | 0 | if (*a == 0 || *a == gs_file_name_check_separator(a, 1, a)) |
991 | 0 | break; /* No match */ |
992 | 0 | } |
993 | | /* Continue matching */ |
994 | 0 | a--; /* Subtract 1 as the loop will increment it again later */ |
995 | 38.4M | } else if (*b == 0) { |
996 | 84.2k | if (b != control->entry[i].path && |
997 | 84.2k | gs_file_name_check_separator(b, -1, b)) { |
998 | 0 | const char *a2 = a; |
999 | 0 | const char *aend = path + strlen(path); |
1000 | 0 | while (aend != a2 && !gs_file_name_check_separator(a2, 1, a2)) |
1001 | 0 | a2++; |
1002 | | /* If the control path ends in a directory separator and we've scanned |
1003 | | to the end of the candidate path with no further directory separators |
1004 | | found, we have a match |
1005 | | */ |
1006 | 0 | if (aend == a2) |
1007 | | /* PATH=abc/? pattern=abc/ */ |
1008 | 0 | goto found; /* Bingo! */ |
1009 | 0 | } |
1010 | | /* PATH=abcd pattern=abc */ |
1011 | 84.2k | break; /* No match */ |
1012 | 38.3M | } else if (gs_file_name_check_separator(a, 1, a) == 1 |
1013 | 38.3M | && gs_file_name_check_separator(b, 1, b) == 1) { |
1014 | | /* On Windows we can get random combinations of "/" and "\" as directory |
1015 | | * separators, and we want "C:\" to match C:/" hence using the pair of |
1016 | | * gs_file_name_check_separator() calls above */ |
1017 | | /* Annoyingly, we can also end up with a combination of explicitly escaped |
1018 | | * '\' characters, and not escaped. So we also need "C:\\" to match "C:\" |
1019 | | * and "C:/" - hence we need to check for, and skip over the |
1020 | | * the extra '\' character - I'm reticent to change the upstream code that |
1021 | | * adds the explicit escape, because that could have unforeseen side effects |
1022 | | * elsewhere. */ |
1023 | 4.97M | if (*(a + 1) != 0 && gs_file_name_check_separator(a + 1, 1, a + 1) == 1) |
1024 | 0 | a++; |
1025 | 4.97M | if (*(b + 1) != 0 && gs_file_name_check_separator(b + 1, 1, b + 1) == 1) |
1026 | 0 | b++; |
1027 | 33.4M | } else if (*a != *b) { |
1028 | 16.6M | break; |
1029 | 16.6M | } |
1030 | 21.7M | a++, b++; |
1031 | 21.7M | } |
1032 | 16.8M | } |
1033 | 460k | return gs_error_invalidfileaccess; |
1034 | | |
1035 | 86.2k | found: |
1036 | 86.2k | return control->entry[i].flags; |
1037 | 546k | } |
1038 | | |
1039 | | int |
1040 | | gp_validate_path_len(const gs_memory_t *mem, |
1041 | | const char *path, |
1042 | | const uint len, |
1043 | | const char *mode) |
1044 | 369k | { |
1045 | 369k | char *buffer, *bufferfull; |
1046 | 369k | uint rlen; |
1047 | 369k | int code = 0; |
1048 | 369k | const char *cdirstr = gp_file_name_current(); |
1049 | 369k | int cdirstrl = strlen(cdirstr); |
1050 | 369k | const char *dirsepstr = gp_file_name_separator(); |
1051 | 369k | int dirsepstrl = strlen(dirsepstr); |
1052 | 369k | int prefix_len = cdirstrl + dirsepstrl; |
1053 | | |
1054 | | /* mem->gs_lib_ctx can be NULL when we're called from mkromfs */ |
1055 | 369k | if (mem->gs_lib_ctx == NULL || |
1056 | 369k | mem->gs_lib_ctx->core->path_control_active == 0) |
1057 | 53.2k | return 0; |
1058 | | |
1059 | | /* For current directory accesses, we need handle both a "bare" name, |
1060 | | * and one with a cwd prefix (in Unix terms, both "myfile.ps" and |
1061 | | * "./myfile.ps". |
1062 | | * |
1063 | | * So we check up front if it's absolute, then just use that. |
1064 | | * If it includes cwd prefix, we try that, then remove the prefix |
1065 | | * and try again. |
1066 | | * If it doesn't include the cwd prefix, we try it, then add the |
1067 | | * prefix and try again. |
1068 | | * To facilitate that, we allocate a large enough buffer to take |
1069 | | * the path *and* the prefix up front. |
1070 | | */ |
1071 | 316k | if (gp_file_name_is_absolute(path, len)) { |
1072 | | /* Absolute path, we don't need anything extra */ |
1073 | 86.2k | prefix_len = cdirstrl = dirsepstrl = 0; |
1074 | 86.2k | } |
1075 | 230k | else if (len > prefix_len && !memcmp(path, cdirstr, cdirstrl) |
1076 | 230k | && !memcmp(path + cdirstrl, dirsepstr, dirsepstrl)) { |
1077 | 0 | prefix_len = 0; |
1078 | 0 | } |
1079 | 316k | rlen = len+1; |
1080 | 316k | bufferfull = (char *)gs_alloc_bytes(mem->thread_safe_memory, rlen + prefix_len, "gp_validate_path"); |
1081 | 316k | if (bufferfull == NULL) |
1082 | 0 | return gs_error_VMerror; |
1083 | | |
1084 | 316k | buffer = bufferfull + prefix_len; |
1085 | 316k | if (gp_file_name_reduce(path, (uint)len, buffer, &rlen) != gp_combine_success) |
1086 | 0 | return gs_error_invalidfileaccess; |
1087 | 316k | buffer[rlen] = 0; |
1088 | | |
1089 | 546k | while (1) { |
1090 | 546k | switch (mode[0]) |
1091 | 546k | { |
1092 | 544k | case 'r': /* Read */ |
1093 | 544k | code = validate(mem, buffer, gs_permit_file_reading); |
1094 | 544k | break; |
1095 | 645 | case 'w': /* Write */ |
1096 | 645 | code = validate(mem, buffer, gs_permit_file_writing); |
1097 | 645 | break; |
1098 | 0 | case 'a': /* Append needs reading and writing */ |
1099 | 0 | code = (validate(mem, buffer, gs_permit_file_reading) | |
1100 | 0 | validate(mem, buffer, gs_permit_file_writing)); |
1101 | 0 | break; |
1102 | 0 | case 'c': /* "Control" */ |
1103 | 0 | code = validate(mem, buffer, gs_permit_file_control); |
1104 | 0 | break; |
1105 | 1.33k | case 'd': /* "Delete" (special case of control) */ |
1106 | 1.33k | code = validate(mem, buffer, gs_permit_file_control); |
1107 | 1.33k | break; |
1108 | 0 | case 'f': /* "Rename from" */ |
1109 | 0 | code = (validate(mem, buffer, gs_permit_file_writing) | |
1110 | 0 | validate(mem, buffer, gs_permit_file_control)); |
1111 | 0 | break; |
1112 | 0 | case 't': /* "Rename to" */ |
1113 | 0 | code = (validate(mem, buffer, gs_permit_file_writing) | |
1114 | 0 | validate(mem, buffer, gs_permit_file_control)); |
1115 | 0 | break; |
1116 | 0 | default: |
1117 | 0 | errprintf(mem, "gp_validate_path: Unknown mode='%s'\n", mode); |
1118 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1119 | 546k | } |
1120 | 546k | if (code < 0 && prefix_len > 0 && buffer > bufferfull) { |
1121 | 230k | buffer = bufferfull; |
1122 | 230k | memcpy(buffer, cdirstr, cdirstrl); |
1123 | 230k | memcpy(buffer + cdirstrl, dirsepstr, dirsepstrl); |
1124 | 230k | continue; |
1125 | 230k | } |
1126 | 316k | else if (code < 0 && cdirstrl > 0 && prefix_len == 0 && buffer == bufferfull) { |
1127 | 0 | buffer = bufferfull + cdirstrl + dirsepstrl; |
1128 | 0 | continue; |
1129 | 0 | } |
1130 | 316k | break; |
1131 | 546k | } |
1132 | 316k | if (code > 0 && (mode[0] == 'd' || mode[0] == 'f') && |
1133 | 316k | (code & gs_path_control_flag_is_scratch_file) != 0) { |
1134 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, buffer, |
1135 | 0 | gs_path_control_flag_is_scratch_file); |
1136 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, buffer, |
1137 | 0 | gs_path_control_flag_is_scratch_file); |
1138 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_control, buffer, |
1139 | 0 | gs_path_control_flag_is_scratch_file); |
1140 | 0 | } |
1141 | | |
1142 | 316k | gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path"); |
1143 | 316k | #ifdef EACCES |
1144 | 316k | if (code == gs_error_invalidfileaccess) |
1145 | 230k | errno = EACCES; |
1146 | 316k | #endif |
1147 | | |
1148 | 316k | return code < 0 ? code : 0; |
1149 | 316k | } |
1150 | | |
1151 | | int |
1152 | | gp_validate_path(const gs_memory_t *mem, |
1153 | | const char *path, |
1154 | | const char *mode) |
1155 | 369k | { |
1156 | 369k | return gp_validate_path_len(mem, path, strlen(path), mode); |
1157 | 369k | } |
1158 | | |
1159 | | int |
1160 | | gp_unlink(gs_memory_t *mem, const char *fname) |
1161 | 1.33k | { |
1162 | 1.33k | if (gp_validate_path(mem, fname, "d") != 0) |
1163 | 0 | return gs_error_invalidaccess; |
1164 | | |
1165 | 1.33k | return gp_unlink_impl(mem, fname); |
1166 | 1.33k | } |
1167 | | |
1168 | | int |
1169 | | gp_rename(gs_memory_t *mem, const char *from, const char *to) |
1170 | 0 | { |
1171 | | /* Always check 'to' before 'from', in case 'from' is a tempfile, |
1172 | | * and testing it might remove it from the list! */ |
1173 | 0 | if (gp_validate_path(mem, to, "t") != 0) |
1174 | 0 | return gs_error_invalidaccess; |
1175 | 0 | if (gp_validate_path(mem, from, "f") != 0) |
1176 | 0 | return gs_error_invalidaccess; |
1177 | | |
1178 | 0 | return gp_rename_impl(mem, from, to); |
1179 | 0 | } |