Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gsioram.c
Line
Count
Source
1
/* Copyright (C) 2001-2025 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
/* %ram% file device implementation */
17
18
/*
19
 *  This file implements a simple ram-based file system.
20
 *
21
 * The fs has no subdirs, only a root directory. Files are stored as a
22
 * resizable array of pointers to blocks.  Timestamps are not implemented
23
 * for performance reasons.
24
 *
25
 * The implementation is in two parts - a ramfs interface that works
26
 * mostly like the unix file system calls, and a layer to hook this up
27
 * to ghostscript.
28
 *
29
 * Macros define an upper limit on the number of blocks a ram device
30
 * can take up, and (in ramfs.c) the size of each block.
31
 *
32
 * Routines for the gs stream interface were graciously stolen from
33
 * sfxstdio.c et al.
34
 */
35
36
#include "string_.h"
37
#include "unistd_.h"
38
#include "gx.h"
39
#include "gserrors.h"
40
#include "gp.h"
41
#include "gscdefs.h"
42
#include "gsparam.h"
43
#include "gsstruct.h"
44
#include "gxiodev.h"
45
#include "gsutil.h"
46
#include "stream.h"
47
#include "ramfs.h"
48
49
/* Function prototypes */
50
static iodev_proc_init(iodev_ram_init);
51
static iodev_proc_finit(iodev_ram_finit);
52
static iodev_proc_open_file(ram_open_file);
53
static iodev_proc_delete_file(ram_delete);
54
static iodev_proc_rename_file(ram_rename);
55
static iodev_proc_file_status(ram_status);
56
static iodev_proc_enumerate_files(ram_enumerate_init);
57
static iodev_proc_enumerate_next(ram_enumerate_next);
58
static iodev_proc_enumerate_close(ram_enumerate_close);
59
static iodev_proc_get_params(ram_get_params);
60
static void ram_finalize(const gs_memory_t *memory, void * vptr);
61
62
const gx_io_device gs_iodev_ram = {
63
    "%ram%", "FileSystem", {
64
        iodev_ram_init, iodev_ram_finit, iodev_no_open_device,
65
        ram_open_file, iodev_no_fopen, iodev_no_fclose,
66
        ram_delete, ram_rename, ram_status,
67
        ram_enumerate_init, ram_enumerate_next, ram_enumerate_close,
68
        ram_get_params, iodev_no_put_params
69
    },
70
    NULL,
71
    NULL
72
};
73
74
typedef struct ramfs_state_s {
75
    gs_memory_t *memory;
76
    ramfs* fs;
77
} ramfs_state;
78
79
385k
#define GETRAMFS(state) (((ramfs_state*)(state))->fs)
80
81
gs_private_st_simple_final(st_ramfs_state, struct ramfs_state_s, "ramfs_state", ram_finalize);
82
83
typedef struct gsram_enum_s {
84
    char *pattern;
85
    ramfs_enum* e;
86
    gs_memory_t *memory;
87
} gsram_enum;
88
89
gs_private_st_ptrs2(st_gsram_enum, struct gsram_enum_s, "gsram_enum",
90
    gsram_enum_enum_ptrs, gsram_enum_reloc_ptrs, pattern, memory);
91
92
/* could make this runtime configurable later.  It doesn't allocate
93
   all the blocks in one go so it's not critical */
94
192k
#define MAXBLOCKS 2000000
95
96
0
#define DEFAULT_BUFFER_SIZE 2048
97
98
/* stream stuff */
99
100
static int
101
s_ram_available(stream *, gs_offset_t *),
102
 s_ram_read_seek(stream *, gs_offset_t),
103
 s_ram_read_close(stream *),
104
 s_ram_read_process(stream_state *, stream_cursor_read *,
105
     stream_cursor_write *, bool);
106
static int
107
s_ram_write_seek(stream *, gs_offset_t),
108
 s_ram_write_flush(stream *),
109
 s_ram_write_close(stream *),
110
 s_ram_write_process(stream_state *, stream_cursor_read *,
111
     stream_cursor_write *, bool);
112
static int
113
s_ram_switch(stream *, bool);
114
115
static int
116
0
ramfs_errno_to_code(int error_number) {
117
0
    switch (error_number) {
118
0
    case RAMFS_NOTFOUND:
119
0
        return_error(gs_error_undefinedfilename);
120
0
    case RAMFS_NOACCESS:
121
0
        return_error(gs_error_invalidfileaccess);
122
0
    case RAMFS_NOMEM:
123
0
        return_error(gs_error_VMerror);
124
0
    case RAMFS_BADRANGE:
125
0
        return_error(gs_error_rangecheck);
126
0
    case RAMFS_DELETEOPENFILE:
127
0
        return_error(gs_error_ioerror);
128
    /* just in case */
129
0
    default:
130
0
        return_error(gs_error_ioerror);
131
0
    }
132
0
}
133
134
static void
135
sread_ram(register stream * s, ramhandle * file, byte * buf, uint len),
136
 swrite_ram(register stream * s, ramhandle * file, byte * buf, uint len),
137
 sappend_ram(register stream * s, ramhandle * file, byte * buf, uint len);
138
139
static int
140
ram_open_file(gx_io_device * iodev, const char *fname, uint len,
141
    const char *file_access, stream ** ps, gs_memory_t * mem)
142
0
{
143
0
    int code = 0;
144
0
    ramhandle * file;
145
0
    char fmode[4];  /* r/w/a, [+], [b], null */
146
0
    int openmode=RAMFS_READ;
147
0
    ramfs * fs;
148
0
    char * namestr = NULL;
149
150
    /* Is there a more efficient way to do this? */
151
0
    namestr = (char *)gs_alloc_bytes(mem, len + 1, "temporary filename string");
152
0
    if(!namestr)
153
0
        return_error(gs_error_VMerror);
154
0
    strncpy(namestr,fname,len);
155
0
    namestr[len] = 0;
156
157
    /* Make sure iodev is valid, and we have initialised the RAM file system (state != NULL) */
158
0
    if (iodev == NULL || iodev->state == NULL) {/*iodev = iodev_default;*/
159
0
        gs_free_object(mem, namestr, "free temporary filename string");
160
0
        return gs_note_error(gs_error_invalidaccess);
161
0
    }
162
0
    fs = GETRAMFS(iodev->state);
163
0
    code = file_prepare_stream(fname, len, file_access, DEFAULT_BUFFER_SIZE,
164
0
    ps, fmode, mem
165
0
    );
166
0
    if (code < 0) goto error;
167
0
    if (fname == 0) {
168
0
        gs_free_object(mem, namestr, "free temporary filename string");
169
0
        return 0;
170
0
    }
171
172
0
    switch (fmode[0]) {
173
0
        case 'a':
174
0
          openmode = RAMFS_WRITE | RAMFS_APPEND;
175
0
          break;
176
0
        case 'r':
177
0
          openmode = RAMFS_READ;
178
0
          if (fmode[1] == '+')
179
0
            openmode |= RAMFS_WRITE;
180
0
          break;
181
0
        case 'w':
182
0
          openmode |= RAMFS_WRITE | RAMFS_TRUNC | RAMFS_CREATE;
183
0
          if (fmode[1] == '+')
184
0
             openmode |= RAMFS_READ;
185
0
    }
186
187
    /* For now, we cheat here in the same way that sfxstdio.c et al cheat -
188
       append mode is faked by opening in write mode and seeking to EOF just
189
       once. This is different from unix semantics, which seeks atomically
190
       before each write, and is actually useful as a distinct mode. */
191
    /* if (fmode[0] == 'a') openmode |= RAMFS_APPEND; */
192
193
0
    file = ramfs_open(mem, fs,namestr,openmode);
194
0
    if(!file) { code = ramfs_errno_to_code(ramfs_error(fs)); goto error; }
195
196
0
    switch (fmode[0]) {
197
0
    case 'a':
198
0
    sappend_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
199
0
    break;
200
0
    case 'r':
201
0
    sread_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
202
0
    break;
203
0
    case 'w':
204
0
    swrite_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
205
0
    }
206
0
    if (fmode[1] == '+') {
207
0
      (*ps)->modes = (*ps)->file_modes |= s_mode_read | s_mode_write;
208
0
    }
209
0
    (*ps)->save_close = (*ps)->procs.close;
210
0
    (*ps)->procs.close = file_close_file;
211
0
 error:
212
0
    gs_free_object(mem, namestr, "free temporary filename string");
213
    /* XXX free stream stuff? */
214
0
    return code;
215
0
}
216
217
/* Initialize a stream for reading an OS file. */
218
static void
219
sread_ram(register stream * s, ramhandle * file, byte * buf, uint len)
220
0
{
221
0
    static const stream_procs p = {
222
0
    s_ram_available, s_ram_read_seek, s_std_read_reset,
223
0
    s_std_read_flush, s_ram_read_close, s_ram_read_process,
224
0
    s_ram_switch
225
0
    };
226
227
0
    s_std_init(s, buf, len, &p,s_mode_read + s_mode_seek);
228
0
    s->file = (gp_file *)file;
229
0
    s->file_modes = s->modes;
230
0
    s->file_offset = 0;
231
0
    ramfile_seek(file, 0, RAMFS_SEEK_END);
232
0
    s->file_limit = ramfile_tell(file);
233
0
    ramfile_seek(file, 0, RAMFS_SEEK_SET);
234
0
}
235
236
/* Procedures for reading from a file */
237
static int
238
s_ram_available(register stream * s, gs_offset_t *pl)
239
0
{
240
0
    long max_avail = s->file_limit - stell(s);
241
242
0
    *pl = max_avail;
243
0
    if(*pl == 0 && ramfile_eof((ramhandle*)s->file))
244
0
    *pl = -1;        /* EOF */
245
0
    return 0;
246
0
}
247
248
static int
249
s_ram_read_seek(register stream * s, gs_offset_t pos)
250
0
{
251
0
    uint end = s->cursor.r.limit - s->cbuf + 1;
252
0
    long offset = pos - s->position;
253
254
0
    if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
255
0
        s->cursor.r.ptr = s->cbuf + offset - 1;
256
0
        return 0;
257
0
    }
258
259
0
    if (pos < 0 || pos > s->file_limit || s->file == NULL ||
260
0
        ramfile_seek((ramhandle*)s->file, s->file_offset + pos, RAMFS_SEEK_SET) != 0)
261
0
        return ERRC;
262
263
0
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
264
0
    s->end_status = 0;
265
0
    s->position = pos;
266
0
    return 0;
267
0
}
268
static int
269
s_ram_read_close(stream * s)
270
0
{
271
0
    ramhandle *file = (ramhandle*)s->file;
272
273
0
    if (file != 0) {
274
0
    s->file = 0;
275
0
    ramfile_close(file);
276
0
    }
277
0
    return 0;
278
0
}
279
280
/*
281
 * Process a buffer for a file reading stream.
282
 * This is the first stream in the pipeline, so pr is irrelevant.
283
 */
284
static int
285
s_ram_read_process(stream_state * st, stream_cursor_read * ignore_pr,
286
    stream_cursor_write * pw, bool last)
287
0
{
288
0
    stream *s = (stream *)st;    /* no separate state */
289
0
    ramhandle *file = (ramhandle*)s->file;
290
0
    uint max_count = pw->limit - pw->ptr;
291
0
    int status = 1;
292
0
    int count;
293
294
0
    if (s->file_limit < S_FILE_LIMIT_MAX) {
295
0
    long limit_count = s->file_offset + s->file_limit -
296
0
    ramfile_tell(file);
297
298
0
    if (max_count > limit_count)
299
0
        max_count = limit_count, status = EOFC;
300
0
    }
301
0
    count = ramfile_read(file,pw->ptr + 1, max_count);
302
0
    if (count < 0) return ERRC;
303
0
    pw->ptr += count;
304
    /*    process_interrupts(s->memory); */
305
0
    return ramfile_eof(file) ? EOFC : status;
306
0
}
307
308
/* ------ File writing ------ */
309
310
/* Initialize a stream for writing a file. */
311
static void
312
swrite_ram(register stream * s, ramhandle * file, byte * buf, uint len)
313
0
{
314
0
    static const stream_procs p = {
315
0
    s_std_noavailable, s_ram_write_seek, s_std_write_reset,
316
0
    s_ram_write_flush, s_ram_write_close, s_ram_write_process,
317
0
    s_ram_switch
318
0
    };
319
320
0
    s_std_init(s, buf, len, &p, s_mode_write + s_mode_seek);
321
0
    s->file = (gp_file *)file;
322
0
    s->file_modes = s->modes;
323
0
    s->file_offset = 0;        /* in case we switch to reading later */
324
0
    s->file_limit = S_FILE_LIMIT_MAX;
325
0
}
326
327
/* Initialize for appending to a file. */
328
static void
329
sappend_ram(register stream * s, ramhandle * file, byte * buf, uint len)
330
0
{
331
0
    swrite_ram(s, file, buf, len);
332
0
    s->modes = s_mode_write + s_mode_append;    /* no seek */
333
0
    s->file_modes = s->modes;
334
0
    ramfile_seek(file,0,RAMFS_SEEK_END);
335
0
    s->position = ramfile_tell(file);
336
0
}
337
338
/* Procedures for writing on a file */
339
static int
340
s_ram_write_seek(stream * s, gs_offset_t pos)
341
0
{
342
    /* We must flush the buffer to reposition. */
343
0
    int code = sflush(s);
344
345
0
    if (code < 0) return code;
346
0
    if (pos < 0 || ramfile_seek((ramhandle*)s->file, pos, RAMFS_SEEK_SET) != 0)
347
0
        return ERRC;
348
0
    s->position = pos;
349
0
    return 0;
350
0
}
351
352
static int
353
s_ram_write_flush(register stream * s)
354
0
{
355
0
    int result = s_process_write_buf(s, false);
356
0
    return result;
357
0
}
358
359
static int
360
s_ram_write_close(register stream * s)
361
0
{
362
0
    s_process_write_buf(s, true);
363
0
    return s_ram_read_close(s);
364
0
}
365
366
/*
367
 * Process a buffer for a file writing stream.
368
 * This is the last stream in the pipeline, so pw is irrelevant.
369
 */
370
static int
371
s_ram_write_process(stream_state * st, stream_cursor_read * pr,
372
    stream_cursor_write * ignore_pw, bool last)
373
0
{
374
0
    uint count = pr->limit - pr->ptr;
375
376
0
    ramhandle *file = (ramhandle*)((stream *) st)->file;
377
0
    int written = ramfile_write(file,pr->ptr + 1, count);
378
379
0
    if (written < 0) return ERRC;
380
0
    pr->ptr += written;
381
0
    return 0;
382
0
}
383
384
static int
385
s_ram_switch(stream * s, bool writing)
386
0
{
387
0
    uint modes = s->file_modes;
388
0
    ramhandle *file = (ramhandle*)s->file;
389
0
    long pos;
390
391
0
    if (writing) {
392
0
    if (!(s->file_modes & s_mode_write)) return ERRC;
393
0
    pos = stell(s);
394
0
    ramfile_seek(file, pos, RAMFS_SEEK_SET);
395
0
    if (modes & s_mode_append) {
396
0
        sappend_ram(s, file, s->cbuf, s->cbsize);    /* sets position */
397
0
    } else {
398
0
        swrite_ram(s, file, s->cbuf, s->cbsize);
399
0
        s->position = pos;
400
0
    }
401
0
    s->modes = modes;
402
0
    } else {
403
0
    if (!(s->file_modes & s_mode_read)) return ERRC;
404
0
    pos = stell(s);
405
0
    if (sflush(s) < 0) return ERRC;
406
0
    sread_ram(s, file, s->cbuf, s->cbsize);
407
0
    s->modes |= modes & s_mode_append;    /* don't lose append info */
408
0
    s->position = pos;
409
0
    }
410
0
    s->file_modes = modes;
411
0
    return 0;
412
0
}
413
414
415
/* gx_io_device stuff */
416
417
static int
418
iodev_ram_init(gx_io_device * iodev, gs_memory_t * mem)
419
192k
{
420
192k
    ramfs* fs = ramfs_new(mem, MAXBLOCKS);
421
192k
    ramfs_state* state = gs_alloc_struct(mem, ramfs_state, &st_ramfs_state,
422
192k
    "ramfs_init(state)"
423
192k
    );
424
192k
    if (fs && state) {
425
192k
    state->fs = fs;
426
192k
    state->memory = mem;
427
192k
    iodev->state = state;
428
192k
    return 0;
429
192k
    }
430
0
    if(fs) ramfs_destroy(mem, fs);
431
0
    if(state) gs_free_object(mem,state,"iodev_ram_init(state)");
432
0
    return_error(gs_error_VMerror);
433
192k
}
434
435
static void
436
iodev_ram_finit(gx_io_device * iodev, gs_memory_t * mem)
437
58.8k
{
438
58.8k
    ramfs_state *state = (ramfs_state *)iodev->state;
439
58.8k
    if (state != NULL)
440
58.8k
    {
441
58.8k
        iodev->state = NULL;
442
58.8k
        gs_free_object(state->memory, state, "iodev_ram_finit");
443
58.8k
    }
444
58.8k
    return;
445
58.8k
}
446
447
static void
448
ram_finalize(const gs_memory_t *memory, void * vptr)
449
192k
{
450
192k
    ramfs* fs = GETRAMFS((ramfs_state*)vptr);
451
192k
    ramfs_destroy((gs_memory_t *)memory, fs);
452
192k
    GETRAMFS((ramfs_state*)vptr) = NULL;
453
192k
}
454
455
static int
456
ram_delete(gx_io_device * iodev, const char *fname)
457
0
{
458
0
    ramfs* fs;
459
460
0
    if (iodev->state == NULL)
461
0
        return_error(gs_error_ioerror);
462
0
    else
463
0
        fs = GETRAMFS(iodev->state);
464
465
0
    if(ramfs_unlink(fs,fname)!=0) {
466
0
    return_error(ramfs_errno_to_code(ramfs_error(fs)));
467
0
    }
468
0
    return 0;
469
0
}
470
471
static int
472
ram_rename(gx_io_device * iodev, const char *from, const char *to)
473
0
{
474
0
    ramfs* fs;
475
476
0
    if (iodev->state == NULL)
477
0
        return_error(gs_error_ioerror);
478
0
    else
479
0
        fs = GETRAMFS(iodev->state);
480
481
0
    if(ramfs_rename(fs,from,to)!=0) {
482
0
    return_error(ramfs_errno_to_code(ramfs_error(fs)));
483
0
    }
484
0
    return 0;
485
0
}
486
487
static int
488
ram_status(gx_io_device * iodev, const char *fname, struct stat *pstat)
489
0
{
490
0
    ramhandle * f;
491
0
    ramfs* fs;
492
493
0
    if (iodev->state == NULL)
494
0
        return_error(gs_error_ioerror);
495
0
    else
496
0
        fs = GETRAMFS(iodev->state);
497
498
0
    f = ramfs_open(((ramfs_state *)iodev->state)->memory, fs,fname,RAMFS_READ);
499
0
    if(!f) return_error(ramfs_errno_to_code(ramfs_error(fs)));
500
501
0
    memset(pstat, 0, sizeof(*pstat));
502
0
    pstat->st_size = ramfile_size(f);
503
    /* The Windows definition of struct stat doesn't include a st_blocks member
504
    pstat->st_blocks = (pstat->st_size+blocksize-1)/blocksize;*/
505
    /* XXX set mtime & ctime */
506
0
    ramfile_close(f);
507
0
    return 0;
508
0
}
509
510
static file_enum *
511
ram_enumerate_init(gs_memory_t * mem, gx_io_device *iodev, const char *pat,
512
                   uint patlen)
513
0
{
514
0
    gsram_enum * penum = gs_alloc_struct(
515
0
    mem, gsram_enum, &st_gsram_enum,
516
0
    "ram_enumerate_files_init(file_enum)"
517
0
    );
518
0
    char *pattern = (char *)gs_alloc_bytes(
519
0
    mem, patlen+1, "ram_enumerate_file_init(pattern)"
520
0
    );
521
522
0
    ramfs_enum * e;
523
524
0
    if (iodev->state == NULL)
525
0
        return NULL;
526
0
    else
527
0
        e = ramfs_enum_new(GETRAMFS(iodev->state));
528
529
0
    if(penum && pattern && e) {
530
0
    memcpy(pattern, pat, patlen);
531
0
    pattern[patlen]=0;
532
533
0
    penum->memory = mem;
534
0
    penum->pattern = pattern;
535
0
    penum->e = e;
536
0
    return (file_enum *)penum;
537
0
    }
538
0
    if (penum) gs_free_object(mem,penum,"ramfs_enum_init(ramfs_enum)");
539
0
    if (pattern)
540
0
    gs_free_object(mem, pattern, "ramfs_enum_init(pattern)");
541
0
    if(e) ramfs_enum_end(e);
542
0
    return NULL;
543
0
}
544
545
static void
546
ram_enumerate_close(gs_memory_t * mem, file_enum *pfen)
547
0
{
548
0
    gsram_enum *penum = (gsram_enum *)pfen;
549
0
    gs_memory_t *mem2 = penum->memory;
550
0
    (void)mem;
551
552
0
    ramfs_enum_end(penum->e);
553
0
    gs_free_object(mem2, penum->pattern, "ramfs_enum_init(pattern)");
554
0
    gs_free_object(mem2, penum, "ramfs_enum_init(ramfs_enum)");
555
0
}
556
557
static uint
558
ram_enumerate_next(gs_memory_t * mem, file_enum *pfen, char *ptr, uint maxlen)
559
0
{
560
0
    gsram_enum *penum = (gsram_enum *)pfen;
561
562
0
    char * filename;
563
0
    while ((filename = ramfs_enum_next(penum->e))) {
564
0
    if (string_match((byte *)filename, strlen(filename),
565
0
        (byte *)penum->pattern,
566
0
        strlen(penum->pattern), 0)) {
567
0
        if (strlen(filename) < maxlen)
568
0
        memcpy(ptr, filename, strlen(filename));
569
0
        return strlen(filename);    /* if > maxlen, caller will detect rangecheck */
570
0
    }
571
0
    }
572
    /* ran off end of list, close the enum */
573
0
    ram_enumerate_close(mem, pfen);
574
0
    return ~(uint)0;
575
0
}
576
577
static int
578
ram_get_params(gx_io_device * iodev, gs_param_list * plist)
579
0
{
580
0
    int code;
581
0
    int i0 = 0, so = 1;
582
0
    bool btrue = true, bfalse = false;
583
0
    ramfs* fs;
584
585
0
    int BlockSize;
586
0
    long Free, LogicalSize;
587
588
0
    if (iodev->state == NULL)
589
0
        return_error(gs_error_ioerror);
590
0
    else
591
0
        fs = GETRAMFS(iodev->state);
592
593
0
    BlockSize = ramfs_blocksize(fs);
594
0
    LogicalSize = MAXBLOCKS;
595
0
    Free = ramfs_blocksfree(fs);
596
597
0
    if (
598
0
    (code = param_write_bool(plist, "HasNames",        &btrue)) < 0 ||
599
0
    (code = param_write_int (plist, "BlockSize",       &BlockSize)) < 0 ||
600
0
    (code = param_write_long(plist, "Free",            &Free)) < 0 ||
601
0
    (code = param_write_int (plist, "InitializeAction",&i0)) < 0 ||
602
0
    (code = param_write_bool(plist, "Mounted",         &btrue)) < 0 ||
603
0
    (code = param_write_bool(plist, "Removable",       &bfalse)) < 0 ||
604
0
    (code = param_write_bool(plist, "Searchable",      &btrue)) < 0 ||
605
0
    (code = param_write_int (plist, "SearchOrder",     &so)) < 0 ||
606
0
    (code = param_write_bool(plist, "Writeable",       &btrue)) < 0 ||
607
0
    (code = param_write_long(plist, "LogicalSize",     &LogicalSize)) < 0
608
0
    )
609
0
    return code;
610
0
    return 0;
611
0
}