ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
unarch_tar_fields.c
Go to the documentation of this file.
1
22#include <string.h>
23
24#include "ra8_attributes.h"
25#include "ra8_check.h"
26#include "unarch_tar_internal.h"
27
29static const char* const s_tag_tar_f = "unarch_tar";
30
31/* The pax extended-header record parser walks length-prefixed key=value records
32 * with a fail-closed guard at every step: the parse body clears clang-tidy's
33 * statement/nesting/cognitive thresholds while staying within the 60-line NASA
34 * Rule 4 gate. Same disposition as the other decode state machines in the tree
35 * (ra8_jpeg_sw_encode, ra8_rmac_phy). */
36
53
72static ra8_err_t internal_octal(const uint8_t* field, size_t len, uint64_t* out)
73{
74 size_t i = 0U;
75 while (i < len) { /* bound: len */
76 if (field[i] != (uint8_t)' ') {
77 break;
78 }
79 i += 1U;
80 }
81 uint64_t v = 0U;
82 size_t digits = 0U;
83 while ((i < len) && (field[i] != 0U) && (field[i] != (uint8_t)' ')) { /* bound: len */
84 const uint8_t c = field[i];
85 if (c < (uint8_t)'0') {
87 }
88 if (c > (uint8_t)'7') {
90 }
91 const uint64_t d = (uint64_t)c - (uint64_t)(uint8_t)'0';
92 if (v > ((UINT64_MAX - d) / (uint64_t)k_tar_octal_base)) {
93 return k_ra8_err_validation_failed; /* would overflow uint64 */
94 }
95 v = (v * (uint64_t)k_tar_octal_base) + d;
96 digits += 1U;
97 i += 1U;
98 }
99 if (digits == 0U) {
100 return k_ra8_err_validation_failed; /* empty field */
101 }
102 *out = v;
103 return k_ra8_ok;
104}
105
125static ra8_err_t internal_base256(const uint8_t* field, size_t len, uint64_t* out)
126{
127 if ((field[0] & (uint8_t)k_tar_b256_negative) != 0U) {
128 return k_ra8_err_validation_failed; /* negative size: hostile */
129 }
130 if ((field[0] & (uint8_t)k_tar_b256_payload) != 0U) {
131 return k_ra8_err_validation_failed; /* > 2^(8*(len-1)) - way over uint64 */
132 }
133 uint64_t v = 0U;
134 for (size_t i = 1U; i < len; ++i) { /* bound: len */
135 const size_t remaining = len - i;
136 if (remaining > (size_t)k_tar_b256_fit) {
137 if (field[i] != 0U) {
138 return k_ra8_err_validation_failed; /* over 64 bits */
139 }
140 continue;
141 }
142 v = (v << (uint64_t)k_tar_shift_byte) | (uint64_t)field[i];
143 }
144 *out = v;
145 return k_ra8_ok;
146}
147
148RA8_PRIV ra8_err_t priv_unarch_tar_num(const uint8_t* field, size_t len, uint64_t* out)
149{
150 RA8_CHECK_NULL_PTR(field, s_tag_tar_f, "num: null field");
151 RA8_CHECK_NULL_PTR(out, s_tag_tar_f, "num: null out");
152 *out = 0U;
153 if (len == 0U) {
155 }
156 if ((field[0] & (uint8_t)k_tar_b256_flag) != 0U) {
157 return internal_base256(field, len, out);
158 }
159 return internal_octal(field, len, out);
160}
161
162RA8_PRIV bool priv_unarch_tar_block_zero(const uint8_t* block)
163{
164 for (uint32_t i = 0U; i < (uint32_t)k_unarch_tar_block; ++i) { /* bound: block size */
165 if (block[i] != 0U) {
166 return false;
167 }
168 }
169 return true;
170}
171
172RA8_PRIV bool priv_unarch_tar_checksum_ok(const uint8_t* block)
173{
174 uint32_t sum = 0U;
175 for (uint32_t i = 0U; i < (uint32_t)k_unarch_tar_block; ++i) { /* bound: block size */
176 const bool in_chksum = (i >= (uint32_t)k_ra8_tar_off_chksum) &&
177 (i < ((uint32_t)k_ra8_tar_off_chksum + (uint32_t)k_ra8_tar_len_chksum));
178 sum += in_chksum ? (uint32_t)k_tar_chksum_space : (uint32_t)block[i];
179 }
180 uint64_t stored = 0U;
181 if (internal_octal(&block[k_ra8_tar_off_chksum], (size_t)k_ra8_tar_len_chksum, &stored) !=
182 k_ra8_ok) {
183 return false;
184 }
185 return stored == (uint64_t)sum;
186}
187
188RA8_PRIV bool priv_unarch_tar_magic_ok(const uint8_t* block)
189{
190 static const uint8_t magic[] = {'u', 's', 't', 'a', 'r'};
191 for (size_t i = 0U; i < sizeof(magic); ++i) {
192 if (block[(size_t)k_ra8_tar_off_magic + i] != magic[i]) {
193 return false;
194 }
195 }
196 const uint8_t term = block[k_ra8_tar_off_magic_term];
197 return (term == 0U) || (term == (uint8_t)' ');
198}
199
201{
202 if (typeflag == (uint8_t)'0') {
203 return k_ra8_tar_type_file;
204 }
205 if (typeflag == 0U) {
206 return k_ra8_tar_type_file; /* pre-POSIX regular-file flag */
207 }
208 if (typeflag == (uint8_t)'5') {
209 return k_ra8_tar_type_dir;
210 }
211 if (typeflag == (uint8_t)'x') {
212 return k_ra8_tar_type_pax;
213 }
214 if (typeflag == (uint8_t)'g') {
215 return k_ra8_tar_type_meta;
216 }
217 if (typeflag == (uint8_t)'K') {
218 return k_ra8_tar_type_meta;
219 }
220 if (typeflag == (uint8_t)'L') {
222 }
224}
225
246static ra8_err_t internal_pax_reclen(const uint8_t* data, size_t len, size_t* reclen, size_t* body)
247{
248 uint64_t v = 0U;
249 size_t digits = 0U;
250 size_t i = 0U;
251 while (i < len) { /* bound: len */
252 const uint8_t c = data[i];
253 if (c == (uint8_t)' ') {
254 break;
255 }
256 if (c < (uint8_t)'0') {
258 }
259 if (c > (uint8_t)'9') {
261 }
262 if (digits >= (size_t)k_tar_pax_len_max) {
263 return k_ra8_err_validation_failed; /* absurd length prefix */
264 }
265 v = (v * (uint64_t)k_tar_decimal_base) + ((uint64_t)c - (uint64_t)(uint8_t)'0');
266 digits += 1U;
267 i += 1U;
268 }
269 if (digits == 0U) {
270 return k_ra8_err_validation_failed; /* no digits before the space */
271 }
272 if (i >= len) {
273 return k_ra8_err_validation_failed; /* ran out before the space */
274 }
275 *reclen = (size_t)v;
276 *body = i + 1U;
277 return k_ra8_ok;
278}
279
299static ra8_err_t internal_pax_size_value(const uint8_t* val, size_t len, uint64_t* out)
300{
301 if (len == 0U) {
303 }
304 uint64_t v = 0U;
305 for (size_t i = 0U; i < len; ++i) { /* bound: len */
306 const uint8_t c = val[i];
307 if (c < (uint8_t)'0') {
309 }
310 if (c > (uint8_t)'9') {
312 }
313 const uint64_t d = (uint64_t)c - (uint64_t)(uint8_t)'0';
314 if (v > ((UINT64_MAX - d) / (uint64_t)k_tar_decimal_base)) {
315 return k_ra8_err_validation_failed; /* would overflow uint64 */
316 }
317 v = (v * (uint64_t)k_tar_decimal_base) + d;
318 }
319 *out = v;
320 return k_ra8_ok;
321}
322
348static ra8_err_t internal_pax_apply(const uint8_t* key,
349 size_t key_len,
350 const uint8_t* val,
351 size_t val_len,
352 char* name_buf,
353 uint16_t name_cap,
354 uint16_t* name_len,
355 bool* have_path,
356 uint64_t* size_ovr,
357 bool* have_size)
358{
359 static const uint8_t key_path[] = {'p', 'a', 't', 'h'};
360 static const uint8_t key_size[] = {'s', 'i', 'z', 'e'};
361 bool is_path = key_len == sizeof(key_path);
362 for (size_t i = 0U; is_path && (i < sizeof(key_path)); ++i) {
363 is_path = key[i] == key_path[i];
364 }
365 if (is_path) {
366 const size_t want = (val_len > (size_t)name_cap) ? (size_t)name_cap : val_len;
367 if (want > 0U) {
368 (void)memcpy(name_buf, val, want);
369 }
370 *name_len = (uint16_t)want;
371 *have_path = true;
372 return k_ra8_ok;
373 }
374 bool is_size = key_len == sizeof(key_size);
375 for (size_t i = 0U; is_size && (i < sizeof(key_size)); ++i) {
376 is_size = key[i] == key_size[i];
377 }
378 if (is_size) {
379 const ra8_err_t verr = internal_pax_size_value(val, val_len, size_ovr);
380 if (verr != k_ra8_ok) {
381 return verr;
382 }
383 *have_size = true;
384 }
385 return k_ra8_ok;
386}
387// NOLINTNEXTLINE(readability-function-size,readability-function-cognitive-complexity) -- keyed pax record state machine: length, key and value share one bounded cursor, so splitting it would duplicate that cursor state across helpers.
389 size_t len,
390 char* name_buf,
391 uint16_t name_cap,
392 uint16_t* name_len,
393 bool* have_path,
394 uint64_t* size_ovr,
395 bool* have_size)
396{
397 RA8_CHECK_NULL_PTR(data, s_tag_tar_f, "pax: null data");
398 RA8_CHECK_NULL_PTR(name_len, s_tag_tar_f, "pax: null name_len");
399 RA8_CHECK_NULL_PTR(have_path, s_tag_tar_f, "pax: null have_path");
400 RA8_CHECK_NULL_PTR(size_ovr, s_tag_tar_f, "pax: null size_ovr");
401 RA8_CHECK_NULL_PTR(have_size, s_tag_tar_f, "pax: null have_size");
402 if (name_cap > 0U) {
403 RA8_CHECK_NULL_PTR(name_buf, s_tag_tar_f, "pax: null name_buf");
404 }
405 size_t off = 0U;
406 while (off < len) { /* bound: every record advances by reclen >= body + 1 */
407 size_t reclen = 0U;
408 size_t body = 0U;
409 const ra8_err_t lerr = internal_pax_reclen(&data[off], len - off, &reclen, &body);
410 if (lerr != k_ra8_ok) {
411 return lerr;
412 }
413 if (reclen <= body) {
414 return k_ra8_err_validation_failed; /* length cannot cover its prefix */
415 }
416 if (reclen > (len - off)) {
417 return k_ra8_err_validation_failed; /* record overruns the data */
418 }
419 if (data[off + reclen - 1U] != (uint8_t)'\n') {
420 return k_ra8_err_validation_failed; /* record must end in newline */
421 }
422 const uint8_t* kv = &data[off + body];
423 const size_t kv_len = reclen - body - 1U; /* strip the newline */
424 size_t key_len = 0U;
425 while ((key_len < kv_len) && (kv[key_len] != (uint8_t)'=')) {
426 key_len += 1U;
427 }
428 if (key_len == kv_len) {
429 return k_ra8_err_validation_failed; /* no key/value delimiter */
430 }
431 const ra8_err_t aerr = internal_pax_apply(kv,
432 key_len,
433 &kv[key_len + 1U],
434 kv_len - key_len - 1U,
435 name_buf,
436 name_cap,
437 name_len,
438 have_path,
439 size_ovr,
440 have_size);
441 if (aerr != k_ra8_ok) {
442 return aerr;
443 }
444 off += reclen;
445 }
446 return k_ra8_ok;
447}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
@ k_unarch_tar_block
On-disk block / header size.
Definition unarch_tar.h:77
ra8_err_t priv_unarch_tar_pax_parse(const uint8_t *data, size_t len, char *name_buf, uint16_t name_cap, uint16_t *name_len, bool *have_path, uint64_t *size_ovr, bool *have_size)
Parse pax extended-header records, extracting path / size.
static ra8_err_t internal_pax_reclen(const uint8_t *data, size_t len, size_t *reclen, size_t *body)
Decode one pax record's decimal length prefix.
bool priv_unarch_tar_block_zero(const uint8_t *block)
Whether a header block is all zero bytes (end-of-archive marker).
static ra8_err_t internal_octal(const uint8_t *field, size_t len, uint64_t *out)
Decode an octal ASCII field (leading spaces, NUL/space terminated).
static const char *const s_tag_tar_f
Log tag for tar field-parser diagnostics.
bool priv_unarch_tar_magic_ok(const uint8_t *block)
Whether a header block carries the ustar / GNU magic.
static ra8_err_t internal_pax_apply(const uint8_t *key, size_t key_len, const uint8_t *val, size_t val_len, char *name_buf, uint16_t name_cap, uint16_t *name_len, bool *have_path, uint64_t *size_ovr, bool *have_size)
Apply one parsed pax record's key/value to the override outputs.
bool priv_unarch_tar_checksum_ok(const uint8_t *block)
Verify a header block's checksum (unsigned byte sum).
ra8_err_t priv_unarch_tar_num(const uint8_t *field, size_t len, uint64_t *out)
Decode a tar numeric field (octal ASCII or GNU base-256).
static ra8_err_t internal_base256(const uint8_t *field, size_t len, uint64_t *out)
Decode a GNU base-256 (big-endian binary) numeric field.
ra8_tar_type_t priv_unarch_tar_classify(uint8_t typeflag)
Classify a header block's typeflag byte.
static ra8_err_t internal_pax_size_value(const uint8_t *val, size_t len, uint64_t *out)
Decimal-decode a pax size record value with overflow checks.
tar_field_const_t
Grammar constants for the numeric and pax record parsers.
@ k_tar_octal_base
Octal radix.
@ k_tar_b256_flag
Base-256 marker bit in byte 0.
@ k_tar_pax_len_max
Max digits in a pax record length.
@ k_tar_shift_byte
Bits per base-256 byte.
@ k_tar_chksum_space
chksum bytes count as spaces.
@ k_tar_b256_payload
Payload mask of base-256 byte 0.
@ k_tar_b256_fit
Trailing bytes that fit in uint64.
@ k_tar_decimal_base
pax record-length / size radix.
@ k_tar_b256_negative
Sign bit of a base-256 value.
Module-private tar field/record parsers shared across the tar TUs.
@ k_ra8_tar_len_chksum
chksum field length.
@ k_ra8_tar_off_magic_term
Byte after "ustar" (NUL or ' ').
@ k_ra8_tar_off_chksum
chksum field offset.
@ k_ra8_tar_off_magic
magic field offset.
ra8_tar_type_t
Normalised classification of a header block's typeflag.
@ k_ra8_tar_type_pax
pax extended header ('x').
@ k_ra8_tar_type_other
Any other member kind (skipped).
@ k_ra8_tar_type_dir
Directory ('5').
@ k_ra8_tar_type_meta
Skipped meta: 'g' global, 'K' longlink.
@ k_ra8_tar_type_file
Regular file ('0' or NUL).
@ k_ra8_tar_type_longname
GNU longname data ('L').