/src/ghostpdl/psi/zfile.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2022 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* 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 | 580k | { |
127 | 580k | long i; |
128 | 580k | 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 | 580k | const char *win_sep2 = "\\"; |
132 | 580k | bool use_windows_pathsep = (gs_file_name_check_separator(win_sep2, 1, win_sep2) == 1); |
133 | 580k | 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 | 580k | if (iodev && iodev != iodev_default(imemory)) { |
139 | 243k | return 0; |
140 | 243k | } |
141 | | |
142 | | /* Assuming a reduced file name. */ |
143 | 337k | if (dict_find_string(&(i_ctx_p->userparams), permitgroup, &permitlist) <= 0) |
144 | 0 | return 0; /* if Permissions not found, just allow access */ |
145 | | |
146 | 337k | for (i=0; i<r_size(permitlist); i++) { |
147 | 337k | ref permitstring; |
148 | 337k | const string_match_params win_filename_params = { |
149 | | '*', '?', '\\', true, true /* ignore case & '/' == '\\' */ |
150 | 337k | }; |
151 | 337k | const byte *permstr; |
152 | 337k | uint permlen; |
153 | 337k | int cwd_len = 0; |
154 | | |
155 | 337k | if (array_get(imemory, permitlist, i, &permitstring) < 0 || |
156 | 337k | r_type(&permitstring) != t_string |
157 | 337k | ) |
158 | 0 | break; /* any problem, just fail */ |
159 | 337k | permstr = permitstring.value.bytes; |
160 | 337k | permlen = r_size(&permitstring); |
161 | | /* |
162 | | * Check if any file name is permitted with "*". |
163 | | */ |
164 | 337k | if (permlen == 1 && permstr[0] == '*') |
165 | 337k | 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 | 337k | } |
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 | 580k | { |
199 | 580k | char fname_reduced[gp_file_name_sizeof]; |
200 | 580k | uint rlen = sizeof(fname_reduced); |
201 | | |
202 | 580k | 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 | 580k | return check_file_permissions_reduced(i_ctx_p, fname_reduced, rlen, iodev, permitgroup); |
205 | 580k | } |
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 | 464k | { |
212 | 464k | i_ctx_t *i_ctx_p = get_minst_from_memory(mem)->i_ctx_p; |
213 | 464k | gs_parsed_file_name_t pname; |
214 | 464k | const char *permitgroup = permission[0] == 'r' ? "PermitFileReading" : "PermitFileWriting"; |
215 | 464k | int code = gs_parse_file_name(&pname, fname, len, imemory); |
216 | 464k | if (code < 0) |
217 | 0 | return code; |
218 | | |
219 | 464k | if (pname.iodev && i_ctx_p->LockFilePermissions |
220 | 464k | && strcmp(pname.iodev->dname, "%pipe%") == 0) { |
221 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
222 | 0 | } |
223 | 464k | else { |
224 | 464k | code = check_file_permissions(i_ctx_p, pname.fname, pname.len, pname.iodev, permitgroup); |
225 | 464k | } |
226 | 464k | return code; |
227 | 464k | } |
228 | | |
229 | | /* <name_string> <access_string> file <file> */ |
230 | | int /* exported for zsysvm.c */ |
231 | | zfile(i_ctx_t *i_ctx_p) |
232 | 93.7k | { |
233 | 93.7k | os_ptr op = osp; |
234 | 93.7k | char file_access[4]; |
235 | 93.7k | gs_parsed_file_name_t pname; |
236 | 93.7k | int code = parse_file_access_string(op, file_access); |
237 | 93.7k | stream *s; |
238 | | |
239 | 93.7k | if (code < 0) |
240 | 0 | return code; |
241 | 93.7k | code = parse_file_name(op-1, &pname, i_ctx_p->LockFilePermissions, imemory); |
242 | 93.7k | if (code < 0) |
243 | 0 | 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 | 93.7k | if (pname.iodev && pname.iodev->dtype == iodev_dtype_stdio) { |
250 | 78.9k | bool statement = (strcmp(pname.iodev->dname, "%statementedit%") == 0); |
251 | 78.9k | bool lineedit = (strcmp(pname.iodev->dname, "%lineedit%") == 0); |
252 | 78.9k | if (pname.fname) |
253 | 0 | return_error(gs_error_invalidfileaccess); |
254 | 78.9k | 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 | 78.9k | pname.iodev->state = i_ctx_p; |
275 | 78.9k | code = (*pname.iodev->procs.open_device)(pname.iodev, |
276 | 78.9k | file_access, &s, imemory); |
277 | 78.9k | pname.iodev->state = NULL; |
278 | 78.9k | } else { |
279 | 14.7k | if (pname.iodev == NULL) |
280 | 7.51k | pname.iodev = iodev_default(imemory); |
281 | 14.7k | code = zopen_file(i_ctx_p, &pname, file_access, &s, imemory); |
282 | 14.7k | } |
283 | 93.7k | if (code < 0) |
284 | 11.3k | return code; |
285 | 82.4k | if (s == NULL) |
286 | 0 | return_error(gs_error_undefinedfilename); |
287 | 82.4k | code = ssetfilename(s, op[-1].value.const_bytes, r_size(op - 1)); |
288 | 82.4k | if (code < 0) { |
289 | 0 | sclose(s); |
290 | 0 | return_error(gs_error_VMerror); |
291 | 0 | } |
292 | 82.4k | make_stream_file(op - 1, s, file_access); |
293 | 82.4k | pop(1); |
294 | 82.4k | return code; |
295 | 82.4k | } |
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 | 669 | { |
321 | 669 | ref *SAFETY; |
322 | 669 | ref *tempfiles; |
323 | 669 | ref kname, bref; |
324 | 669 | int code = 0; |
325 | | |
326 | 669 | if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 || |
327 | 669 | dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0) { |
328 | 0 | return 0; |
329 | 0 | } |
330 | 669 | if ((code = name_ref(imemory, fname, len, &kname, 1)) < 0) { |
331 | 0 | return code; |
332 | 0 | } |
333 | 669 | make_bool(&bref, true); |
334 | 669 | if (add) |
335 | 669 | return idict_put(tempfiles, &kname, &bref); |
336 | 0 | else |
337 | 0 | return idict_undef(tempfiles, &kname); |
338 | 669 | } |
339 | | |
340 | | /* ------ Level 2 extensions ------ */ |
341 | | |
342 | | /* <string> deletefile - */ |
343 | | static int |
344 | | zdeletefile(i_ctx_t *i_ctx_p) |
345 | 668 | { |
346 | 668 | os_ptr op = osp; |
347 | 668 | gs_parsed_file_name_t pname; |
348 | 668 | int code = parse_real_file_name(op, &pname, imemory, "deletefile"); |
349 | 668 | bool is_temp = false; |
350 | | |
351 | 668 | if (code < 0) |
352 | 0 | return code; |
353 | 668 | if (pname.iodev == iodev_default(imemory)) { |
354 | 668 | if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len, |
355 | 668 | pname.iodev, "PermitFileControl")) < 0 && |
356 | 668 | !(is_temp = file_is_tempfile(i_ctx_p, op->value.bytes, r_size(op)))) { |
357 | 0 | return code; |
358 | 0 | } |
359 | 668 | } |
360 | | |
361 | 668 | code = (*pname.iodev->procs.delete_file)(pname.iodev, pname.fname); |
362 | | |
363 | 668 | if (code >= 0 && is_temp) |
364 | 0 | code = record_file_is_tempfile(i_ctx_p, (unsigned char *)pname.fname, strlen(pname.fname), false); |
365 | | |
366 | 668 | gs_free_file_name(&pname, "deletefile"); |
367 | 668 | if (code < 0) |
368 | 0 | return code; |
369 | 668 | pop(1); |
370 | 668 | return 0; |
371 | 668 | } |
372 | | |
373 | | /* <template> <proc> <scratch> filenameforall - */ |
374 | | static int file_continue(i_ctx_t *); |
375 | | static int file_cleanup(i_ctx_t *); |
376 | | static int |
377 | | zfilenameforall(i_ctx_t *i_ctx_p) |
378 | 9.56k | { |
379 | 9.56k | os_ptr op = osp; |
380 | 9.56k | file_enum *pfen; |
381 | 9.56k | gx_io_device *iodev = NULL; |
382 | 9.56k | gs_parsed_file_name_t pname; |
383 | 9.56k | int code = 0; |
384 | | |
385 | 9.56k | check_write_type(*op, t_string); |
386 | 9.56k | check_proc(op[-1]); |
387 | 9.56k | check_read_type(op[-2], t_string); |
388 | | /* Push a mark, the iodev, devicenamelen, the scratch string, the enumerator, */ |
389 | | /* and the procedure, and invoke the continuation. */ |
390 | 9.56k | check_estack(7); |
391 | | /* Get the iodevice */ |
392 | 9.56k | code = parse_file_name(op-2, &pname, i_ctx_p->LockFilePermissions, imemory); |
393 | 9.56k | if (code < 0) |
394 | 0 | return code; |
395 | 9.56k | iodev = (pname.iodev == NULL) ? iodev_default(imemory) : pname.iodev; |
396 | | |
397 | | /* Check for several conditions that just cause us to return success */ |
398 | 9.56k | if (pname.len == 0 || iodev->procs.enumerate_files == iodev_no_enumerate_files) { |
399 | 0 | pop(3); |
400 | 0 | return 0; /* no pattern, or device not found -- just return */ |
401 | 0 | } |
402 | 9.56k | pfen = iodev->procs.enumerate_files(imemory, iodev, (const char *)pname.fname, |
403 | 9.56k | pname.len); |
404 | 9.56k | if (pfen == 0) |
405 | 0 | return_error(gs_error_VMerror); |
406 | 9.56k | push_mark_estack(es_for, file_cleanup); |
407 | 9.56k | ++esp; |
408 | 9.56k | make_istruct(esp, 0, iodev); |
409 | 9.56k | ++esp; |
410 | 9.56k | make_int(esp, r_size(op-2) - pname.len); |
411 | 9.56k | *++esp = *op; |
412 | 9.56k | ++esp; |
413 | 9.56k | make_istruct(esp, 0, pfen); |
414 | 9.56k | *++esp = op[-1]; |
415 | 9.56k | ref_stack_pop(&o_stack, 3); |
416 | 9.56k | code = file_continue(i_ctx_p); |
417 | 9.56k | return (code == o_pop_estack ? o_push_estack : code); |
418 | 9.56k | } |
419 | | /* Continuation operator for enumerating files */ |
420 | | static int |
421 | | file_continue(i_ctx_t *i_ctx_p) |
422 | 10.9k | { |
423 | 10.9k | os_ptr op = osp; |
424 | 10.9k | es_ptr pscratch = esp - 2; |
425 | 10.9k | file_enum *pfen = r_ptr(esp - 1, file_enum); |
426 | 10.9k | int devlen = esp[-3].value.intval; |
427 | 10.9k | gx_io_device *iodev = r_ptr(esp - 4, gx_io_device); |
428 | 10.9k | uint len = r_size(pscratch); |
429 | 10.9k | uint code; |
430 | | |
431 | 10.9k | if (len < devlen) { |
432 | 0 | esp -= 5; /* pop proc, pfen, devlen, iodev , mark */ |
433 | 0 | return_error(gs_error_rangecheck); /* not even room for device len */ |
434 | 0 | } |
435 | | |
436 | 10.9k | do { |
437 | 10.9k | memcpy((char *)pscratch->value.bytes, iodev->dname, devlen); |
438 | 10.9k | code = iodev->procs.enumerate_next(imemory, pfen, (char *)pscratch->value.bytes + devlen, |
439 | 10.9k | len - devlen); |
440 | 10.9k | if (code == ~(uint) 0) { /* all done */ |
441 | 9.56k | esp -= 5; /* pop proc, pfen, devlen, iodev , mark */ |
442 | 9.56k | return o_pop_estack; |
443 | 9.56k | } else if (code > len) { /* overran string */ |
444 | 0 | return_error(gs_error_rangecheck); |
445 | 0 | } |
446 | 1.36k | else if (iodev != iodev_default(imemory) |
447 | 1.36k | || (check_file_permissions(i_ctx_p, (char *)pscratch->value.bytes, code + devlen, iodev, "PermitFileReading")) == 0) { |
448 | 1.36k | push(1); |
449 | 1.36k | ref_assign(op, pscratch); |
450 | 1.36k | r_set_size(op, code + devlen); |
451 | 1.36k | push_op_estack(file_continue); /* come again */ |
452 | 1.36k | *++esp = pscratch[2]; /* proc */ |
453 | 1.36k | return o_push_estack; |
454 | 1.36k | } |
455 | 10.9k | } while(1); |
456 | 10.9k | } |
457 | | /* Cleanup procedure for enumerating files */ |
458 | | static int |
459 | | file_cleanup(i_ctx_t *i_ctx_p) |
460 | 0 | { |
461 | 0 | gx_io_device *iodev = r_ptr(esp + 2, gx_io_device); |
462 | |
|
463 | 0 | iodev->procs.enumerate_close(imemory, r_ptr(esp + 5, file_enum)); |
464 | 0 | return 0; |
465 | 0 | } |
466 | | |
467 | | /* <string1> <string2> renamefile - */ |
468 | | static int |
469 | | zrenamefile(i_ctx_t *i_ctx_p) |
470 | 0 | { |
471 | 0 | int code; |
472 | 0 | os_ptr op = osp; |
473 | 0 | gs_parsed_file_name_t pname1, pname2; |
474 | |
|
475 | 0 | code = parse_real_file_name(op, &pname2, imemory, "renamefile(to)"); |
476 | 0 | if (code < 0) |
477 | 0 | return code; |
478 | | |
479 | 0 | pname1.fname = 0; |
480 | 0 | code = parse_real_file_name(op - 1, &pname1, imemory, "renamefile(from)"); |
481 | 0 | if (code >= 0) { |
482 | 0 | gx_io_device *iodev_dflt = iodev_default(imemory); |
483 | 0 | if (pname1.iodev != pname2.iodev ) { |
484 | 0 | if (pname1.iodev == iodev_dflt) |
485 | 0 | pname1.iodev = pname2.iodev; |
486 | 0 | if (pname2.iodev == iodev_dflt) |
487 | 0 | pname2.iodev = pname1.iodev; |
488 | 0 | } |
489 | 0 | if (pname1.iodev != pname2.iodev || |
490 | 0 | (pname1.iodev == iodev_dflt && |
491 | | /* |
492 | | * We require FileControl permissions on the source path |
493 | | * unless it is a temporary file. Also, we require FileControl |
494 | | * and FileWriting permissions to the destination file/path. |
495 | | */ |
496 | 0 | ((check_file_permissions(i_ctx_p, pname1.fname, pname1.len, |
497 | 0 | pname1.iodev, "PermitFileControl") < 0 && |
498 | 0 | !file_is_tempfile(i_ctx_p, op[-1].value.bytes, r_size(op - 1))) || |
499 | 0 | (check_file_permissions(i_ctx_p, pname2.fname, pname2.len, |
500 | 0 | pname2.iodev, "PermitFileControl") < 0 || |
501 | 0 | check_file_permissions(i_ctx_p, pname2.fname, pname2.len, |
502 | 0 | pname2.iodev, "PermitFileWriting") < 0 )))) { |
503 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
504 | 0 | } else { |
505 | 0 | code = (*pname1.iodev->procs.rename_file)(pname1.iodev, |
506 | 0 | pname1.fname, pname2.fname); |
507 | 0 | } |
508 | 0 | } |
509 | 0 | gs_free_file_name(&pname2, "renamefile(to)"); |
510 | 0 | gs_free_file_name(&pname1, "renamefile(from)"); |
511 | 0 | if (code < 0) |
512 | 0 | return code; |
513 | 0 | pop(2); |
514 | 0 | return 0; |
515 | 0 | } |
516 | | |
517 | | /* <file> status <open_bool> */ |
518 | | /* <string> status <pages> <bytes> <ref_time> <creation_time> true */ |
519 | | /* <string> status false */ |
520 | | static int |
521 | | zstatus(i_ctx_t *i_ctx_p) |
522 | 9.31k | { |
523 | 9.31k | os_ptr op = osp; |
524 | | |
525 | 9.31k | switch (r_type(op)) { |
526 | 704 | case t_file: |
527 | 704 | { |
528 | 704 | stream *s; |
529 | | |
530 | 704 | make_bool(op, (file_is_valid(s, op) ? 1 : 0)); |
531 | 704 | } |
532 | 704 | return 0; |
533 | 8.60k | case t_string: |
534 | 8.60k | { |
535 | 8.60k | gs_parsed_file_name_t pname; |
536 | 8.60k | struct stat fstat; |
537 | 8.60k | int code = parse_file_name(op, &pname, |
538 | 8.60k | i_ctx_p->LockFilePermissions, imemory); |
539 | 8.60k | if (code < 0) { |
540 | 0 | if (code == gs_error_undefinedfilename) { |
541 | 0 | make_bool(op, 0); |
542 | 0 | code = 0; |
543 | 0 | } |
544 | 0 | return code; |
545 | 0 | } |
546 | 8.60k | code = gs_terminate_file_name(&pname, imemory, "status"); |
547 | 8.60k | if (code < 0) |
548 | 0 | return code; |
549 | 8.60k | if ((code = check_file_permissions(i_ctx_p, pname.fname, pname.len, |
550 | 8.60k | pname.iodev, "PermitFileReading")) >= 0) { |
551 | 8.60k | code = (*pname.iodev->procs.file_status)(pname.iodev, |
552 | 8.60k | pname.fname, &fstat); |
553 | 8.60k | } |
554 | 8.60k | switch (code) { |
555 | 4.78k | case 0: |
556 | 4.78k | check_ostack(4); |
557 | | /* |
558 | | * Check to make sure that the file size fits into |
559 | | * a PostScript integer. (On some systems, long is |
560 | | * 32 bits, but file sizes are 64 bits.) |
561 | | */ |
562 | 4.78k | push(4); |
563 | 4.78k | make_int(op - 4, stat_blocks(&fstat)); |
564 | 4.78k | make_int(op - 3, fstat.st_size); |
565 | | /* |
566 | | * We can't check the value simply by using ==, |
567 | | * because signed/unsigned == does the wrong thing. |
568 | | * Instead, since integer assignment only keeps the |
569 | | * bottom bits, we convert the values to double |
570 | | * and then test for equality. This handles all |
571 | | * cases of signed/unsigned or width mismatch. |
572 | | */ |
573 | 4.78k | if ((double)op[-4].value.intval != |
574 | 4.78k | (double)stat_blocks(&fstat) || |
575 | 4.78k | (double)op[-3].value.intval != |
576 | 4.78k | (double)fstat.st_size |
577 | 4.78k | ) |
578 | 0 | return_error(gs_error_limitcheck); |
579 | 4.78k | make_int(op - 2, fstat.st_mtime); |
580 | 4.78k | make_int(op - 1, fstat.st_ctime); |
581 | 4.78k | make_bool(op, 1); |
582 | 4.78k | break; |
583 | 3.82k | case gs_error_undefinedfilename: |
584 | 3.82k | make_bool(op, 0); |
585 | 3.82k | code = 0; |
586 | 8.60k | } |
587 | 8.60k | gs_free_file_name(&pname, "status"); |
588 | 8.60k | return code; |
589 | 8.60k | } |
590 | 0 | default: |
591 | 0 | return_op_typecheck(op); |
592 | 9.31k | } |
593 | 9.31k | } |
594 | | |
595 | | /* ------ Non-standard extensions ------ */ |
596 | | |
597 | | /* <executable_file> .execfile - */ |
598 | | static int |
599 | | zexecfile(i_ctx_t *i_ctx_p) |
600 | 0 | { |
601 | 0 | os_ptr op = osp; |
602 | |
|
603 | 0 | check_type_access(*op, t_file, a_executable | a_read | a_execute); |
604 | 0 | check_estack(4); /* cleanup, file, finish, file */ |
605 | 0 | push_mark_estack(es_other, execfile_cleanup); |
606 | 0 | *++esp = *op; |
607 | 0 | push_op_estack(execfile_finish); |
608 | 0 | return zexec(i_ctx_p); |
609 | 0 | } |
610 | | /* Finish normally. */ |
611 | | static int |
612 | | execfile_finish(i_ctx_t *i_ctx_p) |
613 | 0 | { |
614 | 0 | check_ostack(1); |
615 | 0 | esp -= 2; |
616 | 0 | execfile_cleanup(i_ctx_p); |
617 | 0 | return o_pop_estack; |
618 | 0 | } |
619 | | /* Clean up by closing the file. */ |
620 | | static int |
621 | | execfile_cleanup(i_ctx_t *i_ctx_p) |
622 | 0 | { |
623 | 0 | check_ostack(1); |
624 | 0 | *++osp = esp[2]; |
625 | 0 | return zclosefile(i_ctx_p); |
626 | 0 | } |
627 | | |
628 | | /* - .filenamelistseparator <string> */ |
629 | | static int |
630 | | zfilenamelistseparator(i_ctx_t *i_ctx_p) |
631 | 0 | { |
632 | 0 | os_ptr op = osp; |
633 | |
|
634 | 0 | push(1); |
635 | 0 | make_const_string(op, avm_foreign | a_readonly, 1, |
636 | 0 | (const byte *)&gp_file_name_list_separator); |
637 | 0 | return 0; |
638 | 0 | } |
639 | | |
640 | | /* <name> .filenamesplit <dir> <base> <extension> */ |
641 | | static int |
642 | | zfilenamesplit(i_ctx_t *i_ctx_p) |
643 | 0 | { |
644 | 0 | os_ptr op = osp; |
645 | |
|
646 | 0 | check_read_type(*op, t_string); |
647 | | /****** NOT IMPLEMENTED YET ******/ |
648 | 0 | return_error(gs_error_undefined); |
649 | 0 | } |
650 | | |
651 | | /* <string> .libfile <file> true */ |
652 | | /* <string> .libfile <string> false */ |
653 | | int /* exported for zsysvm.c */ |
654 | | zlibfile(i_ctx_t *i_ctx_p) |
655 | 15.5k | { |
656 | 15.5k | os_ptr op = osp; |
657 | 15.5k | int code; |
658 | 15.5k | byte cname[DEFAULT_BUFFER_SIZE]; |
659 | 15.5k | uint clen; |
660 | 15.5k | gs_parsed_file_name_t pname; |
661 | 15.5k | stream *s; |
662 | 15.5k | gx_io_device *iodev_dflt; |
663 | | |
664 | 15.5k | check_ostack(2); |
665 | 15.5k | code = parse_file_name(op, &pname, i_ctx_p->LockFilePermissions, imemory); |
666 | 15.5k | if (code < 0) |
667 | 0 | return code; |
668 | 15.5k | iodev_dflt = iodev_default(imemory); |
669 | 15.5k | if (pname.iodev == NULL) |
670 | 11.7k | pname.iodev = iodev_dflt; |
671 | 15.5k | if (pname.iodev != iodev_dflt) { /* Non-OS devices don't have search paths (yet). */ |
672 | 3.82k | code = zopen_file(i_ctx_p, &pname, "r", &s, imemory); |
673 | 3.82k | if (s == NULL) code = gs_note_error(gs_error_undefinedfilename); |
674 | 3.82k | if (code >= 0) { |
675 | 683 | code = ssetfilename(s, op->value.const_bytes, r_size(op)); |
676 | 683 | if (code < 0) { |
677 | 0 | sclose(s); |
678 | 0 | return_error(gs_error_VMerror); |
679 | 0 | } |
680 | 683 | } |
681 | 3.82k | if (code < 0) { |
682 | 3.14k | push(1); |
683 | 3.14k | make_false(op); |
684 | 3.14k | return 0; |
685 | 3.14k | } |
686 | 683 | make_stream_file(op, s, "r"); |
687 | 11.7k | } else { |
688 | 11.7k | ref fref; |
689 | | |
690 | 11.7k | code = lib_file_open(i_ctx_p->lib_path, imemory, i_ctx_p, pname.fname, pname.len, |
691 | 11.7k | (char *)cname, sizeof(cname), &clen, &fref); |
692 | 11.7k | if (code >= 0) { |
693 | 2.73k | s = fptr(&fref); |
694 | 2.73k | code = ssetfilename(s, cname, clen); |
695 | 2.73k | if (code < 0) { |
696 | 0 | sclose(s); |
697 | 0 | return_error(gs_error_VMerror); |
698 | 0 | } |
699 | 2.73k | } |
700 | 11.7k | if (code < 0) { |
701 | 9.01k | if (code == gs_error_VMerror || code == gs_error_invalidfileaccess) |
702 | 0 | return code; |
703 | 9.01k | push(1); |
704 | 9.01k | make_false(op); |
705 | 9.01k | return 0; |
706 | 9.01k | } |
707 | 2.73k | ref_assign(op, &fref); |
708 | 2.73k | } |
709 | 15.5k | push(1); |
710 | 3.41k | make_true(op); |
711 | 3.41k | return 0; |
712 | 3.41k | } |
713 | | |
714 | | /* A "simple" prefix is defined as a (possibly empty) string of |
715 | | alphanumeric, underscore, and hyphen characters. */ |
716 | | static bool |
717 | | prefix_is_simple(const char *pstr) |
718 | 669 | { |
719 | 669 | int i; |
720 | 669 | char c; |
721 | | |
722 | 2.67k | for (i = 0; (c = pstr[i]) != 0; i++) { |
723 | 2.00k | if (!(c == '-' || c == '_' || (c >= '0' && c <= '9') || |
724 | 2.00k | (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) |
725 | 0 | return false; |
726 | 2.00k | } |
727 | 669 | return true; |
728 | 669 | } |
729 | | |
730 | | /* <prefix|null> <access_string> .tempfile <name_string> <file> */ |
731 | | static int |
732 | | ztempfile(i_ctx_t *i_ctx_p) |
733 | 669 | { |
734 | 669 | os_ptr op = osp; |
735 | 669 | const char *pstr; |
736 | 669 | char fmode[4]; |
737 | 669 | char fmode_temp[4]; |
738 | 669 | int code = parse_file_access_string(op, fmode_temp); |
739 | 669 | char *prefix = NULL; |
740 | 669 | char *fname= NULL; |
741 | 669 | uint fnlen; |
742 | 669 | gp_file *sfile; |
743 | 669 | stream *s; |
744 | 669 | byte *buf, *sbody; |
745 | | |
746 | 669 | if (code < 0) |
747 | 0 | return code; |
748 | 669 | prefix = (char *)gs_alloc_bytes(imemory, gp_file_name_sizeof, "ztempfile(prefix)"); |
749 | 669 | fname = (char *)gs_alloc_bytes(imemory, gp_file_name_sizeof, "ztempfile(fname)"); |
750 | 669 | if (!prefix || !fname) { |
751 | 0 | code = gs_note_error(gs_error_VMerror); |
752 | 0 | goto done; |
753 | 0 | } |
754 | | |
755 | 669 | snprintf(fmode, sizeof(fmode), "%s%s", fmode_temp, gp_fmode_binary_suffix); |
756 | 669 | if (r_has_type(op - 1, t_null)) |
757 | 669 | pstr = gp_scratch_file_name_prefix; |
758 | 0 | else { |
759 | 0 | uint psize; |
760 | |
|
761 | 0 | check_read_type(op[-1], t_string); |
762 | 0 | psize = r_size(op - 1); |
763 | 0 | if (psize >= gp_file_name_sizeof) { |
764 | 0 | code = gs_note_error(gs_error_rangecheck); |
765 | 0 | goto done; |
766 | 0 | } |
767 | 0 | memcpy(prefix, op[-1].value.const_bytes, psize); |
768 | 0 | prefix[psize] = 0; |
769 | 0 | pstr = prefix; |
770 | 0 | } |
771 | | |
772 | 669 | if (gp_file_name_is_absolute(pstr, strlen(pstr))) { |
773 | 0 | int plen = strlen(pstr); |
774 | 0 | const char *sep = gp_file_name_separator(); |
775 | | #ifdef DEBUG |
776 | | int seplen = strlen(sep); |
777 | | if (seplen != 1) |
778 | | return_error(gs_error_Fatal); |
779 | | #endif |
780 | | /* strip off the file name prefix, leave just the directory name |
781 | | * so we can check if we are allowed to write to it |
782 | | */ |
783 | 0 | for ( ; plen >=0; plen--) { |
784 | 0 | if (pstr[plen] == sep[0]) |
785 | 0 | break; |
786 | 0 | } |
787 | 0 | memcpy(fname, pstr, plen); |
788 | 0 | fname[plen] = '\0'; |
789 | 0 | if (check_file_permissions(i_ctx_p, fname, strlen(fname), |
790 | 0 | NULL, "PermitFileWriting") < 0) { |
791 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
792 | 0 | goto done; |
793 | 0 | } |
794 | 669 | } else if (!prefix_is_simple(pstr)) { |
795 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
796 | 0 | goto done; |
797 | 0 | } |
798 | | |
799 | 669 | s = file_alloc_stream(imemory, "ztempfile(stream)"); |
800 | 669 | if (s == 0) { |
801 | 0 | code = gs_note_error(gs_error_VMerror); |
802 | 0 | goto done; |
803 | 0 | } |
804 | 669 | buf = gs_alloc_bytes(imemory, file_default_buffer_size, |
805 | 669 | "ztempfile(buffer)"); |
806 | 669 | if (buf == 0) { |
807 | 0 | code = gs_note_error(gs_error_VMerror); |
808 | 0 | goto done; |
809 | 0 | } |
810 | 669 | sfile = gp_open_scratch_file(imemory, pstr, fname, fmode); |
811 | 669 | if (sfile == 0) { |
812 | 0 | gs_free_object(imemory, buf, "ztempfile(buffer)"); |
813 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
814 | 0 | goto done; |
815 | 0 | } |
816 | 669 | fnlen = strlen(fname); |
817 | 669 | sbody = ialloc_string(fnlen, ".tempfile(fname)"); |
818 | 669 | if (sbody == 0) { |
819 | 0 | gs_free_object(imemory, buf, "ztempfile(buffer)"); |
820 | 0 | code = gs_note_error(gs_error_VMerror); |
821 | 0 | goto done; |
822 | 0 | } |
823 | 669 | memcpy(sbody, fname, fnlen); |
824 | 669 | file_init_stream(s, sfile, fmode, buf, file_default_buffer_size); |
825 | 669 | code = ssetfilename(s, (const unsigned char*) fname, fnlen); |
826 | 669 | if (code < 0) { |
827 | 0 | gx_io_device *iodev_dflt = iodev_default(imemory); |
828 | 0 | sclose(s); |
829 | 0 | iodev_dflt->procs.delete_file(iodev_dflt, fname); |
830 | 0 | ifree_string(sbody, fnlen, ".tempfile(fname)"); |
831 | 0 | code = gs_note_error(gs_error_VMerror); |
832 | 0 | goto done; |
833 | 0 | } |
834 | 669 | make_string(op - 1, a_readonly | icurrent_space, fnlen, sbody); |
835 | 669 | make_stream_file(op, s, fmode); |
836 | 669 | code = record_file_is_tempfile(i_ctx_p, (unsigned char *)fname, fnlen, true); |
837 | | |
838 | 669 | done: |
839 | 669 | if (prefix) |
840 | 669 | gs_free_object(imemory, prefix, "ztempfile(prefix)"); |
841 | 669 | if (fname) |
842 | 669 | gs_free_object(imemory, fname, "ztempfile(fname)"); |
843 | 669 | return code; |
844 | 669 | } |
845 | | |
846 | | /* Return the filename used to open a file object |
847 | | * this is currently only used by the PDF interpreter to |
848 | | * get a filename corresponding to the PDF file being |
849 | | * executed. Since we always execute PDF files from disk |
850 | | * this will always be OK. |
851 | | */ |
852 | | static int zgetfilename(i_ctx_t *i_ctx_p) |
853 | 0 | { |
854 | 0 | os_ptr op = osp; |
855 | 0 | uint fnlen; |
856 | 0 | gs_const_string pfname; |
857 | 0 | stream *s; |
858 | 0 | byte *sbody; |
859 | 0 | int code; |
860 | |
|
861 | 0 | check_ostack(1); |
862 | 0 | check_read_type(*op, t_file); |
863 | | |
864 | 0 | s = (op)->value.pfile; |
865 | |
|
866 | 0 | code = sfilename(s, &pfname); |
867 | 0 | if (code < 0) { |
868 | 0 | pfname.size = 0; |
869 | 0 | } |
870 | |
|
871 | 0 | fnlen = pfname.size; |
872 | 0 | sbody = ialloc_string(fnlen, ".getfilename"); |
873 | 0 | if (sbody == 0) { |
874 | 0 | code = gs_note_error(gs_error_VMerror); |
875 | 0 | return code; |
876 | 0 | } |
877 | 0 | memcpy(sbody, pfname.data, fnlen); |
878 | 0 | make_string(op, a_readonly | icurrent_space, fnlen, sbody); |
879 | |
|
880 | 0 | return 0; |
881 | 0 | } |
882 | | |
883 | | static int zaddcontrolpath(i_ctx_t *i_ctx_p) |
884 | 15.0k | { |
885 | 15.0k | int code; |
886 | 15.0k | os_ptr op = osp; |
887 | 15.0k | ref nsref; |
888 | 15.0k | unsigned int n = -1; |
889 | | |
890 | 15.0k | check_ostack(2); |
891 | 15.0k | check_read_type(*op, t_string); |
892 | 15.0k | check_type(op[-1], t_name); |
893 | | |
894 | 15.0k | name_string_ref(imemory, op-1, &nsref); |
895 | 15.0k | if (r_size(&nsref) == 17 && |
896 | 15.0k | strncmp((const char *)nsref.value.const_bytes, |
897 | 15.0k | "PermitFileReading", 17) == 0) { |
898 | 12.2k | n = gs_permit_file_reading; |
899 | 12.2k | } else if (r_size(&nsref) == 17 && |
900 | 2.73k | strncmp((const char *)nsref.value.const_bytes, |
901 | 2.73k | "PermitFileWriting", 17) == 0) { |
902 | 1.36k | n = gs_permit_file_writing; |
903 | 1.36k | } else if (r_size(&nsref) == 17 && |
904 | 1.36k | strncmp((const char *)nsref.value.const_bytes, |
905 | 1.36k | "PermitFileControl", 17) == 0) { |
906 | 1.36k | n = gs_permit_file_control; |
907 | 1.36k | } |
908 | | |
909 | 15.0k | if (n == -1) |
910 | 0 | code = gs_note_error(gs_error_rangecheck); |
911 | 15.0k | else if (gs_is_path_control_active(imemory)) |
912 | 0 | code = gs_note_error(gs_error_Fatal); |
913 | 15.0k | else |
914 | 15.0k | code = gs_add_control_path_len(imemory, n, |
915 | 15.0k | (const char *)op[0].value.const_bytes, |
916 | 15.0k | (size_t)r_size(&op[0])); |
917 | 15.0k | pop(2); |
918 | 15.0k | return code; |
919 | 15.0k | } |
920 | | |
921 | | static int zactivatepathcontrol(i_ctx_t *i_ctx_p) |
922 | 683 | { |
923 | 683 | gs_activate_path_control(imemory, 1); |
924 | 683 | return 0; |
925 | 683 | } |
926 | | static int zcurrentpathcontrolstate(i_ctx_t *i_ctx_p) |
927 | 683 | { |
928 | 683 | os_ptr op = osp; |
929 | 683 | push(1); |
930 | 683 | if (gs_is_path_control_active(imemory)) { |
931 | 0 | make_true(op); |
932 | 0 | } |
933 | 683 | else { |
934 | 683 | make_false(op); |
935 | 683 | } |
936 | 683 | return 0; |
937 | 683 | } |
938 | | |
939 | | /* ------ Initialization procedure ------ */ |
940 | | |
941 | | const op_def zfile_op_defs[] = |
942 | | { |
943 | | {"1deletefile", zdeletefile}, |
944 | | {"1.execfile", zexecfile}, |
945 | | {"2file", zfile}, |
946 | | {"3filenameforall", zfilenameforall}, |
947 | | {"0.filenamelistseparator", zfilenamelistseparator}, |
948 | | {"1.filenamesplit", zfilenamesplit}, |
949 | | {"1.libfile", zlibfile}, |
950 | | {"2renamefile", zrenamefile}, |
951 | | {"1status", zstatus}, |
952 | | {"2.tempfile", ztempfile}, |
953 | | /* Internal operators */ |
954 | | {"0%file_continue", file_continue}, |
955 | | {"0%execfile_finish", execfile_finish}, |
956 | | {"1.getfilename", zgetfilename}, |
957 | | /* Control path operators */ |
958 | | {"2.addcontrolpath", zaddcontrolpath}, |
959 | | {"0.activatepathcontrol", zactivatepathcontrol}, |
960 | | {"0.currentpathcontrolstate", zcurrentpathcontrolstate}, |
961 | | op_def_end(0) |
962 | | }; |
963 | | |
964 | | /* ------ File name parsing ------ */ |
965 | | |
966 | | /* Parse a file name into device and individual name. */ |
967 | | /* See gsfname.c for details. */ |
968 | | static int |
969 | | parse_file_name(const ref * op, gs_parsed_file_name_t * pfn, bool safemode, |
970 | | gs_memory_t *memory) |
971 | 127k | { |
972 | 127k | int code; |
973 | | |
974 | 127k | check_read_type(*op, t_string); |
975 | 127k | code = gs_parse_file_name(pfn, (const char *)op->value.const_bytes, |
976 | 127k | r_size(op), memory); |
977 | 127k | if (code < 0) |
978 | 0 | return code; |
979 | | /* |
980 | | * Check here for the %pipe device which is illegal when |
981 | | * LockFilePermissions is true. In the future we might want to allow |
982 | | * the %pipe device to be included on the PermitFile... paths, but |
983 | | * for now it is simply disallowed. |
984 | | */ |
985 | 127k | if (pfn->iodev && safemode && strcmp(pfn->iodev->dname, "%pipe%") == 0) |
986 | 0 | return gs_error_invalidfileaccess; |
987 | 127k | return code; |
988 | 127k | } |
989 | | |
990 | | /* Parse a real (non-device) file name and convert to a C string. */ |
991 | | /* See gsfname.c for details. */ |
992 | | static int |
993 | | parse_real_file_name(const ref *op, gs_parsed_file_name_t *pfn, |
994 | | gs_memory_t *mem, client_name_t cname) |
995 | 668 | { |
996 | 668 | check_read_type(*op, t_string); |
997 | 668 | return gs_parse_real_file_name(pfn, (const char *)op->value.const_bytes, |
998 | 668 | r_size(op), mem, cname); |
999 | 668 | } |
1000 | | |
1001 | | /* Parse the access string for opening a file. */ |
1002 | | /* [4] is for r/w, +, b, \0. */ |
1003 | | static int |
1004 | | parse_file_access_string(const ref *op, char file_access[4]) |
1005 | 94.4k | { |
1006 | 94.4k | const byte *astr; |
1007 | | |
1008 | 94.4k | check_read_type(*op, t_string); |
1009 | 94.4k | astr = op->value.const_bytes; |
1010 | 94.4k | switch (r_size(op)) { |
1011 | 669 | case 2: |
1012 | 669 | if (astr[1] != '+') |
1013 | 0 | return_error(gs_error_invalidfileaccess); |
1014 | 669 | file_access[1] = '+'; |
1015 | 669 | file_access[2] = 0; |
1016 | 669 | break; |
1017 | 93.7k | case 1: |
1018 | 93.7k | file_access[1] = 0; |
1019 | 93.7k | break; |
1020 | 0 | default: |
1021 | 0 | return_error(gs_error_invalidfileaccess); |
1022 | 94.4k | } |
1023 | 94.4k | switch (astr[0]) { |
1024 | 16.7k | case 'r': |
1025 | 94.4k | case 'w': |
1026 | 94.4k | case 'a': |
1027 | 94.4k | break; |
1028 | 0 | default: |
1029 | 0 | return_error(gs_error_invalidfileaccess); |
1030 | 94.4k | } |
1031 | 94.4k | file_access[0] = astr[0]; |
1032 | 94.4k | return 0; |
1033 | 94.4k | } |
1034 | | |
1035 | | /* ------ Stream opening ------ */ |
1036 | | |
1037 | | /* |
1038 | | * Open a file specified by a parsed file name (which may be only a |
1039 | | * device). |
1040 | | */ |
1041 | | int |
1042 | | zopen_file(i_ctx_t *i_ctx_p, const gs_parsed_file_name_t *pfn, |
1043 | | const char *file_access, stream **ps, gs_memory_t *mem) |
1044 | 18.5k | { |
1045 | 18.5k | gx_io_device *const iodev = pfn->iodev; |
1046 | 18.5k | int code = 0; |
1047 | | |
1048 | 18.5k | if (pfn->fname == NULL) { /* just a device */ |
1049 | 0 | iodev->state = i_ctx_p; |
1050 | 0 | code = iodev->procs.open_device(iodev, file_access, ps, mem); |
1051 | 0 | iodev->state = NULL; |
1052 | 0 | return code; |
1053 | 0 | } |
1054 | 18.5k | else { /* file */ |
1055 | 18.5k | iodev_proc_open_file((*open_file)) = iodev->procs.open_file; |
1056 | | |
1057 | 18.5k | if (open_file == 0) |
1058 | 7.51k | open_file = iodev_os_open_file; |
1059 | | /* Check OS files to make sure we allow the type of access */ |
1060 | 18.5k | if (open_file == iodev_os_open_file) { |
1061 | 7.51k | code = check_file_permissions(i_ctx_p, pfn->fname, pfn->len, pfn->iodev, |
1062 | 7.51k | file_access[0] == 'r' ? "PermitFileReading" : "PermitFileWriting"); |
1063 | | |
1064 | 7.51k | if (code < 0 && !file_is_tempfile(i_ctx_p, |
1065 | 0 | (const uchar *)pfn->fname, pfn->len)) |
1066 | 0 | return code; |
1067 | 7.51k | } |
1068 | 18.5k | return open_file(iodev, pfn->fname, pfn->len, file_access, ps, mem); |
1069 | 18.5k | } |
1070 | 18.5k | } |
1071 | | |
1072 | | /* |
1073 | | * Define the file_open procedure for the %os% IODevice (also used, as the |
1074 | | * default, for %pipe% and possibly others). |
1075 | | */ |
1076 | | static int |
1077 | | iodev_os_open_file(gx_io_device * iodev, const char *fname, uint len, |
1078 | | const char *file_access, stream ** ps, gs_memory_t * mem) |
1079 | 106k | { |
1080 | 106k | return file_open_stream(fname, len, file_access, |
1081 | 106k | file_default_buffer_size, ps, |
1082 | 106k | iodev, iodev->procs.gp_fopen, mem); |
1083 | 106k | } |
1084 | | |
1085 | | /* Make a t_file reference to a stream. */ |
1086 | | void |
1087 | | make_stream_file(ref * pfile, stream * s, const char *access) |
1088 | 94.0k | { |
1089 | 94.0k | uint attrs = |
1090 | 94.0k | (access[1] == '+' ? a_write + a_read + a_execute : 0) | |
1091 | 94.0k | imemory_space((gs_ref_memory_t *) s->memory); |
1092 | | |
1093 | 94.0k | if (access[0] == 'r') { |
1094 | 15.7k | make_file(pfile, attrs | (a_read | a_execute), s->read_id, s); |
1095 | 15.7k | s->write_id = 0; |
1096 | 78.3k | } else { |
1097 | 78.3k | make_file(pfile, attrs | a_write, s->write_id, s); |
1098 | 78.3k | s->read_id = 0; |
1099 | 78.3k | } |
1100 | 94.0k | } |
1101 | | |
1102 | | /* return zero for success, -ve for error, +1 for continue */ |
1103 | | static int |
1104 | | lib_file_open_search_with_no_combine(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p, |
1105 | | const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile, |
1106 | | gx_io_device *iodev, bool starting_arg_file, char *fmode) |
1107 | 0 | { |
1108 | 0 | stream *s; |
1109 | 0 | uint blen1 = blen; |
1110 | 0 | struct stat fstat; |
1111 | 0 | int code = 1; |
1112 | |
|
1113 | 0 | if (gp_file_name_reduce(fname, flen, buffer, &blen1) != gp_combine_success) |
1114 | 0 | goto skip; |
1115 | | |
1116 | 0 | if (starting_arg_file || check_file_permissions(i_ctx_p, buffer, blen1, iodev, "PermitFileReading") >= 0) { |
1117 | 0 | if (iodev_os_open_file(iodev, (const char *)buffer, blen1, |
1118 | 0 | (const char *)fmode, &s, (gs_memory_t *)mem) == 0) { |
1119 | 0 | *pclen = blen1; |
1120 | 0 | make_stream_file(pfile, s, "r"); |
1121 | 0 | code = 0; |
1122 | 0 | } |
1123 | 0 | } |
1124 | 0 | else { |
1125 | | /* If we are not allowed to open the file by check_file_permissions_aux() |
1126 | | * and if the file exists, throw an error....... |
1127 | | * Otherwise, keep searching. |
1128 | | */ |
1129 | 0 | if ((*iodev->procs.file_status)(iodev, buffer, &fstat) >= 0) { |
1130 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1131 | 0 | } |
1132 | 0 | } |
1133 | |
|
1134 | 0 | skip: |
1135 | 0 | return code; |
1136 | 0 | } |
1137 | | |
1138 | | /* return zero for success, -ve for error, +1 for continue */ |
1139 | | static int |
1140 | | lib_file_open_search_with_combine(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p, |
1141 | | const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile, |
1142 | | gx_io_device *iodev, bool starting_arg_file, char *fmode) |
1143 | 12.4k | { |
1144 | 12.4k | stream *s; |
1145 | 12.4k | const gs_file_path *pfpath = lib_path; |
1146 | 12.4k | uint pi; |
1147 | 12.4k | int code = 1; |
1148 | | |
1149 | 133k | for (pi = 0; pi < r_size(&pfpath->list) && code == 1; ++pi) { |
1150 | 120k | const ref *prdir = pfpath->list.value.refs + pi; |
1151 | 120k | const char *pstr = (const char *)prdir->value.const_bytes; |
1152 | 120k | uint plen = r_size(prdir), blen1 = blen; |
1153 | 120k | gs_parsed_file_name_t pname; |
1154 | 120k | gp_file_name_combine_result r; |
1155 | | |
1156 | | /* We need to concatenate and parse the file name here |
1157 | | * if this path has a %device% prefix. */ |
1158 | 120k | if (pstr[0] == '%') { |
1159 | | /* We concatenate directly since gp_file_name_combine_* |
1160 | | * rules are not correct for other devices such as %rom% */ |
1161 | 21.4k | code = gs_parse_file_name(&pname, pstr, plen, mem); |
1162 | 21.4k | if (code < 0) { |
1163 | 0 | code = 1; |
1164 | 0 | continue; |
1165 | 0 | } |
1166 | 21.4k | if (blen < max(pname.len, plen) + flen) |
1167 | 0 | return_error(gs_error_limitcheck); |
1168 | 21.4k | memcpy(buffer, pname.fname, pname.len); |
1169 | 21.4k | memcpy(buffer+pname.len, fname, flen); |
1170 | 21.4k | code = pname.iodev->procs.open_file(pname.iodev, buffer, pname.len + flen, fmode, |
1171 | 21.4k | &s, (gs_memory_t *)mem); |
1172 | 21.4k | if (code < 0) { |
1173 | 18.0k | code = 1; |
1174 | 18.0k | continue; |
1175 | 18.0k | } |
1176 | 3.41k | make_stream_file(pfile, s, "r"); |
1177 | | /* fill in the buffer with the device concatenated */ |
1178 | 3.41k | memcpy(buffer, pstr, plen); |
1179 | 3.41k | memcpy(buffer+plen, fname, flen); |
1180 | 3.41k | *pclen = plen + flen; |
1181 | 3.41k | code = 0; |
1182 | 99.1k | } else { |
1183 | 99.1k | r = gp_file_name_combine(pstr, plen, |
1184 | 99.1k | fname, flen, false, buffer, &blen1); |
1185 | 99.1k | if (r != gp_combine_success) |
1186 | 0 | continue; |
1187 | 99.1k | if (starting_arg_file || check_file_permissions(i_ctx_p, buffer, |
1188 | 99.1k | blen1, iodev, "PermitFileReading") >= 0) { |
1189 | | |
1190 | 99.1k | if (iodev_os_open_file(iodev, (const char *)buffer, blen1, |
1191 | 99.1k | (const char *)fmode, &s, (gs_memory_t *)mem) == 0) { |
1192 | 0 | *pclen = blen1; |
1193 | 0 | make_stream_file(pfile, s, "r"); |
1194 | 0 | code = 0; |
1195 | 0 | } |
1196 | 99.1k | } |
1197 | 0 | else { |
1198 | 0 | struct stat fstat; |
1199 | | /* If we are not allowed to open the file by check_file_permissions_aux() |
1200 | | * and if the file exists, throw an error....... |
1201 | | * Otherwise, keep searching. |
1202 | | */ |
1203 | 0 | if ((*iodev->procs.file_status)(iodev, (const char *)buffer, &fstat) >= 0) { |
1204 | 0 | code = gs_note_error(gs_error_invalidfileaccess); |
1205 | 0 | } |
1206 | 0 | } |
1207 | 99.1k | } |
1208 | 120k | } |
1209 | 12.4k | return code; |
1210 | 12.4k | } |
1211 | | |
1212 | | /* Return a file object of of the file searched for using the search paths. */ |
1213 | | /* The fname cannot contain a device part (%...%) but the lib paths might. */ |
1214 | | /* The startup code calls this to open the initialization file gs_init.ps. */ |
1215 | | /* The startup code also calls this to open @-files. */ |
1216 | | int |
1217 | | lib_file_open(gs_file_path_ptr lib_path, const gs_memory_t *mem, i_ctx_t *i_ctx_p, |
1218 | | const char *fname, uint flen, char *buffer, int blen, uint *pclen, ref *pfile) |
1219 | 12.4k | { /* i_ctx_p is NULL running arg (@) files. |
1220 | | * lib_path and mem are never NULL |
1221 | | */ |
1222 | 12.4k | bool starting_arg_file = (i_ctx_p == NULL) ? true : i_ctx_p->starting_arg_file; |
1223 | 12.4k | bool search_with_no_combine = false; |
1224 | 12.4k | bool search_with_combine = false; |
1225 | 12.4k | char fmode[2] = { 'r', 0}; |
1226 | 12.4k | gx_io_device *iodev = iodev_default(mem); |
1227 | 12.4k | gs_main_instance *minst = get_minst_from_memory(mem); |
1228 | 12.4k | int code; |
1229 | | |
1230 | 12.4k | if (i_ctx_p && starting_arg_file) |
1231 | 0 | i_ctx_p->starting_arg_file = false; |
1232 | | |
1233 | | /* when starting arg files (@ files) iodev_default is not yet set */ |
1234 | 12.4k | if (iodev == 0) |
1235 | 0 | iodev = (gx_io_device *)gx_io_device_table[0]; |
1236 | | |
1237 | 12.4k | if (gp_file_name_is_absolute(fname, flen)) { |
1238 | 0 | search_with_no_combine = true; |
1239 | 0 | search_with_combine = false; |
1240 | 12.4k | } else { |
1241 | 12.4k | search_with_no_combine = starting_arg_file; |
1242 | 12.4k | search_with_combine = true; |
1243 | 12.4k | } |
1244 | 12.4k | if (minst->search_here_first) { |
1245 | 0 | if (search_with_no_combine) { |
1246 | 0 | code = lib_file_open_search_with_no_combine(lib_path, mem, i_ctx_p, |
1247 | 0 | fname, flen, buffer, blen, pclen, pfile, |
1248 | 0 | iodev, starting_arg_file, fmode); |
1249 | 0 | if (code <= 0) /* +ve means continue continue */ |
1250 | 0 | return code; |
1251 | 0 | } |
1252 | 0 | if (search_with_combine) { |
1253 | 0 | code = lib_file_open_search_with_combine(lib_path, mem, i_ctx_p, |
1254 | 0 | fname, flen, buffer, blen, pclen, pfile, |
1255 | 0 | iodev, starting_arg_file, fmode); |
1256 | 0 | if (code <= 0) /* +ve means continue searching */ |
1257 | 0 | return code; |
1258 | 0 | } |
1259 | 12.4k | } else { |
1260 | 12.4k | if (search_with_combine) { |
1261 | 12.4k | code = lib_file_open_search_with_combine(lib_path, mem, i_ctx_p, |
1262 | 12.4k | fname, flen, buffer, blen, pclen, pfile, |
1263 | 12.4k | iodev, starting_arg_file, fmode); |
1264 | 12.4k | if (code <= 0) /* +ve means continue searching */ |
1265 | 3.41k | return code; |
1266 | 12.4k | } |
1267 | 9.01k | if (search_with_no_combine) { |
1268 | 0 | code = lib_file_open_search_with_no_combine(lib_path, mem, i_ctx_p, |
1269 | 0 | fname, flen, buffer, blen, pclen, pfile, |
1270 | 0 | iodev, starting_arg_file, fmode); |
1271 | 0 | if (code <= 0) /* +ve means continue searching */ |
1272 | 0 | return code; |
1273 | 0 | } |
1274 | 9.01k | } |
1275 | 12.4k | return_error(gs_error_undefinedfilename); |
1276 | 12.4k | } |
1277 | | |
1278 | | /* The startup code calls this to open @-files. */ |
1279 | | gp_file * |
1280 | | lib_fopen(const gs_file_path_ptr pfpath, const gs_memory_t *mem, const char *fname) |
1281 | 0 | { |
1282 | | /* We need a buffer to hold the expanded file name. */ |
1283 | 0 | char filename_found[DEFAULT_BUFFER_SIZE]; |
1284 | 0 | gp_file *file = NULL; |
1285 | 0 | uint fnamelen; |
1286 | 0 | ref obj; |
1287 | 0 | int code; |
1288 | 0 | stream *s; |
1289 | | |
1290 | | /* open the usual 'stream', then if successful, return the file */ |
1291 | 0 | code = lib_file_open(pfpath, mem, NULL, fname, strlen(fname), |
1292 | 0 | filename_found, sizeof(filename_found), &fnamelen, &obj); |
1293 | |
|
1294 | 0 | if (code < 0) |
1295 | 0 | return NULL; |
1296 | | |
1297 | | /* This all seems a bit grotty. The above code has generated us a stream |
1298 | | * that wraps a file. We actually want the file. So we reach in, and steal |
1299 | | * the file pointer. Nasty. */ |
1300 | 0 | s = ((stream *)(obj.value.pfile)); |
1301 | 0 | file = s->file; |
1302 | | /* Historically we've then just abandoned the stream, resulting in blocks |
1303 | | * leaking. Let's free the leaked blocks (in rather hacky style). First, |
1304 | | * we clear the file reference, and then drop the stream. */ |
1305 | 0 | s->file = NULL; |
1306 | 0 | sclose(s); |
1307 | | /* Then free the stream block itself. */ |
1308 | 0 | gs_free_object(s->memory, s, "lib_fopen"); |
1309 | 0 | return file; |
1310 | 0 | } |
1311 | | |
1312 | | /* Open a file stream that reads a string. */ |
1313 | | /* (This is currently used only by the ccinit feature.) */ |
1314 | | /* The string must be allocated in non-garbage-collectable (foreign) space. */ |
1315 | | int |
1316 | | file_read_string(const byte *str, uint len, ref *pfile, gs_ref_memory_t *imem) |
1317 | 0 | { |
1318 | 0 | stream *s = file_alloc_stream((gs_memory_t *)imem, "file_read_string"); |
1319 | |
|
1320 | 0 | if (s == 0) |
1321 | 0 | return_error(gs_error_VMerror); |
1322 | 0 | sread_string(s, str, len); |
1323 | 0 | s->foreign = 1; |
1324 | 0 | s->write_id = 0; |
1325 | 0 | make_file(pfile, a_readonly | imemory_space(imem), s->read_id, s); |
1326 | 0 | s->save_close = s->procs.close; |
1327 | 0 | s->procs.close = file_close_disable; |
1328 | 0 | return 0; |
1329 | 0 | } |
1330 | | |
1331 | | /* Report an error by storing it in the stream's error_string. */ |
1332 | | int |
1333 | | filter_report_error(stream_state * st, const char *str) |
1334 | 0 | { |
1335 | 0 | if_debug1m('s', st->memory, "[s]stream error: %s\n", str); |
1336 | 0 | strncpy(st->error_string, str, STREAM_MAX_ERROR_STRING); |
1337 | | /* Ensure null termination. */ |
1338 | 0 | st->error_string[STREAM_MAX_ERROR_STRING] = 0; |
1339 | 0 | return 0; |
1340 | 0 | } |
1341 | | |
1342 | | /* Open a file stream for a filter. */ |
1343 | | int |
1344 | | filter_open(const char *file_access, uint buffer_size, ref * pfile, |
1345 | | const stream_procs * procs, const stream_template * templat, |
1346 | | const stream_state * st, gs_memory_t *mem) |
1347 | 6.15k | { |
1348 | 6.15k | stream *s; |
1349 | 6.15k | uint ssize = gs_struct_type_size(templat->stype); |
1350 | 6.15k | stream_state *sst = 0; |
1351 | 6.15k | int code; |
1352 | | |
1353 | 6.15k | if (templat->stype != &st_stream_state) { |
1354 | 6.15k | sst = s_alloc_state(mem, templat->stype, "filter_open(stream_state)"); |
1355 | 6.15k | if (sst == 0) |
1356 | 0 | return_error(gs_error_VMerror); |
1357 | 6.15k | } |
1358 | 6.15k | code = file_open_stream((char *)0, 0, file_access, buffer_size, &s, |
1359 | 6.15k | (gx_io_device *)0, (iodev_proc_fopen_t)0, mem); |
1360 | 6.15k | if (code < 0) { |
1361 | 0 | gs_free_object(mem, sst, "filter_open(stream_state)"); |
1362 | 0 | return code; |
1363 | 0 | } |
1364 | 6.15k | s_std_init(s, s->cbuf, s->bsize, procs, |
1365 | 6.15k | (*file_access == 'r' ? s_mode_read : s_mode_write)); |
1366 | 6.15k | s->procs.process = templat->process; |
1367 | 6.15k | s->save_close = s->procs.close; |
1368 | 6.15k | s->procs.close = file_close_file; |
1369 | 6.15k | if (sst == 0) { |
1370 | | /* This stream doesn't have any state of its own. */ |
1371 | | /* Hack: use the stream itself as the state. */ |
1372 | 0 | sst = (stream_state *) s; |
1373 | 6.15k | } else if (st != 0) /* might not have client parameters */ |
1374 | 6.15k | memcpy(sst, st, ssize); |
1375 | 6.15k | s->state = sst; |
1376 | 6.15k | s_init_state(sst, templat, mem); |
1377 | 6.15k | sst->report_error = filter_report_error; |
1378 | | |
1379 | 6.15k | if (templat->init != 0) { |
1380 | 6.15k | code = (*templat->init)(sst); |
1381 | 6.15k | if (code < 0) { |
1382 | 0 | gs_free_object(mem, sst, "filter_open(stream_state)"); |
1383 | 0 | gs_free_object(mem, s->cbuf, "filter_open(buffer)"); |
1384 | 0 | return code; |
1385 | 0 | } |
1386 | 6.15k | } |
1387 | 6.15k | make_stream_file(pfile, s, file_access); |
1388 | 6.15k | return 0; |
1389 | 6.15k | } |
1390 | | |
1391 | | /* Close a file object. */ |
1392 | | /* This is exported only for gsmain.c. */ |
1393 | | int |
1394 | | file_close(ref * pfile) |
1395 | 0 | { |
1396 | 0 | stream *s; |
1397 | |
|
1398 | 0 | if (file_is_valid(s, pfile)) { /* closing a closed file is a no-op */ |
1399 | 0 | if (sclose(s)) |
1400 | 0 | return_error(gs_error_ioerror); |
1401 | 0 | } |
1402 | 0 | return 0; |
1403 | 0 | } |