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
__archive_clean(struct archive * a)84 __archive_clean(struct archive *a)
85 {
86 archive_string_conversion_free(a);
87 return (ARCHIVE_OK);
88 }
89
90 int
archive_version_number(void)91 archive_version_number(void)
92 {
93 return (ARCHIVE_VERSION_NUMBER);
94 }
95
96 const char *
archive_version_string(void)97 archive_version_string(void)
98 {
99 return (ARCHIVE_VERSION_STRING);
100 }
101
102 int
archive_errno(struct archive * a)103 archive_errno(struct archive *a)
104 {
105 return (a->archive_error_number);
106 }
107
108 const char *
archive_error_string(struct archive * a)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
archive_file_count(struct archive * a)119 archive_file_count(struct archive *a)
120 {
121 return (a->file_count);
122 }
123
124 int
archive_format(struct archive * a)125 archive_format(struct archive *a)
126 {
127 return (a->archive_format);
128 }
129
130 const char *
archive_format_name(struct archive * a)131 archive_format_name(struct archive *a)
132 {
133 return (a->archive_format_name);
134 }
135
136
137 int
archive_compression(struct archive * a)138 archive_compression(struct archive *a)
139 {
140 return archive_filter_code(a, 0);
141 }
142
143 const char *
archive_compression_name(struct archive * a)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
archive_position_compressed(struct archive * a)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
archive_position_uncompressed(struct archive * a)163 archive_position_uncompressed(struct archive *a)
164 {
165 return archive_filter_bytes(a, 0);
166 }
167
168 void
archive_clear_error(struct archive * a)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
archive_set_error(struct archive * a,int error_number,const char * fmt,...)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
archive_copy_error(struct archive * dest,struct archive * src)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
__archive_errx(int retvalue,const char * msg)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
__archive_mktempx(const char * tmpdir,wchar_t * template)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
__archive_mktemp(const char * tmpdir)431 __archive_mktemp(const char *tmpdir)
432 {
433 return __archive_mktempx(tmpdir, NULL);
434 }
435
436 int
__archive_mkstemp(wchar_t * template)437 __archive_mkstemp(wchar_t *template)
438 {
439 return __archive_mktempx(NULL, template);
440 }
441
442 #else
443
444 static int
get_tempdir(struct archive_string * temppath)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
__archive_mktemp(const char * tmpdir)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
__archive_mkstemp(char * template)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
__archive_mktempx(const char * tmpdir,char * template)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
__archive_mktemp(const char * tmpdir)590 __archive_mktemp(const char *tmpdir)
591 {
592 return __archive_mktempx(tmpdir, NULL);
593 }
594
595 int
__archive_mkstemp(char * template)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
__archive_ensure_cloexec_flag(int fd)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
archive_utility_string_sort_helper(char ** strings,unsigned int n)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
archive_utility_string_sort(char ** strings)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