Coverage Report

Created: 2022-04-16 11:23

/src/ghostpdl/base/gpmisc.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2021 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
/* Miscellaneous support for platform facilities */
18
19
#include "errno_.h"
20
#include "stat_.h"
21
#include "unistd_.h"
22
#include "fcntl_.h"
23
#include "stdio_.h"
24
#include "memory_.h"
25
#include "string_.h"
26
#include "gp.h"
27
#include "gpgetenv.h"
28
#include "gpmisc.h"
29
#include "gserrors.h"
30
31
/*
32
 * Get the name of the directory for temporary files, if any.  Currently
33
 * this checks the TMPDIR and TEMP environment variables, in that order.
34
 * The return value and the setting of *ptr and *plen are as for gp_getenv.
35
 */
36
int
37
gp_gettmpdir(char *ptr, int *plen)
38
669
{
39
669
    int max_len = *plen;
40
669
    int code = gp_getenv("TMPDIR", ptr, plen);
41
42
669
    if (code != 1)
43
0
        return code;
44
669
    *plen = max_len;
45
669
    return gp_getenv("TEMP", ptr, plen);
46
669
}
47
48
/*
49
 * Open a temporary file, using O_EXCL and S_I*USR to prevent race
50
 * conditions and symlink attacks.
51
 */
52
FILE *
53
gp_fopentemp(const char *fname, const char *mode)
54
0
{
55
0
    int flags = O_EXCL;
56
    /* Scan the mode to construct the flags. */
57
0
    const char *p = mode;
58
0
    int fildes;
59
0
    FILE *file;
60
61
0
#if defined (O_LARGEFILE)
62
    /* It works for Linux/gcc. */
63
0
    flags |= O_LARGEFILE;
64
#else
65
    /* fixme : Not sure what to do. Unimplemented. */
66
    /* MSVC has no O_LARGEFILE, but MSVC build never calls this function. */
67
#endif
68
0
    while (*p)
69
0
        switch (*p++) {
70
0
        case 'a':
71
0
            flags |= O_CREAT | O_APPEND;
72
0
            break;
73
0
        case 'r':
74
0
            flags |= O_RDONLY;
75
0
            break;
76
0
        case 'w':
77
0
            flags |= O_CREAT | O_WRONLY | O_TRUNC;
78
0
            break;
79
#ifdef O_BINARY
80
            /* Watcom C insists on this non-ANSI flag being set. */
81
        case 'b':
82
            flags |= O_BINARY;
83
            break;
84
#endif
85
0
        case '+':
86
0
            flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
87
0
            break;
88
0
        default:    /* e.g., 'b' */
89
0
            break;
90
0
        }
91
0
    fildes = open(fname, flags, S_IRUSR | S_IWUSR);
92
0
    if (fildes < 0)
93
0
        return 0;
94
    /*
95
     * The DEC VMS C compiler incorrectly defines the second argument of
96
     * fdopen as (char *), rather than following the POSIX.1 standard,
97
     * which defines it as (const char *).  Patch this here.
98
     */
99
0
    file = fdopen(fildes, (char *)mode); /* still really const */
100
0
    if (file == 0)
101
0
        close(fildes);
102
0
    return file;
103
0
}
104
105
/* Append a string to buffer. */
106
static inline bool
107
append(char **bp, const char *bpe, const char **ip, uint len)
108
8.82M
{
109
8.82M
    if (bpe - *bp < len)
110
0
        return false;
111
8.82M
    memcpy(*bp, *ip, len);
112
8.82M
    *bp += len;
113
8.82M
    *ip += len;
114
8.82M
    return true;
115
8.82M
}
116
117
/* Search a separator forward. */
118
static inline uint
119
search_separator(const char **ip, const char *ipe, const char *item, int direction)
120
4.45M
{   uint slen = 0;
121
45.2M
    for (slen = 0; (*ip - ipe) * direction < 0; (*ip) += direction)
122
43.4M
        if((slen = gs_file_name_check_separator(*ip, ipe - *ip, item)) != 0)
123
2.64M
            break;
124
4.45M
    return slen;
125
4.45M
}
126
127
/*
128
 * Combine a file name with a prefix.
129
 * Concatenates two paths and reduce parent references and current
130
 * directory references from the concatenation when possible.
131
 * The trailing zero byte is being added.
132
 *
133
 * Returns "gp_combine_success" if OK and sets '*blen' to the length of
134
 * the combined string. If the combined string is too small for the buffer
135
 * length passed in (as defined by the initial value of *blen), then the
136
 * "gp_combine_small_buffer" code is returned.
137
 *
138
 * Also tolerates/skips leading IODevice specifiers such as %os% or %rom%
139
 * When there is a leading '%' in the 'fname' no other processing is done.
140
 *
141
 * Examples :
142
 *  "/gs/lib" + "../Resource/CMap/H" --> "/gs/Resource/CMap/H"
143
 *  "C:/gs/lib" + "../Resource/CMap/H" --> "C:/gs/Resource/CMap/H"
144
 *  "hard disk:gs:lib" + "::Resource:CMap:H" -->
145
 *    "hard disk:gs:Resource:CMap:H"
146
 *  "DUA1:[GHOSTSCRIPT.LIB" + "-.RESOURCE.CMAP]H" -->
147
 *    "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H"
148
 *      "\\server\share/a/b///c/../d.e/./" + "../x.e/././/../../y.z/v.v" -->
149
 *    "\\server\share/a/y.z/v.v"
150
 *  "%rom%lib/" + "gs_init.ps" --> "%rom%lib/gs_init.ps
151
 *  "" + "%rom%lib/gs_init.ps" --> "%rom%lib/gs_init.ps"
152
 */
153
gp_file_name_combine_result
154
gp_file_name_combine_generic(const char *prefix, uint plen, const char *fname, uint flen,
155
                    bool no_sibling, char *buffer, uint *blen)
156
1.15M
{
157
    /*
158
     * THIS CODE IS SHARED FOR MULTIPLE PLATFORMS.
159
     * PLEASE DON'T CHANGE IT FOR A SPECIFIC PLATFORM.
160
     * Change gp_file_name_combine instead.
161
     */
162
1.15M
    char *bp = buffer, *bpe = buffer + *blen;
163
1.15M
    const char *ip, *ipe;
164
1.15M
    uint slen;
165
1.15M
    uint infix_type = 0; /* 0=none, 1=current, 2=parent. */
166
1.15M
    uint infix_len = 0;
167
1.15M
    uint rlen = gp_file_name_root(fname, flen);
168
    /* We need a special handling of infixes only immediately after a drive. */
169
170
1.15M
    if ( flen > 0 && fname[0] == '%') {
171
        /* IoDevice -- just return the fname as-is since this */
172
        /* function only handles the default file system */
173
        /* NOTE: %os% will subvert the normal processing of prefix and fname */
174
0
        ip = fname;
175
0
        *blen = flen;
176
0
        if (!append(&bp, bpe, &ip, flen))
177
0
            return gp_combine_small_buffer;
178
0
        return gp_combine_success;
179
0
    }
180
1.15M
    if (rlen != 0) {
181
        /* 'fname' is absolute, ignore the prefix. */
182
0
        ip = fname;
183
0
        ipe = fname + flen;
184
1.15M
    } else {
185
        /* Concatenate with prefix. */
186
1.15M
        ip = prefix;
187
1.15M
        ipe = prefix + plen;
188
1.15M
        rlen = gp_file_name_root(prefix, plen);
189
1.15M
    }
190
1.15M
    if (!append(&bp, bpe, &ip, rlen))
191
0
        return gp_combine_small_buffer;
192
1.15M
    slen = gs_file_name_check_separator(bp, buffer - bp, bp); /* Backward search. */
193
1.15M
    if (rlen != 0 && slen == 0) {
194
        /* Patch it against names like "c:dir" on Windows. */
195
0
        const char *sep = gp_file_name_directory_separator();
196
197
0
        slen = strlen(sep);
198
0
        if (!append(&bp, bpe, &sep, slen))
199
0
            return gp_combine_small_buffer;
200
0
        rlen += slen;
201
0
    }
202
3.82M
    for (;;) {
203
3.82M
        const char *item = ip;
204
3.82M
        uint ilen;
205
206
3.82M
        slen = search_separator(&ip, ipe, item, 1);
207
3.82M
        ilen = ip - item;
208
3.82M
        if (ilen == 0 && !gp_file_name_is_empty_item_meanful()) {
209
683
            ip += slen;
210
683
            slen = 0;
211
3.82M
        } else if (gp_file_name_is_current(item, ilen)) {
212
            /* Skip the reference to 'current', except the starting one.
213
             * We keep the starting 'current' for platforms, which
214
             * require relative paths to start with it.
215
             */
216
0
            if (bp == buffer) {
217
0
                if (!append(&bp, bpe, &item, ilen))
218
0
                    return gp_combine_small_buffer;
219
0
                infix_type = 1;
220
0
                infix_len = ilen;
221
0
            } else {
222
0
                ip += slen;
223
0
                slen = 0;
224
0
            }
225
3.82M
        } else if (!gp_file_name_is_parent(item, ilen)) {
226
3.82M
            if (!append(&bp, bpe, &item, ilen))
227
0
                return gp_combine_small_buffer;
228
            /* The 'item' pointer is now broken; it may be restored using 'ilen'. */
229
3.82M
        } else if (bp == buffer + rlen + infix_len) {
230
            /* Input is a parent and the output only contains a root and an infix. */
231
0
            if (rlen != 0)
232
0
                return gp_combine_cant_handle;
233
0
            switch (infix_type) {
234
0
                case 1:
235
                    /* Replace the infix with parent. */
236
0
                    bp = buffer + rlen; /* Drop the old infix, if any. */
237
0
                    infix_len = 0;
238
                    /* Falls through. */
239
0
                case 0:
240
                    /* We have no infix, start with parent. */
241
0
                    if ((no_sibling && ipe == fname + flen && flen != 0) ||
242
0
                            !gp_file_name_is_parent_allowed())
243
0
                        return gp_combine_cant_handle;
244
                    /* Falls through. */
245
0
                case 2:
246
                    /* Append one more parent - falls through. */
247
0
                    DO_NOTHING;
248
0
            }
249
0
            if (!append(&bp, bpe, &item, ilen))
250
0
                return gp_combine_small_buffer;
251
0
            infix_type = 2;
252
0
            infix_len += ilen;
253
            /* Recompute the separator length. We cannot use the old slen on Mac OS. */
254
0
            slen = gs_file_name_check_separator(ip, ipe - ip, ip);
255
0
        } else {
256
            /* Input is a parent and the output continues after infix. */
257
            /* Unappend the last separator and the last item. */
258
0
            uint slen1 = gs_file_name_check_separator(bp, buffer + rlen - bp, bp); /* Backward search. */
259
0
            char *bie = bp - slen1;
260
261
0
            bp = bie;
262
0
            DISCARD(search_separator((const char **)&bp, buffer + rlen, bp, -1));
263
            /* The cast above quiets a gcc warning. We believe it's a bug in the compiler. */
264
            /* Skip the input with separator. We cannot use slen on Mac OS. */
265
0
            ip += gs_file_name_check_separator(ip, ipe - ip, ip);
266
0
            if (no_sibling) {
267
0
                const char *p = ip;
268
269
0
                DISCARD(search_separator(&p, ipe, ip, 1));
270
0
                if (p - ip != bie - bp || memcmp(ip, bp, p - ip))
271
0
                    return gp_combine_cant_handle;
272
0
            }
273
0
            slen = 0;
274
0
        }
275
3.82M
        if (slen) {
276
2.52M
            if (bp == buffer + rlen + infix_len)
277
0
                infix_len += slen;
278
2.52M
            if (!append(&bp, bpe, &ip, slen))
279
0
                return gp_combine_small_buffer;
280
2.52M
        }
281
3.82M
        if (ip == ipe) {
282
1.33M
            if (ipe == fname + flen) {
283
                /* All done.
284
                 * Note that the case (prefix + plen == fname && flen == 0)
285
                 * falls here without appending a separator.
286
                 */
287
1.15M
                const char *zero="";
288
289
1.15M
                if (bp == buffer) {
290
                    /* Must not return empty path. */
291
0
                    const char *current = gp_file_name_current();
292
0
                    int clen = strlen(current);
293
294
0
                    if (!append(&bp, bpe, &current, clen))
295
0
                        return gp_combine_small_buffer;
296
0
                }
297
1.15M
                *blen = bp - buffer;
298
1.15M
                if (!append(&bp, bpe, &zero, 1))
299
0
                    return gp_combine_small_buffer;
300
1.15M
                return gp_combine_success;
301
1.15M
            } else {
302
                /* ipe == prefix + plen */
303
                /* Switch to fname. */
304
172k
                ip = fname;
305
172k
                ipe = fname + flen;
306
172k
                if (slen == 0) {
307
                    /* Insert a separator. */
308
156k
                    const char *sep;
309
310
156k
                    slen = search_separator(&ip, ipe, fname, 1);
311
156k
                    sep = (slen != 0 ? gp_file_name_directory_separator()
312
156k
                                    : gp_file_name_separator());
313
156k
                    slen = strlen(sep);
314
156k
                    if (bp == buffer + rlen + infix_len)
315
0
                        infix_len += slen;
316
156k
                    if (!append(&bp, bpe, &sep, slen))
317
0
                        return gp_combine_small_buffer;
318
156k
                    ip = fname; /* Switch to fname. */
319
156k
                }
320
172k
            }
321
1.33M
        }
322
3.82M
    }
323
1.15M
}
324
325
/*
326
 * Reduces parent references and current directory references when possible.
327
 * The trailing zero byte is being added.
328
 *
329
 * Examples :
330
 *  "/gs/lib/../Resource/CMap/H" --> "/gs/Resource/CMap/H"
331
 *  "C:/gs/lib/../Resource/CMap/H" --> "C:/gs/Resource/CMap/H"
332
 *  "hard disk:gs:lib::Resource:CMap:H" -->
333
 *    "hard disk:gs:Resource:CMap:H"
334
 *  "DUA1:[GHOSTSCRIPT.LIB.-.RESOURCE.CMAP]H" -->
335
 *    "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H"
336
 *      "\\server\share/a/b///c/../d.e/./../x.e/././/../../y.z/v.v" -->
337
 *    "\\server\share/a/y.z/v.v"
338
 *
339
 */
340
gp_file_name_combine_result
341
gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen)
342
987k
{
343
987k
    return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen);
344
987k
}
345
346
/*
347
 * Answers whether a file name is absolute (starts from a root).
348
 */
349
bool
350
gp_file_name_is_absolute(const char *fname, uint flen)
351
345k
{
352
345k
    return (gp_file_name_root(fname, flen) > 0);
353
345k
}
354
355
/*
356
 * Returns length of all starting parent references.
357
 */
358
static uint
359
gp_file_name_prefix(const char *fname, uint flen,
360
                bool (*test)(const char *fname, uint flen))
361
580k
{
362
580k
    uint plen = gp_file_name_root(fname, flen), slen;
363
580k
    const char *ip, *ipe;
364
580k
    const char *item = fname; /* plen == flen could cause an indeterminizm. */
365
366
580k
    if (plen > 0)
367
107k
        return 0;
368
473k
    ip = fname + plen;
369
473k
    ipe = fname + flen;
370
473k
    for (; ip < ipe; ) {
371
473k
        item = ip;
372
473k
        slen = search_separator(&ip, ipe, item, 1);
373
473k
        if (!(*test)(item, ip - item))
374
473k
            break;
375
0
        ip += slen;
376
0
    }
377
473k
    return item - fname;
378
580k
}
379
380
/*
381
 * Returns length of all starting parent references.
382
 */
383
uint
384
gp_file_name_parents(const char *fname, uint flen)
385
580k
{
386
580k
    return gp_file_name_prefix(fname, flen, gp_file_name_is_parent);
387
580k
}
388
389
/*
390
 * Returns length of all starting cwd references.
391
 */
392
uint
393
gp_file_name_cwds(const char *fname, uint flen)
394
0
{
395
0
    return gp_file_name_prefix(fname, flen, gp_file_name_is_current);
396
0
}
397
398
static int
399
generic_pread(gp_file *f, size_t count, gs_offset_t offset, void *buf)
400
0
{
401
0
    int c;
402
0
    int64_t os, curroff = gp_ftell(f);
403
0
    if (curroff < 0) return curroff;
404
405
0
    os = gp_fseek(f, offset, 0);
406
0
    if (os < 0) return os;
407
408
0
    c = gp_fread(buf, 1, count, f);
409
0
    if (c < 0) return c;
410
411
0
    os = gp_fseek(f, curroff, 0);
412
0
    if (os < 0) return os;
413
414
0
    return c;
415
0
}
416
417
static int
418
generic_pwrite(gp_file *f, size_t count, gs_offset_t offset, const void *buf)
419
0
{
420
0
    int c;
421
0
    int64_t os, curroff = gp_ftell(f);
422
0
    if (curroff < 0) return curroff;
423
424
0
    os = gp_fseek(f, offset, 0);
425
0
    if (os < 0) return os;
426
427
0
    c = gp_fwrite(buf, 1, count, f);
428
0
    if (c < 0) return c;
429
430
0
    os = gp_fseek(f, curroff, 0);
431
0
    if (os < 0) return os;
432
433
0
    return c;
434
0
}
435
436
gp_file *gp_file_alloc(const gs_memory_t *mem, const gp_file_ops_t *prototype, size_t size, const char *cname)
437
138k
{
438
138k
    gp_file *file = (gp_file *)gs_alloc_bytes(mem->thread_safe_memory, size, cname ? cname : "gp_file");
439
138k
    if (file == NULL)
440
0
        return NULL;
441
442
138k
    if (prototype)
443
138k
        file->ops = *prototype;
444
138k
    if (file->ops.pread == NULL)
445
0
        file->ops.pread = generic_pread;
446
138k
    if (file->ops.pwrite == NULL)
447
0
        file->ops.pwrite = generic_pwrite;
448
138k
    if (size > sizeof(*prototype))
449
138k
        memset(((char *)file)+sizeof(*prototype),
450
138k
               0,
451
138k
               size - sizeof(*prototype));
452
138k
    file->memory = mem->thread_safe_memory;
453
454
138k
    return file;
455
138k
}
456
457
void gp_file_dealloc(gp_file *file)
458
138k
{
459
138k
    if (file == NULL)
460
0
        return;
461
462
138k
    if (file->buffer)
463
0
        gs_free_object(file->memory, file->buffer, "gp_file");
464
138k
    gs_free_object(file->memory, file, "gp_file");
465
138k
}
466
467
int gp_fprintf(gp_file *f, const char *fmt, ...)
468
0
{
469
0
    va_list args;
470
0
    int n;
471
472
0
    if (f->buffer)
473
0
        goto mid;
474
0
    do {
475
0
        n = f->buffer_size * 2;
476
0
        if (n == 0)
477
0
            n = 256;
478
0
        gs_free_object(f->memory, f->buffer, "gp_file(buffer)");
479
0
        f->buffer = (char *)gs_alloc_bytes(f->memory, n, "gp_file(buffer)");
480
0
        if (f->buffer == NULL)
481
0
            return -1;
482
0
        f->buffer_size = n;
483
0
mid:
484
0
        va_start(args, fmt);
485
0
        n = vsnprintf(f->buffer, f->buffer_size, fmt, args);
486
0
        va_end(args);
487
0
    } while (n >= f->buffer_size);
488
0
    return (f->ops.write)(f, 1, n, f->buffer);
489
0
}
490
typedef struct {
491
    gp_file base;
492
    FILE *file;
493
    int (*close)(FILE *file);
494
} gp_file_FILE;
495
496
static int
497
gp_file_FILE_close(gp_file *file_)
498
1.99k
{
499
1.99k
    gp_file_FILE *file = (gp_file_FILE *)file_;
500
501
1.99k
    return (file->close)(file->file);
502
1.99k
}
503
504
static int
505
gp_file_FILE_getc(gp_file *file_)
506
0
{
507
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
508
509
0
    return fgetc(file->file);
510
0
}
511
512
static int
513
gp_file_FILE_putc(gp_file *file_, int c)
514
0
{
515
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
516
517
0
    return fputc(c, file->file);
518
0
}
519
520
static int
521
gp_file_FILE_read(gp_file *file_, size_t size, unsigned int count, void *buf)
522
366k
{
523
366k
    gp_file_FILE *file = (gp_file_FILE *)file_;
524
525
366k
    return fread(buf, size, count, file->file);
526
366k
}
527
528
static int
529
gp_file_FILE_write(gp_file *file_, size_t size, unsigned int count, const void *buf)
530
129k
{
531
129k
    gp_file_FILE *file = (gp_file_FILE *)file_;
532
533
129k
    return fwrite(buf, size, count, file->file);
534
129k
}
535
536
static int
537
gp_file_FILE_seek(gp_file *file_, gs_offset_t offset, int whence)
538
251k
{
539
251k
    gp_file_FILE *file = (gp_file_FILE *)file_;
540
541
251k
    return gp_fseek_impl(file->file, offset, whence);
542
251k
}
543
544
static gs_offset_t
545
gp_file_FILE_tell(gp_file *file_)
546
3.34k
{
547
3.34k
    gp_file_FILE *file = (gp_file_FILE *)file_;
548
549
3.34k
    return gp_ftell_impl(file->file);
550
3.34k
}
551
552
static int
553
gp_file_FILE_eof(gp_file *file_)
554
366k
{
555
366k
    gp_file_FILE *file = (gp_file_FILE *)file_;
556
557
366k
    return feof(file->file);
558
366k
}
559
560
static gp_file *
561
gp_file_FILE_dup(gp_file *file_, const char *mode)
562
0
{
563
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
564
0
    gp_file *file2 = gp_file_FILE_alloc(file->base.memory);
565
566
0
    if (gp_file_FILE_set(file2, gp_fdup_impl(file->file, mode), NULL))
567
0
        file2 = NULL;
568
569
0
    return file2;
570
0
}
571
572
static int
573
gp_file_FILE_seekable(gp_file *file_)
574
0
{
575
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
576
577
0
    return gp_fseekable_impl(file->file);
578
0
}
579
580
static int
581
gp_file_FILE_pread(gp_file *file_, size_t count, gs_offset_t offset, void *buf)
582
0
{
583
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
584
585
0
    return gp_pread_impl(buf, count, offset, file->file);
586
0
}
587
588
static int
589
gp_file_FILE_pwrite(gp_file *file_, size_t count, gs_offset_t offset, const void *buf)
590
0
{
591
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
592
593
0
    return gp_pwrite_impl(buf, count, offset, file->file);
594
0
}
595
596
static int
597
gp_file_FILE_is_char_buffered(gp_file *file_)
598
0
{
599
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
600
0
    struct stat rstat;
601
602
0
    if (fstat(fileno(file->file), &rstat) != 0)
603
0
        return ERRC;
604
0
    return S_ISCHR(rstat.st_mode);
605
0
}
606
607
static void
608
gp_file_FILE_fflush(gp_file *file_)
609
14.9k
{
610
14.9k
    gp_file_FILE *file = (gp_file_FILE *)file_;
611
612
14.9k
    fflush(file->file);
613
14.9k
}
614
615
static int
616
gp_file_FILE_ferror(gp_file *file_)
617
485k
{
618
485k
    gp_file_FILE *file = (gp_file_FILE *)file_;
619
620
485k
    return ferror(file->file);
621
485k
}
622
623
static FILE *
624
gp_file_FILE_get_file(gp_file *file_)
625
2.68k
{
626
2.68k
    gp_file_FILE *file = (gp_file_FILE *)file_;
627
628
2.68k
    return file->file;
629
2.68k
}
630
631
static void
632
gp_file_FILE_clearerr(gp_file *file_)
633
669
{
634
669
    gp_file_FILE *file = (gp_file_FILE *)file_;
635
636
669
    clearerr(file->file);
637
669
}
638
639
static gp_file *
640
gp_file_FILE_reopen(gp_file *file_, const char *fname, const char *mode)
641
0
{
642
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
643
644
0
    file->file = freopen(fname, mode, file->file);
645
0
    if (file->file == NULL) {
646
0
        gp_file_dealloc(file_);
647
0
        return NULL;
648
0
    }
649
0
    return file_;
650
0
}
651
652
static const gp_file_ops_t gp_file_FILE_prototype =
653
{
654
    gp_file_FILE_close,
655
    gp_file_FILE_getc,
656
    gp_file_FILE_putc,
657
    gp_file_FILE_read,
658
    gp_file_FILE_write,
659
    gp_file_FILE_seek,
660
    gp_file_FILE_tell,
661
    gp_file_FILE_eof,
662
    gp_file_FILE_dup,
663
    gp_file_FILE_seekable,
664
    gp_file_FILE_pread,
665
    gp_file_FILE_pwrite,
666
    gp_file_FILE_is_char_buffered,
667
    gp_file_FILE_fflush,
668
    gp_file_FILE_ferror,
669
    gp_file_FILE_get_file,
670
    gp_file_FILE_clearerr,
671
    gp_file_FILE_reopen
672
};
673
674
gp_file *gp_file_FILE_alloc(const gs_memory_t *mem)
675
138k
{
676
138k
    return gp_file_alloc(mem->non_gc_memory,
677
138k
                         &gp_file_FILE_prototype,
678
138k
                         sizeof(gp_file_FILE),
679
138k
                         "gp_file_FILE");
680
138k
}
681
682
int gp_file_FILE_set(gp_file *file_, FILE *f, int (*close)(FILE *))
683
138k
{
684
138k
    gp_file_FILE *file = (gp_file_FILE *)file_;
685
686
138k
    if (f == NULL) {
687
136k
        gp_file_dealloc(file_);
688
136k
        return 1;
689
136k
    }
690
691
1.99k
    file->file = f;
692
1.99k
    file->close = close ? close : fclose;
693
694
1.99k
    return 0;
695
138k
}
696
697
char *gp_fgets(char *buffer, size_t n, gp_file *f)
698
0
{
699
0
    int c = EOF;
700
0
    char *b = buffer;
701
0
    while (n > 1) {
702
0
        c = gp_fgetc(f);
703
0
  if (c == 0)
704
0
            break;
705
0
  *b++ = c;
706
0
  n--;
707
0
    }
708
0
    if (c == EOF && b == buffer)
709
0
        return NULL;
710
0
    if (gp_ferror(f))
711
0
        return NULL;
712
0
    if (n > 0)
713
0
        *b++ = 0;
714
0
    return buffer;
715
0
}
716
717
gp_file *
718
gp_fopen(const gs_memory_t *mem, const char *fname, const char *mode)
719
367k
{
720
367k
    gp_file *file = NULL;
721
367k
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
722
367k
    gs_fs_list_t *fs = ctx->core->fs;
723
724
367k
    if (gp_validate_path(mem, fname, mode) != 0)
725
230k
        return NULL;
726
727
137k
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
728
137k
    {
729
137k
        int code = 0;
730
137k
        if (fs->fs.open_file)
731
137k
            code = fs->fs.open_file(mem, fs->secret, fname, mode, &file);
732
137k
        if (code < 0)
733
136k
            return NULL;
734
683
        if (file != NULL)
735
683
            break;
736
683
    }
737
738
683
    return file;
739
137k
}
740
741
gp_file *
742
gp_open_printer(const gs_memory_t *mem,
743
                      char         fname[gp_file_name_sizeof],
744
                      int          binary_mode)
745
645
{
746
645
    gp_file *file = NULL;
747
645
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
748
645
    gs_fs_list_t *fs = ctx->core->fs;
749
750
645
    if (gp_validate_path(mem, fname, binary_mode ? "wb" : "w") != 0)
751
0
        return NULL;
752
753
645
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
754
645
    {
755
645
        int code = 0;
756
645
        if (fs->fs.open_printer)
757
645
            code = fs->fs.open_printer(mem, fs->secret, fname, binary_mode, &file);
758
645
        if (code < 0)
759
0
            return NULL;
760
645
        if (file != NULL)
761
645
            break;
762
645
    }
763
764
645
    return file;
765
645
}
766
767
static gp_file *
768
do_open_scratch_file(const gs_memory_t *mem,
769
                     const char        *prefix,
770
                     char              *fname,
771
                     const char        *mode,
772
                     int                rm)
773
669
{
774
669
    gp_file *file = NULL;
775
669
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
776
669
    gs_fs_list_t *fs = ctx->core->fs;
777
669
    int code = 0;
778
779
    /* If the prefix is absolute, then we must check it's a permissible
780
     * path. If not, we're OK. */
781
669
    if (gp_file_name_is_absolute(prefix, strlen(prefix)) &&
782
669
        gp_validate_path(mem, prefix, mode) != 0)
783
0
            return NULL;
784
785
669
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
786
669
    {
787
669
        if (fs->fs.open_scratch)
788
669
            code = fs->fs.open_scratch(mem, fs->secret, prefix, fname, mode, rm, &file);
789
669
        if (code < 0)
790
0
            return NULL;
791
669
        if (file != NULL)
792
669
            break;
793
669
    }
794
795
669
    if (file == NULL) {
796
        /* The file failed to open. Don't add it to the list. */
797
669
    } else if (rm) {
798
        /* This file has already been deleted by the underlying system.
799
         * We don't need to add it to the lists as it will never be
800
         * deleted manually, nor do we need to tidy it up on closedown. */
801
669
    } else {
802
         /* This file was not requested to be deleted. We add it to the
803
          * list so that it will either be deleted by any future call to
804
          * zdeletefile, OR on closedown. */
805
         /* Add the scratch file name to the lists. We can't do this any
806
          * earlier as we didn't know the name until now! Unfortunately
807
          * that makes cleanup harder. */
808
669
        code = gs_add_control_path_flags(mem, gs_permit_file_control, fname,
809
669
                                         gs_path_control_flag_is_scratch_file);
810
669
        if (code >= 0)
811
669
            code = gs_add_control_path_flags(mem, gs_permit_file_reading, fname,
812
669
                                             gs_path_control_flag_is_scratch_file);
813
669
        if (code >= 0)
814
669
            code = gs_add_control_path_flags(mem, gs_permit_file_writing, fname,
815
669
                                         gs_path_control_flag_is_scratch_file);
816
817
669
        if (code < 0) {
818
0
            gp_fclose(file);
819
0
            file = NULL;
820
            /* Call directly through to the unlink implementation. We know
821
             * we're 'permitted' to do this, but we might not be on all the
822
             * required permit lists because of the failure. The only bad
823
             * thing here, is that we're deleting an fname that might not
824
             * have come from the filing system itself. */
825
0
            if (fname && fname[0])
826
0
                gp_unlink_impl(ctx->memory, fname);
827
0
            (void)gs_remove_control_path_flags(mem, gs_permit_file_control, fname,
828
0
                                               gs_path_control_flag_is_scratch_file);
829
0
            (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, fname,
830
0
                                               gs_path_control_flag_is_scratch_file);
831
0
            (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, fname,
832
0
                                               gs_path_control_flag_is_scratch_file);
833
0
        }
834
669
    }
835
836
669
    return file;
837
669
}
838
839
gp_file *
840
gp_open_scratch_file(const gs_memory_t *mem,
841
                     const char        *prefix,
842
                     char              *fname,
843
                     const char        *mode)
844
669
{
845
669
    return do_open_scratch_file(mem, prefix, fname, mode, 0);
846
669
}
847
848
gp_file *
849
gp_open_scratch_file_rm(const gs_memory_t *mem,
850
                        const char        *prefix,
851
                        char              *fname,
852
                        const char        *mode)
853
0
{
854
0
    return do_open_scratch_file(mem, prefix, fname, mode, 1);
855
0
}
856
857
int
858
gp_stat(const gs_memory_t *mem, const char *path, struct stat *buf)
859
0
{
860
0
    if (gp_validate_path(mem, path, "r") != 0) {
861
0
        return -1;
862
0
    }
863
864
0
    return gp_stat_impl(mem, path, buf);
865
0
}
866
867
file_enum *
868
gp_enumerate_files_init(gs_memory_t *mem, const char *pat, uint patlen)
869
7.51k
{
870
7.51k
    return gp_enumerate_files_init_impl(mem, pat, patlen);
871
7.51k
}
872
873
uint
874
gp_enumerate_files_next(gs_memory_t *mem, file_enum * pfen, char *ptr, uint maxlen)
875
7.51k
{
876
7.51k
    uint code = 0;
877
878
7.51k
    while (code == 0) {
879
7.51k
        code = gp_enumerate_files_next_impl(mem, pfen, ptr, maxlen);
880
7.51k
        if (code == ~0) break;
881
0
        if (code > 0) {
882
0
            if (gp_validate_path_len(mem, ptr, code, "r") != 0)
883
0
                code = 0;
884
0
        }
885
0
    }
886
7.51k
    return code;
887
7.51k
}
888
void
889
gp_enumerate_files_close(gs_memory_t *mem, file_enum * pfen)
890
7.51k
{
891
7.51k
    gp_enumerate_files_close_impl(mem, pfen);
892
7.51k
}
893
894
/* Path validation: (FIXME: Move this somewhere better)
895
 *
896
 * The only wildcard we accept is '*'.
897
 *
898
 * A '*' at the end of the path means "in this directory,
899
 * or any subdirectory". Anywhere else it means "a sequence of
900
 * characters not including a director separator".
901
 *
902
 * A sequence of multiple '*'s is equivalent to a single one.
903
 *
904
 * Matching on '*' is simplistic; the matching sequence will end
905
 * as soon as we meet an instance of a character that follows
906
 * the '*' in a pattern. i.e. "foo*bar" will fail to match "fooabbar"
907
 * as the '*' will be held to match just 'a'.
908
 *
909
 * There is no way of specifying a literal '*'; if you find yourself
910
 * wanting to do this, slap yourself until you come to your senses.
911
 *
912
 * Due to the difficulties of using both * and / in writing C comments,
913
 * I shall use \ as the directory separator in the examples below, but
914
 * in practice it means "the directory separator for the current
915
 * platform".
916
 *
917
 * Pattern           Match example
918
 *  *                 any file, in any directory at all.
919
 *  foo\bar           a file, foo\bar.
920
 *  foo\bar\          any file within foo\bar\, but no subdirectories.
921
 *  foo\bar\*         any file within foo\bar\ or any subdirectory thereof.
922
 *  foo\*\bar         any file 'bar' within any single subdirectory of foo
923
 *                    (i.e. foo\baz\bar, but not foo\baz\whoop\bar)
924
 *  foo\out*.tif      e.g. foo\out1.tif
925
 *  foo\out*.*.tif*   e.g. foo\out1.(Red).tif
926
 */
927
928
static int
929
validate(const gs_memory_t *mem,
930
         const char        *path,
931
         gs_path_control_t  type)
932
546k
{
933
546k
    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
934
546k
    gs_path_control_set_t *control;
935
546k
    unsigned int i, n;
936
937
546k
    switch (type) {
938
544k
        case gs_permit_file_reading:
939
544k
            control = &core->permit_reading;
940
544k
            break;
941
645
        case gs_permit_file_writing:
942
645
            control = &core->permit_writing;
943
645
            break;
944
1.33k
        case gs_permit_file_control:
945
1.33k
            control = &core->permit_control;
946
1.33k
            break;
947
0
        default:
948
0
            return gs_error_unknownerror;
949
546k
    }
950
951
546k
    n = control->num;
952
17.3M
    for (i = 0; i < n; i++) {
953
16.8M
        const char *a = path;
954
16.8M
        const char *b = control->entry[i].path;
955
38.5M
        while (1) {
956
38.5M
            if (*a == 0) {
957
645
                if (*b == 0)
958
                    /* PATH=abc pattern=abc */
959
645
                    goto found; /* Bingo! */
960
0
                else
961
                    /* PATH=abc pattern=abcd */
962
0
                    break; /* No match */
963
38.5M
            } else if (*b == '*') {
964
85.5k
                if (b[1] == '*') {
965
                    /* Multiple '*'s are taken to mean the
966
                     * output from a printf. */
967
0
                    b++;
968
0
                    while (b[1] == '*')
969
0
                        b++;
970
                    /* Skip over the permissible matching chars */
971
0
                    while (*a &&
972
0
                           ((*a == ' ' || *a == '-' || *a == '+' ||
973
0
                             (*a >= '0' && *a <= '9') ||
974
0
                             (*a >= 'a' && *a <= 'f') ||
975
0
                             (*a >= 'A' && *a <= 'F'))))
976
0
                            a++;
977
0
                    if (b[1] == 0 && *a == 0)
978
                        /* PATH=abc<%d> pattern=abc** */
979
0
                        goto found;
980
0
                    if (*a == 0)
981
0
                        break; /* No match */
982
85.5k
                } else {
983
85.5k
                    if (b[1] == 0)
984
                        /* PATH=abc???? pattern=abc* */
985
85.5k
                        goto found;
986
                    /* Skip over anything except NUL, directory
987
                     * separator, and the next char to match. */
988
0
                    while (*a && !gs_file_name_check_separator(a, 1, a) && *a != b[1])
989
0
                        a++;
990
0
                    if (*a == 0 || *a == gs_file_name_check_separator(a, 1, a))
991
0
                        break; /* No match */
992
0
                }
993
                /* Continue matching */
994
0
                a--; /* Subtract 1 as the loop will increment it again later */
995
38.4M
            } else if (*b == 0) {
996
84.2k
                if (b != control->entry[i].path &&
997
84.2k
                    gs_file_name_check_separator(b, -1, b)) {
998
0
                    const char *a2 = a;
999
0
                    const char *aend = path + strlen(path);
1000
0
                    while (aend != a2 && !gs_file_name_check_separator(a2, 1, a2))
1001
0
                      a2++;
1002
                    /* If the control path ends in a directory separator and we've scanned
1003
                       to the end of the candidate path with no further directory separators
1004
                       found, we have a match
1005
                     */
1006
0
                    if (aend == a2)
1007
                      /* PATH=abc/? pattern=abc/ */
1008
0
                      goto found; /* Bingo! */
1009
0
                 }
1010
                /* PATH=abcd pattern=abc */
1011
84.2k
                break; /* No match */
1012
38.3M
            } else if (gs_file_name_check_separator(a, 1, a) == 1
1013
38.3M
                        && gs_file_name_check_separator(b, 1, b) == 1) {
1014
                /* On Windows we can get random combinations of "/" and "\" as directory
1015
                 * separators, and we want "C:\" to match C:/" hence using the pair of
1016
                 * gs_file_name_check_separator() calls above */
1017
                 /* Annoyingly, we can also end up with a combination of explicitly escaped
1018
                  * '\' characters, and not escaped. So we also need "C:\\" to match "C:\"
1019
                  * and "C:/" - hence we need to check for, and skip over the
1020
                  * the extra '\' character - I'm reticent to change the upstream code that
1021
                  * adds the explicit escape, because that could have unforeseen side effects
1022
                  * elsewhere. */
1023
4.97M
                 if (*(a + 1) != 0 && gs_file_name_check_separator(a + 1, 1, a + 1) == 1)
1024
0
                     a++;
1025
4.97M
                 if (*(b + 1) != 0 && gs_file_name_check_separator(b + 1, 1, b + 1) == 1)
1026
0
                     b++;
1027
33.4M
            } else if (*a != *b) {
1028
16.6M
                break;
1029
16.6M
            }
1030
21.7M
            a++, b++;
1031
21.7M
        }
1032
16.8M
    }
1033
460k
    return gs_error_invalidfileaccess;
1034
1035
86.2k
found:
1036
86.2k
    return control->entry[i].flags;
1037
546k
}
1038
1039
int
1040
gp_validate_path_len(const gs_memory_t *mem,
1041
                     const char        *path,
1042
                     const uint         len,
1043
                     const char        *mode)
1044
369k
{
1045
369k
    char *buffer, *bufferfull;
1046
369k
    uint rlen;
1047
369k
    int code = 0;
1048
369k
    const char *cdirstr = gp_file_name_current();
1049
369k
    int cdirstrl = strlen(cdirstr);
1050
369k
    const char *dirsepstr = gp_file_name_separator();
1051
369k
    int dirsepstrl = strlen(dirsepstr);
1052
369k
    int prefix_len = cdirstrl + dirsepstrl;
1053
1054
    /* mem->gs_lib_ctx can be NULL when we're called from mkromfs */
1055
369k
    if (mem->gs_lib_ctx == NULL ||
1056
369k
        mem->gs_lib_ctx->core->path_control_active == 0)
1057
53.2k
        return 0;
1058
1059
    /* For current directory accesses, we need handle both a "bare" name,
1060
     * and one with a cwd prefix (in Unix terms, both "myfile.ps" and
1061
     * "./myfile.ps".
1062
     *
1063
     * So we check up front if it's absolute, then just use that.
1064
     * If it includes cwd prefix, we try that, then remove the prefix
1065
     * and try again.
1066
     * If it doesn't include the cwd prefix, we try it, then add the
1067
     * prefix and try again.
1068
     * To facilitate that, we allocate a large enough buffer to take
1069
     * the path *and* the prefix up front.
1070
     */
1071
316k
    if (gp_file_name_is_absolute(path, len)) {
1072
       /* Absolute path, we don't need anything extra */
1073
86.2k
       prefix_len = cdirstrl = dirsepstrl = 0;
1074
86.2k
    }
1075
230k
    else if (len > prefix_len && !memcmp(path, cdirstr, cdirstrl)
1076
230k
             && !memcmp(path + cdirstrl, dirsepstr, dirsepstrl)) {
1077
0
          prefix_len = 0;
1078
0
    }
1079
316k
    rlen = len+1;
1080
316k
    bufferfull = (char *)gs_alloc_bytes(mem->thread_safe_memory, rlen + prefix_len, "gp_validate_path");
1081
316k
    if (bufferfull == NULL)
1082
0
        return gs_error_VMerror;
1083
1084
316k
    buffer = bufferfull + prefix_len;
1085
316k
    if (gp_file_name_reduce(path, (uint)len, buffer, &rlen) != gp_combine_success)
1086
0
        return gs_error_invalidfileaccess;
1087
316k
    buffer[rlen] = 0;
1088
1089
546k
    while (1) {
1090
546k
        switch (mode[0])
1091
546k
        {
1092
544k
        case 'r': /* Read */
1093
544k
            code = validate(mem, buffer, gs_permit_file_reading);
1094
544k
            break;
1095
645
        case 'w': /* Write */
1096
645
            code = validate(mem, buffer, gs_permit_file_writing);
1097
645
            break;
1098
0
        case 'a': /* Append needs reading and writing */
1099
0
            code = (validate(mem, buffer, gs_permit_file_reading) |
1100
0
                    validate(mem, buffer, gs_permit_file_writing));
1101
0
            break;
1102
0
        case 'c': /* "Control" */
1103
0
            code =  validate(mem, buffer, gs_permit_file_control);
1104
0
            break;
1105
1.33k
        case 'd': /* "Delete" (special case of control) */
1106
1.33k
            code =  validate(mem, buffer, gs_permit_file_control);
1107
1.33k
            break;
1108
0
        case 'f': /* "Rename from" */
1109
0
            code = (validate(mem, buffer, gs_permit_file_writing) |
1110
0
                    validate(mem, buffer, gs_permit_file_control));
1111
0
            break;
1112
0
        case 't': /* "Rename to" */
1113
0
            code = (validate(mem, buffer, gs_permit_file_writing) |
1114
0
                    validate(mem, buffer, gs_permit_file_control));
1115
0
            break;
1116
0
        default:
1117
0
            errprintf(mem, "gp_validate_path: Unknown mode='%s'\n", mode);
1118
0
            code = gs_note_error(gs_error_invalidfileaccess);
1119
546k
        }
1120
546k
        if (code < 0 && prefix_len > 0 && buffer > bufferfull) {
1121
230k
            buffer = bufferfull;
1122
230k
            memcpy(buffer, cdirstr, cdirstrl);
1123
230k
            memcpy(buffer + cdirstrl, dirsepstr, dirsepstrl);
1124
230k
            continue;
1125
230k
        }
1126
316k
        else if (code < 0 && cdirstrl > 0 && prefix_len == 0 && buffer == bufferfull) {
1127
0
            buffer = bufferfull + cdirstrl + dirsepstrl;
1128
0
            continue;
1129
0
        }
1130
316k
        break;
1131
546k
    }
1132
316k
    if (code > 0 && (mode[0] == 'd' || mode[0] == 'f') &&
1133
316k
        (code & gs_path_control_flag_is_scratch_file) != 0) {
1134
0
        (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, buffer,
1135
0
                                           gs_path_control_flag_is_scratch_file);
1136
0
        (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, buffer,
1137
0
                                           gs_path_control_flag_is_scratch_file);
1138
0
        (void)gs_remove_control_path_flags(mem, gs_permit_file_control, buffer,
1139
0
                                           gs_path_control_flag_is_scratch_file);
1140
0
    }
1141
1142
316k
    gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path");
1143
316k
#ifdef EACCES
1144
316k
    if (code == gs_error_invalidfileaccess)
1145
230k
        errno = EACCES;
1146
316k
#endif
1147
1148
316k
    return code < 0 ? code : 0;
1149
316k
}
1150
1151
int
1152
gp_validate_path(const gs_memory_t *mem,
1153
                 const char        *path,
1154
                 const char        *mode)
1155
369k
{
1156
369k
    return gp_validate_path_len(mem, path, strlen(path), mode);
1157
369k
}
1158
1159
int
1160
gp_unlink(gs_memory_t *mem, const char *fname)
1161
1.33k
{
1162
1.33k
    if (gp_validate_path(mem, fname, "d") != 0)
1163
0
        return gs_error_invalidaccess;
1164
1165
1.33k
    return gp_unlink_impl(mem, fname);
1166
1.33k
}
1167
1168
int
1169
gp_rename(gs_memory_t *mem, const char *from, const char *to)
1170
0
{
1171
    /* Always check 'to' before 'from', in case 'from' is a tempfile,
1172
     * and testing it might remove it from the list! */
1173
0
    if (gp_validate_path(mem, to, "t") != 0)
1174
0
        return gs_error_invalidaccess;
1175
0
    if (gp_validate_path(mem, from, "f") != 0)
1176
0
        return gs_error_invalidaccess;
1177
1178
0
    return gp_rename_impl(mem, from, to);
1179
0
}