8436d18115
Build liblzma as part of CMake or find one on the system. Modify our port of libarchive to use the liblzma configured for use with CMake.
611 lines
14 KiB
C
611 lines
14 KiB
C
/*-
|
|
* Copyright (c) 2009-2012 Michihiro NAKAJIMA
|
|
* Copyright (c) 2003-2007 Tim Kientzle
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "archive_platform.h"
|
|
__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
|
|
#include <wincrypt.h>
|
|
#endif
|
|
#ifdef HAVE_ZLIB_H
|
|
#include <cm_zlib.h>
|
|
#endif
|
|
#ifdef HAVE_LZMA_H
|
|
#include <cm_lzma.h>
|
|
#endif
|
|
#ifdef HAVE_BZLIB_H
|
|
#include <cm_bzlib.h>
|
|
#endif
|
|
|
|
#include "archive.h"
|
|
#include "archive_private.h"
|
|
#include "archive_string.h"
|
|
|
|
#ifndef O_CLOEXEC
|
|
#define O_CLOEXEC 0
|
|
#endif
|
|
|
|
static int archive_utility_string_sort_helper(char **, unsigned int);
|
|
|
|
/* Generic initialization of 'struct archive' objects. */
|
|
int
|
|
__archive_clean(struct archive *a)
|
|
{
|
|
archive_string_conversion_free(a);
|
|
return (ARCHIVE_OK);
|
|
}
|
|
|
|
int
|
|
archive_version_number(void)
|
|
{
|
|
return (ARCHIVE_VERSION_NUMBER);
|
|
}
|
|
|
|
const char *
|
|
archive_version_string(void)
|
|
{
|
|
return (ARCHIVE_VERSION_STRING);
|
|
}
|
|
|
|
const char *
|
|
archive_version_details(void)
|
|
{
|
|
static struct archive_string str;
|
|
static int init = 0;
|
|
|
|
if (!init) {
|
|
archive_string_init(&str);
|
|
|
|
archive_strcat(&str, ARCHIVE_VERSION_STRING);
|
|
#ifdef HAVE_ZLIB_H
|
|
archive_strcat(&str, " zlib/");
|
|
archive_strcat(&str, ZLIB_VERSION);
|
|
#endif
|
|
#ifdef HAVE_LZMA_H
|
|
archive_strcat(&str, " liblzma/");
|
|
archive_strcat(&str, LZMA_VERSION_STRING);
|
|
#endif
|
|
#ifdef HAVE_BZLIB_H
|
|
{
|
|
const char *p = BZ2_bzlibVersion();
|
|
const char *sep = strchr(p, ',');
|
|
if (sep == NULL)
|
|
sep = p + strlen(p);
|
|
archive_strcat(&str, " bz2lib/");
|
|
archive_strncat(&str, p, sep - p);
|
|
}
|
|
#endif
|
|
}
|
|
return str.s;
|
|
}
|
|
|
|
int
|
|
archive_errno(struct archive *a)
|
|
{
|
|
return (a->archive_error_number);
|
|
}
|
|
|
|
const char *
|
|
archive_error_string(struct archive *a)
|
|
{
|
|
|
|
if (a->error != NULL && *a->error != '\0')
|
|
return (a->error);
|
|
else
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
archive_file_count(struct archive *a)
|
|
{
|
|
return (a->file_count);
|
|
}
|
|
|
|
int
|
|
archive_format(struct archive *a)
|
|
{
|
|
return (a->archive_format);
|
|
}
|
|
|
|
const char *
|
|
archive_format_name(struct archive *a)
|
|
{
|
|
return (a->archive_format_name);
|
|
}
|
|
|
|
|
|
int
|
|
archive_compression(struct archive *a)
|
|
{
|
|
return archive_filter_code(a, 0);
|
|
}
|
|
|
|
const char *
|
|
archive_compression_name(struct archive *a)
|
|
{
|
|
return archive_filter_name(a, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return a count of the number of compressed bytes processed.
|
|
*/
|
|
int64_t
|
|
archive_position_compressed(struct archive *a)
|
|
{
|
|
return archive_filter_bytes(a, -1);
|
|
}
|
|
|
|
/*
|
|
* Return a count of the number of uncompressed bytes processed.
|
|
*/
|
|
int64_t
|
|
archive_position_uncompressed(struct archive *a)
|
|
{
|
|
return archive_filter_bytes(a, 0);
|
|
}
|
|
|
|
void
|
|
archive_clear_error(struct archive *a)
|
|
{
|
|
archive_string_empty(&a->error_string);
|
|
a->error = NULL;
|
|
a->archive_error_number = 0;
|
|
}
|
|
|
|
void
|
|
archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
a->archive_error_number = error_number;
|
|
if (fmt == NULL) {
|
|
a->error = NULL;
|
|
return;
|
|
}
|
|
|
|
archive_string_empty(&(a->error_string));
|
|
va_start(ap, fmt);
|
|
archive_string_vsprintf(&(a->error_string), fmt, ap);
|
|
va_end(ap);
|
|
a->error = a->error_string.s;
|
|
}
|
|
|
|
void
|
|
archive_copy_error(struct archive *dest, struct archive *src)
|
|
{
|
|
dest->archive_error_number = src->archive_error_number;
|
|
|
|
archive_string_copy(&dest->error_string, &src->error_string);
|
|
dest->error = dest->error_string.s;
|
|
}
|
|
|
|
void
|
|
__archive_errx(int retvalue, const char *msg)
|
|
{
|
|
static const char *msg1 = "Fatal Internal Error in libarchive: ";
|
|
size_t s;
|
|
|
|
s = write(2, msg1, strlen(msg1));
|
|
(void)s; /* UNUSED */
|
|
s = write(2, msg, strlen(msg));
|
|
(void)s; /* UNUSED */
|
|
s = write(2, "\n", 1);
|
|
(void)s; /* UNUSED */
|
|
exit(retvalue);
|
|
}
|
|
|
|
/*
|
|
* Create a temporary file
|
|
*/
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
|
|
/*
|
|
* Do not use Windows tmpfile() function.
|
|
* It will make a temporary file under the root directory
|
|
* and it'll cause permission error if a user who is
|
|
* non-Administrator creates temporary files.
|
|
* Also Windows version of mktemp family including _mktemp_s
|
|
* are not secure.
|
|
*/
|
|
int
|
|
__archive_mktemp(const char *tmpdir)
|
|
{
|
|
static const wchar_t num[] = {
|
|
L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
|
|
L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
|
|
L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
|
|
L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
|
|
L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
|
|
L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
|
|
L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
|
|
L'u', L'v', L'w', L'x', L'y', L'z'
|
|
};
|
|
HCRYPTPROV hProv;
|
|
struct archive_wstring temp_name;
|
|
wchar_t *ws;
|
|
DWORD attr;
|
|
wchar_t *xp, *ep;
|
|
int fd;
|
|
|
|
hProv = (HCRYPTPROV)NULL;
|
|
fd = -1;
|
|
ws = NULL;
|
|
archive_string_init(&temp_name);
|
|
|
|
/* Get a temporary directory. */
|
|
if (tmpdir == NULL) {
|
|
size_t l;
|
|
wchar_t *tmp;
|
|
|
|
l = GetTempPathW(0, NULL);
|
|
if (l == 0) {
|
|
la_dosmaperr(GetLastError());
|
|
goto exit_tmpfile;
|
|
}
|
|
tmp = malloc(l*sizeof(wchar_t));
|
|
if (tmp == NULL) {
|
|
errno = ENOMEM;
|
|
goto exit_tmpfile;
|
|
}
|
|
GetTempPathW((DWORD)l, tmp);
|
|
archive_wstrcpy(&temp_name, tmp);
|
|
free(tmp);
|
|
} else {
|
|
if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
|
|
strlen(tmpdir)) < 0)
|
|
goto exit_tmpfile;
|
|
if (temp_name.s[temp_name.length-1] != L'/')
|
|
archive_wstrappend_wchar(&temp_name, L'/');
|
|
}
|
|
|
|
/* Check if temp_name is a directory. */
|
|
attr = GetFileAttributesW(temp_name.s);
|
|
if (attr == (DWORD)-1) {
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
|
|
la_dosmaperr(GetLastError());
|
|
goto exit_tmpfile;
|
|
}
|
|
ws = __la_win_permissive_name_w(temp_name.s);
|
|
if (ws == NULL) {
|
|
errno = EINVAL;
|
|
goto exit_tmpfile;
|
|
}
|
|
attr = GetFileAttributesW(ws);
|
|
if (attr == (DWORD)-1) {
|
|
la_dosmaperr(GetLastError());
|
|
goto exit_tmpfile;
|
|
}
|
|
}
|
|
if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
errno = ENOTDIR;
|
|
goto exit_tmpfile;
|
|
}
|
|
|
|
/*
|
|
* Create a temporary file.
|
|
*/
|
|
archive_wstrcat(&temp_name, L"libarchive_");
|
|
xp = temp_name.s + archive_strlen(&temp_name);
|
|
archive_wstrcat(&temp_name, L"XXXXXXXXXX");
|
|
ep = temp_name.s + archive_strlen(&temp_name);
|
|
|
|
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT)) {
|
|
la_dosmaperr(GetLastError());
|
|
goto exit_tmpfile;
|
|
}
|
|
|
|
for (;;) {
|
|
wchar_t *p;
|
|
HANDLE h;
|
|
|
|
/* Generate a random file name through CryptGenRandom(). */
|
|
p = xp;
|
|
if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
|
|
(BYTE*)p)) {
|
|
la_dosmaperr(GetLastError());
|
|
goto exit_tmpfile;
|
|
}
|
|
for (; p < ep; p++)
|
|
*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
|
|
|
|
free(ws);
|
|
ws = __la_win_permissive_name_w(temp_name.s);
|
|
if (ws == NULL) {
|
|
errno = EINVAL;
|
|
goto exit_tmpfile;
|
|
}
|
|
/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
|
|
* delete this temporary file immediately when this
|
|
* file closed. */
|
|
h = CreateFileW(ws,
|
|
GENERIC_READ | GENERIC_WRITE | DELETE,
|
|
0,/* Not share */
|
|
NULL,
|
|
CREATE_NEW,/* Create a new file only */
|
|
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
|
|
NULL);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
/* The same file already exists. retry with
|
|
* a new filename. */
|
|
if (GetLastError() == ERROR_FILE_EXISTS)
|
|
continue;
|
|
/* Otherwise, fail creation temporary file. */
|
|
la_dosmaperr(GetLastError());
|
|
goto exit_tmpfile;
|
|
}
|
|
fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
|
|
if (fd == -1) {
|
|
CloseHandle(h);
|
|
goto exit_tmpfile;
|
|
} else
|
|
break;/* success! */
|
|
}
|
|
exit_tmpfile:
|
|
if (hProv != (HCRYPTPROV)NULL)
|
|
CryptReleaseContext(hProv, 0);
|
|
free(ws);
|
|
archive_wstring_free(&temp_name);
|
|
return (fd);
|
|
}
|
|
|
|
#else
|
|
|
|
static int
|
|
get_tempdir(struct archive_string *temppath)
|
|
{
|
|
const char *tmp;
|
|
|
|
tmp = getenv("TMPDIR");
|
|
if (tmp == NULL)
|
|
#ifdef _PATH_TMP
|
|
tmp = _PATH_TMP;
|
|
#else
|
|
tmp = "/tmp";
|
|
#endif
|
|
archive_strcpy(temppath, tmp);
|
|
if (temppath->s[temppath->length-1] != '/')
|
|
archive_strappend_char(temppath, '/');
|
|
return (ARCHIVE_OK);
|
|
}
|
|
|
|
#if defined(HAVE_MKSTEMP)
|
|
|
|
/*
|
|
* We can use mkstemp().
|
|
*/
|
|
|
|
int
|
|
__archive_mktemp(const char *tmpdir)
|
|
{
|
|
struct archive_string temp_name;
|
|
int fd = -1;
|
|
|
|
archive_string_init(&temp_name);
|
|
if (tmpdir == NULL) {
|
|
if (get_tempdir(&temp_name) != ARCHIVE_OK)
|
|
goto exit_tmpfile;
|
|
} else {
|
|
archive_strcpy(&temp_name, tmpdir);
|
|
if (temp_name.s[temp_name.length-1] != '/')
|
|
archive_strappend_char(&temp_name, '/');
|
|
}
|
|
archive_strcat(&temp_name, "libarchive_XXXXXX");
|
|
fd = mkstemp(temp_name.s);
|
|
if (fd < 0)
|
|
goto exit_tmpfile;
|
|
__archive_ensure_cloexec_flag(fd);
|
|
unlink(temp_name.s);
|
|
exit_tmpfile:
|
|
archive_string_free(&temp_name);
|
|
return (fd);
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
* We use a private routine.
|
|
*/
|
|
|
|
int
|
|
__archive_mktemp(const char *tmpdir)
|
|
{
|
|
static const char num[] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
|
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
|
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
|
|
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
|
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
|
'u', 'v', 'w', 'x', 'y', 'z'
|
|
};
|
|
struct archive_string temp_name;
|
|
struct stat st;
|
|
int fd;
|
|
char *tp, *ep;
|
|
unsigned seed;
|
|
|
|
fd = -1;
|
|
archive_string_init(&temp_name);
|
|
if (tmpdir == NULL) {
|
|
if (get_tempdir(&temp_name) != ARCHIVE_OK)
|
|
goto exit_tmpfile;
|
|
} else
|
|
archive_strcpy(&temp_name, tmpdir);
|
|
if (temp_name.s[temp_name.length-1] == '/') {
|
|
temp_name.s[temp_name.length-1] = '\0';
|
|
temp_name.length --;
|
|
}
|
|
if (stat(temp_name.s, &st) < 0)
|
|
goto exit_tmpfile;
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
errno = ENOTDIR;
|
|
goto exit_tmpfile;
|
|
}
|
|
archive_strcat(&temp_name, "/libarchive_");
|
|
tp = temp_name.s + archive_strlen(&temp_name);
|
|
archive_strcat(&temp_name, "XXXXXXXXXX");
|
|
ep = temp_name.s + archive_strlen(&temp_name);
|
|
|
|
fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
|
|
__archive_ensure_cloexec_flag(fd);
|
|
if (fd < 0)
|
|
seed = time(NULL);
|
|
else {
|
|
if (read(fd, &seed, sizeof(seed)) < 0)
|
|
seed = time(NULL);
|
|
close(fd);
|
|
}
|
|
do {
|
|
char *p;
|
|
|
|
p = tp;
|
|
while (p < ep)
|
|
*p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
|
|
fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
|
|
0600);
|
|
} while (fd < 0 && errno == EEXIST);
|
|
if (fd < 0)
|
|
goto exit_tmpfile;
|
|
__archive_ensure_cloexec_flag(fd);
|
|
unlink(temp_name.s);
|
|
exit_tmpfile:
|
|
archive_string_free(&temp_name);
|
|
return (fd);
|
|
}
|
|
|
|
#endif /* HAVE_MKSTEMP */
|
|
#endif /* !_WIN32 || __CYGWIN__ */
|
|
|
|
/*
|
|
* Set FD_CLOEXEC flag to a file descriptor if it is not set.
|
|
* We have to set the flag if the platform does not provide O_CLOEXEC
|
|
* or F_DUPFD_CLOEXEC flags.
|
|
*
|
|
* Note: This function is absolutely called after creating a new file
|
|
* descriptor even if the platform seemingly provides O_CLOEXEC or
|
|
* F_DUPFD_CLOEXEC macros because it is possible that the platform
|
|
* merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
|
|
*/
|
|
void
|
|
__archive_ensure_cloexec_flag(int fd)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
(void)fd; /* UNSED */
|
|
#else
|
|
int flags;
|
|
|
|
if (fd >= 0) {
|
|
flags = fcntl(fd, F_GETFD);
|
|
if (flags != -1 && (flags & FD_CLOEXEC) == 0)
|
|
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Utility function to sort a group of strings using quicksort.
|
|
*/
|
|
static int
|
|
archive_utility_string_sort_helper(char **strings, unsigned int n)
|
|
{
|
|
unsigned int i, lesser_count, greater_count;
|
|
char **lesser, **greater, **tmp, *pivot;
|
|
int retval1, retval2;
|
|
|
|
/* A list of 0 or 1 elements is already sorted */
|
|
if (n <= 1)
|
|
return (ARCHIVE_OK);
|
|
|
|
lesser_count = greater_count = 0;
|
|
lesser = greater = NULL;
|
|
pivot = strings[0];
|
|
for (i = 1; i < n; i++)
|
|
{
|
|
if (strcmp(strings[i], pivot) < 0)
|
|
{
|
|
lesser_count++;
|
|
tmp = (char **)realloc(lesser, lesser_count * sizeof(char *));
|
|
if (!tmp)
|
|
return (ARCHIVE_FATAL);
|
|
lesser = tmp;
|
|
lesser[lesser_count - 1] = strings[i];
|
|
}
|
|
else
|
|
{
|
|
greater_count++;
|
|
tmp = (char **)realloc(greater, greater_count * sizeof(char *));
|
|
if (!tmp)
|
|
return (ARCHIVE_FATAL);
|
|
greater = tmp;
|
|
greater[greater_count - 1] = strings[i];
|
|
}
|
|
}
|
|
|
|
/* quicksort(lesser) */
|
|
retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
|
|
for (i = 0; i < lesser_count; i++)
|
|
strings[i] = lesser[i];
|
|
free(lesser);
|
|
|
|
/* pivot */
|
|
strings[lesser_count] = pivot;
|
|
|
|
/* quicksort(greater) */
|
|
retval2 = archive_utility_string_sort_helper(greater, greater_count);
|
|
for (i = 0; i < greater_count; i++)
|
|
strings[lesser_count + 1 + i] = greater[i];
|
|
free(greater);
|
|
|
|
return (retval1 < retval2) ? retval1 : retval2;
|
|
}
|
|
|
|
int
|
|
archive_utility_string_sort(char **strings)
|
|
{
|
|
unsigned int size = 0;
|
|
while (strings[size] != NULL)
|
|
size++;
|
|
return archive_utility_string_sort_helper(strings, size);
|
|
}
|