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