GCC Code Coverage Report


Directory: src/gate/
File: src/gate/encode/tarstreams.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 245 353 69.4%
Functions: 16 20 80.0%
Branches: 84 197 42.6%

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 14 static gate_uint64_t read_octal(char const* buffer, gate_size_t len)
98 {
99 14 gate_uint64_t ret = 0;
100 gate_size_t ndx;
101 gate_uint8_t digit;
102 char chr;
103 14 --len;
104
105
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 12 times.
134 for (ndx = 0; ndx != len; ++ndx)
106 {
107 122 chr = buffer[ndx];
108
3/4
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
122 if ((chr >= '0') && (chr <= '9'))
109 {
110 120 digit = (gate_uint8_t)(chr - '0');
111 120 ret *= 8;
112 120 ret += digit;
113 }
114 else
115 {
116 break;
117 }
118 }
119 14 return ret;
120 }
121 16 static void write_octal(gate_uint64_t num, char* buffer, gate_size_t len)
122 {
123 gate_uint8_t digit;
124 gate_size_t ndx;
125
126 16 --len;
127 16 buffer[len] = 0;
128
129
2/2
✓ Branch 0 taken 142 times.
✓ Branch 1 taken 16 times.
158 for (ndx = len; ndx > 0;)
130 {
131 142 --ndx;
132 142 digit = (gate_uint8_t)(num % 8);
133 142 buffer[ndx] = '0' + digit;
134 142 num /= 8;
135 }
136 16 }
137
138 4 static gate_uint32_t gate_tarwriter_calculate_checksum(gate_tarblock_t* block)
139 {
140 gate_size_t ndx;
141 4 gate_uint32_t header_checksum = 0;
142
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 4 times.
2052 for (ndx = 0; ndx != sizeof(block->data); ++ndx)
143 {
144 2048 header_checksum += (gate_uint8_t)block->data[ndx];
145 }
146 4 return header_checksum & 0x1ffff;
147 }
148 2 static gate_uint32_t gate_tarwriter_calculate_checksum_alt(gate_tarblock_t* block)
149 {
150 gate_size_t ndx;
151 2 gate_int32_t header_checksum = 0;
152
2/2
✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 2 times.
1026 for (ndx = 0; ndx != sizeof(block->data); ++ndx)
153 {
154 1024 header_checksum += (gate_int8_t)block->data[ndx];
155 }
156 2 return ((gate_uint32_t)header_checksum) & 0x1ffff;
157 }
158
159
160
161 2 static void gate_tarwriter_create_checksum(gate_tarblock_t* block)
162 {
163 gate_size_t ndx;
164 2 gate_uint32_t block_checksum = 0;
165
166
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 for (ndx = 0; ndx != sizeof(block->header.checksum); ++ndx)
167 {
168 /* checksum field needs to be filled with blank chars while the checksum is calculated */
169 16 block->header.checksum[ndx] = ' ';
170 }
171
172 2 block_checksum = gate_tarwriter_calculate_checksum(block);
173
174 2 write_octal(block_checksum, block->header.checksum, sizeof(block->header.checksum) - 1);
175 2 }
176
177 2 static gate_bool_t gate_tarreader_verify_checksum(gate_tarblock_t* block)
178 {
179 2 gate_size_t ndx = 0;
180 gate_uint32_t block_checksum;
181 gate_uint32_t block_checksum_alt;
182 2 gate_uint32_t read_checksum = (gate_uint32_t)read_octal(block->header.checksum, sizeof(block->header.checksum));
183
184
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
18 for (ndx = 0; ndx != sizeof(block->header.checksum); ++ndx)
185 {
186 /* checksum field needs to be filled with blank chars while the checksum is calculated */
187 16 block->header.checksum[ndx] = ' ';
188 }
189
190 2 block_checksum = gate_tarwriter_calculate_checksum(block);
191 2 block_checksum_alt = gate_tarwriter_calculate_checksum_alt(block);
192
193
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 return (read_checksum == block_checksum) || (read_checksum == block_checksum_alt);
194 }
195
196
197 static char const* const longlink_token = "././@LongLink";
198 static gate_size_t const longlink_token_len = 13;
199
200
201
202
203 1 gate_result_t gate_tarwriter_create(gate_tarwriter_t* writer, gate_stream_t* stream)
204 {
205 1 gate_result_t ret = GATE_RESULT_FAILED;
206 do
207 {
208 1 gate_mem_clear(writer, sizeof(gate_tarwriter_t));
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (stream == NULL)
210 {
211 ret = GATE_RESULT_INVALIDARG;
212 break;
213 }
214 1 writer->stream = stream;
215 1 gate_object_retain(writer->stream);
216
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))
217 {
218 writer->control_stream = (gate_controlstream_t*)stream;
219 }
220 1 ret = GATE_RESULT_OK;
221 } while (0);
222 1 return ret;
223 }
224
225
226 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)
227 {
228 gate_result_t ret = GATE_RESULT_OK;
229 gate_tarblock_t block = GATE_INIT_EMPTY;
230 gate_size_t written = 0;
231 gate_size_t diff;
232
233 do
234 {
235 gate_mem_copy(&block, src_block, sizeof(block));
236 gate_mem_clear(&block.header.filename[longlink_token_len],
237 sizeof(block.header.filename) - longlink_token_len);
238 gate_mem_copy(block.header.filename, longlink_token, longlink_token_len);
239 block.header.typeflag = GATE_TARHEADER_LONGLINK;
240
241 write_octal(path_len, block.header.size, sizeof(block.header.size));
242
243 gate_tarwriter_create_checksum(&block);
244
245 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data), &written);
246 GATE_BREAK_IF_FAILED(ret);
247 writer->bytes_written += written;
248
249 ret = gate_stream_write_block(writer->stream, path, path_len, &written);
250 GATE_BREAK_IF_FAILED(ret);
251 writer->bytes_written += written;
252
253 diff = path_len % sizeof(block.data);
254 if (diff != 0)
255 {
256 gate_mem_clear(&block, sizeof(block));
257 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data) - diff, &written);
258 GATE_BREAK_IF_FAILED(ret);
259 writer->bytes_written += written;
260 }
261 } while (0);
262
263 return ret;
264 }
265
266 2 gate_result_t gate_tarwriter_add(gate_tarwriter_t* writer, gate_tarentry_t const* entry, gate_stream_t* stream)
267 {
268 2 gate_result_t ret = GATE_RESULT_FAILED;
269 2 gate_tarblock_t block = GATE_INIT_EMPTY;
270 2 gate_uint32_t tar_attribs = 0;
271 2 gate_int64_t unix_time = 0;
272 gate_result_t result;
273 /*gate_uint32_t header_checksum = 0;*/
274 gate_size_t len;
275 2 gate_size_t written = 0;
276 2 gate_uint64_t data_written = 0;
277 gate_size_t diff;
278 gate_size_t filename_len;
279 gate_size_t path_len;
280
281 do
282 {
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (NULL == writer->stream)
284 {
285 ret = GATE_RESULT_NOTAVAILABLE;
286 break;
287 }
288
289 2 path_len = gate_str_length_max(entry->path, sizeof(entry->path));
290
291 2 filename_len = gate_str_print_text(block.header.filename, sizeof(block.header.filename),
292 2 entry->path, path_len);
293
294
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)));
295
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)));
296
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)));
297
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)));
298
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)));
299
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)));
300
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)));
301
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)));
302
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)));
303
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)));
304
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)));
305
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)));
306
307
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (tar_attribs == 0)
308 {
309 2 tar_attribs = 0600;
310 }
311
312 2 write_octal(tar_attribs, block.header.filemode, sizeof(block.header.filemode));
313 2 write_octal(entry->size, block.header.size, sizeof(block.header.size));
314
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FLAG_ENABLED(entry->attribs, GATE_FILEENTRY_ATTRIB_DIRECTORY))
316 {
317 block.header.typeflag = GATE_TARHEADER_DIRTYPE;
318 if (block.header.filename[filename_len - 1] != '/')
319 {
320 if (filename_len < sizeof(block.header.filename) - 1)
321 {
322 block.header.filename[filename_len] = '/';
323 ++filename_len;
324 block.header.filename[filename_len] = 0;
325 }
326 }
327 }
328
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if (GATE_FLAG_ENABLED(entry->attribs, GATE_FILEENTRY_ATTRIB_FILE))
329 {
330 2 block.header.typeflag = GATE_TARHEADER_REGTYPE;
331 }
332 else
333 {
334 ret = GATE_RESULT_NOTSUPPORTED;
335 break;
336 }
337
338 2 gate_mem_copy(block.header.magic_id, "ustar\0", 6);
339 2 block.header.tar_version[0] = '0';
340 2 block.header.tar_version[1] = '0';
341
342 2 result = gate_time_to_unix(entry->time_modified, &unix_time);
343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
344 {
345 unix_time = 0;
346 }
347 2 write_octal(unix_time, block.header.modify_time, sizeof(block.header.modify_time));
348 2 write_octal(unix_time, block.header.change_time, sizeof(block.header.change_time));
349
350 2 result = gate_time_to_unix(entry->time_accessed, &unix_time);
351
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
352 {
353 unix_time = 0;
354 }
355 2 write_octal(unix_time, block.header.access_time, sizeof(block.header.access_time));
356
357 2 write_octal(0 /*entry->owner_id*/, block.header.owner, sizeof(block.header.owner));
358 2 write_octal(entry->group_id, block.header.group, sizeof(block.header.group));
359 2 len = gate_str_length_max(entry->owner_name, sizeof(entry->owner_name));
360 2 gate_str_print_text(block.header.ownername, sizeof(block.header.ownername),
361 2 entry->owner_name, len);
362
363 2 len = gate_str_length_max(entry->group_name, sizeof(entry->group_name));
364 2 gate_str_print_text(block.header.groupname, sizeof(block.header.groupname),
365 2 entry->group_name, len);
366
367 2 gate_tarwriter_create_checksum(&block);
368
369
370
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (path_len >= sizeof(block.header.filename))
371 {
372 /* current filename in header is truncated, we need to inject
373 longlink blocks before our datablock to address this issue
374 */
375 ret = gate_tarwriter_add_long_name(writer, entry->path, path_len, &block);
376 GATE_BREAK_IF_FAILED(ret);
377 }
378
379 2 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data), &written);
380
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
381 2 writer->bytes_written += written;
382
383
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))
384 {
385 2 ret = gate_stream_transfer_limit(stream, entry->size, writer->stream, &data_written);
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
387
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (entry->size != data_written)
388 {
389 ret = GATE_RESULT_INVALIDOUTPUT;
390 break;
391 }
392 2 writer->bytes_written += data_written;
393
394 2 diff = (gate_size_t)(entry->size % sizeof(block.data));
395
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (diff != 0)
396 {
397 2 gate_mem_clear(&block.data, sizeof(block.data));
398 2 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data) - diff, &written);
399
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
400 2 writer->bytes_written += written;
401 }
402 }
403
404 } while (0);
405 2 return ret;
406 }
407 1 gate_result_t gate_tarwriter_add_data(gate_tarwriter_t* writer, gate_tarentry_t const* entry, void const* data)
408 {
409 1 gate_result_t ret = GATE_RESULT_FAILED;
410 gate_memstream_impl_t memstream;
411 1 gate_memstream_create_static_unmanaged_readonly(&memstream, data, (gate_size_t)entry->size, (gate_size_t)entry->size);
412 1 ret = gate_tarwriter_add(writer, entry, (gate_stream_t*)&memstream);
413 1 gate_object_release(&memstream);
414 1 return ret;
415 }
416 1 gate_result_t gate_tarwriter_flush(gate_tarwriter_t* writer)
417 {
418 gate_result_t ret;
419 1 gate_tarblock_t block = GATE_INIT_EMPTY;
420 gate_size_t written;
421
422 do
423 {
424 /* who empty blocks terminate the tar stream */
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 1 ret = gate_stream_write_block(writer->stream, block.data, sizeof(block.data), &written);
429
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
430 1 writer->bytes_written += written;
431
432 1 ret = gate_stream_flush(writer->stream);
433
434 1 gate_object_release(writer->stream);
435 1 writer->stream = NULL;
436 1 writer->control_stream = NULL;
437 } while (0);
438 1 return ret;
439 }
440 1 gate_result_t gate_tarwriter_destroy(gate_tarwriter_t* writer)
441 {
442 1 gate_result_t ret = GATE_RESULT_FAILED;
443
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (writer)
444 {
445
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (writer->stream)
446 {
447 gate_tarwriter_flush(writer);
448 if (writer->stream)
449 {
450 gate_object_release(writer->stream);
451 }
452 }
453
454 1 gate_mem_clear(writer, sizeof(gate_tarwriter_t));
455 1 ret = GATE_RESULT_OK;
456 }
457 1 return ret;
458 }
459
460
461
462 1 gate_result_t gate_tarreader_create(gate_tarreader_t* reader, gate_stream_t* stream)
463 {
464 1 gate_result_t ret = GATE_RESULT_FAILED;
465 do
466 {
467 1 gate_mem_clear(reader, sizeof(gate_tarreader_t));
468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!stream)
469 {
470 ret = GATE_RESULT_INVALIDARG;
471 break;
472 }
473 1 reader->stream = stream;
474 1 gate_object_retain(reader->stream);
475
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (gate_object_implements_interface((gate_object_t*)reader->stream, GATE_INTERFACE_NAME_CONTROLSTREAM))
476 {
477 reader->control_stream = (gate_controlstream_t*)reader->stream;
478 }
479 1 reader->bytes_received = 0;
480 1 reader->bytes_written = 0;
481 1 ret = GATE_RESULT_OK;
482 } while (0);
483
484 1 return ret;
485 }
486
487 gate_result_t gate_tarreader_get_first_entry(gate_tarreader_t* reader, gate_tarentry_t* ptr_entry)
488 {
489 gate_result_t result;
490 if (reader->control_stream)
491 {
492 result = gate_stream_seek(reader->control_stream, 0, GATE_STREAM_SEEK_BEGIN, NULL);
493 if (GATE_SUCCEEDED(result))
494 {
495 result = gate_tarreader_get_next_entry(reader, ptr_entry);
496 }
497 }
498 else
499 {
500 result = GATE_RESULT_NOTSUPPORTED;
501 }
502 return result;
503 }
504
505 3 static gate_bool_t gate_tarreader_is_empty_block(gate_tarblock_t const* block)
506 {
507 3 gate_size_t len = sizeof(block->data);
508 3 char const* ptr = &block->data[0];
509
510
2/2
✓ Branch 0 taken 514 times.
✓ Branch 1 taken 1 times.
515 while (len-- != 0)
511 {
512
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 512 times.
514 if (*ptr != 0) return false;
513 512 ++ptr;
514 }
515 1 return true;
516 }
517
518
519 3 gate_result_t gate_tarreader_get_next_entry(gate_tarreader_t* reader, gate_tarentry_t* ptr_entry)
520 {
521 3 gate_result_t ret = GATE_RESULT_FAILED;
522 3 gate_tarblock_t block = GATE_INIT_EMPTY;
523 3 gate_size_t returned = 0;
524 gate_size_t len;
525 3 gate_strbuilder_t path_builder = GATE_INIT_EMPTY;
526 3 gate_uint32_t longlink_len = 0;
527 3 gate_size_t longlink_block_count = 0;
528 gate_size_t ndx;
529 gate_size_t next_len;
530 3 gate_uint32_t tar_attribs = 0;
531 3 gate_uint64_t unix_time = 0;
532 gate_size_t diff;
533
534 do
535 {
536 3 gate_mem_clear(ptr_entry, sizeof(gate_tarentry_t));
537
538
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (reader->next_block != 0)
539 {
540 /* there are content bytes to be accessed */
541 ret = GATE_RESULT_INVALIDSTATE;
542 break;
543 }
544
545 3 ret = gate_stream_read_block(reader->stream, block.data, sizeof(block.data), &returned);
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 GATE_BREAK_IF_FAILED(ret);
547
548
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (returned == 0)
549 {
550 /* end of stream */
551 ret = GATE_RESULT_ENDOFSTREAM;
552 break;
553 }
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (returned < sizeof(block.data))
555 {
556 /* stream not complete, cannot read one full header entry */
557 ret = GATE_RESULT_INVALIDINPUT;
558 break;
559 }
560
561
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if (gate_tarreader_is_empty_block(&block))
562 {
563 /* end of stream */
564 1 ret = GATE_RESULT_ENDOFSTREAM;
565 1 break;
566 }
567
568
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!gate_tarreader_verify_checksum(&block))
569 {
570 ret = GATE_RESULT_INVALIDHEADER;
571 break;
572 }
573
574 2 gate_strbuilder_create_static(&path_builder, ptr_entry->path, sizeof(ptr_entry->path), 0);
575
576 2 len = gate_str_length_max(block.header.filename, sizeof(block.header.filename));
577
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if ((0 == gate_str_compare(block.header.filename, len, longlink_token, longlink_token_len))
578 && (block.header.typeflag == GATE_TARHEADER_LONGLINK))
579 {
580 /* longlink detected */
581 longlink_len = (gate_uint32_t)read_octal(block.header.size, sizeof(block.header.size));
582 longlink_block_count = longlink_len / 512;
583 if ((longlink_len % 512) != 0)
584 {
585 ++longlink_block_count;
586 }
587 for (ndx = 0; ndx != longlink_block_count; ++ndx)
588 {
589 next_len = (longlink_len >= sizeof(block.data)) ? sizeof(block.data) : longlink_len;
590 ret = gate_stream_read_block(reader->stream, block.data, sizeof(block.data), &returned);
591 GATE_BREAK_IF_FAILED(ret);
592 gate_strbuilder_append_text(&path_builder, block.data, next_len);
593 longlink_len -= (gate_uint32_t)next_len;
594 }
595 GATE_BREAK_IF_FAILED(ret);
596
597 /* now read the real header block */
598 ret = gate_stream_read_block(reader->stream, block.data, sizeof(block.data), &returned);
599 GATE_BREAK_IF_FAILED(ret);
600 }
601 else
602 {
603 /* just extract the filename from the header */
604 2 gate_strbuilder_append_text(&path_builder, block.header.filename, gate_str_length(block.header.filename));
605 }
606 2 tar_attribs = (gate_uint32_t)read_octal(block.header.filemode, sizeof(block.header.filemode));
607
608 2 ptr_entry->attribs = 0;
609 2 ptr_entry->access = 0;
610
611
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNERREAD, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUREAD));
612
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNERWRITE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUWRITE));
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNEREXECUTE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUEXEC));
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPREAD, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TGREAD));
615
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPWRITE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUWRITE));
616
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPEXECUTE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TUEXEC));
617
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_ALLREAD, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TOREAD));
618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_ALLWRITE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TOWRITE));
619
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_ALLEXECUTE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TOEXEC));
620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_OWNERSETID, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TSUID));
621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_GROUPSETID, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TSGID));
622
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_FLAG_SET(ptr_entry->access, GATE_FILEENTRY_ACCESS_NODELETE, GATE_FLAG_ENABLED(tar_attribs, GATE_TARHEADER_TSVTX));
623
624
1/3
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
2 switch (block.header.typeflag)
625 {
626 2 case GATE_TARHEADER_AREGTYPE:
627 case GATE_TARHEADER_REGTYPE:
628 2 ptr_entry->attribs |= GATE_FILEENTRY_ATTRIB_FILE;
629 2 break;
630 case GATE_TARHEADER_DIRTYPE:
631 ptr_entry->attribs |= GATE_FILEENTRY_ATTRIB_DIRECTORY;
632 break;
633 default:
634 ret = GATE_RESULT_NOTSUPPORTED;
635 break;
636 }
637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
638
639 2 reader->next_content_length = read_octal(block.header.size, sizeof(block.header.size));
640 2 reader->next_block = reader->next_content_length;
641 2 diff = (gate_size_t)(reader->next_block % 512U);
642
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (diff != 0)
643 {
644 2 reader->next_block += (512U - diff);
645 }
646 2 ptr_entry->size = reader->next_content_length;
647 2 ptr_entry->owner_id = (gate_uint32_t)read_octal(block.header.owner, sizeof(block.header.owner));
648 2 ptr_entry->group_id = (gate_uint32_t)read_octal(block.header.group, sizeof(block.header.group));
649
650 2 unix_time = read_octal(block.header.modify_time, sizeof(block.header.modify_time));
651 2 gate_time_from_unix((gate_int64_t)unix_time, &ptr_entry->time_modified);
652
653 2 unix_time = read_octal(block.header.access_time, sizeof(block.header.access_time));
654 2 gate_time_from_unix((gate_int64_t)unix_time, &ptr_entry->time_accessed);
655
656 2 gate_str_print_text(ptr_entry->owner_name, sizeof(ptr_entry->owner_name),
657 block.header.ownername, gate_str_length_max(block.header.ownername, sizeof(block.header.ownername)));
658
659 2 gate_str_print_text(ptr_entry->group_name, sizeof(ptr_entry->group_name),
660 block.header.groupname, gate_str_length_max(block.header.groupname, sizeof(block.header.groupname)));
661 2 ret = GATE_RESULT_OK;
662 } while (0);
663
664 3 return ret;
665 }
666 gate_result_t gate_tarreader_find_entry(gate_tarreader_t* reader, gate_string_t const* path, gate_tarentry_t* ptr_entry)
667 {
668 gate_result_t result = GATE_RESULT_OK;
669 gate_tarentry_t entry;
670
671 for (;;)
672 {
673 result = gate_tarreader_get_next_entry(reader, &entry);
674 GATE_BREAK_IF_FAILED(result);
675 if (gate_string_equals_str(path, entry.path))
676 {
677 if (ptr_entry)
678 {
679 gate_mem_copy(ptr_entry, &entry, sizeof(entry));
680 }
681 result = GATE_RESULT_OK;
682 break;
683 }
684 result = gate_tarreader_skip_content(reader, NULL);
685 GATE_BREAK_IF_FAILED(result);
686 }
687 return result;
688 }
689 2 gate_result_t gate_tarreader_extract_content(gate_tarreader_t* reader, gate_stream_t* output_target, gate_uint64_t* bytes_transferred)
690 {
691 2 gate_result_t ret = GATE_RESULT_OK;
692 2 gate_uint64_t exported_bytes = 0;
693
694 do
695 {
696
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (reader->next_content_length != 0)
697 {
698 2 ret = gate_stream_transfer_limit(reader->stream, reader->next_content_length, output_target, &exported_bytes);
699
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
700
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (exported_bytes < reader->next_content_length)
701 {
702 ret = GATE_RESULT_INVALIDINPUT;
703 break;
704 }
705 2 GATE_DEBUG_ASSERT(reader->next_content_length == exported_bytes);
706 2 reader->next_content_length = 0;
707 2 GATE_DEBUG_ASSERT(reader->next_block >= exported_bytes);
708 2 reader->next_block -= exported_bytes;
709 }
710
711
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (reader->next_block != 0)
712 {
713 2 ret = gate_stream_skip(reader->stream, reader->next_block, NULL);
714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_BREAK_IF_FAILED(ret);
715 2 reader->next_block = 0;
716 }
717 } while (0);
718
719
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (bytes_transferred)
720 {
721 2 *bytes_transferred = exported_bytes;
722 }
723
724 2 return ret;
725 }
726 gate_result_t gate_tarreader_skip_content(gate_tarreader_t* reader, gate_uint64_t* optional_bytes_skipped)
727 {
728 gate_result_t ret = GATE_RESULT_OK;
729 do
730 {
731 if (reader->next_block != 0)
732 {
733 ret = gate_stream_skip(reader->stream, reader->next_block, optional_bytes_skipped);
734 GATE_BREAK_IF_FAILED(ret);
735 reader->next_block = 0;
736 }
737 else
738 {
739 if (optional_bytes_skipped)
740 {
741 *optional_bytes_skipped = 0;
742 }
743 }
744 reader->next_content_length = 0;
745 reader->next_block = 0;
746 } while (0);
747 return ret;
748 }
749
750 1 gate_result_t gate_tarreader_destroy(gate_tarreader_t* reader)
751 {
752 1 gate_result_t ret = GATE_RESULT_OK;
753
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (reader->stream)
754 {
755 1 gate_object_release(reader->stream);
756 }
757 1 gate_mem_clear(reader, sizeof(gate_tarreader_t));
758 1 return ret;
759 }
760
761