• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3  * Copyright (C) 2021, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
4  *
5  */
6 #include <stdlib.h>
7 #include <sys/time.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <unistd.h>
11 
12 #include "trace-cmd-private.h"
13 #include "trace-cmd-local.h"
14 
15 struct compress_proto {
16 	struct compress_proto *next;
17 	char *proto_name;
18 	char *proto_version;
19 	int weight;
20 
21 	int (*compress_block)(void *ctx, const void *in, int in_bytes, void *out, int out_bytes);
22 	int (*uncompress_block)(void *ctx, const void *in,  int in_bytes, void *out, int out_bytes);
23 	unsigned int (*compress_size)(void *ctx, unsigned int bytes);
24 	bool (*is_supported)(const char *name, const char *version);
25 	void *(*new_context)(void);
26 	void (*free_context)(void *ctx);
27 };
28 
29 static struct compress_proto *proto_list;
30 
31 struct tracecmd_compression {
32 	int				fd;
33 	unsigned int			capacity;
34 	unsigned int			capacity_read;
35 	unsigned long			pointer;
36 	char				*buffer;
37 	struct compress_proto		*proto;
38 	struct tep_handle		*tep;
39 	struct tracecmd_msg_handle	*msg_handle;
40 	void				*context;
41 };
42 
read_fd(int fd,char * dst,int len)43 static int read_fd(int fd, char *dst, int len)
44 {
45 	size_t size = 0;
46 	int r;
47 
48 	do {
49 		r = read(fd, dst+size, len);
50 		if (r > 0) {
51 			size += r;
52 			len -= r;
53 		} else
54 			break;
55 	} while (r > 0);
56 
57 	if (len)
58 		return -1;
59 	return size;
60 }
61 
write_fd(int fd,const void * data,size_t size)62 static long long write_fd(int fd, const void *data, size_t size)
63 {
64 	long long tot = 0;
65 	long long w;
66 
67 	do {
68 		w = write(fd, data + tot, size - tot);
69 		tot += w;
70 
71 		if (!w)
72 			break;
73 		if (w < 0)
74 			return w;
75 	} while (tot != size);
76 
77 	return tot;
78 }
79 
do_write(struct tracecmd_compression * handle,const void * data,unsigned long long size)80 static long long do_write(struct tracecmd_compression *handle,
81 			  const void *data, unsigned long long size)
82 {
83 	int ret;
84 
85 	if (handle->msg_handle) {
86 		ret = tracecmd_msg_data_send(handle->msg_handle, data, size);
87 		if (ret)
88 			return -1;
89 		return size;
90 	}
91 	return write_fd(handle->fd, data, size);
92 }
93 
buffer_extend(struct tracecmd_compression * handle,unsigned int size)94 static inline int buffer_extend(struct tracecmd_compression *handle, unsigned int size)
95 {
96 	int extend;
97 	char *buf;
98 
99 	if (size <= handle->capacity)
100 		return 0;
101 
102 	extend = (size / BUFSIZ + 1) * BUFSIZ;
103 	buf = realloc(handle->buffer, extend);
104 	if (!buf)
105 		return -1;
106 	handle->buffer = buf;
107 	handle->capacity = extend;
108 
109 	return 0;
110 }
111 
112 /**
113  * tracecmd_compress_lseek - Move the read/write pointer into the compression buffer
114  * @handle: compression handle
115  * @offset: number of bytes to move the pointer, can be negative or positive
116  * @whence: the starting position of the pointer movement,
117  *
118  * Returns the new file pointer on success, or -1 in case of an error.
119  */
tracecmd_compress_lseek(struct tracecmd_compression * handle,off64_t offset,int whence)120 off64_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off64_t offset, int whence)
121 {
122 	unsigned long p;
123 
124 	if (!handle || !handle->buffer)
125 		return (off64_t)-1;
126 
127 	switch (whence) {
128 	case SEEK_CUR:
129 		p = handle->pointer + offset;
130 		break;
131 	case SEEK_END:
132 		p = handle->capacity + offset;
133 		break;
134 	case SEEK_SET:
135 		p = offset;
136 		break;
137 	default:
138 		return (off64_t)-1;
139 	}
140 
141 	if (buffer_extend(handle, p))
142 		return (off64_t)-1;
143 
144 	handle->pointer = p;
145 
146 	return p;
147 }
148 
compress_read(struct tracecmd_compression * handle,char * dst,int len)149 static int compress_read(struct tracecmd_compression *handle, char *dst, int len)
150 {
151 
152 	if (handle->pointer > handle->capacity_read)
153 		return -1;
154 
155 	if (handle->pointer + len > handle->capacity_read)
156 		len = handle->capacity_read - handle->pointer;
157 
158 	memcpy(dst, handle->buffer + handle->pointer, len);
159 
160 	return len;
161 }
162 
163 /**
164  * tracecmd_compress_pread - pread() on compression buffer
165  * @handle: compression handle
166  * @dst: return, store the read data
167  * @len: length of data to be read
168  * @offset: offset in the buffer of data to be read
169  *
170  * Read a @len of data from the compression buffer at given @offset,
171  * without updating the buffer pointer.
172  *
173  * On success returns the number of bytes read, or -1 on failure.
174  */
tracecmd_compress_pread(struct tracecmd_compression * handle,char * dst,int len,off_t offset)175 int tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, int len, off_t offset)
176 {
177 	int ret;
178 
179 	if (!handle || !handle->buffer || offset > handle->capacity_read)
180 		return -1;
181 
182 	ret = tracecmd_compress_lseek(handle, offset, SEEK_SET);
183 	if (ret < 0)
184 		return ret;
185 	return compress_read(handle, dst, len);
186 }
187 
188 /**
189  * tracecmd_compress_buffer_read - read() from compression buffer
190  * @handle: compression handle
191  * @dst: return, store the read data
192  * @len: length of data to be read
193  *
194  * Read a @len of data from the compression buffer
195  *
196  * On success returns the number of bytes read, or -1 on failure.
197  */
tracecmd_compress_buffer_read(struct tracecmd_compression * handle,char * dst,int len)198 int tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, int len)
199 {
200 	int ret;
201 
202 	if (!handle || !handle->buffer)
203 		return -1;
204 
205 	ret = compress_read(handle, dst, len);
206 	if (ret > 0)
207 		handle->pointer += ret;
208 
209 	return ret;
210 }
211 
212 /**
213  * tracecmd_compress_reset - Reset the compression buffer
214  * @handle: compression handle
215  *
216  * Reset the compression buffer, any data currently in the buffer
217  * will be destroyed.
218  *
219  */
tracecmd_compress_reset(struct tracecmd_compression * handle)220 void tracecmd_compress_reset(struct tracecmd_compression *handle)
221 {
222 	if (!handle)
223 		return;
224 
225 	free(handle->buffer);
226 	handle->buffer = NULL;
227 	handle->pointer = 0;
228 	handle->capacity_read = 0;
229 	handle->capacity = 0;
230 }
231 
232 /**
233  * tracecmd_uncompress_block - uncompress a memory block
234  * @handle: compression handle
235  *
236  * Read compressed memory block from the file and uncompress it into
237  * internal buffer. The tracecmd_compress_buffer_read() can be used
238  * to read the uncompressed data from the buffer.
239  *
240  * Returns 0 on success, or -1 in case of an error.
241  */
tracecmd_uncompress_block(struct tracecmd_compression * handle)242 int tracecmd_uncompress_block(struct tracecmd_compression *handle)
243 {
244 	unsigned int s_uncompressed;
245 	unsigned int s_compressed;
246 	char *bytes = NULL;
247 	char buf[4];
248 	int size;
249 	int ret;
250 
251 	if (!handle || !handle->proto || !handle->proto->uncompress_block)
252 		return -1;
253 
254 	tracecmd_compress_reset(handle);
255 
256 	if (read(handle->fd, buf, 4) != 4)
257 		return -1;
258 
259 	s_compressed = tep_read_number(handle->tep, buf, 4);
260 	if (read(handle->fd, buf, 4) != 4)
261 		return -1;
262 
263 	s_uncompressed = tep_read_number(handle->tep, buf, 4);
264 	size = s_uncompressed > s_compressed ? s_uncompressed : s_compressed;
265 
266 	handle->buffer = malloc(size);
267 	if (!handle->buffer)
268 		return -1;
269 
270 	bytes = malloc(s_compressed);
271 	if (!bytes)
272 		goto error;
273 
274 	if (read_fd(handle->fd, bytes, s_compressed) < 0)
275 		goto error;
276 
277 	ret = handle->proto->uncompress_block(handle->context,
278 					      bytes, s_compressed, handle->buffer, size);
279 	if (ret < 0)
280 		goto error;
281 
282 	free(bytes);
283 	handle->pointer = 0;
284 	handle->capacity_read = ret;
285 	handle->capacity = size;
286 	return 0;
287 error:
288 	tracecmd_compress_reset(handle);
289 	free(bytes);
290 	return -1;
291 }
292 
293 /**
294  * tracecmd_compress_block - compress a memory block
295  * @handle: compression handle
296  *
297  * Compress the content of the internal memory buffer and write
298  * the compressed data in the file. The tracecmd_compress_buffer_write()
299  * can be used to write data into the internal memory buffer,
300  * before calling this API.
301  *
302  * Returns 0 on success, or -1 in case of an error.
303  */
tracecmd_compress_block(struct tracecmd_compression * handle)304 int tracecmd_compress_block(struct tracecmd_compression *handle)
305 {
306 	unsigned int size, real_size;
307 	char *buf;
308 	int endian4;
309 	int ret;
310 
311 	if (!handle || !handle->proto ||
312 	    !handle->proto->compress_size || !handle->proto->compress_block)
313 		return -1;
314 
315 	size = handle->proto->compress_size(handle->context, handle->pointer);
316 
317 	buf = malloc(size);
318 	if (!buf)
319 		return -1;
320 
321 	real_size = handle->proto->compress_block(handle->context, handle->buffer, handle->pointer, buf, size);
322 	if (real_size < 0) {
323 		ret = real_size;
324 		goto out;
325 	}
326 
327 	/* Write compressed data size */
328 	endian4 = tep_read_number(handle->tep, &real_size, 4);
329 	ret = do_write(handle, &endian4, 4);
330 	if (ret != 4)
331 		goto out;
332 
333 	/* Write uncompressed data size */
334 	endian4 = tep_read_number(handle->tep, &handle->pointer, 4);
335 	ret = do_write(handle, &endian4, 4);
336 	if (ret != 4) {
337 		ret = -1;
338 		goto out;
339 	}
340 
341 	/* Write compressed data */
342 	ret = do_write(handle, buf, real_size);
343 	if (ret != real_size) {
344 		ret = -1;
345 		goto out;
346 	}
347 
348 	ret = 0;
349 	tracecmd_compress_reset(handle);
350 out:
351 	free(buf);
352 	return ret;
353 }
354 
355 /**
356  * tracecmd_compress_buffer_write - write() to compression buffer
357  * @handle: compression handle
358  * @data: data to be written
359  * @size: size of @data
360  *
361  * Write @data of @size in the compression buffer
362  *
363  * Returns 0 on success, or -1 on failure.
364  */
tracecmd_compress_buffer_write(struct tracecmd_compression * handle,const void * data,unsigned long long size)365 int tracecmd_compress_buffer_write(struct tracecmd_compression *handle,
366 				   const void *data, unsigned long long size)
367 {
368 	if (!handle)
369 		return -1;
370 
371 	if (buffer_extend(handle, handle->pointer + size))
372 		return -1;
373 
374 	memcpy(&handle->buffer[handle->pointer], data, size);
375 	handle->pointer += size;
376 	if (handle->capacity_read < handle->pointer)
377 		handle->capacity_read = handle->pointer;
378 
379 	return 0;
380 }
381 
382 /**
383  * tracecmd_compress_init - initialize the library with available compression algorithms
384  */
tracecmd_compress_init(void)385 void tracecmd_compress_init(void)
386 {
387 	struct timeval time;
388 
389 	gettimeofday(&time, NULL);
390 	srand((time.tv_sec * 1000) + (time.tv_usec / 1000));
391 
392 #ifdef HAVE_ZLIB
393 	tracecmd_zlib_init();
394 #endif
395 	tracecmd_zstd_init();
396 }
397 
compress_proto_select(void)398 static struct compress_proto *compress_proto_select(void)
399 {
400 	struct compress_proto *proto = proto_list;
401 	struct compress_proto *selected = NULL;
402 
403 	while (proto) {
404 		if (!selected || selected->weight > proto->weight)
405 			selected = proto;
406 		proto = proto->next;
407 	}
408 
409 	return selected;
410 }
411 
412 /**
413  * tracecmd_compress_alloc - Allocate a new compression context
414  * @name: name of the compression algorithm.
415  *        If NULL - auto select the best available algorithm
416  * @version: version of the compression algorithm, can be NULL
417  * @fd: file descriptor for reading / writing data
418  * @tep: tep handle, used to encode the data
419  * @msg_handle: message handle, use it for reading / writing data instead of @fd
420  *
421  * Returns NULL on failure or pointer to allocated compression context.
422  * The returned context must be freed by tracecmd_compress_destroy()
423  */
tracecmd_compress_alloc(const char * name,const char * version,int fd,struct tep_handle * tep,struct tracecmd_msg_handle * msg_handle)424 struct tracecmd_compression *tracecmd_compress_alloc(const char *name, const char *version,
425 						     int fd, struct tep_handle *tep,
426 						     struct tracecmd_msg_handle *msg_handle)
427 {
428 	struct tracecmd_compression *new;
429 	struct compress_proto *proto;
430 
431 	if (name) {
432 		proto = proto_list;
433 		while (proto) {
434 			if (proto->is_supported && proto->is_supported(name, version))
435 				break;
436 			proto = proto->next;
437 		}
438 	} else {
439 		proto = compress_proto_select();
440 	}
441 	if (!proto)
442 		return NULL;
443 
444 	new = calloc(1, sizeof(*new));
445 	if (!new)
446 		return NULL;
447 
448 	new->fd = fd;
449 	new->tep = tep;
450 	new->msg_handle = msg_handle;
451 	new->proto = proto;
452 	if (proto->new_context)
453 		new->context = proto->new_context();
454 
455 	return new;
456 }
457 
458 /**
459  * tracecmd_compress_destroy - Free a compression context
460  * @handle: handle to the compression context that will be freed
461  */
tracecmd_compress_destroy(struct tracecmd_compression * handle)462 void tracecmd_compress_destroy(struct tracecmd_compression *handle)
463 {
464 	if (!handle)
465 		return;
466 
467 	tracecmd_compress_reset(handle);
468 
469 	if (handle->proto && handle->proto->free_context)
470 		handle->proto->free_context(handle->context);
471 
472 	free(handle);
473 }
474 
475 /**
476  * tracecmd_compress_is_supported - check if compression algorithm is supported
477  * @name: name of the compression algorithm.
478  * @version: version of the compression algorithm.
479  *
480  * Checks if compression algorithm with given name and version is supported.
481  * Returns true if the algorithm is supported or false if it is not.
482  */
tracecmd_compress_is_supported(const char * name,const char * version)483 bool tracecmd_compress_is_supported(const char *name, const char *version)
484 {
485 	struct compress_proto *proto = proto_list;
486 
487 	if (!name)
488 		return NULL;
489 
490 	while (proto) {
491 		if (proto->is_supported && proto->is_supported(name, version))
492 			return true;
493 		proto = proto->next;
494 	}
495 	return false;
496 }
497 
498 /**
499  * tracecmd_compress_proto_get_name - get name and version of compression algorithm
500  * @compress: compression handle.
501  * @name: return, name of the compression algorithm.
502  * @version: return, version of the compression algorithm.
503  *
504  * Returns 0 on success, or -1 in case of an error. If 0 is returned, the name
505  * and version of the algorithm are stored in @name and @version. The returned
506  * strings must *not* be freed.
507  */
tracecmd_compress_proto_get_name(struct tracecmd_compression * compress,const char ** name,const char ** version)508 int tracecmd_compress_proto_get_name(struct tracecmd_compression *compress,
509 				     const char **name, const char **version)
510 {
511 	if (!compress || !compress->proto)
512 		return -1;
513 
514 	if (name)
515 		*name = compress->proto->proto_name;
516 	if (version)
517 		*version = compress->proto->proto_version;
518 
519 	return 0;
520 }
521 
522 /**
523  * tracecmd_compress_proto_register - register a new compression algorithm
524  * @name: name of the compression algorithm.
525  * @version: version of the compression algorithm.
526  * @weight: weight of the compression algorithm, lower is better.
527  * @compress: compression hook, called to compress a memory block.
528  * @uncompress: uncompression hook, called to uncompress a memory block.
529  * @compress_size: hook, called to get the required minimum size of the buffer
530  *		   for compression given number of bytes.
531  * @is_supported: check hook, called to check if compression with given name and
532  *		   version is supported by this plugin.
533  *
534  * Returns 0 on success, or -1 in case of an error. If algorithm with given name
535  * and version is already registered, -1 is returned.
536  */
tracecmd_compress_proto_register(struct tracecmd_compression_proto * proto)537 int tracecmd_compress_proto_register(struct tracecmd_compression_proto *proto)
538 {
539 	struct compress_proto *new;
540 
541 	if (!proto || !proto->name || !proto->compress || !proto->uncompress)
542 		return -1;
543 
544 	if (tracecmd_compress_is_supported(proto->name, proto->version))
545 		return -1;
546 
547 	new = calloc(1, sizeof(*new));
548 	if (!new)
549 		return -1;
550 
551 	new->proto_name = strdup(proto->name);
552 	if (!new->proto_name)
553 		goto error;
554 
555 	new->proto_version = strdup(proto->version);
556 	if (!new->proto_version)
557 		goto error;
558 
559 	new->compress_block = proto->compress;
560 	new->uncompress_block = proto->uncompress;
561 	new->compress_size = proto->compress_size;
562 	new->is_supported = proto->is_supported;
563 	new->weight = proto->weight;
564 	new->next = proto_list;
565 	new->new_context = proto->new_context;
566 	new->free_context = proto->free_context;
567 	proto_list = new;
568 	return 0;
569 
570 error:
571 	free(new->proto_name);
572 	free(new->proto_version);
573 	free(new);
574 	return -1;
575 }
576 
577 /**
578  * tracecmd_compress_free - free the library resources, related to available compression algorithms
579  *
580  */
tracecmd_compress_free(void)581 void tracecmd_compress_free(void)
582 {
583 	struct compress_proto *proto = proto_list;
584 	struct compress_proto *del;
585 
586 	while (proto) {
587 		del = proto;
588 		proto = proto->next;
589 		free(del->proto_name);
590 		free(del->proto_version);
591 		free(del);
592 	}
593 	proto_list = NULL;
594 }
595 
596 /**
597  * tracecmd_compress_protos_get - get a list of all supported compression algorithms and versions
598  * @names: return, array with names of all supported compression algorithms
599  * @versions: return, array with versions of all supported compression algorithms
600  *
601  * On success, the size of @names and @versions arrays is returned.
602  * Those arrays are allocated by the API and must be freed with free() by the
603  * caller. Both arrays are with same size, each name from @names corresponds to
604  * a version from @versions. The last element in both arrays is a NULL pointer.
605  * On error -1 is returned and @names and @versions arrays are not allocated.
606  */
tracecmd_compress_protos_get(char *** names,char *** versions)607 int tracecmd_compress_protos_get(char ***names, char ***versions)
608 {
609 	struct compress_proto *proto = proto_list;
610 	char **n = NULL;
611 	char **v = NULL;
612 	int c, i;
613 
614 	for (c = 0; proto; proto = proto->next)
615 		c++;
616 
617 	if (c < 1)
618 		return c;
619 
620 	n = calloc(c + 1, sizeof(char *));
621 	if (!n)
622 		goto error;
623 	v = calloc(c + 1, sizeof(char *));
624 	if (!v)
625 		goto error;
626 
627 	proto = proto_list;
628 	for (i = 0; i < c && proto; i++) {
629 		n[i] = proto->proto_name;
630 		v[i] = proto->proto_version;
631 		proto = proto->next;
632 	}
633 
634 	n[i] = NULL;
635 	v[i] = NULL;
636 	*names = n;
637 	*versions = v;
638 	return c;
639 
640 error:
641 	free(n);
642 	free(v);
643 	return -1;
644 }
645 
646 /**
647  * tracecmd_compress_copy_from - Copy and compress data from a file
648  * @handle: compression handle
649  * @fd: file descriptor to uncompressed data to copy from
650  * @chunk_size: size of one compression chunk
651  * @read_size: Pointer to max bytes to read from. The pointer is updated
652  *	       with the actual size of compressed data read. If 0 is passed,
653  *	       read until the EOF is reached.
654  * @write_size: return, size of the compressed data written into @handle
655  *
656  * This function reads uncompressed data from given @fd, compresses the data
657  * using the @handle compression context and writes the compressed data into the
658  * fd associated with the @handle. The data is compressed on chunks with given
659  * @chunk_size size. The compressed data is written in the format:
660  *  - 4 bytes, chunks count
661  *  - for each chunk:
662  *    - 4 bytes, size of compressed data in this chunk
663  *    - 4 bytes, uncompressed size of the data in this chunk
664  *    - data, bytes of <size of compressed data in this chunk>
665  *
666  * On success 0 is returned, @read_size and @write_size are updated with the size of
667  * read and written data.
668  */
tracecmd_compress_copy_from(struct tracecmd_compression * handle,int fd,int chunk_size,unsigned long long * read_size,unsigned long long * write_size)669 int tracecmd_compress_copy_from(struct tracecmd_compression *handle, int fd, int chunk_size,
670 				unsigned long long *read_size, unsigned long long *write_size)
671 {
672 	unsigned int rchunk = 0;
673 	unsigned int chunks = 0;
674 	unsigned int wsize = 0;
675 	unsigned int rsize = 0;
676 	unsigned int rmax = 0;
677 	unsigned int csize;
678 	unsigned int size;
679 	unsigned int all;
680 	unsigned int r;
681 	off64_t offset;
682 	char *buf_from;
683 	char *buf_to;
684 	int endian4;
685 	int ret;
686 
687 	if (!handle || !handle->proto ||
688 	    !handle->proto->compress_block || !handle->proto->compress_size)
689 		return 0;
690 
691 	if (read_size)
692 		rmax = *read_size;
693 	csize = handle->proto->compress_size(handle->context, chunk_size);
694 	buf_from = malloc(chunk_size);
695 	if (!buf_from)
696 		return -1;
697 
698 	buf_to = malloc(csize);
699 	if (!buf_to)
700 		return -1;
701 
702 	/* save the initial offset and write 0 as initial chunk count */
703 	offset = lseek64(handle->fd, 0, SEEK_CUR);
704 	write_fd(handle->fd, &chunks, 4);
705 
706 	do {
707 		all = 0;
708 		if (rmax > 0 && (rmax - rsize) < chunk_size)
709 			rchunk = (rmax - rsize);
710 		else
711 			rchunk = chunk_size;
712 
713 		do {
714 			r = read(fd, buf_from + all, rchunk - all);
715 			all += r;
716 
717 			if (r <= 0)
718 				break;
719 		} while (all != rchunk);
720 
721 
722 		if (r < 0 || (rmax > 0 && rsize >= rmax))
723 			break;
724 		rsize += all;
725 		size = csize;
726 		if (all > 0) {
727 			ret = handle->proto->compress_block(handle->context,
728 							    buf_from, all, buf_to, size);
729 			if (ret < 0) {
730 				if (errno == EINTR)
731 					continue;
732 				break;
733 			}
734 			size = ret;
735 			/* Write compressed data size */
736 			endian4 = tep_read_number(handle->tep, &size, 4);
737 			ret = write_fd(handle->fd, &endian4, 4);
738 			if (ret != 4)
739 				break;
740 
741 			/* Write uncompressed data size */
742 			endian4 = tep_read_number(handle->tep, &all, 4);
743 			ret = write_fd(handle->fd, &endian4, 4);
744 			if (ret != 4)
745 				break;
746 
747 			/* Write the compressed data */
748 			ret = write_fd(handle->fd, buf_to, size);
749 			if (ret != size)
750 				break;
751 			/* data + compress header */
752 			wsize += (size + 8);
753 			chunks++;
754 		}
755 	} while (all > 0);
756 
757 	free(buf_from);
758 	free(buf_to);
759 
760 	if (all)
761 		return -1;
762 
763 	if (lseek64(handle->fd, offset, SEEK_SET) == (off_t)-1)
764 		return -1;
765 
766 	endian4 = tep_read_number(handle->tep, &chunks, 4);
767 	/* write chunks count*/
768 	write_fd(handle->fd, &chunks, 4);
769 	if (lseek64(handle->fd, 0, SEEK_END) == (off_t)-1)
770 		return -1;
771 
772 	if (read_size)
773 		*read_size = rsize;
774 	if (write_size)
775 		*write_size = wsize;
776 	return 0;
777 }
778 
779 /**
780  * tracecmd_load_chunks_info - Read compression chunks information from the file
781  * @handle: compression handle
782  * @chunks_info: return, array with compression chunks information
783  *
784  * This function reads information of all compression chunks in the current
785  * compression block from the file and fills that information in a newly
786  * allocated array @chunks_info which is returned.
787  *
788  * On success count of compression chunks is returned. Array of that count is
789  * allocated and returned in @chunks_info. Each entry describes one compression
790  * chunk. On error -1 is returned. In case of success, @chunks_info must be
791  * freed by free().
792  */
tracecmd_load_chunks_info(struct tracecmd_compression * handle,struct tracecmd_compress_chunk ** chunks_info)793 int tracecmd_load_chunks_info(struct tracecmd_compression *handle,
794 			      struct tracecmd_compress_chunk **chunks_info)
795 {
796 	struct tracecmd_compress_chunk *chunks = NULL;
797 	unsigned long long size = 0;
798 	unsigned int count = 0;
799 	off64_t offset;
800 	int ret = -1;
801 	char buf[4];
802 	int i;
803 
804 	if (!handle)
805 		return -1;
806 
807 	offset = lseek64(handle->fd, 0, SEEK_CUR);
808 	if (offset == (off64_t)-1)
809 		return -1;
810 
811 	if (read(handle->fd, buf, 4) != 4)
812 		return -1;
813 
814 	count = tep_read_number(handle->tep, buf, 4);
815 	if (!count) {
816 		ret = 0;
817 		goto out;
818 	}
819 
820 	chunks = calloc(count, sizeof(struct tracecmd_compress_chunk));
821 	if (!chunks)
822 		goto out;
823 
824 	for (i = 0; i < count; i++) {
825 		chunks[i].zoffset = lseek64(handle->fd, 0, SEEK_CUR);
826 		if (chunks[i].zoffset == (off_t)-1)
827 			goto out;
828 		if (read(handle->fd, buf, 4) != 4)
829 			goto out;
830 		chunks[i].zsize = tep_read_number(handle->tep, buf, 4);
831 		chunks[i].offset = size;
832 		if (read(handle->fd, buf, 4) != 4)
833 			goto out;
834 		chunks[i].size = tep_read_number(handle->tep, buf, 4);
835 		size += chunks[i].size;
836 		if (lseek64(handle->fd, chunks[i].zsize, SEEK_CUR) == (off64_t)-1)
837 			goto out;
838 	}
839 
840 	ret = count;
841 out:
842 	if (lseek64(handle->fd, offset, SEEK_SET) == (off64_t)-1)
843 		ret = -1;
844 
845 	if (ret > 0 && chunks_info)
846 		*chunks_info = chunks;
847 	else
848 		free(chunks);
849 
850 	return ret;
851 }
852 
853 /**
854  * tracecmd_uncompress_chunk - Uncompress given compression chunk.
855  * @handle: compression handle
856  * @chunk: chunk, that will be uncompressed in @data
857  * @data: Preallocated memory for uncompressed data. Must have enough space
858  * to hold the uncompressed data.
859  *
860  * This function uncompresses the chunk described by @chunk and stores
861  * the uncompressed data in the preallocated memory @data.
862  *
863  * On success 0 is returned and the uncompressed data is stored in @data.
864  * On error -1 is returned.
865  */
tracecmd_uncompress_chunk(struct tracecmd_compression * handle,struct tracecmd_compress_chunk * chunk,char * data)866 int tracecmd_uncompress_chunk(struct tracecmd_compression *handle,
867 			      struct tracecmd_compress_chunk *chunk, char *data)
868 {
869 	char *bytes_in = NULL;
870 	int ret = -1;
871 
872 	if (!handle || !handle->proto || !handle->proto->uncompress_block || !chunk || !data)
873 		return -1;
874 
875 	if (lseek64(handle->fd, chunk->zoffset + 8, SEEK_SET) == (off_t)-1)
876 		return -1;
877 
878 	bytes_in = malloc(chunk->zsize);
879 	if (!bytes_in)
880 		return -1;
881 
882 	if (read_fd(handle->fd, bytes_in, chunk->zsize) < 0)
883 		goto out;
884 
885 	if (handle->proto->uncompress_block(handle->context,
886 					    bytes_in, chunk->zsize, data, chunk->size) < 0)
887 		goto out;
888 
889 	ret = 0;
890 out:
891 	free(bytes_in);
892 	return ret;
893 }
894 
895 /**
896  * tracecmd_uncompress_copy_to - Uncompress data and copy to a file
897  * @handle: compression handle
898  * @fd: file descriptor to uncompressed data to copy into
899  * @read_size: return, size of the compressed data read from @handle
900  * @write_size: return, size of the uncompressed data written into @fd
901  *
902  * This function reads compressed data from the fd, associated with @handle,
903  * uncompresses it using the @handle compression context and writes
904  * the uncompressed data into the fd. The compressed data must be in the format:
905  *  - 4 bytes, chunks count
906  *  - for each chunk:
907  *    - 4 bytes, size of compressed data in this chunk
908  *    - 4 bytes, uncompressed size of the data in this chunk
909  *    - data, bytes of <size of compressed data in this chunk>
910  *
911  * On success 0 is returned, @read_size and @write_size are updated with
912  * the size of read and written data.
913  */
tracecmd_uncompress_copy_to(struct tracecmd_compression * handle,int fd,unsigned long long * read_size,unsigned long long * write_size)914 int tracecmd_uncompress_copy_to(struct tracecmd_compression *handle, int fd,
915 				unsigned long long *read_size, unsigned long long *write_size)
916 {
917 	unsigned int s_uncompressed;
918 	unsigned int s_compressed;
919 	unsigned int rsize = 0;
920 	unsigned int wsize = 0;
921 	char *bytes_out = NULL;
922 	char *bytes_in = NULL;
923 	int size_out = 0;
924 	int size_in = 0;
925 	int chunks;
926 	char buf[4];
927 	char *tmp;
928 	int ret;
929 
930 	if (!handle || !handle->proto || !handle->proto->uncompress_block)
931 		return -1;
932 
933 	if (read(handle->fd, buf, 4) != 4)
934 		return -1;
935 
936 	chunks = tep_read_number(handle->tep, buf, 4);
937 	rsize += 4;
938 
939 	while (chunks) {
940 		if (read(handle->fd, buf, 4) != 4)
941 			break;
942 
943 		s_compressed = tep_read_number(handle->tep, buf, 4);
944 		rsize += 4;
945 		if (read(handle->fd, buf, 4) != 4)
946 			break;
947 
948 		s_uncompressed = tep_read_number(handle->tep, buf, 4);
949 		rsize += 4;
950 		if (!bytes_in || size_in < s_compressed) {
951 			tmp = realloc(bytes_in, s_compressed);
952 			if (!tmp)
953 				break;
954 
955 			bytes_in = tmp;
956 			size_in = s_compressed;
957 		}
958 
959 		if (!bytes_out || size_out < s_uncompressed) {
960 			tmp = realloc(bytes_out, s_uncompressed);
961 			if (!tmp)
962 				break;
963 			bytes_out = tmp;
964 			size_out = s_uncompressed;
965 		}
966 
967 		if (read_fd(handle->fd, bytes_in, s_compressed) < 0)
968 			break;
969 
970 		rsize += s_compressed;
971 		ret = handle->proto->uncompress_block(handle->context, bytes_in, s_compressed,
972 						      bytes_out, s_uncompressed);
973 		if (ret < 0)
974 			break;
975 
976 		write_fd(fd, bytes_out, ret);
977 		wsize += ret;
978 		chunks--;
979 	}
980 	free(bytes_in);
981 	free(bytes_out);
982 	if (chunks)
983 		return -1;
984 
985 	if (read_size)
986 		*read_size = rsize;
987 	if (write_size)
988 		*write_size = wsize;
989 
990 	return 0;
991 }
992