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