archive_util.c revision 358090
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/10/contrib/libarchive/libarchive/archive_util.c 358090 2020-02-19 01:51:44Z 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			CloseHandle(h);
369			goto exit_tmpfile;
370		} else
371			break;/* success! */
372	}
373exit_tmpfile:
374	if (hProv != (HCRYPTPROV)NULL)
375		CryptReleaseContext(hProv, 0);
376	free(ws);
377	if (template == temp_name.s)
378		archive_wstring_free(&temp_name);
379	return (fd);
380}
381
382int
383__archive_mktemp(const char *tmpdir)
384{
385	return __archive_mktempx(tmpdir, NULL);
386}
387
388int
389__archive_mkstemp(wchar_t *template)
390{
391	return __archive_mktempx(NULL, template);
392}
393
394#else
395
396static int
397get_tempdir(struct archive_string *temppath)
398{
399	const char *tmp;
400
401	tmp = getenv("TMPDIR");
402	if (tmp == NULL)
403#ifdef _PATH_TMP
404		tmp = _PATH_TMP;
405#else
406                tmp = "/tmp";
407#endif
408	archive_strcpy(temppath, tmp);
409	if (temppath->s[temppath->length-1] != '/')
410		archive_strappend_char(temppath, '/');
411	return (ARCHIVE_OK);
412}
413
414#if defined(HAVE_MKSTEMP)
415
416/*
417 * We can use mkstemp().
418 */
419
420int
421__archive_mktemp(const char *tmpdir)
422{
423	struct archive_string temp_name;
424	int fd = -1;
425
426	archive_string_init(&temp_name);
427	if (tmpdir == NULL) {
428		if (get_tempdir(&temp_name) != ARCHIVE_OK)
429			goto exit_tmpfile;
430	} else {
431		archive_strcpy(&temp_name, tmpdir);
432		if (temp_name.s[temp_name.length-1] != '/')
433			archive_strappend_char(&temp_name, '/');
434	}
435	archive_strcat(&temp_name, "libarchive_XXXXXX");
436	fd = mkstemp(temp_name.s);
437	if (fd < 0)
438		goto exit_tmpfile;
439	__archive_ensure_cloexec_flag(fd);
440	unlink(temp_name.s);
441exit_tmpfile:
442	archive_string_free(&temp_name);
443	return (fd);
444}
445
446int
447__archive_mkstemp(char *template)
448{
449	int fd = -1;
450	fd = mkstemp(template);
451	if (fd >= 0)
452		__archive_ensure_cloexec_flag(fd);
453	return (fd);
454}
455
456#else /* !HAVE_MKSTEMP */
457
458/*
459 * We use a private routine.
460 */
461
462static int
463__archive_mktempx(const char *tmpdir, char *template)
464{
465        static const char num[] = {
466		'0', '1', '2', '3', '4', '5', '6', '7',
467		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
468		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
469		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
470		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
471		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
472		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
473		'u', 'v', 'w', 'x', 'y', 'z'
474        };
475	struct archive_string temp_name;
476	struct stat st;
477	int fd;
478	char *tp, *ep;
479
480	fd = -1;
481	if (template == NULL) {
482		archive_string_init(&temp_name);
483		if (tmpdir == NULL) {
484			if (get_tempdir(&temp_name) != ARCHIVE_OK)
485				goto exit_tmpfile;
486		} else
487			archive_strcpy(&temp_name, tmpdir);
488		if (temp_name.s[temp_name.length-1] == '/') {
489			temp_name.s[temp_name.length-1] = '\0';
490			temp_name.length --;
491		}
492		if (la_stat(temp_name.s, &st) < 0)
493			goto exit_tmpfile;
494		if (!S_ISDIR(st.st_mode)) {
495			errno = ENOTDIR;
496			goto exit_tmpfile;
497		}
498		archive_strcat(&temp_name, "/libarchive_");
499		tp = temp_name.s + archive_strlen(&temp_name);
500		archive_strcat(&temp_name, "XXXXXXXXXX");
501		ep = temp_name.s + archive_strlen(&temp_name);
502		template = temp_name.s;
503	} else {
504		tp = strchr(template, 'X');
505		if (tp == NULL)	/* No X, programming error */
506			abort();
507		for (ep = tp; *ep == 'X'; ep++)
508			continue;
509		if (*ep)	/* X followed by non X, programming error */
510			abort();
511	}
512
513	do {
514		char *p;
515
516		p = tp;
517		archive_random(p, ep - p);
518		while (p < ep) {
519			int d = *((unsigned char *)p) % sizeof(num);
520			*p++ = num[d];
521		}
522		fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
523			  0600);
524	} while (fd < 0 && errno == EEXIST);
525	if (fd < 0)
526		goto exit_tmpfile;
527	__archive_ensure_cloexec_flag(fd);
528	if (template == temp_name.s)
529		unlink(temp_name.s);
530exit_tmpfile:
531	if (template == temp_name.s)
532		archive_string_free(&temp_name);
533	return (fd);
534}
535
536int
537__archive_mktemp(const char *tmpdir)
538{
539	return __archive_mktempx(tmpdir, NULL);
540}
541
542int
543__archive_mkstemp(char *template)
544{
545	return __archive_mktempx(NULL, template);
546}
547
548#endif /* !HAVE_MKSTEMP */
549#endif /* !_WIN32 || __CYGWIN__ */
550
551/*
552 * Set FD_CLOEXEC flag to a file descriptor if it is not set.
553 * We have to set the flag if the platform does not provide O_CLOEXEC
554 * or F_DUPFD_CLOEXEC flags.
555 *
556 * Note: This function is absolutely called after creating a new file
557 * descriptor even if the platform seemingly provides O_CLOEXEC or
558 * F_DUPFD_CLOEXEC macros because it is possible that the platform
559 * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
560 */
561void
562__archive_ensure_cloexec_flag(int fd)
563{
564#if defined(_WIN32) && !defined(__CYGWIN__)
565	(void)fd; /* UNUSED */
566#else
567	int flags;
568
569	if (fd >= 0) {
570		flags = fcntl(fd, F_GETFD);
571		if (flags != -1 && (flags & FD_CLOEXEC) == 0)
572			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
573	}
574#endif
575}
576
577/*
578 * Utility function to sort a group of strings using quicksort.
579 */
580static int
581archive_utility_string_sort_helper(char **strings, unsigned int n)
582{
583	unsigned int i, lesser_count, greater_count;
584	char **lesser, **greater, **tmp, *pivot;
585	int retval1, retval2;
586
587	/* A list of 0 or 1 elements is already sorted */
588	if (n <= 1)
589		return (ARCHIVE_OK);
590
591	lesser_count = greater_count = 0;
592	lesser = greater = NULL;
593	pivot = strings[0];
594	for (i = 1; i < n; i++)
595	{
596		if (strcmp(strings[i], pivot) < 0)
597		{
598			lesser_count++;
599			tmp = (char **)realloc(lesser,
600				lesser_count * sizeof(char *));
601			if (!tmp) {
602				free(greater);
603				free(lesser);
604				return (ARCHIVE_FATAL);
605			}
606			lesser = tmp;
607			lesser[lesser_count - 1] = strings[i];
608		}
609		else
610		{
611			greater_count++;
612			tmp = (char **)realloc(greater,
613				greater_count * sizeof(char *));
614			if (!tmp) {
615				free(greater);
616				free(lesser);
617				return (ARCHIVE_FATAL);
618			}
619			greater = tmp;
620			greater[greater_count - 1] = strings[i];
621		}
622	}
623
624	/* quicksort(lesser) */
625	retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
626	for (i = 0; i < lesser_count; i++)
627		strings[i] = lesser[i];
628	free(lesser);
629
630	/* pivot */
631	strings[lesser_count] = pivot;
632
633	/* quicksort(greater) */
634	retval2 = archive_utility_string_sort_helper(greater, greater_count);
635	for (i = 0; i < greater_count; i++)
636		strings[lesser_count + 1 + i] = greater[i];
637	free(greater);
638
639	return (retval1 < retval2) ? retval1 : retval2;
640}
641
642int
643archive_utility_string_sort(char **strings)
644{
645	  unsigned int size = 0;
646	  while (strings[size] != NULL)
647		size++;
648	  return archive_utility_string_sort_helper(strings, size);
649}
650