Coverage Report

Created: 2025-08-28 07:06

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