Coverage Report

Created: 2026-07-16 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcups/cups/file.c
Line
Count
Source
1
//
2
// File functions for CUPS.
3
//
4
// Since stdio files max out at 256 files on many systems, we have to
5
// write similar functions without this limit.  At the same time, using
6
// our own file functions allows us to provide transparent support of
7
// different line endings, gzip'd print files, etc.
8
//
9
// Copyright © 2021-2026 by OpenPrinting.
10
// Copyright © 2007-2019 by Apple Inc.
11
// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
12
//
13
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
14
// information.
15
//
16
17
#include "file-private.h"
18
#include "debug-internal.h"
19
#include <sys/stat.h>
20
#include <sys/types.h>
21
#include <zlib.h>
22
#ifndef va_copy
23
#  define va_copy(__list1, __list2) ((void)(__list1 = __list2))
24
#endif
25
26
27
//
28
// Internal structures...
29
//
30
31
struct _cups_file_s     // CUPS file structure...
32
{
33
  int   fd;     // File descriptor
34
  bool    compressed;   // Compression used?
35
  char    mode,     // Mode ('r' or 'w')
36
    buf[4096],    // Buffer
37
    *ptr,     // Pointer into buffer
38
    *end;     // End of buffer data
39
  bool    is_stdio,   // stdin/out/err?
40
    eof;      // End of file?
41
  off_t   pos,      // Position in file
42
    bufpos;     // File position for start of buffer
43
44
  z_stream  stream;     // (De)compression stream
45
  Bytef   cbuf[4096];   // (De)compression buffer
46
  uLong   crc;      // (De)compression CRC
47
48
  char    *printf_buffer;   // cupsFilePrintf buffer
49
  size_t  printf_size;    // Size of cupsFilePrintf buffer
50
};
51
52
53
//
54
// Local functions...
55
//
56
57
static bool cups_compress(cups_file_t *fp, const char *buf, size_t bytes);
58
static ssize_t  cups_fill(cups_file_t *fp);
59
static int  cups_open(const char *filename, int oflag, int mode);
60
static ssize_t  cups_read(cups_file_t *fp, char *buf, size_t bytes);
61
static bool cups_write(cups_file_t *fp, const char *buf, size_t bytes);
62
63
64
//
65
// 'cupsFileClose()' - Close a CUPS file.
66
//
67
68
bool          // O - `true` on success, `false` on error
69
cupsFileClose(cups_file_t *fp)    // I - CUPS file
70
14.9k
{
71
14.9k
  int fd;       // File descriptor
72
14.9k
  char  mode;       // Open mode
73
14.9k
  bool  status;       // Return status
74
75
76
  // Range check...
77
14.9k
  if (!fp)
78
0
    return (false);
79
80
  // Flush pending write data...
81
14.9k
  if (fp->mode == 'w')
82
10.1k
    status = cupsFileFlush(fp);
83
4.82k
  else
84
4.82k
    status = true;
85
86
14.9k
  if (fp->compressed && status)
87
2.42k
  {
88
2.42k
    if (fp->mode == 'r')
89
1.97k
    {
90
      // Free decompression data...
91
1.97k
      inflateEnd(&fp->stream);
92
1.97k
    }
93
447
    else
94
447
    {
95
      // Flush any remaining compressed data...
96
447
      unsigned char trailer[8]; // Trailer CRC and length
97
447
      bool    done;   // Done writing...
98
99
100
447
      fp->stream.avail_in = 0;
101
102
447
      for (done = false;;)
103
1.41k
      {
104
1.41k
        if (fp->stream.next_out > fp->cbuf)
105
536
  {
106
536
    status = cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf));
107
108
536
    fp->stream.next_out  = fp->cbuf;
109
536
    fp->stream.avail_out = sizeof(fp->cbuf);
110
536
  }
111
112
1.41k
        if (done || !status)
113
447
    break;
114
115
969
        done = deflate(&fp->stream, Z_FINISH) == Z_STREAM_END && fp->stream.next_out == fp->cbuf;
116
969
      }
117
118
      // Write the CRC and length...
119
447
      trailer[0] = (unsigned char)fp->crc;
120
447
      trailer[1] = (unsigned char)(fp->crc >> 8);
121
447
      trailer[2] = (unsigned char)(fp->crc >> 16);
122
447
      trailer[3] = (unsigned char)(fp->crc >> 24);
123
447
      trailer[4] = (unsigned char)fp->pos;
124
447
      trailer[5] = (unsigned char)(fp->pos >> 8);
125
447
      trailer[6] = (unsigned char)(fp->pos >> 16);
126
447
      trailer[7] = (unsigned char)(fp->pos >> 24);
127
128
447
      status = cups_write(fp, (char *)trailer, 8);
129
130
      // Free all memory used by the compression stream...
131
447
      deflateEnd(&(fp->stream));
132
447
    }
133
2.42k
  }
134
135
  // If this is one of the cupsFileStdin/out/err files, return now and don't
136
  // actually free memory or close (these last the life of the process...)
137
14.9k
  if (fp->is_stdio)
138
0
    return (status);
139
140
  // Save the file descriptor we used and free memory...
141
14.9k
  fd   = fp->fd;
142
14.9k
  mode = fp->mode;
143
144
14.9k
  free(fp->printf_buffer);
145
14.9k
  free(fp);
146
147
  // Close the file, returning the close status...
148
14.9k
  if (mode == 's')
149
0
    status = httpAddrClose(NULL, fd);
150
14.9k
  else if (close(fd) < 0)
151
0
    status = false;
152
153
14.9k
  return (status);
154
14.9k
}
155
156
157
//
158
// 'cupsFileEOF()' - Return the end-of-file status.
159
//
160
161
bool          // O - `true` on end of file, `false` otherwise
162
cupsFileEOF(cups_file_t *fp)    // I - CUPS file
163
0
{
164
0
  return (fp ? fp->eof : true);
165
0
}
166
167
168
//
169
// 'cupsFileFind()' - Find a file using the specified path.
170
//
171
// This function allows the paths in the path string to be separated by
172
// colons (POSIX standard) or semicolons (Windows standard) and stores the
173
// result in the buffer supplied.  If the file cannot be found in any of
174
// the supplied paths, `NULL` is returned.  A `NULL` path only matches the
175
// current directory.
176
//
177
178
const char *        // O - Full path to file or `NULL` if not found
179
cupsFileFind(const char *filename,  // I - File to find
180
             const char *path,    // I - Colon/semicolon-separated path
181
             bool       executable, // I - `true` = executable files, `false` = any file/dir
182
       char       *buffer,  // I - Filename buffer
183
       size_t     bufsize)  // I - Size of filename buffer
184
894
{
185
894
  char  *bufptr,      // Current position in buffer
186
894
  *bufend;      // End of buffer
187
188
189
  // Range check input...
190
894
  if (!filename || !buffer || bufsize < 2)
191
0
    return (NULL);
192
193
894
  if (!path)
194
0
  {
195
    // No path, so check current directory...
196
0
    if (!access(filename, 0))
197
0
    {
198
0
      cupsCopyString(buffer, filename, (size_t)bufsize);
199
0
      return (buffer);
200
0
    }
201
0
    else
202
0
    {
203
0
      return (NULL);
204
0
    }
205
0
  }
206
207
  // Now check each path and return the first match...
208
894
  bufend = buffer + bufsize - 1;
209
894
  bufptr = buffer;
210
211
4.02k
  while (*path)
212
3.12k
  {
213
#ifdef _WIN32
214
    if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255))))
215
#else
216
3.12k
    if (*path == ';' || *path == ':')
217
447
#endif // _WIN32
218
447
    {
219
447
      if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
220
447
        *bufptr++ = '/';
221
222
447
      cupsCopyString(bufptr, filename, (size_t)(bufend - bufptr));
223
224
#ifdef _WIN32
225
      if (!access(buffer, 0))
226
#else
227
447
      if (!access(buffer, executable ? X_OK : 0))
228
0
#endif // _WIN32
229
0
        return (buffer);
230
231
447
      bufptr = buffer;
232
447
    }
233
2.68k
    else if (bufptr < bufend)
234
2.68k
      *bufptr++ = *path;
235
236
3.12k
    path ++;
237
3.12k
  }
238
239
  // Check the last path...
240
894
  if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
241
894
    *bufptr++ = '/';
242
243
894
  cupsCopyString(bufptr, filename, (size_t)(bufend - bufptr));
244
245
894
  if (!access(buffer, 0))
246
447
    return (buffer);
247
447
  else
248
447
    return (NULL);
249
894
}
250
251
252
//
253
// 'cupsFileFlush()' - Flush pending output.
254
//
255
256
bool          // O - `true` on success, `false` on error
257
cupsFileFlush(cups_file_t *fp)    // I - CUPS file
258
167k
{
259
167k
  ssize_t bytes;      // Bytes to write
260
167k
  bool    ret;      // Return value
261
262
263
  // Range check input...
264
167k
  if (!fp || fp->mode != 'w')
265
0
    return (false);
266
267
167k
  bytes = (ssize_t)(fp->ptr - fp->buf);
268
269
167k
  if (bytes > 0)
270
157k
  {
271
157k
    if (fp->compressed)
272
465
      ret = cups_compress(fp, fp->buf, (size_t)bytes);
273
157k
    else
274
157k
      ret = cups_write(fp, fp->buf, (size_t)bytes);
275
276
157k
    fp->ptr = fp->buf;
277
278
157k
    return (ret);
279
157k
  }
280
281
9.48k
  return (true);
282
167k
}
283
284
285
//
286
// 'cupsFileGetChar()' - Get a single character from a file.
287
//
288
289
int         // O - Character or `-1` on end of file
290
cupsFileGetChar(cups_file_t *fp)  // I - CUPS file
291
20.7M
{
292
  // Range check input...
293
20.7M
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
294
0
    return (-1);
295
296
20.7M
  if (fp->eof)
297
0
    return (-1);
298
299
  // If the input buffer is empty, try to read more data...
300
20.7M
  if (fp->ptr >= fp->end)
301
8.10k
  {
302
8.10k
    if (cups_fill(fp) <= 0)
303
1.61k
      return (-1);
304
8.10k
  }
305
306
  // Return the next character in the buffer...
307
20.7M
  fp->pos ++;
308
309
20.7M
  return (*(fp->ptr)++ & 255);
310
20.7M
}
311
312
313
//
314
// 'cupsFileGetConf()' - Get a line from a configuration file.
315
//
316
317
char *          // O  - Line read or `NULL` on end of file or error
318
cupsFileGetConf(cups_file_t *fp,  // I  - CUPS file
319
                char        *buf, // O  - String buffer
320
    size_t      buflen, // I  - Size of string buffer
321
                char        **value,  // O  - Pointer to value
322
    int         *linenum) // IO - Current line number
323
0
{
324
0
  char  *ptr;       // Pointer into line
325
326
327
  // Range check input...
328
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2 || !value)
329
0
  {
330
0
    if (value)
331
0
      *value = NULL;
332
333
0
    return (NULL);
334
0
  }
335
336
  // Read the next non-comment line...
337
0
  *value = NULL;
338
339
0
  while (cupsFileGets(fp, buf, buflen))
340
0
  {
341
0
    (*linenum) ++;
342
343
    // Handle escaped characters and strip any comments...
344
0
    for (ptr = buf; *ptr; ptr ++)
345
0
    {
346
0
      if (*ptr == '#')
347
0
      {
348
        // Strip comment text...
349
0
        *ptr = '\0';
350
0
        break;
351
0
      }
352
0
      else if (*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '#' || ptr[1] == 'n' || ptr[1] == 'r'))
353
0
      {
354
  // \\, \#, \n, or \r, remove backslash and update the escaped char as needed...
355
0
  _cups_strcpy(ptr, ptr + 1);
356
357
0
  if (*ptr == 'n')
358
0
    *ptr = '\n';
359
0
  else if (*ptr == 'r')
360
0
    *ptr = '\r';
361
0
      }
362
0
    }
363
364
    // Strip leading whitespace...
365
0
    for (ptr = buf; *ptr; ptr ++)
366
0
    {
367
0
      if (!_cups_isspace(*ptr))
368
0
        break;
369
0
    }
370
371
0
    if (ptr > buf)
372
0
      _cups_strcpy(buf, ptr);
373
374
    // See if there is anything left...
375
0
    if (buf[0])
376
0
    {
377
      // Yes, grab any value and return...
378
0
      for (ptr = buf; *ptr; ptr ++)
379
0
      {
380
0
        if (_cups_isspace(*ptr))
381
0
    break;
382
0
      }
383
384
0
      if (*ptr)
385
0
      {
386
        // Have a value, skip any other spaces...
387
0
        while (_cups_isspace(*ptr))
388
0
    *ptr++ = '\0';
389
390
0
        if (*ptr)
391
0
    *value = ptr;
392
393
        // Strip trailing whitespace and > for lines that begin with <...
394
0
        ptr += strlen(ptr) - 1;
395
396
0
        if (buf[0] == '<' && *ptr == '>')
397
0
        {
398
0
    *ptr-- = '\0';
399
0
  }
400
0
  else if (buf[0] == '<' && *ptr != '>')
401
0
        {
402
    // Syntax error...
403
0
    *value = NULL;
404
0
    return (buf);
405
0
  }
406
407
0
        while (ptr > *value && _cups_isspace(*ptr))
408
0
    *ptr-- = '\0';
409
0
      }
410
411
      // Return the line...
412
0
      return (buf);
413
0
    }
414
0
  }
415
416
0
  return (NULL);
417
0
}
418
419
420
//
421
// 'cupsFileGetLine()' - Get a CR and/or LF-terminated line that may
422
//                       contain binary data.
423
//
424
// This function differs from @link cupsFileGets@ in that the trailing CR
425
// and LF are preserved, as is any binary data on the line. The buffer is
426
// `nul`-terminated, however you should use the returned length to determine
427
// the number of bytes on the line.
428
//
429
430
size_t          // O - Number of bytes on line or 0 on end of file
431
cupsFileGetLine(cups_file_t *fp,  // I - File to read from
432
                char        *buf, // I - Buffer
433
                size_t      buflen) // I - Size of buffer
434
0
{
435
0
  int   ch;     // Character from file
436
0
  char    *ptr,     // Current position in line buffer
437
0
    *end;     // End of line buffer
438
439
440
  // Range check input...
441
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 3)
442
0
    return (0);
443
444
  // Now loop until we have a valid line...
445
0
  for (ptr = buf, end = buf + buflen - 2; ptr < end ;)
446
0
  {
447
0
    if (fp->ptr >= fp->end)
448
0
    {
449
0
      if (cups_fill(fp) <= 0)
450
0
        break;
451
0
    }
452
453
0
    *ptr++ = ch = *(fp->ptr)++;
454
0
    fp->pos ++;
455
456
0
    if (ch == '\r')
457
0
    {
458
      // Check for CR LF...
459
0
      if (fp->ptr >= fp->end)
460
0
      {
461
0
  if (cups_fill(fp) <= 0)
462
0
          break;
463
0
      }
464
465
0
      if (*(fp->ptr) == '\n')
466
0
      {
467
0
        *ptr++ = *(fp->ptr)++;
468
0
  fp->pos ++;
469
0
      }
470
471
0
      break;
472
0
    }
473
0
    else if (ch == '\n')
474
0
    {
475
      // Line feed ends a line...
476
0
      break;
477
0
    }
478
0
  }
479
480
0
  *ptr = '\0';
481
482
0
  return ((size_t)(ptr - buf));
483
0
}
484
485
486
//
487
// 'cupsFileGets()' - Get a CR and/or LF-terminated line.
488
//
489
490
char *          // O - Line read or `NULL` on end of file or error
491
cupsFileGets(cups_file_t *fp,   // I - CUPS file
492
             char        *buf,    // O - String buffer
493
       size_t      buflen)  // I - Size of string buffer
494
574k
{
495
574k
  int   ch;     // Character from file
496
574k
  char    *ptr,     // Current position in line buffer
497
574k
    *end;     // End of line buffer
498
499
500
  // Range check input...
501
574k
  if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2)
502
0
    return (NULL);
503
504
  // Now loop until we have a valid line...
505
20.8M
  for (ptr = buf, end = buf + buflen - 1; ptr < end ;)
506
20.7M
  {
507
20.7M
    if (fp->ptr >= fp->end)
508
9.12k
    {
509
9.12k
      if (cups_fill(fp) <= 0)
510
3.23k
      {
511
3.23k
        if (ptr == buf)
512
1.61k
    return (NULL);
513
1.61k
  else
514
1.61k
          break;
515
3.23k
      }
516
9.12k
    }
517
518
20.7M
    ch = *(fp->ptr)++;
519
20.7M
    fp->pos ++;
520
521
20.7M
    if (ch == '\r')
522
493k
    {
523
      // Check for CR LF...
524
493k
      if (fp->ptr >= fp->end)
525
137
      {
526
137
  if (cups_fill(fp) <= 0)
527
3
          break;
528
137
      }
529
530
493k
      if (*(fp->ptr) == '\n')
531
22.7k
      {
532
22.7k
        fp->ptr ++;
533
22.7k
  fp->pos ++;
534
22.7k
      }
535
536
493k
      break;
537
493k
    }
538
20.2M
    else if (ch == '\n')
539
31.2k
    {
540
      // Line feed ends a line...
541
31.2k
      break;
542
31.2k
    }
543
20.2M
    else
544
20.2M
    {
545
20.2M
      *ptr++ = (char)ch;
546
20.2M
    }
547
20.7M
  }
548
549
573k
  *ptr = '\0';
550
551
573k
  return (buf);
552
574k
}
553
554
555
//
556
// 'cupsFileIsCompressed()' - Return whether a file is compressed.
557
//
558
559
bool          // O - `true` if compressed, `false` if not
560
cupsFileIsCompressed(cups_file_t *fp) // I - CUPS file
561
2.06k
{
562
2.06k
  return (fp ? fp->compressed : false);
563
2.06k
}
564
565
566
//
567
// 'cupsFileLock()' - Temporarily lock access to a file.
568
//
569
570
bool          // O - `true` on success, `false` on error
571
cupsFileLock(cups_file_t *fp,   // I - CUPS file
572
             bool        block)   // I - `true` to wait for the lock, `false` to fail right away
573
1.02k
{
574
  // Range check...
575
1.02k
  if (!fp || fp->mode == 's')
576
0
    return (false);
577
578
  // Try the lock...
579
#ifdef _WIN32
580
  return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0) == 0);
581
#else
582
1.02k
  return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0) == 0);
583
1.02k
#endif // _WIN32
584
1.02k
}
585
586
587
//
588
// 'cupsFileNumber()' - Return the file descriptor associated with a CUPS file.
589
//
590
591
int         // O - File descriptor
592
cupsFileNumber(cups_file_t *fp)   // I - CUPS file
593
1.61k
{
594
1.61k
  if (fp)
595
1.61k
    return (fp->fd);
596
0
  else
597
0
    return (-1);
598
1.61k
}
599
600
601
//
602
// 'cupsFileOpen()' - Open a CUPS file.
603
//
604
// This function opens a file or socket for use with the CUPS file API.
605
//
606
// The "filename" argument is a filename or socket address.
607
//
608
// The "mode" argument can be "r" to read, "w" to write, overwriting any
609
// existing file, "a" to append to an existing file or create a new file,
610
// or "s" to open a socket connection.
611
//
612
// When opening for writing ("w"), an optional number from `1` to `9` can be
613
// supplied which enables Flate compression of the file.  Compression is
614
// not supported for the "a" (append) mode.
615
//
616
// When opening for writing ("w") or append ("a"), an optional 'm###' suffix
617
// can be used to set the permissions of the opened file.
618
//
619
// When opening a socket connection, the filename is a string of the form
620
// "address:port" or "hostname:port". The socket will make an IPv4 or IPv6
621
// connection as needed, generally preferring IPv6 connections when there is
622
// a choice.
623
//
624
625
cups_file_t *       // O - CUPS file or `NULL` if the file or socket cannot be opened
626
cupsFileOpen(const char *filename,  // I - Name of file
627
             const char *mode)    // I - Open mode
628
14.9k
{
629
14.9k
  cups_file_t *fp;      // New CUPS file
630
14.9k
  int   fd;     // File descriptor
631
14.9k
  char    hostname[1024],   // Hostname
632
14.9k
    *portname;    // Port "name" (number or service)
633
14.9k
  http_addrlist_t *addrlist;    // Host address list
634
14.9k
  int   perm = 0664;    // Permissions for write/append
635
14.9k
  const char  *ptr;     // Pointer into mode string
636
637
638
  // Range check input...
639
14.9k
  if (!filename || !mode || (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || (*mode == 'a' && isdigit(mode[1] & 255)))
640
0
    return (NULL);
641
642
14.9k
  if ((ptr = strchr(mode, 'm')) != NULL && ptr[1] >= '0' && ptr[1] <= '7')
643
0
  {
644
    // Get permissions from mode string...
645
0
    perm = (int)strtol(mode + 1, NULL, 8);
646
0
  }
647
648
  // Open the file...
649
14.9k
  switch (*mode)
650
14.9k
  {
651
0
    case 'a' : // Append file
652
0
        fd = cups_open(filename, O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE | O_BINARY, perm);
653
0
        break;
654
655
4.82k
    case 'r' : // Read file
656
4.82k
  fd = open(filename, O_RDONLY | O_LARGEFILE | O_BINARY, 0);
657
4.82k
  break;
658
659
10.1k
    case 'w' : // Write file
660
10.1k
        fd    = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY, perm);
661
10.1k
  if (fd < 0 && errno == ENOENT)
662
7.15k
  {
663
7.15k
    fd = cups_open(filename, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE | O_BINARY, perm);
664
7.15k
    if (fd < 0 && errno == EEXIST)
665
0
      fd = cups_open(filename, O_WRONLY | O_LARGEFILE | O_BINARY, perm);
666
7.15k
  }
667
668
10.1k
  if (fd >= 0)
669
#ifdef _WIN32
670
    _chsize(fd, 0);
671
#else
672
10.1k
    ftruncate(fd, 0);
673
10.1k
#endif // _WIN32
674
10.1k
        break;
675
676
0
    case 's' : // Read/write socket
677
0
        cupsCopyString(hostname, filename, sizeof(hostname));
678
0
  if ((portname = strrchr(hostname, ':')) != NULL)
679
0
    *portname++ = '\0';
680
0
  else
681
0
    return (NULL);
682
683
        // Lookup the hostname and service...
684
0
        if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
685
0
    return (NULL);
686
687
        // Connect to the server...
688
0
        if (!httpAddrConnect(addrlist, &fd, 30000, NULL))
689
0
  {
690
0
    httpAddrFreeList(addrlist);
691
0
    return (NULL);
692
0
  }
693
694
0
  httpAddrFreeList(addrlist);
695
0
  break;
696
697
0
    default : // Remove bogus compiler warning...
698
0
        return (NULL);
699
14.9k
  }
700
701
14.9k
  if (fd < 0)
702
2
    return (NULL);
703
704
  // Create the CUPS file structure...
705
14.9k
  if ((fp = cupsFileOpenFd(fd, mode)) == NULL)
706
0
  {
707
0
    if (*mode == 's')
708
0
      httpAddrClose(NULL, fd);
709
0
    else
710
0
      close(fd);
711
0
  }
712
713
  // Return it...
714
14.9k
  return (fp);
715
14.9k
}
716
717
718
//
719
// 'cupsFileOpenFd()' - Open a CUPS file using a file descriptor.
720
//
721
// This function prepares a file descriptor for use with the CUPS file API.
722
//
723
// The "fd" argument specifies the file descriptor.
724
//
725
// The "mode" argument can be "r" to read, "w" to write, "a" to append,
726
// or "s" to treat the file descriptor as a bidirectional socket connection.
727
//
728
// When opening for writing ("w"), an optional number from `1` to `9` can be
729
// supplied which enables Flate compression of the file.  Compression is
730
// not supported for the "a" (append) mode.
731
//
732
733
cups_file_t *       // O - CUPS file or `NULL` if the file could not be opened
734
cupsFileOpenFd(int        fd,   // I - File descriptor
735
         const char *mode)  // I - Open mode
736
14.9k
{
737
14.9k
  cups_file_t *fp;      // New CUPS file
738
739
740
  // Range check input...
741
14.9k
  if (fd < 0 || !mode || (*mode != 'r' && *mode != 'w' && *mode != 'a' && *mode != 's') || (*mode == 'a' && isdigit(mode[1] & 255)))
742
0
    return (NULL);
743
744
  // Allocate memory...
745
14.9k
  if ((fp = calloc(1, sizeof(cups_file_t))) == NULL)
746
0
    return (NULL);
747
748
  // Open the file...
749
14.9k
  fp->fd = fd;
750
751
14.9k
  switch (*mode)
752
14.9k
  {
753
0
    case 'a' :
754
10.1k
    case 'w' :
755
10.1k
        if (*mode == 'a')
756
0
          fp->pos = lseek(fd, 0, SEEK_END);
757
758
10.1k
  fp->mode = 'w';
759
10.1k
  fp->ptr  = fp->buf;
760
10.1k
  fp->end  = fp->buf + sizeof(fp->buf);
761
762
10.1k
  if (mode[1] >= '1' && mode[1] <= '9')
763
447
  {
764
    // Open a compressed stream, so write the standard gzip file header...
765
447
          unsigned char header[10]; // gzip file header
766
447
    time_t  curtime;  // Current time
767
768
769
447
          curtime   = time(NULL);
770
447
    header[0] = 0x1f;
771
447
    header[1] = 0x8b;
772
447
    header[2] = Z_DEFLATED;
773
447
    header[3] = 0;
774
447
    header[4] = (unsigned char)curtime;
775
447
    header[5] = (unsigned char)(curtime >> 8);
776
447
    header[6] = (unsigned char)(curtime >> 16);
777
447
    header[7] = (unsigned char)(curtime >> 24);
778
447
    header[8] = 0;
779
447
    header[9] = 0x03;
780
781
447
    if (!cups_write(fp, (char *)header, 10))
782
0
    {
783
0
            free(fp);
784
0
      return (NULL);
785
0
    }
786
787
          // Initialize the compressor...
788
447
          if (deflateInit2(&(fp->stream), mode[1] - '0', Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) < Z_OK)
789
0
          {
790
0
            free(fp);
791
0
      return (NULL);
792
0
          }
793
794
447
    fp->stream.next_out  = fp->cbuf;
795
447
    fp->stream.avail_out = sizeof(fp->cbuf);
796
447
    fp->compressed       = true;
797
447
    fp->crc              = crc32(0L, Z_NULL, 0);
798
447
  }
799
10.1k
        break;
800
801
10.1k
    case 'r' :
802
4.82k
  fp->mode = 'r';
803
4.82k
  break;
804
805
0
    case 's' :
806
0
        fp->mode = 's';
807
0
  break;
808
809
0
    default : // Remove bogus compiler warning...
810
0
        return (NULL);
811
14.9k
  }
812
813
  // Don't pass this file to child processes...
814
14.9k
#ifndef _WIN32
815
14.9k
  if (fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC))
816
0
    DEBUG_printf("cupsFileOpenFd: fcntl(F_SETFD, FD_CLOEXEC) failed - %s", strerror(errno));
817
14.9k
#endif // !_WIN32
818
819
14.9k
  return (fp);
820
14.9k
}
821
822
823
//
824
// '_cupsFilePeekAhead()' - See if the requested character is buffered up.
825
//
826
827
bool          // O - `true` if present, `false` otherwise
828
_cupsFilePeekAhead(cups_file_t *fp, // I - CUPS file
829
                   int         ch)  // I - Character
830
0
{
831
0
  return (fp && fp->ptr && memchr(fp->ptr, ch, (size_t)(fp->end - fp->ptr)));
832
0
}
833
834
835
//
836
// 'cupsFilePeekChar()' - Peek at the next character from a file.
837
//
838
839
int         // O - Character or `-1` on end of file
840
cupsFilePeekChar(cups_file_t *fp) // I - CUPS file
841
0
{
842
  // Range check input...
843
0
  if (!fp || (fp->mode != 'r' && fp->mode != 's'))
844
0
    return (-1);
845
846
  // If the input buffer is empty, try to read more data...
847
0
  if (fp->ptr >= fp->end)
848
0
  {
849
0
    if (cups_fill(fp) <= 0)
850
0
      return (-1);
851
0
  }
852
853
  // Return the next character in the buffer...
854
0
  return (*(fp->ptr) & 255);
855
0
}
856
857
858
//
859
// 'cupsFilePrintf()' - Write a formatted string.
860
//
861
862
bool          // O - `true` on success, `false` on error
863
cupsFilePrintf(cups_file_t *fp,   // I - CUPS file
864
               const char  *format, // I - Printf-style format string
865
         ...)     // I - Additional args as necessary
866
3.66k
{
867
3.66k
  va_list ap, ap2;    // Argument list
868
3.66k
  ssize_t bytes;      // Formatted size
869
870
871
3.66k
  if (!fp || !format || (fp->mode != 'w' && fp->mode != 's'))
872
0
    return (false);
873
874
3.66k
  if (!fp->printf_buffer)
875
3.66k
  {
876
    // Start with an 1k printf buffer...
877
3.66k
    if ((fp->printf_buffer = malloc(1024)) == NULL)
878
0
      return (false);
879
880
3.66k
    fp->printf_size = 1024;
881
3.66k
  }
882
883
3.66k
  va_start(ap, format);
884
3.66k
  va_copy(ap2, ap);
885
3.66k
  bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap2);
886
3.66k
  va_end(ap2);
887
888
3.66k
  if (bytes >= (ssize_t)fp->printf_size)
889
0
  {
890
    // Expand the printf buffer...
891
0
    char  *temp;      // Temporary buffer pointer
892
893
0
    if (bytes > 1048576)
894
0
    {
895
0
      va_end(ap);
896
0
      return (-1);
897
0
    }
898
899
0
    if ((temp = realloc(fp->printf_buffer, (size_t)(bytes + 1))) == NULL)
900
0
    {
901
0
      va_end(ap);
902
0
      return (-1);
903
0
    }
904
905
0
    fp->printf_buffer = temp;
906
0
    fp->printf_size   = (size_t)(bytes + 1);
907
908
0
    bytes = vsnprintf(fp->printf_buffer, fp->printf_size, format, ap);
909
0
  }
910
911
3.66k
  va_end(ap);
912
913
3.66k
  if (bytes < 0)
914
0
    return (-1);
915
916
3.66k
  if (fp->mode == 's')
917
0
  {
918
0
    if (!cups_write(fp, fp->printf_buffer, (size_t)bytes))
919
0
      return (false);
920
921
0
    fp->pos += bytes;
922
923
0
    return ((int)bytes);
924
0
  }
925
926
3.66k
  if ((fp->ptr + bytes) > fp->end)
927
20
  {
928
20
    if (!cupsFileFlush(fp))
929
0
      return (false);
930
20
  }
931
932
3.66k
  fp->pos += bytes;
933
934
3.66k
  if ((size_t)bytes > sizeof(fp->buf))
935
0
  {
936
0
    if (fp->compressed)
937
0
      return (cups_compress(fp, fp->printf_buffer, (size_t)bytes));
938
0
    else
939
0
      return (cups_write(fp, fp->printf_buffer, (size_t)bytes));
940
0
  }
941
3.66k
  else
942
3.66k
  {
943
3.66k
    memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
944
3.66k
    fp->ptr += bytes;
945
946
3.66k
    if (fp->is_stdio && !cupsFileFlush(fp))
947
0
      return (false);
948
3.66k
    else
949
3.66k
      return (true);
950
3.66k
  }
951
3.66k
}
952
953
954
//
955
// 'cupsFilePutChar()' - Write a character.
956
//
957
958
bool          // O - `true` on success, `false` on error
959
cupsFilePutChar(cups_file_t *fp,  // I - CUPS file
960
                int         c)    // I - Character to write
961
1.61k
{
962
  // Range check input...
963
1.61k
  if (!fp || (fp->mode != 'w' && fp->mode != 's'))
964
0
    return (false);
965
966
1.61k
  if (fp->mode == 's')
967
0
  {
968
    // Send character immediately over socket...
969
0
    char ch;        // Output character
970
971
0
    ch = (char)c;
972
973
0
    if (send(fp->fd, &ch, 1, 0) < 1)
974
0
      return (false);
975
0
  }
976
1.61k
  else
977
1.61k
  {
978
    // Buffer it up...
979
1.61k
    if (fp->ptr >= fp->end)
980
2
    {
981
2
      if (!cupsFileFlush(fp))
982
0
  return (false);
983
2
    }
984
985
1.61k
    *(fp->ptr) ++ = (char)c;
986
1.61k
  }
987
988
1.61k
  fp->pos ++;
989
990
1.61k
  return (true);
991
1.61k
}
992
993
994
//
995
// 'cupsFilePutConf()' - Write a configuration line.
996
//
997
// This function handles any escaping of the value.
998
//
999
1000
bool          // O - `true` on success, `false` on error
1001
cupsFilePutConf(cups_file_t *fp,  // I - CUPS file
1002
                const char  *directive, // I - Directive
1003
    const char  *value) // I - Value
1004
0
{
1005
0
  if (!fp || !directive || !*directive)
1006
0
    return (false);
1007
1008
0
  if (!cupsFilePuts(fp, directive))
1009
0
    return (false);
1010
1011
0
  if (value && *value)
1012
0
  {
1013
0
    const char  *start,     // Start of current fragment
1014
0
    *ptr;     // Pointer into value
1015
1016
0
    if (!cupsFilePutChar(fp, ' '))
1017
0
      return (false);
1018
1019
0
    for (start = ptr = value; *ptr; ptr ++)
1020
0
    {
1021
0
      if (strchr("#\\\n\r", *ptr) != NULL)
1022
0
      {
1023
        // Character that needs to be escaped...
1024
0
        if (ptr > start)
1025
0
        {
1026
          // Write unescaped portion...
1027
0
    if (!cupsFileWrite(fp, start, (size_t)(ptr - start)))
1028
0
      return (false);
1029
0
        }
1030
1031
0
        start = ptr + 1;
1032
1033
0
        if (*ptr == '\\')
1034
0
        {
1035
          // "\" (for escaping)
1036
0
          if (!cupsFilePuts(fp, "\\\\"))
1037
0
            return (false);
1038
0
        }
1039
0
        else if (*ptr == '#')
1040
0
        {
1041
          // "#" (for comment)
1042
0
          if (!cupsFilePuts(fp, "\\#"))
1043
0
            return (false);
1044
0
        }
1045
0
        else if (*ptr == '\n')
1046
0
        {
1047
          // LF
1048
0
          if (!cupsFilePuts(fp, "\\n"))
1049
0
            return (false);
1050
0
        }
1051
0
        else if (!cupsFilePuts(fp, "\\r"))
1052
0
        {
1053
0
    return (false);
1054
0
  }
1055
0
      }
1056
0
    }
1057
1058
0
    if (ptr > start)
1059
0
    {
1060
      // Write remaining unescaped portion...
1061
0
      if (!cupsFileWrite(fp, start, (size_t)(ptr - start)))
1062
0
  return (false);
1063
0
    }
1064
0
  }
1065
1066
0
  return (cupsFilePutChar(fp, '\n'));
1067
0
}
1068
1069
1070
//
1071
// 'cupsFilePuts()' - Write a string.
1072
//
1073
// Like the `fputs` function, no newline is appended to the string.
1074
//
1075
1076
bool          // O - `true` on success, `false` on error
1077
cupsFilePuts(cups_file_t *fp,   // I - CUPS file
1078
             const char  *s)    // I - String to write
1079
1.61k
{
1080
1.61k
  size_t  bytes;      // Bytes to write
1081
1082
1083
  // Range check input...
1084
1.61k
  if (!fp || !s || (fp->mode != 'w' && fp->mode != 's'))
1085
0
    return (false);
1086
1087
  // Write the string...
1088
1.61k
  bytes = strlen(s);
1089
1090
1.61k
  if (fp->mode == 's')
1091
0
  {
1092
0
    if (!cups_write(fp, s, bytes))
1093
0
      return (false);
1094
1095
0
    fp->pos += bytes;
1096
1097
0
    return (true);
1098
0
  }
1099
1100
1.61k
  if ((fp->ptr + bytes) > fp->end)
1101
13
  {
1102
13
    if (!cupsFileFlush(fp))
1103
0
      return (false);
1104
13
  }
1105
1106
1.61k
  fp->pos += bytes;
1107
1108
1.61k
  if (bytes > sizeof(fp->buf))
1109
0
  {
1110
0
    if (fp->compressed)
1111
0
      return (cups_compress(fp, s, bytes) > 0);
1112
0
    else
1113
0
      return (cups_write(fp, s, bytes) > 0);
1114
0
  }
1115
1.61k
  else
1116
1.61k
  {
1117
1.61k
    memcpy(fp->ptr, s, bytes);
1118
1.61k
    fp->ptr += bytes;
1119
1120
1.61k
    if (fp->is_stdio && !cupsFileFlush(fp))
1121
0
      return (false);
1122
1.61k
    else
1123
1.61k
      return (true);
1124
1.61k
  }
1125
1.61k
}
1126
1127
1128
//
1129
// 'cupsFileRead()' - Read from a file.
1130
//
1131
1132
ssize_t         // O - Number of bytes read or -1 on error
1133
cupsFileRead(cups_file_t *fp,   // I - CUPS file
1134
             char        *buf,    // O - Buffer
1135
       size_t      bytes)   // I - Number of bytes to read
1136
71.6M
{
1137
71.6M
  size_t  total;      // Total bytes read
1138
71.6M
  ssize_t count;      // Bytes read
1139
1140
1141
  // Range check input...
1142
71.6M
  if (!fp || !buf || (fp->mode != 'r' && fp->mode != 's'))
1143
0
    return (-1);
1144
1145
71.6M
  if (bytes == 0)
1146
0
    return (0);
1147
1148
71.6M
  if (fp->eof)
1149
0
    return (-1);
1150
1151
  // Loop until all bytes are read...
1152
71.6M
  total = 0;
1153
143M
  while (bytes > 0)
1154
71.7M
  {
1155
71.7M
    if (fp->ptr >= fp->end)
1156
176k
    {
1157
176k
      if (cups_fill(fp) <= 0)
1158
3.46k
      {
1159
3.46k
        if (total > 0)
1160
2.27k
          return ((ssize_t)total);
1161
1.19k
  else
1162
1.19k
    return (-1);
1163
3.46k
      }
1164
176k
    }
1165
1166
71.7M
    count = (ssize_t)(fp->end - fp->ptr);
1167
71.7M
    if (count > (ssize_t)bytes)
1168
71.6M
      count = (ssize_t)bytes;
1169
1170
71.7M
    memcpy(buf, fp->ptr,(size_t) count);
1171
71.7M
    fp->ptr += count;
1172
71.7M
    fp->pos += count;
1173
1174
    // Update the counts for the last read...
1175
71.7M
    bytes -= (size_t)count;
1176
71.7M
    total += (size_t)count;
1177
71.7M
    buf   += count;
1178
71.7M
  }
1179
1180
  // Return the total number of bytes read...
1181
71.6M
  return ((ssize_t)total);
1182
71.6M
}
1183
1184
1185
//
1186
// 'cupsFileRewind()' - Set the current file position to the beginning of the file.
1187
//
1188
1189
off_t         // O - New file position or `-1` on error
1190
cupsFileRewind(cups_file_t *fp)   // I - CUPS file
1191
4.85k
{
1192
  // Range check input...
1193
4.85k
  if (!fp || fp->mode != 'r')
1194
0
    return (-1);
1195
1196
  // Handle special cases...
1197
4.85k
  if (fp->bufpos == 0)
1198
464
  {
1199
    // No seeking necessary...
1200
464
    fp->pos = 0;
1201
1202
464
    if (fp->ptr)
1203
464
    {
1204
464
      fp->ptr = fp->buf;
1205
464
      fp->eof = false;
1206
464
    }
1207
1208
464
    return (0);
1209
464
  }
1210
1211
  // Otherwise, seek in the file and cleanup any compression buffers...
1212
4.39k
  if (fp->compressed)
1213
399
  {
1214
399
    inflateEnd(&fp->stream);
1215
399
    fp->compressed = false;
1216
399
  }
1217
1218
4.39k
  if (lseek(fp->fd, 0, SEEK_SET))
1219
0
    return (-1);
1220
1221
4.39k
  fp->bufpos = 0;
1222
4.39k
  fp->pos    = 0;
1223
4.39k
  fp->ptr    = NULL;
1224
4.39k
  fp->end    = NULL;
1225
4.39k
  fp->eof    = false;
1226
1227
4.39k
  return (0);
1228
4.39k
}
1229
1230
1231
//
1232
// 'cupsFileSeek()' - Seek in a file.
1233
//
1234
1235
off_t         // O - New file position or `-1` on error
1236
cupsFileSeek(cups_file_t *fp,   // I - CUPS file
1237
             off_t       pos)   // I - Position in file
1238
3.23k
{
1239
3.23k
  ssize_t bytes;      // Number bytes in buffer
1240
1241
1242
  // Range check input...
1243
3.23k
  if (!fp || pos < 0 || fp->mode != 'r')
1244
0
    return (-1);
1245
1246
  // Handle special cases...
1247
3.23k
  if (pos == 0)
1248
1.61k
    return (cupsFileRewind(fp));
1249
1250
1.61k
  if (fp->ptr)
1251
0
  {
1252
0
    bytes = (ssize_t)(fp->end - fp->buf);
1253
1254
0
    if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1255
0
    {
1256
      // No seeking necessary...
1257
0
      fp->pos = pos;
1258
0
      fp->ptr = fp->buf + (pos - fp->bufpos);
1259
0
      fp->eof = false;
1260
1261
0
      return (pos);
1262
0
    }
1263
0
  }
1264
1265
1.61k
  if (!fp->compressed && !fp->ptr)
1266
1.61k
  {
1267
    // Preload a buffer to determine whether the file is compressed...
1268
1.61k
    if (cups_fill(fp) <= 0)
1269
0
      return (-1);
1270
1.61k
  }
1271
1272
  // Seek forwards or backwards...
1273
1.61k
  fp->eof = false;
1274
1275
1.61k
  if (pos < fp->bufpos)
1276
0
  {
1277
    // Need to seek backwards...
1278
0
    if (fp->compressed)
1279
0
    {
1280
0
      inflateEnd(&fp->stream);
1281
1282
0
      lseek(fp->fd, 0, SEEK_SET);
1283
0
      fp->bufpos = 0;
1284
0
      fp->pos    = 0;
1285
0
      fp->ptr    = NULL;
1286
0
      fp->end    = NULL;
1287
1288
0
      while ((bytes = cups_fill(fp)) > 0)
1289
0
      {
1290
0
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1291
0
    break;
1292
0
      }
1293
1294
0
      if (bytes <= 0)
1295
0
        return (-1);
1296
1297
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1298
0
      fp->pos = pos;
1299
0
    }
1300
0
    else
1301
0
    {
1302
0
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1303
0
      fp->pos    = fp->bufpos;
1304
0
      fp->ptr    = NULL;
1305
0
      fp->end    = NULL;
1306
0
    }
1307
0
  }
1308
1.61k
  else
1309
1.61k
  {
1310
    // Need to seek forwards...
1311
1.61k
    if (fp->compressed)
1312
211
    {
1313
4.62k
      while ((bytes = cups_fill(fp)) > 0)
1314
4.41k
      {
1315
4.41k
        if (pos >= fp->bufpos && pos < (fp->bufpos + bytes))
1316
0
    break;
1317
4.41k
      }
1318
1319
211
      if (bytes <= 0)
1320
211
        return (-1);
1321
1322
0
      fp->ptr = fp->buf + pos - fp->bufpos;
1323
0
      fp->pos = pos;
1324
0
    }
1325
1.40k
    else
1326
1.40k
    {
1327
1.40k
      fp->bufpos = lseek(fp->fd, pos, SEEK_SET);
1328
1.40k
      fp->pos    = fp->bufpos;
1329
1.40k
      fp->ptr    = NULL;
1330
1.40k
      fp->end    = NULL;
1331
1.40k
    }
1332
1.61k
  }
1333
1334
1.40k
  return (fp->pos);
1335
1.61k
}
1336
1337
1338
//
1339
// 'cupsFileStderr()' - Return a CUPS file associated with stderr.
1340
//
1341
1342
cups_file_t *       // O - CUPS file
1343
cupsFileStderr(void)
1344
0
{
1345
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1346
1347
1348
  // Open file descriptor 2 as needed...
1349
0
  if (!cg->stdio_files[2])
1350
0
  {
1351
    // Flush any pending output on the stdio file...
1352
0
    fflush(stderr);
1353
1354
    // Open file descriptor 2...
1355
0
    if ((cg->stdio_files[2] = cupsFileOpenFd(2, "w")) != NULL)
1356
0
      cg->stdio_files[2]->is_stdio = true;
1357
0
  }
1358
1359
0
  return (cg->stdio_files[2]);
1360
0
}
1361
1362
1363
//
1364
// 'cupsFileStdin()' - Return a CUPS file associated with stdin.
1365
//
1366
1367
cups_file_t *       // O - CUPS file
1368
cupsFileStdin(void)
1369
0
{
1370
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1371
1372
1373
  // Open file descriptor 0 as needed...
1374
0
  if (!cg->stdio_files[0])
1375
0
  {
1376
    // Open file descriptor 0...
1377
0
    if ((cg->stdio_files[0] = cupsFileOpenFd(0, "r")) != NULL)
1378
0
      cg->stdio_files[0]->is_stdio = true;
1379
0
  }
1380
1381
0
  return (cg->stdio_files[0]);
1382
0
}
1383
1384
1385
//
1386
// 'cupsFileStdout()' - Return a CUPS file associated with stdout.
1387
//
1388
1389
cups_file_t *       // O - CUPS file
1390
cupsFileStdout(void)
1391
0
{
1392
0
  _cups_globals_t *cg = _cupsGlobals(); // Pointer to library globals...
1393
1394
1395
  // Open file descriptor 1 as needed...
1396
0
  if (!cg->stdio_files[1])
1397
0
  {
1398
    // Flush any pending output on the stdio file...
1399
0
    fflush(stdout);
1400
1401
    // Open file descriptor 1...
1402
0
    if ((cg->stdio_files[1] = cupsFileOpenFd(1, "w")) != NULL)
1403
0
      cg->stdio_files[1]->is_stdio = true;
1404
0
  }
1405
1406
0
  return (cg->stdio_files[1]);
1407
0
}
1408
1409
1410
//
1411
// 'cupsFileTell()' - Return the current file position.
1412
//
1413
1414
off_t         // O - File position
1415
cupsFileTell(cups_file_t *fp)   // I - CUPS file
1416
1.61k
{
1417
1.61k
  return (fp ? fp->pos : 0);
1418
1.61k
}
1419
1420
1421
//
1422
// 'cupsFileUnlock()' - Unlock access to a file.
1423
//
1424
1425
bool          // O - `true` on success, `false` on error
1426
cupsFileUnlock(cups_file_t *fp)   // I - CUPS file
1427
513
{
1428
  // Range check...
1429
513
  if (!fp || fp->mode == 's')
1430
0
    return (false);
1431
1432
  // Unlock...
1433
#ifdef _WIN32
1434
  return (_locking(fp->fd, _LK_UNLCK, 0) == 0);
1435
#else
1436
513
  return (lockf(fp->fd, F_ULOCK, 0) == 0);
1437
513
#endif // _WIN32
1438
513
}
1439
1440
1441
//
1442
// 'cupsFileWrite()' - Write to a file.
1443
//
1444
1445
bool          // O - `true` on success, `false` on error
1446
cupsFileWrite(cups_file_t *fp,    // I - CUPS file
1447
              const char  *buf,   // I - Buffer
1448
        size_t      bytes)  // I - Number of bytes to write
1449
9.39M
{
1450
  // Range check input...
1451
9.39M
  if (!fp || !buf || (fp->mode != 'w' && fp->mode != 's'))
1452
0
    return (false);
1453
1454
9.39M
  if (bytes == 0)
1455
0
    return (true);
1456
1457
  // Write the buffer...
1458
9.39M
  if (fp->mode == 's')
1459
0
  {
1460
0
    if (!cups_write(fp, buf, bytes))
1461
0
      return (false);
1462
1463
0
    fp->pos += (off_t)bytes;
1464
1465
0
    return (true);
1466
0
  }
1467
1468
9.39M
  if ((fp->ptr + bytes) > fp->end)
1469
155k
  {
1470
155k
    if (!cupsFileFlush(fp))
1471
0
      return (false);
1472
155k
  }
1473
1474
9.39M
  fp->pos += (off_t)bytes;
1475
1476
9.39M
  if (bytes > sizeof(fp->buf))
1477
31.3k
  {
1478
31.3k
    if (fp->compressed)
1479
119
      return (cups_compress(fp, buf, bytes));
1480
31.2k
    else
1481
31.2k
      return (cups_write(fp, buf, bytes));
1482
31.3k
  }
1483
9.36M
  else
1484
9.36M
  {
1485
9.36M
    memcpy(fp->ptr, buf, bytes);
1486
9.36M
    fp->ptr += bytes;
1487
9.36M
    return (true);
1488
9.36M
  }
1489
9.39M
}
1490
1491
1492
//
1493
// 'cups_compress()' - Compress a buffer of data.
1494
//
1495
1496
static bool       // O - `true` on success, `false` on error
1497
cups_compress(cups_file_t *fp,    // I - CUPS file
1498
              const char  *buf,   // I - Buffer
1499
        size_t      bytes)  // I - Number bytes
1500
584
{
1501
584
  int status;       // Deflate status
1502
1503
1504
  // Update the CRC...
1505
584
  fp->crc = crc32(fp->crc, (const Bytef *)buf, (uInt)bytes);
1506
1507
  // Deflate the bytes...
1508
584
  fp->stream.next_in  = (Bytef *)buf;
1509
584
  fp->stream.avail_in = (uInt)bytes;
1510
1511
1.22k
  while (fp->stream.avail_in > 0)
1512
641
  {
1513
    // Flush the current buffer...
1514
641
    if (fp->stream.avail_out < (uInt)(sizeof(fp->cbuf) / 8))
1515
71
    {
1516
71
      if (!cups_write(fp, (char *)fp->cbuf, (size_t)(fp->stream.next_out - fp->cbuf)))
1517
0
        return (false);
1518
1519
71
      fp->stream.next_out  = fp->cbuf;
1520
71
      fp->stream.avail_out = sizeof(fp->cbuf);
1521
71
    }
1522
1523
641
    if ((status = deflate(&(fp->stream), Z_NO_FLUSH)) < Z_OK && status != Z_BUF_ERROR)
1524
0
      return (false);
1525
641
  }
1526
1527
584
  return (true);
1528
584
}
1529
1530
1531
//
1532
// 'cups_fill()' - Fill the input buffer.
1533
//
1534
1535
static ssize_t        // O - Number of bytes or -1
1536
cups_fill(cups_file_t *fp)    // I - CUPS file
1537
200k
{
1538
200k
  ssize_t   bytes;    // Number of bytes read
1539
200k
  int     status;   // Decompression status
1540
200k
  const unsigned char *ptr,   // Pointer into buffer
1541
200k
      *end;   // End of buffer
1542
1543
1544
200k
  if (fp->ptr && fp->end)
1545
190k
    fp->bufpos += fp->end - fp->buf;
1546
1547
200k
  while (!fp->ptr || fp->compressed)
1548
192k
  {
1549
    // Check to see if we have read any data yet; if not, see if we have a compressed file...
1550
192k
    if (!fp->ptr)
1551
9.21k
    {
1552
      // Reset the file position in case we are seeking...
1553
9.21k
      fp->compressed = false;
1554
1555
      // Read the first bytes in the file to determine if we have a gzip'd file...
1556
9.21k
      if ((bytes = cups_read(fp, (char *)fp->buf, sizeof(fp->buf))) < 0)
1557
0
      {
1558
        // Can't read from file!
1559
0
        fp->eof = true;
1560
1561
0
  return (-1);
1562
0
      }
1563
1564
9.21k
      if (bytes < 10 || fp->buf[0] != 0x1f || (fp->buf[1] & 255) != 0x8b || fp->buf[2] != 8 || (fp->buf[3] & 0xe0) != 0)
1565
6.29k
      {
1566
        // Not a gzip'd file!
1567
6.29k
  fp->ptr = fp->buf;
1568
6.29k
  fp->end = fp->buf + bytes;
1569
1570
6.29k
  return (bytes);
1571
6.29k
      }
1572
1573
      // Parse header junk: extra data, original name, and comment...
1574
2.92k
      ptr = (unsigned char *)fp->buf + 10;
1575
2.92k
      end = (unsigned char *)fp->buf + bytes;
1576
1577
2.92k
      if (fp->buf[3] & 0x04)
1578
131
      {
1579
        // Skip extra data...
1580
131
  if ((ptr + 2) > end)
1581
4
  {
1582
    // Can't read from file!
1583
4
          fp->eof = true;
1584
4
    errno   = EIO;
1585
1586
4
    return (-1);
1587
4
  }
1588
1589
127
  bytes = ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
1590
127
  ptr   += 2 + bytes;
1591
1592
127
  if (ptr > end)
1593
26
  {
1594
    // Can't read from file!
1595
26
          fp->eof = true;
1596
26
    errno   = EIO;
1597
1598
26
    return (-1);
1599
26
  }
1600
127
      }
1601
1602
2.89k
      if (fp->buf[3] & 0x08)
1603
96
      {
1604
        // Skip original name data...
1605
1.37k
  while (ptr < end && *ptr)
1606
1.27k
          ptr ++;
1607
1608
96
  if (ptr < end)
1609
79
  {
1610
79
          ptr ++;
1611
79
  }
1612
17
  else
1613
17
  {
1614
    // Can't read from file!
1615
17
          fp->eof = true;
1616
17
    errno   = EIO;
1617
1618
17
    return (-1);
1619
17
  }
1620
96
      }
1621
1622
2.87k
      if (fp->buf[3] & 0x10)
1623
72
      {
1624
        // Skip comment data...
1625
5.23k
  while (ptr < end && *ptr)
1626
5.15k
          ptr ++;
1627
1628
72
  if (ptr < end)
1629
52
  {
1630
52
          ptr ++;
1631
52
  }
1632
20
  else
1633
20
  {
1634
    // Can't read from file!
1635
20
          fp->eof = true;
1636
20
    errno   = EIO;
1637
1638
20
    return (-1);
1639
20
  }
1640
72
      }
1641
1642
2.85k
      if (fp->buf[3] & 0x02)
1643
105
      {
1644
        // Skip header CRC data...
1645
105
  ptr += 2;
1646
1647
105
  if (ptr > end)
1648
6
  {
1649
    // Can't read from file!
1650
6
          fp->eof = true;
1651
6
    errno   = EIO;
1652
1653
6
    return (-1);
1654
6
  }
1655
105
      }
1656
1657
      // Copy the flate-compressed data to the compression buffer...
1658
2.85k
      if ((bytes = end - ptr) > 0)
1659
2.84k
        memcpy(fp->cbuf, ptr, (size_t)bytes);
1660
1661
      // Setup the decompressor data...
1662
2.85k
      fp->stream.zalloc    = (alloc_func)0;
1663
2.85k
      fp->stream.zfree     = (free_func)0;
1664
2.85k
      fp->stream.opaque    = (voidpf)0;
1665
2.85k
      fp->stream.next_in   = (Bytef *)fp->cbuf;
1666
2.85k
      fp->stream.next_out  = NULL;
1667
2.85k
      fp->stream.avail_in  = (uInt)bytes;
1668
2.85k
      fp->stream.avail_out = 0;
1669
2.85k
      fp->crc              = crc32(0L, Z_NULL, 0);
1670
1671
2.85k
      if (inflateInit2(&(fp->stream), -15) != Z_OK)
1672
0
      {
1673
0
        fp->eof = true;
1674
0
        errno   = EIO;
1675
1676
0
  return (-1);
1677
0
      }
1678
1679
2.85k
      fp->compressed = true;
1680
2.85k
    }
1681
1682
186k
    if (fp->compressed)
1683
186k
    {
1684
      // If we have reached end-of-file, return immediately...
1685
186k
      if (fp->eof)
1686
181
  return (0);
1687
1688
      // Fill the decompression buffer as needed...
1689
186k
      if (fp->stream.avail_in == 0)
1690
3.53k
      {
1691
3.53k
  if ((bytes = cups_read(fp, (char *)fp->cbuf, sizeof(fp->cbuf))) <= 0)
1692
1.66k
  {
1693
1.66k
    fp->eof = true;
1694
1695
1.66k
          return (bytes);
1696
1.66k
  }
1697
1698
1.87k
  fp->stream.next_in  = fp->cbuf;
1699
1.87k
  fp->stream.avail_in = (uInt)bytes;
1700
1.87k
      }
1701
1702
      // Decompress data from the buffer...
1703
184k
      fp->stream.next_out  = (Bytef *)fp->buf;
1704
184k
      fp->stream.avail_out = sizeof(fp->buf);
1705
1706
184k
      status = inflate(&(fp->stream), Z_NO_FLUSH);
1707
1708
184k
      if (fp->stream.next_out > (Bytef *)fp->buf)
1709
184k
        fp->crc = crc32(fp->crc, (Bytef *)fp->buf, (uInt)(fp->stream.next_out - (Bytef *)fp->buf));
1710
1711
184k
      if (status == Z_STREAM_END)
1712
476
      {
1713
        // Read the CRC and length...
1714
476
  unsigned char trailer[8]; // Trailer bytes
1715
476
  uLong   tcrc;   // Trailer CRC
1716
476
  ssize_t   tbytes = 0; // Number of bytes
1717
1718
476
  if (fp->stream.avail_in > 0)
1719
473
  {
1720
    // Get the first N trailer bytes from the inflate stream...
1721
473
    if (fp->stream.avail_in > sizeof(trailer))
1722
109
      tbytes = (ssize_t)sizeof(trailer);
1723
364
    else
1724
364
      tbytes = (ssize_t)fp->stream.avail_in;
1725
1726
473
    memcpy(trailer, fp->stream.next_in, (size_t)tbytes);
1727
473
    fp->stream.next_in  += tbytes;
1728
473
    fp->stream.avail_in -= (size_t)tbytes;
1729
473
  }
1730
1731
        // Reset the compressed flag so that we re-read the file header...
1732
476
        inflateEnd(&fp->stream);
1733
1734
476
  fp->compressed = false;
1735
1736
        // Get any remaining trailer bytes...
1737
476
        if (tbytes < (ssize_t)sizeof(trailer))
1738
14
  {
1739
14
    if (read(fp->fd, trailer + tbytes, sizeof(trailer) - (size_t)tbytes) < ((ssize_t)sizeof(trailer) - tbytes))
1740
9
    {
1741
      // Can't get it, so mark end-of-file...
1742
9
      fp->eof = true;
1743
9
      errno   = EIO;
1744
1745
9
      return (-1);
1746
9
    }
1747
14
  }
1748
1749
        // Calculate and compare the CRC...
1750
467
  tcrc = ((((((uLong)trailer[3] << 8) | (uLong)trailer[2]) << 8) | (uLong)trailer[1]) << 8) | (uLong)trailer[0];
1751
1752
467
  if (tcrc != fp->crc)
1753
148
  {
1754
    // Bad CRC, mark end-of-file...
1755
148
    fp->eof = true;
1756
148
    errno   = EIO;
1757
1758
148
    return (-1);
1759
148
  }
1760
467
      }
1761
184k
      else if (status < Z_OK)
1762
125
      {
1763
125
        fp->eof = true;
1764
125
        errno   = EIO;
1765
1766
125
  return (-1);
1767
125
      }
1768
1769
184k
      bytes = (ssize_t)sizeof(fp->buf) - (ssize_t)fp->stream.avail_out;
1770
1771
      // Return the decompressed data...
1772
184k
      fp->ptr = fp->buf;
1773
184k
      fp->end = fp->buf + bytes;
1774
1775
184k
      if (bytes)
1776
184k
  return (bytes);
1777
184k
    }
1778
186k
  }
1779
1780
  // Read a buffer's full of data...
1781
7.33k
  if ((bytes = cups_read(fp, fp->buf, sizeof(fp->buf))) <= 0)
1782
6.33k
  {
1783
    // Can't read from file!
1784
6.33k
    fp->eof = true;
1785
6.33k
    fp->ptr = fp->buf;
1786
6.33k
    fp->end = fp->buf;
1787
6.33k
  }
1788
999
  else
1789
999
  {
1790
    // Return the bytes we read...
1791
999
    fp->eof = false;
1792
999
    fp->ptr = fp->buf;
1793
999
    fp->end = fp->buf + bytes;
1794
999
  }
1795
1796
7.33k
  return (bytes);
1797
200k
}
1798
1799
1800
//
1801
// 'cups_open()' - Safely open a file for writing.
1802
//
1803
// We don't allow appending to directories or files that are hard-linked or
1804
// symlinked.
1805
//
1806
1807
static int        // O - File descriptor or -1 otherwise
1808
cups_open(const char *filename,   // I - Filename
1809
          int        oflag,   // I - Open flags
1810
    int        mode)    // I - Open permissions
1811
17.3k
{
1812
17.3k
  int   fd;     // File descriptor
1813
17.3k
  struct stat fileinfo;   // File information
1814
17.3k
#ifndef _WIN32
1815
17.3k
  struct stat linkinfo;   // Link information
1816
17.3k
#endif // !_WIN32
1817
1818
1819
  // Open the file...
1820
17.3k
  if ((fd = open(filename, oflag, mode)) < 0)
1821
7.15k
    return (-1);
1822
1823
  // Then verify that the file descriptor doesn't point to a directory or hard-linked file.
1824
10.1k
  if (fstat(fd, &fileinfo))
1825
0
  {
1826
0
    int temp = errno;
1827
0
    close(fd);
1828
0
    errno = temp;
1829
0
    return (-1);
1830
0
  }
1831
1832
10.1k
  if (fileinfo.st_nlink != 1)
1833
0
  {
1834
0
    close(fd);
1835
0
    errno = EPERM;
1836
0
    return (-1);
1837
0
  }
1838
1839
#ifdef _WIN32
1840
  if (fileinfo.st_mode & _S_IFDIR)
1841
#else
1842
10.1k
  if (S_ISDIR(fileinfo.st_mode))
1843
0
#endif // _WIN32
1844
0
  {
1845
0
    close(fd);
1846
0
    errno = EISDIR;
1847
0
    return (-1);
1848
0
  }
1849
1850
10.1k
#ifndef _WIN32
1851
  // Then use lstat to determine whether the filename is a symlink...
1852
10.1k
  if (lstat(filename, &linkinfo))
1853
0
  {
1854
0
    int temp = errno;
1855
0
    close(fd);
1856
0
    errno = temp;
1857
0
    return (-1);
1858
0
  }
1859
1860
10.1k
  if (S_ISLNK(linkinfo.st_mode) ||
1861
10.1k
      fileinfo.st_dev != linkinfo.st_dev ||
1862
10.1k
      fileinfo.st_ino != linkinfo.st_ino ||
1863
#ifdef HAVE_ST_GEN
1864
      fileinfo.st_gen != linkinfo.st_gen ||
1865
#endif // HAVE_ST_GEN
1866
10.1k
      fileinfo.st_nlink != linkinfo.st_nlink ||
1867
10.1k
      fileinfo.st_mode != linkinfo.st_mode)
1868
0
  {
1869
    // Yes, don't allow!
1870
0
    close(fd);
1871
0
    errno = EPERM;
1872
0
    return (-1);
1873
0
  }
1874
10.1k
#endif // !_WIN32
1875
1876
10.1k
  return (fd);
1877
10.1k
}
1878
1879
1880
//
1881
// 'cups_read()' - Read from a file descriptor.
1882
//
1883
1884
static ssize_t        // O - Number of bytes read or -1
1885
cups_read(cups_file_t *fp,    // I - CUPS file
1886
          char        *buf,   // I - Buffer
1887
    size_t      bytes)    // I - Number bytes
1888
20.0k
{
1889
20.0k
  ssize_t total;      // Total bytes read
1890
1891
1892
  // Loop until we read at least 0 bytes...
1893
20.0k
  for (;;)
1894
20.0k
  {
1895
#ifdef _WIN32
1896
    if (fp->mode == 's')
1897
      total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
1898
    else
1899
      total = (ssize_t)read(fp->fd, buf, (unsigned)bytes);
1900
#else
1901
20.0k
    if (fp->mode == 's')
1902
0
      total = recv(fp->fd, buf, bytes, 0);
1903
20.0k
    else
1904
20.0k
      total = read(fp->fd, buf, bytes);
1905
20.0k
#endif // _WIN32
1906
1907
20.0k
    if (total >= 0)
1908
20.0k
      break;
1909
1910
    // Reads can be interrupted by signals and unavailable resources...
1911
0
    if (errno == EAGAIN || errno == EINTR)
1912
0
      continue;
1913
0
    else
1914
0
      return (-1);
1915
0
  }
1916
1917
  // Return the total number of bytes read...
1918
20.0k
  return (total);
1919
20.0k
}
1920
1921
1922
//
1923
// 'cups_write()' - Write to a file descriptor.
1924
//
1925
1926
static bool       // O - `true` on success, `false` on error
1927
cups_write(cups_file_t *fp,   // I - CUPS file
1928
           const char  *buf,    // I - Buffer
1929
     size_t      bytes)   // I - Number bytes
1930
190k
{
1931
190k
  ssize_t count;      // Count this time
1932
1933
1934
  // Loop until all bytes are written...
1935
380k
  while (bytes > 0)
1936
190k
  {
1937
#ifdef _WIN32
1938
    if (fp->mode == 's')
1939
      count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
1940
    else
1941
      count = (ssize_t)write(fp->fd, buf, (unsigned)bytes);
1942
#else
1943
190k
    if (fp->mode == 's')
1944
0
      count = send(fp->fd, buf, bytes, 0);
1945
190k
    else
1946
190k
      count = write(fp->fd, buf, bytes);
1947
190k
#endif // _WIN32
1948
1949
190k
    if (count < 0)
1950
0
    {
1951
      // Writes can be interrupted by signals and unavailable resources...
1952
0
      if (errno == EAGAIN || errno == EINTR)
1953
0
        continue;
1954
0
      else
1955
0
        return (false);
1956
0
    }
1957
1958
    // Update the counts for the last write call...
1959
190k
    bytes -= (size_t)count;
1960
190k
    buf   += count;
1961
190k
  }
1962
1963
  // Return the total number of bytes written...
1964
190k
  return (true);
1965
190k
}