Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gpmisc.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
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
287k
{
39
287k
    int max_len = *plen;
40
287k
    int code = gp_getenv("TMPDIR", ptr, plen);
41
42
287k
    if (code != 1)
43
0
        return code;
44
287k
    *plen = max_len;
45
287k
    return gp_getenv("TEMP", ptr, plen);
46
287k
}
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
3.17G
{
109
3.17G
    if (bpe - *bp < len)
110
11.2k
        return false;
111
3.17G
    memcpy(*bp, *ip, len);
112
3.17G
    *bp += len;
113
3.17G
    *ip += len;
114
3.17G
    return true;
115
3.17G
}
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
2.06G
{   uint slen = 0;
121
15.0G
    for (slen = 0; (*ip - ipe) * direction < 0; (*ip) += direction)
122
14.6G
        if((slen = gs_file_name_check_separator(*ip, ipe - *ip, item)) != 0)
123
1.67G
            break;
124
2.06G
    return slen;
125
2.06G
}
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
226M
{
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
226M
    char *bp = buffer, *bpe = buffer + *blen;
163
226M
    const char *ip, *ipe;
164
226M
    uint slen;
165
226M
    uint infix_type = 0; /* 0=none, 1=current, 2=parent. */
166
226M
    uint infix_len = 0;
167
226M
    uint rlen = gp_file_name_root(fname, flen);
168
    /* We need a special handling of infixes only immediately after a drive. */
169
170
226M
    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
226M
    if (rlen != 0) {
181
        /* 'fname' is absolute, ignore the prefix. */
182
0
        ip = fname;
183
0
        ipe = fname + flen;
184
226M
    } else {
185
        /* Concatenate with prefix. */
186
226M
        ip = prefix;
187
226M
        ipe = prefix + plen;
188
226M
        rlen = gp_file_name_root(prefix, plen);
189
226M
    }
190
226M
    if (!append(&bp, bpe, &ip, rlen))
191
0
        return gp_combine_small_buffer;
192
1.46G
    for (;;) {
193
1.46G
        const char *item = ip;
194
1.46G
        uint ilen;
195
196
1.46G
        slen = search_separator(&ip, ipe, item, 1);
197
1.46G
        ilen = ip - item;
198
1.46G
        if (ilen == 0 && !gp_file_name_is_empty_item_meanful()) {
199
198k
            ip += slen;
200
198k
            slen = 0;
201
1.46G
        } 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
29.1k
            if (bp == buffer) {
207
11.2k
                if (!append(&bp, bpe, &item, ilen))
208
0
                    return gp_combine_small_buffer;
209
11.2k
                infix_type = 1;
210
11.2k
                infix_len = ilen;
211
17.8k
            } else {
212
17.8k
                ip += slen;
213
17.8k
                slen = 0;
214
17.8k
            }
215
1.46G
        } else if (!gp_file_name_is_parent(item, ilen)) {
216
1.46G
            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
1.46G
        } else if (bp == buffer + rlen + infix_len) {
220
            /* Input is a parent and the output only contains a root and an infix. */
221
15
            if (rlen != 0)
222
0
                return gp_combine_cant_handle;
223
15
            switch (infix_type) {
224
5
                case 1:
225
                    /* Replace the infix with parent. */
226
5
                    bp = buffer + rlen; /* Drop the old infix, if any. */
227
5
                    infix_len = 0;
228
                    /* Falls through. */
229
15
                case 0:
230
                    /* We have no infix, start with parent. */
231
15
                    if ((no_sibling && ipe == fname + flen && flen != 0) ||
232
15
                            !gp_file_name_is_parent_allowed())
233
0
                        return gp_combine_cant_handle;
234
                    /* Falls through. */
235
15
                case 2:
236
                    /* Append one more parent - falls through. */
237
15
                    DO_NOTHING;
238
15
            }
239
15
            if (!append(&bp, bpe, &item, ilen))
240
0
                return gp_combine_small_buffer;
241
15
            infix_type = 2;
242
15
            infix_len += ilen;
243
            /* Recompute the separator length. We cannot use the old slen on Mac OS. */
244
15
            slen = gs_file_name_check_separator(ip, ipe - ip, ip);
245
11.5k
        } else {
246
            /* Input is a parent and the output continues after infix. */
247
            /* Unappend the last separator and the last item. */
248
11.5k
            uint slen1 = gs_file_name_check_separator(bp, buffer + rlen - bp, bp); /* Backward search. */
249
11.5k
            char *bie = bp - slen1;
250
251
11.5k
            bp = bie;
252
11.5k
            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
11.5k
            ip += gs_file_name_check_separator(ip, ipe - ip, ip);
256
11.5k
            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
11.5k
            slen = 0;
264
11.5k
        }
265
1.46G
        if (slen) {
266
1.17G
            if (bp == buffer + rlen + infix_len)
267
11.2k
                infix_len += slen;
268
1.17G
            if (!append(&bp, bpe, &ip, slen))
269
0
                return gp_combine_small_buffer;
270
1.17G
        }
271
1.46G
        if (ip == ipe) {
272
314M
            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
226M
                const char *zero="";
278
279
226M
                if (bp == buffer) {
280
                    /* Must not return empty path. */
281
18
                    const char *current = gp_file_name_current();
282
18
                    int clen = strlen(current);
283
284
18
                    if (!append(&bp, bpe, &current, clen))
285
0
                        return gp_combine_small_buffer;
286
18
                }
287
226M
                *blen = bp - buffer;
288
226M
                if (!append(&bp, bpe, &zero, 1))
289
11.2k
                    return gp_combine_small_buffer;
290
226M
                return gp_combine_success;
291
226M
            } else {
292
                /* ipe == prefix + plen */
293
                /* Switch to fname. */
294
87.4M
                ip = fname;
295
87.4M
                ipe = fname + flen;
296
87.4M
                if (slen == 0) {
297
                    /* Insert a separator. */
298
78.7M
                    const char *sep;
299
300
78.7M
                    slen = search_separator(&ip, ipe, fname, 1);
301
78.7M
                    sep = (slen != 0 ? gp_file_name_directory_separator()
302
78.7M
                                    : gp_file_name_separator());
303
78.7M
                    slen = strlen(sep);
304
78.7M
                    if (bp == buffer + rlen + infix_len)
305
0
                        infix_len += slen;
306
78.7M
                    if (!append(&bp, bpe, &sep, slen))
307
0
                        return gp_combine_small_buffer;
308
78.7M
                    ip = fname; /* Switch to fname. */
309
78.7M
                }
310
87.4M
            }
311
314M
        }
312
1.46G
    }
313
226M
}
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
139M
{
333
139M
    return gp_file_name_combine(fname, flen, fname + flen, 0, false, buffer, blen);
334
139M
}
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
75.0M
{
342
75.0M
    return (gp_file_name_root(fname, flen) > 0);
343
75.0M
}
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
53.1M
{
352
53.1M
    uint plen = gp_file_name_root(fname, flen), slen;
353
53.1M
    const char *ip, *ipe;
354
53.1M
    const char *item = fname; /* plen == flen could cause an indeterminizm. */
355
356
53.1M
    if (plen > 0)
357
37.1M
        return 0;
358
15.9M
    ip = fname + plen;
359
15.9M
    ipe = fname + flen;
360
15.9M
    for (; ip < ipe; ) {
361
15.9M
        item = ip;
362
15.9M
        slen = search_separator(&ip, ipe, item, 1);
363
15.9M
        if (!(*test)(item, ip - item))
364
15.9M
            break;
365
5
        ip += slen;
366
5
    }
367
15.9M
    return item - fname;
368
53.1M
}
369
370
/*
371
 * Returns length of all starting parent references.
372
 */
373
uint
374
gp_file_name_parents(const char *fname, uint flen)
375
53.1M
{
376
53.1M
    return gp_file_name_prefix(fname, flen, gp_file_name_is_parent);
377
53.1M
}
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
74.7M
{
428
74.7M
    gp_file *file = (gp_file *)gs_alloc_bytes(mem->thread_safe_memory, size, cname ? cname : "gp_file");
429
74.7M
    if (file == NULL)
430
0
        return NULL;
431
432
74.7M
    if (prototype)
433
74.7M
        file->ops = *prototype;
434
74.7M
    if (file->ops.pread == NULL)
435
0
        file->ops.pread = generic_pread;
436
74.7M
    if (file->ops.pwrite == NULL)
437
0
        file->ops.pwrite = generic_pwrite;
438
74.7M
    if (size > sizeof(*prototype))
439
74.7M
        memset(((char *)file)+sizeof(*prototype),
440
74.7M
               0,
441
74.7M
               size - sizeof(*prototype));
442
74.7M
    file->memory = mem->thread_safe_memory;
443
444
74.7M
    return file;
445
74.7M
}
446
447
void gp_file_dealloc(gp_file *file)
448
74.7M
{
449
74.7M
    if (file == NULL)
450
0
        return;
451
452
74.7M
    if (file->buffer)
453
0
        gs_free_object(file->memory, file->buffer, "gp_file");
454
74.7M
    gs_free_object(file->memory, file, "gp_file");
455
74.7M
}
456
457
int gp_fprintf(gp_file *f, const char *fmt, ...)
458
0
{
459
0
    va_list args;
460
0
    int n;
461
462
0
    if (f->buffer)
463
0
        goto mid;
464
0
    do {
465
0
        n = f->buffer_size * 2;
466
0
        if (n == 0)
467
0
            n = 256;
468
0
        gs_free_object(f->memory, f->buffer, "gp_file(buffer)");
469
0
        f->buffer = (char *)gs_alloc_bytes(f->memory, n, "gp_file(buffer)");
470
0
        if (f->buffer == NULL)
471
0
            return -1;
472
0
        f->buffer_size = n;
473
0
mid:
474
0
        va_start(args, fmt);
475
0
        n = vsnprintf(f->buffer, f->buffer_size, fmt, args);
476
0
        va_end(args);
477
0
    } while (n >= f->buffer_size);
478
0
    return (f->ops.write)(f, 1, n, f->buffer);
479
0
}
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
577k
{
489
577k
    gp_file_FILE *file = (gp_file_FILE *)file_;
490
491
577k
    return (file->close)(file->file);
492
577k
}
493
494
static int
495
gp_file_FILE_getc(gp_file *file_)
496
0
{
497
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
498
499
0
    return fgetc(file->file);
500
0
}
501
502
static int
503
gp_file_FILE_putc(gp_file *file_, int c)
504
17.8k
{
505
17.8k
    gp_file_FILE *file = (gp_file_FILE *)file_;
506
507
17.8k
    return fputc(c, file->file);
508
17.8k
}
509
510
static int
511
gp_file_FILE_read(gp_file *file_, size_t size, unsigned int count, void *buf)
512
814M
{
513
814M
    gp_file_FILE *file = (gp_file_FILE *)file_;
514
515
814M
    return fread(buf, size, count, file->file);
516
814M
}
517
518
static int
519
gp_file_FILE_write(gp_file *file_, size_t size, unsigned int count, const void *buf)
520
126M
{
521
126M
    gp_file_FILE *file = (gp_file_FILE *)file_;
522
523
126M
    return fwrite(buf, size, count, file->file);
524
126M
}
525
526
static int
527
gp_file_FILE_seek(gp_file *file_, gs_offset_t offset, int whence)
528
18.0M
{
529
18.0M
    gp_file_FILE *file = (gp_file_FILE *)file_;
530
531
18.0M
    return gp_fseek_impl(file->file, offset, whence);
532
18.0M
}
533
534
static gs_offset_t
535
gp_file_FILE_tell(gp_file *file_)
536
4.98M
{
537
4.98M
    gp_file_FILE *file = (gp_file_FILE *)file_;
538
539
4.98M
    return gp_ftell_impl(file->file);
540
4.98M
}
541
542
static int
543
gp_file_FILE_eof(gp_file *file_)
544
805M
{
545
805M
    gp_file_FILE *file = (gp_file_FILE *)file_;
546
547
805M
    return feof(file->file);
548
805M
}
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.18k
{
565
4.18k
    gp_file_FILE *file = (gp_file_FILE *)file_;
566
567
4.18k
    return gp_fseekable_impl(file->file);
568
4.18k
}
569
570
static int
571
gp_file_FILE_pread(gp_file *file_, size_t count, gs_offset_t offset, void *buf)
572
7.19M
{
573
7.19M
    gp_file_FILE *file = (gp_file_FILE *)file_;
574
575
7.19M
    return gp_pread_impl(buf, count, offset, file->file);
576
7.19M
}
577
578
static int
579
gp_file_FILE_pwrite(gp_file *file_, size_t count, gs_offset_t offset, const void *buf)
580
42.3M
{
581
42.3M
    gp_file_FILE *file = (gp_file_FILE *)file_;
582
583
42.3M
    return gp_pwrite_impl(buf, count, offset, file->file);
584
42.3M
}
585
586
static int
587
gp_file_FILE_is_char_buffered(gp_file *file_)
588
0
{
589
0
    gp_file_FILE *file = (gp_file_FILE *)file_;
590
0
    struct stat rstat;
591
592
0
    if (fstat(fileno(file->file), &rstat) != 0)
593
0
        return ERRC;
594
0
    return S_ISCHR(rstat.st_mode);
595
0
}
596
597
static void
598
gp_file_FILE_fflush(gp_file *file_)
599
83.7M
{
600
83.7M
    gp_file_FILE *file = (gp_file_FILE *)file_;
601
602
83.7M
    fflush(file->file);
603
83.7M
}
604
605
static int
606
gp_file_FILE_ferror(gp_file *file_)
607
162M
{
608
162M
    gp_file_FILE *file = (gp_file_FILE *)file_;
609
610
162M
    return ferror(file->file);
611
162M
}
612
613
static FILE *
614
gp_file_FILE_get_file(gp_file *file_)
615
590k
{
616
590k
    gp_file_FILE *file = (gp_file_FILE *)file_;
617
618
590k
    return file->file;
619
590k
}
620
621
static void
622
gp_file_FILE_clearerr(gp_file *file_)
623
94.5k
{
624
94.5k
    gp_file_FILE *file = (gp_file_FILE *)file_;
625
626
94.5k
    clearerr(file->file);
627
94.5k
}
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
74.7M
{
666
74.7M
    return gp_file_alloc(mem->non_gc_memory,
667
74.7M
                         &gp_file_FILE_prototype,
668
74.7M
                         sizeof(gp_file_FILE),
669
74.7M
                         "gp_file_FILE");
670
74.7M
}
671
672
int gp_file_FILE_set(gp_file *file_, FILE *f, int (*close)(FILE *))
673
74.7M
{
674
74.7M
    gp_file_FILE *file = (gp_file_FILE *)file_;
675
676
74.7M
    if (f == NULL) {
677
74.2M
        gp_file_dealloc(file_);
678
74.2M
        return 1;
679
74.2M
    }
680
681
577k
    file->file = f;
682
577k
    file->close = close ? close : fclose;
683
684
577k
    return 0;
685
74.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
74.4M
{
712
74.4M
    gp_file *file = NULL;
713
74.4M
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
714
74.4M
    gs_fs_list_t *fs = ctx->core->fs;
715
716
74.4M
    if (gp_validate_path(mem, fname, mode) != 0)
717
26.8k
        return NULL;
718
719
74.4M
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
720
74.4M
    {
721
74.4M
        int code = 0;
722
74.4M
        if (fs->fs.open_file)
723
74.4M
            code = fs->fs.open_file(mem, fs->secret, fname, mode, &file);
724
74.4M
        if (code < 0)
725
74.2M
            return NULL;
726
217k
        if (file != NULL)
727
217k
            break;
728
217k
    }
729
730
217k
    return file;
731
74.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
72.2k
{
738
72.2k
    gp_file *file = NULL;
739
72.2k
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
740
72.2k
    gs_fs_list_t *fs = ctx->core->fs;
741
742
72.2k
    if (gp_validate_path(mem, fname, binary_mode ? "wb" : "w") != 0)
743
0
        return NULL;
744
745
72.2k
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
746
72.2k
    {
747
72.2k
        int code = 0;
748
72.2k
        if (fs->fs.open_printer)
749
72.2k
            code = fs->fs.open_printer(mem, fs->secret, fname, binary_mode, &file);
750
72.2k
        if (code < 0)
751
0
            return NULL;
752
72.2k
        if (file != NULL)
753
72.2k
            break;
754
72.2k
    }
755
756
72.2k
    return file;
757
72.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
287k
{
766
287k
    gp_file *file = NULL;
767
287k
    gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
768
287k
    gs_fs_list_t *fs = ctx->core->fs;
769
287k
    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
287k
    if (gp_file_name_is_absolute(prefix, strlen(prefix)) &&
774
287k
        gp_validate_path(mem, prefix, mode) != 0)
775
0
            return NULL;
776
777
287k
    for (fs = ctx->core->fs; fs != NULL; fs = fs->next)
778
287k
    {
779
287k
        if (fs->fs.open_scratch)
780
287k
            code = fs->fs.open_scratch(mem, fs->secret, prefix, fname, mode, rm, &file);
781
287k
        if (code < 0)
782
0
            return NULL;
783
287k
        if (file != NULL)
784
287k
            break;
785
287k
    }
786
787
287k
    if (file == NULL) {
788
        /* The file failed to open. Don't add it to the list. */
789
287k
    } 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
215k
    } 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
215k
        code = gs_add_control_path_flags(mem, gs_permit_file_control, fname,
801
215k
                                         gs_path_control_flag_is_scratch_file);
802
215k
        if (code >= 0)
803
215k
            code = gs_add_control_path_flags(mem, gs_permit_file_reading, fname,
804
215k
                                             gs_path_control_flag_is_scratch_file);
805
215k
        if (code >= 0)
806
215k
            code = gs_add_control_path_flags(mem, gs_permit_file_writing, fname,
807
215k
                                         gs_path_control_flag_is_scratch_file);
808
809
215k
        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
215k
    }
827
828
287k
    return file;
829
287k
}
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
215k
{
837
215k
    return do_open_scratch_file(mem, prefix, fname, mode, 0);
838
215k
}
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
72.8k
{
846
72.8k
    return do_open_scratch_file(mem, prefix, fname, mode, 1);
847
72.8k
}
848
849
int
850
gp_stat(const gs_memory_t *mem, const char *path, struct stat *buf)
851
3
{
852
3
    if (gp_validate_path(mem, path, "r") != 0) {
853
3
        return -1;
854
3
    }
855
856
0
    return gp_stat_impl(mem, path, buf);
857
3
}
858
859
file_enum *
860
gp_enumerate_files_init(gs_memory_t *mem, const char *pat, uint patlen)
861
1.78M
{
862
1.78M
    return gp_enumerate_files_init_impl(mem, pat, patlen);
863
1.78M
}
864
865
uint
866
gp_enumerate_files_next(gs_memory_t *mem, file_enum * pfen, char *ptr, uint maxlen)
867
1.78M
{
868
1.78M
    uint code = 0;
869
870
1.78M
    while (code == 0) {
871
1.78M
        code = gp_enumerate_files_next_impl(mem, pfen, ptr, maxlen);
872
1.78M
        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
1.78M
    return code;
879
1.78M
}
880
void
881
gp_enumerate_files_close(gs_memory_t *mem, file_enum * pfen)
882
1.78M
{
883
1.78M
    gp_enumerate_files_close_impl(mem, pfen);
884
1.78M
}
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
62.1M
{
925
62.1M
    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
926
62.1M
    gs_path_control_set_t *control;
927
62.1M
    unsigned int i, n;
928
929
62.1M
    switch (type) {
930
61.7M
        case gs_permit_file_reading:
931
61.7M
            control = &core->permit_reading;
932
61.7M
            break;
933
61.5k
        case gs_permit_file_writing:
934
61.5k
            control = &core->permit_writing;
935
61.5k
            break;
936
325k
        case gs_permit_file_control:
937
325k
            control = &core->permit_control;
938
325k
            break;
939
0
        default:
940
0
            return gs_error_unknownerror;
941
62.1M
    }
942
943
62.1M
    n = control->num;
944
1.58G
    for (i = 0; i < n; i++) {
945
1.58G
        const char *a = path;
946
1.58G
        const char *b = control->entry[i].path;
947
17.5G
        while (1) {
948
17.5G
            if (*a == 0) {
949
178k
                if (*b == 0)
950
                    /* PATH=abc pattern=abc */
951
163k
                    goto found; /* Bingo! */
952
15.0k
                else
953
                    /* PATH=abc pattern=abcd */
954
15.0k
                    break; /* No match */
955
17.5G
            } else if (*b == '*') {
956
61.9M
                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
61.9M
                } else {
975
61.9M
                    if (b[1] == 0)
976
                        /* PATH=abc???? pattern=abc* */
977
61.9M
                        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
17.4G
            } else if (*b == 0) {
988
61.7M
                if (b != control->entry[i].path &&
989
61.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
61.7M
                break; /* No match */
1004
17.4G
            } else if (gs_file_name_check_separator(a, 1, a) == 1
1005
17.4G
                        && 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
3.67G
                 if (*(a + 1) != 0 && gs_file_name_check_separator(a + 1, 1, a + 1) == 1)
1016
0
                     a++;
1017
3.67G
                 if (*(b + 1) != 0 && gs_file_name_check_separator(b + 1, 1, b + 1) == 1)
1018
0
                     b++;
1019
13.7G
            } else if (*a != *b) {
1020
1.45G
                break;
1021
1.45G
            }
1022
15.9G
            a++, b++;
1023
15.9G
        }
1024
1.58G
    }
1025
25.5k
    return gs_error_invalidfileaccess;
1026
1027
62.0M
found:
1028
62.0M
    return control->entry[i].flags;
1029
62.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
74.8M
{
1037
74.8M
    char *buffer, *bufferfull = NULL;
1038
74.8M
    uint rlen;
1039
74.8M
    int code = 0;
1040
74.8M
    const char *cdirstr = gp_file_name_current();
1041
74.8M
    int cdirstrl = strlen(cdirstr);
1042
74.8M
    const char *dirsepstr = gp_file_name_separator();
1043
74.8M
    int dirsepstrl = strlen(dirsepstr);
1044
74.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
74.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
74.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
62.1M
    if (gp_file_name_is_absolute(path, len)) {
1066
       /* Absolute path, we don't need anything extra */
1067
62.1M
       prefix_len = cdirstrl = dirsepstrl = 0;
1068
62.1M
    }
1069
11.2k
    else if (len > prefix_len && !memcmp(path, cdirstr, cdirstrl)
1070
11.2k
             && !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
62.1M
    if (path[0] == '|' || (len > 5 && memcmp(path, "%pipe", 5) == 0)) {
1078
12
        bufferfull = buffer = (char *)gs_alloc_bytes(mem->thread_safe_memory, len + 1, "gp_validate_path");
1079
12
        if (buffer == NULL)
1080
0
            return gs_error_VMerror;
1081
12
        memcpy(buffer, path, len);
1082
12
        buffer[len] = 0;
1083
12
        rlen = len;
1084
12
    }
1085
62.1M
    else {
1086
62.1M
        char *test = (char *)path, *test1;
1087
62.1M
        uint tlen = len, slen;
1088
62.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
503M
        while (tlen > 0) {
1094
503M
            if (test[0] == '|' || (tlen > 5 && memcmp(test, "%pipe", 5) == 0)) {
1095
1.32k
                code = gs_note_error(gs_error_invalidfileaccess);
1096
1.32k
                goto exit;
1097
1.32k
            }
1098
503M
            test1 = test;
1099
503M
            slen = search_separator((const char **)&test, path + len, test1, 1);
1100
503M
            if(slen == 0)
1101
57.2M
                break;
1102
446M
            test += slen;
1103
446M
            tlen -= test - test1;
1104
446M
            if (test >= path + len)
1105
4.87M
                break;
1106
446M
        }
1107
1108
62.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
62.1M
        do {
1113
62.1M
            bufferfull = (char *)gs_alloc_bytes(mem->thread_safe_memory, rlen + prefix_len, "gp_validate_path");
1114
62.1M
            if (bufferfull == NULL)
1115
0
                return gs_error_VMerror;
1116
1117
62.1M
            buffer = bufferfull + prefix_len;
1118
62.1M
            res = gp_file_name_reduce(path, (uint)len, buffer, &rlen);
1119
62.1M
            if (res == gp_combine_small_buffer) {
1120
9
                rlen += 1;
1121
9
                gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path");
1122
9
                rlen += 1;
1123
9
                continue;
1124
9
            }
1125
62.1M
            if (res != gp_combine_success) {
1126
0
                code = gs_note_error(gs_error_invalidfileaccess);
1127
0
                goto exit;
1128
0
            }
1129
62.1M
        } while (res != gp_combine_success);
1130
62.1M
        buffer[rlen] = 0;
1131
62.1M
    }
1132
62.1M
    while (1) {
1133
62.1M
        switch (mode[0])
1134
62.1M
        {
1135
61.7M
        case 'r': /* Read */
1136
61.7M
            code = validate(mem, buffer, gs_permit_file_reading);
1137
61.7M
            break;
1138
61.5k
        case 'w': /* Write */
1139
61.5k
            code = validate(mem, buffer, gs_permit_file_writing);
1140
61.5k
            break;
1141
0
        case 'a': /* Append needs reading and writing */
1142
0
            code = (validate(mem, buffer, gs_permit_file_reading) |
1143
0
                    validate(mem, buffer, gs_permit_file_writing));
1144
0
            break;
1145
0
        case 'c': /* "Control" */
1146
0
            code =  validate(mem, buffer, gs_permit_file_control);
1147
0
            break;
1148
325k
        case 'd': /* "Delete" (special case of control) */
1149
325k
            code =  validate(mem, buffer, gs_permit_file_control);
1150
325k
            break;
1151
0
        case 'f': /* "Rename from" */
1152
0
            code = (validate(mem, buffer, gs_permit_file_writing) |
1153
0
                    validate(mem, buffer, gs_permit_file_control));
1154
0
            break;
1155
0
        case 't': /* "Rename to" */
1156
0
            code = (validate(mem, buffer, gs_permit_file_writing) |
1157
0
                    validate(mem, buffer, gs_permit_file_control));
1158
0
            break;
1159
0
        default:
1160
0
            errprintf(mem, "gp_validate_path: Unknown mode='%s'\n", mode);
1161
0
            code = gs_note_error(gs_error_invalidfileaccess);
1162
62.1M
        }
1163
62.1M
        if (code < 0 && prefix_len > 0 && buffer > bufferfull) {
1164
11.2k
            uint newlen = rlen + cdirstrl + dirsepstrl;
1165
11.2k
            char *newbuffer;
1166
11.2k
            int code;
1167
1168
11.2k
            buffer = bufferfull;
1169
11.2k
            memcpy(buffer, cdirstr, cdirstrl);
1170
11.2k
            memcpy(buffer + cdirstrl, dirsepstr, dirsepstrl);
1171
1172
            /* We've prepended a './' or similar for the current working directory. We need
1173
             * to execute file_name_reduce on that, to eliminate any '../' or similar from
1174
             * the (new) full path.
1175
             */
1176
11.2k
            newbuffer = (char *)gs_alloc_bytes(mem->thread_safe_memory, newlen + 1, "gp_validate_path");
1177
11.2k
            if (newbuffer == NULL) {
1178
0
                code = gs_note_error(gs_error_VMerror);
1179
0
                goto exit;
1180
0
            }
1181
1182
11.2k
            memcpy(newbuffer, buffer, rlen + cdirstrl + dirsepstrl);
1183
11.2k
            newbuffer[newlen] = 0x00;
1184
1185
11.2k
            code = gp_file_name_reduce(newbuffer, (uint)newlen, buffer, &newlen);
1186
11.2k
            gs_free_object(mem->thread_safe_memory, newbuffer, "gp_validate_path");
1187
11.2k
            if (code != gp_combine_success) {
1188
11.1k
                code = gs_note_error(gs_error_invalidfileaccess);
1189
11.1k
                goto exit;
1190
11.1k
            }
1191
1192
32
            continue;
1193
11.2k
        }
1194
62.0M
        else if (code < 0 && cdirstrl > 0 && prefix_len == 0 && buffer == bufferfull
1195
62.0M
            && memcmp(buffer, cdirstr, cdirstrl) && !memcmp(buffer + cdirstrl, dirsepstr, dirsepstrl)) {
1196
0
            continue;
1197
0
        }
1198
62.0M
        break;
1199
62.1M
    }
1200
62.0M
    if (code > 0 && (mode[0] == 'd' || mode[0] == 'f') &&
1201
62.0M
        (code & gs_path_control_flag_is_scratch_file) != 0) {
1202
102k
        (void)gs_remove_control_path_flags(mem, gs_permit_file_reading, buffer,
1203
102k
                                           gs_path_control_flag_is_scratch_file);
1204
102k
        (void)gs_remove_control_path_flags(mem, gs_permit_file_writing, buffer,
1205
102k
                                           gs_path_control_flag_is_scratch_file);
1206
102k
        (void)gs_remove_control_path_flags(mem, gs_permit_file_control, buffer,
1207
102k
                                           gs_path_control_flag_is_scratch_file);
1208
102k
    }
1209
1210
62.1M
exit:
1211
62.1M
    gs_free_object(mem->thread_safe_memory, bufferfull, "gp_validate_path");
1212
62.1M
#ifdef EACCES
1213
62.1M
    if (code == gs_error_invalidfileaccess)
1214
26.8k
        errno = EACCES;
1215
62.1M
#endif
1216
1217
62.1M
    return code < 0 ? code : 0;
1218
62.0M
}
1219
1220
int
1221
gp_validate_path(const gs_memory_t *mem,
1222
                 const char        *path,
1223
                 const char        *mode)
1224
74.8M
{
1225
74.8M
    return gp_validate_path_len(mem, path, strlen(path), mode);
1226
74.8M
}
1227
1228
int
1229
gp_unlink(gs_memory_t *mem, const char *fname)
1230
325k
{
1231
325k
    if (gp_validate_path(mem, fname, "d") != 0)
1232
15
        return gs_error_invalidaccess;
1233
1234
325k
    return gp_unlink_impl(mem, fname);
1235
325k
}
1236
1237
int
1238
gp_rename(gs_memory_t *mem, const char *from, const char *to)
1239
0
{
1240
    /* Always check 'to' before 'from', in case 'from' is a tempfile,
1241
     * and testing it might remove it from the list! */
1242
0
    if (gp_validate_path(mem, to, "t") != 0)
1243
0
        return gs_error_invalidaccess;
1244
0
    if (gp_validate_path(mem, from, "f") != 0)
1245
0
        return gs_error_invalidaccess;
1246
1247
0
    return gp_rename_impl(mem, from, to);
1248
0
}