Import libarchive-3.0.2.
[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                 archive_wstring_append_from_mbs(&temp_name, tmpdir,
247                     strlen(tmpdir));
248                 if (temp_name.s[temp_name.length-1] != L'/')
249                         archive_wstrappend_wchar(&temp_name, L'/');
250         }
251
252         /* Check if temp_name is a directory. */
253         attr = GetFileAttributesW(temp_name.s);
254         if (attr == (DWORD)-1) {
255                 if (GetLastError() != ERROR_FILE_NOT_FOUND) {
256                         la_dosmaperr(GetLastError());
257                         goto exit_tmpfile;
258                 }
259                 ws = __la_win_permissive_name_w(temp_name.s);
260                 if (ws == NULL) {
261                         errno = EINVAL;
262                         goto exit_tmpfile;
263                 }
264                 attr = GetFileAttributesW(ws);
265                 if (attr == (DWORD)-1) {
266                         la_dosmaperr(GetLastError());
267                         goto exit_tmpfile;
268                 }
269         }
270         if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
271                 errno = ENOTDIR;
272                 goto exit_tmpfile;
273         }
274
275         /*
276          * Create a temporary file.
277          */
278         archive_wstrcat(&temp_name, L"libarchive_");
279         xp = temp_name.s + archive_strlen(&temp_name);
280         archive_wstrcat(&temp_name, L"XXXXXXXXXX");
281         ep = temp_name.s + archive_strlen(&temp_name);
282
283         if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
284                 CRYPT_VERIFYCONTEXT)) {
285                 la_dosmaperr(GetLastError());
286                 goto exit_tmpfile;
287         }
288
289         for (;;) {
290                 wchar_t *p;
291                 HANDLE h;
292
293                 /* Generate a random file name through CryptGenRandom(). */
294                 p = xp;
295                 if (!CryptGenRandom(hProv, (ep - p)*sizeof(wchar_t), (BYTE*)p)) {
296                         la_dosmaperr(GetLastError());
297                         goto exit_tmpfile;
298                 }
299                 for (; p < ep; p++)
300                         *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
301
302                 free(ws);
303                 ws = __la_win_permissive_name_w(temp_name.s);
304                 if (ws == NULL) {
305                         errno = EINVAL;
306                         goto exit_tmpfile;
307                 }
308                 /* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
309                  * delete this temporary file immediately when this
310                  * file closed. */
311                 h = CreateFileW(ws,
312                     GENERIC_READ | GENERIC_WRITE | DELETE,
313                     0,/* Not share */
314                     NULL,
315                     CREATE_NEW,/* Create a new file only */
316                     FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
317                     NULL);
318                 if (h == INVALID_HANDLE_VALUE) {
319                         /* The same file already exists. retry with
320                          * a new filename. */
321                         if (GetLastError() == ERROR_FILE_EXISTS)
322                                 continue;
323                         /* Otherwise, fail creation temporary file. */
324                         la_dosmaperr(GetLastError());
325                         goto exit_tmpfile;
326                 }
327                 fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
328                 if (fd == -1) {
329                         CloseHandle(h);
330                         goto exit_tmpfile;
331                 } else
332                         break;/* success! */
333         }
334 exit_tmpfile:
335         if (hProv != (HCRYPTPROV)NULL)
336                 CryptReleaseContext(hProv, 0);
337         free(ws);
338         archive_wstring_free(&temp_name);
339         return (fd);
340 }
341
342 #else
343
344 static int
345 get_tempdir(struct archive_string *temppath)
346 {
347         const char *tmp;
348
349         tmp = getenv("TMPDIR");
350         if (tmp == NULL)
351 #ifdef _PATH_TMP
352                 tmp = _PATH_TMP;
353 #else
354                 tmp = "/tmp";
355 #endif
356         archive_strcpy(temppath, tmp);
357         if (temppath->s[temppath->length-1] != '/')
358                 archive_strappend_char(temppath, '/');
359         return (ARCHIVE_OK);
360 }
361
362 #if defined(HAVE_MKSTEMP)
363
364 /*
365  * We can use mkstemp().
366  */
367
368 int
369 __archive_mktemp(const char *tmpdir)
370 {
371         struct archive_string temp_name;
372         int fd = -1;
373
374         archive_string_init(&temp_name);
375         if (tmpdir == NULL) {
376                 if (get_tempdir(&temp_name) != ARCHIVE_OK)
377                         goto exit_tmpfile;
378         } else {
379                 archive_strcpy(&temp_name, tmpdir);
380                 if (temp_name.s[temp_name.length-1] != '/')
381                         archive_strappend_char(&temp_name, '/');
382         }
383         archive_strcat(&temp_name, "libarchive_XXXXXX");
384         fd = mkstemp(temp_name.s);
385         if (fd < 0)
386                 goto exit_tmpfile;
387         unlink(temp_name.s);
388 exit_tmpfile:
389         archive_string_free(&temp_name);
390         return (fd);
391 }
392
393 #else
394
395 /*
396  * We use a private routine.
397  */
398
399 int
400 __archive_mktemp(const char *tmpdir)
401 {
402         static const char num[] = {
403                 '0', '1', '2', '3', '4', '5', '6', '7',
404                 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
405                 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
406                 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
407                 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
408                 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
409                 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
410                 'u', 'v', 'w', 'x', 'y', 'z'
411         };
412         struct archive_string temp_name;
413         struct stat st;
414         int fd;
415         char *tp, *ep;
416         unsigned seed;
417
418         fd = -1;
419         archive_string_init(&temp_name);
420         if (tmpdir == NULL) {
421                 if (get_tempdir(&temp_name) != ARCHIVE_OK)
422                         goto exit_tmpfile;
423         } else
424                 archive_strcpy(&temp_name, tmpdir);
425         if (temp_name.s[temp_name.length-1] == '/') {
426                 temp_name.s[temp_name.length-1] = '\0';
427                 temp_name.length --;
428         }
429         if (stat(temp_name.s, &st) < 0)
430                 goto exit_tmpfile;
431         if (!S_ISDIR(st.st_mode)) {
432                 errno = ENOTDIR;
433                 goto exit_tmpfile;
434         }
435         archive_strcat(&temp_name, "/libarchive_");
436         tp = temp_name.s + archive_strlen(&temp_name);
437         archive_strcat(&temp_name, "XXXXXXXXXX");
438         ep = temp_name.s + archive_strlen(&temp_name);
439
440         fd = open("/dev/random", O_RDONLY);
441         if (fd < 0)
442                 seed = time(NULL);
443         else {
444                 if (read(fd, &seed, sizeof(seed)) < 0)
445                         seed = time(NULL);
446                 close(fd);
447         }
448         do {
449                 char *p;
450
451                 p = tp;
452                 while (p < ep)
453                         *p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
454                 fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600);
455         } while (fd < 0 && errno == EEXIST);
456         if (fd < 0)
457                 goto exit_tmpfile;
458         unlink(temp_name.s);
459 exit_tmpfile:
460         archive_string_free(&temp_name);
461         return (fd);
462 }
463
464 #endif /* HAVE_MKSTEMP */
465 #endif /* !_WIN32 || __CYGWIN__ */