summaryrefslogtreecommitdiffstats
path: root/src/liblzma/common/common.c
blob: adb50d785d52befe2355f91449bb22f55488114f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
///////////////////////////////////////////////////////////////////////////////
//
/// \file       common.c
/// \brief      Common functions needed in many places in liblzma
//
//  Author:     Lasse Collin
//
//  This file has been put into the public domain.
//  You can do whatever you want with this file.
//
///////////////////////////////////////////////////////////////////////////////

#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 //
///////////////////////

lzma_attr_alloc_size(1)
extern void *
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;
}


lzma_attr_alloc_size(1)
extern void *
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;
}


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 //
//////////////////////////////////////

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));
	strm->internal->sequence = ISEQ_RUN;
	strm->internal->allow_buf_error = false;

	strm->total_in = 0;
	strm->total_out = 0;

	return LZMA_OK;
}


extern LZMA_API(lzma_ret)
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
			|| !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;

	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;
		}

		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)
			return LZMA_PROG_ERROR;

		break;

	case ISEQ_FULL_FLUSH:
		if (action != LZMA_FULL_FLUSH
				|| strm->internal->avail_in != strm->avail_in)
			return LZMA_PROG_ERROR;

		break;

	case ISEQ_FINISH:
		if (action != LZMA_FINISH
				|| strm->internal->avail_in != strm->avail_in)
			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;

	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);

	// Updating next_in and next_out has to be skipped when they are NULL
	// to avoid null pointer + 0 (undefined behavior). Do this by checking
	// in_pos > 0 and out_pos > 0 because this way NULL + non-zero (a bug)
	// will get caught one way or other.
	if (in_pos > 0) {
		strm->next_in += in_pos;
		strm->avail_in -= in_pos;
		strm->total_in += in_pos;
	}

	if (out_pos > 0) {
		strm->next_out += out_pos;
		strm->avail_out -= out_pos;
		strm->total_out += out_pos;
	}

	strm->internal->avail_in = strm->avail_in;

	switch (ret) {
	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;

	case LZMA_STREAM_END:
		if (strm->internal->sequence == ISEQ_SYNC_FLUSH
				|| strm->internal->sequence == ISEQ_FULL_FLUSH
				|| strm->internal->sequence
					== ISEQ_FULL_BARRIER)
			strm->internal->sequence = ISEQ_RUN;
		else
			strm->internal->sequence = ISEQ_END;

	// Fall through

	case LZMA_NO_CHECK:
	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).
		strm->internal->allow_buf_error = false;
		break;

	default:
		// All the other errors are fatal; coding cannot be continued.
		assert(ret != LZMA_BUF_ERROR);
		strm->internal->sequence = ISEQ_ERROR;
		break;
	}

	return ret;
}


extern LZMA_API(void)
lzma_end(lzma_stream *strm)
{
	if (strm != NULL && strm->internal != NULL) {
		lzma_next_end(&strm->internal->next, strm->allocator);
		lzma_free(strm->internal, strm->allocator);
		strm->internal = NULL;
	}

	return;
}


#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)
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;

	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);
}