1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13   FastLZ - lightning-fast lossless compression library
14
15   Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
16   Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
17   Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
18
19   Permission is hereby granted, free of charge, to any person obtaining a copy
20   of this software and associated documentation files (the "Software"), to deal
21   in the Software without restriction, including without limitation the rights
22   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23   copies of the Software, and to permit persons to whom the Software is
24   furnished to do so, subject to the following conditions:
25
26   The above copyright notice and this permission notice shall be included in
27   all copies or substantial portions of the Software.
28
29   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35   THE SOFTWARE.
36   */
37
38#include "osdep.h"
39#include "cudbg.h"
40#include "cudbg_lib_common.h"
41#include "fastlz.h"
42
43#ifndef _KERNEL
44#include <string.h>
45#endif
46
47static unsigned char sixpack_magic[8] = {137, '6', 'P', 'K', 13, 10, 26, 10};
48
49#define CUDBG_BLOCK_SIZE      (63*1024)
50#define CUDBG_CHUNK_BUF_LEN   16
51#define CUDBG_MIN_COMPR_LEN   32	/*min data length for applying compression*/
52
53/* for Adler-32 checksum algorithm, see RFC 1950 Section 8.2 */
54
55#define ADLER32_BASE 65521
56
57static unsigned long
58update_adler32(unsigned long checksum,
59	       const void *buf, int len)
60{
61	const unsigned char *ptr = (const unsigned char *)buf;
62	unsigned long s1 = checksum & 0xffff;
63	unsigned long s2 = (checksum >> 16) & 0xffff;
64
65	while (len > 0) {
66		unsigned k = len < 5552 ? len : 5552;
67		len -= k;
68
69		while (k >= 8) {
70			s1 += *ptr++; s2 += s1;
71			s1 += *ptr++; s2 += s1;
72			s1 += *ptr++; s2 += s1;
73			s1 += *ptr++; s2 += s1;
74			s1 += *ptr++; s2 += s1;
75			s1 += *ptr++; s2 += s1;
76			s1 += *ptr++; s2 += s1;
77			s1 += *ptr++; s2 += s1;
78			k -= 8;
79		}
80
81		while (k-- > 0) {
82			s1 += *ptr++; s2 += s1;
83		}
84		s1 = s1 % ADLER32_BASE;
85		s2 = s2 % ADLER32_BASE;
86	}
87	return (s2 << 16) + s1;
88}
89
90int
91write_magic(struct cudbg_buffer *_out_buff)
92{
93	int rc;
94
95	rc = write_to_buf(_out_buff->data, _out_buff->size, &_out_buff->offset,
96			  sixpack_magic, 8);
97
98	return rc;
99}
100
101int
102write_to_buf(void *out_buf, u32 out_buf_size, u32 *offset, void *in_buf,
103	     u32 in_buf_size)
104{
105	int rc = 0;
106
107	if (*offset >= out_buf_size) {
108		rc = CUDBG_STATUS_OUTBUFF_OVERFLOW;
109		goto err;
110	}
111
112	memcpy((char *)out_buf + *offset, in_buf, in_buf_size);
113	*offset = *offset + in_buf_size;
114
115err:
116	return rc;
117}
118
119int
120read_from_buf(void *in_buf, u32 in_buf_size, u32 *offset, void *out_buf,
121	      u32 out_buf_size)
122{
123	if (in_buf_size - *offset < out_buf_size)
124		return 0;
125
126	memcpy((char *)out_buf, (char *)in_buf + *offset, out_buf_size);
127	*offset =  *offset + out_buf_size;
128	return out_buf_size;
129}
130
131int
132write_chunk_header(struct cudbg_buffer *_outbuf, int id, int options,
133		   unsigned long size, unsigned long checksum,
134		   unsigned long extra)
135{
136	unsigned char buffer[CUDBG_CHUNK_BUF_LEN];
137	int rc = 0;
138
139	buffer[0] = id & 255;
140	buffer[1] = (unsigned char)(id >> 8);
141	buffer[2] = options & 255;
142	buffer[3] = (unsigned char)(options >> 8);
143	buffer[4] = size & 255;
144	buffer[5] = (size >> 8) & 255;
145	buffer[6] = (size >> 16) & 255;
146	buffer[7] = (size >> 24) & 255;
147	buffer[8] = checksum & 255;
148	buffer[9] = (checksum >> 8) & 255;
149	buffer[10] = (checksum >> 16) & 255;
150	buffer[11] = (checksum >> 24) & 255;
151	buffer[12] = extra & 255;
152	buffer[13] = (extra >> 8) & 255;
153	buffer[14] = (extra >> 16) & 255;
154	buffer[15] = (extra >> 24) & 255;
155
156	rc = write_to_buf(_outbuf->data, _outbuf->size, &_outbuf->offset,
157			  buffer, 16);
158
159	return rc;
160}
161
162int
163write_compression_hdr(struct cudbg_buffer *pin_buff,
164		      struct cudbg_buffer *pout_buff)
165{
166	struct cudbg_buffer tmp_buffer;
167	unsigned long fsize = pin_buff->size;
168	unsigned char *buffer;
169	unsigned long checksum;
170	int rc;
171	char *shown_name = "abc";
172
173	/* Always release inner scratch buffer, before releasing outer. */
174	rc = get_scratch_buff(pout_buff, 10, &tmp_buffer);
175
176	if (rc)
177		goto err;
178
179	buffer = (unsigned char *)tmp_buffer.data;
180
181	rc = write_magic(pout_buff);
182
183	if (rc)
184		goto err1;
185
186	/* chunk for File Entry */
187	buffer[0] = fsize & 255;
188	buffer[1] = (fsize >> 8) & 255;
189	buffer[2] = (fsize >> 16) & 255;
190	buffer[3] = (fsize >> 24) & 255;
191	buffer[4] = 0;
192	buffer[5] = 0;
193	buffer[6] = 0;
194	buffer[7] = 0;
195	buffer[8] = (strlen(shown_name)+1) & 255;
196	buffer[9] = (unsigned char)((strlen(shown_name)+1) >> 8);
197	checksum = 1L;
198	checksum = update_adler32(checksum, buffer, 10);
199	checksum = update_adler32(checksum, shown_name,
200				  (int)strlen(shown_name)+1);
201
202	rc = write_chunk_header(pout_buff, 1, 0,
203				10+(unsigned long)strlen(shown_name)+1,
204				checksum, 0);
205
206	if (rc)
207		goto err1;
208
209	rc = write_to_buf(pout_buff->data, pout_buff->size,
210			  &(pout_buff->offset), buffer, 10);
211
212	if (rc)
213		goto err1;
214
215	rc = write_to_buf(pout_buff->data, pout_buff->size,
216			   &(pout_buff->offset), shown_name,
217			   (u32)strlen(shown_name)+1);
218
219	if (rc)
220		goto err1;
221
222err1:
223	release_scratch_buff(&tmp_buffer, pout_buff);
224err:
225	return rc;
226}
227
228int
229compress_buff(struct cudbg_buffer *pin_buff, struct cudbg_buffer *pout_buff)
230{
231	struct cudbg_buffer tmp_buffer;
232	struct cudbg_hdr *cudbg_hdr;
233	unsigned long checksum;
234	unsigned char *result;
235	unsigned int bytes_read;
236	int chunk_size, level = 2, rc = 0;
237	int compress_method = 1;
238
239	bytes_read = pin_buff->size;
240	rc = get_scratch_buff(pout_buff, CUDBG_BLOCK_SIZE, &tmp_buffer);
241
242	if (rc)
243		goto err;
244
245	result = (unsigned char *)tmp_buffer.data;
246
247	if (bytes_read < 32)
248		compress_method = 0;
249
250	cudbg_hdr = (struct cudbg_hdr *)  pout_buff->data;
251
252	switch (compress_method) {
253	case 1:
254		chunk_size = fastlz_compress_level(level, pin_buff->data,
255						   bytes_read, result);
256
257		checksum = update_adler32(1L, result, chunk_size);
258
259		if ((chunk_size > 62000) && (cudbg_hdr->reserved[7] < (u32)
260		    chunk_size))   /* 64512 */
261			cudbg_hdr->reserved[7] = (u32) chunk_size;
262
263		rc = write_chunk_header(pout_buff, 17, 1, chunk_size, checksum,
264					bytes_read);
265
266		if (rc)
267			goto err_put_buff;
268
269		rc = write_to_buf(pout_buff->data, pout_buff->size,
270				  &pout_buff->offset, result, chunk_size);
271
272		if (rc)
273			goto err_put_buff;
274
275		break;
276
277		/* uncompressed, also fallback method */
278	case 0:
279	default:
280		checksum = update_adler32(1L, pin_buff->data, bytes_read);
281
282		rc = write_chunk_header(pout_buff, 17, 0, bytes_read, checksum,
283					bytes_read);
284
285		if (rc)
286			goto err_put_buff;
287
288		rc = write_to_buf(pout_buff->data, pout_buff->size,
289				  &pout_buff->offset, pin_buff->data,
290				  bytes_read);
291		if (rc)
292			goto err_put_buff;
293
294		break;
295	}
296
297err_put_buff:
298	release_scratch_buff(&tmp_buffer, pout_buff);
299err:
300	return rc;
301}
302
303/* return non-zero if magic sequence is detected */
304/* warning: reset the read pointer to the beginning of the file */
305int
306detect_magic(struct cudbg_buffer *_c_buff)
307{
308	unsigned char buffer[8];
309	size_t bytes_read;
310	int c;
311
312	bytes_read = read_from_buf(_c_buff->data, _c_buff->size,
313				   &_c_buff->offset, buffer, 8);
314
315	if (bytes_read < 8)
316		return 0;
317
318	for (c = 0; c < 8; c++)
319		if (buffer[c] != sixpack_magic[c])
320			return 0;
321
322	return -1;
323}
324
325static unsigned long
326readU16(const unsigned char *ptr)
327{
328	return ptr[0]+(ptr[1]<<8);
329}
330
331static unsigned long
332readU32(const unsigned char *ptr)
333{
334	return ptr[0]+(ptr[1]<<8)+(ptr[2]<<16)+(ptr[3]<<24);
335}
336
337int
338read_chunk_header(struct cudbg_buffer *pc_buff, int *pid, int *poptions,
339		  unsigned long *psize, unsigned long *pchecksum,
340		  unsigned long *pextra)
341{
342	unsigned char buffer[CUDBG_CHUNK_BUF_LEN];
343	int byte_r = read_from_buf(pc_buff->data, pc_buff->size,
344				   &pc_buff->offset, buffer, 16);
345	if (byte_r == 0)
346		return 0;
347
348	*pid = readU16(buffer) & 0xffff;
349	*poptions = readU16(buffer+2) & 0xffff;
350	*psize = readU32(buffer+4) & 0xffffffff;
351	*pchecksum = readU32(buffer+8) & 0xffffffff;
352	*pextra = readU32(buffer+12) & 0xffffffff;
353	return 0;
354}
355
356int
357validate_buffer(struct cudbg_buffer *compressed_buffer)
358{
359	if (!detect_magic(compressed_buffer))
360		return CUDBG_STATUS_INVALID_BUFF;
361
362	return 0;
363}
364
365int
366decompress_buffer(struct cudbg_buffer *pc_buff,
367		  struct cudbg_buffer *pd_buff)
368{
369	struct cudbg_buffer tmp_compressed_buffer;
370	struct cudbg_buffer tmp_decompressed_buffer;
371	unsigned char *compressed_buffer;
372	unsigned char *decompressed_buffer;
373	unsigned char buffer[CUDBG_MIN_COMPR_LEN];
374	unsigned long chunk_size;
375	unsigned long chunk_checksum;
376	unsigned long chunk_extra;
377	unsigned long checksum;
378	unsigned long total_extracted = 0;
379	unsigned long r;
380	unsigned long remaining;
381	unsigned long bytes_read;
382	u32 decompressed_size = 0;
383	int chunk_id, chunk_options, rc;
384
385	if (pd_buff->size < 2 * CUDBG_BLOCK_SIZE)
386		return CUDBG_STATUS_SMALL_BUFF;
387
388	rc = get_scratch_buff(pd_buff, CUDBG_BLOCK_SIZE,
389			      &tmp_compressed_buffer);
390
391	if (rc)
392		goto err_cbuff;
393
394	rc = get_scratch_buff(pd_buff, CUDBG_BLOCK_SIZE,
395			      &tmp_decompressed_buffer);
396	if (rc)
397		goto err_dcbuff;
398
399	compressed_buffer = (unsigned char *)tmp_compressed_buffer.data;
400	decompressed_buffer = (unsigned char *)tmp_decompressed_buffer.data;
401
402	/* main loop */
403
404	for (;;) {
405		if (pc_buff->offset > pc_buff->size)
406			break;
407
408		rc =  read_chunk_header(pc_buff, &chunk_id, &chunk_options,
409					&chunk_size, &chunk_checksum,
410					&chunk_extra);
411		if (rc != 0)
412			break;
413
414		/* skip 8+16 */
415		if ((chunk_id == 1) && (chunk_size > 10) &&
416		    (chunk_size < CUDBG_BLOCK_SIZE)) {
417
418			bytes_read = read_from_buf(pc_buff->data, pc_buff->size,
419						   &pc_buff->offset, buffer,
420						   chunk_size);
421
422			if (bytes_read == 0)
423				return 0;
424
425			checksum = update_adler32(1L, buffer, chunk_size);
426			if (checksum != chunk_checksum)
427				return CUDBG_STATUS_CHKSUM_MISSMATCH;
428
429			decompressed_size = (u32)readU32(buffer);
430
431			if (pd_buff->size < decompressed_size) {
432
433				pd_buff->size = 2 * CUDBG_BLOCK_SIZE +
434						decompressed_size;
435				pc_buff->offset -= chunk_size + 16;
436				return CUDBG_STATUS_SMALL_BUFF;
437			}
438			total_extracted = 0;
439
440		}
441
442		if (chunk_size > CUDBG_BLOCK_SIZE) {
443			/* Release old allocated memory */
444			release_scratch_buff(&tmp_decompressed_buffer, pd_buff);
445			release_scratch_buff(&tmp_compressed_buffer, pd_buff);
446
447			/* allocate new memory with chunk_size size */
448			rc = get_scratch_buff(pd_buff, chunk_size,
449					      &tmp_compressed_buffer);
450			if (rc)
451				goto err_cbuff;
452
453			rc = get_scratch_buff(pd_buff, chunk_size,
454					      &tmp_decompressed_buffer);
455			if (rc)
456				goto err_dcbuff;
457
458			compressed_buffer = (unsigned char *)tmp_compressed_buffer.data;
459			decompressed_buffer = (unsigned char *)tmp_decompressed_buffer.data;
460		}
461
462		if ((chunk_id == 17) && decompressed_size) {
463			/* uncompressed */
464			switch (chunk_options) {
465				/* stored, simply copy to output */
466			case 0:
467				total_extracted += chunk_size;
468				remaining = chunk_size;
469				checksum = 1L;
470				for (;;) {
471					/* Write a funtion for this */
472					r = (CUDBG_BLOCK_SIZE < remaining) ?
473					    CUDBG_BLOCK_SIZE : remaining;
474					bytes_read =
475					read_from_buf(pc_buff->data,
476						      pc_buff->size,
477						      &pc_buff->offset, buffer,
478						      r);
479
480					if (bytes_read == 0)
481						return 0;
482
483					write_to_buf(pd_buff->data,
484						     pd_buff->size,
485						     &pd_buff->offset, buffer,
486						     bytes_read);
487					checksum = update_adler32(checksum,
488								  buffer,
489								  bytes_read);
490					remaining -= bytes_read;
491
492					/* verify everything is written
493					 * correctly */
494					if (checksum != chunk_checksum)
495						return
496						CUDBG_STATUS_CHKSUM_MISSMATCH;
497				}
498
499				break;
500
501				/* compressed using FastLZ */
502			case 1:
503				bytes_read = read_from_buf(pc_buff->data,
504							   pc_buff->size,
505							   &pc_buff->offset,
506							   compressed_buffer,
507							   chunk_size);
508
509				if (bytes_read == 0)
510					return 0;
511
512				checksum = update_adler32(1L, compressed_buffer,
513							  chunk_size);
514				total_extracted += chunk_extra;
515
516				/* verify that the chunk data is correct */
517				if (checksum != chunk_checksum) {
518					return CUDBG_STATUS_CHKSUM_MISSMATCH;
519				} else {
520					/* decompress and verify */
521					remaining =
522					fastlz_decompress(compressed_buffer,
523							  chunk_size,
524							  decompressed_buffer,
525							  chunk_extra);
526
527					if (remaining != chunk_extra) {
528						rc =
529						CUDBG_STATUS_DECOMPRESS_FAIL;
530						goto err;
531					} else {
532						write_to_buf(pd_buff->data,
533							     pd_buff->size,
534							     &pd_buff->offset,
535							     decompressed_buffer,
536							     chunk_extra);
537					}
538				}
539				break;
540
541			default:
542				break;
543			}
544
545		}
546
547	}
548
549err:
550	release_scratch_buff(&tmp_decompressed_buffer, pd_buff);
551err_dcbuff:
552	release_scratch_buff(&tmp_compressed_buffer, pd_buff);
553
554err_cbuff:
555	return rc;
556}
557