1 /* Copyright (C) 1989, 1995, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: zvmem.c,v 1.8 2004/08/04 19:36:13 stefan Exp $ */
18 /* "Virtual memory" operators */
19 #include "ghost.h"
20 #include "gsstruct.h"
21 #include "oper.h"
22 #include "estack.h" /* for checking in restore */
23 #include "ialloc.h"
24 #include "idict.h" /* ditto */
25 #include "igstate.h"
26 #include "isave.h"
27 #include "dstack.h"
28 #include "stream.h" /* for files.h */
29 #include "files.h" /* for e-stack processing */
30 #include "store.h"
31 #include "gsmatrix.h" /* for gsstate.h */
32 #include "gsstate.h"
33
34 /* Define whether we validate memory before/after save/restore. */
35 /* Note that we only actually do this if DEBUG is set and -Z? is selected. */
36 private const bool I_VALIDATE_BEFORE_SAVE = true;
37 private const bool I_VALIDATE_AFTER_SAVE = true;
38 private const bool I_VALIDATE_BEFORE_RESTORE = true;
39 private const bool I_VALIDATE_AFTER_RESTORE = true;
40
41 /* 'Save' structure */
42 typedef struct vm_save_s vm_save_t;
43 struct vm_save_s {
44 gs_state *gsave; /* old graphics state */
45 };
46
47 gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype",
48 vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave);
49
50 /* Clean up the stacks and validate storage. */
51 private void
ivalidate_clean_spaces(i_ctx_t * i_ctx_p)52 ivalidate_clean_spaces(i_ctx_t *i_ctx_p)
53 {
54 if (gs_debug_c('?')) {
55 ref_stack_cleanup(&d_stack);
56 ref_stack_cleanup(&e_stack);
57 ref_stack_cleanup(&o_stack);
58 ivalidate_spaces();
59 }
60 }
61
62 /* - save <save> */
63 int
zsave(i_ctx_t * i_ctx_p)64 zsave(i_ctx_t *i_ctx_p)
65 {
66 os_ptr op = osp;
67 uint space = icurrent_space;
68 vm_save_t *vmsave;
69 ulong sid;
70 int code;
71 gs_state *prev;
72
73 if (I_VALIDATE_BEFORE_SAVE)
74 ivalidate_clean_spaces(i_ctx_p);
75 ialloc_set_space(idmemory, avm_local);
76 vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave");
77 ialloc_set_space(idmemory, space);
78 if (vmsave == 0)
79 return_error(e_VMerror);
80 sid = alloc_save_state(idmemory, vmsave);
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 private int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
103 private int restore_check_stack(const ref_stack_t *, const alloc_save_t *, bool);
104 private 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(&o_stack, asave, false)) < 0 ||
127 (code = restore_check_stack(&e_stack, asave, true)) < 0 ||
128 (code = restore_check_stack(&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 last = alloc_restore_state_step(asave);
156 }
157 while (!last);
158 {
159 uint space = icurrent_space;
160
161 ialloc_set_space(idmemory, avm_local);
162 ifree_object(vmsave, "zrestore");
163 ialloc_set_space(idmemory, space);
164 }
165 dict_set_top(); /* reload dict stack cache */
166 if (I_VALIDATE_AFTER_RESTORE)
167 ivalidate_clean_spaces(i_ctx_p);
168 /* If the i_ctx_p LockFilePermissions is true, but the userparams */
169 /* we just restored is false, we need to make sure that we do not */
170 /* cause an 'invalidaccess' in setuserparams. Temporarily set */
171 /* LockFilePermissions false until the gs_lev2.ps can do a */
172 /* setuserparams from the restored userparam dictionary. */
173 i_ctx_p->LockFilePermissions = false;
174 return 0;
175 }
176 /* Check the operand of a restore. */
177 private int
restore_check_operand(os_ptr op,alloc_save_t ** pasave,gs_dual_memory_t * idmem)178 restore_check_operand(os_ptr op, alloc_save_t ** pasave,
179 gs_dual_memory_t *idmem)
180 {
181 vm_save_t *vmsave;
182 ulong sid;
183 alloc_save_t *asave;
184
185 check_type(*op, t_save);
186 vmsave = r_ptr(op, vm_save_t);
187 if (vmsave == 0) /* invalidated save */
188 return_error(e_invalidrestore);
189 sid = op->value.saveid;
190 asave = alloc_find_save(idmem, sid);
191 if (asave == 0)
192 return_error(e_invalidrestore);
193 *pasave = asave;
194 return 0;
195 }
196 /* Check a stack to make sure all its elements are older than a save. */
197 private int
restore_check_stack(const ref_stack_t * pstack,const alloc_save_t * asave,bool is_estack)198 restore_check_stack(const ref_stack_t * pstack, const alloc_save_t * asave,
199 bool is_estack)
200 {
201 ref_stack_enum_t rsenum;
202
203 ref_stack_enum_begin(&rsenum, pstack);
204 do {
205 const ref *stkp = rsenum.ptr;
206 uint size = rsenum.size;
207
208 for (; size; stkp++, size--) {
209 const void *ptr;
210
211 switch (r_type(stkp)) {
212 case t_array:
213 ptr = stkp->value.refs;
214 break;
215 case t_dictionary:
216 ptr = stkp->value.pdict;
217 break;
218 case t_file:
219 /* Don't check executable or closed literal */
220 /* files on the e-stack. */
221 {
222 stream *s;
223
224 if (is_estack &&
225 (r_has_attr(stkp, a_executable) ||
226 file_is_invalid(s, stkp))
227 )
228 continue;
229 }
230 ptr = stkp->value.pfile;
231 break;
232 case t_name:
233 /* Names are special because of how they are allocated. */
234 if (alloc_name_is_since_save((const gs_memory_t *)pstack->memory,
235 stkp, asave))
236 return_error(e_invalidrestore);
237 continue;
238 case t_string:
239 /* Don't check empty executable strings */
240 /* on the e-stack. */
241 if (r_size(stkp) == 0 &&
242 r_has_attr(stkp, a_executable) && is_estack
243 )
244 continue;
245 ptr = stkp->value.bytes;
246 break;
247 case t_mixedarray:
248 case t_shortarray:
249 ptr = stkp->value.packed;
250 break;
251 case t_device:
252 ptr = stkp->value.pdevice;
253 break;
254 case t_fontID:
255 case t_struct:
256 case t_astruct:
257 ptr = stkp->value.pstruct;
258 break;
259 default:
260 continue;
261 }
262 if (alloc_is_since_save(ptr, asave))
263 return_error(e_invalidrestore);
264 }
265 } while (ref_stack_enum_next(&rsenum));
266 return 0; /* OK */
267 }
268 /*
269 * If the new save level is zero, fix up the contents of a stack
270 * by clearing the l_new bit in all the entries (since we can't tolerate
271 * values with l_new set if the save level is zero).
272 * Also, in any case, fix up the e-stack by replacing empty executable
273 * strings and closed executable files that are newer than the save
274 * with canonical ones that aren't.
275 *
276 * Note that this procedure is only called if restore_check_stack succeeded.
277 */
278 private void
restore_fix_stack(ref_stack_t * pstack,const alloc_save_t * asave,bool is_estack)279 restore_fix_stack(ref_stack_t * pstack, const alloc_save_t * asave,
280 bool is_estack)
281 {
282 ref_stack_enum_t rsenum;
283
284 ref_stack_enum_begin(&rsenum, pstack);
285 do {
286 ref *stkp = rsenum.ptr;
287 uint size = rsenum.size;
288
289 for (; size; stkp++, size--) {
290 r_clear_attrs(stkp, l_new); /* always do it, no harm */
291 if (is_estack) {
292 ref ofile;
293
294 ref_assign(&ofile, stkp);
295 switch (r_type(stkp)) {
296 case t_string:
297 if (r_size(stkp) == 0 &&
298 alloc_is_since_save(stkp->value.bytes,
299 asave)
300 ) {
301 make_empty_const_string(stkp,
302 avm_foreign);
303 break;
304 }
305 continue;
306 case t_file:
307 if (alloc_is_since_save(stkp->value.pfile,
308 asave)
309 ) {
310 make_invalid_file(stkp);
311 break;
312 }
313 continue;
314 default:
315 continue;
316 }
317 r_copy_attrs(stkp, a_all | a_executable,
318 &ofile);
319 }
320 }
321 } while (ref_stack_enum_next(&rsenum));
322 }
323
324 /* - vmstatus <save_level> <vm_used> <vm_maximum> */
325 private int
zvmstatus(i_ctx_t * i_ctx_p)326 zvmstatus(i_ctx_t *i_ctx_p)
327 {
328 os_ptr op = osp;
329 gs_memory_status_t mstat, dstat;
330
331 gs_memory_status(imemory, &mstat);
332 if (imemory == imemory_global) {
333 gs_memory_status_t sstat;
334
335 gs_memory_status(imemory_system, &sstat);
336 mstat.allocated += sstat.allocated;
337 mstat.used += sstat.used;
338 }
339 gs_memory_status(imemory->non_gc_memory, &dstat);
340 push(3);
341 make_int(op - 2, imemory_save_level(iimemory_local));
342 make_int(op - 1, mstat.used);
343 make_int(op, mstat.allocated + dstat.allocated - dstat.used);
344 return 0;
345 }
346
347 /* ------ Non-standard extensions ------ */
348
349 /* <save> .forgetsave - */
350 private int
zforgetsave(i_ctx_t * i_ctx_p)351 zforgetsave(i_ctx_t *i_ctx_p)
352 {
353 os_ptr op = osp;
354 alloc_save_t *asave;
355 vm_save_t *vmsave;
356 int code = restore_check_operand(op, &asave, idmemory);
357
358 if (code < 0)
359 return 0;
360 vmsave = alloc_save_client_data(asave);
361 /* Reset l_new in all stack entries if the new save level is zero. */
362 restore_fix_stack(&o_stack, asave, false);
363 restore_fix_stack(&e_stack, asave, false);
364 restore_fix_stack(&d_stack, asave, false);
365 /*
366 * Forget the gsaves, by deleting the bottom gstate on
367 * the current stack and the top one on the saved stack and then
368 * concatenating the stacks together.
369 */
370 {
371 gs_state *pgs = igs;
372 gs_state *last;
373
374 while (gs_state_saved(last = gs_state_saved(pgs)) != 0)
375 pgs = last;
376 gs_state_swap_saved(last, vmsave->gsave);
377 gs_grestore(last);
378 gs_grestore(last);
379 }
380 /* Forget the save in the memory manager. */
381 alloc_forget_save(asave);
382 {
383 uint space = icurrent_space;
384
385 ialloc_set_space(idmemory, avm_local);
386 /* See above for why we clear the gsave pointer here. */
387 vmsave->gsave = 0;
388 ifree_object(vmsave, "zrestore");
389 ialloc_set_space(idmemory, space);
390 }
391 pop(1);
392 return 0;
393 }
394
395 /* ------ Initialization procedure ------ */
396
397 const op_def zvmem_op_defs[] =
398 {
399 {"1.forgetsave", zforgetsave},
400 {"1restore", zrestore},
401 {"0save", zsave},
402 {"0vmstatus", zvmstatus},
403 op_def_end(0)
404 };
405