xz/src/liblzma/common/common.c

472 lines
11 KiB
C
Raw Normal View History

2007-12-08 22:42:33 +00:00
///////////////////////////////////////////////////////////////////////////////
//
/// \file common.c
/// \brief Common functions needed in many places in liblzma
2007-12-08 22:42:33 +00:00
//
// Author: Lasse Collin
2007-12-08 22:42:33 +00:00
//
// This file has been put into the public domain.
// You can do whatever you want with this file.
2007-12-08 22:42:33 +00:00
//
///////////////////////////////////////////////////////////////////////////////
#include "common.h"
/////////////
// Version //
/////////////
extern LZMA_API(uint32_t)
lzma_version_number(void)
{
return LZMA_VERSION;
}
extern LZMA_API(const char *)
lzma_version_string(void)
{
return LZMA_VERSION_STRING;
}
///////////////////////
// Memory allocation //
///////////////////////
extern void * lzma_attribute((__malloc__)) lzma_attr_alloc_size(1)
lzma_alloc(size_t size, const lzma_allocator *allocator)
{
// Some malloc() variants return NULL if called with size == 0.
if (size == 0)
size = 1;
void *ptr;
if (allocator != NULL && allocator->alloc != NULL)
ptr = allocator->alloc(allocator->opaque, 1, size);
else
ptr = malloc(size);
return ptr;
}
extern void * lzma_attribute((__malloc__)) lzma_attr_alloc_size(1)
lzma_alloc_zero(size_t size, const lzma_allocator *allocator)
{
// Some calloc() variants return NULL if called with size == 0.
if (size == 0)
size = 1;
void *ptr;
if (allocator != NULL && allocator->alloc != NULL) {
ptr = allocator->alloc(allocator->opaque, 1, size);
if (ptr != NULL)
memzero(ptr, size);
} else {
ptr = calloc(1, size);
}
return ptr;
}
extern void
lzma_free(void *ptr, const lzma_allocator *allocator)
{
if (allocator != NULL && allocator->free != NULL)
allocator->free(allocator->opaque, ptr);
else
free(ptr);
return;
}
//////////
// Misc //
//////////
extern size_t
lzma_bufcpy(const uint8_t *restrict in, size_t *restrict in_pos,
size_t in_size, uint8_t *restrict out,
size_t *restrict out_pos, size_t out_size)
{
const size_t in_avail = in_size - *in_pos;
const size_t out_avail = out_size - *out_pos;
const size_t copy_size = my_min(in_avail, out_avail);
// Call memcpy() only if there is something to copy. If there is
// nothing to copy, in or out might be NULL and then the memcpy()
// call would trigger undefined behavior.
if (copy_size > 0)
memcpy(out + *out_pos, in + *in_pos, copy_size);
*in_pos += copy_size;
*out_pos += copy_size;
return copy_size;
}
2007-12-08 22:42:33 +00:00
extern lzma_ret
lzma_next_filter_init(lzma_next_coder *next, const lzma_allocator *allocator,
const lzma_filter_info *filters)
{
lzma_next_coder_init(filters[0].init, next, allocator);
next->id = filters[0].id;
return filters[0].init == NULL
? LZMA_OK : filters[0].init(next, allocator, filters);
}
extern lzma_ret
lzma_next_filter_update(lzma_next_coder *next, const lzma_allocator *allocator,
const lzma_filter *reversed_filters)
{
// Check that the application isn't trying to change the Filter ID.
// End of filters is indicated with LZMA_VLI_UNKNOWN in both
// reversed_filters[0].id and next->id.
if (reversed_filters[0].id != next->id)
return LZMA_PROG_ERROR;
if (reversed_filters[0].id == LZMA_VLI_UNKNOWN)
return LZMA_OK;
assert(next->update != NULL);
return next->update(next->coder, allocator, NULL, reversed_filters);
}
extern void
lzma_next_end(lzma_next_coder *next, const lzma_allocator *allocator)
{
if (next->init != (uintptr_t)(NULL)) {
// To avoid tiny end functions that simply call
// lzma_free(coder, allocator), we allow leaving next->end
// NULL and call lzma_free() here.
if (next->end != NULL)
next->end(next->coder, allocator);
else
lzma_free(next->coder, allocator);
// Reset the variables so the we don't accidentally think
// that it is an already initialized coder.
*next = LZMA_NEXT_CODER_INIT;
}
return;
}
//////////////////////////////////////
// External to internal API wrapper //
//////////////////////////////////////
2007-12-08 22:42:33 +00:00
extern lzma_ret
lzma_strm_init(lzma_stream *strm)
{
if (strm == NULL)
return LZMA_PROG_ERROR;
if (strm->internal == NULL) {
strm->internal = lzma_alloc(sizeof(lzma_internal),
strm->allocator);
if (strm->internal == NULL)
return LZMA_MEM_ERROR;
strm->internal->next = LZMA_NEXT_CODER_INIT;
}
memzero(strm->internal->supported_actions,
sizeof(strm->internal->supported_actions));
2007-12-08 22:42:33 +00:00
strm->internal->sequence = ISEQ_RUN;
strm->internal->allow_buf_error = false;
2007-12-08 22:42:33 +00:00
strm->total_in = 0;
strm->total_out = 0;
return LZMA_OK;
}
extern LZMA_API(lzma_ret)
2007-12-08 22:42:33 +00:00
lzma_code(lzma_stream *strm, lzma_action action)
{
// Sanity checks
if ((strm->next_in == NULL && strm->avail_in != 0)
|| (strm->next_out == NULL && strm->avail_out != 0)
|| strm->internal == NULL
|| strm->internal->next.code == NULL
|| (unsigned int)(action) > LZMA_ACTION_MAX
2007-12-08 22:42:33 +00:00
|| !strm->internal->supported_actions[action])
return LZMA_PROG_ERROR;
// Check if unsupported members have been set to non-zero or non-NULL,
// which would indicate that some new feature is wanted.
if (strm->reserved_ptr1 != NULL
|| strm->reserved_ptr2 != NULL
|| strm->reserved_ptr3 != NULL
|| strm->reserved_ptr4 != NULL
|| strm->reserved_int2 != 0
|| strm->reserved_int3 != 0
|| strm->reserved_int4 != 0
|| strm->reserved_enum1 != LZMA_RESERVED_ENUM
|| strm->reserved_enum2 != LZMA_RESERVED_ENUM)
return LZMA_OPTIONS_ERROR;
2007-12-08 22:42:33 +00:00
switch (strm->internal->sequence) {
case ISEQ_RUN:
switch (action) {
case LZMA_RUN:
break;
case LZMA_SYNC_FLUSH:
strm->internal->sequence = ISEQ_SYNC_FLUSH;
break;
case LZMA_FULL_FLUSH:
strm->internal->sequence = ISEQ_FULL_FLUSH;
break;
case LZMA_FINISH:
strm->internal->sequence = ISEQ_FINISH;
break;
case LZMA_FULL_BARRIER:
strm->internal->sequence = ISEQ_FULL_BARRIER;
break;
2007-12-08 22:42:33 +00:00
}
break;
case ISEQ_SYNC_FLUSH:
// The same action must be used until we return
// LZMA_STREAM_END, and the amount of input must not change.
if (action != LZMA_SYNC_FLUSH
|| strm->internal->avail_in != strm->avail_in)
2007-12-08 22:42:33 +00:00
return LZMA_PROG_ERROR;
break;
case ISEQ_FULL_FLUSH:
if (action != LZMA_FULL_FLUSH
|| strm->internal->avail_in != strm->avail_in)
2007-12-08 22:42:33 +00:00
return LZMA_PROG_ERROR;
break;
case ISEQ_FINISH:
if (action != LZMA_FINISH
|| strm->internal->avail_in != strm->avail_in)
2007-12-08 22:42:33 +00:00
return LZMA_PROG_ERROR;
break;
case ISEQ_FULL_BARRIER:
if (action != LZMA_FULL_BARRIER
|| strm->internal->avail_in != strm->avail_in)
return LZMA_PROG_ERROR;
break;
2007-12-08 22:42:33 +00:00
case ISEQ_END:
return LZMA_STREAM_END;
case ISEQ_ERROR:
default:
return LZMA_PROG_ERROR;
}
size_t in_pos = 0;
size_t out_pos = 0;
lzma_ret ret = strm->internal->next.code(
strm->internal->next.coder, strm->allocator,
strm->next_in, &in_pos, strm->avail_in,
strm->next_out, &out_pos, strm->avail_out, action);
strm->next_in += in_pos;
strm->avail_in -= in_pos;
strm->total_in += in_pos;
strm->next_out += out_pos;
strm->avail_out -= out_pos;
strm->total_out += out_pos;
strm->internal->avail_in = strm->avail_in;
switch (ret) {
2007-12-08 22:42:33 +00:00
case LZMA_OK:
// Don't return LZMA_BUF_ERROR when it happens the first time.
// This is to avoid returning LZMA_BUF_ERROR when avail_out
// was zero but still there was no more data left to written
// to next_out.
if (out_pos == 0 && in_pos == 0) {
if (strm->internal->allow_buf_error)
ret = LZMA_BUF_ERROR;
else
strm->internal->allow_buf_error = true;
} else {
strm->internal->allow_buf_error = false;
}
break;
case LZMA_TIMED_OUT:
strm->internal->allow_buf_error = false;
ret = LZMA_OK;
break;
case LZMA_SEEK_NEEDED:
strm->internal->allow_buf_error = false;
// If LZMA_FINISH was used, reset it back to the
// LZMA_RUN-based state so that new input can be supplied
// by the application.
if (strm->internal->sequence == ISEQ_FINISH)
strm->internal->sequence = ISEQ_RUN;
break;
2007-12-08 22:42:33 +00:00
case LZMA_STREAM_END:
if (strm->internal->sequence == ISEQ_SYNC_FLUSH
|| strm->internal->sequence == ISEQ_FULL_FLUSH
|| strm->internal->sequence
== ISEQ_FULL_BARRIER)
2007-12-08 22:42:33 +00:00
strm->internal->sequence = ISEQ_RUN;
else
strm->internal->sequence = ISEQ_END;
// Fall through
case LZMA_NO_CHECK:
2007-12-08 22:42:33 +00:00
case LZMA_UNSUPPORTED_CHECK:
case LZMA_GET_CHECK:
case LZMA_MEMLIMIT_ERROR:
// Something else than LZMA_OK, but not a fatal error,
// that is, coding may be continued (except if ISEQ_END).
2007-12-08 22:42:33 +00:00
strm->internal->allow_buf_error = false;
break;
default:
// All the other errors are fatal; coding cannot be continued.
assert(ret != LZMA_BUF_ERROR);
2007-12-08 22:42:33 +00:00
strm->internal->sequence = ISEQ_ERROR;
break;
}
return ret;
}
extern LZMA_API(void)
2007-12-08 22:42:33 +00:00
lzma_end(lzma_stream *strm)
{
if (strm != NULL && strm->internal != NULL) {
lzma_next_end(&strm->internal->next, strm->allocator);
2007-12-08 22:42:33 +00:00
lzma_free(strm->internal, strm->allocator);
strm->internal = NULL;
}
return;
}
2008-09-06 12:14:30 +00:00
liblzma: Vaccinate against an ill patch from RHEL/CentOS 7. RHEL/CentOS 7 shipped with 5.1.2alpha, including the threaded encoder that is behind #ifdef LZMA_UNSTABLE in the API headers. In 5.1.2alpha these symbols are under XZ_5.1.2alpha in liblzma.map. API/ABI compatibility tracking isn't done between development releases so newer releases didn't have XZ_5.1.2alpha anymore. Later RHEL/CentOS 7 updated xz to 5.2.2 but they wanted to keep the exported symbols compatible with 5.1.2alpha. After checking the ABI changes it turned out that >= 5.2.0 ABI is backward compatible with the threaded encoder functions from 5.1.2alpha (but not vice versa as fixes and extensions to these functions were made between 5.1.2alpha and 5.2.0). In RHEL/CentOS 7, XZ Utils 5.2.2 was patched with xz-5.2.2-compat-libs.patch to modify liblzma.map: - XZ_5.1.2alpha was added with lzma_stream_encoder_mt and lzma_stream_encoder_mt_memusage. This matched XZ Utils 5.1.2alpha. - XZ_5.2 was replaced with XZ_5.2.2. It is clear that this was an error; the intention was to keep using XZ_5.2 (XZ_5.2.2 has never been used in XZ Utils). So XZ_5.2.2 lists all symbols that were listed under XZ_5.2 before the patch. lzma_stream_encoder_mt and _mt_memusage are included too so they are listed both here and under XZ_5.1.2alpha. The patch didn't add any __asm__(".symver ...") lines to the .c files. Thus the resulting liblzma.so exports the threaded encoder functions under XZ_5.1.2alpha only. Listing the two functions also under XZ_5.2.2 in liblzma.map has no effect without matching .symver lines. The lack of XZ_5.2 in RHEL/CentOS 7 means that binaries linked against unpatched XZ Utils 5.2.x won't run on RHEL/CentOS 7. This is unfortunate but this alone isn't too bad as the problem is contained within RHEL/CentOS 7 and doesn't affect users of other distributions. It could also be fixed internally in RHEL/CentOS 7. The second problem is more serious: In XZ Utils 5.2.2 the API headers don't have #ifdef LZMA_UNSTABLE for obvious reasons. This is true in RHEL/CentOS 7 version too. Thus now programs using new APIs can be compiled without an extra #define. However, the programs end up depending on symbol version XZ_5.1.2alpha (and possibly also XZ_5.2.2) instead of XZ_5.2 as they would with an unpatched XZ Utils 5.2.2. This means that such binaries won't run on other distributions shipping XZ Utils >= 5.2.0 as they don't provide XZ_5.1.2alpha or XZ_5.2.2; they only provide XZ_5.2 (and XZ_5.0). (This includes RHEL/CentOS 8 as the patch luckily isn't included there anymore with XZ Utils 5.2.4.) Binaries built by RHEL/CentOS 7 users get distributed and then people wonder why they don't run on some other distribution. Seems that people have found out about the patch and been copying it to some build scripts, seemingly curing the symptoms but actually spreading the illness further and outside RHEL/CentOS 7. The ill patch seems to be from late 2016 (RHEL 7.3) and in 2017 it had spread at least to EasyBuild. I heard about the events only recently. :-( This commit splits liblzma.map into two versions: one for GNU/Linux and another for other OSes that can use symbol versioning (FreeBSD, Solaris, maybe others). The Linux-specific file and the matching additions to .c files add full compatibility with binaries that have been built against a RHEL/CentOS-patched liblzma. Builds for OSes other than GNU/Linux won't get the vaccine as they should be immune to the problem (I really hope that no build script uses the RHEL/CentOS 7 patch outside GNU/Linux). The RHEL/CentOS compatibility symbols XZ_5.1.2alpha and XZ_5.2.2 are intentionally put *after* XZ_5.2 in liblzma_linux.map. This way if one forgets to #define HAVE_SYMBOL_VERSIONS_LINUX when building, the resulting liblzma.so.5 will have lzma_stream_encoder_mt@@XZ_5.2 since XZ_5.2 {...} is the first one that lists that function. Without HAVE_SYMBOL_VERSIONS_LINUX @XZ_5.1.2alpha and @XZ_5.2.2 will be missing but that's still a minor problem compared to only having lzma_stream_encoder_mt@@XZ_5.1.2alpha! The "local: *;" line was moved to XZ_5.0 so that it doesn't need to be moved around. It doesn't matter where it is put. Having two similar liblzma_*.map files is a bit silly as it is, at least for now, easily possible to generate the generic one from the Linux-specific file. But that adds extra steps and increases the risk of mistakes when supporting more than one build system. So I rather maintain two files in parallel and let validate_map.sh check that they are in sync when "make mydist" is run. This adds .symver lines for lzma_stream_encoder_mt@XZ_5.2.2 and lzma_stream_encoder_mt_memusage@XZ_5.2.2 even though these weren't exported by RHEL/CentOS 7 (only @@XZ_5.1.2alpha was for these two). I added these anyway because someone might misunderstand the RHEL/CentOS 7 patch and think that @XZ_5.2.2 (@@XZ_5.2.2) versions were exported too. At glance one could suggest using __typeof__ to copy the function prototypes when making aliases. However, this doesn't work trivially because __typeof__ won't copy attributes (lzma_nothrow, lzma_pure) and it won't change symbol visibility from hidden to default (done by LZMA_API()). Attributes could be copied with __copy__ attribute but that needs GCC 9 and a fallback method would be needed anyway. This uses __symver__ attribute with GCC >= 10 and __asm__(".symver ...") with everything else. The attribute method is required for LTO (-flto) support with GCC. Using -flto with GCC older than 10 is now broken on GNU/Linux and will not be fixed (can silently result in a broken liblzma build that has dangerously incorrect symbol versions). LTO builds with Clang seem to work with the traditional __asm__(".symver ...") method. Thanks to Boud Roukema for reporting the problem and discussing the details and testing the fix.
2022-09-04 20:23:00 +00:00
#ifdef HAVE_SYMBOL_VERSIONS_LINUX
// This is for compatibility with binaries linked against liblzma that
// has been patched with xz-5.2.2-compat-libs.patch from RHEL/CentOS 7.
LZMA_SYMVER_API("lzma_get_progress@XZ_5.2.2",
void, lzma_get_progress_522)(lzma_stream *strm,
uint64_t *progress_in, uint64_t *progress_out) lzma_nothrow
__attribute__((__alias__("lzma_get_progress_52")));
LZMA_SYMVER_API("lzma_get_progress@@XZ_5.2",
void, lzma_get_progress_52)(lzma_stream *strm,
uint64_t *progress_in, uint64_t *progress_out) lzma_nothrow;
#define lzma_get_progress lzma_get_progress_52
#endif
extern LZMA_API(void)
lzma_get_progress(lzma_stream *strm,
uint64_t *progress_in, uint64_t *progress_out)
{
if (strm->internal->next.get_progress != NULL) {
strm->internal->next.get_progress(strm->internal->next.coder,
progress_in, progress_out);
} else {
*progress_in = strm->total_in;
*progress_out = strm->total_out;
}
return;
}
extern LZMA_API(lzma_check)
2008-09-06 12:14:30 +00:00
lzma_get_check(const lzma_stream *strm)
{
// Return LZMA_CHECK_NONE if we cannot know the check type.
// It's a bug in the application if this happens.
if (strm->internal->next.get_check == NULL)
return LZMA_CHECK_NONE;
2008-09-06 12:14:30 +00:00
return strm->internal->next.get_check(strm->internal->next.coder);
}
extern LZMA_API(uint64_t)
lzma_memusage(const lzma_stream *strm)
{
uint64_t memusage;
uint64_t old_memlimit;
if (strm == NULL || strm->internal == NULL
|| strm->internal->next.memconfig == NULL
|| strm->internal->next.memconfig(
strm->internal->next.coder,
&memusage, &old_memlimit, 0) != LZMA_OK)
return 0;
return memusage;
}
extern LZMA_API(uint64_t)
lzma_memlimit_get(const lzma_stream *strm)
{
uint64_t old_memlimit;
uint64_t memusage;
if (strm == NULL || strm->internal == NULL
|| strm->internal->next.memconfig == NULL
|| strm->internal->next.memconfig(
strm->internal->next.coder,
&memusage, &old_memlimit, 0) != LZMA_OK)
return 0;
return old_memlimit;
}
extern LZMA_API(lzma_ret)
lzma_memlimit_set(lzma_stream *strm, uint64_t new_memlimit)
{
// Dummy variables to simplify memconfig functions
uint64_t old_memlimit;
uint64_t memusage;
if (strm == NULL || strm->internal == NULL
|| strm->internal->next.memconfig == NULL)
return LZMA_PROG_ERROR;
// Zero is a special value that cannot be used as an actual limit.
// If 0 was specified, use 1 instead.
if (new_memlimit == 0)
new_memlimit = 1;
return strm->internal->next.memconfig(strm->internal->next.coder,
&memusage, &old_memlimit, new_memlimit);
}