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