1 /* Copyright (C) 2001-2019 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 /* %rom% IODevice implementation for a compressed in-memory filesystem */
18
19 /*
20 * This file implements a special %rom% IODevice designed for embedded
21 * use. It accesses a compressed filesytem image which may be stored
22 * in literal ROM, or more commonly is just static data linked directly
23 * into the executable. This can be used for storing postscript library
24 * files, fonts, Resources or other data files that Ghostscript needs
25 * to run.
26 */
27
28 #include "std.h"
29 #include "stdint_.h"
30 #include "string_.h"
31 #include "gsiorom.h"
32 #include "gx.h"
33 #include "gpcheck.h"
34 #include "gserrors.h"
35 #include "gsstruct.h"
36 #include "gsutil.h"
37 #include "gxiodev.h"
38 #include "stream.h"
39 #include "stat_.h"
40 #include "zlib.h"
41
42 /* device method prototypes */
43 static iodev_proc_init(romfs_init);
44 static iodev_proc_finit(romfs_finit);
45 static iodev_proc_open_file(romfs_open_file);
46 static iodev_proc_file_status(romfs_file_status);
47 static iodev_proc_enumerate_files(romfs_enumerate_files_init);
48 static iodev_proc_enumerate_next(romfs_enumerate_next);
49 static iodev_proc_enumerate_close(romfs_enumerate_close);
50 /* close is handled by stream closure */
51
52 /* device definition */
53 const gx_io_device gs_iodev_rom =
54 {
55 "%rom%", "FileSystem",
56 {romfs_init, romfs_finit, iodev_no_open_device,
57 romfs_open_file,
58 iodev_no_fopen, iodev_no_fclose,
59 iodev_no_delete_file, iodev_no_rename_file,
60 romfs_file_status,
61 romfs_enumerate_files_init, romfs_enumerate_next, romfs_enumerate_close,
62 iodev_no_get_params, iodev_no_put_params
63 },
64 NULL,
65 NULL
66 };
67
68 /* internal state for our device */
69 typedef struct romfs_state_s {
70 int atblock; /* for later when we decompress by blocks */
71 } romfs_state;
72
73 gs_private_st_simple(st_romfs_state, struct romfs_state_s, "romfs_state");
74
75 typedef struct romfs_file_enum_s {
76 char *pattern; /* pattern pointer */
77 int list_index; /* next node to visit */
78 gs_memory_t *memory; /* memory structure used */
79 } romfs_file_enum;
80
81 gs_private_st_ptrs1(st_romfs_file_enum, struct romfs_file_enum_s, "romfs_file_enum",
82 romfs_file_enum_enum_ptrs, romfs_file_enum_reloc_ptrs, pattern);
83
84 static uint32_t get_u32_big_endian(const uint32_t *a);
85
86 /* coverity[ -tainted_data_return ] */
87 /* coverity[ -tainted_data_argument : arg-0 ] */
88 static uint32_t
get_u32_big_endian(const uint32_t * a)89 get_u32_big_endian(const uint32_t *a)
90 {
91 uint32_t v;
92 const unsigned char *c=(const unsigned char *)a;
93
94 v = (c[0]<<24) | (c[1]<<16) | (c[2]<<8) | c[3];
95 return v;
96 }
97
98 /* ------ Block streams, potentially compressed (read only) ------ */
99
100 /* String stream procedures */
101 static int
102 s_block_read_available(stream *, gs_offset_t *),
103 s_block_read_seek(stream *, gs_offset_t),
104 s_block_read_close(stream *),
105 s_block_read_process(stream_state *, stream_cursor_read *,
106 stream_cursor_write *, bool);
107
108 /* Initialize a stream for reading from a collection of blocks */
109 static void
sread_block(register stream * s,const byte * ptr,uint len,const uint32_t * node)110 sread_block(register stream *s, const byte *ptr, uint len, const uint32_t *node )
111 {
112 static const stream_procs p = {
113 s_block_read_available, s_block_read_seek, s_std_read_reset,
114 s_std_read_flush, s_block_read_close, s_block_read_process,
115 NULL /* no read_switch */
116 };
117 s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek);
118 s->end_status = 0;
119 s->file = (gp_file *)node; /* convenient place to put it for %rom% files */
120 s->file_modes = s->modes;
121 s->file_offset = 0;
122 s->file_limit = S_FILE_LIMIT_MAX;
123 }
124
125 /* Return the number of available bytes */
126 static int
s_block_read_available(stream * s,gs_offset_t * pl)127 s_block_read_available(stream *s, gs_offset_t *pl)
128 {
129 uint32_t *node = (uint32_t *)s->file;
130 uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
131
132 *pl = filelen - s->position - (sbufptr(s) - s->cbuf);
133 if (*pl == 0 && s->close_at_eod) /* EOF */
134 *pl = -1;
135 return 0;
136 }
137
138 /* Seek in a string being read. Return 0 if OK, ERRC if not. */
139 static int
s_block_read_seek(register stream * s,gs_offset_t pos)140 s_block_read_seek(register stream * s, gs_offset_t pos)
141 {
142 uint32_t *node = (uint32_t *)s->file;
143 uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
144 uint end = s->cursor.r.limit - s->cbuf + 1;
145 long offset = pos - s->position;
146
147 if (pos < 0 || pos > filelen)
148 return ERRC;
149 if (offset < 0 || offset > end) {
150 /* Need to pull a different block into the buffer */
151 stream_cursor_write pw;
152
153 /* buffer stays aligned to blocks */
154 offset = (s->file_offset + pos) % ROMFS_BLOCKSIZE;
155 s->position = pos - offset;
156 pw.ptr = s->cbuf - 1;
157 pw.limit = pw.ptr + s->cbsize;
158 s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
159 if ((s->end_status = s_block_read_process((stream_state *)s, NULL, &pw, 0)) == ERRC)
160 return ERRC;
161 if (s->end_status == 1)
162 s->end_status = 0;
163 s->cursor.r.ptr = s->cbuf - 1;
164 s->cursor.r.limit = pw.ptr; /* limit of the block just read */
165 }
166 /* Now set the read pointer to the correct place in the buffer */
167 s->cursor.r.ptr = s->cbuf + offset - 1;
168 return 0;
169 }
170
171 static int
s_block_read_close(stream * s)172 s_block_read_close(stream * s)
173 {
174 gs_free_object(s->memory, s->cbuf, "file_close(buffer)");
175 s->file = 0; /* disconnect the node */
176 /* Increment the IDs to prevent further access. */
177 s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
178 return 0;
179 }
180
181 static int
s_block_read_process(stream_state * st,stream_cursor_read * ignore_pr,stream_cursor_write * pw,bool last)182 s_block_read_process(stream_state * st, stream_cursor_read * ignore_pr,
183 stream_cursor_write * pw, bool last)
184 {
185 int code;
186 stream *s = (stream *)st; /* no separate state */
187 uint32_t *node = (uint32_t *)s->file;
188 uint max_count = pw->limit - pw->ptr;
189 int status = 1;
190 int compression = ((get_u32_big_endian(node) & 0x80000000) != 0) ? 1 : 0;
191 uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
192 uint32_t blocks = (filelen+ROMFS_BLOCKSIZE-1) / ROMFS_BLOCKSIZE;
193 uint32_t iblock = (s->position + s->file_offset + (s->cursor.r.limit + 1 - s->cbuf)) / ROMFS_BLOCKSIZE;
194 uint32_t block_length = get_u32_big_endian(node+1+(2*iblock));
195 uint32_t block_offset = get_u32_big_endian(node+2+(2*iblock));
196 unsigned const char *block_data = ((unsigned char *)node) + block_offset;
197 int count = iblock < (blocks - 1) ? ROMFS_BLOCKSIZE : filelen - (ROMFS_BLOCKSIZE * iblock);
198
199 if (s->position + (s->cursor.r.limit - s->cbuf + 1) >= filelen || block_data == NULL)
200 return EOFC; /* at EOF */
201 if (s->file_limit < S_FILE_LIMIT_MAX) {
202 /* Adjust count for subfile limit */
203 uint32_t limit_count = s->file_offset + s->file_limit - s->position;
204
205 if (count > limit_count)
206 count = limit_count;
207 }
208 /* get the block into the buffer */
209 if (compression) {
210 unsigned long buflen = ROMFS_BLOCKSIZE;
211 byte *dest = (pw->ptr + 1); /* destination for unpack */
212 int need_copy = false;
213
214 /* If the dest is not in our buffer, we can only use it if there */
215 /* is enough space in it */
216 if ((dest < s->cbuf) || (dest >= (s->cbuf + s->cbsize))) {
217 /* the destination is _not_ in our buffer. If the area isn't */
218 /* big enough we need to ucompress to our buffer, then copy */
219 /* the data afterward. INVARIANT: if the buffer is outside */
220 /* the cbuf, then the cbuf must be empty. */
221 if (max_count < count) {
222 #ifdef DEBUG
223 if ((sbufptr(s)) != s->cursor.r.limit)
224 emprintf(s->memory, "cbuf not empty as expected\n.");
225 #endif
226 dest = s->cbuf;
227 need_copy = true;
228 }
229 }
230 /* Decompress the data into this block */
231 code = uncompress (dest, &buflen, block_data, block_length);
232 if (code != Z_OK || count != buflen)
233 return ERRC;
234 if (need_copy) {
235 memcpy(pw->ptr+1, dest, max_count);
236 count = max_count;
237 }
238 } else {
239 /* not compressed -- just copy it */
240 count = block_length;
241 if (count > max_count)
242 count = max_count;
243 memcpy(pw->ptr+1, block_data, count);
244 }
245 if (count < 0)
246 count = 0;
247 pw->ptr += count;
248 process_interrupts(s->memory);
249 return status;
250 }
251
252 static int
romfs_init(gx_io_device * iodev,gs_memory_t * mem)253 romfs_init(gx_io_device *iodev, gs_memory_t *mem)
254 {
255 romfs_state *state = gs_alloc_struct(mem, romfs_state, &st_romfs_state,
256 "romfs_init(state)");
257 if (!state)
258 return_error(gs_error_VMerror);
259 iodev->state = state;
260 return 0;
261 }
262
263 static void
romfs_finit(gx_io_device * iodev,gs_memory_t * mem)264 romfs_finit(gx_io_device *iodev, gs_memory_t *mem)
265 {
266 gs_free_object(mem, iodev->state, "romfs_finit");
267 iodev->state = NULL;
268 return;
269 }
270
271 static int
romfs_open_file(gx_io_device * iodev,const char * fname,uint namelen,const char * access,stream ** ps,gs_memory_t * mem)272 romfs_open_file(gx_io_device *iodev, const char *fname, uint namelen,
273 const char *access, stream **ps, gs_memory_t *mem)
274 {
275 extern const uint32_t *gs_romfs[];
276 int code;
277 const uint32_t *node_scan = gs_romfs[0], *node = NULL;
278 uint32_t filelen, blocks;
279 int i;
280 char *filename;
281 char fmode[4] = "\000\000\000\000";
282
283 /* return an empty stream on error */
284 *ps = NULL;
285
286 /* scan the inodes to find the requested file */
287 for (i=0; node_scan != 0; i++, node_scan = gs_romfs[i]) {
288 filelen = get_u32_big_endian(node_scan) & 0x7fffffff; /* ignore compression bit */
289 blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
290 filename = (char *)(&(node_scan[1+(2*blocks)]));
291 if ((namelen == strlen(filename)) &&
292 (strncmp(filename, fname, namelen) == 0)) {
293 node = node_scan;
294 break;
295 }
296 }
297 /* inode points to the file (or NULL if not found */
298 if (node == NULL)
299 return_error(gs_error_undefinedfilename);
300
301 /* Initialize a stream for reading this romfs file using a common function */
302 /* we get a buffer that is larger than what we need for decompression */
303 /* we need extra space since some filters may leave data in the buffer when */
304 /* calling 'read_process' */
305 code = file_prepare_stream(fname, namelen, access, ROMFS_BLOCKSIZE+256, ps, fmode, mem);
306 if (code < 0)
307 return code;
308 sread_block(*ps, (*ps)->cbuf, (*ps)->cbsize, node);
309 /* return success */
310 return 0;
311 }
312
313 static int
romfs_file_status(gx_io_device * iodev,const char * fname,struct stat * pstat)314 romfs_file_status(gx_io_device * iodev, const char *fname, struct stat *pstat)
315 {
316 extern const uint32_t *gs_romfs[];
317 extern const time_t gs_romfs_buildtime;
318 const uint32_t *node_scan = gs_romfs[0], *node = NULL;
319 uint32_t filelen, blocks;
320 int i;
321 char *filename;
322 uint namelen = strlen(fname);
323
324 /* a build time of zero indicates we have the "dummy" romfs
325 * used when COMPILE_INITS==0 - returning a specific error here
326 * gives us a quick way to check for that.
327 */
328 if (gs_romfs_buildtime == (time_t)0) {
329 return_error(gs_error_unregistered);
330 }
331
332 memset(pstat, 0, sizeof(struct stat));
333 /* scan the inodes to find the requested file */
334 for (i=0; node_scan != 0; i++, node_scan = gs_romfs[i]) {
335 filelen = get_u32_big_endian(node_scan) & 0x7fffffff; /* ignore compression bit */
336 blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
337 filename = (char *)(&(node_scan[1+(2*blocks)]));
338 if ((namelen == strlen(filename)) &&
339 (strncmp(filename, fname, namelen) == 0)) {
340 node = node_scan;
341 break;
342 }
343 }
344 /* inode points to the file (or NULL if not found */
345 if (node == NULL)
346 return_error(gs_error_undefinedfilename);
347
348 /* fill in the values used by zstatus */
349 pstat->st_size = filelen;
350 pstat->st_mtime = gs_romfs_buildtime;
351 pstat->st_ctime = gs_romfs_buildtime;
352 return 0; /* success */
353 }
354
355 static file_enum *
romfs_enumerate_files_init(gs_memory_t * mem,gx_io_device * iodev,const char * pat,uint patlen)356 romfs_enumerate_files_init(gs_memory_t * mem, gx_io_device *iodev, const char *pat,
357 uint patlen)
358 {
359 romfs_file_enum *penum = gs_alloc_struct(mem, romfs_file_enum, &st_romfs_file_enum,
360 "romfs_enumerate_files_init(file_enum)");
361 if (penum == NULL)
362 return NULL;
363 memset(penum, 0, sizeof(romfs_file_enum));
364 penum->pattern = (char *)gs_alloc_bytes(mem, patlen+1, "romfs_enumerate_file_init(pattern)");
365 penum->list_index = 0; /* start at first node */
366 penum->memory = mem;
367 if (penum->pattern == NULL) {
368 romfs_enumerate_close(mem, (file_enum *) penum);
369 return NULL;
370 }
371 memcpy(penum->pattern, pat, patlen); /* Copy string to buffer */
372 penum->pattern[patlen]=0; /* Terminate string */
373
374 return (file_enum *)penum;
375 }
376
377 static void
romfs_enumerate_close(gs_memory_t * mem,file_enum * pfen)378 romfs_enumerate_close(gs_memory_t * mem, file_enum *pfen)
379 {
380 romfs_file_enum *penum = (romfs_file_enum *)pfen;
381 gs_memory_t *mem2 = penum->memory;
382 (void)mem;
383
384 if (penum->pattern)
385 gs_free_object(mem2, penum->pattern, "romfs_enum_init(pattern)");
386 gs_free_object(mem2, penum, "romfs_enum_init(romfs_enum)");
387 }
388
389 static uint
romfs_enumerate_next(gs_memory_t * mem,file_enum * pfen,char * ptr,uint maxlen)390 romfs_enumerate_next(gs_memory_t * mem, file_enum *pfen, char *ptr, uint maxlen)
391 {
392 extern const uint32_t *gs_romfs[];
393 romfs_file_enum *penum = (romfs_file_enum *)pfen;
394 (void)mem;
395
396 while (gs_romfs[penum->list_index] != 0) {
397 const uint32_t *node = gs_romfs[penum->list_index];
398 uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
399 uint32_t blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
400 char *filename = (char *)(&(node[1+(2*blocks)]));
401
402 penum->list_index++; /* bump to next unconditionally */
403 if (string_match((byte *)filename, strlen(filename),
404 (byte *)penum->pattern,
405 strlen(penum->pattern), 0)) {
406 if (strlen(filename) < maxlen)
407 memcpy(ptr, filename, strlen(filename));
408 return strlen(filename); /* if > maxlen, caller will detect rangecheck */
409 }
410 }
411 /* ran off end of list, close the enum */
412 romfs_enumerate_close(mem, pfen);
413 return ~(uint)0;
414 }
415