Coverage Report

Created: 2026-09-14 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmlibarchive/libarchive/archive_util.c
Line
Count
Source
1
/*-
2
 * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
3
 * Copyright (c) 2003-2007 Tim Kientzle
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 */
26
27
#include "archive_platform.h"
28
29
#ifdef HAVE_SYS_TYPES_H
30
#include <sys/types.h>
31
#endif
32
#ifdef HAVE_ERRNO_H
33
#include <errno.h>
34
#endif
35
#ifdef HAVE_FCNTL_H
36
#include <fcntl.h>
37
#endif
38
#ifdef HAVE_STDLIB_H
39
#include <stdlib.h>
40
#endif
41
#ifdef HAVE_STRING_H
42
#include <string.h>
43
#endif
44
#if defined(_WIN32) && !defined(__CYGWIN__)
45
#include <bcrypt.h>
46
47
/* Common in other bcrypt implementations, but missing from VS2008. */
48
#ifndef BCRYPT_SUCCESS
49
#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
50
#endif
51
#endif
52
#ifdef HAVE_ZLIB_H
53
#include <cm3p/zlib.h>
54
#endif
55
#ifdef HAVE_LZMA_H
56
#include <cm3p/lzma.h>
57
#endif
58
#ifdef HAVE_BZLIB_H
59
#include <cm3p/bzlib.h>
60
#endif
61
#ifdef HAVE_LZ4_H
62
#include <lz4.h>
63
#endif
64
65
#include "archive.h"
66
#include "archive_private.h"
67
#include "archive_random_private.h"
68
#include "archive_string.h"
69
70
#ifndef O_CLOEXEC
71
#define O_CLOEXEC 0
72
#endif
73
74
#if ARCHIVE_VERSION_NUMBER < 4000000
75
static int __LA_LIBC_CC archive_utility_string_sort_helper(const void *, const void *);
76
#endif
77
78
/* Generic initialization of 'struct archive' objects. */
79
int
80
__archive_clean(struct archive *a)
81
66.8k
{
82
66.8k
  archive_string_conversion_free(a);
83
66.8k
  return (ARCHIVE_OK);
84
66.8k
}
85
86
int
87
archive_version_number(void)
88
0
{
89
0
  return (ARCHIVE_VERSION_NUMBER);
90
0
}
91
92
const char *
93
archive_version_string(void)
94
0
{
95
0
  return (ARCHIVE_VERSION_STRING);
96
0
}
97
98
int
99
archive_errno(struct archive *a)
100
260
{
101
260
  return (a->archive_error_number);
102
260
}
103
104
const char *
105
archive_error_string(struct archive *a)
106
32.5k
{
107
32.5k
  if (a->error != NULL && *a->error != '\0')
108
30.9k
    return (a->error);
109
1.60k
  else
110
1.60k
    return (NULL);
111
32.5k
}
112
113
int
114
archive_file_count(struct archive *a)
115
0
{
116
0
  return (a->file_count);
117
0
}
118
119
int
120
archive_format(struct archive *a)
121
0
{
122
0
  return (a->archive_format);
123
0
}
124
125
const char *
126
archive_format_name(struct archive *a)
127
0
{
128
0
  return (a->archive_format_name);
129
0
}
130
131
132
int
133
archive_compression(struct archive *a)
134
0
{
135
0
  return archive_filter_code(a, 0);
136
0
}
137
138
const char *
139
archive_compression_name(struct archive *a)
140
0
{
141
0
  return archive_filter_name(a, 0);
142
0
}
143
144
145
/*
146
 * Return a count of the number of compressed bytes processed.
147
 */
148
la_int64_t
149
archive_position_compressed(struct archive *a)
150
0
{
151
0
  return archive_filter_bytes(a, -1);
152
0
}
153
154
/*
155
 * Return a count of the number of uncompressed bytes processed.
156
 */
157
la_int64_t
158
archive_position_uncompressed(struct archive *a)
159
0
{
160
0
  return archive_filter_bytes(a, 0);
161
0
}
162
163
void
164
archive_clear_error(struct archive *a)
165
392k
{
166
392k
  archive_string_empty(&a->error_string);
167
392k
  a->error = NULL;
168
392k
  a->archive_error_number = 0;
169
392k
}
170
171
void
172
archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
173
358k
{
174
358k
  va_list ap;
175
176
358k
  a->archive_error_number = error_number;
177
358k
  if (fmt == NULL) {
178
0
    a->error = NULL;
179
0
    return;
180
0
  }
181
182
358k
  archive_string_empty(&(a->error_string));
183
358k
  va_start(ap, fmt);
184
358k
  archive_string_vsprintf(&(a->error_string), fmt, ap);
185
358k
  va_end(ap);
186
358k
  a->error = a->error_string.s;
187
358k
}
188
189
void
190
archive_copy_error(struct archive *dest, struct archive *src)
191
0
{
192
0
  dest->archive_error_number = src->archive_error_number;
193
194
0
  archive_string_copy(&dest->error_string, &src->error_string);
195
0
  dest->error = dest->error_string.s;
196
0
}
197
198
void
199
__archive_errx(int retvalue, const char *msg)
200
0
{
201
0
  static const char msg1[] = "Fatal Internal Error in libarchive: ";
202
0
  size_t s;
203
204
0
  s = write(2, msg1, strlen(msg1));
205
0
  (void)s; /* UNUSED */
206
0
  s = write(2, msg, strlen(msg));
207
0
  (void)s; /* UNUSED */
208
0
  s = write(2, "\n", 1);
209
0
  (void)s; /* UNUSED */
210
0
  exit(retvalue);
211
0
}
212
213
/*
214
 * Create a temporary file
215
 */
216
#if defined(_WIN32) && !defined(__CYGWIN__)
217
218
/*
219
 * Do not use Windows tmpfile() function.
220
 * It will make a temporary file under the root directory
221
 * and it'll cause permission error if a user who is
222
 * non-Administrator creates temporary files.
223
 * Also Windows version of mktemp family including _mktemp_s
224
 * are not secure.
225
 */
226
static int
227
__archive_mktempx(const char *tmpdir, wchar_t *template)
228
{
229
  static const wchar_t prefix[] = L"libarchive_";
230
  static const wchar_t suffix[] = L"XXXXXXXXXX";
231
  static const wchar_t num[] = {
232
    L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
233
    L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
234
    L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
235
    L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
236
    L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
237
    L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
238
    L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
239
    L'u', L'v', L'w', L'x', L'y', L'z'
240
  };
241
  struct archive_wstring temp_name;
242
  wchar_t *ws;
243
  DWORD attr;
244
  wchar_t *xp, *ep;
245
  int fd;
246
  BCRYPT_ALG_HANDLE hAlg = NULL;
247
  fd = -1;
248
  ws = NULL;
249
  archive_string_init(&temp_name);
250
251
  if (template == NULL) {
252
    /* Get a temporary directory. */
253
    if (tmpdir == NULL) {
254
      wchar_t buf[MAX_PATH + 1];
255
      wchar_t *p = buf, *buf2 = NULL;
256
      size_t l, s;
257
258
      s = MAX_PATH + 1;
259
      l = GetTempPathW((DWORD)s, buf);
260
      if (l == 0) {
261
        la_dosmaperr(GetLastError());
262
        goto exit_tmpfile;
263
      }
264
      while (l > s) {
265
        wchar_t *tmp;
266
267
        s = l;
268
        tmp = realloc(buf2, s * sizeof(wchar_t));
269
        if (tmp == NULL) {
270
          free(buf2);
271
          errno = ENOMEM;
272
          goto exit_tmpfile;
273
        }
274
        p = buf2 = tmp;
275
        l = GetTempPathW((DWORD)s, buf2);
276
        if (l == 0) {
277
          free(buf2);
278
          la_dosmaperr(GetLastError());
279
          goto exit_tmpfile;
280
        }
281
      }
282
      archive_wstrcpy(&temp_name, p);
283
      free(buf2);
284
    } else {
285
      if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
286
          strlen(tmpdir)) < 0)
287
        goto exit_tmpfile;
288
      if (temp_name.length == 0 ||
289
          temp_name.s[temp_name.length-1] != L'/')
290
        archive_wstrappend_wchar(&temp_name, L'/');
291
    }
292
293
    /* Check if temp_name is a directory. */
294
    attr = GetFileAttributesW(temp_name.s);
295
    if (attr == (DWORD)-1) {
296
      if (GetLastError() != ERROR_FILE_NOT_FOUND) {
297
        la_dosmaperr(GetLastError());
298
        goto exit_tmpfile;
299
      }
300
      ws = __la_win_permissive_name_w(temp_name.s);
301
      if (ws == NULL) {
302
        errno = EINVAL;
303
        goto exit_tmpfile;
304
      }
305
      attr = GetFileAttributesW(ws);
306
      if (attr == (DWORD)-1) {
307
        la_dosmaperr(GetLastError());
308
        goto exit_tmpfile;
309
      }
310
    }
311
    if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
312
      errno = ENOTDIR;
313
      goto exit_tmpfile;
314
    }
315
316
    /*
317
     * Create a temporary file.
318
     */
319
    archive_wstrcat(&temp_name, prefix);
320
    archive_wstrcat(&temp_name, suffix);
321
    ep = temp_name.s + archive_strlen(&temp_name);
322
    xp = ep - wcslen(suffix);
323
    template = temp_name.s;
324
  } else {
325
    xp = wcschr(template, L'X');
326
    if (xp == NULL) /* No X, programming error */
327
      abort();
328
    for (ep = xp; *ep == L'X'; ep++)
329
      continue;
330
    if (*ep)  /* X followed by non X, programming error */
331
      abort();
332
  }
333
334
  if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
335
    NULL, 0))) {
336
    la_dosmaperr(GetLastError());
337
    goto exit_tmpfile;
338
  }
339
340
  for (;;) {
341
    wchar_t *p;
342
    HANDLE h;
343
# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
344
    CREATEFILE2_EXTENDED_PARAMETERS createExParams;
345
#endif
346
347
    /* Generate a random file name through CryptGenRandom(). */
348
    p = xp;
349
    if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
350
        (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
351
      la_dosmaperr(GetLastError());
352
      goto exit_tmpfile;
353
    }
354
    for (; p < ep; p++)
355
      *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
356
357
    free(ws);
358
    ws = __la_win_permissive_name_w(template);
359
    if (ws == NULL) {
360
      errno = EINVAL;
361
      goto exit_tmpfile;
362
    }
363
    if (template == temp_name.s) {
364
      attr = FILE_ATTRIBUTE_TEMPORARY |
365
             FILE_FLAG_DELETE_ON_CLOSE;
366
    } else {
367
      /* mkstemp */
368
      attr = FILE_ATTRIBUTE_NORMAL;
369
    }
370
# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
371
    ZeroMemory(&createExParams, sizeof(createExParams));
372
    createExParams.dwSize = sizeof(createExParams);
373
    createExParams.dwFileAttributes = attr & 0xFFFF;
374
    createExParams.dwFileFlags = attr & 0xFFF00000;
375
    h = CreateFile2(ws,
376
        GENERIC_READ | GENERIC_WRITE | DELETE,
377
        0,/* Not share */
378
      CREATE_NEW,
379
      &createExParams);
380
#else
381
    h = CreateFileW(ws,
382
        GENERIC_READ | GENERIC_WRITE | DELETE,
383
        0,/* Not share */
384
        NULL,
385
        CREATE_NEW,/* Create a new file only */
386
        attr,
387
        NULL);
388
#endif
389
    if (h == INVALID_HANDLE_VALUE) {
390
      /* The same file already exists. retry with
391
       * a new filename. */
392
      if (GetLastError() == ERROR_FILE_EXISTS)
393
        continue;
394
      /* Otherwise, fail creation temporary file. */
395
      la_dosmaperr(GetLastError());
396
      goto exit_tmpfile;
397
    }
398
    fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
399
    if (fd == -1) {
400
      la_dosmaperr(GetLastError());
401
      CloseHandle(h);
402
      goto exit_tmpfile;
403
    } else
404
      break;/* success! */
405
  }
406
exit_tmpfile:
407
  if (hAlg != NULL)
408
    BCryptCloseAlgorithmProvider(hAlg, 0);
409
  free(ws);
410
  if (template == temp_name.s)
411
    archive_wstring_free(&temp_name);
412
  return (fd);
413
}
414
415
int
416
__archive_mktemp(const char *tmpdir)
417
{
418
  return __archive_mktempx(tmpdir, NULL);
419
}
420
421
int
422
__archive_mkstemp(wchar_t *template)
423
{
424
  return __archive_mktempx(NULL, template);
425
}
426
427
#else
428
429
static int
430
__archive_issetugid(void)
431
0
{
432
#ifdef HAVE_ISSETUGID
433
  return (issetugid());
434
#elif HAVE_GETRESUID
435
  uid_t ruid, euid, suid;
436
  gid_t rgid, egid, sgid;
437
  if (getresuid(&ruid, &euid, &suid) != 0)
438
    return (-1);
439
  if (ruid != euid || ruid != suid)
440
    return (1);
441
  if (getresgid(&rgid, &egid, &sgid) != 0)
442
    return (-1);
443
  if (rgid != egid || rgid != sgid)
444
    return (1);
445
#elif HAVE_GETEUID
446
0
  if (geteuid() != getuid())
447
0
    return (1);
448
0
#if HAVE_GETEGID
449
0
  if (getegid() != getgid())
450
0
    return (1);
451
0
#endif
452
0
#endif
453
0
  return (0);
454
0
}
455
456
int
457
__archive_get_tempdir(struct archive_string *temppath)
458
0
{
459
0
  const char *tmp = NULL;
460
461
0
  if (__archive_issetugid() == 0)
462
0
    tmp = getenv("TMPDIR");
463
0
  if (tmp == NULL)
464
#ifdef _PATH_TMP
465
    tmp = _PATH_TMP;
466
#else
467
0
                tmp = "/tmp";
468
0
#endif
469
0
  archive_strcpy(temppath, tmp);
470
0
  if (temppath->length == 0 || temppath->s[temppath->length-1] != '/')
471
0
    archive_strappend_char(temppath, '/');
472
0
  return (ARCHIVE_OK);
473
0
}
474
475
#if defined(HAVE_MKSTEMP)
476
477
/*
478
 * We can use mkstemp().
479
 */
480
481
int
482
__archive_mktemp(const char *tmpdir)
483
0
{
484
0
  struct archive_string temp_name;
485
0
  int fd = -1;
486
487
0
  archive_string_init(&temp_name);
488
0
  if (tmpdir == NULL) {
489
0
    if (__archive_get_tempdir(&temp_name) != ARCHIVE_OK)
490
0
      goto exit_tmpfile;
491
0
  } else {
492
0
    archive_strcpy(&temp_name, tmpdir);
493
0
    if (temp_name.length == 0 ||
494
0
        temp_name.s[temp_name.length-1] != '/')
495
0
      archive_strappend_char(&temp_name, '/');
496
0
  }
497
0
#ifdef O_TMPFILE
498
0
  fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600); 
499
0
  if(fd >= 0)
500
0
    goto exit_tmpfile;
501
0
#endif
502
0
  archive_strcat(&temp_name, "libarchive_XXXXXX");
503
0
  fd = mkstemp(temp_name.s);
504
0
  if (fd < 0)
505
0
    goto exit_tmpfile;
506
0
  __archive_ensure_cloexec_flag(fd);
507
0
  unlink(temp_name.s);
508
0
exit_tmpfile:
509
0
  archive_string_free(&temp_name);
510
0
  return (fd);
511
0
}
512
513
int
514
__archive_mkstemp(char *template)
515
0
{
516
0
  int fd = -1;
517
0
  fd = mkstemp(template);
518
0
  if (fd >= 0)
519
0
    __archive_ensure_cloexec_flag(fd);
520
0
  return (fd);
521
0
}
522
523
#else /* !HAVE_MKSTEMP */
524
525
/*
526
 * We use a private routine.
527
 */
528
529
static int
530
__archive_mktempx(const char *tmpdir, char *template)
531
{
532
        static const char num[] = {
533
    '0', '1', '2', '3', '4', '5', '6', '7',
534
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
535
    'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
536
    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
537
    'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
538
    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
539
    'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
540
    'u', 'v', 'w', 'x', 'y', 'z'
541
        };
542
  struct archive_string temp_name;
543
  struct stat st;
544
  int fd;
545
  char *tp, *ep;
546
547
  fd = -1;
548
  if (template == NULL) {
549
    archive_string_init(&temp_name);
550
    if (tmpdir == NULL) {
551
      if (__archive_get_tempdir(&temp_name) != ARCHIVE_OK)
552
        goto exit_tmpfile;
553
    } else
554
      archive_strcpy(&temp_name, tmpdir);
555
    if (temp_name.length > 0 && temp_name.s[temp_name.length-1] == '/') {
556
      temp_name.s[temp_name.length-1] = '\0';
557
      temp_name.length --;
558
    }
559
    if (la_stat(temp_name.s, &st) < 0)
560
      goto exit_tmpfile;
561
    if (!S_ISDIR(st.st_mode)) {
562
      errno = ENOTDIR;
563
      goto exit_tmpfile;
564
    }
565
    archive_strcat(&temp_name, "/libarchive_");
566
    tp = temp_name.s + archive_strlen(&temp_name);
567
    archive_strcat(&temp_name, "XXXXXXXXXX");
568
    ep = temp_name.s + archive_strlen(&temp_name);
569
    template = temp_name.s;
570
  } else {
571
    tp = strchr(template, 'X');
572
    if (tp == NULL) /* No X, programming error */
573
      abort();
574
    for (ep = tp; *ep == 'X'; ep++)
575
      continue;
576
    if (*ep)  /* X followed by non X, programming error */
577
      abort();
578
  }
579
580
  do {
581
    char *p;
582
583
    p = tp;
584
    archive_random(p, ep - p);
585
    while (p < ep) {
586
      int d = *((unsigned char *)p) % sizeof(num);
587
      *p++ = num[d];
588
    }
589
    fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
590
        0600);
591
  } while (fd < 0 && errno == EEXIST);
592
  if (fd < 0)
593
    goto exit_tmpfile;
594
  __archive_ensure_cloexec_flag(fd);
595
  if (template == temp_name.s)
596
    unlink(temp_name.s);
597
exit_tmpfile:
598
  if (template == temp_name.s)
599
    archive_string_free(&temp_name);
600
  return (fd);
601
}
602
603
int
604
__archive_mktemp(const char *tmpdir)
605
{
606
  return __archive_mktempx(tmpdir, NULL);
607
}
608
609
int
610
__archive_mkstemp(char *template)
611
{
612
  return __archive_mktempx(NULL, template);
613
}
614
615
#endif /* !HAVE_MKSTEMP */
616
#endif /* !_WIN32 || __CYGWIN__ */
617
618
/*
619
 * Set FD_CLOEXEC flag to a file descriptor if it is not set.
620
 * We have to set the flag if the platform does not provide O_CLOEXEC
621
 * or F_DUPFD_CLOEXEC flags.
622
 *
623
 * Note: This function is absolutely called after creating a new file
624
 * descriptor even if the platform seemingly provides O_CLOEXEC or
625
 * F_DUPFD_CLOEXEC macros because it is possible that the platform
626
 * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
627
 */
628
void
629
__archive_ensure_cloexec_flag(int fd)
630
87.7k
{
631
#if defined(_WIN32) && !defined(__CYGWIN__)
632
  (void)fd; /* UNUSED */
633
#else
634
87.7k
  int flags;
635
636
87.7k
  if (fd >= 0) {
637
61.6k
    flags = fcntl(fd, F_GETFD);
638
61.6k
    if (flags != -1 && (flags & FD_CLOEXEC) == 0)
639
0
      fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
640
61.6k
  }
641
87.7k
#endif
642
87.7k
}
643
644
#if ARCHIVE_VERSION_NUMBER < 4000000
645
/*
646
 * Utility functions to sort a group of strings using quicksort.
647
 */
648
static int
649
__LA_LIBC_CC
650
archive_utility_string_sort_helper(const void *p1, const void *p2)
651
0
{
652
0
  const char * const * const s1 = p1;
653
0
  const char * const * const s2 = p2;
654
655
0
  return strcmp(*s1, *s2);
656
0
}
657
658
int
659
archive_utility_string_sort(char **strings)
660
0
{
661
0
  size_t size = 0;
662
0
  while (strings[size] != NULL)
663
0
    size++;
664
0
  qsort(strings, size, sizeof(char *),
665
0
        archive_utility_string_sort_helper);
666
0
  return (ARCHIVE_OK);
667
0
}
668
#endif