/src/ghostpdl/base/gpmisc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, 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 | 287k | { |
39 | 287k | int max_len = *plen; |
40 | 287k | int code = gp_getenv("TMPDIR", ptr, plen); |
41 | | |
42 | 287k | if (code != 1) |
43 | 0 | return code; |
44 | 287k | *plen = max_len; |
45 | 287k | return gp_getenv("TEMP", ptr, plen); |
46 | 287k | } |
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 | 3.17G | { |
109 | 3.17G | if (bpe - *bp < len) |
110 | 11.2k | return false; |
111 | 3.17G | memcpy(*bp, *ip, len); |
112 | 3.17G | *bp += len; |
113 | 3.17G | *ip += len; |
114 | 3.17G | return true; |
115 | 3.17G | } |
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 | 2.06G | { uint slen = 0; |
121 | 15.0G | for (slen = 0; (*ip - ipe) * direction < 0; (*ip) += direction) |
122 | 14.6G | if((slen = gs_file_name_check_separator(*ip, ipe - *ip, item)) != 0) |
123 | 1.67G | break; |
124 | 2.06G | return slen; |
125 | 2.06G | } |
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 | 226M | { |
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 | 226M | char *bp = buffer, *bpe = buffer + *blen; |
163 | 226M | const char *ip, *ipe; |
164 | 226M | uint slen; |
165 | 226M | uint infix_type = 0; /* 0=none, 1=current, 2=parent. */ |
166 | 226M | uint infix_len = 0; |
167 | 226M | uint rlen = gp_file_name_root(fname, flen); |
168 | | /* We need a special handling of infixes only immediately after a drive. */ |
169 | | |
170 | 226M | 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 | 226M | if (rlen != 0) { |
181 | | /* 'fname' is absolute, ignore the prefix. */ |
182 | 0 | ip = fname; |
183 | 0 | ipe = fname + flen; |
184 | 226M | } else { |
185 | | /* Concatenate with prefix. */ |
186 | 226M | ip = prefix; |
187 | 226M | ipe = prefix + plen; |
188 | 226M | rlen = gp_file_name_root(prefix, plen); |
189 | 226M | } |
190 | 226M | if (!append(&bp, bpe, &ip, rlen)) |
191 | 0 | return gp_combine_small_buffer; |
192 | 1.46G | for (;;) { |
193 | 1.46G | const char *item = ip; |
194 | 1.46G | uint ilen; |
195 | | |
196 | 1.46G | slen = search_separator(&ip, ipe, item, 1); |
197 | 1.46G | ilen = ip - item; |
198 | 1.46G | if (ilen == 0 && !gp_file_name_is_empty_item_meanful()) { |
199 | 198k | ip += slen; |
200 | 198k | slen = 0; |
201 | 1.46G | } else if (gp_file_name_is_current(item, ilen)) { |
202 | | /* Skip the reference to 'current', except the starting one. |
203 | | * We keep the starting 'current' for platforms, which |
204 | | * require relative paths to start with it. |
205 | | */ |
206 | 29.1k | if (bp == buffer) { |
207 | 11.2k | if (!append(&bp, bpe, &item, ilen)) |
208 | 0 | return gp_combine_small_buffer; |
209 | 11.2k | infix_type = 1; |
210 | 11.2k | infix_len = ilen; |
211 | 17.8k | } else { |
212 | 17.8k | ip += slen; |
213 | 17.8k | slen = 0; |
214 | 17.8k | } |
215 | 1.46G | } else if (!gp_file_name_is_parent(item, ilen)) { |
216 | 1.46G | if (!append(&bp, bpe, &item, ilen)) |
217 | 0 | return gp_combine_small_buffer; |
218 | | /* The 'item' pointer is now broken; it may be restored using 'ilen'. */ |
219 | 1.46G | } else if (bp == buffer + rlen + infix_len) { |
220 | | /* Input is a parent and the output only contains a root and an infix. */ |
221 | 15 | if (rlen != 0) |
222 | 0 | return gp_combine_cant_handle; |
223 | 15 | switch (infix_type) { |
224 | 5 | case 1: |
225 | | /* Replace the infix with parent. */ |
226 | 5 | bp = buffer + rlen; /* Drop the old infix, if any. */ |
227 | 5 | infix_len = 0; |
228 | | /* Falls through. */ |
229 | 15 | case 0: |
230 | | /* We have no infix, start with parent. */ |
231 | 15 | if ((no_sibling && ipe == fname + flen && flen != 0) || |
232 | 15 | !gp_file_name_is_parent_allowed()) |
233 | 0 | return gp_combine_cant_handle; |
234 | | /* Falls through. */ |
235 | 15 | case 2: |
236 | | /* Append one more parent - falls through. */ |
237 | 15 | DO_NOTHING; |
238 | 15 | } |
239 | 15 | if (!append(&bp, bpe, &item, ilen)) |
240 | 0 | return gp_combine_small_buffer; |
241 | 15 | infix_type = 2; |
242 | 15 | infix_len += ilen; |
243 | | /* Recompute the separator length. We cannot use the old slen on Mac OS. */ |
244 | 15 | slen = gs_file_name_check_separator(ip, ipe - ip, ip); |
245 | 11.5k | } else { |
246 | | /* Input is a parent and the output continues after infix. */ |
247 | | /* Unappend the last separator and the last item. */ |
248 | 11.5k | uint slen1 = gs_file_name_check_separator(bp, buffer + rlen - bp, bp); /* Backward search. */ |
249 | 11.5k | char *bie = bp - slen1; |
250 | | |
251 | 11.5k | bp = bie; |
252 | 11.5k | DISCARD(search_separator((const char **)&bp, buffer + rlen, bp, -1)); |
253 | | /* The cast above quiets a gcc warning. We believe it's a bug in the compiler. */ |
254 | | /* Skip the input with separator. We cannot use slen on Mac OS. */ |
255 | 11.5k | ip += gs_file_name_check_separator(ip, ipe - ip, ip); |
256 | 11.5k | if (no_sibling) { |
257 | 0 | const char *p = ip; |
258 | |
|
259 | 0 | DISCARD(search_separator(&p, ipe, ip, 1)); |
260 | 0 | if (p - ip != bie - bp || memcmp(ip, bp, p - ip)) |
261 | 0 | return gp_combine_cant_handle; |
262 | 0 | } |
263 | 11.5k | slen = 0; |
264 | 11.5k | } |
265 | 1.46G | if (slen) { |
266 | 1.17G | if (bp == buffer + rlen + infix_len) |
267 | 11.2k | infix_len += slen; |
268 | 1.17G | if (!append(&bp, bpe, &ip, slen)) |
269 | 0 | return gp_combine_small_buffer; |
270 | 1.17G | } |
271 | 1.46G | if (ip == ipe) { |
272 | 314M | if (ipe == fname + flen) { |
273 | | /* All done. |
274 | | * Note that the case (prefix + plen == fname && flen == 0) |
275 | | * falls here without appending a separator. |
276 | | */ |
277 | 226M | const char *zero=""; |
278 | | |
279 | 226M | if (bp == buffer) { |
280 | | /* Must not return empty path. */ |
281 | 18 | const char *current = gp_file_name_current(); |
282 | 18 | int clen = strlen(current); |
283 | | |
284 | 18 | if (!append(&bp, bpe, ¤t, clen)) |
285 | 0 | return gp_combine_small_buffer; |
286 | 18 | } |
287 | 226M | *blen = bp - buffer; |
288 | 226M | if (!append(&bp, bpe, &zero, 1)) |
289 | 11.2k | return gp_combine_small_buffer; |
290 | 226M | return gp_combine_success; |
291 | 226M | } else { |
292 | | /* ipe == prefix + plen */ |
293 | | /* Switch to fname. */ |
294 | 87.4M | ip = fname; |
295 | 87.4M | ipe = fname + flen; |
296 | 87.4M | if (slen == 0) { |
297 | | /* Insert a separator. */ |
298 | 78.7M | const char *sep; |
299 | | |
300 | 78.7M | slen = search_separator(&ip, ipe, fname, 1); |
301 | 78.7M | sep = (slen != 0 ? gp_file_name_directory_separator() |
302 | 78.7M | : gp_file_name_separator()); |
303 | 78.7M | slen = strlen(sep); |
304 | 78.7M | if (bp == buffer + rlen + infix_len) |
305 | 0 | infix_len += slen; |
306 | 78.7M | if (!append(&bp, bpe, &sep, slen)) |
307 | 0 | return gp_combine_small_buffer; |
308 | 78.7M | ip = fname; /* Switch to fname. */ |
309 | 78.7M | } |
310 | 87.4M | } |
311 | 314M | } |
312 | 1.46G | } |
313 | 226M | } |
314 | | |
315 | | /* |
316 | | * Reduces parent references and current directory references when possible. |
317 | | * The trailing zero byte is being added. |
318 | | * |
319 | | * Examples : |
320 | | * "/gs/lib/../Resource/CMap/H" --> "/gs/Resource/CMap/H" |
321 | | * "C:/gs/lib/../Resource/CMap/H" --> "C:/gs/Resource/CMap/H" |
322 | | * "hard disk:gs:lib::Resource:CMap:H" --> |
323 | | * "hard disk:gs:Resource:CMap:H" |
324 | | * "DUA1:[GHOSTSCRIPT.LIB.-.RESOURCE.CMAP]H" --> |
325 | | * "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H" |
326 | | * "\\server\share/a/b///c/../d.e/./../x.e/././/../../y.z/v.v" --> |
327 | | * "\\server\share/a/y.z/v.v" |
328 | | * |
329 | | */ |
330 | | gp_file_name_combine_result |
331 | | gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen) |
332 | 139M | { |
333 | 139M | return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen); |
334 | 139M | } |
335 | | |
336 | | /* |
337 | | * Answers whether a file name is absolute (starts from a root). |
338 | | */ |
339 | | bool |
340 | | gp_file_name_is_absolute(const char *fname, uint flen) |
341 | 75.0M | { |
342 | 75.0M | return (gp_file_name_root(fname, flen) > 0); |
343 | 75.0M | } |
344 | | |
345 | | /* |
346 | | * Returns length of all starting parent references. |
347 | | */ |
348 | | static uint |
349 | | gp_file_name_prefix(const char *fname, uint flen, |
350 | | bool (*test)(const char *fname, uint flen)) |
351 | 53.1M | { |
352 | 53.1M | uint plen = gp_file_name_root(fname, flen), slen; |
353 | 53.1M | const char *ip, *ipe; |
354 | 53.1M | const char *item = fname; /* plen == flen could cause an indeterminizm. */ |
355 | | |
356 | 53.1M | if (plen > 0) |
357 | 37.1M | return 0; |
358 | 15.9M | ip = fname + plen; |
359 | 15.9M | ipe = fname + flen; |
360 | 15.9M | for (; ip < ipe; ) { |
361 | 15.9M | item = ip; |
362 | 15.9M | slen = search_separator(&ip, ipe, item, 1); |
363 | 15.9M | if (!(*test)(item, ip - item)) |
364 | 15.9M | break; |
365 | 5 | ip += slen; |
366 | 5 | } |
367 | 15.9M | return item - fname; |
368 | 53.1M | } |
369 | | |
370 | | /* |
371 | | * Returns length of all starting parent references. |
372 | | */ |
373 | | uint |
374 | | gp_file_name_parents(const char *fname, uint flen) |
375 | 53.1M | { |
376 | 53.1M | return gp_file_name_prefix(fname, flen, gp_file_name_is_parent); |
377 | 53.1M | } |
378 | | |
379 | | /* |
380 | | * Returns length of all starting cwd references. |
381 | | */ |
382 | | uint |
383 | | gp_file_name_cwds(const char *fname, uint flen) |
384 | 0 | { |
385 | 0 | return gp_file_name_prefix(fname, flen, gp_file_name_is_current); |
386 | 0 | } |
387 | | |
388 | | static int |
389 | | generic_pread(gp_file *f, size_t count, gs_offset_t offset, void *buf) |
390 | 0 | { |
391 | 0 | int c; |
392 | 0 | int64_t os, curroff = gp_ftell(f); |
393 | 0 | if (curroff < 0) return curroff; |
394 | | |
395 | 0 | os = gp_fseek(f, offset, 0); |
396 | 0 | if (os < 0) return os; |
397 | | |
398 | 0 | c = gp_fread(buf, 1, count, f); |
399 | 0 | if (c < 0) return c; |
400 | | |
401 | 0 | os = gp_fseek(f, curroff, 0); |
402 | 0 | if (os < 0) return os; |
403 | | |
404 | 0 | return c; |
405 | 0 | } |
406 | | |
407 | | static int |
408 | | generic_pwrite(gp_file *f, size_t count, gs_offset_t offset, const void *buf) |
409 | 0 | { |
410 | 0 | int c; |
411 | 0 | int64_t os, curroff = gp_ftell(f); |
412 | 0 | if (curroff < 0) return curroff; |
413 | | |
414 | 0 | os = gp_fseek(f, offset, 0); |
415 | 0 | if (os < 0) return os; |
416 | | |
417 | 0 | c = gp_fwrite(buf, 1, count, f); |
418 | 0 | if (c < 0) return c; |
419 | | |
420 | 0 | os = gp_fseek(f, curroff, 0); |
421 | 0 | if (os < 0) return os; |
422 | | |
423 | 0 | return c; |
424 | 0 | } |
425 | | |
426 | | gp_file *gp_file_alloc(const gs_memory_t *mem, const gp_file_ops_t *prototype, size_t size, const char *cname) |
427 | 74.7M | { |
428 | 74.7M | gp_file *file = (gp_file *)gs_alloc_bytes(mem->thread_safe_memory, size, cname ? cname : "gp_file"); |
429 | 74.7M | if (file == NULL) |
430 | 0 | return NULL; |
431 | | |
432 | 74.7M | if (prototype) |
433 | 74.7M | file->ops = *prototype; |
434 | 74.7M | if (file->ops.pread == NULL) |
435 | 0 | file->ops.pread = generic_pread; |
436 | 74.7M | if (file->ops.pwrite == NULL) |
437 | 0 | file->ops.pwrite = generic_pwrite; |
438 | 74.7M | if (size > sizeof(*prototype)) |
439 | 74.7M | memset(((char *)file)+sizeof(*prototype), |
440 | 74.7M | 0, |
441 | 74.7M | size - sizeof(*prototype)); |
442 | 74.7M | file->memory = mem->thread_safe_memory; |
443 | | |
444 | 74.7M | return file; |
445 | 74.7M | } |
446 | | |
447 | | void gp_file_dealloc(gp_file *file) |
448 | 74.7M | { |
449 | 74.7M | if (file == NULL) |
450 | 0 | return; |
451 | | |
452 | 74.7M | if (file->buffer) |
453 | 0 | gs_free_object(file->memory, file->buffer, "gp_file"); |
454 | 74.7M | gs_free_object(file->memory, file, "gp_file"); |
455 | 74.7M | } |
456 | | |
457 | | int gp_fprintf(gp_file *f, const char *fmt, ...) |
458 | 0 | { |
459 | 0 | va_list args; |
460 | 0 | int n; |
461 | |
|
462 | 0 | if (f->buffer) |
463 | 0 | goto mid; |
464 | 0 | do { |
465 | 0 | n = f->buffer_size * 2; |
466 | 0 | if (n == 0) |
467 | 0 | n = 256; |
468 | 0 | gs_free_object(f->memory, f->buffer, "gp_file(buffer)"); |
469 | 0 | f->buffer = (char *)gs_alloc_bytes(f->memory, n, "gp_file(buffer)"); |
470 | 0 | if (f->buffer == NULL) |
471 | 0 | return -1; |
472 | 0 | f->buffer_size = n; |
473 | 0 | mid: |
474 | 0 | va_start(args, fmt); |
475 | 0 | n = vsnprintf(f->buffer, f->buffer_size, fmt, args); |
476 | 0 | va_end(args); |
477 | 0 | } while (n >= f->buffer_size); |
478 | 0 | return (f->ops.write)(f, 1, n, f->buffer); |
479 | 0 | } |
480 | | typedef struct { |
481 | | gp_file base; |
482 | | FILE *file; |
483 | | int (*close)(FILE *file); |
484 | | } gp_file_FILE; |
485 | | |
486 | | static int |
487 | | gp_file_FILE_close(gp_file *file_) |
488 | 577k | { |
489 | 577k | gp_file_FILE *file = (gp_file_FILE *)file_; |
490 | | |
491 | 577k | return (file->close)(file->file); |
492 | 577k | } |
493 | | |
494 | | static int |
495 | | gp_file_FILE_getc(gp_file *file_) |
496 | 0 | { |
497 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
498 | |
|
499 | 0 | return fgetc(file->file); |
500 | 0 | } |
501 | | |
502 | | static int |
503 | | gp_file_FILE_putc(gp_file *file_, int c) |
504 | 17.8k | { |
505 | 17.8k | gp_file_FILE *file = (gp_file_FILE *)file_; |
506 | | |
507 | 17.8k | return fputc(c, file->file); |
508 | 17.8k | } |
509 | | |
510 | | static int |
511 | | gp_file_FILE_read(gp_file *file_, size_t size, unsigned int count, void *buf) |
512 | 814M | { |
513 | 814M | gp_file_FILE *file = (gp_file_FILE *)file_; |
514 | | |
515 | 814M | return fread(buf, size, count, file->file); |
516 | 814M | } |
517 | | |
518 | | static int |
519 | | gp_file_FILE_write(gp_file *file_, size_t size, unsigned int count, const void *buf) |
520 | 126M | { |
521 | 126M | gp_file_FILE *file = (gp_file_FILE *)file_; |
522 | | |
523 | 126M | return fwrite(buf, size, count, file->file); |
524 | 126M | } |
525 | | |
526 | | static int |
527 | | gp_file_FILE_seek(gp_file *file_, gs_offset_t offset, int whence) |
528 | 18.0M | { |
529 | 18.0M | gp_file_FILE *file = (gp_file_FILE *)file_; |
530 | | |
531 | 18.0M | return gp_fseek_impl(file->file, offset, whence); |
532 | 18.0M | } |
533 | | |
534 | | static gs_offset_t |
535 | | gp_file_FILE_tell(gp_file *file_) |
536 | 4.98M | { |
537 | 4.98M | gp_file_FILE *file = (gp_file_FILE *)file_; |
538 | | |
539 | 4.98M | return gp_ftell_impl(file->file); |
540 | 4.98M | } |
541 | | |
542 | | static int |
543 | | gp_file_FILE_eof(gp_file *file_) |
544 | 805M | { |
545 | 805M | gp_file_FILE *file = (gp_file_FILE *)file_; |
546 | | |
547 | 805M | return feof(file->file); |
548 | 805M | } |
549 | | |
550 | | static gp_file * |
551 | | gp_file_FILE_dup(gp_file *file_, const char *mode) |
552 | 0 | { |
553 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
554 | 0 | gp_file *file2 = gp_file_FILE_alloc(file->base.memory); |
555 | |
|
556 | 0 | if (gp_file_FILE_set(file2, gp_fdup_impl(file->file, mode), NULL)) |
557 | 0 | file2 = NULL; |
558 | |
|
559 | 0 | return file2; |
560 | 0 | } |
561 | | |
562 | | static int |
563 | | gp_file_FILE_seekable(gp_file *file_) |
564 | 4.18k | { |
565 | 4.18k | gp_file_FILE *file = (gp_file_FILE *)file_; |
566 | | |
567 | 4.18k | return gp_fseekable_impl(file->file); |
568 | 4.18k | } |
569 | | |
570 | | static int |
571 | | gp_file_FILE_pread(gp_file *file_, size_t count, gs_offset_t offset, void *buf) |
572 | 7.19M | { |
573 | 7.19M | gp_file_FILE *file = (gp_file_FILE *)file_; |
574 | | |
575 | 7.19M | return gp_pread_impl(buf, count, offset, file->file); |
576 | 7.19M | } |
577 | | |
578 | | static int |
579 | | gp_file_FILE_pwrite(gp_file *file_, size_t count, gs_offset_t offset, const void *buf) |
580 | 42.3M | { |
581 | 42.3M | gp_file_FILE *file = (gp_file_FILE *)file_; |
582 | | |
583 | 42.3M | return gp_pwrite_impl(buf, count, offset, file->file); |
584 | 42.3M | } |
585 | | |
586 | | static int |
587 | | gp_file_FILE_is_char_buffered(gp_file *file_) |
588 | 0 | { |
589 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
590 | 0 | struct stat rstat; |
591 | |
|
592 | 0 | if (fstat(fileno(file->file), &rstat) != 0) |
593 | 0 | return ERRC; |
594 | 0 | return S_ISCHR(rstat.st_mode); |
595 | 0 | } |
596 | | |
597 | | static void |
598 | | gp_file_FILE_fflush(gp_file *file_) |
599 | 83.7M | { |
600 | 83.7M | gp_file_FILE *file = (gp_file_FILE *)file_; |
601 | | |
602 | 83.7M | fflush(file->file); |
603 | 83.7M | } |
604 | | |
605 | | static int |
606 | | gp_file_FILE_ferror(gp_file *file_) |
607 | 162M | { |
608 | 162M | gp_file_FILE *file = (gp_file_FILE *)file_; |
609 | | |
610 | 162M | return ferror(file->file); |
611 | 162M | } |
612 | | |
613 | | static FILE * |
614 | | gp_file_FILE_get_file(gp_file *file_) |
615 | 590k | { |
616 | 590k | gp_file_FILE *file = (gp_file_FILE *)file_; |
617 | | |
618 | 590k | return file->file; |
619 | 590k | } |
620 | | |
621 | | static void |
622 | | gp_file_FILE_clearerr(gp_file *file_) |
623 | 94.5k | { |
624 | 94.5k | gp_file_FILE *file = (gp_file_FILE *)file_; |
625 | | |
626 | 94.5k | clearerr(file->file); |
627 | 94.5k | } |
628 | | |
629 | | static gp_file * |
630 | | gp_file_FILE_reopen(gp_file *file_, const char *fname, const char *mode) |
631 | 0 | { |
632 | 0 | gp_file_FILE *file = (gp_file_FILE *)file_; |
633 | |
|
634 | 0 | file->file = freopen(fname, mode, file->file); |
635 | 0 | if (file->file == NULL) { |
636 | 0 | gp_file_dealloc(file_); |
637 | 0 | return NULL; |
638 | 0 | } |
639 | 0 | return file_; |
640 | 0 | } |
641 | | |
642 | | static const gp_file_ops_t gp_file_FILE_prototype = |
643 | | { |
644 | | gp_file_FILE_close, |
645 | | gp_file_FILE_getc, |
646 | | gp_file_FILE_putc, |
647 | | gp_file_FILE_read, |
648 | | gp_file_FILE_write, |
649 | | gp_file_FILE_seek, |
650 | | gp_file_FILE_tell, |
651 | | gp_file_FILE_eof, |
652 | | gp_file_FILE_dup, |
653 | | gp_file_FILE_seekable, |
654 | | gp_file_FILE_pread, |
655 | | gp_file_FILE_pwrite, |
656 | | gp_file_FILE_is_char_buffered, |
657 | | gp_file_FILE_fflush, |
658 | | gp_file_FILE_ferror, |
659 | | gp_file_FILE_get_file, |
660 | | gp_file_FILE_clearerr, |
661 | | gp_file_FILE_reopen |
662 | | }; |
663 | | |
664 | | gp_file *gp_file_FILE_alloc(const gs_memory_t *mem) |
665 | 74.7M | { |
666 | 74.7M | return gp_file_alloc(mem->non_gc_memory, |
667 | 74.7M | &gp_file_FILE_prototype, |
668 | 74.7M | sizeof(gp_file_FILE), |
669 | 74.7M | "gp_file_FILE"); |
670 | 74.7M | } |
671 | | |
672 | | int gp_file_FILE_set(gp_file *file_, FILE *f, int (*close)(FILE *)) |
673 | 74.7M | { |
674 | 74.7M | gp_file_FILE *file = (gp_file_FILE *)file_; |
675 | | |
676 | 74.7M | if (f == NULL) { |
677 | 74.2M | gp_file_dealloc(file_); |
678 | 74.2M | return 1; |
679 | 74.2M | } |
680 | | |
681 | 577k | file->file = f; |
682 | 577k | file->close = close ? close : fclose; |
683 | | |
684 | 577k | return 0; |
685 | 74.7M | } |
686 | | |
687 | | char *gp_fgets(char *buffer, size_t n, gp_file *f) |
688 | 0 | { |
689 | 0 | int c = EOF; |
690 | 0 | char *b = buffer; |
691 | 0 | while (n > 1) { |
692 | 0 | c = gp_fgetc(f); |
693 | 0 | if (c == 0 || c == EOF) |
694 | 0 | break; |
695 | 0 | *b++ = c; |
696 | 0 | n--; |
697 | 0 | if (c == '\n') |
698 | 0 | break; |
699 | 0 | } |
700 | 0 | if (c == EOF && b == buffer) |
701 | 0 | return NULL; |
702 | 0 | if (gp_ferror(f)) |
703 | 0 | return NULL; |
704 | 0 | if (n > 0) |
705 | 0 | *b++ = 0; |
706 | 0 | return buffer; |
707 | 0 | } |
708 | | |
709 | | gp_file * |
710 | | gp_fopen(const gs_memory_t *mem, const char *fname, const char *mode) |
711 | 74.4M | { |
712 | 74.4M | gp_file *file = NULL; |
713 | 74.4M | gs_lib_ctx_t *ctx = mem->gs_lib_ctx; |
714 | 74.4M | gs_fs_list_t *fs = ctx->core->fs; |
715 | | |
716 | 74.4M | if (gp_validate_path(mem, fname, mode) != 0) |
717 | 26.8k | return NULL; |
718 | | |
719 | 74.4M | for (fs = ctx->core->fs; fs != NULL; fs = fs->next) |
720 | 74.4M | { |
721 | 74.4M | int code = 0; |
722 | 74.4M | if (fs->fs.open_file) |
723 | 74.4M | code = fs->fs.open_file(mem, fs->secret, fname, mode, &file); |
724 | 74.4M | if (code < 0) |
725 | 74.2M | return NULL; |
726 | 217k | if (file != NULL) |
727 | 217k | break; |
728 | 217k | } |
729 | | |
730 | 217k | return file; |
731 | 74.4M | } |
732 | | |
733 | | gp_file * |
734 | | gp_open_printer(const gs_memory_t *mem, |
735 | | char fname[gp_file_name_sizeof], |
736 | | int binary_mode) |
737 | 72.2k | { |
738 | 72.2k | gp_file *file = NULL; |
739 | 72.2k | gs_lib_ctx_t *ctx = mem->gs_lib_ctx; |
740 | 72.2k | gs_fs_list_t *fs = ctx->core->fs; |
741 | | |
742 | 72.2k | if (gp_validate_path(mem, fname, binary_mode ? "wb" : "w") != 0) |
743 | 0 | return NULL; |
744 | | |
745 | 72.2k | for (fs = ctx->core->fs; fs != NULL; fs = fs->next) |
746 | 72.2k | { |
747 | 72.2k | int code = 0; |
748 | 72.2k | if (fs->fs.open_printer) |
749 | 72.2k | code = fs->fs.open_printer(mem, fs->secret, fname, binary_mode, &file); |
750 | 72.2k | if (code < 0) |
751 | 0 | return NULL; |
752 | 72.2k | if (file != NULL) |
753 | 72.2k | break; |
754 | 72.2k | } |
755 | | |
756 | 72.2k | return file; |
757 | 72.2k | } |
758 | | |
759 | | static gp_file * |
760 | | do_open_scratch_file(const gs_memory_t *mem, |
761 | | const char *prefix, |
762 | | char *fname, |
763 | | const char *mode, |
764 | | int rm) |
765 | 287k | { |
766 | 287k | gp_file *file = NULL; |
767 | 287k | gs_lib_ctx_t *ctx = mem->gs_lib_ctx; |
768 | 287k | gs_fs_list_t *fs = ctx->core->fs; |
769 | 287k | int code = 0; |
770 | | |
771 | | /* If the prefix is absolute, then we must check it's a permissible |
772 | | * path. If not, we're OK. */ |
773 | 287k | if (gp_file_name_is_absolute(prefix, strlen(prefix)) && |
774 | 287k | gp_validate_path(mem, prefix, mode) != 0) |
775 | 0 | return NULL; |
776 | | |
777 | 287k | for (fs = ctx->core->fs; fs != NULL; fs = fs->next) |
778 | 287k | { |
779 | 287k | if (fs->fs.open_scratch) |
780 | 287k | code = fs->fs.open_scratch(mem, fs->secret, prefix, fname, mode, rm, &file); |
781 | 287k | if (code < 0) |
782 | 0 | return NULL; |
783 | 287k | if (file != NULL) |
784 | 287k | break; |
785 | 287k | } |
786 | | |
787 | 287k | if (file == NULL) { |
788 | | /* The file failed to open. Don't add it to the list. */ |
789 | 287k | } else if (rm) { |
790 | | /* This file has already been deleted by the underlying system. |
791 | | * We don't need to add it to the lists as it will never be |
792 | | * deleted manually, nor do we need to tidy it up on closedown. */ |
793 | 215k | } else { |
794 | | /* This file was not requested to be deleted. We add it to the |
795 | | * list so that it will either be deleted by any future call to |
796 | | * zdeletefile, OR on closedown. */ |
797 | | /* Add the scratch file name to the lists. We can't do this any |
798 | | * earlier as we didn't know the name until now! Unfortunately |
799 | | * that makes cleanup harder. */ |
800 | 215k | code = gs_add_control_path_flags(mem, gs_permit_file_control, fname, |
801 | 215k | gs_path_control_flag_is_scratch_file); |
802 | 215k | if (code >= 0) |
803 | 215k | code = gs_add_control_path_flags(mem, gs_permit_file_reading, fname, |
804 | 215k | gs_path_control_flag_is_scratch_file); |
805 | 215k | if (code >= 0) |
806 | 215k | code = gs_add_control_path_flags(mem, gs_permit_file_writing, fname, |
807 | 215k | gs_path_control_flag_is_scratch_file); |
808 | | |
809 | 215k | if (code < 0) { |
810 | 0 | gp_fclose(file); |
811 | 0 | file = NULL; |
812 | | /* Call directly through to the unlink implementation. We know |
813 | | * we're 'permitted' to do this, but we might not be on all the |
814 | | * required permit lists because of the failure. The only bad |
815 | | * thing here, is that we're deleting an fname that might not |
816 | | * have come from the filing system itself. */ |
817 | 0 | if (fname && fname[0]) |
818 | 0 | gp_unlink_impl(ctx->memory, fname); |
819 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_control, fname, |
820 | 0 | gs_path_control_flag_is_scratch_file); |
821 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, fname, |
822 | 0 | gs_path_control_flag_is_scratch_file); |
823 | 0 | (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, fname, |
824 | 0 | gs_path_control_flag_is_scratch_file); |
825 | 0 | } |
826 | 215k | } |
827 | | |
828 | 287k | return file; |
829 | 287k | } |
830 | | |
831 | | gp_file * |
832 | | gp_open_scratch_file(const gs_memory_t *mem, |
833 | | const char *prefix, |
834 | | char fname[gp_file_name_sizeof], |
835 | | const char *mode) |
836 | 215k | { |
837 | 215k | return do_open_scratch_file(mem, prefix, fname, mode, 0); |
838 | 215k | } |
839 | | |
840 | | gp_file * |
841 | | gp_open_scratch_file_rm(const gs_memory_t *mem, |
842 | | const char *prefix, |
843 | | char fname[gp_file_name_sizeof], |
844 | | const char *mode) |
845 | 72.8k | { |
846 | 72.8k | return do_open_scratch_file(mem, prefix, fname, mode, 1); |
847 | 72.8k | } |
848 | | |
849 | | int |
850 | | gp_stat(const gs_memory_t *mem, const char *path, struct stat *buf) |
851 | 3 | { |
852 | 3 | if (gp_validate_path(mem, path, "r") != 0) { |
853 | 3 | return -1; |
854 | 3 | } |
855 | | |
856 | 0 | return gp_stat_impl(mem, path, buf); |
857 | 3 | } |
858 | | |
859 | | file_enum * |
860 | | gp_enumerate_files_init(gs_memory_t *mem, const char *pat, uint patlen) |
861 | 1.78M | { |
862 | 1.78M | return gp_enumerate_files_init_impl(mem, pat, patlen); |
863 | 1.78M | } |
864 | | |
865 | | uint |
866 | | gp_enumerate_files_next(gs_memory_t *mem, file_enum * pfen, char *ptr, uint maxlen) |
867 | 1.78M | { |
868 | 1.78M | uint code = 0; |
869 | | |
870 | 1.78M | while (code == 0) { |
871 | 1.78M | code = gp_enumerate_files_next_impl(mem, pfen, ptr, maxlen); |
872 | 1.78M | if (code == ~0) break; |
873 | 0 | if (code > 0 && ptr != NULL) { |
874 | 0 | if (gp_validate_path_len(mem, ptr, code, "r") != 0) |
875 | 0 | code = 0; |
876 | 0 | } |
877 | 0 | } |
878 | 1.78M | return code; |
879 | 1.78M | } |
880 | | void |
881 | | gp_enumerate_files_close(gs_memory_t *mem, file_enum * pfen) |
882 | 1.78M | { |
883 | 1.78M | gp_enumerate_files_close_impl(mem, pfen); |
884 | 1.78M | } |
885 | | |
886 | | /* Path validation: (FIXME: Move this somewhere better) |
887 | | * |
888 | | * The only wildcard we accept is '*'. |
889 | | * |
890 | | * A '*' at the end of the path means "in this directory, |
891 | | * or any subdirectory". Anywhere else it means "a sequence of |
892 | | * characters not including a director separator". |
893 | | * |
894 | | * A sequence of multiple '*'s is equivalent to a single one. |
895 | | * |
896 | | * Matching on '*' is simplistic; the matching sequence will end |
897 | | * as soon as we meet an instance of a character that follows |
898 | | * the '*' in a pattern. i.e. "foo*bar" will fail to match "fooabbar" |
899 | | * as the '*' will be held to match just 'a'. |
900 | | * |
901 | | * There is no way of specifying a literal '*'; if you find yourself |
902 | | * wanting to do this, slap yourself until you come to your senses. |
903 | | * |
904 | | * Due to the difficulties of using both * and / in writing C comments, |
905 | | * I shall use \ as the directory separator in the examples below, but |
906 | | * in practice it means "the directory separator for the current |
907 | | * platform". |
908 | | * |
909 | | * Pattern Match example |
910 | | * * any file, in any directory at all. |
911 | | * foo\bar a file, foo\bar. |
912 | | * foo\bar\ any file within foo\bar\, but no subdirectories. |
913 | | * foo\bar\* any file within foo\bar\ or any subdirectory thereof. |
914 | | * foo\*\bar any file 'bar' within any single subdirectory of foo |
915 | | * (i.e. foo\baz\bar, but not foo\baz\whoop\bar) |
916 | | * foo\out*.tif e.g. foo\out1.tif |
917 | | * foo\out*.*.tif* e.g. foo\out1.(Red).tif |
918 | | */ |
919 | | |
920 | | static int |
921 | | validate(const gs_memory_t *mem, |
922 | | const char *path, |
923 | | gs_path_control_t type) |
924 | 62.1M | { |
925 | 62.1M | gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core; |
926 | 62.1M | gs_path_control_set_t *control; |
927 | 62.1M | unsigned int i, n; |
928 | | |
929 | 62.1M | switch (type) { |
930 | 61.7M | case gs_permit_file_reading: |
931 | 61.7M | control = &core->permit_reading; |
932 | 61.7M | break; |
933 | 61.5k | case gs_permit_file_writing: |
934 | 61.5k | control = &core->permit_writing; |
935 | 61.5k | break; |
936 | 325k | case gs_permit_file_control: |
937 | 325k | control = &core->permit_control; |
938 | 325k | break; |
939 | 0 | default: |
940 | 0 | return gs_error_unknownerror; |
941 | 62.1M | } |
942 | | |
943 | 62.1M | n = control->num; |
944 | 1.58G | for (i = 0; i < n; i++) { |
945 | 1.58G | const char *a = path; |
946 | 1.58G | const char *b = control->entry[i].path; |
947 | 17.5G | while (1) { |
948 | 17.5G | if (*a == 0) { |
949 | 178k | if (*b == 0) |
950 | | /* PATH=abc pattern=abc */ |
951 | 163k | goto found; /* Bingo! */ |
952 | 15.0k | else |
953 | | /* PATH=abc pattern=abcd */ |
954 | 15.0k | break; /* No match */ |
955 | 17.5G | } else if (*b == '*') { |
956 | 61.9M | if (b[1] == '*') { |
957 | | /* Multiple '*'s are taken to mean the |
958 | | * output from a printf. */ |
959 | 0 | b++; |
960 | 0 | while (b[1] == '*') |
961 | 0 | b++; |
962 | | /* Skip over the permissible matching chars */ |
963 | 0 | while (*a && |
964 | 0 | ((*a == ' ' || *a == '-' || *a == '+' || |
965 | 0 | (*a >= '0' && *a <= '9') || |
966 | 0 | (*a >= 'a' && *a <= 'f') || |
967 | 0 | (*a >= 'A' && *a <= 'F')))) |
968 | 0 | a++; |
969 | 0 | if (b[1] == 0 && *a == 0) |
970 | | /* PATH=abc<%d> pattern=abc** */ |
971 | 0 | goto found; |
972 | 0 | if (*a == 0) |
973 | 0 | break; /* No match */ |
974 | 61.9M | } else { |
975 | 61.9M | if (b[1] == 0) |
976 | | /* PATH=abc???? pattern=abc* */ |
977 | 61.9M | goto found; |
978 | | /* Skip over anything except NUL, directory |
979 | | * separator, and the next char to match. */ |
980 | 0 | while (*a && !gs_file_name_check_separator(a, 1, a) && *a != b[1]) |
981 | 0 | a++; |
982 | 0 | if (*a == 0 || *a == gs_file_name_check_separator(a, 1, a)) |
983 | 0 | break; /* No match */ |
984 | 0 | } |
985 | | /* Continue matching */ |
986 | 0 | a--; /* Subtract 1 as the loop will increment it again later */ |
987 | 17.4G | } else if (*b == 0) { |
988 | 61.7M | if (b != control->entry[i].path && |
989 | 61.7M | gs_file_name_check_separator(b, -1, b)) { |
990 | 0 | const char *a2 = a; |
991 | 0 | const char *aend = path + strlen(path); |
992 | 0 | while (aend != a2 && !gs_file_name_check_separator(a2, 1, a2)) |
993 | 0 | a2++; |
994 | | /* If the control path ends in a directory separator and we've scanned |
995 | | to the end of the candidate path with no further directory separators |
996 | | found, we have a match |
997 | | */ |
998 | 0 | if (aend == a2) |
999 | | /* PATH=abc/? pattern=abc/ */ |
1000 | 0 | goto found; /* Bingo! */ |
1001 | 0 | } |
1002 | | /* PATH=abcd pattern=abc */ |
1003 | 61.7M | break; /* No match */ |
1004 | 17.4G | } else if (gs_file_name_check_separator(a, 1, a) == 1 |
1005 | 17.4G | && gs_file_name_check_separator(b, 1, b) == 1) { |
1006 | | /* On Windows we can get random combinations of "/" and "\" as directory |
1007 | | * separators, and we want "C:\" to match C:/" hence using the pair of |
1008 | | * gs_file_name_check_separator() calls above */ |
1009 | | /* Annoyingly, we can also end up with a combination of explicitly escaped |
1010 | | * '\' characters, and not escaped. So we also need "C:\\" to match "C:\" |
1011 | | * and "C:/" - hence we need to check for, and skip over the |
1012 | | * the extra '\' character - I'm reticent to change the upstream code that |
1013 | | * adds the explicit escape, because that could have unforeseen side effects |
1014 | | * elsewhere. */ |
1015 | 3.67G | if (*(a + 1) != 0 && gs_file_name_check_separator(a + 1, 1, a + 1) == 1) |
1016 | 0 | a++; |
1017 | 3.67G | if (*(b + 1) != 0 && gs_file_name_check_separator(b + 1, 1, b + 1) == 1) |
1018 | 0 | b++; |
1019 | 13.7G | } else if (*a != *b) { |
1020 | 1.45G | break; |
1021 | 1.45G | } |
1022 | 15.9G | a++, b++; |
1023 | 15.9G | } |
1024 | 1.58G | } |
1025 | 25.5k | return gs_error_invalidfileaccess; |
1026 | | |
1027 | 62.0M | found: |
1028 | 62.0M | return control->entry[i].flags; |
1029 | 62.1M | } |
1030 | | |
1031 | | int |
1032 | | gp_validate_path_len(const gs_memory_t *mem, |
1033 | | const char *path, |
1034 | | const uint len, |
1035 | | const char *mode) |
1036 | 74.8M | { |
1037 | 74.8M | char *buffer, *bufferfull = NULL; |
1038 | 74.8M | uint rlen; |
1039 | 74.8M | int code = 0; |
1040 | 74.8M | const char *cdirstr = gp_file_name_current(); |
1041 | 74.8M | int cdirstrl = strlen(cdirstr); |
1042 | 74.8M | const char *dirsepstr = gp_file_name_separator(); |
1043 | 74.8M | int dirsepstrl = strlen(dirsepstr); |
1044 | 74.8M | int prefix_len = cdirstrl + dirsepstrl; |
1045 | | |
1046 | | /* mem->gs_lib_ctx can be NULL when we're called from mkromfs */ |
1047 | | /* If path == NULL, don't care */ |
1048 | 74.8M | if (path == NULL || mem->gs_lib_ctx == NULL || |
1049 | | /* Can't use gs_is_path_control_active(mem) here because it fails to build with mkromfs */ |
1050 | 74.8M | mem->gs_lib_ctx->core->path_control_active == 0) |
1051 | 12.7M | return 0; |
1052 | | |
1053 | | /* For current directory accesses, we need handle both a "bare" name, |
1054 | | * and one with a cwd prefix (in Unix terms, both "myfile.ps" and |
1055 | | * "./myfile.ps". |
1056 | | * |
1057 | | * So we check up front if it's absolute, then just use that. |
1058 | | * If it includes cwd prefix, we try that, then remove the prefix |
1059 | | * and try again. |
1060 | | * If it doesn't include the cwd prefix, we try it, then add the |
1061 | | * prefix and try again. |
1062 | | * To facilitate that, we allocate a large enough buffer to take |
1063 | | * the path *and* the prefix up front. |
1064 | | */ |
1065 | 62.1M | if (gp_file_name_is_absolute(path, len)) { |
1066 | | /* Absolute path, we don't need anything extra */ |
1067 | 62.1M | prefix_len = cdirstrl = dirsepstrl = 0; |
1068 | 62.1M | } |
1069 | 11.2k | else if (len > prefix_len && !memcmp(path, cdirstr, cdirstrl) |
1070 | 11.2k | && !memcmp(path + cdirstrl, dirsepstr, dirsepstrl)) { |
1071 | 0 | prefix_len = 0; |
1072 | 0 | } |
1073 | | |
1074 | | /* "%pipe%" do not follow the normal rules for path definitions, so we |
1075 | | don't "reduce" them to avoid unexpected results |
1076 | | */ |
1077 | 62.1M | if (path[0] == '|' || (len > 5 && memcmp(path, "%pipe", 5) == 0)) { |
1078 | 12 | bufferfull = buffer = (char *)gs_alloc_bytes(mem->thread_safe_memory, len + 1, "gp_validate_path"); |
1079 | 12 | if (buffer == NULL) |
1080 | 0 | return gs_error_VMerror; |
1081 | 12 | memcpy(buffer, path, len); |
1082 | 12 | buffer[len] = 0; |
1083 | 12 | rlen = len; |
1084 | 12 | } |
1085 | 62.1M | else { |
1086 | 62.1M | char *test = (char *)path, *test1; |
1087 | 62.1M | uint tlen = len, slen; |
1088 | 62.1M | gp_file_name_combine_result res; |
1089 | | |
1090 | | /* Look for any pipe (%pipe% or '|' specifications between path separators |
1091 | | * Reject any path spec which has a %pipe% or '|' anywhere except at the start. |
1092 | | */ |
1093 | 503M | while (tlen > 0) { |
1094 | 503M | if (test[0] == '|' || (tlen > 5 && memcmp(test, "%pipe", 5) == 0)) { |
1095 | 1.32k | code = gs_note_error(gs_error_invalidfileaccess); |
1096 | 1.32k | goto exit; |
1097 | 1.32k | } |
1098 | 503M | test1 = test; |
1099 | 503M | slen = search_separator((const char **)&test, path + len, test1, 1); |
1100 | 503M | if(slen == 0) |
1101 | 57.2M | break; |
1102 | 446M | test += slen; |
1103 | 446M | tlen -= test - test1; |
1104 | 446M | if (test >= path + len) |
1105 | 4.87M | break; |
1106 | 446M | } |
1107 | | |
1108 | 62.1M | rlen = len+1; |
1109 | | /* There is a very, very small chance that gp_file_name_reduce() can return a longer |
1110 | | string than simply rlen + prefix_len. The do/while loop is to cope with that. |
1111 | | */ |
1112 | 62.1M | do { |
1113 | 62.1M | bufferfull = (char *)gs_alloc_bytes(mem->thread_safe_memory, rlen + prefix_len, "gp_validate_path"); |
1114 | 62.1M | if (bufferfull == NULL) |
1115 | 0 | return gs_error_VMerror; |
1116 | | |
1117 | 62.1M | buffer = bufferfull + prefix_len; |
1118 | 62.1M | res = gp_file_name_reduce(path, (uint)len, buffer, &rlen); |
1119 | 62.1M | if (res == gp_combine_small_buffer) { |
1120 | 9 | rlen += 1; |
1121 | 9 | gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path"); |
1122 | 9 | rlen += 1; |
1123 | 9 | continue; |
1124 | 9 | } |
1125 | 62.1M | if (res != gp_combine_success) { |
1126 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1127 | 0 | goto exit; |
1128 | 0 | } |
1129 | 62.1M | } while (res != gp_combine_success); |
1130 | 62.1M | buffer[rlen] = 0; |
1131 | 62.1M | } |
1132 | 62.1M | while (1) { |
1133 | 62.1M | switch (mode[0]) |
1134 | 62.1M | { |
1135 | 61.7M | case 'r': /* Read */ |
1136 | 61.7M | code = validate(mem, buffer, gs_permit_file_reading); |
1137 | 61.7M | break; |
1138 | 61.5k | case 'w': /* Write */ |
1139 | 61.5k | code = validate(mem, buffer, gs_permit_file_writing); |
1140 | 61.5k | break; |
1141 | 0 | case 'a': /* Append needs reading and writing */ |
1142 | 0 | code = (validate(mem, buffer, gs_permit_file_reading) | |
1143 | 0 | validate(mem, buffer, gs_permit_file_writing)); |
1144 | 0 | break; |
1145 | 0 | case 'c': /* "Control" */ |
1146 | 0 | code = validate(mem, buffer, gs_permit_file_control); |
1147 | 0 | break; |
1148 | 325k | case 'd': /* "Delete" (special case of control) */ |
1149 | 325k | code = validate(mem, buffer, gs_permit_file_control); |
1150 | 325k | break; |
1151 | 0 | case 'f': /* "Rename from" */ |
1152 | 0 | code = (validate(mem, buffer, gs_permit_file_writing) | |
1153 | 0 | validate(mem, buffer, gs_permit_file_control)); |
1154 | 0 | break; |
1155 | 0 | case 't': /* "Rename to" */ |
1156 | 0 | code = (validate(mem, buffer, gs_permit_file_writing) | |
1157 | 0 | validate(mem, buffer, gs_permit_file_control)); |
1158 | 0 | break; |
1159 | 0 | default: |
1160 | 0 | errprintf(mem, "gp_validate_path: Unknown mode='%s'\n", mode); |
1161 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1162 | 62.1M | } |
1163 | 62.1M | if (code < 0 && prefix_len > 0 && buffer > bufferfull) { |
1164 | 11.2k | uint newlen = rlen + cdirstrl + dirsepstrl; |
1165 | 11.2k | char *newbuffer; |
1166 | 11.2k | int code; |
1167 | | |
1168 | 11.2k | buffer = bufferfull; |
1169 | 11.2k | memcpy(buffer, cdirstr, cdirstrl); |
1170 | 11.2k | memcpy(buffer + cdirstrl, dirsepstr, dirsepstrl); |
1171 | | |
1172 | | /* We've prepended a './' or similar for the current working directory. We need |
1173 | | * to execute file_name_reduce on that, to eliminate any '../' or similar from |
1174 | | * the (new) full path. |
1175 | | */ |
1176 | 11.2k | newbuffer = (char *)gs_alloc_bytes(mem->thread_safe_memory, newlen + 1, "gp_validate_path"); |
1177 | 11.2k | if (newbuffer == NULL) { |
1178 | 0 | code = gs_note_error(gs_error_VMerror); |
1179 | 0 | goto exit; |
1180 | 0 | } |
1181 | | |
1182 | 11.2k | memcpy(newbuffer, buffer, rlen + cdirstrl + dirsepstrl); |
1183 | 11.2k | newbuffer[newlen] = 0x00; |
1184 | | |
1185 | 11.2k | code = gp_file_name_reduce(newbuffer, (uint)newlen, buffer, &newlen); |
1186 | 11.2k | gs_free_object(mem->thread_safe_memory, newbuffer, "gp_validate_path"); |
1187 | 11.2k | if (code != gp_combine_success) { |
1188 | 11.1k | code = gs_note_error(gs_error_invalidfileaccess); |
1189 | 11.1k | goto exit; |
1190 | 11.1k | } |
1191 | | |
1192 | 32 | continue; |
1193 | 11.2k | } |
1194 | 62.0M | else if (code < 0 && cdirstrl > 0 && prefix_len == 0 && buffer == bufferfull |
1195 | 62.0M | && memcmp(buffer, cdirstr, cdirstrl) && !memcmp(buffer + cdirstrl, dirsepstr, dirsepstrl)) { |
1196 | 0 | continue; |
1197 | 0 | } |
1198 | 62.0M | break; |
1199 | 62.1M | } |
1200 | 62.0M | if (code > 0 && (mode[0] == 'd' || mode[0] == 'f') && |
1201 | 62.0M | (code & gs_path_control_flag_is_scratch_file) != 0) { |
1202 | 102k | (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, buffer, |
1203 | 102k | gs_path_control_flag_is_scratch_file); |
1204 | 102k | (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, buffer, |
1205 | 102k | gs_path_control_flag_is_scratch_file); |
1206 | 102k | (void)gs_remove_control_path_flags(mem, gs_permit_file_control, buffer, |
1207 | 102k | gs_path_control_flag_is_scratch_file); |
1208 | 102k | } |
1209 | | |
1210 | 62.1M | exit: |
1211 | 62.1M | gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path"); |
1212 | 62.1M | #ifdef EACCES |
1213 | 62.1M | if (code == gs_error_invalidfileaccess) |
1214 | 26.8k | errno = EACCES; |
1215 | 62.1M | #endif |
1216 | | |
1217 | 62.1M | return code < 0 ? code : 0; |
1218 | 62.0M | } |
1219 | | |
1220 | | int |
1221 | | gp_validate_path(const gs_memory_t *mem, |
1222 | | const char *path, |
1223 | | const char *mode) |
1224 | 74.8M | { |
1225 | 74.8M | return gp_validate_path_len(mem, path, strlen(path), mode); |
1226 | 74.8M | } |
1227 | | |
1228 | | int |
1229 | | gp_unlink(gs_memory_t *mem, const char *fname) |
1230 | 325k | { |
1231 | 325k | if (gp_validate_path(mem, fname, "d") != 0) |
1232 | 15 | return gs_error_invalidaccess; |
1233 | | |
1234 | 325k | return gp_unlink_impl(mem, fname); |
1235 | 325k | } |
1236 | | |
1237 | | int |
1238 | | gp_rename(gs_memory_t *mem, const char *from, const char *to) |
1239 | 0 | { |
1240 | | /* Always check 'to' before 'from', in case 'from' is a tempfile, |
1241 | | * and testing it might remove it from the list! */ |
1242 | 0 | if (gp_validate_path(mem, to, "t") != 0) |
1243 | 0 | return gs_error_invalidaccess; |
1244 | 0 | if (gp_validate_path(mem, from, "f") != 0) |
1245 | 0 | return gs_error_invalidaccess; |
1246 | | |
1247 | 0 | return gp_rename_impl(mem, from, to); |
1248 | 0 | } |