xref: /freebsd/contrib/libarchive/libarchive/archive_util.c (revision 2e113ef82465598b8c26e0ca415fbe90677fbd47)
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
get_tempdir(struct archive_string * temppath)448 get_tempdir(struct archive_string *temppath)
449 {
450 	const char *tmp;
451 
452 	tmp = getenv("TMPDIR");
453 	if (tmp == NULL)
454 #ifdef _PATH_TMP
455 		tmp = _PATH_TMP;
456 #else
457                 tmp = "/tmp";
458 #endif
459 	archive_strcpy(temppath, tmp);
460 	if (temppath->length == 0 || temppath->s[temppath->length-1] != '/')
461 		archive_strappend_char(temppath, '/');
462 	return (ARCHIVE_OK);
463 }
464 
465 #if defined(HAVE_MKSTEMP)
466 
467 /*
468  * We can use mkstemp().
469  */
470 
471 int
__archive_mktemp(const char * tmpdir)472 __archive_mktemp(const char *tmpdir)
473 {
474 	struct archive_string temp_name;
475 	int fd = -1;
476 
477 	archive_string_init(&temp_name);
478 	if (tmpdir == NULL) {
479 		if (get_tempdir(&temp_name) != ARCHIVE_OK)
480 			goto exit_tmpfile;
481 	} else {
482 		archive_strcpy(&temp_name, tmpdir);
483 		if (temp_name.length == 0 ||
484 		    temp_name.s[temp_name.length-1] != '/')
485 			archive_strappend_char(&temp_name, '/');
486 	}
487 #ifdef O_TMPFILE
488 	fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600);
489 	if(fd >= 0)
490 		goto exit_tmpfile;
491 #endif
492 	archive_strcat(&temp_name, "libarchive_XXXXXX");
493 	fd = mkstemp(temp_name.s);
494 	if (fd < 0)
495 		goto exit_tmpfile;
496 	__archive_ensure_cloexec_flag(fd);
497 	unlink(temp_name.s);
498 exit_tmpfile:
499 	archive_string_free(&temp_name);
500 	return (fd);
501 }
502 
503 int
__archive_mkstemp(char * template)504 __archive_mkstemp(char *template)
505 {
506 	int fd = -1;
507 	fd = mkstemp(template);
508 	if (fd >= 0)
509 		__archive_ensure_cloexec_flag(fd);
510 	return (fd);
511 }
512 
513 #else /* !HAVE_MKSTEMP */
514 
515 /*
516  * We use a private routine.
517  */
518 
519 static int
__archive_mktempx(const char * tmpdir,char * template)520 __archive_mktempx(const char *tmpdir, char *template)
521 {
522         static const char num[] = {
523 		'0', '1', '2', '3', '4', '5', '6', '7',
524 		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
525 		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
526 		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
527 		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
528 		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
529 		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
530 		'u', 'v', 'w', 'x', 'y', 'z'
531         };
532 	struct archive_string temp_name;
533 	struct stat st;
534 	int fd;
535 	char *tp, *ep;
536 
537 	fd = -1;
538 	if (template == NULL) {
539 		archive_string_init(&temp_name);
540 		if (tmpdir == NULL) {
541 			if (get_tempdir(&temp_name) != ARCHIVE_OK)
542 				goto exit_tmpfile;
543 		} else
544 			archive_strcpy(&temp_name, tmpdir);
545 		if (temp_name.length > 0 && temp_name.s[temp_name.length-1] == '/') {
546 			temp_name.s[temp_name.length-1] = '\0';
547 			temp_name.length --;
548 		}
549 		if (la_stat(temp_name.s, &st) < 0)
550 			goto exit_tmpfile;
551 		if (!S_ISDIR(st.st_mode)) {
552 			errno = ENOTDIR;
553 			goto exit_tmpfile;
554 		}
555 		archive_strcat(&temp_name, "/libarchive_");
556 		tp = temp_name.s + archive_strlen(&temp_name);
557 		archive_strcat(&temp_name, "XXXXXXXXXX");
558 		ep = temp_name.s + archive_strlen(&temp_name);
559 		template = temp_name.s;
560 	} else {
561 		tp = strchr(template, 'X');
562 		if (tp == NULL)	/* No X, programming error */
563 			abort();
564 		for (ep = tp; *ep == 'X'; ep++)
565 			continue;
566 		if (*ep)	/* X followed by non X, programming error */
567 			abort();
568 	}
569 
570 	do {
571 		char *p;
572 
573 		p = tp;
574 		archive_random(p, ep - p);
575 		while (p < ep) {
576 			int d = *((unsigned char *)p) % sizeof(num);
577 			*p++ = num[d];
578 		}
579 		fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
580 			  0600);
581 	} while (fd < 0 && errno == EEXIST);
582 	if (fd < 0)
583 		goto exit_tmpfile;
584 	__archive_ensure_cloexec_flag(fd);
585 	if (template == temp_name.s)
586 		unlink(temp_name.s);
587 exit_tmpfile:
588 	if (template == temp_name.s)
589 		archive_string_free(&temp_name);
590 	return (fd);
591 }
592 
593 int
__archive_mktemp(const char * tmpdir)594 __archive_mktemp(const char *tmpdir)
595 {
596 	return __archive_mktempx(tmpdir, NULL);
597 }
598 
599 int
__archive_mkstemp(char * template)600 __archive_mkstemp(char *template)
601 {
602 	return __archive_mktempx(NULL, template);
603 }
604 
605 #endif /* !HAVE_MKSTEMP */
606 #endif /* !_WIN32 || __CYGWIN__ */
607 
608 /*
609  * Set FD_CLOEXEC flag to a file descriptor if it is not set.
610  * We have to set the flag if the platform does not provide O_CLOEXEC
611  * or F_DUPFD_CLOEXEC flags.
612  *
613  * Note: This function is absolutely called after creating a new file
614  * descriptor even if the platform seemingly provides O_CLOEXEC or
615  * F_DUPFD_CLOEXEC macros because it is possible that the platform
616  * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
617  */
618 void
__archive_ensure_cloexec_flag(int fd)619 __archive_ensure_cloexec_flag(int fd)
620 {
621 #if defined(_WIN32) && !defined(__CYGWIN__)
622 	(void)fd; /* UNUSED */
623 #else
624 	int flags;
625 
626 	if (fd >= 0) {
627 		flags = fcntl(fd, F_GETFD);
628 		if (flags != -1 && (flags & FD_CLOEXEC) == 0)
629 			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
630 	}
631 #endif
632 }
633 
634 #if ARCHIVE_VERSION_NUMBER < 4000000
635 /*
636  * Utility functions to sort a group of strings using quicksort.
637  */
638 static int
639 __LA_LIBC_CC
archive_utility_string_sort_helper(const void * p1,const void * p2)640 archive_utility_string_sort_helper(const void *p1, const void *p2)
641 {
642 	const char * const * const s1 = p1;
643 	const char * const * const s2 = p2;
644 
645 	return strcmp(*s1, *s2);
646 }
647 
648 int
archive_utility_string_sort(char ** strings)649 archive_utility_string_sort(char **strings)
650 {
651 	size_t size = 0;
652 	while (strings[size] != NULL)
653 		size++;
654 	qsort(strings, size, sizeof(char *),
655 	      archive_utility_string_sort_helper);
656 	return (ARCHIVE_OK);
657 }
658 #endif
659