Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/base/gsiorom.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
/* %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
89
get_u32_big_endian(const uint32_t *a)
90
2.90G
{
91
2.90G
    uint32_t v;
92
2.90G
    const unsigned char *c=(const unsigned char *)a;
93
94
2.90G
    v = (c[0]<<24) | (c[1]<<16) | (c[2]<<8) | c[3];
95
2.90G
    return v;
96
2.90G
}
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
110
sread_block(register stream *s,  const byte *ptr, uint len, const uint32_t *node )
111
4.40M
{
112
4.40M
    static const stream_procs p = {
113
4.40M
         s_block_read_available, s_block_read_seek, s_std_read_reset,
114
4.40M
         s_std_read_flush, s_block_read_close, s_block_read_process,
115
         NULL   /* no read_switch */
116
4.40M
    };
117
4.40M
    s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek);
118
4.40M
    s->end_status = 0;
119
4.40M
    s->file = (gp_file *)node;  /* convenient place to put it for %rom% files */
120
4.40M
    s->file_modes = s->modes;
121
4.40M
    s->file_offset = 0;
122
4.40M
    s->file_limit = S_FILE_LIMIT_MAX;
123
4.40M
}
124
125
/* Return the number of available bytes */
126
static int
127
s_block_read_available(stream *s, gs_offset_t *pl)
128
3.36M
{
129
3.36M
    uint32_t *node = (uint32_t *)s->file;
130
3.36M
    uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
131
132
3.36M
    *pl = filelen - s->position - (sbufptr(s) - s->cbuf);
133
3.36M
    if (*pl == 0 && s->close_at_eod)  /* EOF */
134
1.41k
        *pl = -1;
135
3.36M
    return 0;
136
3.36M
}
137
138
/* Seek in a string being read.  Return 0 if OK, ERRC if not. */
139
static int
140
s_block_read_seek(register stream * s, gs_offset_t pos)
141
10.0M
{
142
10.0M
    uint32_t *node = (uint32_t *)s->file;
143
10.0M
    uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
144
10.0M
    uint end = s->cursor.r.limit - s->cbuf + 1;
145
10.0M
    long offset = pos - s->position;
146
147
10.0M
    if (pos < 0 || pos > filelen)
148
1.41k
        return ERRC;
149
10.0M
    if (offset < 0 || offset > end) {
150
        /* Need to pull a different block into the buffer */
151
4.17M
        stream_cursor_write pw;
152
153
        /* buffer stays aligned to blocks */
154
4.17M
        offset = (s->file_offset + pos) % ROMFS_BLOCKSIZE;
155
4.17M
        s->position = pos - offset;
156
4.17M
        pw.ptr = s->cbuf - 1;
157
4.17M
        pw.limit = pw.ptr + s->cbsize;
158
4.17M
        s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
159
4.17M
        if ((s->end_status = s_block_read_process((stream_state *)s, NULL, &pw, 0)) == ERRC)
160
0
            return ERRC;
161
4.17M
        if (s->end_status == 1)
162
4.17M
            s->end_status = 0;
163
4.17M
        s->cursor.r.ptr = s->cbuf - 1;
164
4.17M
        s->cursor.r.limit = pw.ptr;   /* limit of the block just read */
165
4.17M
    }
166
    /* Now set the read pointer to the correct place in the buffer */
167
10.0M
    s->cursor.r.ptr = s->cbuf + offset - 1;
168
10.0M
    return 0;
169
10.0M
}
170
171
static int
172
s_block_read_close(stream * s)
173
4.40M
{
174
4.40M
    gs_free_object(s->memory, s->cbuf, "file_close(buffer)");
175
4.40M
    s->file = 0;      /* disconnect the node */
176
    /* Increment the IDs to prevent further access. */
177
4.40M
    s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
178
4.40M
    return 0;
179
4.40M
}
180
181
static int
182
s_block_read_process(stream_state * st, stream_cursor_read * ignore_pr,
183
                      stream_cursor_write * pw, bool last)
184
18.8M
{
185
18.8M
    int  code;
186
18.8M
    stream *s = (stream *)st; /* no separate state */
187
18.8M
    uint32_t *node = (uint32_t *)s->file;
188
18.8M
    uint max_count = pw->limit - pw->ptr;
189
18.8M
    int status = 1;
190
18.8M
    int compression = ((get_u32_big_endian(node) & 0x80000000) != 0) ? 1 : 0;
191
18.8M
    uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
192
18.8M
    uint32_t blocks = (filelen+ROMFS_BLOCKSIZE-1) / ROMFS_BLOCKSIZE;
193
18.8M
    uint32_t iblock = (s->position + s->file_offset + (s->cursor.r.limit + 1 - s->cbuf)) / ROMFS_BLOCKSIZE;
194
18.8M
    uint32_t block_length = get_u32_big_endian(node+1+(2*iblock));
195
18.8M
    uint32_t block_offset = get_u32_big_endian(node+2+(2*iblock));
196
18.8M
    unsigned const char *block_data = ((unsigned char *)node) + block_offset;
197
18.8M
    int count = iblock < (blocks - 1) ? ROMFS_BLOCKSIZE : filelen - (ROMFS_BLOCKSIZE * iblock);
198
199
18.8M
    if (s->position + (s->cursor.r.limit - s->cbuf + 1) >= filelen || block_data == NULL)
200
740k
        return EOFC;      /* at EOF */
201
18.1M
    if (s->file_limit < S_FILE_LIMIT_MAX) {
202
        /* Adjust count for subfile limit */
203
0
        uint32_t limit_count = s->file_offset + s->file_limit - s->position;
204
205
0
        if (count > limit_count)
206
0
            count = limit_count;
207
0
    }
208
    /* get the block into the buffer */
209
18.1M
    if (compression) {
210
17.8M
        unsigned long buflen = ROMFS_BLOCKSIZE;
211
17.8M
        byte *dest = (pw->ptr + 1); /* destination for unpack */
212
17.8M
        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
17.8M
        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
5.04M
            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
0
                dest = s->cbuf;
227
0
                need_copy = true;
228
0
            }
229
5.04M
        }
230
        /* Decompress the data into this block */
231
17.8M
        code = uncompress (dest, &buflen, block_data, block_length);
232
17.8M
        if (code != Z_OK || count != buflen)
233
0
            return ERRC;
234
17.8M
        if (need_copy) {
235
0
            memcpy(pw->ptr+1, dest, max_count);
236
0
            count = max_count;
237
0
        }
238
17.8M
    } else {
239
        /* not compressed -- just copy it */
240
236k
        count = block_length;
241
236k
        if (count > max_count)
242
0
            count = max_count;
243
236k
        memcpy(pw->ptr+1, block_data, count);
244
236k
    }
245
18.1M
    if (count < 0)
246
0
        count = 0;
247
18.1M
    pw->ptr += count;
248
18.1M
    process_interrupts(s->memory);
249
18.1M
    return status;
250
18.1M
}
251
252
static int
253
romfs_init(gx_io_device *iodev, gs_memory_t *mem)
254
89.2k
{
255
89.2k
    romfs_state *state = gs_alloc_struct(mem, romfs_state, &st_romfs_state,
256
89.2k
                                         "romfs_init(state)");
257
89.2k
    if (!state)
258
0
        return_error(gs_error_VMerror);
259
89.2k
    iodev->state = state;
260
89.2k
    return 0;
261
89.2k
}
262
263
static void
264
romfs_finit(gx_io_device *iodev, gs_memory_t *mem)
265
40.9k
{
266
40.9k
    gs_free_object(mem, iodev->state, "romfs_finit");
267
40.9k
    iodev->state = NULL;
268
40.9k
    return;
269
40.9k
}
270
271
static int
272
romfs_open_file(gx_io_device *iodev, const char *fname, uint namelen,
273
    const char *access, stream **ps, gs_memory_t *mem)
274
12.7M
{
275
12.7M
    extern const uint32_t *gs_romfs[];
276
12.7M
    int code;
277
12.7M
    const uint32_t *node_scan = gs_romfs[0], *node = NULL;
278
12.7M
    uint32_t filelen, blocks;
279
12.7M
    int i;
280
12.7M
    char *filename;
281
12.7M
    char fmode[4] = "\000\000\000\000";
282
283
    /* return an empty stream on error */
284
12.7M
    *ps = NULL;
285
286
    /* scan the inodes to find the requested file */
287
2.46G
    for (i=0; node_scan != 0; i++, node_scan = gs_romfs[i]) {
288
2.46G
        filelen = get_u32_big_endian(node_scan) & 0x7fffffff; /* ignore compression bit */
289
2.46G
        blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
290
2.46G
        filename = (char *)(&(node_scan[1+(2*blocks)]));
291
2.46G
        if ((namelen == strlen(filename)) &&
292
2.46G
            (strncmp(filename, fname, namelen) == 0)) {
293
4.40M
            node = node_scan;
294
4.40M
            break;
295
4.40M
        }
296
2.46G
    }
297
    /* inode points to the file (or NULL if not found */
298
12.7M
    if (node == NULL)
299
8.32M
        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
4.40M
    code = file_prepare_stream(fname, namelen, access, ROMFS_BLOCKSIZE+256, ps, fmode, mem);
306
4.40M
    if (code < 0)
307
7
        return code;
308
4.40M
    sread_block(*ps, (*ps)->cbuf, (*ps)->cbsize, node);
309
    /* return success */
310
4.40M
    return 0;
311
4.40M
}
312
313
static int
314
romfs_file_status(gx_io_device * iodev, const char *fname, struct stat *pstat)
315
2.03M
{
316
2.03M
    extern const uint32_t *gs_romfs[];
317
2.03M
    extern const time_t gs_romfs_buildtime;
318
2.03M
    const uint32_t *node_scan = gs_romfs[0], *node = NULL;
319
2.03M
    uint32_t filelen, blocks;
320
2.03M
    int i;
321
2.03M
    char *filename;
322
2.03M
    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
2.03M
    if (gs_romfs_buildtime == (time_t)0) {
329
0
        return_error(gs_error_unregistered);
330
0
    }
331
332
2.03M
    memset(pstat, 0, sizeof(struct stat));
333
    /* scan the inodes to find the requested file */
334
279M
    for (i=0; node_scan != 0; i++, node_scan = gs_romfs[i]) {
335
279M
        filelen = get_u32_big_endian(node_scan) & 0x7fffffff; /* ignore compression bit */
336
279M
        blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
337
279M
        filename = (char *)(&(node_scan[1+(2*blocks)]));
338
279M
        if ((namelen == strlen(filename)) &&
339
279M
            (strncmp(filename, fname, namelen) == 0)) {
340
1.38M
            node = node_scan;
341
1.38M
            break;
342
1.38M
        }
343
279M
    }
344
    /* inode points to the file (or NULL if not found */
345
2.03M
    if (node == NULL)
346
652k
        return_error(gs_error_undefinedfilename);
347
348
    /* fill in the values used by zstatus */
349
1.38M
    pstat->st_size = filelen;
350
1.38M
    pstat->st_mtime = gs_romfs_buildtime;
351
1.38M
    pstat->st_ctime = gs_romfs_buildtime;
352
1.38M
    return 0; /* success */
353
2.03M
}
354
355
static file_enum *
356
romfs_enumerate_files_init(gs_memory_t * mem, gx_io_device *iodev, const char *pat,
357
                           uint patlen)
358
267k
{
359
267k
    romfs_file_enum *penum = gs_alloc_struct(mem, romfs_file_enum, &st_romfs_file_enum,
360
267k
                                                        "romfs_enumerate_files_init(file_enum)");
361
267k
    if (penum == NULL)
362
0
        return NULL;
363
267k
    memset(penum, 0, sizeof(romfs_file_enum));
364
267k
    penum->pattern = (char *)gs_alloc_bytes(mem, patlen+1, "romfs_enumerate_file_init(pattern)");
365
267k
    penum->list_index = 0;    /* start at first node */
366
267k
    penum->memory = mem;
367
267k
    if (penum->pattern == NULL) {
368
0
        romfs_enumerate_close(mem, (file_enum *) penum);
369
0
        return NULL;
370
0
    }
371
267k
    memcpy(penum->pattern, pat, patlen);  /* Copy string to buffer */
372
267k
    penum->pattern[patlen]=0;     /* Terminate string */
373
374
267k
    return (file_enum *)penum;
375
267k
}
376
377
static void
378
romfs_enumerate_close(gs_memory_t * mem, file_enum *pfen)
379
267k
{
380
267k
    romfs_file_enum *penum = (romfs_file_enum *)pfen;
381
267k
    gs_memory_t *mem2 = penum->memory;
382
267k
    (void)mem;
383
384
267k
    if (penum->pattern)
385
267k
        gs_free_object(mem2, penum->pattern, "romfs_enum_init(pattern)");
386
267k
    gs_free_object(mem2, penum, "romfs_enum_init(romfs_enum)");
387
267k
}
388
389
static uint
390
romfs_enumerate_next(gs_memory_t * mem, file_enum *pfen, char *ptr, uint maxlen)
391
446k
{
392
446k
    extern const uint32_t *gs_romfs[];
393
446k
    romfs_file_enum *penum = (romfs_file_enum *)pfen;
394
446k
    (void)mem;
395
396
72.2M
    while (gs_romfs[penum->list_index] != 0) {
397
72.0M
        const uint32_t *node = gs_romfs[penum->list_index];
398
72.0M
        uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
399
72.0M
        uint32_t blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
400
72.0M
        char *filename = (char *)(&(node[1+(2*blocks)]));
401
402
72.0M
        penum->list_index++;    /* bump to next unconditionally */
403
72.0M
        if (string_match((byte *)filename, strlen(filename),
404
72.0M
                         (byte *)penum->pattern,
405
72.0M
                         strlen(penum->pattern), 0)) {
406
178k
            if (strlen(filename) < maxlen)
407
178k
                memcpy(ptr, filename, strlen(filename));
408
178k
            return strlen(filename);  /* if > maxlen, caller will detect rangecheck */
409
178k
        }
410
72.0M
    }
411
    /* ran off end of list, close the enum */
412
267k
    romfs_enumerate_close(mem, pfen);
413
267k
    return ~(uint)0;
414
446k
}
415
416
int
417
romfs_file_len(gs_memory_t * mem, const char *fname)
418
0
{
419
0
    extern const uint32_t *gs_romfs[];
420
0
    extern const time_t gs_romfs_buildtime;
421
0
    const uint32_t *node_scan = gs_romfs[0], *node = NULL;
422
0
    uint32_t filelen, blocks;
423
0
    int i;
424
0
    char *filename;
425
0
    uint namelen = strlen(fname);
426
427
    /* a build time of zero indicates we have the "dummy" romfs
428
     * used when COMPILE_INITS==0 - returning a specific error here
429
     * gives us a quick way to check for that.
430
     */
431
0
    if (gs_romfs_buildtime == (time_t)0) {
432
0
        return_error(gs_error_unregistered);
433
0
    }
434
435
    /* scan the inodes to find the requested file */
436
0
    for (i=0; node_scan != 0; i++, node_scan = gs_romfs[i]) {
437
0
        filelen = get_u32_big_endian(node_scan) & 0x7fffffff; /* ignore compression bit */
438
0
        blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
439
0
        filename = (char *)(&(node_scan[1+(2*blocks)]));
440
0
        if ((namelen == strlen(filename)) &&
441
0
            (strncmp(filename, fname, namelen) == 0)) {
442
0
            node = node_scan;
443
0
            break;
444
0
        }
445
0
    }
446
    /* inode points to the file (or NULL if not found */
447
0
    if (node == NULL)
448
0
        return_error(gs_error_undefinedfilename);
449
450
0
    return (int)filelen;
451
0
}