/src/ghostpdl/psi/zfile.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2024 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 | | /* Non-I/O file operators */ |
18 | | #include "memory_.h" |
19 | | #include "string_.h" |
20 | | #include "unistd_.h" |
21 | | #include "stat_.h" /* get system header early to avoid name clash on Cygwin */ |
22 | | #include "ghost.h" |
23 | | #include "gscdefs.h" /* for gx_io_device_table */ |
24 | | #include "gsutil.h" /* for bytes_compare */ |
25 | | #include "gp.h" |
26 | | #include "gpmisc.h" |
27 | | #include "gsfname.h" |
28 | | #include "gsstruct.h" /* for registering root */ |
29 | | #include "gxalloc.h" /* for streams */ |
30 | | #include "oper.h" |
31 | | #include "dstack.h" /* for systemdict */ |
32 | | #include "estack.h" /* for filenameforall, .execfile */ |
33 | | #include "ialloc.h" |
34 | | #include "ilevel.h" /* %names only work in Level 2 */ |
35 | | #include "iname.h" |
36 | | #include "isave.h" /* for restore */ |
37 | | #include "idict.h" |
38 | | #include "iddict.h" |
39 | | #include "iutil.h" |
40 | | #include "stream.h" |
41 | | #include "strimpl.h" |
42 | | #include "sfilter.h" |
43 | | #include "gxiodev.h" /* must come after stream.h */ |
44 | | /* and before files.h */ |
45 | | #include "files.h" |
46 | | #include "main.h" /* for gs_lib_paths */ |
47 | | #include "store.h" |
48 | | #include "zfile.h" |
49 | | |
50 | | /* Import the IODevice table. */ |
51 | | extern_gx_io_device_table(); |
52 | | |
53 | | /* Import the dtype of the stdio IODevices. */ |
54 | | extern const char iodev_dtype_stdio[]; |
55 | | |
56 | | /* Forward references: file name parsing. */ |
57 | | static int parse_file_name(const ref * op, gs_parsed_file_name_t * pfn, |
58 | | bool safemode, gs_memory_t *memory); |
59 | | static int parse_real_file_name(const ref * op, |
60 | | gs_parsed_file_name_t * pfn, |
61 | | gs_memory_t *mem, client_name_t cname); |
62 | | static int parse_file_access_string(const ref *op, char file_access[4]); |
63 | | |
64 | | /* Forward references: other. */ |
65 | | static int execfile_finish(i_ctx_t *); |
66 | | static int execfile_cleanup(i_ctx_t *); |
67 | | static iodev_proc_open_file(iodev_os_open_file); |
68 | | stream_proc_report_error(filter_report_error); |
69 | | |
70 | | /* |
71 | | * Since there can be many file objects referring to the same file/stream, |
72 | | * we can't simply free a stream when we close it. On the other hand, |
73 | | * we don't want freed streams to clutter up memory needlessly. |
74 | | * Our solution is to retain the freed streams, and reuse them. |
75 | | * To prevent an old file object from being able to access a reused stream, |
76 | | * we keep a serial number in each stream, and check it against a serial |
77 | | * number stored in the file object (as the "size"); when we close a file, |
78 | | * we increment its serial number. If the serial number ever overflows, |
79 | | * we leave it at zero, and do not reuse the stream. |
80 | | * (This will never happen.) |
81 | | * |
82 | | * Storage management for this scheme is a little tricky. We maintain an |
83 | | * invariant that says that a stream opened at a given save level always |
84 | | * uses a stream structure allocated at that level. By doing this, we don't |
85 | | * need to keep track separately of streams open at a level vs. streams |
86 | | * allocated at a level. To make this interact properly with save and |
87 | | * restore, we maintain a list of all streams allocated at this level, both |
88 | | * open and closed. We store this list in the allocator: this is a hack, |
89 | | * but it simplifies bookkeeping (in particular, it guarantees the list is |
90 | | * restored properly by a restore). |
91 | | * |
92 | | * We want to close streams freed by restore and by garbage collection. We |
93 | | * use the finalization procedure for this. For restore, we don't have to |
94 | | * do anything special to make this happen. For garbage collection, we do |
95 | | * something more drastic: we simply clear the list of known streams (at all |
96 | | * save levels). Any streams open at the time of garbage collection will no |
97 | | * longer participate in the list of known streams, but this does no harm; |
98 | | * it simply means that they won't get reused, and can only be reclaimed by |
99 | | * a future garbage collection or restore. |
100 | | */ |
101 | | |
102 | | /* |
103 | | * Define the default stream buffer sizes. For file streams, |
104 | | * this is arbitrary, since the C library or operating system |
105 | | * does its own buffering in addition. |
106 | | * However, a buffer size of at least 2K bytes is necessary to prevent |
107 | | * JPEG decompression from running very slow. When less than 2K, an |
108 | | * intermediate filter is installed that transfers 1 byte at a time |
109 | | * causing many aborted roundtrips through the JPEG filter code. |
110 | | */ |
111 | | #define DEFAULT_BUFFER_SIZE 2048 |
112 | | extern const uint file_default_buffer_size; |
113 | | |
114 | | /* Make an invalid file object. */ |
115 | | void |
116 | | make_invalid_file(i_ctx_t *i_ctx_p, ref * fp) |
117 | 0 | { |
118 | 0 | make_file(fp, avm_invalid_file_entry, ~0, i_ctx_p->invalid_file_stream); |
119 | 0 | } |
120 | | |
121 | | /* Check a file name for permission by stringmatch on one of the */ |
122 | | /* strings of the permitgroup array. */ |
123 | | static int |
124 | | check_file_permissions_reduced(i_ctx_t *i_ctx_p, const char *fname, int len, |
125 | | gx_io_device *iodev, const char *permitgroup) |
126 | 53.1M | { |
127 | 53.1M | long i; |
128 | 53.1M | ref *permitlist = NULL; |
129 | | /* an empty string (first character == 0) if '\' character is */ |
130 | | /* recognized as a file name separator as on DOS & Windows */ |
131 | 53.1M | const char *win_sep2 = "\\"; |
132 | 53.1M | bool use_windows_pathsep = (gs_file_name_check_separator(win_sep2, 1, win_sep2) == 1); |
133 | 53.1M | uint plen = gp_file_name_parents(fname, len); |
134 | | |
135 | | /* we're protecting arbitrary file system accesses, not Postscript device accesses. |
136 | | * Although, note that %pipe% is explicitly checked for and disallowed elsewhere |
137 | | */ |
138 | 53.1M | if (iodev && iodev != iodev_default(imemory)) { |
139 | 15.9M | return 0; |
140 | 15.9M | } |
141 | | |
142 | | /* Assuming a reduced file name. */ |
143 | 37.2M | if (dict_find_string(&(i_ctx_p->userparams), permitgroup, &permitlist) <= 0) |
144 | 0 | return 0; /* if Permissions not found, just allow access */ |
145 | | |
146 | 37.2M | for (i=0; i<r_size(permitlist); i++) { |
147 | 37.2M | ref permitstring; |
148 | 37.2M | const string_match_params win_filename_params = { |
149 | 37.2M | '*', '?', '\\', true, true /* ignore case & '/' == '\\' */ |
150 | 37.2M | }; |
151 | 37.2M | const byte *permstr; |
152 | 37.2M | uint permlen; |
153 | 37.2M | int cwd_len = 0; |
154 | | |
155 | 37.2M | if (array_get(imemory, permitlist, i, &permitstring) < 0 || |
156 | 37.2M | r_type(&permitstring) != t_string |
157 | 37.2M | ) |
158 | 0 | break; /* any problem, just fail */ |
159 | 37.2M | permstr = permitstring.value.bytes; |
160 | 37.2M | permlen = r_size(&permitstring); |
161 | | /* |
162 | | * Check if any file name is permitted with "*". |
163 | | */ |
164 | 37.2M | if (permlen == 1 && permstr[0] == '*') |
165 | 37.2M | return 0; /* success */ |
166 | | /* |
167 | | * If the filename starts with parent references, |
168 | | * the permission element must start with same number of parent references. |
169 | | */ |
170 | 0 | if (plen != 0 && plen != gp_file_name_parents((const char *)permstr, permlen)) |
171 | 0 | continue; |
172 | 0 | cwd_len = gp_file_name_cwds((const char *)permstr, permlen); |
173 | | /* |
174 | | * If the permission starts with "./", absolute paths |
175 | | * are not permitted. |
176 | | */ |
177 | 0 | if (cwd_len > 0 && gp_file_name_is_absolute(fname, len)) |
178 | 0 | continue; |
179 | | /* |
180 | | * If the permission starts with "./", relative paths |
181 | | * with no "./" are allowed as well as with "./". |
182 | | * 'fname' has no "./" because it is reduced. |
183 | | */ |
184 | 0 | if (string_match( (const unsigned char*) fname, len, |
185 | 0 | permstr + cwd_len, permlen - cwd_len, |
186 | 0 | use_windows_pathsep ? &win_filename_params : NULL)) |
187 | 0 | return 0; /* success */ |
188 | 0 | } |
189 | | /* not found */ |
190 | 0 | return gs_error_invalidfileaccess; |
191 | 37.2M | } |
192 | | |
193 | | /* Check a file name for permission by stringmatch on one of the */ |
194 | | /* strings of the permitgroup array */ |
195 | | static int |
196 | | check_file_permissions(i_ctx_t *i_ctx_p, const char *fname, int len, |
197 | | gx_io_device *iodev, const char *permitgroup) |
198 | 53.1M | { |
199 | 53.1M | char fname_reduced[gp_file_name_sizeof]; |
200 | 53.1M | uint rlen = sizeof(fname_reduced); |
201 | | |
202 | 53.1M | if (gp_file_name_reduce(fname, len, fname_reduced, &rlen) != gp_combine_success) |
203 | 0 | return gs_error_invalidaccess; /* fail if we couldn't reduce */ |
204 | 53.1M | return check_file_permissions_reduced(i_ctx_p, fname_reduced, rlen, iodev, permitgroup); |
205 | 53.1M | } |
206 | | |
207 | | /* z_check_file_permissions: see zfile.h for explanation |
208 | | */ |
209 | | int |
210 | | z_check_file_permissions(gs_memory_t *mem, const char *fname, const int len, const char *permission) |
211 | 13.4M | { |
212 | 13.4M | i_ctx_t *i_ctx_p = get_minst_from_memory(mem)->i_ctx_p; |
213 | 13.4M | gs_parsed_file_name_t pname; |
214 | 13.4M | const char *permitgroup = permission[0] == 'r' ? "PermitFileReading" : "PermitFileWriting"; |
215 | 13.4M | int code = gs_parse_file_name(&pname, fname, len, imemory); |
216 | 13.4M | if (code < 0) |
217 | 0 | return code; |
218 | | |
219 | 13.4M | if (pname.iodev && i_ctx_p->LockFilePermissions |
220 | 13.4M | && strcmp(pname.iodev->dname, "%pipe%") == 0) { |
221 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
222 | 0 | } |
223 | 13.4M | else { |
224 | 13.4M | code = check_file_permissions(i_ctx_p, pname.fname, pname.len, pname.iodev, permitgroup); |
225 | 13.4M | } |
226 | 13.4M | return code; |
227 | 13.4M | } |
228 | | |
229 | | /* <name_string> <access_string> file <file> */ |
230 | | int /* exported for zsysvm.c */ |
231 | | zfile(i_ctx_t *i_ctx_p) |
232 | 43.1M | { |
233 | 43.1M | os_ptr op = osp; |
234 | 43.1M | char file_access[4]; |
235 | 43.1M | gs_parsed_file_name_t pname; |
236 | 43.1M | int code = parse_file_access_string(op, file_access); |
237 | 43.1M | stream *s; |
238 | | |
239 | 43.1M | if (code < 0) |
240 | 44 | return code; |
241 | 43.1M | code = parse_file_name(op-1, &pname, i_ctx_p->LockFilePermissions, imemory); |
242 | 43.1M | if (code < 0) |
243 | 7 | return code; |
244 | | /* |
245 | | * HACK: temporarily patch the current context pointer into the |
246 | | * state pointer for stdio-related devices. See ziodev.c for |
247 | | * more information. |
248 | | */ |
249 | 43.1M | if (pname.iodev && pname.iodev->dtype == iodev_dtype_stdio) { |
250 | 38.8M | bool statement = (strcmp(pname.iodev->dname, "%statementedit%") == 0); |
251 | 38.8M | bool lineedit = (strcmp(pname.iodev->dname, "%lineedit%") == 0); |
252 | 38.8M | if (pname.fname) |
253 | 0 | return_error(gs_error_invalidfileaccess); |
254 | 38.8M | if (statement || lineedit) { |
255 | | /* These need special code to support callouts */ |
256 | 0 | gx_io_device *indev = gs_findiodevice(imemory, |
257 | 0 | (const byte *)"%stdin", 6); |
258 | 0 | stream *ins; |
259 | 0 | if (strcmp(file_access, "r")) |
260 | 0 | return_error(gs_error_invalidfileaccess); |
261 | 0 | indev->state = i_ctx_p; |
262 | 0 | code = (indev->procs.open_device)(indev, file_access, &ins, imemory); |
263 | 0 | indev->state = 0; |
264 | 0 | if (code < 0) |
265 | 0 | return code; |
266 | 0 | check_ostack(2); |
267 | 0 | push(2); |
268 | 0 | make_stream_file(op - 3, ins, file_access); |
269 | 0 | make_bool(op-2, statement); |
270 | 0 | make_int(op-1, 0); |
271 | 0 | make_string(op, icurrent_space, 0, NULL); |
272 | 0 | return zfilelineedit(i_ctx_p); |
273 | 0 | } |
274 | 38.8M | pname.iodev->state = i_ctx_p; |
275 | 38.8M | code = (*pname.iodev->procs.open_device)(pname.iodev, |
276 | 38.8M | file_access, &s, imemory); |
277 | 38.8M | pname.iodev->state = NULL; |
278 | 38.8M | } else { |
279 | 4.28M | if (pname.iodev == NULL) |
280 | 1.79M | pname.iodev = iodev_default(imemory); |
281 | 4.28M | code = zopen_file(i_ctx_p, &pname, file_access, &s, imemory); |
282 | 4.28M | } |
283 | 43.1M | if (code < 0) |
284 | 3.39M | return code; |
285 | 39.7M | if (s == NULL) |
286 | 43 | return_error(gs_error_undefinedfilename); |
287 | 39.7M | code = ssetfilename(s, op[-1].value.const_bytes, r_size(op - 1)); |
288 | 39.7M | if (code < 0) { |
289 | 0 | sclose(s); |
290 | 0 | return_error(gs_error_VMerror); |
291 | 0 | } |
292 | 39.7M | make_stream_file(op - 1, s, file_access); |
293 | 39.7M | pop(1); |
294 | 39.7M | return code; |
295 | 39.7M | } |
296 | | |
297 | | /* |
298 | | * Files created with .tempfile permit some operations even if the |
299 | | * temp directory is not explicitly named on the PermitFile... path |
300 | | * The names 'SAFETY' and 'tempfiles' are defined by gs_init.ps |
301 | | */ |
302 | | static bool |
303 | | file_is_tempfile(i_ctx_t *i_ctx_p, const uchar *fname, int len) |
304 | 0 | { |
305 | 0 | ref *SAFETY; |
306 | 0 | ref *tempfiles; |
307 | 0 | ref kname; |
308 | |
|
309 | 0 | if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 || |
310 | 0 | dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0) |
311 | 0 | return false; |
312 | 0 | if (name_ref(imemory, fname, len, &kname, -1) < 0 || |
313 | 0 | dict_find(tempfiles, &kname, &SAFETY) <= 0) |
314 | 0 | return false; |
315 | 0 | return true; |
316 | 0 | } |
317 | | |
318 | | static int |
319 | | record_file_is_tempfile(i_ctx_t *i_ctx_p, const uchar *fname, int len, bool add) |
320 | 94.5k | { |
321 | 94.5k | ref *SAFETY; |
322 | 94.5k | ref *tempfiles; |
323 | 94.5k | ref kname, bref; |
324 | 94.5k | int code = 0; |
325 | | |
326 | 94.5k | if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 || |
327 | 94.5k | dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0) { |
328 | 0 | return 0; |
329 | 0 | } |
330 | 94.5k | if ((code = name_ref(imemory, fname, len, &kname, 1)) < 0) { |
331 | 0 | return code; |
332 | 0 | } |
333 | 94.5k | make_bool(&bref, true); |
334 | 94.5k | if (add) |
335 | 94.5k | return idict_put(tempfiles, &kname, &bref); |
336 | 0 | else |
337 | 0 | return idict_undef(tempfiles, &kname); |
338 | 94.5k | } |
339 | | |
340 | | /* ------ Level 2 extensions ------ */ |
341 | | |
342 | | /* <string> deletefile - */ |
343 | | static int |
344 | | zdeletefile(i_ctx_t *i_ctx_p) |
345 | 94.2k | { |
346 | 94.2k | os_ptr op = osp; |
347 | 94.2k | gs_parsed_file_name_t pname; |
348 | 94.2k | int code; |
349 | 94.2k | bool is_temp = false; |
350 | | |
351 | 94.2k | check_op(1); |
352 | 94.2k | code = parse_real_file_name(op, &pname, imemory, "deletefile"); |
353 | 94.2k | if (code < 0) |
354 | 4 | return code; |
355 | 94.1k | if (pname.iodev == iodev_default(imemory)) { |
356 | 94.1k | if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len, |
357 | 94.1k | pname.iodev, "PermitFileControl")) < 0 && |
358 | 94.1k | !(is_temp = file_is_tempfile(i_ctx_p, op->value.bytes, r_size(op)))) { |
359 | 0 | return code; |
360 | 0 | } |
361 | 94.1k | } |
362 | | |
363 | 94.1k | code = (*pname.iodev->procs.delete_file)(pname.iodev, pname.fname); |
364 | | |
365 | 94.1k | if (code >= 0 && is_temp) |
366 | 0 | code = record_file_is_tempfile(i_ctx_p, (unsigned char *)pname.fname, strlen(pname.fname), false); |
367 | | |
368 | 94.1k | gs_free_file_name(&pname, "deletefile"); |
369 | 94.1k | if (code < 0) |
370 | 15 | return code; |
371 | 94.1k | pop(1); |
372 | 94.1k | return 0; |
373 | 94.1k | } |
374 | | |
375 | | /* <template> <proc> <scratch> filenameforall - */ |
376 | | static int file_continue(i_ctx_t *); |
377 | | static int file_cleanup(i_ctx_t *); |
378 | | static int |
379 | | zfilenameforall(i_ctx_t *i_ctx_p) |
380 | 2.27M | { |
381 | 2.27M | os_ptr op = osp; |
382 | 2.27M | file_enum *pfen; |
383 | 2.27M | gx_io_device *iodev = NULL; |
384 | 2.27M | gs_parsed_file_name_t pname; |
385 | 2.27M | int code = 0; |
386 | | |
387 | 2.27M | check_op(3); |
388 | 2.27M | check_write_type(*op, t_string); |
389 | 2.27M | check_proc(op[-1]); |
390 | 2.27M | check_read_type(op[-2], t_string); |
391 | | /* Push a mark, the iodev, devicenamelen, the scratch string, the enumerator, */ |
392 | | /* and the procedure, and invoke the continuation. */ |
393 | 2.27M | check_estack(7); |
394 | | /* Get the iodevice */ |
395 | 2.27M | code = parse_file_name(op-2, &pname, i_ctx_p->LockFilePermissions, imemory); |
396 | 2.27M | if (code < 0) |
397 | 0 | return code; |
398 | 2.27M | iodev = (pname.iodev == NULL) ? iodev_default(imemory) : pname.iodev; |
399 | | |
400 | | /* Check for several conditions that just cause us to return success */ |
401 | 2.27M | if (pname.len == 0 || iodev->procs.enumerate_files == iodev_no_enumerate_files) { |
402 | 0 | pop(3); |
403 | 0 | return 0; /* no pattern, or device not found -- just return */ |
404 | 0 | } |
405 | 2.27M | pfen = iodev->procs.enumerate_files(imemory, iodev, (const char *)pname.fname, |
406 | 2.27M | pname.len); |
407 | 2.27M | if (pfen == 0) |
408 | 0 | return_error(gs_error_VMerror); |
409 | 2.27M | push_mark_estack(es_for, file_cleanup); |
410 | 2.27M | ++esp; |
411 | 2.27M | make_istruct(esp, 0, iodev); |
412 | 2.27M | ++esp; |
413 | 2.27M | make_int(esp, r_size(op-2) - pname.len); |
414 | 2.27M | *++esp = *op; |
415 | 2.27M | ++esp; |
416 | 2.27M | make_istruct(esp, 0, pfen); |
417 | 2.27M | *++esp = op[-1]; |
418 | 2.27M | ref_stack_pop(&o_stack, 3); |
419 | 2.27M | code = file_continue(i_ctx_p); |
420 | 2.27M | return code; |
421 | 2.27M | } |
422 | | /* Continuation operator for enumerating files */ |
423 | | static int |
424 | | file_continue(i_ctx_t *i_ctx_p) |
425 | 2.59M | { |
426 | 2.59M | os_ptr op = osp; |
427 | 2.59M | es_ptr pscratch = esp - 2; |
428 | 2.59M | file_enum *pfen = r_ptr(esp - 1, file_enum); |
429 | 2.59M | int devlen = esp[-3].value.intval; |
430 | 2.59M | gx_io_device *iodev = r_ptr(esp - 4, gx_io_device); |
431 | 2.59M | uint len = r_size(pscratch); |
432 | 2.59M | uint code; |
433 | | |
434 | 2.59M | if (len < devlen) { |
435 | 0 | esp -= 6; /* pop proc, pfen, scratch, devlen, iodev , mark */ |
436 | 0 | return_error(gs_error_rangecheck); /* not even room for device len */ |
437 | 0 | } |
438 | | |
439 | 2.59M | do { |
440 | 2.59M | memcpy((char *)pscratch->value.bytes, iodev->dname, devlen); |
441 | 2.59M | code = iodev->procs.enumerate_next(imemory, pfen, (char *)pscratch->value.bytes + devlen, |
442 | 2.59M | len - devlen); |
443 | 2.59M | if (code == ~(uint) 0) { /* all done */ |
444 | 2.27M | esp -= 6; /* pop proc, pfen, scratch, devlen, iodev , mark */ |
445 | 2.27M | return o_pop_estack; |
446 | 2.27M | } else if (code > len - devlen) { /* overran string */ |
447 | 0 | return_error(gs_error_rangecheck); |
448 | 0 | } |
449 | 324k | else if (iodev != iodev_default(imemory) |
450 | 324k | || (check_file_permissions(i_ctx_p, (char *)pscratch->value.bytes, code + devlen, iodev, "PermitFileReading")) == 0) { |
451 | 324k | push(1); |
452 | 324k | ref_assign(op, pscratch); |
453 | 324k | r_set_size(op, code + devlen); |
454 | 324k | push_op_estack(file_continue); /* come again */ |
455 | 324k | *++esp = pscratch[2]; /* proc */ |
456 | 324k | return o_push_estack; |
457 | 324k | } |
458 | 2.59M | } while(1); |
459 | 2.59M | } |
460 | | /* Cleanup procedure for enumerating files */ |
461 | | static int |
462 | | file_cleanup(i_ctx_t *i_ctx_p) |
463 | 0 | { |
464 | 0 | gx_io_device *iodev = r_ptr(esp + 2, gx_io_device); |
465 | |
|
466 | 0 | iodev->procs.enumerate_close(imemory, r_ptr(esp + 5, file_enum)); |
467 | | /* See bug #707007, gp_enumerate_file_close() explicitly frees the file enumerator */ |
468 | 0 | make_null(esp + 5); |
469 | 0 | return 0; |
470 | 0 | } |
471 | | |
472 | | /* <string1> <string2> renamefile - */ |
473 | | static int |
474 | | zrenamefile(i_ctx_t *i_ctx_p) |
475 | 12 | { |
476 | 12 | int code; |
477 | 12 | os_ptr op = osp; |
478 | 12 | gs_parsed_file_name_t pname1, pname2; |
479 | | |
480 | 12 | check_op(2); |
481 | 2 | code = parse_real_file_name(op, &pname2, imemory, "renamefile(to)"); |
482 | 2 | if (code < 0) |
483 | 2 | return code; |
484 | | |
485 | 0 | pname1.fname = 0; |
486 | 0 | code = parse_real_file_name(op - 1, &pname1, imemory, "renamefile(from)"); |
487 | 0 | if (code >= 0) { |
488 | 0 | gx_io_device *iodev_dflt = iodev_default(imemory); |
489 | 0 | if (pname1.iodev != pname2.iodev ) { |
490 | 0 | if (pname1.iodev == iodev_dflt) |
491 | 0 | pname1.iodev = pname2.iodev; |
492 | 0 | if (pname2.iodev == iodev_dflt) |
493 | 0 | pname2.iodev = pname1.iodev; |
494 | 0 | } |
495 | 0 | if (pname1.iodev != pname2.iodev || |
496 | 0 | (pname1.iodev == iodev_dflt && |
497 | | /* |
498 | | * We require FileControl permissions on the source path |
499 | | * unless it is a temporary file. Also, we require FileControl |
500 | | * and FileWriting permissions to the destination file/path. |
501 | | */ |
502 | 0 | ((check_file_permissions(i_ctx_p, pname1.fname, pname1.len, |
503 | 0 | pname1.iodev, "PermitFileControl") < 0 && |
504 | 0 | !file_is_tempfile(i_ctx_p, op[-1].value.bytes, r_size(op - 1))) || |
505 | 0 | (check_file_permissions(i_ctx_p, pname2.fname, pname2.len, |
506 | 0 | pname2.iodev, "PermitFileControl") < 0 || |
507 | 0 | check_file_permissions(i_ctx_p, pname2.fname, pname2.len, |
508 | 0 | pname2.iodev, "PermitFileWriting") < 0 )))) { |
509 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
510 | 0 | } else { |
511 | 0 | code = (*pname1.iodev->procs.rename_file)(pname1.iodev, |
512 | 0 | pname1.fname, pname2.fname); |
513 | 0 | } |
514 | 0 | } |
515 | 0 | gs_free_file_name(&pname2, "renamefile(to)"); |
516 | 0 | gs_free_file_name(&pname1, "renamefile(from)"); |
517 | 0 | if (code < 0) |
518 | 0 | return code; |
519 | 0 | pop(2); |
520 | 0 | return 0; |
521 | 0 | } |
522 | | |
523 | | /* <file> status <open_bool> */ |
524 | | /* <string> status <pages> <bytes> <ref_time> <creation_time> true */ |
525 | | /* <string> status false */ |
526 | | static int |
527 | | zstatus(i_ctx_t *i_ctx_p) |
528 | 2.80M | { |
529 | 2.80M | os_ptr op = osp; |
530 | | |
531 | 2.80M | check_op(1); |
532 | 2.80M | switch (r_type(op)) { |
533 | 346k | case t_file: |
534 | 346k | { |
535 | 346k | stream *s; |
536 | | |
537 | 346k | make_bool(op, (file_is_valid(s, op) ? 1 : 0)); |
538 | 346k | } |
539 | 346k | return 0; |
540 | 2.45M | case t_string: |
541 | 2.45M | { |
542 | 2.45M | gs_parsed_file_name_t pname; |
543 | 2.45M | struct stat fstat; |
544 | 2.45M | int code = parse_file_name(op, &pname, |
545 | 2.45M | i_ctx_p->LockFilePermissions, imemory); |
546 | 2.45M | if (code < 0) { |
547 | 2 | if (code == gs_error_undefinedfilename) { |
548 | 2 | make_bool(op, 0); |
549 | 2 | code = 0; |
550 | 2 | } |
551 | 2 | return code; |
552 | 2 | } |
553 | 2.45M | code = gs_terminate_file_name(&pname, imemory, "status"); |
554 | 2.45M | if (code < 0) |
555 | 0 | return code; |
556 | 2.45M | if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len, |
557 | 2.45M | pname.iodev, "PermitFileReading")) >= 0) { |
558 | 2.45M | code = (*pname.iodev->procs.file_status)(pname.iodev, |
559 | 2.45M | pname.fname, &fstat); |
560 | 2.45M | } |
561 | 2.45M | switch (code) { |
562 | 1.18M | case 0: |
563 | 1.18M | check_ostack(4); |
564 | | /* |
565 | | * Check to make sure that the file size fits into |
566 | | * a PostScript integer. (On some systems, long is |
567 | | * 32 bits, but file sizes are 64 bits.) |
568 | | */ |
569 | 1.18M | push(4); |
570 | 1.18M | make_int(op - 4, stat_blocks(&fstat)); |
571 | 1.18M | make_int(op - 3, fstat.st_size); |
572 | | /* |
573 | | * We can't check the value simply by using ==, |
574 | | * because signed/unsigned == does the wrong thing. |
575 | | * Instead, since integer assignment only keeps the |
576 | | * bottom bits, we convert the values to double |
577 | | * and then test for equality. This handles all |
578 | | * cases of signed/unsigned or width mismatch. |
579 | | */ |
580 | 1.18M | if ((double)op[-4].value.intval != |
581 | 1.18M | (double)stat_blocks(&fstat) || |
582 | 1.18M | (double)op[-3].value.intval != |
583 | 1.18M | (double)fstat.st_size |
584 | 1.18M | ) |
585 | 0 | return_error(gs_error_limitcheck); |
586 | 1.18M | make_int(op - 2, fstat.st_mtime); |
587 | 1.18M | make_int(op - 1, fstat.st_ctime); |
588 | 1.18M | make_bool(op, 1); |
589 | 1.18M | break; |
590 | 1.26M | case gs_error_undefinedfilename: |
591 | 1.26M | make_bool(op, 0); |
592 | 1.26M | code = 0; |
593 | 2.45M | } |
594 | 2.45M | gs_free_file_name(&pname, "status"); |
595 | 2.45M | return code; |
596 | 2.45M | } |
597 | 7 | default: |
598 | 7 | return_op_typecheck(op); |
599 | 2.80M | } |
600 | 2.80M | } |
601 | | |
602 | | /* ------ Non-standard extensions ------ */ |
603 | | |
604 | | /* <executable_file> .execfile - */ |
605 | | static int |
606 | | zexecfile(i_ctx_t *i_ctx_p) |
607 | 0 | { |
608 | 0 | os_ptr op = osp; |
609 | |
|
610 | 0 | check_op(1); |
611 | 0 | check_type_access(*op, t_file, a_executable | a_read | a_execute); |
612 | 0 | check_estack(4); /* cleanup, file, finish, file */ |
613 | 0 | push_mark_estack(es_other, execfile_cleanup); |
614 | 0 | *++esp = *op; |
615 | 0 | push_op_estack(execfile_finish); |
616 | 0 | return zexec(i_ctx_p); |
617 | 0 | } |
618 | | /* Finish normally. */ |
619 | | static int |
620 | | execfile_finish(i_ctx_t *i_ctx_p) |
621 | 0 | { |
622 | 0 | check_ostack(1); |
623 | 0 | esp -= 2; |
624 | 0 | execfile_cleanup(i_ctx_p); |
625 | 0 | return o_pop_estack; |
626 | 0 | } |
627 | | /* Clean up by closing the file. */ |
628 | | static int |
629 | | execfile_cleanup(i_ctx_t *i_ctx_p) |
630 | 0 | { |
631 | 0 | check_ostack(1); |
632 | 0 | *++osp = esp[2]; |
633 | 0 | return zclosefile(i_ctx_p); |
634 | 0 | } |
635 | | |
636 | | /* - .filenamelistseparator <string> */ |
637 | | static int |
638 | | zfilenamelistseparator(i_ctx_t *i_ctx_p) |
639 | 0 | { |
640 | 0 | os_ptr op = osp; |
641 | |
|
642 | 0 | push(1); |
643 | 0 | make_const_string(op, avm_foreign | a_readonly, 1, |
644 | 0 | (const byte *)&gp_file_name_list_separator); |
645 | 0 | return 0; |
646 | 0 | } |
647 | | |
648 | | /* <name> .filenamesplit <dir> <base> <extension> */ |
649 | | static int |
650 | | zfilenamesplit(i_ctx_t *i_ctx_p) |
651 | 0 | { |
652 | 0 | os_ptr op = osp; |
653 | |
|
654 | 0 | check_op(1); |
655 | 0 | check_read_type(*op, t_string); |
656 | | /****** NOT IMPLEMENTED YET ******/ |
657 | 0 | return_error(gs_error_undefined); |
658 | 0 | } |
659 | | |
660 | | /* <string> .libfile <file> true */ |
661 | | /* <string> .libfile <string> false */ |
662 | | int /* exported for zsysvm.c */ |
663 | | zlibfile(i_ctx_t *i_ctx_p) |
664 | 5.25M | { |
665 | 5.25M | os_ptr op = osp; |
666 | 5.25M | int code; |
667 | 5.25M | byte cname[DEFAULT_BUFFER_SIZE]; |
668 | 5.25M | uint clen; |
669 | 5.25M | gs_parsed_file_name_t pname; |
670 | 5.25M | stream *s; |
671 | 5.25M | gx_io_device *iodev_dflt; |
672 | | |
673 | 5.25M | check_op(1); /* Enough arguments */ |
674 | 5.25M | check_ostack(2); /* Enough space for our results */ |
675 | 5.25M | code = parse_file_name(op, &pname, i_ctx_p->LockFilePermissions, imemory); |
676 | 5.25M | if (code < 0) |
677 | 766 | return code; |
678 | 5.25M | iodev_dflt = iodev_default(imemory); |
679 | 5.25M | if (pname.iodev == NULL) |
680 | 3.86M | pname.iodev = iodev_dflt; |
681 | 5.25M | if (pname.iodev != iodev_dflt) { /* Non-OS devices don't have search paths (yet). */ |
682 | 1.39M | code = zopen_file(i_ctx_p, &pname, "r", &s, imemory); |
683 | 1.39M | if (s == NULL) code = gs_note_error(gs_error_undefinedfilename); |
684 | 1.39M | if (code >= 0) { |
685 | 162k | code = ssetfilename(s, op->value.const_bytes, r_size(op)); |
686 | 162k | if (code < 0) { |
687 | 0 | sclose(s); |
688 | 0 | return_error(gs_error_VMerror); |
689 | 0 | } |
690 | 162k | } |
691 | 1.39M | if (code < 0) { |
692 | 1.23M | push(1); |
693 | 1.23M | make_false(op); |
694 | 1.23M | return 0; |
695 | 1.23M | } |
696 | 162k | make_stream_file(op, s, "r"); |
697 | 3.86M | } else { |
698 | 3.86M | ref fref; |
699 | | |
700 | 3.86M | code = lib_file_open(i_ctx_p->lib_path, imemory, i_ctx_p, pname.fname, pname.len, |
701 | 3.86M | (char *)cname, sizeof(cname), &clen, &fref); |
702 | 3.86M | if (code >= 0) { |
703 | 649k | s = fptr(&fref); |
704 | 649k | code = ssetfilename(s, cname, clen); |
705 | 649k | if (code < 0) { |
706 | 0 | sclose(s); |
707 | 0 | return_error(gs_error_VMerror); |
708 | 0 | } |
709 | 649k | } |
710 | 3.86M | if (code < 0) { |
711 | 3.21M | if (code == gs_error_VMerror || code == gs_error_invalidfileaccess) |
712 | 0 | return code; |
713 | 3.21M | push(1); |
714 | 3.21M | make_false(op); |
715 | 3.21M | return 0; |
716 | 3.21M | } |
717 | 649k | ref_assign(op, &fref); |
718 | 649k | } |
719 | 5.25M | push(1); |
720 | 812k | make_true(op); |
721 | 812k | return 0; |
722 | 812k | } |
723 | | |
724 | | /* A "simple" prefix is defined as a (possibly empty) string of |
725 | | alphanumeric, underscore, and hyphen characters. */ |
726 | | static bool |
727 | | prefix_is_simple(const char *pstr) |
728 | 94.5k | { |
729 | 94.5k | int i; |
730 | 94.5k | char c; |
731 | | |
732 | 378k | for (i = 0; (c = pstr[i]) != 0; i++) { |
733 | 283k | if (!(c == '-' || c == '_' || (c >= '0' && c <= '9') || |
734 | 283k | (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) |
735 | 0 | return false; |
736 | 283k | } |
737 | 94.5k | return true; |
738 | 94.5k | } |
739 | | |
740 | | /* <prefix|null> <access_string> .tempfile <name_string> <file> */ |
741 | | static int |
742 | | ztempfile(i_ctx_t *i_ctx_p) |
743 | 94.5k | { |
744 | 94.5k | os_ptr op = osp; |
745 | 94.5k | const char *pstr; |
746 | 94.5k | char fmode[4]; |
747 | 94.5k | char fmode_temp[4]; |
748 | 94.5k | int code; |
749 | 94.5k | char *prefix = NULL; |
750 | 94.5k | char *fname= NULL; |
751 | 94.5k | uint fnlen; |
752 | 94.5k | gp_file *sfile; |
753 | 94.5k | stream *s; |
754 | 94.5k | byte *buf, *sbody; |
755 | | |
756 | 94.5k | check_op(2); |
757 | 94.5k | code = parse_file_access_string(op, fmode_temp); |
758 | 94.5k | if (code < 0) |
759 | 0 | return code; |
760 | 94.5k | prefix = (char *)gs_alloc_bytes(imemory, gp_file_name_sizeof, "ztempfile(prefix)"); |
761 | 94.5k | fname = (char *)gs_alloc_bytes(imemory, gp_file_name_sizeof, "ztempfile(fname)"); |
762 | 94.5k | if (!prefix || !fname) { |
763 | 0 | code = gs_note_error(gs_error_VMerror); |
764 | 0 | goto done; |
765 | 0 | } |
766 | | |
767 | 94.5k | snprintf(fmode, sizeof(fmode), "%s%s", fmode_temp, gp_fmode_binary_suffix); |
768 | 94.5k | if (r_has_type(op - 1, t_null)) |
769 | 94.5k | pstr = gp_scratch_file_name_prefix; |
770 | 0 | else { |
771 | 0 | uint psize; |
772 | |
|
773 | 0 | check_read_type(op[-1], t_string); |
774 | 0 | psize = r_size(op - 1); |
775 | 0 | if (psize >= gp_file_name_sizeof) { |
776 | 0 | code = gs_note_error(gs_error_rangecheck); |
777 | 0 | goto done; |
778 | 0 | } |
779 | 0 | memcpy(prefix, op[-1].value.const_bytes, psize); |
780 | 0 | prefix[psize] = 0; |
781 | 0 | pstr = prefix; |
782 | 0 | } |
783 | | |
784 | 94.5k | if (gp_file_name_is_absolute(pstr, strlen(pstr))) { |
785 | 0 | int plen = strlen(pstr); |
786 | 0 | const char *sep = gp_file_name_separator(); |
787 | 0 | int seplen = strlen(sep); |
788 | | |
789 | | /* This should not be possible if gp_file_name_is_absolute is true I think |
790 | | * But let's avoid the problem. |
791 | | */ |
792 | 0 | if (plen < seplen) |
793 | 0 | return_error(gs_error_Fatal); |
794 | | |
795 | 0 | plen -= seplen; |
796 | | /* strip off the file name prefix, leave just the directory name |
797 | | * so we can check if we are allowed to write to it |
798 | | */ |
799 | 0 | for ( ; plen >=0; plen--) { |
800 | 0 | if ( gs_file_name_check_separator(&pstr[plen], seplen, &pstr[plen])) |
801 | 0 | break; |
802 | 0 | } |
803 | 0 | if (plen < 0) |
804 | 0 | return_error(gs_error_Fatal); |
805 | | |
806 | 0 | memcpy(fname, pstr, plen); |
807 | 0 | fname[plen] = '\0'; |
808 | 0 | if (check_file_permissions(i_ctx_p, fname, strlen(fname), |
809 | 0 | NULL, "PermitFileWriting") < 0) { |
810 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
811 | 0 | goto done; |
812 | 0 | } |
813 | 94.5k | } else if (!prefix_is_simple(pstr)) { |
814 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
815 | 0 | goto done; |
816 | 0 | } |
817 | | |
818 | 94.5k | s = file_alloc_stream(imemory, "ztempfile(stream)"); |
819 | 94.5k | if (s == 0) { |
820 | 0 | code = gs_note_error(gs_error_VMerror); |
821 | 0 | goto done; |
822 | 0 | } |
823 | 94.5k | buf = gs_alloc_bytes(imemory, file_default_buffer_size, |
824 | 94.5k | "ztempfile(buffer)"); |
825 | 94.5k | if (buf == 0) { |
826 | 0 | code = gs_note_error(gs_error_VMerror); |
827 | 0 | goto done; |
828 | 0 | } |
829 | 94.5k | sfile = gp_open_scratch_file(imemory, pstr, fname, fmode); |
830 | 94.5k | if (sfile == 0) { |
831 | 0 | gs_free_object(imemory, buf, "ztempfile(buffer)"); |
832 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
833 | 0 | goto done; |
834 | 0 | } |
835 | 94.5k | fnlen = strlen(fname); |
836 | 94.5k | sbody = ialloc_string(fnlen, ".tempfile(fname)"); |
837 | 94.5k | if (sbody == 0) { |
838 | 0 | gs_free_object(imemory, buf, "ztempfile(buffer)"); |
839 | 0 | code = gs_note_error(gs_error_VMerror); |
840 | 0 | goto done; |
841 | 0 | } |
842 | 94.5k | memcpy(sbody, fname, fnlen); |
843 | 94.5k | file_init_stream(s, sfile, fmode, buf, file_default_buffer_size); |
844 | 94.5k | code = ssetfilename(s, (const unsigned char*) fname, fnlen); |
845 | 94.5k | if (code < 0) { |
846 | 0 | gx_io_device *iodev_dflt = iodev_default(imemory); |
847 | 0 | sclose(s); |
848 | 0 | iodev_dflt->procs.delete_file(iodev_dflt, fname); |
849 | 0 | ifree_string(sbody, fnlen, ".tempfile(fname)"); |
850 | 0 | code = gs_note_error(gs_error_VMerror); |
851 | 0 | goto done; |
852 | 0 | } |
853 | 94.5k | make_string(op - 1, a_readonly | icurrent_space, fnlen, sbody); |
854 | 94.5k | make_stream_file(op, s, fmode); |
855 | 94.5k | code = record_file_is_tempfile(i_ctx_p, (unsigned char *)fname, fnlen, true); |
856 | | |
857 | 94.5k | done: |
858 | 94.5k | if (prefix) |
859 | 94.5k | gs_free_object(imemory, prefix, "ztempfile(prefix)"); |
860 | 94.5k | if (fname) |
861 | 94.5k | gs_free_object(imemory, fname, "ztempfile(fname)"); |
862 | 94.5k | return code; |
863 | 94.5k | } |
864 | | |
865 | | /* Return the filename used to open a file object |
866 | | * this is currently only used by the PDF interpreter to |
867 | | * get a filename corresponding to the PDF file being |
868 | | * executed. Since we always execute PDF files from disk |
869 | | * this will always be OK. |
870 | | */ |
871 | | static int zgetfilename(i_ctx_t *i_ctx_p) |
872 | 0 | { |
873 | 0 | os_ptr op = osp; |
874 | 0 | uint fnlen; |
875 | 0 | gs_const_string pfname; |
876 | 0 | stream *s; |
877 | 0 | byte *sbody; |
878 | 0 | int code; |
879 | |
|
880 | 0 | check_op(1); |
881 | 0 | check_ostack(1); |
882 | 0 | check_read_type(*op, t_file); |
883 | | |
884 | 0 | s = (op)->value.pfile; |
885 | |
|
886 | 0 | code = sfilename(s, &pfname); |
887 | 0 | if (code < 0) { |
888 | 0 | pfname.size = 0; |
889 | 0 | } |
890 | |
|
891 | 0 | fnlen = pfname.size; |
892 | 0 | sbody = ialloc_string(fnlen, ".getfilename"); |
893 | 0 | if (sbody == 0) { |
894 | 0 | code = gs_note_error(gs_error_VMerror); |
895 | 0 | return code; |
896 | 0 | } |
897 | 0 | memcpy(sbody, pfname.data, fnlen); |
898 | 0 | make_string(op, a_readonly | icurrent_space, fnlen, sbody); |
899 | |
|
900 | 0 | return 0; |
901 | 0 | } |
902 | | |
903 | | static int zaddcontrolpath(i_ctx_t *i_ctx_p) |
904 | 3.57M | { |
905 | 3.57M | int code; |
906 | 3.57M | os_ptr op = osp; |
907 | 3.57M | ref nsref; |
908 | 3.57M | unsigned int n = -1; |
909 | | |
910 | 3.57M | check_op(2); |
911 | 3.57M | check_read_type(*op, t_string); |
912 | 3.57M | check_type(op[-1], t_name); |
913 | | |
914 | 3.57M | name_string_ref(imemory, op-1, &nsref); |
915 | 3.57M | if (r_size(&nsref) == 17 && |
916 | 3.57M | strncmp((const char *)nsref.value.const_bytes, |
917 | 3.57M | "PermitFileReading", 17) == 0) { |
918 | 2.92M | n = gs_permit_file_reading; |
919 | 2.92M | } else if (r_size(&nsref) == 17 && |
920 | 649k | strncmp((const char *)nsref.value.const_bytes, |
921 | 649k | "PermitFileWriting", 17) == 0) { |
922 | 324k | n = gs_permit_file_writing; |
923 | 324k | } else if (r_size(&nsref) == 17 && |
924 | 324k | strncmp((const char *)nsref.value.const_bytes, |
925 | 324k | "PermitFileControl", 17) == 0) { |
926 | 324k | n = gs_permit_file_control; |
927 | 324k | } |
928 | | |
929 | 3.57M | if (n == -1) |
930 | 0 | code = gs_note_error(gs_error_rangecheck); |
931 | 3.57M | else if (gs_is_path_control_active(imemory)) |
932 | 0 | code = gs_note_error(gs_error_Fatal); |
933 | 3.57M | else |
934 | 3.57M | code = gs_add_control_path_len(imemory, n, |
935 | 3.57M | (const char *)op[0].value.const_bytes, |
936 | 3.57M | (size_t)r_size(&op[0])); |
937 | 3.57M | pop(2); |
938 | 3.57M | return code; |
939 | 3.57M | } |
940 | | |
941 | | static int zactivatepathcontrol(i_ctx_t *i_ctx_p) |
942 | 162k | { |
943 | 162k | gs_activate_path_control(imemory, 1); |
944 | 162k | return 0; |
945 | 162k | } |
946 | | static int zcurrentpathcontrolstate(i_ctx_t *i_ctx_p) |
947 | 162k | { |
948 | 162k | os_ptr op = osp; |
949 | 162k | push(1); |
950 | 162k | if (gs_is_path_control_active(imemory)) { |
951 | 1 | make_true(op); |
952 | 1 | } |
953 | 162k | else { |
954 | 162k | make_false(op); |
955 | 162k | } |
956 | 162k | return 0; |
957 | 162k | } |
958 | | |
959 | | /* ------ Initialization procedure ------ */ |
960 | | |
961 | | const op_def zfile_op_defs[] = |
962 | | { |
963 | | {"1deletefile", zdeletefile}, |
964 | | {"1.execfile", zexecfile}, |
965 | | {"2file", zfile}, |
966 | | {"3filenameforall", zfilenameforall}, |
967 | | {"0.filenamelistseparator", zfilenamelistseparator}, |
968 | | {"1.filenamesplit", zfilenamesplit}, |
969 | | {"1.libfile", zlibfile}, |
970 | | {"2renamefile", zrenamefile}, |
971 | | {"1status", zstatus}, |
972 | | {"2.tempfile", ztempfile}, |
973 | | /* Internal operators */ |
974 | | {"0%file_continue", file_continue}, |
975 | | {"0%execfile_finish", execfile_finish}, |
976 | | {"1.getfilename", zgetfilename}, |
977 | | /* Control path operators */ |
978 | | {"2.addcontrolpath", zaddcontrolpath}, |
979 | | {"0.activatepathcontrol", zactivatepathcontrol}, |
980 | | {"0.currentpathcontrolstate", zcurrentpathcontrolstate}, |
981 | | op_def_end(0) |
982 | | }; |
983 | | |
984 | | /* ------ File name parsing ------ */ |
985 | | |
986 | | /* Parse a file name into device and individual name. */ |
987 | | /* See gsfname.c for details. */ |
988 | | static int |
989 | | parse_file_name(const ref * op, gs_parsed_file_name_t * pfn, bool safemode, |
990 | | gs_memory_t *memory) |
991 | 53.1M | { |
992 | 53.1M | int code; |
993 | | |
994 | 53.1M | check_read_type(*op, t_string); |
995 | 53.1M | code = gs_parse_file_name(pfn, (const char *)op->value.const_bytes, |
996 | 53.1M | r_size(op), memory); |
997 | 53.1M | if (code < 0) |
998 | 769 | return code; |
999 | | /* |
1000 | | * Check here for the %pipe device which is illegal when |
1001 | | * LockFilePermissions is true. In the future we might want to allow |
1002 | | * the %pipe device to be included on the PermitFile... paths, but |
1003 | | * for now it is simply disallowed. |
1004 | | */ |
1005 | 53.1M | if (pfn->iodev && safemode && strcmp(pfn->iodev->dname, "%pipe%") == 0) |
1006 | 0 | return gs_error_invalidfileaccess; |
1007 | 53.1M | return code; |
1008 | 53.1M | } |
1009 | | |
1010 | | /* Parse a real (non-device) file name and convert to a C string. */ |
1011 | | /* See gsfname.c for details. */ |
1012 | | static int |
1013 | | parse_real_file_name(const ref *op, gs_parsed_file_name_t *pfn, |
1014 | | gs_memory_t *mem, client_name_t cname) |
1015 | 94.2k | { |
1016 | 94.2k | check_read_type(*op, t_string); |
1017 | 94.1k | return gs_parse_real_file_name(pfn, (const char *)op->value.const_bytes, |
1018 | 94.1k | r_size(op), mem, cname); |
1019 | 94.2k | } |
1020 | | |
1021 | | /* Parse the access string for opening a file. */ |
1022 | | /* [4] is for r/w, +, b, \0. */ |
1023 | | static int |
1024 | | parse_file_access_string(const ref *op, char file_access[4]) |
1025 | 43.2M | { |
1026 | 43.2M | const byte *astr; |
1027 | | |
1028 | 43.2M | check_read_type(*op, t_string); |
1029 | 43.2M | astr = op->value.const_bytes; |
1030 | 43.2M | switch (r_size(op)) { |
1031 | 94.5k | case 2: |
1032 | 94.5k | if (astr[1] != '+') |
1033 | 0 | return_error(gs_error_invalidfileaccess); |
1034 | 94.5k | file_access[1] = '+'; |
1035 | 94.5k | file_access[2] = 0; |
1036 | 94.5k | break; |
1037 | 43.1M | case 1: |
1038 | 43.1M | file_access[1] = 0; |
1039 | 43.1M | break; |
1040 | 10 | default: |
1041 | 10 | return_error(gs_error_invalidfileaccess); |
1042 | 43.2M | } |
1043 | 43.2M | switch (astr[0]) { |
1044 | 4.70M | case 'r': |
1045 | 43.2M | case 'w': |
1046 | 43.2M | case 'a': |
1047 | 43.2M | break; |
1048 | 4 | default: |
1049 | 4 | return_error(gs_error_invalidfileaccess); |
1050 | 43.2M | } |
1051 | 43.2M | file_access[0] = astr[0]; |
1052 | 43.2M | return 0; |
1053 | 43.2M | } |
1054 | | |
1055 | | /* ------ Stream opening ------ */ |
1056 | | |
1057 | | /* |
1058 | | * Open a file specified by a parsed file name (which may be only a |
1059 | | * device). |
1060 | | */ |
1061 | | int |
1062 | | zopen_file(i_ctx_t *i_ctx_p, const gs_parsed_file_name_t *pfn, |
1063 | | const char *file_access, stream **ps, gs_memory_t *mem) |
1064 | 5.68M | { |
1065 | 5.68M | gx_io_device *const iodev = pfn->iodev; |
1066 | 5.68M | int code = 0; |
1067 | | |
1068 | 5.68M | if (pfn->fname == NULL) { /* just a device */ |
1069 | 0 | iodev->state = i_ctx_p; |
1070 | 0 | code = iodev->procs.open_device(iodev, file_access, ps, mem); |
1071 | 0 | iodev->state = NULL; |
1072 | 0 | return code; |
1073 | 0 | } |
1074 | 5.68M | else { /* file */ |
1075 | 5.68M | iodev_proc_open_file((*open_file)) = iodev->procs.open_file; |
1076 | | |
1077 | 5.68M | if (open_file == 0) |
1078 | 1.79M | open_file = iodev_os_open_file; |
1079 | | /* Check OS files to make sure we allow the type of access */ |
1080 | 5.68M | if (open_file == iodev_os_open_file) { |
1081 | 1.79M | code = check_file_permissions(i_ctx_p, pfn->fname, pfn->len, pfn->iodev, |
1082 | 1.79M | file_access[0] == 'r' ? "PermitFileReading" : "PermitFileWriting"); |
1083 | | |
1084 | 1.79M | if (code < 0 && !file_is_tempfile(i_ctx_p, |
1085 | 0 | (const uchar *)pfn->fname, pfn->len)) |
1086 | 0 | return code; |
1087 | 1.79M | } |
1088 | 5.68M | return open_file(iodev, pfn->fname, pfn->len, file_access, ps, mem); |
1089 | 5.68M | } |
1090 | 5.68M | } |
1091 | | |
1092 | | /* |
1093 | | * Define the file_open procedure for the %os% IODevice (also used, as the |
1094 | | * default, for %pipe% and possibly others). |
1095 | | */ |
1096 | | static int |
1097 | | iodev_os_open_file(gx_io_device * iodev, const char *fname, uint len, |
1098 | | const char *file_access, stream ** ps, gs_memory_t * mem) |
1099 | 37.1M | { |
1100 | 37.1M | return file_open_stream(fname, len, file_access, |
1101 | 37.1M | file_default_buffer_size, ps, |
1102 | 37.1M | iodev, iodev->procs.gp_fopen, mem); |
1103 | 37.1M | } |
1104 | | |
1105 | | /* Make a t_file reference to a stream. */ |
1106 | | void |
1107 | | make_stream_file(ref * pfile, stream * s, const char *access) |
1108 | 42.7M | { |
1109 | 42.7M | uint attrs = |
1110 | 42.7M | (access[1] == '+' ? a_write + a_read + a_execute : 0) | |
1111 | 42.7M | imemory_space((gs_ref_memory_t *) s->memory); |
1112 | | |
1113 | 42.7M | if (access[0] == 'r') { |
1114 | 3.76M | make_file(pfile, attrs | (a_read | a_execute), s->read_id, s); |
1115 | 3.76M | s->write_id = 0; |
1116 | 38.9M | } else { |
1117 | 38.9M | make_file(pfile, attrs | a_write, s->write_id, s); |
1118 | 38.9M | s->read_id = 0; |
1119 | 38.9M | } |
1120 | 42.7M | } |
1121 | | |
1122 | | /* return zero for success, -ve for error, +1 for continue */ |
1123 | | static int |
1124 | | lib_file_open_search_with_no_combine(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p, |
1125 | | const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile, |
1126 | | gx_io_device *iodev, bool starting_arg_file, char *fmode) |
1127 | 8 | { |
1128 | 8 | stream *s; |
1129 | 8 | uint blen1 = blen; |
1130 | 8 | struct stat fstat; |
1131 | 8 | int code = 1; |
1132 | | |
1133 | 8 | if (gp_file_name_reduce(fname, flen, buffer, &blen1) != gp_combine_success) |
1134 | 0 | goto skip; |
1135 | | |
1136 | 8 | if (starting_arg_file || check_file_permissions(i_ctx_p, buffer, blen1, iodev, "PermitFileReading") >= 0) { |
1137 | 8 | if (iodev_os_open_file(iodev, (const char *)buffer, blen1, |
1138 | 8 | (const char *)fmode, &s, (gs_memory_t *)mem) == 0) { |
1139 | 0 | *pclen = blen1; |
1140 | 0 | make_stream_file(pfile, s, "r"); |
1141 | 0 | code = 0; |
1142 | 0 | } |
1143 | 8 | } |
1144 | 0 | else { |
1145 | | /* If we are not allowed to open the file by check_file_permissions_aux() |
1146 | | * and if the file exists, throw an error....... |
1147 | | * Otherwise, keep searching. |
1148 | | */ |
1149 | 0 | if ((*iodev->procs.file_status)(iodev, buffer, &fstat) >= 0) { |
1150 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1151 | 0 | } |
1152 | 0 | } |
1153 | | |
1154 | 8 | skip: |
1155 | 8 | return code; |
1156 | 8 | } |
1157 | | |
1158 | | /* return zero for success, -ve for error, +1 for continue */ |
1159 | | static int |
1160 | | lib_file_open_search_with_combine(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p, |
1161 | | const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile, |
1162 | | gx_io_device *iodev, bool starting_arg_file, char *fmode) |
1163 | 4.02M | { |
1164 | 4.02M | stream *s; |
1165 | 4.02M | const gs_file_path *pfpath = lib_path; |
1166 | 4.02M | uint pi; |
1167 | 4.02M | int code = 1; |
1168 | | |
1169 | 46.5M | for (pi = 0; pi < r_size(&pfpath->list) && code == 1; ++pi) { |
1170 | 42.5M | const ref *prdir = pfpath->list.value.refs + pi; |
1171 | 42.5M | const char *pstr = (const char *)prdir->value.const_bytes; |
1172 | 42.5M | uint plen = r_size(prdir), blen1 = blen; |
1173 | 42.5M | gs_parsed_file_name_t pname; |
1174 | 42.5M | gp_file_name_combine_result r; |
1175 | | |
1176 | | /* We need to concatenate and parse the file name here |
1177 | | * if this path has a %device% prefix. */ |
1178 | 42.5M | if (pstr[0] == '%') { |
1179 | | /* We concatenate directly since gp_file_name_combine_* |
1180 | | * rules are not correct for other devices such as %rom% */ |
1181 | 7.23M | code = gs_parse_file_name(&pname, pstr, plen, mem); |
1182 | 7.23M | if (code < 0) { |
1183 | 0 | code = 1; |
1184 | 0 | continue; |
1185 | 0 | } |
1186 | 7.23M | if (blen < max(pname.len, plen) + flen) |
1187 | 0 | return_error(gs_error_limitcheck); |
1188 | 7.23M | memcpy(buffer, pname.fname, pname.len); |
1189 | 7.23M | memcpy(buffer+pname.len, fname, flen); |
1190 | 7.23M | if (pname.iodev->procs.open_file == NULL) { |
1191 | 0 | code = 1; |
1192 | 0 | continue; |
1193 | 0 | } |
1194 | 7.23M | code = pname.iodev->procs.open_file(pname.iodev, buffer, pname.len + flen, fmode, |
1195 | 7.23M | &s, (gs_memory_t *)mem); |
1196 | 7.23M | if (code < 0) { |
1197 | 6.42M | code = 1; |
1198 | 6.42M | continue; |
1199 | 6.42M | } |
1200 | 812k | make_stream_file(pfile, s, "r"); |
1201 | | /* fill in the buffer with the device concatenated */ |
1202 | 812k | memcpy(buffer, pstr, plen); |
1203 | 812k | memcpy(buffer+plen, fname, flen); |
1204 | 812k | *pclen = plen + flen; |
1205 | 812k | code = 0; |
1206 | 35.3M | } else { |
1207 | 35.3M | r = gp_file_name_combine(pstr, plen, |
1208 | 35.3M | fname, flen, false, buffer, &blen1); |
1209 | 35.3M | if (r != gp_combine_success) |
1210 | 0 | continue; |
1211 | 35.3M | if (starting_arg_file || check_file_permissions(i_ctx_p, buffer, |
1212 | 35.3M | blen1, iodev, "PermitFileReading") >= 0) { |
1213 | | |
1214 | 35.3M | if (iodev_os_open_file(iodev, (const char *)buffer, blen1, |
1215 | 35.3M | (const char *)fmode, &s, (gs_memory_t *)mem) == 0) { |
1216 | 0 | *pclen = blen1; |
1217 | 0 | make_stream_file(pfile, s, "r"); |
1218 | 0 | code = 0; |
1219 | 0 | } |
1220 | 35.3M | } |
1221 | 0 | else { |
1222 | 0 | struct stat fstat; |
1223 | | /* If we are not allowed to open the file by check_file_permissions_aux() |
1224 | | * and if the file exists, throw an error....... |
1225 | | * Otherwise, keep searching. |
1226 | | */ |
1227 | 0 | if ((*iodev->procs.file_status)(iodev, (const char *)buffer, &fstat) >= 0) { |
1228 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1229 | 0 | } |
1230 | 0 | } |
1231 | 35.3M | } |
1232 | 42.5M | } |
1233 | 4.02M | return code; |
1234 | 4.02M | } |
1235 | | |
1236 | | /* Return a file object of of the file searched for using the search paths. */ |
1237 | | /* The fname cannot contain a device part (%...%) but the lib paths might. */ |
1238 | | /* The startup code calls this to open the initialization file gs_init.ps. */ |
1239 | | /* The startup code also calls this to open @-files. */ |
1240 | | int |
1241 | | lib_file_open(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p, |
1242 | | const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile) |
1243 | 4.02M | { /* i_ctx_p is NULL running arg (@) files. |
1244 | | * lib_path and mem are never NULL |
1245 | | */ |
1246 | 4.02M | bool starting_arg_file = (i_ctx_p == NULL) ? true : i_ctx_p->starting_arg_file; |
1247 | 4.02M | bool search_with_no_combine = false; |
1248 | 4.02M | bool search_with_combine = false; |
1249 | 4.02M | char fmode[2] = { 'r', 0}; |
1250 | 4.02M | gx_io_device *iodev = iodev_default(mem); |
1251 | 4.02M | gs_main_instance *minst = get_minst_from_memory(mem); |
1252 | 4.02M | int code; |
1253 | | |
1254 | 4.02M | if (i_ctx_p && starting_arg_file) |
1255 | 0 | i_ctx_p->starting_arg_file = false; |
1256 | | |
1257 | | /* when starting arg files (@ files) iodev_default is not yet set */ |
1258 | 4.02M | if (iodev == 0) |
1259 | 0 | iodev = (gx_io_device *)gx_io_device_table[0]; |
1260 | | |
1261 | 4.02M | if (gp_file_name_is_absolute(fname, flen)) { |
1262 | 8 | search_with_no_combine = true; |
1263 | 8 | search_with_combine = false; |
1264 | 4.02M | } else { |
1265 | 4.02M | search_with_no_combine = starting_arg_file; |
1266 | 4.02M | search_with_combine = true; |
1267 | 4.02M | } |
1268 | 4.02M | if (minst->search_here_first) { |
1269 | 0 | if (search_with_no_combine) { |
1270 | 0 | code = lib_file_open_search_with_no_combine(lib_path, mem, i_ctx_p, |
1271 | 0 | fname, flen, buffer, blen, pclen, pfile, |
1272 | 0 | iodev, starting_arg_file, fmode); |
1273 | 0 | if (code <= 0) /* +ve means continue continue */ |
1274 | 0 | return code; |
1275 | 0 | } |
1276 | 0 | if (search_with_combine) { |
1277 | 0 | code = lib_file_open_search_with_combine(lib_path, mem, i_ctx_p, |
1278 | 0 | fname, flen, buffer, blen, pclen, pfile, |
1279 | 0 | iodev, starting_arg_file, fmode); |
1280 | 0 | if (code <= 0) /* +ve means continue searching */ |
1281 | 0 | return code; |
1282 | 0 | } |
1283 | 4.02M | } else { |
1284 | 4.02M | if (search_with_combine) { |
1285 | 4.02M | code = lib_file_open_search_with_combine(lib_path, mem, i_ctx_p, |
1286 | 4.02M | fname, flen, buffer, blen, pclen, pfile, |
1287 | 4.02M | iodev, starting_arg_file, fmode); |
1288 | 4.02M | if (code <= 0) /* +ve means continue searching */ |
1289 | 812k | return code; |
1290 | 4.02M | } |
1291 | 3.21M | if (search_with_no_combine) { |
1292 | 8 | code = lib_file_open_search_with_no_combine(lib_path, mem, i_ctx_p, |
1293 | 8 | fname, flen, buffer, blen, pclen, pfile, |
1294 | 8 | iodev, starting_arg_file, fmode); |
1295 | 8 | if (code <= 0) /* +ve means continue searching */ |
1296 | 0 | return code; |
1297 | 8 | } |
1298 | 3.21M | } |
1299 | 4.02M | return_error(gs_error_undefinedfilename); |
1300 | 4.02M | } |
1301 | | |
1302 | | /* The startup code calls this to open @-files. */ |
1303 | | stream * |
1304 | | lib_sopen(const gs_file_path_ptr pfpath, const gs_memory_t *mem, const char *fname) |
1305 | 0 | { |
1306 | | /* We need a buffer to hold the expanded file name. */ |
1307 | 0 | char filename_found[DEFAULT_BUFFER_SIZE]; |
1308 | 0 | uint fnamelen; |
1309 | 0 | ref obj; |
1310 | 0 | int code; |
1311 | | |
1312 | | /* open the usual 'stream', then if successful, return the file */ |
1313 | 0 | code = lib_file_open(pfpath, mem, NULL, fname, strlen(fname), |
1314 | 0 | filename_found, sizeof(filename_found), &fnamelen, &obj); |
1315 | |
|
1316 | 0 | if (code < 0) |
1317 | 0 | return NULL; |
1318 | | |
1319 | 0 | return((stream *)(obj.value.pfile)); |
1320 | 0 | } |
1321 | | |
1322 | | /* Open a file stream that reads a string. */ |
1323 | | /* (This is currently used only by the ccinit feature.) */ |
1324 | | /* The string must be allocated in non-garbage-collectable (foreign) space. */ |
1325 | | int |
1326 | | file_read_string(const byte *str, uint len, ref *pfile, gs_ref_memory_t *imem) |
1327 | 0 | { |
1328 | 0 | stream *s = file_alloc_stream((gs_memory_t *)imem, "file_read_string"); |
1329 | |
|
1330 | 0 | if (s == 0) |
1331 | 0 | return_error(gs_error_VMerror); |
1332 | 0 | sread_string(s, str, len); |
1333 | 0 | s->foreign = 1; |
1334 | 0 | s->write_id = 0; |
1335 | 0 | make_file(pfile, a_readonly | imemory_space(imem), s->read_id, s); |
1336 | 0 | s->save_close = s->procs.close; |
1337 | 0 | s->procs.close = file_close_disable; |
1338 | 0 | return 0; |
1339 | 0 | } |
1340 | | |
1341 | | /* Report an error by storing it in the stream's error_string. */ |
1342 | | int |
1343 | | filter_report_error(stream_state * st, const char *str) |
1344 | 0 | { |
1345 | 0 | if_debug1m('s', st->memory, "[s]stream error: %s\n", str); |
1346 | 0 | strncpy(st->error_string, str, STREAM_MAX_ERROR_STRING); |
1347 | | /* Ensure null termination. */ |
1348 | 0 | st->error_string[STREAM_MAX_ERROR_STRING] = 0; |
1349 | 0 | return 0; |
1350 | 0 | } |
1351 | | |
1352 | | /* Open a file stream for a filter. */ |
1353 | | int |
1354 | | filter_open(const char *file_access, uint buffer_size, ref * pfile, |
1355 | | const stream_procs * procs, const stream_template * templat, |
1356 | | const stream_state * st, gs_memory_t *mem) |
1357 | 1.54M | { |
1358 | 1.54M | stream *s; |
1359 | 1.54M | uint ssize = gs_struct_type_size(templat->stype); |
1360 | 1.54M | stream_state *sst = 0; |
1361 | 1.54M | int code; |
1362 | | |
1363 | 1.54M | if (templat->stype != &st_stream_state) { |
1364 | 1.47M | sst = s_alloc_state(mem, templat->stype, "filter_open(stream_state)"); |
1365 | 1.47M | if (sst == 0) |
1366 | 0 | return_error(gs_error_VMerror); |
1367 | 1.47M | } |
1368 | 1.54M | code = file_open_stream((char *)0, 0, file_access, buffer_size, &s, |
1369 | 1.54M | (gx_io_device *)0, (iodev_proc_fopen_t)0, mem); |
1370 | 1.54M | if (code < 0) { |
1371 | 0 | gs_free_object(mem, sst, "filter_open(stream_state)"); |
1372 | 0 | return code; |
1373 | 0 | } |
1374 | 1.54M | s_std_init(s, s->cbuf, s->bsize, procs, |
1375 | 1.54M | (*file_access == 'r' ? s_mode_read : s_mode_write)); |
1376 | 1.54M | s->procs.process = templat->process; |
1377 | 1.54M | s->save_close = s->procs.close; |
1378 | 1.54M | s->procs.close = file_close_file; |
1379 | 1.54M | if (sst == 0) { |
1380 | | /* This stream doesn't have any state of its own. */ |
1381 | | /* Hack: use the stream itself as the state. */ |
1382 | 67.2k | sst = (stream_state *) s; |
1383 | 1.47M | } else if (st != 0) /* might not have client parameters */ |
1384 | 1.47M | memcpy(sst, st, ssize); |
1385 | 1.54M | s->state = sst; |
1386 | 1.54M | s_init_state(sst, templat, mem); |
1387 | 1.54M | sst->report_error = filter_report_error; |
1388 | | |
1389 | 1.54M | if (templat->init != 0) { |
1390 | 1.47M | code = (*templat->init)(sst); |
1391 | 1.47M | if (code < 0) { |
1392 | 0 | gs_free_object(mem, sst, "filter_open(stream_state)"); |
1393 | 0 | gs_free_object(mem, s->cbuf, "filter_open(buffer)"); |
1394 | 0 | return code; |
1395 | 0 | } |
1396 | 1.47M | } |
1397 | 1.54M | make_stream_file(pfile, s, file_access); |
1398 | 1.54M | return 0; |
1399 | 1.54M | } |
1400 | | |
1401 | | /* Close a file object. */ |
1402 | | /* This is exported only for gsmain.c. */ |
1403 | | int |
1404 | | file_close(ref * pfile) |
1405 | 0 | { |
1406 | 0 | stream *s; |
1407 | |
|
1408 | 0 | if (file_is_valid(s, pfile)) { /* closing a closed file is a no-op */ |
1409 | 0 | if (sclose(s)) |
1410 | 0 | return_error(gs_error_ioerror); |
1411 | 0 | } |
1412 | 0 | return 0; |
1413 | 0 | } |