]> git.ipfire.org Git - thirdparty/libarchive.git/blob - libarchive/archive_util.c
Use CreateFile2 instead of CreateFileW on Win8+ builds
[thirdparty/libarchive.git] / libarchive / archive_util.c
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: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
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
70 static int archive_utility_string_sort_helper(char **, unsigned int);
71
72 /* Generic initialization of 'struct archive' objects. */
73 int
74 __archive_clean(struct archive *a)
75 {
76 archive_string_conversion_free(a);
77 return (ARCHIVE_OK);
78 }
79
80 int
81 archive_version_number(void)
82 {
83 return (ARCHIVE_VERSION_NUMBER);
84 }
85
86 const char *
87 archive_version_string(void)
88 {
89 return (ARCHIVE_VERSION_STRING);
90 }
91
92 int
93 archive_errno(struct archive *a)
94 {
95 return (a->archive_error_number);
96 }
97
98 const char *
99 archive_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
108 int
109 archive_file_count(struct archive *a)
110 {
111 return (a->file_count);
112 }
113
114 int
115 archive_format(struct archive *a)
116 {
117 return (a->archive_format);
118 }
119
120 const char *
121 archive_format_name(struct archive *a)
122 {
123 return (a->archive_format_name);
124 }
125
126
127 int
128 archive_compression(struct archive *a)
129 {
130 return archive_filter_code(a, 0);
131 }
132
133 const char *
134 archive_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 */
143 la_int64_t
144 archive_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 */
152 la_int64_t
153 archive_position_uncompressed(struct archive *a)
154 {
155 return archive_filter_bytes(a, 0);
156 }
157
158 void
159 archive_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
166 void
167 archive_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
184 void
185 archive_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
193 void
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 */
221 static 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 # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
327 CREATEFILE2_EXTENDED_PARAMETERS createExParams;
328 #endif
329
330 /* Generate a random file name through CryptGenRandom(). */
331 p = xp;
332 if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
333 (BYTE*)p)) {
334 la_dosmaperr(GetLastError());
335 goto exit_tmpfile;
336 }
337 for (; p < ep; p++)
338 *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
339
340 free(ws);
341 ws = __la_win_permissive_name_w(template);
342 if (ws == NULL) {
343 errno = EINVAL;
344 goto exit_tmpfile;
345 }
346 if (template == temp_name.s) {
347 attr = FILE_ATTRIBUTE_TEMPORARY |
348 FILE_FLAG_DELETE_ON_CLOSE;
349 } else {
350 /* mkstemp */
351 attr = FILE_ATTRIBUTE_NORMAL;
352 }
353 # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
354 ZeroMemory(&createExParams, sizeof(createExParams));
355 createExParams.dwSize = sizeof(createExParams);
356 createExParams.dwFileAttributes = attr & 0xFFFF;
357 createExParams.dwFileFlags = attr & 0xFFF00000;
358 h = CreateFile2(ws,
359 GENERIC_READ | GENERIC_WRITE | DELETE,
360 0,/* Not share */
361 CREATE_NEW,
362 &createExParams);
363 #else
364 h = CreateFileW(ws,
365 GENERIC_READ | GENERIC_WRITE | DELETE,
366 0,/* Not share */
367 NULL,
368 CREATE_NEW,/* Create a new file only */
369 attr,
370 NULL);
371 #endif
372 if (h == INVALID_HANDLE_VALUE) {
373 /* The same file already exists. retry with
374 * a new filename. */
375 if (GetLastError() == ERROR_FILE_EXISTS)
376 continue;
377 /* Otherwise, fail creation temporary file. */
378 la_dosmaperr(GetLastError());
379 goto exit_tmpfile;
380 }
381 fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
382 if (fd == -1) {
383 la_dosmaperr(GetLastError());
384 CloseHandle(h);
385 goto exit_tmpfile;
386 } else
387 break;/* success! */
388 }
389 exit_tmpfile:
390 if (hProv != (HCRYPTPROV)NULL)
391 CryptReleaseContext(hProv, 0);
392 free(ws);
393 if (template == temp_name.s)
394 archive_wstring_free(&temp_name);
395 return (fd);
396 }
397
398 int
399 __archive_mktemp(const char *tmpdir)
400 {
401 return __archive_mktempx(tmpdir, NULL);
402 }
403
404 int
405 __archive_mkstemp(wchar_t *template)
406 {
407 return __archive_mktempx(NULL, template);
408 }
409
410 #else
411
412 static int
413 get_tempdir(struct archive_string *temppath)
414 {
415 const char *tmp;
416
417 tmp = getenv("TMPDIR");
418 if (tmp == NULL)
419 #ifdef _PATH_TMP
420 tmp = _PATH_TMP;
421 #else
422 tmp = "/tmp";
423 #endif
424 archive_strcpy(temppath, tmp);
425 if (temppath->s[temppath->length-1] != '/')
426 archive_strappend_char(temppath, '/');
427 return (ARCHIVE_OK);
428 }
429
430 #if defined(HAVE_MKSTEMP)
431
432 /*
433 * We can use mkstemp().
434 */
435
436 int
437 __archive_mktemp(const char *tmpdir)
438 {
439 struct archive_string temp_name;
440 int fd = -1;
441
442 archive_string_init(&temp_name);
443 if (tmpdir == NULL) {
444 if (get_tempdir(&temp_name) != ARCHIVE_OK)
445 goto exit_tmpfile;
446 } else {
447 archive_strcpy(&temp_name, tmpdir);
448 if (temp_name.s[temp_name.length-1] != '/')
449 archive_strappend_char(&temp_name, '/');
450 }
451 #ifdef O_TMPFILE
452 fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600);
453 if(fd >= 0)
454 goto exit_tmpfile;
455 #endif
456 archive_strcat(&temp_name, "libarchive_XXXXXX");
457 fd = mkstemp(temp_name.s);
458 if (fd < 0)
459 goto exit_tmpfile;
460 __archive_ensure_cloexec_flag(fd);
461 unlink(temp_name.s);
462 exit_tmpfile:
463 archive_string_free(&temp_name);
464 return (fd);
465 }
466
467 int
468 __archive_mkstemp(char *template)
469 {
470 int fd = -1;
471 fd = mkstemp(template);
472 if (fd >= 0)
473 __archive_ensure_cloexec_flag(fd);
474 return (fd);
475 }
476
477 #else /* !HAVE_MKSTEMP */
478
479 /*
480 * We use a private routine.
481 */
482
483 static int
484 __archive_mktempx(const char *tmpdir, char *template)
485 {
486 static const char num[] = {
487 '0', '1', '2', '3', '4', '5', '6', '7',
488 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
489 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
490 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
491 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
492 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
493 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
494 'u', 'v', 'w', 'x', 'y', 'z'
495 };
496 struct archive_string temp_name;
497 struct stat st;
498 int fd;
499 char *tp, *ep;
500
501 fd = -1;
502 if (template == NULL) {
503 archive_string_init(&temp_name);
504 if (tmpdir == NULL) {
505 if (get_tempdir(&temp_name) != ARCHIVE_OK)
506 goto exit_tmpfile;
507 } else
508 archive_strcpy(&temp_name, tmpdir);
509 if (temp_name.s[temp_name.length-1] == '/') {
510 temp_name.s[temp_name.length-1] = '\0';
511 temp_name.length --;
512 }
513 if (la_stat(temp_name.s, &st) < 0)
514 goto exit_tmpfile;
515 if (!S_ISDIR(st.st_mode)) {
516 errno = ENOTDIR;
517 goto exit_tmpfile;
518 }
519 archive_strcat(&temp_name, "/libarchive_");
520 tp = temp_name.s + archive_strlen(&temp_name);
521 archive_strcat(&temp_name, "XXXXXXXXXX");
522 ep = temp_name.s + archive_strlen(&temp_name);
523 template = temp_name.s;
524 } else {
525 tp = strchr(template, 'X');
526 if (tp == NULL) /* No X, programming error */
527 abort();
528 for (ep = tp; *ep == 'X'; ep++)
529 continue;
530 if (*ep) /* X followed by non X, programming error */
531 abort();
532 }
533
534 do {
535 char *p;
536
537 p = tp;
538 archive_random(p, ep - p);
539 while (p < ep) {
540 int d = *((unsigned char *)p) % sizeof(num);
541 *p++ = num[d];
542 }
543 fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
544 0600);
545 } while (fd < 0 && errno == EEXIST);
546 if (fd < 0)
547 goto exit_tmpfile;
548 __archive_ensure_cloexec_flag(fd);
549 if (template == temp_name.s)
550 unlink(temp_name.s);
551 exit_tmpfile:
552 if (template == temp_name.s)
553 archive_string_free(&temp_name);
554 return (fd);
555 }
556
557 int
558 __archive_mktemp(const char *tmpdir)
559 {
560 return __archive_mktempx(tmpdir, NULL);
561 }
562
563 int
564 __archive_mkstemp(char *template)
565 {
566 return __archive_mktempx(NULL, template);
567 }
568
569 #endif /* !HAVE_MKSTEMP */
570 #endif /* !_WIN32 || __CYGWIN__ */
571
572 /*
573 * Set FD_CLOEXEC flag to a file descriptor if it is not set.
574 * We have to set the flag if the platform does not provide O_CLOEXEC
575 * or F_DUPFD_CLOEXEC flags.
576 *
577 * Note: This function is absolutely called after creating a new file
578 * descriptor even if the platform seemingly provides O_CLOEXEC or
579 * F_DUPFD_CLOEXEC macros because it is possible that the platform
580 * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
581 */
582 void
583 __archive_ensure_cloexec_flag(int fd)
584 {
585 #if defined(_WIN32) && !defined(__CYGWIN__)
586 (void)fd; /* UNUSED */
587 #else
588 int flags;
589
590 if (fd >= 0) {
591 flags = fcntl(fd, F_GETFD);
592 if (flags != -1 && (flags & FD_CLOEXEC) == 0)
593 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
594 }
595 #endif
596 }
597
598 /*
599 * Utility function to sort a group of strings using quicksort.
600 */
601 static int
602 archive_utility_string_sort_helper(char **strings, unsigned int n)
603 {
604 unsigned int i, lesser_count, greater_count;
605 char **lesser, **greater, **tmp, *pivot;
606 int retval1, retval2;
607
608 /* A list of 0 or 1 elements is already sorted */
609 if (n <= 1)
610 return (ARCHIVE_OK);
611
612 lesser_count = greater_count = 0;
613 lesser = greater = NULL;
614 pivot = strings[0];
615 for (i = 1; i < n; i++)
616 {
617 if (strcmp(strings[i], pivot) < 0)
618 {
619 lesser_count++;
620 tmp = (char **)realloc(lesser,
621 lesser_count * sizeof(char *));
622 if (!tmp) {
623 free(greater);
624 free(lesser);
625 return (ARCHIVE_FATAL);
626 }
627 lesser = tmp;
628 lesser[lesser_count - 1] = strings[i];
629 }
630 else
631 {
632 greater_count++;
633 tmp = (char **)realloc(greater,
634 greater_count * sizeof(char *));
635 if (!tmp) {
636 free(greater);
637 free(lesser);
638 return (ARCHIVE_FATAL);
639 }
640 greater = tmp;
641 greater[greater_count - 1] = strings[i];
642 }
643 }
644
645 /* quicksort(lesser) */
646 retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
647 for (i = 0; i < lesser_count; i++)
648 strings[i] = lesser[i];
649 free(lesser);
650
651 /* pivot */
652 strings[lesser_count] = pivot;
653
654 /* quicksort(greater) */
655 retval2 = archive_utility_string_sort_helper(greater, greater_count);
656 for (i = 0; i < greater_count; i++)
657 strings[lesser_count + 1 + i] = greater[i];
658 free(greater);
659
660 return (retval1 < retval2) ? retval1 : retval2;
661 }
662
663 int
664 archive_utility_string_sort(char **strings)
665 {
666 unsigned int size = 0;
667 while (strings[size] != NULL)
668 size++;
669 return archive_utility_string_sort_helper(strings, size);
670 }