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 size_t capacity;
34 size_t capacity_read;
35 size_t 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 ssize_t read_fd(int fd, char *dst, int len)
44 {
45 size_t size = 0;
46 ssize_t 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 ssize_t write_fd(int fd, const void *data, size_t size)
63 {
64 ssize_t tot = 0;
65 ssize_t 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,size_t size)80 static ssize_t do_write(struct tracecmd_compression *handle,
81 const void *data, size_t 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,size_t size)94 static inline int buffer_extend(struct tracecmd_compression *handle, size_t size)
95 {
96 ssize_t 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,off_t offset,int whence)120 off_t tracecmd_compress_lseek(struct tracecmd_compression *handle, off_t offset, int whence)
121 {
122 unsigned long p;
123
124 if (!handle || !handle->buffer)
125 return (off_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 (off_t)-1;
139 }
140
141 if (buffer_extend(handle, p))
142 return (off_t)-1;
143
144 handle->pointer = p;
145
146 return p;
147 }
148
compress_read(struct tracecmd_compression * handle,char * dst,size_t len)149 static ssize_t compress_read(struct tracecmd_compression *handle, char *dst, size_t 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,size_t len,off_t offset)175 ssize_t tracecmd_compress_pread(struct tracecmd_compression *handle, char *dst, size_t len, off_t offset)
176 {
177 ssize_t 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,size_t len)198 ssize_t tracecmd_compress_buffer_read(struct tracecmd_compression *handle, char *dst, size_t len)
199 {
200 ssize_t 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,size_t size)365 int tracecmd_compress_buffer_write(struct tracecmd_compression *handle,
366 const void *data, size_t 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,size_t * read_size,size_t * write_size)669 int tracecmd_compress_copy_from(struct tracecmd_compression *handle, int fd, int chunk_size,
670 size_t *read_size, size_t *write_size)
671 {
672 size_t rchunk = 0;
673 size_t chunks = 0;
674 size_t rsize = 0;
675 size_t rmax = 0;
676 size_t csize;
677 size_t size;
678 size_t all;
679 size_t r;
680 off_t end_offset;
681 off_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 free(buf_from);
701 return -1;
702 }
703
704 /* save the initial offset and write 0 as initial chunk count */
705 offset = lseek(handle->fd, 0, SEEK_CUR);
706 write_fd(handle->fd, &chunks, 4);
707
708 do {
709 all = 0;
710 if (rmax > 0 && (rmax - rsize) < chunk_size)
711 rchunk = (rmax - rsize);
712 else
713 rchunk = chunk_size;
714
715 do {
716 r = read(fd, buf_from + all, rchunk - all);
717 if (r <= 0)
718 break;
719
720 all += r;
721 } while (all != rchunk);
722
723
724 if (r < 0 || (rmax > 0 && rsize >= rmax))
725 break;
726 rsize += all;
727 size = csize;
728 if (all > 0) {
729 ret = handle->proto->compress_block(handle->context,
730 buf_from, all, buf_to, size);
731 if (ret < 0) {
732 if (errno == EINTR)
733 continue;
734 break;
735 }
736 size = ret;
737 /* Write compressed data size */
738 endian4 = tep_read_number(handle->tep, &size, 4);
739 ret = write_fd(handle->fd, &endian4, 4);
740 if (ret != 4)
741 break;
742
743 /* Write uncompressed data size */
744 endian4 = tep_read_number(handle->tep, &all, 4);
745 ret = write_fd(handle->fd, &endian4, 4);
746 if (ret != 4)
747 break;
748
749 /* Write the compressed data */
750 ret = write_fd(handle->fd, buf_to, size);
751 if (ret != size)
752 break;
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 (lseek(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 end_offset = lseek(handle->fd, 0, SEEK_END);
770 if (end_offset == (off_t)-1)
771 return -1;
772
773 if (read_size)
774 *read_size = rsize;
775 if (write_size)
776 *write_size = end_offset - offset;
777 return 0;
778 }
779
780 /**
781 * tracecmd_load_chunks_info - Read compression chunks information from the file
782 * @handle: compression handle
783 * @chunks_info: return, array with compression chunks information
784 *
785 * This function reads information of all compression chunks in the current
786 * compression block from the file and fills that information in a newly
787 * allocated array @chunks_info which is returned.
788 *
789 * On success count of compression chunks is returned. Array of that count is
790 * allocated and returned in @chunks_info. Each entry describes one compression
791 * chunk. On error -1 is returned. In case of success, @chunks_info must be
792 * freed by free().
793 */
tracecmd_load_chunks_info(struct tracecmd_compression * handle,struct tracecmd_compress_chunk ** chunks_info)794 int tracecmd_load_chunks_info(struct tracecmd_compression *handle,
795 struct tracecmd_compress_chunk **chunks_info)
796 {
797 struct tracecmd_compress_chunk *chunks = NULL;
798 size_t size = 0;
799 unsigned int count = 0;
800 off_t offset;
801 int ret = -1;
802 char buf[4];
803 int i;
804
805 if (!handle)
806 return -1;
807
808 offset = lseek(handle->fd, 0, SEEK_CUR);
809 if (offset == (off_t)-1)
810 return -1;
811
812 if (read(handle->fd, buf, 4) != 4)
813 return -1;
814
815 count = tep_read_number(handle->tep, buf, 4);
816 if (!count) {
817 ret = 0;
818 goto out;
819 }
820
821 chunks = calloc(count, sizeof(struct tracecmd_compress_chunk));
822 if (!chunks)
823 goto out;
824
825 for (i = 0; i < count; i++) {
826 chunks[i].zoffset = lseek(handle->fd, 0, SEEK_CUR);
827 if (chunks[i].zoffset == (off_t)-1)
828 goto out;
829 if (read(handle->fd, buf, 4) != 4)
830 goto out;
831 chunks[i].zsize = tep_read_number(handle->tep, buf, 4);
832 chunks[i].offset = size;
833 if (read(handle->fd, buf, 4) != 4)
834 goto out;
835 chunks[i].size = tep_read_number(handle->tep, buf, 4);
836 size += chunks[i].size;
837 if (lseek(handle->fd, chunks[i].zsize, SEEK_CUR) == (off_t)-1)
838 goto out;
839 }
840
841 ret = count;
842 out:
843 if (lseek(handle->fd, offset, SEEK_SET) == (off_t)-1)
844 ret = -1;
845
846 if (ret > 0 && chunks_info)
847 *chunks_info = chunks;
848 else
849 free(chunks);
850
851 return ret;
852 }
853
854 /**
855 * tracecmd_uncompress_chunk - Uncompress given compression chunk.
856 * @handle: compression handle
857 * @chunk: chunk, that will be uncompressed in @data
858 * @data: Preallocated memory for uncompressed data. Must have enough space
859 * to hold the uncompressed data.
860 *
861 * This function uncompresses the chunk described by @chunk and stores
862 * the uncompressed data in the preallocated memory @data.
863 *
864 * On success 0 is returned and the uncompressed data is stored in @data.
865 * On error -1 is returned.
866 */
tracecmd_uncompress_chunk(struct tracecmd_compression * handle,struct tracecmd_compress_chunk * chunk,char * data)867 int tracecmd_uncompress_chunk(struct tracecmd_compression *handle,
868 struct tracecmd_compress_chunk *chunk, char *data)
869 {
870 char *bytes_in = NULL;
871 int ret = -1;
872
873 if (!handle || !handle->proto || !handle->proto->uncompress_block || !chunk || !data)
874 return -1;
875
876 if (lseek(handle->fd, chunk->zoffset + 8, SEEK_SET) == (off_t)-1)
877 return -1;
878
879 bytes_in = malloc(chunk->zsize);
880 if (!bytes_in)
881 return -1;
882
883 if (read_fd(handle->fd, bytes_in, chunk->zsize) < 0)
884 goto out;
885
886 if (handle->proto->uncompress_block(handle->context,
887 bytes_in, chunk->zsize, data, chunk->size) < 0)
888 goto out;
889
890 ret = 0;
891 out:
892 free(bytes_in);
893 return ret;
894 }
895
896 /**
897 * tracecmd_uncompress_copy_to - Uncompress data and copy to a file
898 * @handle: compression handle
899 * @fd: file descriptor to uncompressed data to copy into
900 * @read_size: return, size of the compressed data read from @handle
901 * @write_size: return, size of the uncompressed data written into @fd
902 *
903 * This function reads compressed data from the fd, associated with @handle,
904 * uncompresses it using the @handle compression context and writes
905 * the uncompressed data into the fd. The compressed data must be in the format:
906 * - 4 bytes, chunks count
907 * - for each chunk:
908 * - 4 bytes, size of compressed data in this chunk
909 * - 4 bytes, uncompressed size of the data in this chunk
910 * - data, bytes of <size of compressed data in this chunk>
911 *
912 * On success 0 is returned, @read_size and @write_size are updated with
913 * the size of read and written data.
914 */
tracecmd_uncompress_copy_to(struct tracecmd_compression * handle,int fd,size_t * read_size,size_t * write_size)915 int tracecmd_uncompress_copy_to(struct tracecmd_compression *handle, int fd,
916 size_t *read_size, size_t *write_size)
917 {
918 size_t s_uncompressed;
919 size_t s_compressed;
920 size_t rsize = 0;
921 size_t wsize = 0;
922 char *bytes_out = NULL;
923 char *bytes_in = NULL;
924 int size_out = 0;
925 int size_in = 0;
926 int chunks;
927 char buf[4];
928 char *tmp;
929 int ret;
930
931 if (!handle || !handle->proto || !handle->proto->uncompress_block)
932 return -1;
933
934 if (read(handle->fd, buf, 4) != 4)
935 return -1;
936
937 chunks = tep_read_number(handle->tep, buf, 4);
938 rsize += 4;
939
940 while (chunks) {
941 if (read(handle->fd, buf, 4) != 4)
942 break;
943
944 s_compressed = tep_read_number(handle->tep, buf, 4);
945 rsize += 4;
946 if (read(handle->fd, buf, 4) != 4)
947 break;
948
949 s_uncompressed = tep_read_number(handle->tep, buf, 4);
950 rsize += 4;
951 if (!bytes_in || size_in < s_compressed) {
952 tmp = realloc(bytes_in, s_compressed);
953 if (!tmp)
954 break;
955
956 bytes_in = tmp;
957 size_in = s_compressed;
958 }
959
960 if (!bytes_out || size_out < s_uncompressed) {
961 tmp = realloc(bytes_out, s_uncompressed);
962 if (!tmp)
963 break;
964 bytes_out = tmp;
965 size_out = s_uncompressed;
966 }
967
968 if (read_fd(handle->fd, bytes_in, s_compressed) < 0)
969 break;
970
971 rsize += s_compressed;
972 ret = handle->proto->uncompress_block(handle->context, bytes_in, s_compressed,
973 bytes_out, s_uncompressed);
974 if (ret < 0)
975 break;
976
977 write_fd(fd, bytes_out, ret);
978 wsize += ret;
979 chunks--;
980 }
981 free(bytes_in);
982 free(bytes_out);
983 if (chunks)
984 return -1;
985
986 if (read_size)
987 *read_size = rsize;
988 if (write_size)
989 *write_size = wsize;
990
991 return 0;
992 }
993