archive_util.c revision 232153
1228753Smm/*-
2232153Smm * Copyright (c) 2009,2010 Michihiro NAKAJIMA
3228753Smm * Copyright (c) 2003-2007 Tim Kientzle
4228753Smm * All rights reserved.
5228753Smm *
6228753Smm * Redistribution and use in source and binary forms, with or without
7228753Smm * modification, are permitted provided that the following conditions
8228753Smm * are met:
9228753Smm * 1. Redistributions of source code must retain the above copyright
10228753Smm *    notice, this list of conditions and the following disclaimer.
11228753Smm * 2. Redistributions in binary form must reproduce the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer in the
13228753Smm *    documentation and/or other materials provided with the distribution.
14228753Smm *
15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25228753Smm */
26228753Smm
27228753Smm#include "archive_platform.h"
28228763Smm__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_util.c 232153 2012-02-25 10:58:02Z mm $");
29228753Smm
30228753Smm#ifdef HAVE_SYS_TYPES_H
31228753Smm#include <sys/types.h>
32228753Smm#endif
33232153Smm#ifdef HAVE_ERRNO_H
34232153Smm#include <errno.h>
35232153Smm#endif
36232153Smm#ifdef HAVE_FCNTL_H
37232153Smm#include <fcntl.h>
38232153Smm#endif
39228753Smm#ifdef HAVE_STDLIB_H
40228753Smm#include <stdlib.h>
41228753Smm#endif
42228753Smm#ifdef HAVE_STRING_H
43228753Smm#include <string.h>
44228753Smm#endif
45232153Smm#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
46232153Smm#include <wincrypt.h>
47232153Smm#endif
48228753Smm
49228753Smm#include "archive.h"
50228753Smm#include "archive_private.h"
51228753Smm#include "archive_string.h"
52228753Smm
53232153Smm/* Generic initialization of 'struct archive' objects. */
54228753Smmint
55232153Smm__archive_clean(struct archive *a)
56228753Smm{
57232153Smm	archive_string_conversion_free(a);
58232153Smm	return (ARCHIVE_OK);
59228753Smm}
60228753Smm
61228753Smmint
62228753Smmarchive_version_number(void)
63228753Smm{
64228753Smm	return (ARCHIVE_VERSION_NUMBER);
65228753Smm}
66228753Smm
67228753Smmconst char *
68228753Smmarchive_version_string(void)
69228753Smm{
70228753Smm	return (ARCHIVE_VERSION_STRING);
71228753Smm}
72228753Smm
73228753Smmint
74228753Smmarchive_errno(struct archive *a)
75228753Smm{
76228753Smm	return (a->archive_error_number);
77228753Smm}
78228753Smm
79228753Smmconst char *
80228753Smmarchive_error_string(struct archive *a)
81228753Smm{
82228753Smm
83228753Smm	if (a->error != NULL  &&  *a->error != '\0')
84228753Smm		return (a->error);
85228753Smm	else
86232153Smm		return (NULL);
87228753Smm}
88228753Smm
89228753Smmint
90228753Smmarchive_file_count(struct archive *a)
91228753Smm{
92228753Smm	return (a->file_count);
93228753Smm}
94228753Smm
95228753Smmint
96228753Smmarchive_format(struct archive *a)
97228753Smm{
98228753Smm	return (a->archive_format);
99228753Smm}
100228753Smm
101228753Smmconst char *
102228753Smmarchive_format_name(struct archive *a)
103228753Smm{
104228753Smm	return (a->archive_format_name);
105228753Smm}
106228753Smm
107228753Smm
108228753Smmint
109228753Smmarchive_compression(struct archive *a)
110228753Smm{
111232153Smm	return archive_filter_code(a, 0);
112228753Smm}
113228753Smm
114228753Smmconst char *
115228753Smmarchive_compression_name(struct archive *a)
116228753Smm{
117232153Smm	return archive_filter_name(a, 0);
118228753Smm}
119228753Smm
120228753Smm
121228753Smm/*
122228753Smm * Return a count of the number of compressed bytes processed.
123228753Smm */
124228753Smmint64_t
125228753Smmarchive_position_compressed(struct archive *a)
126228753Smm{
127232153Smm	return archive_filter_bytes(a, -1);
128228753Smm}
129228753Smm
130228753Smm/*
131228753Smm * Return a count of the number of uncompressed bytes processed.
132228753Smm */
133228753Smmint64_t
134228753Smmarchive_position_uncompressed(struct archive *a)
135228753Smm{
136232153Smm	return archive_filter_bytes(a, 0);
137228753Smm}
138228753Smm
139228753Smmvoid
140228753Smmarchive_clear_error(struct archive *a)
141228753Smm{
142228753Smm	archive_string_empty(&a->error_string);
143228753Smm	a->error = NULL;
144228753Smm	a->archive_error_number = 0;
145228753Smm}
146228753Smm
147228753Smmvoid
148228753Smmarchive_set_error(struct archive *a, int error_number, const char *fmt, ...)
149228753Smm{
150228753Smm	va_list ap;
151228753Smm
152228753Smm	a->archive_error_number = error_number;
153228753Smm	if (fmt == NULL) {
154228753Smm		a->error = NULL;
155228753Smm		return;
156228753Smm	}
157228753Smm
158232153Smm	archive_string_empty(&(a->error_string));
159228753Smm	va_start(ap, fmt);
160228753Smm	archive_string_vsprintf(&(a->error_string), fmt, ap);
161228753Smm	va_end(ap);
162228753Smm	a->error = a->error_string.s;
163228753Smm}
164228753Smm
165228753Smmvoid
166228753Smmarchive_copy_error(struct archive *dest, struct archive *src)
167228753Smm{
168228753Smm	dest->archive_error_number = src->archive_error_number;
169228753Smm
170228753Smm	archive_string_copy(&dest->error_string, &src->error_string);
171228753Smm	dest->error = dest->error_string.s;
172228753Smm}
173228753Smm
174228753Smmvoid
175228753Smm__archive_errx(int retvalue, const char *msg)
176228753Smm{
177228753Smm	static const char *msg1 = "Fatal Internal Error in libarchive: ";
178228753Smm	size_t s;
179228753Smm
180228753Smm	s = write(2, msg1, strlen(msg1));
181228753Smm	(void)s; /* UNUSED */
182228753Smm	s = write(2, msg, strlen(msg));
183228753Smm	(void)s; /* UNUSED */
184228753Smm	s = write(2, "\n", 1);
185228753Smm	(void)s; /* UNUSED */
186228753Smm	exit(retvalue);
187228753Smm}
188228753Smm
189228753Smm/*
190232153Smm * Create a temporary file
191228753Smm */
192232153Smm#if defined(_WIN32) && !defined(__CYGWIN__)
193232153Smm
194232153Smm/*
195232153Smm * Do not use Windows tmpfile() function.
196232153Smm * It will make a temporary file under the root directory
197232153Smm * and it'll cause permission error if a user who is
198232153Smm * non-Administrator creates temporary files.
199232153Smm * Also Windows version of mktemp family including _mktemp_s
200232153Smm * are not secure.
201232153Smm */
202228753Smmint
203232153Smm__archive_mktemp(const char *tmpdir)
204228753Smm{
205232153Smm	static const wchar_t num[] = {
206232153Smm		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
207232153Smm		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
208232153Smm		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
209232153Smm		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
210232153Smm		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
211232153Smm		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
212232153Smm		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
213232153Smm		L'u', L'v', L'w', L'x', L'y', L'z'
214232153Smm	};
215232153Smm	HCRYPTPROV hProv;
216232153Smm	struct archive_wstring temp_name;
217232153Smm	wchar_t *ws;
218232153Smm	DWORD attr;
219232153Smm	wchar_t *xp, *ep;
220232153Smm	int fd;
221228753Smm
222232153Smm	hProv = (HCRYPTPROV)NULL;
223232153Smm	fd = -1;
224232153Smm	ws = NULL;
225232153Smm	archive_string_init(&temp_name);
226232153Smm
227232153Smm	/* Get a temporary directory. */
228232153Smm	if (tmpdir == NULL) {
229232153Smm		size_t l;
230232153Smm		wchar_t *tmp;
231232153Smm
232232153Smm		l = GetTempPathW(0, NULL);
233232153Smm		if (l == 0) {
234232153Smm			la_dosmaperr(GetLastError());
235232153Smm			goto exit_tmpfile;
236232153Smm		}
237232153Smm		tmp = malloc(l*sizeof(wchar_t));
238232153Smm		if (tmp == NULL) {
239232153Smm			errno = ENOMEM;
240232153Smm			goto exit_tmpfile;
241232153Smm		}
242232153Smm		GetTempPathW(l, tmp);
243232153Smm		archive_wstrcpy(&temp_name, tmp);
244232153Smm		free(tmp);
245232153Smm	} else {
246232153Smm		archive_wstring_append_from_mbs(&temp_name, tmpdir,
247232153Smm		    strlen(tmpdir));
248232153Smm		if (temp_name.s[temp_name.length-1] != L'/')
249232153Smm			archive_wstrappend_wchar(&temp_name, L'/');
250228753Smm	}
251228753Smm
252232153Smm	/* Check if temp_name is a directory. */
253232153Smm	attr = GetFileAttributesW(temp_name.s);
254232153Smm	if (attr == (DWORD)-1) {
255232153Smm		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
256232153Smm			la_dosmaperr(GetLastError());
257232153Smm			goto exit_tmpfile;
258228753Smm		}
259232153Smm		ws = __la_win_permissive_name_w(temp_name.s);
260232153Smm		if (ws == NULL) {
261232153Smm			errno = EINVAL;
262232153Smm			goto exit_tmpfile;
263232153Smm		}
264232153Smm		attr = GetFileAttributesW(ws);
265232153Smm		if (attr == (DWORD)-1) {
266232153Smm			la_dosmaperr(GetLastError());
267232153Smm			goto exit_tmpfile;
268232153Smm		}
269228753Smm	}
270232153Smm	if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
271232153Smm		errno = ENOTDIR;
272232153Smm		goto exit_tmpfile;
273232153Smm	}
274228753Smm
275232153Smm	/*
276232153Smm	 * Create a temporary file.
277232153Smm	 */
278232153Smm	archive_wstrcat(&temp_name, L"libarchive_");
279232153Smm	xp = temp_name.s + archive_strlen(&temp_name);
280232153Smm	archive_wstrcat(&temp_name, L"XXXXXXXXXX");
281232153Smm	ep = temp_name.s + archive_strlen(&temp_name);
282228753Smm
283232153Smm	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
284232153Smm		CRYPT_VERIFYCONTEXT)) {
285232153Smm		la_dosmaperr(GetLastError());
286232153Smm		goto exit_tmpfile;
287232153Smm	}
288232153Smm
289232153Smm	for (;;) {
290232153Smm		wchar_t *p;
291232153Smm		HANDLE h;
292232153Smm
293232153Smm		/* Generate a random file name through CryptGenRandom(). */
294232153Smm		p = xp;
295232153Smm		if (!CryptGenRandom(hProv, (ep - p)*sizeof(wchar_t), (BYTE*)p)) {
296232153Smm			la_dosmaperr(GetLastError());
297232153Smm			goto exit_tmpfile;
298232153Smm		}
299232153Smm		for (; p < ep; p++)
300232153Smm			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
301232153Smm
302232153Smm		free(ws);
303232153Smm		ws = __la_win_permissive_name_w(temp_name.s);
304232153Smm		if (ws == NULL) {
305232153Smm			errno = EINVAL;
306232153Smm			goto exit_tmpfile;
307232153Smm		}
308232153Smm		/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
309232153Smm		 * delete this temporary file immediately when this
310232153Smm		 * file closed. */
311232153Smm		h = CreateFileW(ws,
312232153Smm		    GENERIC_READ | GENERIC_WRITE | DELETE,
313232153Smm		    0,/* Not share */
314232153Smm		    NULL,
315232153Smm		    CREATE_NEW,/* Create a new file only */
316232153Smm		    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
317232153Smm		    NULL);
318232153Smm		if (h == INVALID_HANDLE_VALUE) {
319232153Smm			/* The same file already exists. retry with
320232153Smm			 * a new filename. */
321232153Smm			if (GetLastError() == ERROR_FILE_EXISTS)
322232153Smm				continue;
323232153Smm			/* Otherwise, fail creation temporary file. */
324232153Smm			la_dosmaperr(GetLastError());
325232153Smm			goto exit_tmpfile;
326232153Smm		}
327232153Smm		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
328232153Smm		if (fd == -1) {
329232153Smm			CloseHandle(h);
330232153Smm			goto exit_tmpfile;
331232153Smm		} else
332232153Smm			break;/* success! */
333232153Smm	}
334232153Smmexit_tmpfile:
335232153Smm	if (hProv != (HCRYPTPROV)NULL)
336232153Smm		CryptReleaseContext(hProv, 0);
337232153Smm	free(ws);
338232153Smm	archive_wstring_free(&temp_name);
339232153Smm	return (fd);
340228753Smm}
341232153Smm
342232153Smm#else
343232153Smm
344232153Smmstatic int
345232153Smmget_tempdir(struct archive_string *temppath)
346232153Smm{
347232153Smm	const char *tmp;
348232153Smm
349232153Smm	tmp = getenv("TMPDIR");
350232153Smm	if (tmp == NULL)
351232153Smm#ifdef _PATH_TMP
352232153Smm		tmp = _PATH_TMP;
353232153Smm#else
354232153Smm                tmp = "/tmp";
355232153Smm#endif
356232153Smm	archive_strcpy(temppath, tmp);
357232153Smm	if (temppath->s[temppath->length-1] != '/')
358232153Smm		archive_strappend_char(temppath, '/');
359232153Smm	return (ARCHIVE_OK);
360232153Smm}
361232153Smm
362232153Smm#if defined(HAVE_MKSTEMP)
363232153Smm
364232153Smm/*
365232153Smm * We can use mkstemp().
366232153Smm */
367232153Smm
368232153Smmint
369232153Smm__archive_mktemp(const char *tmpdir)
370232153Smm{
371232153Smm	struct archive_string temp_name;
372232153Smm	int fd = -1;
373232153Smm
374232153Smm	archive_string_init(&temp_name);
375232153Smm	if (tmpdir == NULL) {
376232153Smm		if (get_tempdir(&temp_name) != ARCHIVE_OK)
377232153Smm			goto exit_tmpfile;
378232153Smm	} else {
379232153Smm		archive_strcpy(&temp_name, tmpdir);
380232153Smm		if (temp_name.s[temp_name.length-1] != '/')
381232153Smm			archive_strappend_char(&temp_name, '/');
382232153Smm	}
383232153Smm	archive_strcat(&temp_name, "libarchive_XXXXXX");
384232153Smm	fd = mkstemp(temp_name.s);
385232153Smm	if (fd < 0)
386232153Smm		goto exit_tmpfile;
387232153Smm	unlink(temp_name.s);
388232153Smmexit_tmpfile:
389232153Smm	archive_string_free(&temp_name);
390232153Smm	return (fd);
391232153Smm}
392232153Smm
393232153Smm#else
394232153Smm
395232153Smm/*
396232153Smm * We use a private routine.
397232153Smm */
398232153Smm
399232153Smmint
400232153Smm__archive_mktemp(const char *tmpdir)
401232153Smm{
402232153Smm        static const char num[] = {
403232153Smm		'0', '1', '2', '3', '4', '5', '6', '7',
404232153Smm		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
405232153Smm		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
406232153Smm		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
407232153Smm		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
408232153Smm		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
409232153Smm		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
410232153Smm		'u', 'v', 'w', 'x', 'y', 'z'
411232153Smm        };
412232153Smm	struct archive_string temp_name;
413232153Smm	struct stat st;
414232153Smm	int fd;
415232153Smm	char *tp, *ep;
416232153Smm	unsigned seed;
417232153Smm
418232153Smm	fd = -1;
419232153Smm	archive_string_init(&temp_name);
420232153Smm	if (tmpdir == NULL) {
421232153Smm		if (get_tempdir(&temp_name) != ARCHIVE_OK)
422232153Smm			goto exit_tmpfile;
423232153Smm	} else
424232153Smm		archive_strcpy(&temp_name, tmpdir);
425232153Smm	if (temp_name.s[temp_name.length-1] == '/') {
426232153Smm		temp_name.s[temp_name.length-1] = '\0';
427232153Smm		temp_name.length --;
428232153Smm	}
429232153Smm	if (stat(temp_name.s, &st) < 0)
430232153Smm		goto exit_tmpfile;
431232153Smm	if (!S_ISDIR(st.st_mode)) {
432232153Smm		errno = ENOTDIR;
433232153Smm		goto exit_tmpfile;
434232153Smm	}
435232153Smm	archive_strcat(&temp_name, "/libarchive_");
436232153Smm	tp = temp_name.s + archive_strlen(&temp_name);
437232153Smm	archive_strcat(&temp_name, "XXXXXXXXXX");
438232153Smm	ep = temp_name.s + archive_strlen(&temp_name);
439232153Smm
440232153Smm	fd = open("/dev/random", O_RDONLY);
441232153Smm	if (fd < 0)
442232153Smm		seed = time(NULL);
443232153Smm	else {
444232153Smm		if (read(fd, &seed, sizeof(seed)) < 0)
445232153Smm			seed = time(NULL);
446232153Smm		close(fd);
447232153Smm	}
448232153Smm	do {
449232153Smm		char *p;
450232153Smm
451232153Smm		p = tp;
452232153Smm		while (p < ep)
453232153Smm			*p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
454232153Smm		fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600);
455232153Smm	} while (fd < 0 && errno == EEXIST);
456232153Smm	if (fd < 0)
457232153Smm		goto exit_tmpfile;
458232153Smm	unlink(temp_name.s);
459232153Smmexit_tmpfile:
460232153Smm	archive_string_free(&temp_name);
461232153Smm	return (fd);
462232153Smm}
463232153Smm
464232153Smm#endif /* HAVE_MKSTEMP */
465232153Smm#endif /* !_WIN32 || __CYGWIN__ */
466