1 /* Copyright (C) 2001-2006 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, modified
8 or distributed except as expressly authorized under the terms of that
9 license. Refer to licensing information at http://www.artifex.com/
10 or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11 San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
12 */
13
14 /* $Id: zvmem.c 9043 2008-08-28 22:48:19Z giles $ */
15 /* "Virtual memory" operators */
16 #include "stat_.h" /* get system header early to avoid name clash on Cygwin */
17 #include "ghost.h"
18 #include "gsstruct.h"
19 #include "oper.h"
20 #include "estack.h" /* for checking in restore */
21 #include "ialloc.h"
22 #include "idict.h" /* ditto */
23 #include "igstate.h"
24 #include "isave.h"
25 #include "dstack.h"
26 #include "stream.h" /* for files.h */
27 #include "files.h" /* for e-stack processing */
28 #include "store.h"
29 #include "gsmatrix.h" /* for gsstate.h */
30 #include "gsstate.h"
31
32 /* Define whether we validate memory before/after save/restore. */
33 /* Note that we only actually do this if DEBUG is set and -Z? is selected. */
34 static const bool I_VALIDATE_BEFORE_SAVE = true;
35 static const bool I_VALIDATE_AFTER_SAVE = true;
36 static const bool I_VALIDATE_BEFORE_RESTORE = true;
37 static const bool I_VALIDATE_AFTER_RESTORE = true;
38
39 /* 'Save' structure */
40 typedef struct vm_save_s vm_save_t;
41 struct vm_save_s {
42 gs_state *gsave; /* old graphics state */
43 };
44
45 gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype",
46 vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave);
47
48 /* Clean up the stacks and validate storage. */
49 static void
ivalidate_clean_spaces(i_ctx_t * i_ctx_p)50 ivalidate_clean_spaces(i_ctx_t *i_ctx_p)
51 {
52 if (gs_debug_c('?')) {
53 ref_stack_cleanup(&d_stack);
54 ref_stack_cleanup(&e_stack);
55 ref_stack_cleanup(&o_stack);
56 ivalidate_spaces();
57 }
58 }
59
60 /* - save <save> */
61 int
zsave(i_ctx_t * i_ctx_p)62 zsave(i_ctx_t *i_ctx_p)
63 {
64 os_ptr op = osp;
65 uint space = icurrent_space;
66 vm_save_t *vmsave;
67 ulong sid;
68 int code;
69 gs_state *prev;
70
71 if (I_VALIDATE_BEFORE_SAVE)
72 ivalidate_clean_spaces(i_ctx_p);
73 ialloc_set_space(idmemory, avm_local);
74 vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave");
75 ialloc_set_space(idmemory, space);
76 if (vmsave == 0)
77 return_error(e_VMerror);
78 code = alloc_save_state(idmemory, vmsave, &sid);
79 if (code < 0)
80 return code;
81 if (sid == 0) {
82 ifree_object(vmsave, "zsave");
83 return_error(e_VMerror);
84 }
85 if_debug2('u', "[u]vmsave 0x%lx, id = %lu\n",
86 (ulong) vmsave, (ulong) sid);
87 code = gs_gsave_for_save(igs, &prev);
88 if (code < 0)
89 return code;
90 code = gs_gsave(igs);
91 if (code < 0)
92 return code;
93 vmsave->gsave = prev;
94 push(1);
95 make_tav(op, t_save, 0, saveid, sid);
96 if (I_VALIDATE_AFTER_SAVE)
97 ivalidate_clean_spaces(i_ctx_p);
98 return 0;
99 }
100
101 /* <save> restore - */
102 static int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
103 static int restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t *, const alloc_save_t *, bool);
104 static void restore_fix_stack(ref_stack_t *, const alloc_save_t *, bool);
105 int
zrestore(i_ctx_t * i_ctx_p)106 zrestore(i_ctx_t *i_ctx_p)
107 {
108 os_ptr op = osp;
109 alloc_save_t *asave;
110 bool last;
111 vm_save_t *vmsave;
112 int code = restore_check_operand(op, &asave, idmemory);
113
114 if (code < 0)
115 return code;
116 if_debug2('u', "[u]vmrestore 0x%lx, id = %lu\n",
117 (ulong) alloc_save_client_data(asave),
118 (ulong) op->value.saveid);
119 if (I_VALIDATE_BEFORE_RESTORE)
120 ivalidate_clean_spaces(i_ctx_p);
121 /* Check the contents of the stacks. */
122 osp--;
123 {
124 int code;
125
126 if ((code = restore_check_stack(i_ctx_p, &o_stack, asave, false)) < 0 ||
127 (code = restore_check_stack(i_ctx_p, &e_stack, asave, true)) < 0 ||
128 (code = restore_check_stack(i_ctx_p, &d_stack, asave, false)) < 0
129 ) {
130 osp++;
131 return code;
132 }
133 }
134 /* Reset l_new in all stack entries if the new save level is zero. */
135 /* Also do some special fixing on the e-stack. */
136 restore_fix_stack(&o_stack, asave, false);
137 restore_fix_stack(&e_stack, asave, true);
138 restore_fix_stack(&d_stack, asave, false);
139 /* Iteratively restore the state of memory, */
140 /* also doing a grestoreall at each step. */
141 do {
142 vmsave = alloc_save_client_data(alloc_save_current(idmemory));
143 /* Restore the graphics state. */
144 gs_grestoreall_for_restore(igs, vmsave->gsave);
145 /*
146 * If alloc_save_space decided to do a second save, the vmsave
147 * object was allocated one save level less deep than the
148 * current level, so ifree_object won't actually free it;
149 * however, it points to a gsave object that definitely
150 * *has* been freed. In order not to trip up the garbage
151 * collector, we clear the gsave pointer now.
152 */
153 vmsave->gsave = 0;
154 /* Now it's safe to restore the state of memory. */
155 code = alloc_restore_step_in(idmemory, asave);
156 if (code < 0)
157 return code;
158 last = code;
159 }
160 while (!last);
161 {
162 uint space = icurrent_space;
163
164 ialloc_set_space(idmemory, avm_local);
165 ifree_object(vmsave, "zrestore");
166 ialloc_set_space(idmemory, space);
167 }
168 dict_set_top(); /* reload dict stack cache */
169 if (I_VALIDATE_AFTER_RESTORE)
170 ivalidate_clean_spaces(i_ctx_p);
171 /* If the i_ctx_p LockFilePermissions is true, but the userparams */
172 /* we just restored is false, we need to make sure that we do not */
173 /* cause an 'invalidaccess' in setuserparams. Temporarily set */
174 /* LockFilePermissions false until the gs_lev2.ps can do a */
175 /* setuserparams from the restored userparam dictionary. */
176 i_ctx_p->LockFilePermissions = false;
177 return 0;
178 }
179 /* Check the operand of a restore. */
180 static int
restore_check_operand(os_ptr op,alloc_save_t ** pasave,gs_dual_memory_t * idmem)181 restore_check_operand(os_ptr op, alloc_save_t ** pasave,
182 gs_dual_memory_t *idmem)
183 {
184 vm_save_t *vmsave;
185 ulong sid;
186 alloc_save_t *asave;
187
188 check_type(*op, t_save);
189 vmsave = r_ptr(op, vm_save_t);
190 if (vmsave == 0) /* invalidated save */
191 return_error(e_invalidrestore);
192 sid = op->value.saveid;
193 asave = alloc_find_save(idmem, sid);
194 if (asave == 0)
195 return_error(e_invalidrestore);
196 *pasave = asave;
197 return 0;
198 }
199 /* Check a stack to make sure all its elements are older than a save. */
200 static int
restore_check_stack(const i_ctx_t * i_ctx_p,const ref_stack_t * pstack,const alloc_save_t * asave,bool is_estack)201 restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t * pstack,
202 const alloc_save_t * asave, bool is_estack)
203 {
204 ref_stack_enum_t rsenum;
205
206 ref_stack_enum_begin(&rsenum, pstack);
207 do {
208 const ref *stkp = rsenum.ptr;
209 uint size = rsenum.size;
210
211 for (; size; stkp++, size--) {
212 const void *ptr;
213
214 switch (r_type(stkp)) {
215 case t_array:
216 /*
217 * Zero-length arrays are a special case: see the
218 * t_*array case (label rr:) in igc.c:gc_trace.
219 */
220 if (r_size(stkp) == 0) {
221 /*stkp->value.refs = (void *)0;*/
222 continue;
223 }
224 ptr = stkp->value.refs;
225 break;
226 case t_dictionary:
227 ptr = stkp->value.pdict;
228 break;
229 case t_file:
230 /* Don't check executable or closed literal */
231 /* files on the e-stack. */
232 {
233 stream *s;
234
235 if (is_estack &&
236 (r_has_attr(stkp, a_executable) ||
237 file_is_invalid(s, stkp))
238 )
239 continue;
240 }
241 ptr = stkp->value.pfile;
242 break;
243 case t_name:
244 /* Names are special because of how they are allocated. */
245 if (alloc_name_is_since_save((const gs_memory_t *)pstack->memory,
246 stkp, asave))
247 return_error(e_invalidrestore);
248 continue;
249 case t_string:
250 /* Don't check empty executable strings */
251 /* on the e-stack. */
252 if (r_size(stkp) == 0 &&
253 r_has_attr(stkp, a_executable) && is_estack
254 )
255 continue;
256 ptr = stkp->value.bytes;
257 break;
258 case t_mixedarray:
259 case t_shortarray:
260 /* See the t_array case above. */
261 if (r_size(stkp) == 0) {
262 /*stkp->value.packed = (void *)0;*/
263 continue;
264 }
265 ptr = stkp->value.packed;
266 break;
267 case t_device:
268 ptr = stkp->value.pdevice;
269 break;
270 case t_fontID:
271 case t_struct:
272 case t_astruct:
273 ptr = stkp->value.pstruct;
274 break;
275 case t_save:
276 /* See the comment in isave.h regarding the following. */
277 if (i_ctx_p->language_level <= 2)
278 continue;
279 ptr = alloc_find_save(&gs_imemory, stkp->value.saveid);
280 /*
281 * Invalid save objects aren't supposed to be possible
282 * in LL3, but just in case....
283 */
284 if (ptr == 0)
285 return_error(e_invalidrestore);
286 if (ptr == asave)
287 continue;
288 break;
289 default:
290 continue;
291 }
292 if (alloc_is_since_save(ptr, asave))
293 return_error(e_invalidrestore);
294 }
295 } while (ref_stack_enum_next(&rsenum));
296 return 0; /* OK */
297 }
298 /*
299 * If the new save level is zero, fix up the contents of a stack
300 * by clearing the l_new bit in all the entries (since we can't tolerate
301 * values with l_new set if the save level is zero).
302 * Also, in any case, fix up the e-stack by replacing empty executable
303 * strings and closed executable files that are newer than the save
304 * with canonical ones that aren't.
305 *
306 * Note that this procedure is only called if restore_check_stack succeeded.
307 */
308 static void
restore_fix_stack(ref_stack_t * pstack,const alloc_save_t * asave,bool is_estack)309 restore_fix_stack(ref_stack_t * pstack, const alloc_save_t * asave,
310 bool is_estack)
311 {
312 ref_stack_enum_t rsenum;
313
314 ref_stack_enum_begin(&rsenum, pstack);
315 do {
316 ref *stkp = rsenum.ptr;
317 uint size = rsenum.size;
318
319 for (; size; stkp++, size--) {
320 r_clear_attrs(stkp, l_new); /* always do it, no harm */
321 if (is_estack) {
322 ref ofile;
323
324 ref_assign(&ofile, stkp);
325 switch (r_type(stkp)) {
326 case t_string:
327 if (r_size(stkp) == 0 &&
328 alloc_is_since_save(stkp->value.bytes,
329 asave)
330 ) {
331 make_empty_const_string(stkp,
332 avm_foreign);
333 break;
334 }
335 continue;
336 case t_file:
337 if (alloc_is_since_save(stkp->value.pfile,
338 asave)
339 ) {
340 make_invalid_file(stkp);
341 break;
342 }
343 continue;
344 default:
345 continue;
346 }
347 r_copy_attrs(stkp, a_all | a_executable,
348 &ofile);
349 }
350 }
351 } while (ref_stack_enum_next(&rsenum));
352 }
353
354 /* - vmstatus <save_level> <vm_used> <vm_maximum> */
355 static int
zvmstatus(i_ctx_t * i_ctx_p)356 zvmstatus(i_ctx_t *i_ctx_p)
357 {
358 os_ptr op = osp;
359 gs_memory_status_t mstat, dstat;
360
361 gs_memory_status(imemory, &mstat);
362 if (imemory == imemory_global) {
363 gs_memory_status_t sstat;
364
365 gs_memory_status(imemory_system, &sstat);
366 mstat.allocated += sstat.allocated;
367 mstat.used += sstat.used;
368 }
369 gs_memory_status(imemory->non_gc_memory, &dstat);
370 push(3);
371 make_int(op - 2, imemory_save_level(iimemory_local));
372 make_int(op - 1, mstat.used);
373 make_int(op, mstat.allocated + dstat.allocated - dstat.used);
374 return 0;
375 }
376
377 /* ------ Non-standard extensions ------ */
378
379 /* <save> .forgetsave - */
380 static int
zforgetsave(i_ctx_t * i_ctx_p)381 zforgetsave(i_ctx_t *i_ctx_p)
382 {
383 os_ptr op = osp;
384 alloc_save_t *asave;
385 vm_save_t *vmsave;
386 int code = restore_check_operand(op, &asave, idmemory);
387
388 if (code < 0)
389 return 0;
390 vmsave = alloc_save_client_data(asave);
391 /* Reset l_new in all stack entries if the new save level is zero. */
392 restore_fix_stack(&o_stack, asave, false);
393 restore_fix_stack(&e_stack, asave, false);
394 restore_fix_stack(&d_stack, asave, false);
395 /*
396 * Forget the gsaves, by deleting the bottom gstate on
397 * the current stack and the top one on the saved stack and then
398 * concatenating the stacks together.
399 */
400 {
401 gs_state *pgs = igs;
402 gs_state *last;
403
404 while (gs_state_saved(last = gs_state_saved(pgs)) != 0)
405 pgs = last;
406 gs_state_swap_saved(last, vmsave->gsave);
407 gs_grestore(last);
408 gs_grestore(last);
409 }
410 /* Forget the save in the memory manager. */
411 code = alloc_forget_save_in(idmemory, asave);
412 if (code < 0)
413 return code;
414 {
415 uint space = icurrent_space;
416
417 ialloc_set_space(idmemory, avm_local);
418 /* See above for why we clear the gsave pointer here. */
419 vmsave->gsave = 0;
420 ifree_object(vmsave, "zrestore");
421 ialloc_set_space(idmemory, space);
422 }
423 pop(1);
424 return 0;
425 }
426
427 /* ------ Initialization procedure ------ */
428
429 const op_def zvmem_op_defs[] =
430 {
431 {"1.forgetsave", zforgetsave},
432 {"1restore", zrestore},
433 {"0save", zsave},
434 {"0vmstatus", zvmstatus},
435 op_def_end(0)
436 };
437