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