GCC Code Coverage Report


Directory: src/gate/
File: src/gate/encode/tarstreams.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 271 353 76.8%
Functions: 19 20 95.0%
Branches: 94 201 46.8%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/encode/tarstreams.h"
30 #include "gate/results.h"
31 #include "gate/debugging.h"
32
33 /* TAR file format information taken from:
34 https://en.wikipedia.org/wiki/Tar_(computing)
35 https://www.gnu.org/software/tar/manual/html_node/Standard.html
36 */
37
38 typedef struct gate_tarheader
39 {
40 char filename[100];
41 char filemode[8];
42 char owner[8];
43 char group[8];
44 char size[12];
45 char modify_time[12];
46 char checksum[8];
47 char typeflag;
48 char linkname[100];
49 char magic_id[6];
50 char tar_version[2];
51 char ownername[32];
52 char groupname[32];
53 char device_major[8];
54 char device_minor[8];
55 char prefix[131];
56 char access_time[12];
57 char change_time[12];
58 } gate_tarheader_t;
59
60
61 #define GATE_TARHEADER_REGTYPE '0' /* regular file */
62 #define GATE_TARHEADER_AREGTYPE '\0' /* regular file */
63 #define GATE_TARHEADER_LNKTYPE '1' /* link */
64 #define GATE_TARHEADER_SYMTYPE '2' /* reserved */
65 #define GATE_TARHEADER_CHRTYPE '3' /* character special */
66 #define GATE_TARHEADER_BLKTYPE '4' /* block special */
67 #define GATE_TARHEADER_DIRTYPE '5' /* directory */
68 #define GATE_TARHEADER_FIFOTYPE '6' /* FIFO special */
69 #define GATE_TARHEADER_CONTTYPE '7' /* reserved */
70 #define GATE_TARHEADER_XGLTYPE 'g' /* Global extended header */
71 #define GATE_TARHEADER_XHDTYPE 'x' /* Extended header referring to the next file in the archive */
72
73 #define GATE_TARHEADER_LONGLINK 'L' /* Long-Link */
74
75 /* Bits used in the mode field, values in octal. */
76 #define GATE_TARHEADER_TSUID 04000 /* set UID on execution */
77 #define GATE_TARHEADER_TSGID 02000 /* set GID on execution */
78 #define GATE_TARHEADER_TSVTX 01000 /* reserved */
79 /* file permissions */
80 #define GATE_TARHEADER_TUREAD 00400 /* read by owner */
81 #define GATE_TARHEADER_TUWRITE 00200 /* write by owner */
82 #define GATE_TARHEADER_TUEXEC 00100 /* execute/search by owner */
83 #define GATE_TARHEADER_TGREAD 00040 /* read by group */
84 #define GATE_TARHEADER_TGWRITE 00020 /* write by group */
85 #define GATE_TARHEADER_TGEXEC 00010 /* execute/search by group */
86 #define GATE_TARHEADER_TOREAD 00004 /* read by other */
87 #define GATE_TARHEADER_TOWRITE 00002 /* write by other */
88 #define GATE_TARHEADER_TOEXEC 00001 /* execute/search by other */
89
90
91 typedef union gate_tarblock
92 {
93 char data[512];
94 gate_tarheader_t header;
95 } gate_tarblock_t;
96
97 28 static gate_uint64_t read_octal(char const* buffer, gate_size_t len)
98 {
99 28 gate_uint64_t ret = 0;
100 gate_size_t ndx;
101 28 --len;
102
103
2/2
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 24 times.
268 for (ndx = 0; ndx != len; ++ndx)
104 {
105 244 char const chr = buffer[ndx];
106
3/4
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 240 times.
✗ Branch 3 not taken.
244 if ((chr >= '0') && (chr <= '9'))
107 {
108 240 gate_uint8_t const digit = (gate_uint8_t)(chr - '0');
109 240 ret *= 8;
110 240 ret += digit;
111 }
112 else
113 {
114 break;
115 }
116 }
117 28 return ret;
118 }
119 16 static void write_octal(gate_uint64_t num, char* buffer, gate_size_t len)
120 {
121 gate_size_t ndx;
122
123 16 --len;
124 16 buffer[len] = 0;
125
126
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 16 times.
158 for (ndx = len; ndx > 0;)
127 {
128 142 gate_uint8_t const digit = (gate_uint8_t)(num % 8);
129 142 --ndx;
130 142 buffer[ndx] = '0' + digit;
131 142 num /= 8;
132 }
133 16 }
134
135 6 static gate_uint32_t gate_tarwriter_calculate_checksum(gate_tarblock_t* block)
136 {
137 gate_size_t ndx;
138 6 gate_uint32_t header_checksum = 0;
139
2/2
✓ Branch 0 taken 3072 times.
✓ Branch 1 taken 6 times.
3078 for (ndx = 0; ndx != sizeof(block->data); ++ndx)
140 {
141 3072 header_checksum += (gate_uint8_t)block->data[ndx];
142 }
143 6 return header_checksum & 0x1ffff;
144 }
145 4 static gate_uint32_t gate_tarwriter_calculate_checksum_alt(gate_tarblock_t* block)
146 {
147 gate_size_t ndx;
148 4 gate_int32_t header_checksum = 0;
149
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 4 times.
2052 for (ndx = 0; ndx != sizeof(block->data); ++ndx)
150 {
151 2048 header_checksum += (gate_int8_t)block->data[ndx];
152 }
153 4 return ((gate_uint32_t)header_checksum) & 0x1ffff;
154 }
155
156
157
158 2 static void gate_tarwriter_create_checksum(gate_tarblock_t* block)
159 {
160 gate_size_t ndx;
161 2 gate_uint32_t block_checksum = 0;
162
163
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 for (ndx = 0; ndx != sizeof(block->header.checksum); ++ndx)
164 {
165 /* checksum field needs to be filled with blank chars while the checksum is calculated */
166 16 block->header.checksum[ndx] = ' ';
167 }
168
169 2 block_checksum = gate_tarwriter_calculate_checksum(block);
170
171 2 write_octal(block_checksum, block->header.checksum, sizeof(block->header.checksum) - 1);
172 2 }
173
174 4 static gate_bool_t gate_tarreader_verify_checksum(gate_tarblock_t* block)
175 {
176 4 gate_size_t ndx = 0;
177 gate_uint32_t block_checksum;
178 gate_uint32_t block_checksum_alt;
179 4 gate_uint32_t read_checksum = (gate_uint32_t)read_octal(block->header.checksum, sizeof(block->header.checksum));
180
181
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (ndx = 0; ndx != sizeof(block->header.checksum); ++ndx)
182 {
183 /* checksum field needs to be filled with blank chars while the checksum is calculated */
184 32 block->header.checksum[ndx] = ' ';
185 }
186
187 4 block_checksum = gate_tarwriter_calculate_checksum(block);
188 4 block_checksum_alt = gate_tarwriter_calculate_checksum_alt(block);
189
190
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 return (read_checksum == block_checksum) || (read_checksum == block_checksum_alt);
191 }
192
193
194 static char const* const longlink_token = "././@LongLink";
195 static gate_size_t const longlink_token_len = 13;
196
197
198
199
200 1 gate_result_t gate_tarwriter_create(gate_tarwriter_t* writer, gate_stream_t* stream)
201 {
202 1 gate_result_t ret = GATE_RESULT_FAILED;
203 do
204 {
205 1 gate_mem_clear(writer, sizeof(gate_tarwriter_t));
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (stream == NULL)
207 {
208 ret = GATE_RESULT_INVALIDARG;
209 break;
210 }
211 1 writer->stream = stream;
212 1 gate_object_retain(writer->stream);
213
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_object_implements_interface((gate_object_t*)stream, GATE_INTERFACE_NAME_CONTROLSTREAM))
214 {
215 writer->control_stream = (gate_controlstream_t*)stream;
216 }
217 1 ret = GATE_RESULT_OK;
218 } while (0);
219 1 return ret;
220 }
221
222
223 static gate_result_t gate_tarwriter_add_long_name(gate_tarwriter_t* writer, char const* path, gate_size_t path_len, gate_tarblock_t const* src_block)
224 {
225 gate_result_t ret = GATE_RESULT_OK;
226 gate_tarblock_t block = GATE_INIT_EMPTY;
227 gate_size_t written = 0;
228 gate_size_t diff;
229
230 do
231 {
232 gate_mem_copy(&block, src_block, sizeof(block));
233 gate_mem_clear(&block.header.filename[longlink_token_len],
234 sizeof(block.header.filename) - longlink_token_len);
235 gate_mem_copy(block.header.filename, longlink_token, longlink_token_len);
236 block.header.typeflag = GATE_TARHEADER_LONGLINK;
237
238 write_octal(path_len, block.header.size, sizeof(block.header.size));
239
240 gate_tarwriter_create_checksum(&block);
241
242 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data), &written);
243 GATE_BREAK_IF_FAILED(ret);
244 writer->bytes_written += written;
245
246 ret = gate_stream_write_block(writer->stream, path, path_len, &written);
247 GATE_BREAK_IF_FAILED(ret);
248 writer->bytes_written += written;
249
250 diff = path_len % sizeof(block.data);
251 if (diff != 0)
252 {
253 gate_mem_clear(&block, sizeof(block));
254 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data) - diff, &written);
255 GATE_BREAK_IF_FAILED(ret);
256 writer->bytes_written += written;
257 }
258 } while (0);
259
260 return ret;
261 }
262
263 2 gate_result_t gate_tarwriter_add(gate_tarwriter_t* writer, gate_tarentry_t const* entry, gate_stream_t* stream)
264 {
265 2 gate_result_t ret = GATE_RESULT_FAILED;
266 2 gate_tarblock_t block = GATE_INIT_EMPTY;
267 2 gate_uint32_t tar_attribs = 0;
268 2 gate_int64_t unix_time = 0;
269 gate_result_t result;
270 /*gate_uint32_t header_checksum = 0;*/
271 gate_size_t len;
272 2 gate_size_t written = 0;
273 2 gate_uint64_t data_written = 0;
274 gate_size_t diff;
275 gate_size_t filename_len;
276 gate_size_t path_len;
277
278 do
279 {
280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (NULL == writer->stream)
281 {
282 ret = GATE_RESULT_NOTAVAILABLE;
283 break;
284 }
285
286 2 path_len = gate_str_length_max(entry->path, sizeof(entry->path));
287
288 2 filename_len = gate_str_print_text(block.header.filename, sizeof(block.header.filename),
289 2 entry->path, path_len);
290
291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TUREAD, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_OWNERREAD)));
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TUWRITE, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_OWNERWRITE)));
293
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TUEXEC, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_OWNEREXECUTE)));
294
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TGREAD, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_GROUPREAD)));
295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TGWRITE, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_GROUPWRITE)));
296
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TGEXEC, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_GROUPEXECUTE)));
297
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TOREAD, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_ALLREAD)));
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TOWRITE, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_ALLWRITE)));
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TOEXEC, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_ALLEXECUTE)));
300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TSUID, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_OWNERSETID)));
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TSGID, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_GROUPSETID)));
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(tar_attribs, GATE_TARHEADER_TSVTX, (GATE_FLAG_ENABLED(entry->access, GATE_FILEENTRY_ACCESS_NODELETE)));
303
304
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (tar_attribs == 0)
305 {
306 2 tar_attribs = 0600;
307 }
308
309 2 write_octal(tar_attribs, block.header.filemode, sizeof(block.header.filemode));
310 2 write_octal(entry->size, block.header.size, sizeof(block.header.size));
311
312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FLAG_ENABLED(entry->attribs, GATE_FILEENTRY_ATTRIB_DIRECTORY))
313 {
314 block.header.typeflag = GATE_TARHEADER_DIRTYPE;
315 if (block.header.filename[filename_len - 1] != '/')
316 {
317 if (filename_len < sizeof(block.header.filename) - 1)
318 {
319 block.header.filename[filename_len] = '/';
320 ++filename_len;
321 block.header.filename[filename_len] = 0;
322 }
323 }
324 }
325
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if (GATE_FLAG_ENABLED(entry->attribs, GATE_FILEENTRY_ATTRIB_FILE))
326 {
327 2 block.header.typeflag = GATE_TARHEADER_REGTYPE;
328 }
329 else
330 {
331 ret = GATE_RESULT_NOTSUPPORTED;
332 break;
333 }
334
335 2 gate_mem_copy(block.header.magic_id, "ustar\0", 6);
336 2 block.header.tar_version[0] = '0';
337 2 block.header.tar_version[1] = '0';
338
339 2 result = gate_time_to_unix(entry->time_modified, &unix_time);
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
341 {
342 unix_time = 0;
343 }
344 2 write_octal(unix_time, block.header.modify_time, sizeof(block.header.modify_time));
345 2 write_octal(unix_time, block.header.change_time, sizeof(block.header.change_time));
346
347 2 result = gate_time_to_unix(entry->time_accessed, &unix_time);
348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
349 {
350 unix_time = 0;
351 }
352 2 write_octal(unix_time, block.header.access_time, sizeof(block.header.access_time));
353
354 2 write_octal(0 /*entry->owner_id*/, block.header.owner, sizeof(block.header.owner));
355 2 write_octal(entry->group_id, block.header.group, sizeof(block.header.group));
356 2 len = gate_str_length_max(entry->owner_name, sizeof(entry->owner_name));
357 2 gate_str_print_text(block.header.ownername, sizeof(block.header.ownername),
358 2 entry->owner_name, len);
359
360 2 len = gate_str_length_max(entry->group_name, sizeof(entry->group_name));
361 2 gate_str_print_text(block.header.groupname, sizeof(block.header.groupname),
362 2 entry->group_name, len);
363
364 2 gate_tarwriter_create_checksum(&block);
365
366
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (path_len >= sizeof(block.header.filename))
368 {
369 /* current filename in header is truncated, we need to inject
370 longlink blocks before our datablock to address this issue
371 */
372 ret = gate_tarwriter_add_long_name(writer, entry->path, path_len, &block);
373 GATE_BREAK_IF_FAILED(ret);
374 }
375
376 2 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data), &written);
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
378 2 writer->bytes_written += written;
379
380
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if ((stream != NULL) && (entry->size > 0))
381 {
382 2 ret = gate_stream_transfer_limit(stream, entry->size, writer->stream, &data_written);
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (entry->size != data_written)
385 {
386 ret = GATE_RESULT_INVALIDOUTPUT;
387 break;
388 }
389 2 writer->bytes_written += data_written;
390
391 2 diff = (gate_size_t)(entry->size % sizeof(block.data));
392
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (diff != 0)
393 {
394 2 gate_mem_clear(&block.data, sizeof(block.data));
395 2 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data) - diff, &written);
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
397 2 writer->bytes_written += written;
398 }
399 }
400
401 } while (0);
402 2 return ret;
403 }
404 1 gate_result_t gate_tarwriter_add_data(gate_tarwriter_t* writer, gate_tarentry_t const* entry, void const* data)
405 {
406 1 gate_result_t ret = GATE_RESULT_FAILED;
407 gate_memstream_impl_t memstream;
408 1 gate_memstream_create_static_unmanaged_readonly(&memstream, data, (gate_size_t)entry->size, (gate_size_t)entry->size);
409 1 ret = gate_tarwriter_add(writer, entry, (gate_stream_t*)&memstream);
410 1 gate_object_release(&memstream);
411 1 return ret;
412 }
413 1 gate_result_t gate_tarwriter_flush(gate_tarwriter_t* writer)
414 {
415 gate_result_t ret;
416 1 gate_tarblock_t block = GATE_INIT_EMPTY;
417 gate_size_t written;
418
419 do
420 {
421 /* who empty blocks terminate the tar stream */
422 1 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data), &written);
423
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
424 1 writer->bytes_written += written;
425 1 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data), &written);
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
427 1 writer->bytes_written += written;
428
429 1 ret = gate_stream_flush(writer->stream);
430
431 1 gate_object_release(writer->stream);
432 1 writer->stream = NULL;
433 1 writer->control_stream = NULL;
434 } while (0);
435 1 return ret;
436 }
437 1 gate_result_t gate_tarwriter_destroy(gate_tarwriter_t* writer)
438 {
439 1 gate_result_t ret = GATE_RESULT_FAILED;
440
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (writer)
441 {
442
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (writer->stream)
443 {
444 gate_tarwriter_flush(writer);
445 if (writer->stream)
446 {
447 gate_object_release(writer->stream);
448 }
449 }
450
451 1 gate_mem_clear(writer, sizeof(gate_tarwriter_t));
452 1 ret = GATE_RESULT_OK;
453 }
454 1 return ret;
455 }
456
457
458
459 2 gate_result_t gate_tarreader_create(gate_tarreader_t* reader, gate_stream_t* stream)
460 {
461 2 gate_result_t ret = GATE_RESULT_FAILED;
462 do
463 {
464 2 gate_mem_clear(reader, sizeof(gate_tarreader_t));
465
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!stream)
466 {
467 ret = GATE_RESULT_INVALIDARG;
468 break;
469 }
470 2 reader->stream = stream;
471 2 gate_object_retain(reader->stream);
472
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (gate_object_implements_interface((gate_object_t*)reader->stream, GATE_INTERFACE_NAME_CONTROLSTREAM))
473 {
474 1 reader->control_stream = (gate_controlstream_t*)reader->stream;
475 }
476 2 reader->bytes_received = 0;
477 2 reader->bytes_written = 0;
478 2 ret = GATE_RESULT_OK;
479 } while (0);
480
481 2 return ret;
482 }
483
484 1 gate_result_t gate_tarreader_get_first_entry(gate_tarreader_t* reader, gate_tarentry_t* ptr_entry)
485 {
486 gate_result_t result;
487
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (reader->control_stream)
488 {
489 1 result = gate_stream_seek(reader->control_stream, 0, GATE_STREAM_SEEK_BEGIN, NULL);
490
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(result))
491 {
492 1 result = gate_tarreader_get_next_entry(reader, ptr_entry);
493 }
494 }
495 else
496 {
497 result = GATE_RESULT_NOTSUPPORTED;
498 }
499 1 return result;
500 }
501
502 5 static gate_bool_t gate_tarreader_is_empty_block(gate_tarblock_t const* block)
503 {
504 5 gate_size_t len = sizeof(block->data);
505 5 char const* ptr = &block->data[0];
506
507
2/2
✓ Branch 0 taken 516 times.
✓ Branch 1 taken 1 times.
517 while (len-- != 0)
508 {
509
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 512 times.
516 if (*ptr != 0) return false;
510 512 ++ptr;
511 }
512 1 return true;
513 }
514
515
516 5 gate_result_t gate_tarreader_get_next_entry(gate_tarreader_t* reader, gate_tarentry_t* ptr_entry)
517 {
518 5 gate_result_t ret = GATE_RESULT_FAILED;
519 5 gate_tarblock_t block = GATE_INIT_EMPTY;
520 5 gate_size_t returned = 0;
521 gate_size_t len;
522 5 gate_strbuilder_t path_builder = GATE_INIT_EMPTY;
523 5 gate_uint32_t longlink_len = 0;
524 5 gate_size_t longlink_block_count = 0;
525 gate_size_t ndx;
526 gate_size_t next_len;
527 5 gate_uint32_t tar_attribs = 0;
528 5 gate_uint64_t unix_time = 0;
529 gate_size_t diff;
530
531 do
532 {
533 5 gate_mem_clear(ptr_entry, sizeof(gate_tarentry_t));
534
535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (reader->next_block != 0)
536 {
537 /* there are content bytes to be accessed */
538 ret = GATE_RESULT_INVALIDSTATE;
539 break;
540 }
541
542 5 ret = gate_stream_read_block(reader->stream, block.data, sizeof(block.data), &returned);
543
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 GATE_BREAK_IF_FAILED(ret);
544
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (returned == 0)
546 {
547 /* end of stream */
548 ret = GATE_RESULT_ENDOFSTREAM;
549 break;
550 }
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (returned < sizeof(block.data))
552 {
553 /* stream not complete, cannot read one full header entry */
554 ret = GATE_RESULT_INVALIDINPUT;
555 break;
556 }
557
558
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 if (gate_tarreader_is_empty_block(&block))
559 {
560 /* end of stream */
561 1 ret = GATE_RESULT_ENDOFSTREAM;
562 1 break;
563 }
564
565
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!gate_tarreader_verify_checksum(&block))
566 {
567 ret = GATE_RESULT_INVALIDHEADER;
568 break;
569 }
570
571 4 gate_strbuilder_create_static(&path_builder, ptr_entry->path, sizeof(ptr_entry->path), 0);
572
573 4 len = gate_str_length_max(block.header.filename, sizeof(block.header.filename));
574
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if ((0 == gate_str_compare(block.header.filename, len, longlink_token, longlink_token_len))
575 && (block.header.typeflag == GATE_TARHEADER_LONGLINK))
576 {
577 /* longlink detected */
578 longlink_len = (gate_uint32_t)read_octal(block.header.size, sizeof(block.header.size));
579 longlink_block_count = longlink_len / 512;
580 if ((longlink_len % 512) != 0)
581 {
582 ++longlink_block_count;
583 }
584 for (ndx = 0; ndx != longlink_block_count; ++ndx)
585 {
586 next_len = (longlink_len >= sizeof(block.data)) ? sizeof(block.data) : longlink_len;
587 ret = gate_stream_read_block(reader->stream, block.data, sizeof(block.data), &returned);
588 GATE_BREAK_IF_FAILED(ret);
589 gate_strbuilder_append_text(&path_builder, block.data, next_len);
590 longlink_len -= (gate_uint32_t)next_len;
591 }
592 GATE_BREAK_IF_FAILED(ret);
593
594 /* now read the real header block */
595 ret = gate_stream_read_block(reader->stream, block.data, sizeof(block.data), &returned);
596 GATE_BREAK_IF_FAILED(ret);
597 }
598 else
599 {
600 /* just extract the filename from the header */
601 4 gate_strbuilder_append_text(&path_builder, block.header.filename, gate_str_length(block.header.filename));
602 }
603 4 tar_attribs = (gate_uint32_t)read_octal(block.header.filemode, sizeof(block.header.filemode));
604
605 4 ptr_entry->attribs = 0;
606 4 ptr_entry->access = 0;
607
608
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNERREAD, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUREAD));
609
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNERWRITE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUWRITE));
610
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNEREXECUTE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUEXEC));
611
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPREAD, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TGREAD));
612
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPWRITE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUWRITE));
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPEXECUTE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUEXEC));
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_ALLREAD, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TOREAD));
615
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_ALLWRITE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TOWRITE));
616
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_ALLEXECUTE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TOEXEC));
617
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNERSETID, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TSUID));
618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPSETID, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TSGID));
619
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_NODELETE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TSVTX));
620
621
1/3
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
4 switch (block.header.typeflag)
622 {
623 4 case GATE_TARHEADER_AREGTYPE:
624 case GATE_TARHEADER_REGTYPE:
625 4 ptr_entry->attribs |= GATE_FILEENTRY_ATTRIB_FILE;
626 4 break;
627 case GATE_TARHEADER_DIRTYPE:
628 ptr_entry->attribs |= GATE_FILEENTRY_ATTRIB_DIRECTORY;
629 break;
630 default:
631 ret = GATE_RESULT_NOTSUPPORTED;
632 break;
633 }
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
635
636 4 reader->next_content_length = read_octal(block.header.size, sizeof(block.header.size));
637 4 reader->next_block = reader->next_content_length;
638 4 diff = (gate_size_t)(reader->next_block % 512U);
639
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (diff != 0)
640 {
641 4 reader->next_block += (512U - diff);
642 }
643 4 ptr_entry->size = reader->next_content_length;
644 4 ptr_entry->owner_id = (gate_uint32_t)read_octal(block.header.owner, sizeof(block.header.owner));
645 4 ptr_entry->group_id = (gate_uint32_t)read_octal(block.header.group, sizeof(block.header.group));
646
647 4 unix_time = read_octal(block.header.modify_time, sizeof(block.header.modify_time));
648 4 gate_time_from_unix((gate_int64_t)unix_time, &ptr_entry->time_modified);
649
650 4 unix_time = read_octal(block.header.access_time, sizeof(block.header.access_time));
651 4 gate_time_from_unix((gate_int64_t)unix_time, &ptr_entry->time_accessed);
652
653 4 gate_str_print_text(ptr_entry->owner_name, sizeof(ptr_entry->owner_name),
654 block.header.ownername, gate_str_length_max(block.header.ownername, sizeof(block.header.ownername)));
655
656 4 gate_str_print_text(ptr_entry->group_name, sizeof(ptr_entry->group_name),
657 block.header.groupname, gate_str_length_max(block.header.groupname, sizeof(block.header.groupname)));
658 4 ret = GATE_RESULT_OK;
659 } while (0);
660
661 5 return ret;
662 }
663 1 gate_result_t gate_tarreader_find_entry(gate_tarreader_t* reader, gate_string_t const* path, gate_tarentry_t* ptr_entry)
664 {
665 1 gate_result_t result = GATE_RESULT_OK;
666 gate_tarentry_t entry;
667
668 for (;;)
669 {
670 1 result = gate_tarreader_get_next_entry(reader, &entry);
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(result);
672
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (gate_string_equals_str(path, entry.path))
673 {
674
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ptr_entry)
675 {
676 1 gate_mem_copy(ptr_entry, &entry, sizeof(entry));
677 }
678 1 result = GATE_RESULT_OK;
679 1 break;
680 }
681 result = gate_tarreader_skip_content(reader, NULL);
682 GATE_BREAK_IF_FAILED(result);
683 }
684 1 return result;
685 }
686 3 gate_result_t gate_tarreader_extract_content(gate_tarreader_t* reader, gate_stream_t* output_target, gate_uint64_t* bytes_transferred)
687 {
688 3 gate_result_t ret = GATE_RESULT_OK;
689 3 gate_uint64_t exported_bytes = 0;
690
691 do
692 {
693
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (reader->next_content_length != 0)
694 {
695 3 ret = gate_stream_transfer_limit(reader->stream, reader->next_content_length, output_target, &exported_bytes);
696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
697
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (exported_bytes < reader->next_content_length)
698 {
699 ret = GATE_RESULT_INVALIDINPUT;
700 break;
701 }
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_DEBUG_ASSERT(reader->next_content_length == exported_bytes);
703 3 reader->next_content_length = 0;
704
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_DEBUG_ASSERT(reader->next_block >= exported_bytes);
705 3 reader->next_block -= exported_bytes;
706 }
707
708
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (reader->next_block != 0)
709 {
710 3 ret = gate_stream_skip(reader->stream, reader->next_block, NULL);
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
712 3 reader->next_block = 0;
713 }
714 } while (0);
715
716
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (bytes_transferred)
717 {
718 3 *bytes_transferred = exported_bytes;
719 }
720
721 3 return ret;
722 }
723 1 gate_result_t gate_tarreader_skip_content(gate_tarreader_t* reader, gate_uint64_t* optional_bytes_skipped)
724 {
725 1 gate_result_t ret = GATE_RESULT_OK;
726 do
727 {
728
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (reader->next_block != 0)
729 {
730 1 ret = gate_stream_skip(reader->stream, reader->next_block, optional_bytes_skipped);
731
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
732 1 reader->next_block = 0;
733 }
734 else
735 {
736 if (optional_bytes_skipped)
737 {
738 *optional_bytes_skipped = 0;
739 }
740 }
741 1 reader->next_content_length = 0;
742 1 reader->next_block = 0;
743 } while (0);
744 1 return ret;
745 }
746
747 2 gate_result_t gate_tarreader_destroy(gate_tarreader_t* reader)
748 {
749 2 gate_result_t ret = GATE_RESULT_OK;
750
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (reader->stream)
751 {
752 2 gate_object_release(reader->stream);
753 }
754 2 gate_mem_clear(reader, sizeof(gate_tarreader_t));
755 2 return ret;
756 }
757
758