Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gsioram.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
/* %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
324k
#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
162k
#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
    /* just in case */
127
0
    default:
128
0
        return_error(gs_error_ioerror);
129
0
    }
130
0
}
131
132
static void
133
sread_ram(register stream * s, ramhandle * file, byte * buf, uint len),
134
 swrite_ram(register stream * s, ramhandle * file, byte * buf, uint len),
135
 sappend_ram(register stream * s, ramhandle * file, byte * buf, uint len);
136
137
static int
138
ram_open_file(gx_io_device * iodev, const char *fname, uint len,
139
    const char *file_access, stream ** ps, gs_memory_t * mem)
140
0
{
141
0
    int code = 0;
142
0
    ramhandle * file;
143
0
    char fmode[4];  /* r/w/a, [+], [b], null */
144
0
    int openmode=RAMFS_READ;
145
0
    ramfs * fs;
146
0
    char * namestr = NULL;
147
148
    /* Is there a more efficient way to do this? */
149
0
    namestr = (char *)gs_alloc_bytes(mem, len + 1, "temporary filename string");
150
0
    if(!namestr)
151
0
        return_error(gs_error_VMerror);
152
0
    strncpy(namestr,fname,len);
153
0
    namestr[len] = 0;
154
155
    /* Make sure iodev is valid, and we have initialised the RAM file system (state != NULL) */
156
0
    if (iodev == NULL || iodev->state == NULL) {/*iodev = iodev_default;*/
157
0
        gs_free_object(mem, namestr, "free temporary filename string");
158
0
        return gs_note_error(gs_error_invalidaccess);
159
0
    }
160
0
    fs = GETRAMFS(iodev->state);
161
0
    code = file_prepare_stream(fname, len, file_access, DEFAULT_BUFFER_SIZE,
162
0
    ps, fmode, mem
163
0
    );
164
0
    if (code < 0) goto error;
165
0
    if (fname == 0) {
166
0
        gs_free_object(mem, namestr, "free temporary filename string");
167
0
        return 0;
168
0
    }
169
170
0
    switch (fmode[0]) {
171
0
        case 'a':
172
0
          openmode = RAMFS_WRITE | RAMFS_APPEND;
173
0
          break;
174
0
        case 'r':
175
0
          openmode = RAMFS_READ;
176
0
          if (fmode[1] == '+')
177
0
            openmode |= RAMFS_WRITE;
178
0
          break;
179
0
        case 'w':
180
0
          openmode |= RAMFS_WRITE | RAMFS_TRUNC | RAMFS_CREATE;
181
0
          if (fmode[1] == '+')
182
0
             openmode |= RAMFS_READ;
183
0
    }
184
185
    /* For now, we cheat here in the same way that sfxstdio.c et al cheat -
186
       append mode is faked by opening in write mode and seeking to EOF just
187
       once. This is different from unix semantics, which seeks atomically
188
       before each write, and is actually useful as a distinct mode. */
189
    /* if (fmode[0] == 'a') openmode |= RAMFS_APPEND; */
190
191
0
    file = ramfs_open(mem, fs,namestr,openmode);
192
0
    if(!file) { code = ramfs_errno_to_code(ramfs_error(fs)); goto error; }
193
194
0
    switch (fmode[0]) {
195
0
    case 'a':
196
0
    sappend_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
197
0
    break;
198
0
    case 'r':
199
0
    sread_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
200
0
    break;
201
0
    case 'w':
202
0
    swrite_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
203
0
    }
204
0
    if (fmode[1] == '+') {
205
0
      (*ps)->modes = (*ps)->file_modes |= s_mode_read | s_mode_write;
206
0
    }
207
0
    (*ps)->save_close = (*ps)->procs.close;
208
0
    (*ps)->procs.close = file_close_file;
209
0
 error:
210
0
    gs_free_object(mem, namestr, "free temporary filename string");
211
    /* XXX free stream stuff? */
212
0
    return code;
213
0
}
214
215
/* Initialize a stream for reading an OS file. */
216
static void
217
sread_ram(register stream * s, ramhandle * file, byte * buf, uint len)
218
0
{
219
0
    static const stream_procs p = {
220
0
    s_ram_available, s_ram_read_seek, s_std_read_reset,
221
0
    s_std_read_flush, s_ram_read_close, s_ram_read_process,
222
0
    s_ram_switch
223
0
    };
224
225
0
    s_std_init(s, buf, len, &p,s_mode_read + s_mode_seek);
226
0
    s->file = (gp_file *)file;
227
0
    s->file_modes = s->modes;
228
0
    s->file_offset = 0;
229
0
    ramfile_seek(file, 0, RAMFS_SEEK_END);
230
0
    s->file_limit = ramfile_tell(file);
231
0
    ramfile_seek(file, 0, RAMFS_SEEK_SET);
232
0
}
233
234
/* Procedures for reading from a file */
235
static int
236
s_ram_available(register stream * s, gs_offset_t *pl)
237
0
{
238
0
    long max_avail = s->file_limit - stell(s);
239
240
0
    *pl = max_avail;
241
0
    if(*pl == 0 && ramfile_eof((ramhandle*)s->file))
242
0
    *pl = -1;        /* EOF */
243
0
    return 0;
244
0
}
245
246
static int
247
s_ram_read_seek(register stream * s, gs_offset_t pos)
248
0
{
249
0
    uint end = s->cursor.r.limit - s->cbuf + 1;
250
0
    long offset = pos - s->position;
251
252
0
    if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
253
0
        s->cursor.r.ptr = s->cbuf + offset - 1;
254
0
        return 0;
255
0
    }
256
257
0
    if (pos < 0 || pos > s->file_limit || s->file == NULL ||
258
0
        ramfile_seek((ramhandle*)s->file, s->file_offset + pos, RAMFS_SEEK_SET) != 0)
259
0
        return ERRC;
260
261
0
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
262
0
    s->end_status = 0;
263
0
    s->position = pos;
264
0
    return 0;
265
0
}
266
static int
267
s_ram_read_close(stream * s)
268
0
{
269
0
    ramhandle *file = (ramhandle*)s->file;
270
271
0
    if (file != 0) {
272
0
    s->file = 0;
273
0
    ramfile_close(file);
274
0
    }
275
0
    return 0;
276
0
}
277
278
/*
279
 * Process a buffer for a file reading stream.
280
 * This is the first stream in the pipeline, so pr is irrelevant.
281
 */
282
static int
283
s_ram_read_process(stream_state * st, stream_cursor_read * ignore_pr,
284
    stream_cursor_write * pw, bool last)
285
0
{
286
0
    stream *s = (stream *)st;    /* no separate state */
287
0
    ramhandle *file = (ramhandle*)s->file;
288
0
    uint max_count = pw->limit - pw->ptr;
289
0
    int status = 1;
290
0
    int count;
291
292
0
    if (s->file_limit < S_FILE_LIMIT_MAX) {
293
0
    long limit_count = s->file_offset + s->file_limit -
294
0
    ramfile_tell(file);
295
296
0
    if (max_count > limit_count)
297
0
        max_count = limit_count, status = EOFC;
298
0
    }
299
0
    count = ramfile_read(file,pw->ptr + 1, max_count);
300
0
    if (count < 0) return ERRC;
301
0
    pw->ptr += count;
302
    /*    process_interrupts(s->memory); */
303
0
    return ramfile_eof(file) ? EOFC : status;
304
0
}
305
306
/* ------ File writing ------ */
307
308
/* Initialize a stream for writing a file. */
309
static void
310
swrite_ram(register stream * s, ramhandle * file, byte * buf, uint len)
311
0
{
312
0
    static const stream_procs p = {
313
0
    s_std_noavailable, s_ram_write_seek, s_std_write_reset,
314
0
    s_ram_write_flush, s_ram_write_close, s_ram_write_process,
315
0
    s_ram_switch
316
0
    };
317
318
0
    s_std_init(s, buf, len, &p, s_mode_write + s_mode_seek);
319
0
    s->file = (gp_file *)file;
320
0
    s->file_modes = s->modes;
321
0
    s->file_offset = 0;        /* in case we switch to reading later */
322
0
    s->file_limit = S_FILE_LIMIT_MAX;
323
0
}
324
325
/* Initialize for appending to a file. */
326
static void
327
sappend_ram(register stream * s, ramhandle * file, byte * buf, uint len)
328
0
{
329
0
    swrite_ram(s, file, buf, len);
330
0
    s->modes = s_mode_write + s_mode_append;    /* no seek */
331
0
    s->file_modes = s->modes;
332
0
    ramfile_seek(file,0,RAMFS_SEEK_END);
333
0
    s->position = ramfile_tell(file);
334
0
}
335
336
/* Procedures for writing on a file */
337
static int
338
s_ram_write_seek(stream * s, gs_offset_t pos)
339
0
{
340
    /* We must flush the buffer to reposition. */
341
0
    int code = sflush(s);
342
343
0
    if (code < 0) return code;
344
0
    if (pos < 0 || ramfile_seek((ramhandle*)s->file, pos, RAMFS_SEEK_SET) != 0)
345
0
        return ERRC;
346
0
    s->position = pos;
347
0
    return 0;
348
0
}
349
350
static int
351
s_ram_write_flush(register stream * s)
352
0
{
353
0
    int result = s_process_write_buf(s, false);
354
0
    return result;
355
0
}
356
357
static int
358
s_ram_write_close(register stream * s)
359
0
{
360
0
    s_process_write_buf(s, true);
361
0
    return s_ram_read_close(s);
362
0
}
363
364
/*
365
 * Process a buffer for a file writing stream.
366
 * This is the last stream in the pipeline, so pw is irrelevant.
367
 */
368
static int
369
s_ram_write_process(stream_state * st, stream_cursor_read * pr,
370
    stream_cursor_write * ignore_pw, bool last)
371
0
{
372
0
    uint count = pr->limit - pr->ptr;
373
374
0
    ramhandle *file = (ramhandle*)((stream *) st)->file;
375
0
    int written = ramfile_write(file,pr->ptr + 1, count);
376
377
0
    if (written < 0) return ERRC;
378
0
    pr->ptr += written;
379
0
    return 0;
380
0
}
381
382
static int
383
s_ram_switch(stream * s, bool writing)
384
0
{
385
0
    uint modes = s->file_modes;
386
0
    ramhandle *file = (ramhandle*)s->file;
387
0
    long pos;
388
389
0
    if (writing) {
390
0
    if (!(s->file_modes & s_mode_write)) return ERRC;
391
0
    pos = stell(s);
392
0
    ramfile_seek(file, pos, RAMFS_SEEK_SET);
393
0
    if (modes & s_mode_append) {
394
0
        sappend_ram(s, file, s->cbuf, s->cbsize);    /* sets position */
395
0
    } else {
396
0
        swrite_ram(s, file, s->cbuf, s->cbsize);
397
0
        s->position = pos;
398
0
    }
399
0
    s->modes = modes;
400
0
    } else {
401
0
    if (!(s->file_modes & s_mode_read)) return ERRC;
402
0
    pos = stell(s);
403
0
    if (sflush(s) < 0) return ERRC;
404
0
    sread_ram(s, file, s->cbuf, s->cbsize);
405
0
    s->modes |= modes & s_mode_append;    /* don't lose append info */
406
0
    s->position = pos;
407
0
    }
408
0
    s->file_modes = modes;
409
0
    return 0;
410
0
}
411
412
413
/* gx_io_device stuff */
414
415
static int
416
iodev_ram_init(gx_io_device * iodev, gs_memory_t * mem)
417
162k
{
418
162k
    ramfs* fs = ramfs_new(mem, MAXBLOCKS);
419
162k
    ramfs_state* state = gs_alloc_struct(mem, ramfs_state, &st_ramfs_state,
420
162k
    "ramfs_init(state)"
421
162k
    );
422
162k
    if (fs && state) {
423
162k
    state->fs = fs;
424
162k
    state->memory = mem;
425
162k
    iodev->state = state;
426
162k
    return 0;
427
162k
    }
428
0
    if(fs) ramfs_destroy(mem, fs);
429
0
    if(state) gs_free_object(mem,state,"iodev_ram_init(state)");
430
0
    return_error(gs_error_VMerror);
431
162k
}
432
433
static void
434
iodev_ram_finit(gx_io_device * iodev, gs_memory_t * mem)
435
47.6k
{
436
47.6k
    ramfs_state *state = (ramfs_state *)iodev->state;
437
47.6k
    if (state != NULL)
438
47.6k
    {
439
47.6k
        iodev->state = NULL;
440
47.6k
        gs_free_object(state->memory, state, "iodev_ram_finit");
441
47.6k
    }
442
47.6k
    return;
443
47.6k
}
444
445
static void
446
ram_finalize(const gs_memory_t *memory, void * vptr)
447
162k
{
448
162k
    ramfs* fs = GETRAMFS((ramfs_state*)vptr);
449
162k
    ramfs_destroy((gs_memory_t *)memory, fs);
450
162k
    GETRAMFS((ramfs_state*)vptr) = NULL;
451
162k
}
452
453
static int
454
ram_delete(gx_io_device * iodev, const char *fname)
455
0
{
456
0
    ramfs* fs;
457
458
0
    if (iodev->state == NULL)
459
0
        return_error(gs_error_ioerror);
460
0
    else
461
0
        fs = GETRAMFS(iodev->state);
462
463
0
    if(ramfs_unlink(fs,fname)!=0) {
464
0
    return_error(ramfs_errno_to_code(ramfs_error(fs)));
465
0
    }
466
0
    return 0;
467
0
}
468
469
static int
470
ram_rename(gx_io_device * iodev, const char *from, const char *to)
471
0
{
472
0
    ramfs* fs;
473
474
0
    if (iodev->state == NULL)
475
0
        return_error(gs_error_ioerror);
476
0
    else
477
0
        fs = GETRAMFS(iodev->state);
478
479
0
    if(ramfs_rename(fs,from,to)!=0) {
480
0
    return_error(ramfs_errno_to_code(ramfs_error(fs)));
481
0
    }
482
0
    return 0;
483
0
}
484
485
static int
486
ram_status(gx_io_device * iodev, const char *fname, struct stat *pstat)
487
0
{
488
0
    ramhandle * f;
489
0
    ramfs* fs;
490
491
0
    if (iodev->state == NULL)
492
0
        return_error(gs_error_ioerror);
493
0
    else
494
0
        fs = GETRAMFS(iodev->state);
495
496
0
    f = ramfs_open(((ramfs_state *)iodev->state)->memory, fs,fname,RAMFS_READ);
497
0
    if(!f) return_error(ramfs_errno_to_code(ramfs_error(fs)));
498
499
0
    memset(pstat, 0, sizeof(*pstat));
500
0
    pstat->st_size = ramfile_size(f);
501
    /* The Windows definition of struct stat doesn't include a st_blocks member
502
    pstat->st_blocks = (pstat->st_size+blocksize-1)/blocksize;*/
503
    /* XXX set mtime & ctime */
504
0
    ramfile_close(f);
505
0
    return 0;
506
0
}
507
508
static file_enum *
509
ram_enumerate_init(gs_memory_t * mem, gx_io_device *iodev, const char *pat,
510
                   uint patlen)
511
0
{
512
0
    gsram_enum * penum = gs_alloc_struct(
513
0
    mem, gsram_enum, &st_gsram_enum,
514
0
    "ram_enumerate_files_init(file_enum)"
515
0
    );
516
0
    char *pattern = (char *)gs_alloc_bytes(
517
0
    mem, patlen+1, "ram_enumerate_file_init(pattern)"
518
0
    );
519
520
0
    ramfs_enum * e;
521
522
0
    if (iodev->state == NULL)
523
0
        return NULL;
524
0
    else
525
0
        e = ramfs_enum_new(GETRAMFS(iodev->state));
526
527
0
    if(penum && pattern && e) {
528
0
    memcpy(pattern, pat, patlen);
529
0
    pattern[patlen]=0;
530
531
0
    penum->memory = mem;
532
0
    penum->pattern = pattern;
533
0
    penum->e = e;
534
0
    return (file_enum *)penum;
535
0
    }
536
0
    if (penum) gs_free_object(mem,penum,"ramfs_enum_init(ramfs_enum)");
537
0
    if (pattern)
538
0
    gs_free_object(mem, pattern, "ramfs_enum_init(pattern)");
539
0
    if(e) ramfs_enum_end(e);
540
0
    return NULL;
541
0
}
542
543
static void
544
ram_enumerate_close(gs_memory_t * mem, file_enum *pfen)
545
0
{
546
0
    gsram_enum *penum = (gsram_enum *)pfen;
547
0
    gs_memory_t *mem2 = penum->memory;
548
0
    (void)mem;
549
550
0
    ramfs_enum_end(penum->e);
551
0
    gs_free_object(mem2, penum->pattern, "ramfs_enum_init(pattern)");
552
0
    gs_free_object(mem2, penum, "ramfs_enum_init(ramfs_enum)");
553
0
}
554
555
static uint
556
ram_enumerate_next(gs_memory_t * mem, file_enum *pfen, char *ptr, uint maxlen)
557
0
{
558
0
    gsram_enum *penum = (gsram_enum *)pfen;
559
560
0
    char * filename;
561
0
    while ((filename = ramfs_enum_next(penum->e))) {
562
0
    if (string_match((byte *)filename, strlen(filename),
563
0
        (byte *)penum->pattern,
564
0
        strlen(penum->pattern), 0)) {
565
0
        if (strlen(filename) < maxlen)
566
0
        memcpy(ptr, filename, strlen(filename));
567
0
        return strlen(filename);    /* if > maxlen, caller will detect rangecheck */
568
0
    }
569
0
    }
570
    /* ran off end of list, close the enum */
571
0
    ram_enumerate_close(mem, pfen);
572
0
    return ~(uint)0;
573
0
}
574
575
static int
576
ram_get_params(gx_io_device * iodev, gs_param_list * plist)
577
0
{
578
0
    int code;
579
0
    int i0 = 0, so = 1;
580
0
    bool btrue = true, bfalse = false;
581
0
    ramfs* fs;
582
583
0
    int BlockSize;
584
0
    long Free, LogicalSize;
585
586
0
    if (iodev->state == NULL)
587
0
        return_error(gs_error_ioerror);
588
0
    else
589
0
        fs = GETRAMFS(iodev->state);
590
591
0
    BlockSize = ramfs_blocksize(fs);
592
0
    LogicalSize = MAXBLOCKS;
593
0
    Free = ramfs_blocksfree(fs);
594
595
0
    if (
596
0
    (code = param_write_bool(plist, "HasNames",        &btrue)) < 0 ||
597
0
    (code = param_write_int (plist, "BlockSize",       &BlockSize)) < 0 ||
598
0
    (code = param_write_long(plist, "Free",            &Free)) < 0 ||
599
0
    (code = param_write_int (plist, "InitializeAction",&i0)) < 0 ||
600
0
    (code = param_write_bool(plist, "Mounted",         &btrue)) < 0 ||
601
0
    (code = param_write_bool(plist, "Removable",       &bfalse)) < 0 ||
602
0
    (code = param_write_bool(plist, "Searchable",      &btrue)) < 0 ||
603
0
    (code = param_write_int (plist, "SearchOrder",     &so)) < 0 ||
604
0
    (code = param_write_bool(plist, "Writeable",       &btrue)) < 0 ||
605
0
    (code = param_write_long(plist, "LogicalSize",     &LogicalSize)) < 0
606
0
    )
607
0
    return code;
608
0
    return 0;
609
0
}