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