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