Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/base/gp_unifs.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* "Unix-like" file system platform routines for Ghostscript */
18
19
/* prevent gp.h from defining fopen */
20
#define fopen fopen
21
22
#include "stdio_.h"             /* for FILENAME_MAX */
23
#include "memory_.h"
24
#include "string_.h"
25
#include "gx.h"
26
27
#include "gp.h"
28
#include "gpmisc.h"
29
#include "gsstruct.h"
30
#include "gsutil.h"             /* for string_match */
31
#include "stat_.h"
32
#include "dirent_.h"
33
#include "unistd_.h"
34
#include <stdlib.h>             /* for mkstemp/mktemp */
35
36
#if !defined(HAVE_FSEEKO)
37
#define ftello ftell
38
#define fseeko fseek
39
#define ftello64 ftell
40
#define fseeko64 fseek
41
#endif
42
43
/* Provide a definition of the maximum path length in case the system
44
 * headers don't define it. This should be gp_file_name_sizeof from
45
 * gp.h once that value is properly sent in a system-dependent way.
46
 * HP-UX 11i 11.11 incorrectly defines FILENAME_MAX as 14.
47
 */
48
#ifdef FILENAME_MAX
49
#  if FILENAME_MAX < 80  /* arbitrary */
50
#    undef FILENAME_MAX
51
#  endif
52
#endif
53
#ifndef FILENAME_MAX
54
#  define FILENAME_MAX 1024
55
#endif
56
57
/* Library routines not declared in a standard header */
58
extern char *mktemp(char *);
59
60
/* ------ File naming and accessing ------ */
61
62
/* Define the default scratch file name prefix. */
63
const char gp_scratch_file_name_prefix[] = "gs_";
64
65
/* Define the name of the null output file. */
66
const char gp_null_file_name[] = "/dev/null";
67
68
/* Define the name that designates the current directory. */
69
const char gp_current_directory_name[] = ".";
70
71
/* Create and open a scratch file with a given name prefix. */
72
/* Write the actual file name at fname. */
73
FILE *
74
gp_open_scratch_file_impl(const gs_memory_t *mem,
75
                          const char        *prefix,
76
                                char         fname[gp_file_name_sizeof],
77
                          const char        *mode,
78
                                int          remove)
79
5.54k
{       /* The -8 is for XXXXXX plus a possible final / and -. */
80
#ifdef GS_NO_FILESYSTEM
81
    return NULL;
82
#else
83
5.54k
    int prefix_length = strlen(prefix);
84
5.54k
    int len = gp_file_name_sizeof - prefix_length - 8;
85
5.54k
    FILE *fp;
86
87
5.54k
    if (gp_file_name_is_absolute(prefix, prefix_length))
88
0
        *fname = 0;
89
5.54k
    else if (gp_gettmpdir(fname, &len) != 0)
90
5.54k
        strcpy(fname, "/tmp/");
91
0
    else {
92
0
        if (strlen(fname) != 0 && fname[strlen(fname) - 1] != '/')
93
0
            strcat(fname, "/");
94
0
    }
95
5.54k
    if (strlen(fname) + prefix_length + 8 >= gp_file_name_sizeof)
96
0
        return 0;               /* file name too long */
97
5.54k
    strcat(fname, prefix);
98
    /* Prevent trailing X's in path from being converted by mktemp. */
99
5.54k
    if (*fname != 0 && fname[strlen(fname) - 1] == 'X')
100
0
        strcat(fname, "-");
101
5.54k
    strcat(fname, "XXXXXX");
102
103
5.54k
#ifdef HAVE_MKSTEMP
104
5.54k
    {
105
5.54k
        int file;
106
5.54k
        char ofname[gp_file_name_sizeof];
107
108
        /* save the old filename template in case mkstemp fails */
109
5.54k
        memcpy(ofname, fname, gp_file_name_sizeof);
110
5.54k
#  ifdef HAVE_MKSTEMP64
111
5.54k
        file = mkstemp64(fname);
112
#  else
113
        file = mkstemp(fname);
114
#  endif
115
5.54k
        if (file < 0) {
116
0
            emprintf1(mem, "**** Could not open temporary file %s\n", ofname);
117
0
            return NULL;
118
0
        }
119
#  if defined(O_LARGEFILE) && defined(__hpux)
120
        fcntl(file, F_SETFD, fcntl(file, F_GETFD) | O_LARGEFILE);
121
#  else
122
        /* Fixme : what to do with b64 and 32-bit mkstemp? Unimplemented. */
123
5.54k
#  endif
124
125
5.54k
        fp = fdopen(file, mode);
126
5.54k
        if (fp == NULL) {
127
0
      close(file);
128
0
  }
129
5.54k
    }
130
#else
131
    /* Coverity thinks that any use of mktemp() is insecure. But if we
132
    reach here then there is no mkstemp() alternative available, so there's
133
    not much we can do. Haven't been able to disable this - e.g. '//
134
    coverity[SECURE_TEMP]' doesn't have any affect. */
135
    mktemp(fname);
136
    fp = gp_fopentemp(fname, mode);
137
#endif
138
5.54k
    if (fp == NULL)
139
0
        emprintf1(mem, "**** Could not open temporary file %s\n", fname);
140
141
5.54k
    if (remove)
142
0
        unlink(fname); /* unlink, not gp_unlink here. */
143
144
5.54k
    return fp;
145
5.54k
#endif
146
5.54k
}
147
148
/* Open a file with the given name, as a stream of uninterpreted bytes. */
149
FILE *
150
gp_fopen_impl(gs_memory_t *mem, const char *fname, const char *mode)
151
4.49M
{
152
4.49M
#if defined(HAVE_FILE64)
153
4.49M
    return fopen64(fname, mode);
154
#else
155
    return fopen(fname, mode);
156
#endif
157
4.49M
}
158
159
int
160
gp_unlink_impl(gs_memory_t *mem, const char *fname)
161
16.5k
{
162
16.5k
    return unlink(fname);
163
16.5k
}
164
165
int
166
gp_rename_impl(gs_memory_t *mem, const char *from, const char *to)
167
0
{
168
0
    return rename(from, to);
169
0
}
170
171
int gp_stat_impl(const gs_memory_t *mem, const char *path, struct stat *buf)
172
0
{
173
0
    return stat(path, buf);
174
0
}
175
176
int gp_can_share_fdesc(void)
177
0
{
178
0
#if defined(HAVE_PREAD_PWRITE) && HAVE_PREAD_PWRITE == 1
179
0
    return 1;
180
#else
181
    return 0; /* can't share FILE * descriptors w/o pread due to seek..read..seek */
182
#endif
183
0
}
184
185
FILE *gp_fdup_impl(FILE *f, const char *mode)
186
0
{
187
#ifdef GS_NO_FILESYSTEM
188
    return NULL;
189
#else
190
0
    int fd = fileno(f);
191
0
    if (fd < 0)
192
0
        return NULL;
193
0
    fd = dup(fd);
194
0
    if (fd < 0)
195
0
        return NULL;
196
0
    return fdopen(fd, mode);
197
0
#endif
198
0
}
199
200
int gp_pread_impl(char *buf, size_t count, gs_offset_t offset, FILE *f)
201
0
{
202
#ifdef GS_NO_FILESYSTEM
203
    return 0;
204
#elif defined(HAVE_PREAD_PWRITE) && HAVE_PREAD_PWRITE == 1
205
    return pread(fileno(f), buf, count, offset);
206
#else
207
    int c;
208
    int64_t os, curroff = gp_ftell_impl(f);
209
    if (curroff < 0) return curroff;
210
211
    os = gp_fseek_impl(f, offset, 0);
212
    if (os < 0) return os;
213
214
    c = fread(buf, 1, count, f);
215
    if (c < 0) return c;
216
217
    os = gp_fseek_impl(f, curroff, 0);
218
    if (os < 0) return os;
219
220
    return c;
221
#endif
222
0
}
223
224
int gp_pwrite_impl(const char *buf, size_t count, gs_offset_t offset, FILE *f)
225
0
{
226
#ifdef GS_NO_FILESYSTEM
227
    return 0;
228
#elif defined(HAVE_PREAD_PWRITE) && HAVE_PREAD_PWRITE == 1
229
    return pwrite(fileno(f), buf, count, offset);
230
#else
231
    int c;
232
    int64_t os, curroff = gp_ftell_impl(f);
233
    if (curroff < 0) return curroff;
234
235
    os = gp_fseek_impl(f, offset, 0);
236
    if (os < 0) return os;
237
238
    c = fwrite(buf, 1, count, f);
239
    if (c < 0) return c;
240
241
    os = gp_fseek_impl(f, curroff, 0);
242
    if (os < 0) return os;
243
244
    return c;
245
#endif
246
0
}
247
248
/* Set a file into binary or text mode. */
249
int
250
gp_setmode_binary_impl(FILE * pfile, bool mode) /* lgtm [cpp/useless-expression] */
251
5.17k
{
252
5.17k
    return 0;                   /* Noop under Unix */
253
5.17k
}
254
255
/* ------ File enumeration ------ */
256
257
/* Thanks to Fritz Elfert (Fritz_Elfert@wue.maus.de) for */
258
/* the original version of the following code, and Richard Mlynarik */
259
/* (mly@adoc.xerox.com) for an improved version. */
260
261
#ifdef GS_NO_FILESYSTEM
262
struct file_enum_s {
263
    int dummy;
264
};
265
266
static file_enum dummy_enum;
267
#else
268
typedef struct dirstack_s dirstack;
269
struct dirstack_s {
270
    dirstack *next;
271
    DIR *entry;
272
};
273
274
gs_private_st_ptrs1(st_dirstack, dirstack, "dirstack",
275
                    dirstack_enum_ptrs, dirstack_reloc_ptrs, next);
276
277
struct file_enum_s {
278
    DIR *dirp;                  /* pointer to current open directory   */
279
    char *pattern;              /* original pattern                    */
280
    char *work;                 /* current path                        */
281
    int worklen;                /* strlen (work)                       */
282
    dirstack *dstack;           /* directory stack                     */
283
    int patlen;
284
    int pathead;                /* how much of pattern to consider
285
                                 *  when listing files in current directory */
286
    bool first_time;
287
    gs_memory_t *memory;
288
};
289
gs_private_st_ptrs3(st_file_enum, struct file_enum_s, "file_enum",
290
          file_enum_enum_ptrs, file_enum_reloc_ptrs, pattern, work, dstack);
291
292
/* Private procedures */
293
294
/* Do a wild-card match. */
295
#ifdef DEBUG
296
static bool
297
wmatch(const byte * str, uint len, const byte * pstr, uint plen,
298
       const gs_memory_t *mem)
299
{
300
    bool match = string_match(str, len, pstr, plen, NULL);
301
302
    if (gs_debug_c('e')) {
303
        int i;
304
        dmlputs(mem, "[e]string_match(\"");
305
        for (i=0; i<len; i++)
306
            errprintf(mem, "%c", str[i]);
307
        dmputs(mem, "\", \"");
308
        for (i=0; i<plen; i++)
309
            errprintf(mem, "%c", pstr[i]);
310
        dmprintf1(mem, "\") = %s\n", (match ? "TRUE" : "false"));
311
    }
312
    return match;
313
}
314
#else
315
0
#define wmatch(S,L,PS,PL,M) string_match(S,L,PS,PL,NULL)
316
#endif
317
318
/* Search a string backward for a character. */
319
/* (This substitutes for strrchr, which some systems don't provide.) */
320
static char *
321
rchr(char *str, char ch, int len)
322
119k
{
323
119k
    register char *p = str + len;
324
325
238k
    while (p > str)
326
238k
        if (*--p == ch)
327
119k
            return p;
328
0
    return 0;
329
119k
}
330
331
/* Pop a directory from the enumeration stack. */
332
static bool
333
popdir(file_enum * pfen)
334
119k
{
335
119k
    dirstack *d = pfen->dstack;
336
337
119k
    if (d == 0)
338
119k
        return false;
339
0
    pfen->dirp = d->entry;
340
0
    pfen->dstack = d->next;
341
0
    gs_free_object(pfen->memory, d, "gp_enumerate_files(popdir)");
342
0
    return true;
343
119k
}
344
#endif
345
346
/* Initialize an enumeration. */
347
file_enum *
348
gp_enumerate_files_init_impl(gs_memory_t * mem, const char *pat, uint patlen)
349
119k
{
350
#ifdef GS_NO_FILESYSTEM
351
    return &dummy_enum;
352
#else
353
119k
    file_enum *pfen;
354
119k
    char *p;
355
119k
    char *work;
356
357
    /* Reject attempts to enumerate paths longer than the */
358
    /* system-dependent limit. */
359
119k
    if (patlen > FILENAME_MAX)
360
0
        return 0;
361
362
    /* Reject attempts to enumerate with a pattern containing zeroes. */
363
119k
    {
364
119k
        const char *p1;
365
366
5.81M
        for (p1 = pat; p1 < pat + patlen; p1++)
367
5.69M
            if (*p1 == 0)
368
0
                return 0;
369
119k
    }
370
    /* >>> Should crunch strings of repeated "/"'s in pat to a single "/"
371
     * >>>  to match stupid unix filesystem "conventions" */
372
373
119k
    pfen = gs_alloc_struct(mem, file_enum, &st_file_enum,
374
119k
                           "gp_enumerate_files");
375
119k
    if (pfen == 0)
376
0
        return 0;
377
378
    /* pattern and work could be allocated as strings, */
379
    /* but it's simpler for GC and freeing to allocate them as bytes. */
380
381
119k
    pfen->memory = mem;
382
119k
    pfen->dstack = 0;
383
119k
    pfen->first_time = true;
384
119k
    pfen->patlen = patlen;
385
119k
    pfen->work = 0;
386
119k
    pfen->pattern =
387
119k
        (char *)gs_alloc_bytes(mem, patlen + 1,
388
119k
                               "gp_enumerate_files(pattern)");
389
119k
    if (pfen->pattern == 0)
390
0
        goto fail1;
391
119k
    memcpy(pfen->pattern, pat, patlen);
392
119k
    pfen->pattern[patlen] = 0;
393
394
119k
    work = (char *)gs_alloc_bytes(mem, FILENAME_MAX + 1,
395
119k
                                  "gp_enumerate_files(work)");
396
119k
    if (work == 0)
397
0
        goto fail2;
398
119k
    pfen->work = work;
399
400
119k
    p = work;
401
119k
    memcpy(p, pat, patlen);
402
119k
    p += patlen;
403
119k
    *p = 0;
404
405
    /* Remove directory specifications beyond the first wild card. */
406
    /* Some systems don't have strpbrk, so we code it open. */
407
119k
    p = pfen->work;
408
5.69M
    while (*p != '*' && *p != '?' && *p != 0)
409
5.57M
        p++;
410
238k
    while (*p != '/' && *p != 0)
411
119k
        p++;
412
119k
    if (*p == '/')
413
0
        *p = 0;
414
    /* Substring for first wildcard match */
415
119k
    pfen->pathead = p - work;
416
417
    /* Select the next higher directory-level. */
418
119k
    p = rchr(work, '/', p - work);
419
119k
    if (!p) {                   /* No directory specification */
420
0
        work[0] = 0;
421
0
        pfen->worklen = 0;
422
119k
    } else {
423
119k
        if (p == work) {        /* Root directory -- don't turn "/" into "" */
424
0
            p++;
425
0
        }
426
119k
        *p = 0;
427
119k
        pfen->worklen = p - work;
428
119k
    }
429
430
119k
    return pfen;
431
432
0
fail2:
433
0
    gs_free_object(mem, pfen->pattern, "gp_enumerate_files(pattern)");
434
0
fail1:
435
0
    gs_free_object(mem, pfen, "gp_enumerate_files");
436
0
    return NULL;
437
0
#endif
438
0
}
439
440
/* Enumerate the next file. */
441
uint
442
gp_enumerate_files_next_impl(gs_memory_t * mem, file_enum * pfen, char *ptr, uint maxlen)
443
119k
{
444
#ifdef GS_NO_FILESYSTEM
445
    return ~(uint)0;
446
#else
447
119k
    const dir_entry *de;
448
119k
    char *work = pfen->work;
449
119k
    int worklen = pfen->worklen;
450
119k
    char *pattern = pfen->pattern;
451
119k
    int pathead = pfen->pathead;
452
119k
    int len;
453
454
119k
    if (pfen->first_time) {
455
119k
        pfen->dirp = ((worklen == 0) ? opendir(".") : opendir(work));
456
119k
        if_debug1m('e', pfen->memory, "[e]file_enum:First-Open '%s'\n", work);
457
119k
        pfen->first_time = false;
458
119k
        if (pfen->dirp == 0) {  /* first opendir failed */
459
119k
            gp_enumerate_files_close(mem, pfen);
460
119k
            return ~(uint) 0;
461
119k
        }
462
119k
    }
463
0
  top:de = readdir(pfen->dirp);
464
0
    if (de == 0) {              /* No more entries in this directory */
465
0
        char *p;
466
467
0
        if_debug0m('e', pfen->memory, "[e]file_enum:Closedir\n");
468
0
        closedir(pfen->dirp);
469
        /* Back working directory and matching pattern up one level */
470
0
        p = rchr(work, '/', worklen);
471
0
        if (p != 0) {
472
0
            if (p == work)
473
0
                p++;
474
0
            *p = 0;
475
0
            worklen = p - work;
476
0
        } else
477
0
            worklen = 0;
478
0
        if (pathead != pfen->patlen) {
479
0
            p = rchr(pattern, '/', pathead);
480
0
            if (p != 0)
481
0
                pathead = p - pattern;
482
0
            else
483
0
                pathead = 0;
484
0
        }
485
486
0
        if (popdir(pfen)) {     /* Back up the directory tree. */
487
0
            if_debug1m('e', pfen->memory, "[e]file_enum:Dir popped '%s'\n", work);
488
0
            goto top;
489
0
        } else {
490
0
            if_debug0m('e', pfen->memory, "[e]file_enum:Dirstack empty\n");
491
0
            gp_enumerate_files_close(mem, pfen);
492
0
            return ~(uint) 0;
493
0
        }
494
0
    }
495
    /* Skip . and .. */
496
0
    len = strlen(de->d_name);
497
0
    if (len <= 2 && (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")))
498
0
        goto top;
499
0
    if (len + worklen + 1 > FILENAME_MAX)
500
        /* Should be an error, I suppose */
501
0
        goto top;
502
0
    if (worklen == 0) {         /* "Current" directory (evil un*x kludge) */
503
0
        memcpy(work, de->d_name, len + 1);
504
0
    } else if (worklen == 1 && work[0] == '/') {        /* Root directory */
505
0
        memcpy(work + 1, de->d_name, len + 1);
506
0
        len = len + 1;
507
0
    } else {
508
0
        work[worklen] = '/';
509
0
        memcpy(work + worklen + 1, de->d_name, len + 1);
510
0
        len = worklen + 1 + len;
511
0
    }
512
513
    /* Test for a match at this directory level */
514
0
    if (!wmatch((byte *) work, len, (byte *) pattern, pathead, pfen->memory))
515
0
        goto top;
516
517
    /* Perhaps descend into subdirectories */
518
0
    if (pathead < maxlen) {
519
        /* Using stat() to decide whether this item is a directory then opening
520
        using opendir(), results in Coverity complaining about races. Se
521
        instead we simple call opendir() immediately and look at whether it
522
        succeeded. */
523
0
        DIR *dp = opendir(work);
524
0
        if (!dp) {
525
            /* Not a directory. */
526
0
            goto winner;
527
0
        }
528
529
0
        if (pfen->patlen == pathead + 1) {      /* Listing "foo/?/" -- return this entry */
530
0
            closedir(dp);
531
0
            work[len++] = '/';
532
0
            goto winner;
533
0
        }
534
535
        /* >>> Should optimise the case in which the next level */
536
        /* >>> of directory has no wildcards. */
537
#ifdef DEBUG
538
        {
539
            char save_end = pattern[pathead];
540
541
            pattern[pathead] = 0;
542
            if_debug2m('e', pfen->memory, "[e]file_enum:fname='%s', p='%s'\n",
543
                       work, pattern);
544
            pattern[pathead] = save_end;
545
        }
546
#endif /* DEBUG */
547
0
        {                  /* Advance to the next directory-delimiter */
548
            /* in pattern */
549
0
            char *p;
550
0
            dirstack *d;
551
552
0
      if (pattern[pathead] == 0) {
553
0
                pathead = pfen->patlen;
554
0
      } else {
555
0
                for (p = pattern + pathead + 1;; p++) {
556
0
                    if (*p == 0) {  /* No more subdirectories to match */
557
0
                        pathead = pfen->patlen;
558
0
                        break;
559
0
                    } else if (*p == '/') {
560
0
                        pathead = p - pattern;
561
0
                        break;
562
0
                    }
563
0
                }
564
0
            }
565
566
            /* Push a directory onto the enumeration stack. */
567
0
            d = gs_alloc_struct(pfen->memory, dirstack,
568
0
                                &st_dirstack,
569
0
                                "gp_enumerate_files(pushdir)");
570
0
            if (d != 0) {
571
0
                d->next = pfen->dstack;
572
0
                d->entry = pfen->dirp;
573
0
                pfen->dstack = d;
574
0
            } else
575
0
                DO_NOTHING;     /* >>> gs_error_VMerror!!! */
576
577
0
            if_debug1m('e', pfen->memory, "[e]file_enum:Dir pushed '%s'\n",
578
0
                       work);
579
0
            worklen = len;
580
0
            pfen->dirp = dp;
581
0
            goto top;
582
0
        }
583
0
    }
584
0
  winner:
585
    /* We have a winner! */
586
0
    pfen->worklen = worklen;
587
0
    pfen->pathead = pathead;
588
0
    memcpy(ptr, work, len > maxlen ? maxlen : len);
589
590
0
    return len;
591
0
#endif
592
0
}
593
594
/* Clean up the file enumeration. */
595
void
596
gp_enumerate_files_close_impl(gs_memory_t * mem, file_enum * pfen)
597
119k
{
598
#ifdef GS_NO_FILESYSTEM
599
    /* No cleanup necessary */
600
#else
601
119k
    gs_memory_t *mem2 = pfen->memory;
602
119k
    (void)mem;
603
604
119k
    if_debug0m('e', mem2, "[e]file_enum:Cleanup\n");
605
119k
    while (popdir(pfen))        /* clear directory stack */
606
119k
        DO_NOTHING;
607
119k
    gs_free_object(mem2, (byte *) pfen->work,
608
119k
                   "gp_enumerate_close(work)");
609
119k
    gs_free_object(mem2, (byte *) pfen->pattern,
610
119k
                   "gp_enumerate_files_close(pattern)");
611
119k
    gs_free_object(mem2, pfen, "gp_enumerate_files_close");
612
119k
#endif
613
119k
}
614
615
/* Test-cases:
616
   (../?*r*?/?*.ps) {==} 100 string filenameforall
617
   (../?*r*?/?*.ps*) {==} 100 string filenameforall
618
   (../?*r*?/) {==} 100 string filenameforall
619
   (/t*?/?*.ps) {==} 100 string filenameforall
620
 */
621
622
/* --------- 64 bit file access ----------- */
623
624
gs_offset_t gp_ftell_impl(FILE *strm)
625
27.6k
{
626
27.6k
#if defined(HAVE_FILE64)
627
27.6k
    return ftello64(strm);
628
#else
629
    return ftello(strm);
630
#endif
631
27.6k
}
632
633
int gp_fseek_impl(FILE *strm, gs_offset_t offset, int origin)
634
505k
{
635
505k
#if defined(HAVE_FILE64)
636
505k
    return fseeko64(strm, offset, origin);
637
#else
638
    off_t offset1 = (off_t)offset;
639
640
    if (offset != offset1)
641
        return -1;
642
    return fseeko(strm, offset1, origin);
643
#endif
644
505k
}
645
646
bool gp_fseekable_impl(FILE *f)
647
0
{
648
0
    struct stat s;
649
0
    int fno;
650
651
0
    fno = fileno(f);
652
0
    if (fno < 0)
653
0
        return(false);
654
655
0
    if (fstat(fno, &s) < 0)
656
0
        return(false);
657
658
0
    return((bool)S_ISREG(s.st_mode));
659
0
}