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