xref: /freebsd/contrib/libarchive/libarchive/archive_util.c (revision 38f0b757fd84d17d0fc24739a7cda160c4516d81)
1 /*-
2  * Copyright (c) 2009-2012 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$");
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 
49 #include "archive.h"
50 #include "archive_private.h"
51 #include "archive_string.h"
52 
53 #ifndef O_CLOEXEC
54 #define O_CLOEXEC	0
55 #endif
56 
57 /* Generic initialization of 'struct archive' objects. */
58 int
59 __archive_clean(struct archive *a)
60 {
61 	archive_string_conversion_free(a);
62 	return (ARCHIVE_OK);
63 }
64 
65 int
66 archive_version_number(void)
67 {
68 	return (ARCHIVE_VERSION_NUMBER);
69 }
70 
71 const char *
72 archive_version_string(void)
73 {
74 	return (ARCHIVE_VERSION_STRING);
75 }
76 
77 int
78 archive_errno(struct archive *a)
79 {
80 	return (a->archive_error_number);
81 }
82 
83 const char *
84 archive_error_string(struct archive *a)
85 {
86 
87 	if (a->error != NULL  &&  *a->error != '\0')
88 		return (a->error);
89 	else
90 		return (NULL);
91 }
92 
93 int
94 archive_file_count(struct archive *a)
95 {
96 	return (a->file_count);
97 }
98 
99 int
100 archive_format(struct archive *a)
101 {
102 	return (a->archive_format);
103 }
104 
105 const char *
106 archive_format_name(struct archive *a)
107 {
108 	return (a->archive_format_name);
109 }
110 
111 
112 int
113 archive_compression(struct archive *a)
114 {
115 	return archive_filter_code(a, 0);
116 }
117 
118 const char *
119 archive_compression_name(struct archive *a)
120 {
121 	return archive_filter_name(a, 0);
122 }
123 
124 
125 /*
126  * Return a count of the number of compressed bytes processed.
127  */
128 int64_t
129 archive_position_compressed(struct archive *a)
130 {
131 	return archive_filter_bytes(a, -1);
132 }
133 
134 /*
135  * Return a count of the number of uncompressed bytes processed.
136  */
137 int64_t
138 archive_position_uncompressed(struct archive *a)
139 {
140 	return archive_filter_bytes(a, 0);
141 }
142 
143 void
144 archive_clear_error(struct archive *a)
145 {
146 	archive_string_empty(&a->error_string);
147 	a->error = NULL;
148 	a->archive_error_number = 0;
149 }
150 
151 void
152 archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
153 {
154 	va_list ap;
155 
156 	a->archive_error_number = error_number;
157 	if (fmt == NULL) {
158 		a->error = NULL;
159 		return;
160 	}
161 
162 	archive_string_empty(&(a->error_string));
163 	va_start(ap, fmt);
164 	archive_string_vsprintf(&(a->error_string), fmt, ap);
165 	va_end(ap);
166 	a->error = a->error_string.s;
167 }
168 
169 void
170 archive_copy_error(struct archive *dest, struct archive *src)
171 {
172 	dest->archive_error_number = src->archive_error_number;
173 
174 	archive_string_copy(&dest->error_string, &src->error_string);
175 	dest->error = dest->error_string.s;
176 }
177 
178 void
179 __archive_errx(int retvalue, const char *msg)
180 {
181 	static const char *msg1 = "Fatal Internal Error in libarchive: ";
182 	size_t s;
183 
184 	s = write(2, msg1, strlen(msg1));
185 	(void)s; /* UNUSED */
186 	s = write(2, msg, strlen(msg));
187 	(void)s; /* UNUSED */
188 	s = write(2, "\n", 1);
189 	(void)s; /* UNUSED */
190 	exit(retvalue);
191 }
192 
193 /*
194  * Create a temporary file
195  */
196 #if defined(_WIN32) && !defined(__CYGWIN__)
197 
198 /*
199  * Do not use Windows tmpfile() function.
200  * It will make a temporary file under the root directory
201  * and it'll cause permission error if a user who is
202  * non-Administrator creates temporary files.
203  * Also Windows version of mktemp family including _mktemp_s
204  * are not secure.
205  */
206 int
207 __archive_mktemp(const char *tmpdir)
208 {
209 	static const wchar_t num[] = {
210 		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
211 		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
212 		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
213 		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
214 		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
215 		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
216 		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
217 		L'u', L'v', L'w', L'x', L'y', L'z'
218 	};
219 	HCRYPTPROV hProv;
220 	struct archive_wstring temp_name;
221 	wchar_t *ws;
222 	DWORD attr;
223 	wchar_t *xp, *ep;
224 	int fd;
225 
226 	hProv = (HCRYPTPROV)NULL;
227 	fd = -1;
228 	ws = NULL;
229 	archive_string_init(&temp_name);
230 
231 	/* Get a temporary directory. */
232 	if (tmpdir == NULL) {
233 		size_t l;
234 		wchar_t *tmp;
235 
236 		l = GetTempPathW(0, NULL);
237 		if (l == 0) {
238 			la_dosmaperr(GetLastError());
239 			goto exit_tmpfile;
240 		}
241 		tmp = malloc(l*sizeof(wchar_t));
242 		if (tmp == NULL) {
243 			errno = ENOMEM;
244 			goto exit_tmpfile;
245 		}
246 		GetTempPathW((DWORD)l, tmp);
247 		archive_wstrcpy(&temp_name, tmp);
248 		free(tmp);
249 	} else {
250 		if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
251 		    strlen(tmpdir)) < 0)
252 			goto exit_tmpfile;
253 		if (temp_name.s[temp_name.length-1] != L'/')
254 			archive_wstrappend_wchar(&temp_name, L'/');
255 	}
256 
257 	/* Check if temp_name is a directory. */
258 	attr = GetFileAttributesW(temp_name.s);
259 	if (attr == (DWORD)-1) {
260 		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
261 			la_dosmaperr(GetLastError());
262 			goto exit_tmpfile;
263 		}
264 		ws = __la_win_permissive_name_w(temp_name.s);
265 		if (ws == NULL) {
266 			errno = EINVAL;
267 			goto exit_tmpfile;
268 		}
269 		attr = GetFileAttributesW(ws);
270 		if (attr == (DWORD)-1) {
271 			la_dosmaperr(GetLastError());
272 			goto exit_tmpfile;
273 		}
274 	}
275 	if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
276 		errno = ENOTDIR;
277 		goto exit_tmpfile;
278 	}
279 
280 	/*
281 	 * Create a temporary file.
282 	 */
283 	archive_wstrcat(&temp_name, L"libarchive_");
284 	xp = temp_name.s + archive_strlen(&temp_name);
285 	archive_wstrcat(&temp_name, L"XXXXXXXXXX");
286 	ep = temp_name.s + archive_strlen(&temp_name);
287 
288 	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
289 		CRYPT_VERIFYCONTEXT)) {
290 		la_dosmaperr(GetLastError());
291 		goto exit_tmpfile;
292 	}
293 
294 	for (;;) {
295 		wchar_t *p;
296 		HANDLE h;
297 
298 		/* Generate a random file name through CryptGenRandom(). */
299 		p = xp;
300 		if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
301 		    (BYTE*)p)) {
302 			la_dosmaperr(GetLastError());
303 			goto exit_tmpfile;
304 		}
305 		for (; p < ep; p++)
306 			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
307 
308 		free(ws);
309 		ws = __la_win_permissive_name_w(temp_name.s);
310 		if (ws == NULL) {
311 			errno = EINVAL;
312 			goto exit_tmpfile;
313 		}
314 		/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
315 		 * delete this temporary file immediately when this
316 		 * file closed. */
317 		h = CreateFileW(ws,
318 		    GENERIC_READ | GENERIC_WRITE | DELETE,
319 		    0,/* Not share */
320 		    NULL,
321 		    CREATE_NEW,/* Create a new file only */
322 		    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
323 		    NULL);
324 		if (h == INVALID_HANDLE_VALUE) {
325 			/* The same file already exists. retry with
326 			 * a new filename. */
327 			if (GetLastError() == ERROR_FILE_EXISTS)
328 				continue;
329 			/* Otherwise, fail creation temporary file. */
330 			la_dosmaperr(GetLastError());
331 			goto exit_tmpfile;
332 		}
333 		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
334 		if (fd == -1) {
335 			CloseHandle(h);
336 			goto exit_tmpfile;
337 		} else
338 			break;/* success! */
339 	}
340 exit_tmpfile:
341 	if (hProv != (HCRYPTPROV)NULL)
342 		CryptReleaseContext(hProv, 0);
343 	free(ws);
344 	archive_wstring_free(&temp_name);
345 	return (fd);
346 }
347 
348 #else
349 
350 static int
351 get_tempdir(struct archive_string *temppath)
352 {
353 	const char *tmp;
354 
355 	tmp = getenv("TMPDIR");
356 	if (tmp == NULL)
357 #ifdef _PATH_TMP
358 		tmp = _PATH_TMP;
359 #else
360                 tmp = "/tmp";
361 #endif
362 	archive_strcpy(temppath, tmp);
363 	if (temppath->s[temppath->length-1] != '/')
364 		archive_strappend_char(temppath, '/');
365 	return (ARCHIVE_OK);
366 }
367 
368 #if defined(HAVE_MKSTEMP)
369 
370 /*
371  * We can use mkstemp().
372  */
373 
374 int
375 __archive_mktemp(const char *tmpdir)
376 {
377 	struct archive_string temp_name;
378 	int fd = -1;
379 
380 	archive_string_init(&temp_name);
381 	if (tmpdir == NULL) {
382 		if (get_tempdir(&temp_name) != ARCHIVE_OK)
383 			goto exit_tmpfile;
384 	} else {
385 		archive_strcpy(&temp_name, tmpdir);
386 		if (temp_name.s[temp_name.length-1] != '/')
387 			archive_strappend_char(&temp_name, '/');
388 	}
389 	archive_strcat(&temp_name, "libarchive_XXXXXX");
390 	fd = mkstemp(temp_name.s);
391 	if (fd < 0)
392 		goto exit_tmpfile;
393 	__archive_ensure_cloexec_flag(fd);
394 	unlink(temp_name.s);
395 exit_tmpfile:
396 	archive_string_free(&temp_name);
397 	return (fd);
398 }
399 
400 #else
401 
402 /*
403  * We use a private routine.
404  */
405 
406 int
407 __archive_mktemp(const char *tmpdir)
408 {
409         static const char num[] = {
410 		'0', '1', '2', '3', '4', '5', '6', '7',
411 		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
412 		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
413 		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
414 		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
415 		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
416 		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
417 		'u', 'v', 'w', 'x', 'y', 'z'
418         };
419 	struct archive_string temp_name;
420 	struct stat st;
421 	int fd;
422 	char *tp, *ep;
423 	unsigned seed;
424 
425 	fd = -1;
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 		temp_name.s[temp_name.length-1] = '\0';
434 		temp_name.length --;
435 	}
436 	if (stat(temp_name.s, &st) < 0)
437 		goto exit_tmpfile;
438 	if (!S_ISDIR(st.st_mode)) {
439 		errno = ENOTDIR;
440 		goto exit_tmpfile;
441 	}
442 	archive_strcat(&temp_name, "/libarchive_");
443 	tp = temp_name.s + archive_strlen(&temp_name);
444 	archive_strcat(&temp_name, "XXXXXXXXXX");
445 	ep = temp_name.s + archive_strlen(&temp_name);
446 
447 	fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
448 	__archive_ensure_cloexec_flag(fd);
449 	if (fd < 0)
450 		seed = time(NULL);
451 	else {
452 		if (read(fd, &seed, sizeof(seed)) < 0)
453 			seed = time(NULL);
454 		close(fd);
455 	}
456 	do {
457 		char *p;
458 
459 		p = tp;
460 		while (p < ep)
461 			*p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
462 		fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
463 			  0600);
464 	} while (fd < 0 && errno == EEXIST);
465 	if (fd < 0)
466 		goto exit_tmpfile;
467 	__archive_ensure_cloexec_flag(fd);
468 	unlink(temp_name.s);
469 exit_tmpfile:
470 	archive_string_free(&temp_name);
471 	return (fd);
472 }
473 
474 #endif /* HAVE_MKSTEMP */
475 #endif /* !_WIN32 || __CYGWIN__ */
476 
477 /*
478  * Set FD_CLOEXEC flag to a file descriptor if it is not set.
479  * We have to set the flag if the platform does not provide O_CLOEXEC
480  * or F_DUPFD_CLOEXEC flags.
481  *
482  * Note: This function is absolutely called after creating a new file
483  * descriptor even if the platform seemingly provides O_CLOEXEC or
484  * F_DUPFD_CLOEXEC macros because it is possible that the platform
485  * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
486  */
487 void
488 __archive_ensure_cloexec_flag(int fd)
489 {
490 #if defined(_WIN32) && !defined(__CYGWIN__)
491 	(void)fd; /* UNSED */
492 #else
493 	int flags;
494 
495 	if (fd >= 0) {
496 		flags = fcntl(fd, F_GETFD);
497 		if (flags != -1 && (flags & FD_CLOEXEC) == 0)
498 			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
499 	}
500 #endif
501 }
502