xref: /freebsd/contrib/libarchive/libarchive/archive_util.c (revision 401026e4825a05abba6f945cf1b74b3328876fa2)
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
__archive_clean(struct archive * a)86 __archive_clean(struct archive *a)
87 {
88 	archive_string_conversion_free(a);
89 	return (ARCHIVE_OK);
90 }
91 
92 int
archive_version_number(void)93 archive_version_number(void)
94 {
95 	return (ARCHIVE_VERSION_NUMBER);
96 }
97 
98 const char *
archive_version_string(void)99 archive_version_string(void)
100 {
101 	return (ARCHIVE_VERSION_STRING);
102 }
103 
104 int
archive_errno(struct archive * a)105 archive_errno(struct archive *a)
106 {
107 	return (a->archive_error_number);
108 }
109 
110 const char *
archive_error_string(struct archive * a)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
archive_file_count(struct archive * a)121 archive_file_count(struct archive *a)
122 {
123 	return (a->file_count);
124 }
125 
126 int
archive_format(struct archive * a)127 archive_format(struct archive *a)
128 {
129 	return (a->archive_format);
130 }
131 
132 const char *
archive_format_name(struct archive * a)133 archive_format_name(struct archive *a)
134 {
135 	return (a->archive_format_name);
136 }
137 
138 
139 int
archive_compression(struct archive * a)140 archive_compression(struct archive *a)
141 {
142 	return archive_filter_code(a, 0);
143 }
144 
145 const char *
archive_compression_name(struct archive * a)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
archive_position_compressed(struct archive * a)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
archive_position_uncompressed(struct archive * a)165 archive_position_uncompressed(struct archive *a)
166 {
167 	return archive_filter_bytes(a, 0);
168 }
169 
170 void
archive_clear_error(struct archive * a)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
archive_set_error(struct archive * a,int error_number,const char * fmt,...)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
archive_copy_error(struct archive * dest,struct archive * src)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
__archive_errx(int retvalue,const char * msg)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
__archive_mktempx(const char * tmpdir,wchar_t * template)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
__archive_mktemp(const char * tmpdir)434 __archive_mktemp(const char *tmpdir)
435 {
436 	return __archive_mktempx(tmpdir, NULL);
437 }
438 
439 int
__archive_mkstemp(wchar_t * template)440 __archive_mkstemp(wchar_t *template)
441 {
442 	return __archive_mktempx(NULL, template);
443 }
444 
445 #else
446 
447 static int
__archive_issetugid(void)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(&ruid, &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
__archive_get_tempdir(struct archive_string * temppath)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
__archive_mktemp(const char * tmpdir)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
__archive_mkstemp(char * template)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
__archive_mktempx(const char * tmpdir,char * template)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
__archive_mktemp(const char * tmpdir)622 __archive_mktemp(const char *tmpdir)
623 {
624 	return __archive_mktempx(tmpdir, NULL);
625 }
626 
627 int
__archive_mkstemp(char * template)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
__archive_ensure_cloexec_flag(int fd)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
archive_utility_string_sort_helper(const void * p1,const void * p2)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
archive_utility_string_sort(char ** strings)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