Coverage Report

Created: 2026-10-28 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gpmisc.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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
/* 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
264k
{
39
264k
    int max_len = *plen;
40
264k
    int code = gp_getenv("TMPDIR", ptr, plen);
41
42
264k
    if (code != 1)
43
0
        return code;
44
264k
    *plen = max_len;
45
264k
    return gp_getenv("TEMP", ptr, plen);
46
264k
}
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
2.12G
{
109
2.12G
    if (bpe - *bp < len)
110
9.10k
        return false;
111
2.12G
    memcpy(*bp, *ip, len);
112
2.12G
    *bp += len;
113
2.12G
    *ip += len;
114
2.12G
    return true;
115
2.12G
}
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
1.33G
{   uint slen = 0;
121
9.41G
    for (slen = 0; (*ip - ipe) * direction < 0; (*ip) += direction)
122
9.16G
        if((slen = gs_file_name_check_separator(*ip, ipe - *ip, item)) != 0)
123
1.07G
            break;
124
1.33G
    return slen;
125
1.33G
}
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
153M
{
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
153M
    char *bp = buffer, *bpe = buffer + *blen;
163
153M
    const char *ip, *ipe;
164
153M
    uint slen;
165
153M
    uint infix_type = 0; /* 0=none, 1=current, 2=parent. */
166
153M
    uint infix_len = 0;
167
153M
    uint rlen = gp_file_name_root(fname, flen);
168
    /* We need a special handling of infixes only immediately after a drive. */
169
170
153M
    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
153M
    if (rlen != 0) {
181
        /* 'fname' is absolute, ignore the prefix. */
182
0
        ip = fname;
183
0
        ipe = fname + flen;
184
153M
    } else {
185
        /* Concatenate with prefix. */
186
153M
        ip = prefix;
187
153M
        ipe = prefix + plen;
188
153M
        rlen = gp_file_name_root(prefix, plen);
189
153M
    }
190
153M
    if (!append(&bp, bpe, &ip, rlen))
191
0
        return gp_combine_small_buffer;
192
982M
    for (;;) {
193
982M
        const char *item = ip;
194
982M
        uint ilen;
195
196
982M
        slen = search_separator(&ip, ipe, item, 1);
197
982M
        ilen = ip - item;
198
982M
        if (ilen == 0 && !gp_file_name_is_empty_item_meanful()) {
199
200k
            ip += slen;
200
200k
            slen = 0;
201
982M
        } else if (gp_file_name_is_current(item, ilen)) {
202
            /* Skip the reference to 'current', except the starting one.
203
             * We keep the starting 'current' for platforms, which
204
             * require relative paths to start with it.
205
             */
206
20.5k
            if (bp == buffer) {
207
9.14k
                if (!append(&bp, bpe, &item, ilen))
208
0
                    return gp_combine_small_buffer;
209
9.14k
                infix_type = 1;
210
9.14k
                infix_len = ilen;
211
11.3k
            } else {
212
11.3k
                ip += slen;
213
11.3k
                slen = 0;
214
11.3k
            }
215
982M
        } else if (!gp_file_name_is_parent(item, ilen)) {
216
982M
            if (!append(&bp, bpe, &item, ilen))
217
0
                return gp_combine_small_buffer;
218
            /* The 'item' pointer is now broken; it may be restored using 'ilen'. */
219
982M
        } else if (bp == buffer + rlen + infix_len) {
220
            /* Input is a parent and the output only contains a root and an infix. */
221
9
            if (rlen != 0)
222
0
                return gp_combine_cant_handle;
223
9
            switch (infix_type) {
224
3
                case 1:
225
                    /* Replace the infix with parent. */
226
3
                    bp = buffer + rlen; /* Drop the old infix, if any. */
227
3
                    infix_len = 0;
228
                    /* Falls through. */
229
9
                case 0:
230
                    /* We have no infix, start with parent. */
231
9
                    if ((no_sibling && ipe == fname + flen && flen != 0) ||
232
9
                            !gp_file_name_is_parent_allowed())
233
0
                        return gp_combine_cant_handle;
234
                    /* Falls through. */
235
9
                case 2:
236
                    /* Append one more parent - falls through. */
237
9
                    DO_NOTHING;
238
9
            }
239
9
            if (!append(&bp, bpe, &item, ilen))
240
0
                return gp_combine_small_buffer;
241
9
            infix_type = 2;
242
9
            infix_len += ilen;
243
            /* Recompute the separator length. We cannot use the old slen on Mac OS. */
244
9
            slen = gs_file_name_check_separator(ip, ipe - ip, ip);
245
5.87k
        } else {
246
            /* Input is a parent and the output continues after infix. */
247
            /* Unappend the last separator and the last item. */
248
5.87k
            uint slen1 = gs_file_name_check_separator(bp, buffer + rlen - bp, bp); /* Backward search. */
249
5.87k
            char *bie = bp - slen1;
250
251
5.87k
            bp = bie;
252
5.87k
            DISCARD(search_separator((const char **)&bp, buffer + rlen, bp, -1));
253
            /* The cast above quiets a gcc warning. We believe it's a bug in the compiler. */
254
            /* Skip the input with separator. We cannot use slen on Mac OS. */
255
5.87k
            ip += gs_file_name_check_separator(ip, ipe - ip, ip);
256
5.87k
            if (no_sibling) {
257
0
                const char *p = ip;
258
259
0
                DISCARD(search_separator(&p, ipe, ip, 1));
260
0
                if (p - ip != bie - bp || memcmp(ip, bp, p - ip))
261
0
                    return gp_combine_cant_handle;
262
0
            }
263
5.87k
            slen = 0;
264
5.87k
        }
265
982M
        if (slen) {
266
787M
            if (bp == buffer + rlen + infix_len)
267
9.12k
                infix_len += slen;
268
787M
            if (!append(&bp, bpe, &ip, slen))
269
0
                return gp_combine_small_buffer;
270
787M
        }
271
982M
        if (ip == ipe) {
272
209M
            if (ipe == fname + flen) {
273
                /* All done.
274
                 * Note that the case (prefix + plen == fname && flen == 0)
275
                 * falls here without appending a separator.
276
                 */
277
153M
                const char *zero="";
278
279
153M
                if (bp == buffer) {
280
                    /* Must not return empty path. */
281
12
                    const char *current = gp_file_name_current();
282
12
                    int clen = strlen(current);
283
284
12
                    if (!append(&bp, bpe, &current, clen))
285
0
                        return gp_combine_small_buffer;
286
12
                }
287
153M
                *blen = bp - buffer;
288
153M
                if (!append(&bp, bpe, &zero, 1))
289
9.10k
                    return gp_combine_small_buffer;
290
153M
                return gp_combine_success;
291
153M
            } else {
292
                /* ipe == prefix + plen */
293
                /* Switch to fname. */
294
56.4M
                ip = fname;
295
56.4M
                ipe = fname + flen;
296
56.4M
                if (slen == 0) {
297
                    /* Insert a separator. */
298
50.7M
                    const char *sep;
299
300
50.7M
                    slen = search_separator(&ip, ipe, fname, 1);
301
50.7M
                    sep = (slen != 0 ? gp_file_name_directory_separator()
302
50.7M
                                    : gp_file_name_separator());
303
50.7M
                    slen = strlen(sep);
304
50.7M
                    if (bp == buffer + rlen + infix_len)
305
0
                        infix_len += slen;
306
50.7M
                    if (!append(&bp, bpe, &sep, slen))
307
0
                        return gp_combine_small_buffer;
308
50.7M
                    ip = fname; /* Switch to fname. */
309
50.7M
                }
310
56.4M
            }
311
209M
        }
312
982M
    }
313
153M
}
314
315
/*
316
 * Reduces parent references and current directory references when possible.
317
 * The trailing zero byte is being added.
318
 *
319
 * Examples :
320
 *  "/gs/lib/../Resource/CMap/H" --> "/gs/Resource/CMap/H"
321
 *  "C:/gs/lib/../Resource/CMap/H" --> "C:/gs/Resource/CMap/H"
322
 *  "hard disk:gs:lib::Resource:CMap:H" -->
323
 *    "hard disk:gs:Resource:CMap:H"
324
 *  "DUA1:[GHOSTSCRIPT.LIB.-.RESOURCE.CMAP]H" -->
325
 *    "DUA1:[GHOSTSCRIPT.RESOURCE.CMAP]H"
326
 *      "\\server\share/a/b///c/../d.e/./../x.e/././/../../y.z/v.v" -->
327
 *    "\\server\share/a/y.z/v.v"
328
 *
329
 */
330
gp_file_name_combine_result
331
gp_file_name_reduce(const char *fname, uint flen, char *buffer, uint *blen)
332
97.1M
{
333
97.1M
    return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen);
334
97.1M
}
335
336
/*
337
 * Answers whether a file name is absolute (starts from a root).
338
 */
339
bool
340
gp_file_name_is_absolute(const char *fname, uint flen)
341
43.5M
{
342
43.5M
    return (gp_file_name_root(fname, flen) > 0);
343
43.5M
}
344
345
/*
346
 * Returns length of all starting parent references.
347
 */
348
static uint
349
gp_file_name_prefix(const char *fname, uint flen,
350
                bool (*test)(const char *fname, uint flen))
351
38.6M
{
352
38.6M
    uint plen = gp_file_name_root(fname, flen), slen;
353
38.6M
    const char *ip, *ipe;
354
38.6M
    const char *item = fname; /* plen == flen could cause an indeterminizm. */
355
356
38.6M
    if (plen > 0)
357
26.6M
        return 0;
358
11.9M
    ip = fname + plen;
359
11.9M
    ipe = fname + flen;
360
11.9M
    for (; ip < ipe; ) {
361
11.9M
        item = ip;
362
11.9M
        slen = search_separator(&ip, ipe, item, 1);
363
11.9M
        if (!(*test)(item, ip - item))
364
11.9M
            break;
365
3
        ip += slen;
366
3
    }
367
11.9M
    return item - fname;
368
38.6M
}
369
370
/*
371
 * Returns length of all starting parent references.
372
 */
373
uint
374
gp_file_name_parents(const char *fname, uint flen)
375
38.6M
{
376
38.6M
    return gp_file_name_prefix(fname, flen, gp_file_name_is_parent);
377
38.6M
}
378
379
/*
380
 * Returns length of all starting cwd references.
381
 */
382
uint
383
gp_file_name_cwds(const char *fname, uint flen)
384
0
{
385
0
    return gp_file_name_prefix(fname, flen, gp_file_name_is_current);
386
0
}
387
388
static int
389
generic_pread(gp_file *f, size_t count, gs_offset_t offset, void *buf)
390
0
{
391
0
    int c;
392
0
    int64_t os, curroff = gp_ftell(f);
393
0
    if (curroff < 0) return curroff;
394
395
0
    os = gp_fseek(f, offset, 0);
396
0
    if (os < 0) return os;
397
398
0
    c = gp_fread(buf, 1, count, f);
399
0
    if (c < 0) return c;
400
401
0
    os = gp_fseek(f, curroff, 0);
402
0
    if (os < 0) return os;
403
404
0
    return c;
405
0
}
406
407
static int
408
generic_pwrite(gp_file *f, size_t count, gs_offset_t offset, const void *buf)
409
0
{
410
0
    int c;
411
0
    int64_t os, curroff = gp_ftell(f);
412
0
    if (curroff < 0) return curroff;
413
414
0
    os = gp_fseek(f, offset, 0);
415
0
    if (os < 0) return os;
416
417
0
    c = gp_fwrite(buf, 1, count, f);
418
0
    if (c < 0) return c;
419
420
0
    os = gp_fseek(f, curroff, 0);
421
0
    if (os < 0) return os;
422
423
0
    return c;
424
0
}
425
426
gp_file *gp_file_alloc(const gs_memory_t *mem, const gp_file_ops_t *prototype, size_t size, const char *cname)
427
47.7M
{
428
47.7M
    gp_file *file = (gp_file *)gs_alloc_bytes(mem->thread_safe_memory, size, cname ? cname : "gp_file");
429
47.7M
    if (file == NULL)
430
0
        return NULL;
431
432
47.7M
    if (prototype)
433
47.7M
        file->ops = *prototype;
434
47.7M
    if (file->ops.pread == NULL)
435
0
        file->ops.pread = generic_pread;
436
47.7M
    if (file->ops.pwrite == NULL)
437
0
        file->ops.pwrite = generic_pwrite;
438
47.7M
    if (size > sizeof(*prototype))
439
47.7M
        memset(((char *)file)+sizeof(*prototype),
440
47.7M
               0,
441
47.7M
               size - sizeof(*prototype));
442
47.7M
    file->memory = mem->thread_safe_memory;
443
444
47.7M
    return file;
445
47.7M
}
446
447
void gp_file_dealloc(gp_file *file)
448
47.7M
{
449
47.7M
    if (file == NULL)
450
0
        return;
451
452
47.7M
    if (file->buffer)
453
1.41k
        gs_free_object(file->memory, file->buffer, "gp_file");
454
47.7M
    gs_free_object(file->memory, file, "gp_file");
455
47.7M
}
456
457
int gp_fprintf(gp_file *f, const char *fmt, ...)
458
5.09M
{
459
5.09M
    va_list args;
460
5.09M
    int n;
461
462
5.09M
    if (f->buffer)
463
5.09M
        goto mid;
464
1.41k
    do {
465
1.41k
        n = f->buffer_size * 2;
466
1.41k
        if (n == 0)
467
1.41k
            n = 256;
468
1.41k
        gs_free_object(f->memory, f->buffer, "gp_file(buffer)");
469
1.41k
        f->buffer = (char *)gs_alloc_bytes(f->memory, n, "gp_file(buffer)");
470
1.41k
        if (f->buffer == NULL)
471
0
            return -1;
472
1.41k
        f->buffer_size = n;
473
5.09M
mid:
474
5.09M
        va_start(args, fmt);
475
5.09M
        n = vsnprintf(f->buffer, f->buffer_size, fmt, args);
476
5.09M
        va_end(args);
477
5.09M
    } while (n >= f->buffer_size);
478
5.09M
    return (f->ops.write)(f, 1, n, f->buffer);
479
1.41k
}
480
typedef struct {
481
    gp_file base;
482
    FILE *file;
483
    int (*close)(FILE *file);
484
} gp_file_FILE;
485
486
static int
487
gp_file_FILE_close(gp_file *file_)
488
556k
{
489
556k
    gp_file_FILE *file = (gp_file_FILE *)file_;
490
491
556k
    return (file->close)(file->file);
492
556k
}
493
494
static int
495
gp_file_FILE_getc(gp_file *file_)
496
91.9k
{
497
91.9k
    gp_file_FILE *file = (gp_file_FILE *)file_;
498
499
91.9k
    return fgetc(file->file);
500
91.9k
}
501
502
static int
503
gp_file_FILE_putc(gp_file *file_, int c)
504
24.6k
{
505
24.6k
    gp_file_FILE *file = (gp_file_FILE *)file_;
506
507
24.6k
    return fputc(c, file->file);
508
24.6k
}
509
510
static int
511
gp_file_FILE_read(gp_file *file_, size_t size, unsigned int count, void *buf)
512
512M
{
513
512M
    gp_file_FILE *file = (gp_file_FILE *)file_;
514
515
512M
    return fread(buf, size, count, file->file);
516
512M
}
517
518
static int
519
gp_file_FILE_write(gp_file *file_, size_t size, unsigned int count, const void *buf)
520
100M
{
521
100M
    gp_file_FILE *file = (gp_file_FILE *)file_;
522
523
100M
    return fwrite(buf, size, count, file->file);
524
100M
}
525
526
static int
527
gp_file_FILE_seek(gp_file *file_, gs_offset_t offset, int whence)
528
12.5M
{
529
12.5M
    gp_file_FILE *file = (gp_file_FILE *)file_;
530
531
12.5M
    return gp_fseek_impl(file->file, offset, whence);
532
12.5M
}
533
534
static gs_offset_t
535
gp_file_FILE_tell(gp_file *file_)
536
2.79M
{
537
2.79M
    gp_file_FILE *file = (gp_file_FILE *)file_;
538
539
2.79M
    return gp_ftell_impl(file->file);
540
2.79M
}
541
542
static int
543
gp_file_FILE_eof(gp_file *file_)
544
508M
{
545
508M
    gp_file_FILE *file = (gp_file_FILE *)file_;
546
547
508M
    return feof(file->file);
548
508M
}
549
550
static gp_file *
551
gp_file_FILE_dup(gp_file *file_, const char *mode)
552
0
{
553
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
554
0
    gp_file *file2 = gp_file_FILE_alloc(file->base.memory);
555
556
0
    if (gp_file_FILE_set(file2, gp_fdup_impl(file->file, mode), NULL))
557
0
        file2 = NULL;
558
559
0
    return file2;
560
0
}
561
562
static int
563
gp_file_FILE_seekable(gp_file *file_)
564
4.40k
{
565
4.40k
    gp_file_FILE *file = (gp_file_FILE *)file_;
566
567
4.40k
    return gp_fseekable_impl(file->file);
568
4.40k
}
569
570
static int
571
gp_file_FILE_pread(gp_file *file_, size_t count, gs_offset_t offset, void *buf)
572
2.13M
{
573
2.13M
    gp_file_FILE *file = (gp_file_FILE *)file_;
574
575
2.13M
    return gp_pread_impl(buf, count, offset, file->file);
576
2.13M
}
577
578
static int
579
gp_file_FILE_pwrite(gp_file *file_, size_t count, gs_offset_t offset, const void *buf)
580
10.7M
{
581
10.7M
    gp_file_FILE *file = (gp_file_FILE *)file_;
582
583
10.7M
    return gp_pwrite_impl(buf, count, offset, file->file);
584
10.7M
}
585
586
static int
587
gp_file_FILE_is_char_buffered(gp_file *file_)
588
20.4k
{
589
20.4k
    gp_file_FILE *file = (gp_file_FILE *)file_;
590
20.4k
    struct stat rstat;
591
592
20.4k
    if (fstat(fileno(file->file), &rstat) != 0)
593
0
        return ERRC;
594
20.4k
    return S_ISCHR(rstat.st_mode);
595
20.4k
}
596
597
static void
598
gp_file_FILE_fflush(gp_file *file_)
599
47.0M
{
600
47.0M
    gp_file_FILE *file = (gp_file_FILE *)file_;
601
602
47.0M
    fflush(file->file);
603
47.0M
}
604
605
static int
606
gp_file_FILE_ferror(gp_file *file_)
607
68.1M
{
608
68.1M
    gp_file_FILE *file = (gp_file_FILE *)file_;
609
610
68.1M
    return ferror(file->file);
611
68.1M
}
612
613
static FILE *
614
gp_file_FILE_get_file(gp_file *file_)
615
537k
{
616
537k
    gp_file_FILE *file = (gp_file_FILE *)file_;
617
618
537k
    return file->file;
619
537k
}
620
621
static void
622
gp_file_FILE_clearerr(gp_file *file_)
623
108k
{
624
108k
    gp_file_FILE *file = (gp_file_FILE *)file_;
625
626
108k
    clearerr(file->file);
627
108k
}
628
629
static gp_file *
630
gp_file_FILE_reopen(gp_file *file_, const char *fname, const char *mode)
631
0
{
632
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
633
634
0
    file->file = freopen(fname, mode, file->file);
635
0
    if (file->file == NULL) {
636
0
        gp_file_dealloc(file_);
637
0
        return NULL;
638
0
    }
639
0
    return file_;
640
0
}
641
642
static const gp_file_ops_t gp_file_FILE_prototype =
643
{
644
    gp_file_FILE_close,
645
    gp_file_FILE_getc,
646
    gp_file_FILE_putc,
647
    gp_file_FILE_read,
648
    gp_file_FILE_write,
649
    gp_file_FILE_seek,
650
    gp_file_FILE_tell,
651
    gp_file_FILE_eof,
652
    gp_file_FILE_dup,
653
    gp_file_FILE_seekable,
654
    gp_file_FILE_pread,
655
    gp_file_FILE_pwrite,
656
    gp_file_FILE_is_char_buffered,
657
    gp_file_FILE_fflush,
658
    gp_file_FILE_ferror,
659
    gp_file_FILE_get_file,
660
    gp_file_FILE_clearerr,
661
    gp_file_FILE_reopen
662
};
663
664
gp_file *gp_file_FILE_alloc(const gs_memory_t *mem)
665
47.7M
{
666
47.7M
    return gp_file_alloc(mem->non_gc_memory,
667
47.7M
                         &gp_file_FILE_prototype,
668
47.7M
                         sizeof(gp_file_FILE),
669
47.7M
                         "gp_file_FILE");
670
47.7M
}
671
672
int gp_file_FILE_set(gp_file *file_, FILE *f, int (*close)(FILE *))
673
47.7M
{
674
47.7M
    gp_file_FILE *file = (gp_file_FILE *)file_;
675
676
47.7M
    if (f == NULL) {
677
47.1M
        gp_file_dealloc(file_);
678
47.1M
        return 1;
679
47.1M
    }
680
681
556k
    file->file = f;
682
556k
    file->close = close ? close : fclose;
683
684
556k
    return 0;
685
47.7M
}
686
687
char *gp_fgets(char *buffer, size_t n, gp_file *f)
688
0
{
689
0
    int c = EOF;
690
0
    char *b = buffer;
691
0
    while (n > 1) {
692
0
        c = gp_fgetc(f);
693
0
        if (c == 0 || c == EOF)
694
0
            break;
695
0
  *b++ = c;
696
0
  n--;
697
0
        if (c == '\n')
698
0
            break;
699
0
    }
700
0
    if (c == EOF && b == buffer)
701
0
        return NULL;
702
0
    if (gp_ferror(f))
703
0
        return NULL;
704
0
    if (n > 0)
705
0
        *b++ = 0;
706
0
    return buffer;
707
0
}
708
709
gp_file *
710
gp_fopen(const gs_memory_t *mem, const char *fname, const char *mode)
711
47.4M
{
712
47.4M
    gp_file *file = NULL;
713
47.4M
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
714
47.4M
    gs_fs_list_t *fs = ctx->core->fs;
715
716
47.4M
    if (gp_validate_path(mem, fname, mode) != 0)
717
18.1k
        return NULL;
718
719
47.4M
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
720
47.4M
    {
721
47.4M
        int code = 0;
722
47.4M
        if (fs->fs.open_file)
723
47.4M
            code = fs->fs.open_file(mem, fs->secret, fname, mode, &file);
724
47.4M
        if (code < 0)
725
47.1M
            return NULL;
726
218k
        if (file != NULL)
727
218k
            break;
728
218k
    }
729
730
218k
    return file;
731
47.4M
}
732
733
gp_file *
734
gp_open_printer(const gs_memory_t *mem,
735
                      char         fname[gp_file_name_sizeof],
736
                      int          binary_mode)
737
73.2k
{
738
73.2k
    gp_file *file = NULL;
739
73.2k
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
740
73.2k
    gs_fs_list_t *fs = ctx->core->fs;
741
742
73.2k
    if (gp_validate_path(mem, fname, binary_mode ? "wb" : "w") != 0)
743
0
        return NULL;
744
745
73.2k
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
746
73.2k
    {
747
73.2k
        int code = 0;
748
73.2k
        if (fs->fs.open_printer)
749
73.2k
            code = fs->fs.open_printer(mem, fs->secret, fname, binary_mode, &file);
750
73.2k
        if (code < 0)
751
0
            return NULL;
752
73.2k
        if (file != NULL)
753
73.2k
            break;
754
73.2k
    }
755
756
73.2k
    return file;
757
73.2k
}
758
759
static gp_file *
760
do_open_scratch_file(const gs_memory_t *mem,
761
                     const char        *prefix,
762
                     char              *fname,
763
                     const char        *mode,
764
                     int                rm)
765
264k
{
766
264k
    gp_file *file = NULL;
767
264k
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
768
264k
    gs_fs_list_t *fs = ctx->core->fs;
769
264k
    int code = 0;
770
771
    /* If the prefix is absolute, then we must check it's a permissible
772
     * path. If not, we're OK. */
773
264k
    if (gp_file_name_is_absolute(prefix, strlen(prefix)) &&
774
0
        gp_validate_path(mem, prefix, mode) != 0)
775
0
            return NULL;
776
777
264k
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
778
264k
    {
779
264k
        if (fs->fs.open_scratch)
780
264k
            code = fs->fs.open_scratch(mem, fs->secret, prefix, fname, mode, rm, &file);
781
264k
        if (code < 0)
782
0
            return NULL;
783
264k
        if (file != NULL)
784
264k
            break;
785
264k
    }
786
787
264k
    if (file == NULL) {
788
        /* The file failed to open. Don't add it to the list. */
789
264k
    } else if (rm) {
790
        /* This file has already been deleted by the underlying system.
791
         * We don't need to add it to the lists as it will never be
792
         * deleted manually, nor do we need to tidy it up on closedown. */
793
204k
    } else {
794
         /* This file was not requested to be deleted. We add it to the
795
          * list so that it will either be deleted by any future call to
796
          * zdeletefile, OR on closedown. */
797
         /* Add the scratch file name to the lists. We can't do this any
798
          * earlier as we didn't know the name until now! Unfortunately
799
          * that makes cleanup harder. */
800
204k
        code = gs_add_control_path_flags(mem, gs_permit_file_control, fname,
801
204k
                                         gs_path_control_flag_is_scratch_file);
802
204k
        if (code >= 0)
803
204k
            code = gs_add_control_path_flags(mem, gs_permit_file_reading, fname,
804
204k
                                             gs_path_control_flag_is_scratch_file);
805
204k
        if (code >= 0)
806
204k
            code = gs_add_control_path_flags(mem, gs_permit_file_writing, fname,
807
204k
                                         gs_path_control_flag_is_scratch_file);
808
809
204k
        if (code < 0) {
810
0
            gp_fclose(file);
811
0
            file = NULL;
812
            /* Call directly through to the unlink implementation. We know
813
             * we're 'permitted' to do this, but we might not be on all the
814
             * required permit lists because of the failure. The only bad
815
             * thing here, is that we're deleting an fname that might not
816
             * have come from the filing system itself. */
817
0
            if (fname && fname[0])
818
0
                gp_unlink_impl(ctx->memory, fname);
819
0
            (void)gs_remove_control_path_flags(mem, gs_permit_file_control, fname,
820
0
                                               gs_path_control_flag_is_scratch_file);
821
0
            (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, fname,
822
0
                                               gs_path_control_flag_is_scratch_file);
823
0
            (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, fname,
824
0
                                               gs_path_control_flag_is_scratch_file);
825
0
        }
826
204k
    }
827
828
264k
    return file;
829
264k
}
830
831
gp_file *
832
gp_open_scratch_file(const gs_memory_t *mem,
833
                     const char        *prefix,
834
                     char              fname[gp_file_name_sizeof],
835
                     const char        *mode)
836
204k
{
837
204k
    return do_open_scratch_file(mem, prefix, fname, mode, 0);
838
204k
}
839
840
gp_file *
841
gp_open_scratch_file_rm(const gs_memory_t *mem,
842
                        const char        *prefix,
843
                        char              fname[gp_file_name_sizeof],
844
                        const char        *mode)
845
59.7k
{
846
59.7k
    return do_open_scratch_file(mem, prefix, fname, mode, 1);
847
59.7k
}
848
849
int
850
gp_stat(const gs_memory_t *mem, const char *path, struct stat *buf)
851
2
{
852
2
    if (gp_validate_path(mem, path, "r") != 0) {
853
2
        return -1;
854
2
    }
855
856
0
    return gp_stat_impl(mem, path, buf);
857
2
}
858
859
file_enum *
860
gp_enumerate_files_init(gs_memory_t *mem, const char *pat, uint patlen)
861
2.04M
{
862
2.04M
    return gp_enumerate_files_init_impl(mem, pat, patlen);
863
2.04M
}
864
865
uint
866
gp_enumerate_files_next(gs_memory_t *mem, file_enum * pfen, char *ptr, uint maxlen)
867
2.04M
{
868
2.04M
    uint code = 0;
869
870
2.04M
    while (code == 0) {
871
2.04M
        code = gp_enumerate_files_next_impl(mem, pfen, ptr, maxlen);
872
2.04M
        if (code == ~0) break;
873
0
        if (code > 0 && ptr != NULL) {
874
0
            if (gp_validate_path_len(mem, ptr, code, "r") != 0)
875
0
                code = 0;
876
0
        }
877
0
    }
878
2.04M
    return code;
879
2.04M
}
880
void
881
gp_enumerate_files_close(gs_memory_t *mem, file_enum * pfen)
882
2.04M
{
883
2.04M
    gp_enumerate_files_close_impl(mem, pfen);
884
2.04M
}
885
886
/* Path validation: (FIXME: Move this somewhere better)
887
 *
888
 * The only wildcard we accept is '*'.
889
 *
890
 * A '*' at the end of the path means "in this directory,
891
 * or any subdirectory". Anywhere else it means "a sequence of
892
 * characters not including a director separator".
893
 *
894
 * A sequence of multiple '*'s is equivalent to a single one.
895
 *
896
 * Matching on '*' is simplistic; the matching sequence will end
897
 * as soon as we meet an instance of a character that follows
898
 * the '*' in a pattern. i.e. "foo*bar" will fail to match "fooabbar"
899
 * as the '*' will be held to match just 'a'.
900
 *
901
 * There is no way of specifying a literal '*'; if you find yourself
902
 * wanting to do this, slap yourself until you come to your senses.
903
 *
904
 * Due to the difficulties of using both * and / in writing C comments,
905
 * I shall use \ as the directory separator in the examples below, but
906
 * in practice it means "the directory separator for the current
907
 * platform".
908
 *
909
 * Pattern           Match example
910
 *  *                 any file, in any directory at all.
911
 *  foo\bar           a file, foo\bar.
912
 *  foo\bar\          any file within foo\bar\, but no subdirectories.
913
 *  foo\bar\*         any file within foo\bar\ or any subdirectory thereof.
914
 *  foo\*\bar         any file 'bar' within any single subdirectory of foo
915
 *                    (i.e. foo\baz\bar, but not foo\baz\whoop\bar)
916
 *  foo\out*.tif      e.g. foo\out1.tif
917
 *  foo\out*.*.tif*   e.g. foo\out1.(Red).tif
918
 */
919
920
static int
921
validate(const gs_memory_t *mem,
922
         const char        *path,
923
         gs_path_control_t  type)
924
35.1M
{
925
35.1M
    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
926
35.1M
    gs_path_control_set_t *control;
927
35.1M
    unsigned int i, n;
928
929
35.1M
    switch (type) {
930
34.7M
        case gs_permit_file_reading:
931
34.7M
            control = &core->permit_reading;
932
34.7M
            break;
933
86.9k
        case gs_permit_file_writing:
934
86.9k
            control = &core->permit_writing;
935
86.9k
            break;
936
310k
        case gs_permit_file_control:
937
310k
            control = &core->permit_control;
938
310k
            break;
939
0
        default:
940
0
            return gs_error_unknownerror;
941
35.1M
    }
942
943
35.1M
    n = control->num;
944
854M
    for (i = 0; i < n; i++) {
945
854M
        const char *a = path;
946
854M
        const char *b = control->entry[i].path;
947
9.81G
        while (1) {
948
9.81G
            if (*a == 0) {
949
357k
                if (*b == 0)
950
                    /* PATH=abc pattern=abc */
951
349k
                    goto found; /* Bingo! */
952
8.39k
                else
953
                    /* PATH=abc pattern=abcd */
954
8.39k
                    break; /* No match */
955
9.81G
            } else if (*b == '*') {
956
34.6M
                if (b[1] == '*') {
957
                    /* Multiple '*'s are taken to mean the
958
                     * output from a printf. */
959
0
                    b++;
960
0
                    while (b[1] == '*')
961
0
                        b++;
962
                    /* Skip over the permissible matching chars */
963
0
                    while (*a &&
964
0
                           ((*a == ' ' || *a == '-' || *a == '+' ||
965
0
                             (*a >= '0' && *a <= '9') ||
966
0
                             (*a >= 'a' && *a <= 'f') ||
967
0
                             (*a >= 'A' && *a <= 'F'))))
968
0
                            a++;
969
0
                    if (b[1] == 0 && *a == 0)
970
                        /* PATH=abc<%d> pattern=abc** */
971
0
                        goto found;
972
0
                    if (*a == 0)
973
0
                        break; /* No match */
974
34.6M
                } else {
975
34.6M
                    if (b[1] == 0)
976
                        /* PATH=abc???? pattern=abc* */
977
34.6M
                        goto found;
978
                    /* Skip over anything except NUL, directory
979
                     * separator, and the next char to match. */
980
0
                    while (*a && !gs_file_name_check_separator(a, 1, a) && *a != b[1])
981
0
                        a++;
982
0
                    if (*a == 0 || *a == gs_file_name_check_separator(a, 1, a))
983
0
                        break; /* No match */
984
0
                }
985
                /* Continue matching */
986
0
                a--; /* Subtract 1 as the loop will increment it again later */
987
9.77G
            } else if (*b == 0) {
988
34.7M
                if (b != control->entry[i].path &&
989
34.7M
                    gs_file_name_check_separator(b, -1, b)) {
990
0
                    const char *a2 = a;
991
0
                    const char *aend = path + strlen(path);
992
0
                    while (aend != a2 && !gs_file_name_check_separator(a2, 1, a2))
993
0
                      a2++;
994
                    /* If the control path ends in a directory separator and we've scanned
995
                       to the end of the candidate path with no further directory separators
996
                       found, we have a match
997
                     */
998
0
                    if (aend == a2)
999
                      /* PATH=abc/? pattern=abc/ */
1000
0
                      goto found; /* Bingo! */
1001
0
                 }
1002
                /* PATH=abcd pattern=abc */
1003
34.7M
                break; /* No match */
1004
9.74G
            } else if (gs_file_name_check_separator(a, 1, a) == 1
1005
2.23G
                        && gs_file_name_check_separator(b, 1, b) == 1) {
1006
                /* On Windows we can get random combinations of "/" and "\" as directory
1007
                 * separators, and we want "C:\" to match C:/" hence using the pair of
1008
                 * gs_file_name_check_separator() calls above */
1009
                 /* Annoyingly, we can also end up with a combination of explicitly escaped
1010
                  * '\' characters, and not escaped. So we also need "C:\\" to match "C:\"
1011
                  * and "C:/" - hence we need to check for, and skip over the
1012
                  * the extra '\' character - I'm reticent to change the upstream code that
1013
                  * adds the explicit escape, because that could have unforeseen side effects
1014
                  * elsewhere. */
1015
2.02G
                 if (*(a + 1) != 0 && gs_file_name_check_separator(a + 1, 1, a + 1) == 1)
1016
0
                     a++;
1017
2.02G
                 if (*(b + 1) != 0 && gs_file_name_check_separator(b + 1, 1, b + 1) == 1)
1018
0
                     b++;
1019
7.71G
            } else if (*a != *b) {
1020
784M
                break;
1021
784M
            }
1022
8.95G
            a++, b++;
1023
8.95G
        }
1024
854M
    }
1025
104k
    return gs_error_invalidfileaccess;
1026
1027
35.0M
found:
1028
35.0M
    return control->entry[i].flags;
1029
35.1M
}
1030
1031
int
1032
gp_validate_path_len(const gs_memory_t *mem,
1033
                     const char        *path,
1034
                     const uint         len,
1035
                     const char        *mode)
1036
47.8M
{
1037
47.8M
    char *buffer, *bufferfull = NULL;
1038
47.8M
    uint rlen;
1039
47.8M
    int code = 0;
1040
47.8M
    const char *cdirstr = gp_file_name_current();
1041
47.8M
    int cdirstrl = strlen(cdirstr);
1042
47.8M
    const char *dirsepstr = gp_file_name_separator();
1043
47.8M
    int dirsepstrl = strlen(dirsepstr);
1044
47.8M
    int prefix_len = cdirstrl + dirsepstrl;
1045
1046
    /* mem->gs_lib_ctx can be NULL when we're called from mkromfs */
1047
    /* If path == NULL, don't care */
1048
47.8M
    if (path == NULL || mem->gs_lib_ctx == NULL ||
1049
        /* Can't use gs_is_path_control_active(mem) here because it fails to build with mkromfs */
1050
47.8M
        mem->gs_lib_ctx->core->path_control_active == 0)
1051
12.7M
        return 0;
1052
1053
    /* For current directory accesses, we need handle both a "bare" name,
1054
     * and one with a cwd prefix (in Unix terms, both "myfile.ps" and
1055
     * "./myfile.ps".
1056
     *
1057
     * So we check up front if it's absolute, then just use that.
1058
     * If it includes cwd prefix, we try that, then remove the prefix
1059
     * and try again.
1060
     * If it doesn't include the cwd prefix, we try it, then add the
1061
     * prefix and try again.
1062
     * To facilitate that, we allocate a large enough buffer to take
1063
     * the path *and* the prefix up front.
1064
     */
1065
35.1M
    if (gp_file_name_is_absolute(path, len)) {
1066
       /* Absolute path, we don't need anything extra */
1067
35.0M
       prefix_len = cdirstrl = dirsepstrl = 0;
1068
35.0M
    }
1069
9.13k
    else if (len > prefix_len && !memcmp(path, cdirstr, cdirstrl)
1070
203
             && !memcmp(path + cdirstrl, dirsepstr, dirsepstrl)) {
1071
0
          prefix_len = 0;
1072
0
    }
1073
1074
    /* "%pipe%" do not follow the normal rules for path definitions, so we
1075
       don't "reduce" them to avoid unexpected results
1076
     */
1077
35.1M
    if (path[0] == '|' || (len > 5 && memcmp(path, "%pipe", 5) == 0)) {
1078
10
        bufferfull = buffer = (char *)gs_alloc_bytes(mem->thread_safe_memory, len + 1, "gp_validate_path");
1079
10
        if (buffer == NULL)
1080
0
            return gs_error_VMerror;
1081
10
        memcpy(buffer, path, len);
1082
10
        buffer[len] = 0;
1083
10
        rlen = len;
1084
10
    }
1085
35.1M
    else {
1086
35.1M
        char *test = (char *)path, *test1;
1087
35.1M
        uint tlen = len, slen;
1088
35.1M
        gp_file_name_combine_result res;
1089
1090
        /* Look for any pipe (%pipe% or '|' specifications between path separators
1091
         * Reject any path spec which has a %pipe% or '|' anywhere except at the start.
1092
         */
1093
285M
        while (tlen > 0) {
1094
285M
            if (test[0] == '|' || (tlen > 5 && memcmp(test, "%pipe", 5) == 0)) {
1095
1.16k
                code = gs_note_error(gs_error_invalidfileaccess);
1096
1.16k
                goto exit;
1097
1.16k
            }
1098
285M
            test1 = test;
1099
285M
            slen = search_separator((const char **)&test, path + len, test1, 1);
1100
285M
            if(slen == 0)
1101
33.1M
                break;
1102
251M
            test += slen;
1103
251M
            tlen -= test - test1;
1104
251M
            if (test >= path + len)
1105
1.98M
                break;
1106
251M
        }
1107
1108
35.1M
        rlen = len+1;
1109
        /* There is a very, very small chance that gp_file_name_reduce() can return a longer
1110
           string than simply rlen + prefix_len. The do/while loop is to cope with that.
1111
         */
1112
35.1M
        do {
1113
35.1M
            bufferfull = (char *)gs_alloc_bytes(mem->thread_safe_memory, rlen + prefix_len, "gp_validate_path");
1114
35.1M
            if (bufferfull == NULL)
1115
0
                return gs_error_VMerror;
1116
1117
35.1M
            buffer = bufferfull + prefix_len;
1118
35.1M
            res = gp_file_name_reduce(path, (uint)len, buffer, &rlen);
1119
35.1M
            if (res == gp_combine_small_buffer) {
1120
6
                rlen += 1;
1121
6
                gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path");
1122
6
                rlen += 1;
1123
6
                continue;
1124
6
            }
1125
35.1M
            if (res != gp_combine_success) {
1126
0
                code = gs_note_error(gs_error_invalidfileaccess);
1127
0
                goto exit;
1128
0
            }
1129
35.1M
        } while (res != gp_combine_success);
1130
35.1M
        buffer[rlen] = 0;
1131
35.1M
    }
1132
35.1M
    while (1) {
1133
35.1M
        int modebyte = 0;
1134
1135
70.1M
        for (modebyte = 0; modebyte < strlen(mode); modebyte++) {
1136
35.1M
            switch (mode[modebyte])
1137
35.1M
            {
1138
34.7M
            case 'r': /* Read */
1139
34.7M
                code = validate(mem, buffer, gs_permit_file_reading);
1140
34.7M
                break;
1141
64.8k
            case 'w': /* Write */
1142
64.8k
                code = validate(mem, buffer, gs_permit_file_writing);
1143
64.8k
                break;
1144
22.0k
            case '+': /* read and Write */
1145
22.0k
                code = (validate(mem, buffer, gs_permit_file_reading) |
1146
22.0k
                        validate(mem, buffer, gs_permit_file_writing));
1147
22.0k
                break;
1148
0
            case 'a': /* Append needs reading and writing */
1149
0
                code = (validate(mem, buffer, gs_permit_file_reading) |
1150
0
                        validate(mem, buffer, gs_permit_file_writing));
1151
0
                break;
1152
0
            case 'c': /* "Control" */
1153
0
                code =  validate(mem, buffer, gs_permit_file_control);
1154
0
                break;
1155
310k
            case 'd': /* "Delete" (special case of control) */
1156
310k
                code =  validate(mem, buffer, gs_permit_file_control);
1157
310k
                break;
1158
0
            case 'f': /* "Rename from" */
1159
0
                code = (validate(mem, buffer, gs_permit_file_writing) |
1160
0
                        validate(mem, buffer, gs_permit_file_control));
1161
0
                break;
1162
0
            case 't': /* "Rename to" */
1163
0
                code = (validate(mem, buffer, gs_permit_file_writing) |
1164
0
                        validate(mem, buffer, gs_permit_file_control));
1165
0
                break;
1166
42.9k
            case 'b': /* binary */
1167
42.9k
                code = 0;
1168
42.9k
                break;
1169
0
            default:
1170
0
                errprintf(mem, "gp_validate_path: Unknown mode='%s'\n", mode);
1171
0
                code = gs_note_error(gs_error_invalidfileaccess);
1172
0
                goto exit;
1173
35.1M
            }
1174
35.1M
            if (code < 0 && prefix_len > 0 && buffer > bufferfull) {
1175
9.12k
                uint newlen = rlen + cdirstrl + dirsepstrl;
1176
9.12k
                char *newbuffer;
1177
9.12k
                int code;
1178
1179
9.12k
                buffer = bufferfull;
1180
9.12k
                memcpy(buffer, cdirstr, cdirstrl);
1181
9.12k
                memcpy(buffer + cdirstrl, dirsepstr, dirsepstrl);
1182
1183
                /* We've prepended a './' or similar for the current working directory. We need
1184
                 * to execute file_name_reduce on that, to eliminate any '../' or similar from
1185
                 * the (new) full path.
1186
                 */
1187
9.12k
                newbuffer = (char *)gs_alloc_bytes(mem->thread_safe_memory, newlen + 1, "gp_validate_path");
1188
9.12k
                if (newbuffer == NULL) {
1189
0
                    code = gs_note_error(gs_error_VMerror);
1190
0
                    goto exit;
1191
0
                }
1192
1193
9.12k
                memcpy(newbuffer, buffer, rlen + cdirstrl + dirsepstrl);
1194
9.12k
                newbuffer[newlen] = 0x00;
1195
1196
9.12k
                code = gp_file_name_reduce(newbuffer, (uint)newlen, buffer, &newlen);
1197
9.12k
                gs_free_object(mem->thread_safe_memory, newbuffer, "gp_validate_path");
1198
9.12k
                if (code != gp_combine_success) {
1199
9.09k
                    code = gs_note_error(gs_error_invalidfileaccess);
1200
9.09k
                    goto exit;
1201
9.09k
                }
1202
1203
22
                continue;
1204
9.12k
            }
1205
35.1M
            else if (code < 0 && cdirstrl > 0 && prefix_len == 0 && buffer == bufferfull
1206
0
                && memcmp(buffer, cdirstr, cdirstrl) && !memcmp(buffer + cdirstrl, dirsepstr, dirsepstrl)) {
1207
0
                continue;
1208
0
            }
1209
            /* Early exit if access is not permitted */
1210
35.1M
            if (code < 0)
1211
95.8k
                break;
1212
35.1M
        }
1213
35.0M
        break;
1214
35.1M
    }
1215
35.0M
    if (code > 0 && (mode[0] == 'd' || mode[0] == 'f') &&
1216
204k
        (code & gs_path_control_flag_is_scratch_file) != 0) {
1217
204k
        (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, buffer,
1218
204k
                                           gs_path_control_flag_is_scratch_file);
1219
204k
        (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, buffer,
1220
204k
                                           gs_path_control_flag_is_scratch_file);
1221
204k
        (void)gs_remove_control_path_flags(mem, gs_permit_file_control, buffer,
1222
204k
                                           gs_path_control_flag_is_scratch_file);
1223
204k
    }
1224
1225
35.1M
exit:
1226
35.1M
    gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path");
1227
35.1M
#ifdef EACCES
1228
35.1M
    if (code == gs_error_invalidfileaccess)
1229
35.1M
        errno = EACCES;
1230
35.1M
#endif
1231
1232
35.1M
    return code < 0 ? code : 0;
1233
35.0M
}
1234
1235
int
1236
gp_validate_path(const gs_memory_t *mem,
1237
                 const char        *path,
1238
                 const char        *mode)
1239
47.8M
{
1240
47.8M
    return gp_validate_path_len(mem, path, strlen(path), mode);
1241
47.8M
}
1242
1243
int
1244
gp_unlink(gs_memory_t *mem, const char *fname)
1245
310k
{
1246
310k
    if (gp_validate_path(mem, fname, "d") != 0)
1247
87.9k
        return gs_error_invalidaccess;
1248
1249
222k
    return gp_unlink_impl(mem, fname);
1250
310k
}
1251
1252
int
1253
gp_rename(gs_memory_t *mem, const char *from, const char *to)
1254
0
{
1255
    /* Always check 'to' before 'from', in case 'from' is a tempfile,
1256
     * and testing it might remove it from the list! */
1257
0
    if (gp_validate_path(mem, to, "t") != 0)
1258
0
        return gs_error_invalidaccess;
1259
0
    if (gp_validate_path(mem, from, "f") != 0)
1260
0
        return gs_error_invalidaccess;
1261
1262
0
    return gp_rename_impl(mem, from, to);
1263
0
}