Coverage Report

Created: 2025-06-24 07:01

/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
7.47G
{
91
7.47G
    uint32_t v;
92
7.47G
    const unsigned char *c=(const unsigned char *)a;
93
94
7.47G
    v = (((uint32_t)c[0])<<24) | (((uint32_t)c[1])<<16) | (((uint32_t)c[2])<<8) | c[3];
95
7.47G
    return v;
96
7.47G
}
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
15.4M
{
112
15.4M
    static const stream_procs p = {
113
15.4M
         s_block_read_available, s_block_read_seek, s_std_read_reset,
114
15.4M
         s_std_read_flush, s_block_read_close, s_block_read_process,
115
         NULL   /* no read_switch */
116
15.4M
    };
117
15.4M
    s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek);
118
15.4M
    s->end_status = 0;
119
15.4M
    s->file = (gp_file *)node;  /* convenient place to put it for %rom% files */
120
15.4M
    s->file_modes = s->modes;
121
15.4M
    s->file_offset = 0;
122
15.4M
    s->file_limit = S_FILE_LIMIT_MAX;
123
15.4M
}
124
125
/* Return the number of available bytes */
126
static int
127
s_block_read_available(stream *s, gs_offset_t *pl)
128
13.6M
{
129
13.6M
    uint32_t *node = (uint32_t *)s->file;
130
13.6M
    uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
131
132
13.6M
    *pl = filelen - s->position - (sbufptr(s) - s->cbuf);
133
13.6M
    if (*pl == 0 && s->close_at_eod)  /* EOF */
134
5.99k
        *pl = -1;
135
13.6M
    return 0;
136
13.6M
}
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
40.7M
{
142
40.7M
    uint32_t *node = (uint32_t *)s->file;
143
40.7M
    uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
144
40.7M
    uint end = s->cursor.r.limit - s->cbuf + 1;
145
40.7M
    long offset = pos - s->position;
146
147
40.7M
    if (pos < 0 || pos > filelen)
148
5.99k
        return ERRC;
149
40.7M
    if (offset < 0 || offset > end) {
150
        /* Need to pull a different block into the buffer */
151
16.9M
        stream_cursor_write pw;
152
153
        /* buffer stays aligned to blocks */
154
16.9M
        offset = (s->file_offset + pos) % ROMFS_BLOCKSIZE;
155
16.9M
        s->position = pos - offset;
156
16.9M
        pw.ptr = s->cbuf - 1;
157
16.9M
        pw.limit = pw.ptr + s->cbsize;
158
16.9M
        s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
159
16.9M
        if ((s->end_status = s_block_read_process((stream_state *)s, NULL, &pw, 0)) == ERRC)
160
0
            return ERRC;
161
16.9M
        if (s->end_status == 1)
162
16.9M
            s->end_status = 0;
163
16.9M
        s->cursor.r.ptr = s->cbuf - 1;
164
16.9M
        s->cursor.r.limit = pw.ptr;   /* limit of the block just read */
165
16.9M
    }
166
    /* Now set the read pointer to the correct place in the buffer */
167
40.7M
    s->cursor.r.ptr = s->cbuf + offset - 1;
168
40.7M
    return 0;
169
40.7M
}
170
171
static int
172
s_block_read_close(stream * s)
173
15.4M
{
174
15.4M
    gs_free_object(s->memory, s->cbuf, "file_close(buffer)");
175
15.4M
    s->file = 0;      /* disconnect the node */
176
    /* Increment the IDs to prevent further access. */
177
15.4M
    s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
178
15.4M
    return 0;
179
15.4M
}
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
63.8M
{
185
63.8M
    int  code;
186
63.8M
    stream *s = (stream *)st; /* no separate state */
187
63.8M
    uint32_t *node = (uint32_t *)s->file;
188
63.8M
    uint max_count = pw->limit - pw->ptr;
189
63.8M
    int status = 1;
190
63.8M
    int compression = ((get_u32_big_endian(node) & 0x80000000) != 0) ? 1 : 0;
191
63.8M
    uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
192
63.8M
    uint32_t blocks = (filelen+ROMFS_BLOCKSIZE-1) / ROMFS_BLOCKSIZE;
193
63.8M
    uint32_t iblock = (s->position + s->file_offset + (s->cursor.r.limit + 1 - s->cbuf)) / ROMFS_BLOCKSIZE;
194
63.8M
    uint32_t block_length = get_u32_big_endian(node+1+(2*iblock));
195
63.8M
    uint32_t block_offset = get_u32_big_endian(node+2+(2*iblock));
196
63.8M
    unsigned const char *block_data = ((unsigned char *)node) + block_offset;
197
63.8M
    int count = iblock < (blocks - 1) ? ROMFS_BLOCKSIZE : filelen - (ROMFS_BLOCKSIZE * iblock);
198
199
63.8M
    if (s->position + (s->cursor.r.limit - s->cbuf + 1) >= filelen || block_data == NULL)
200
1.33M
        return EOFC;      /* at EOF */
201
62.5M
    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
62.5M
    if (compression) {
210
62.0M
        unsigned long buflen = ROMFS_BLOCKSIZE;
211
62.0M
        byte *dest = (pw->ptr + 1); /* destination for unpack */
212
62.0M
        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
62.0M
        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
21.4M
            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
21.4M
        }
230
        /* Decompress the data into this block */
231
62.0M
        code = uncompress (dest, &buflen, block_data, block_length);
232
62.0M
        if (code != Z_OK || count != buflen)
233
0
            return ERRC;
234
62.0M
        if (need_copy) {
235
0
            memcpy(pw->ptr+1, dest, max_count);
236
0
            count = max_count;
237
0
        }
238
62.0M
    } else {
239
        /* not compressed -- just copy it */
240
540k
        count = block_length;
241
540k
        if (count > max_count)
242
0
            count = max_count;
243
540k
        memcpy(pw->ptr+1, block_data, count);
244
540k
    }
245
62.5M
    if (count < 0)
246
0
        count = 0;
247
62.5M
    pw->ptr += count;
248
62.5M
    process_interrupts(s->memory);
249
62.5M
    return status;
250
62.5M
}
251
252
static int
253
romfs_init(gx_io_device *iodev, gs_memory_t *mem)
254
162k
{
255
162k
    romfs_state *state = gs_alloc_struct(mem, romfs_state, &st_romfs_state,
256
162k
                                         "romfs_init(state)");
257
162k
    if (!state)
258
0
        return_error(gs_error_VMerror);
259
162k
    iodev->state = state;
260
162k
    return 0;
261
162k
}
262
263
static void
264
romfs_finit(gx_io_device *iodev, gs_memory_t *mem)
265
71.8k
{
266
71.8k
    gs_free_object(mem, iodev->state, "romfs_finit");
267
71.8k
    iodev->state = NULL;
268
71.8k
    return;
269
71.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
38.0M
{
275
38.0M
    extern const uint32_t *gs_romfs[];
276
38.0M
    int code;
277
38.0M
    const uint32_t *node_scan = gs_romfs[0], *node = NULL;
278
38.0M
    uint32_t filelen, blocks;
279
38.0M
    int i;
280
38.0M
    char *filename;
281
38.0M
    char fmode[4] = "\000\000\000\000";
282
283
    /* return an empty stream on error */
284
38.0M
    *ps = NULL;
285
286
    /* scan the inodes to find the requested file */
287
6.53G
    for (i=0; node_scan != 0; i++, node_scan = gs_romfs[i]) {
288
6.51G
        filelen = get_u32_big_endian(node_scan) & 0x7fffffff; /* ignore compression bit */
289
6.51G
        blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
290
6.51G
        filename = (char *)(&(node_scan[1+(2*blocks)]));
291
6.51G
        if ((namelen == strlen(filename)) &&
292
6.51G
            (strncmp(filename, fname, namelen) == 0)) {
293
15.4M
            node = node_scan;
294
15.4M
            break;
295
15.4M
        }
296
6.51G
    }
297
    /* inode points to the file (or NULL if not found */
298
38.0M
    if (node == NULL)
299
22.5M
        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
15.4M
    code = file_prepare_stream(fname, namelen, access, ROMFS_BLOCKSIZE+256, ps, fmode, mem);
306
15.4M
    if (code < 0)
307
0
        return code;
308
15.4M
    (*ps)->modes = s_mode_read;
309
15.4M
    sread_block(*ps, (*ps)->cbuf, (*ps)->cbsize, node);
310
    /* return success */
311
15.4M
    return 0;
312
15.4M
}
313
314
static int
315
romfs_file_status(gx_io_device * iodev, const char *fname, struct stat *pstat)
316
3.91M
{
317
3.91M
    extern const uint32_t *gs_romfs[];
318
3.91M
    extern const time_t gs_romfs_buildtime;
319
3.91M
    const uint32_t *node_scan = gs_romfs[0], *node = NULL;
320
3.91M
    uint32_t filelen, blocks;
321
3.91M
    int i;
322
3.91M
    char *filename;
323
3.91M
    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
3.91M
    if (gs_romfs_buildtime == (time_t)0) {
330
0
        return_error(gs_error_unregistered);
331
0
    }
332
333
3.91M
    memset(pstat, 0, sizeof(struct stat));
334
    /* scan the inodes to find the requested file */
335
524M
    for (i=0; node_scan != 0; i++, node_scan = gs_romfs[i]) {
336
523M
        filelen = get_u32_big_endian(node_scan) & 0x7fffffff; /* ignore compression bit */
337
523M
        blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
338
523M
        filename = (char *)(&(node_scan[1+(2*blocks)]));
339
523M
        if ((namelen == strlen(filename)) &&
340
523M
            (strncmp(filename, fname, namelen) == 0)) {
341
2.65M
            node = node_scan;
342
2.65M
            break;
343
2.65M
        }
344
523M
    }
345
    /* inode points to the file (or NULL if not found */
346
3.91M
    if (node == NULL)
347
1.26M
        return_error(gs_error_undefinedfilename);
348
349
    /* fill in the values used by zstatus */
350
2.65M
    pstat->st_size = filelen;
351
2.65M
    pstat->st_mtime = gs_romfs_buildtime;
352
2.65M
    pstat->st_ctime = gs_romfs_buildtime;
353
2.65M
    return 0; /* success */
354
3.91M
}
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
487k
{
360
487k
    romfs_file_enum *penum = gs_alloc_struct(mem, romfs_file_enum, &st_romfs_file_enum,
361
487k
                                                        "romfs_enumerate_files_init(file_enum)");
362
487k
    if (penum == NULL)
363
0
        return NULL;
364
487k
    memset(penum, 0, sizeof(romfs_file_enum));
365
487k
    penum->pattern = (char *)gs_alloc_bytes(mem, patlen+1, "romfs_enumerate_file_init(pattern)");
366
487k
    penum->list_index = 0;    /* start at first node */
367
487k
    penum->memory = mem;
368
487k
    if (penum->pattern == NULL) {
369
0
        romfs_enumerate_close(mem, (file_enum *) penum);
370
0
        return NULL;
371
0
    }
372
487k
    memcpy(penum->pattern, pat, patlen);  /* Copy string to buffer */
373
487k
    penum->pattern[patlen]=0;     /* Terminate string */
374
375
487k
    return (file_enum *)penum;
376
487k
}
377
378
static void
379
romfs_enumerate_close(gs_memory_t * mem, file_enum *pfen)
380
487k
{
381
487k
    romfs_file_enum *penum = (romfs_file_enum *)pfen;
382
487k
    gs_memory_t *mem2 = penum->memory;
383
487k
    (void)mem;
384
385
487k
    if (penum->pattern)
386
487k
        gs_free_object(mem2, penum->pattern, "romfs_enum_init(pattern)");
387
487k
    gs_free_object(mem2, penum, "romfs_enum_init(romfs_enum)");
388
487k
}
389
390
static uint
391
romfs_enumerate_next(gs_memory_t * mem, file_enum *pfen, char *ptr, uint maxlen)
392
812k
{
393
812k
    extern const uint32_t *gs_romfs[];
394
812k
    romfs_file_enum *penum = (romfs_file_enum *)pfen;
395
812k
    (void)mem;
396
397
131M
    while (gs_romfs[penum->list_index] != 0) {
398
131M
        const uint32_t *node = gs_romfs[penum->list_index];
399
131M
        uint32_t filelen = get_u32_big_endian(node) & 0x7fffffff; /* ignore compression bit */
400
131M
        uint32_t blocks = (filelen+ROMFS_BLOCKSIZE-1)/ ROMFS_BLOCKSIZE;
401
131M
        char *filename = (char *)(&(node[1+(2*blocks)]));
402
403
131M
        penum->list_index++;    /* bump to next unconditionally */
404
131M
        if (string_match((byte *)filename, strlen(filename),
405
131M
                         (byte *)penum->pattern,
406
131M
                         strlen(penum->pattern), 0)) {
407
324k
            if (strlen(filename) < maxlen)
408
324k
                memcpy(ptr, filename, strlen(filename));
409
324k
            return strlen(filename);  /* if > maxlen, caller will detect rangecheck */
410
324k
        }
411
131M
    }
412
    /* ran off end of list, close the enum */
413
487k
    romfs_enumerate_close(mem, pfen);
414
487k
    return ~(uint)0;
415
812k
}
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
}