• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-mcu / 1650280640

31 Jan 2025 01:18PM UTC coverage: 25.682%. Remained the same
1650280640

push

gitlab-ci

danielskinstad
chore: explicit error for no files in zephyr-image-update-module

In order to check the error, we needed a way to get the filename. Since
we don't have access to the update state data, we set it when reading
the manifest.

Ticket: MEN-7804

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>

1 of 3 new or added lines in 1 file covered. (33.33%)

116 existing lines in 1 file now uncovered.

715 of 2784 relevant lines covered (25.68%)

7.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.87
/core/src/mender-artifact.c
1
/**
2
 * @file      mender-artifact.c
3
 * @brief     Mender artifact parser
4
 *
5
 * Copyright joelguittet and mender-mcu-client contributors
6
 * Copyright Northern.tech AS
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
#include <errno.h>
22

23
#include "mender-artifact.h"
24
#include "mender-deployment-data.h"
25
#include "mender-log.h"
26

27
/**
28
 * @brief Device type key
29
 */
30
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
31
#define MENDER_ARTIFACT_DEVICE_TYPE_KEY "device_type"
32
#endif
33

34
/**
35
 * @brief TAR file header
36
 */
37
typedef struct {
38
    char name[100];
39
    char mode[8];
40
    char uid[8];
41
    char gid[8];
42
    char size[12];
43
    char mtime[12];
44
    char chksum[8];
45
    char typeflag;
46
    char linkname[100];
47
    char magic[6];
48
    char version[2];
49
    char uname[32];
50
    char gname[32];
51
    char devmajor[8];
52
    char devminor[8];
53
    char prefix[155];
54
} mender_artifact_tar_header_t;
55

56
struct data_mdata_cache {
57
    const char *filename;
58
    char       *checksum_fname;
59
    cJSON      *meta_data;
60
    const char *deployment_id;
61
    const char *artifact_name;
62
    const char *payload_type;
63
    bool        valid;
64
};
65

66
/**
67
 * @brief Supported artifact format and version
68
 */
69
#define MENDER_ARTIFACT_SUPPORTED_FORMAT  "mender"
70
#define MENDER_ARTIFACT_SUPPORTED_VERSION 3
71

72
/**
73
 * @brief Parse header of TAR file
74
 * @param ctx Artifact context
75
 * @return MENDER_DONE if the data have been parsed, MENDER_OK if there is not enough data to parse, error code if an error occurred
76
 */
77
static mender_err_t artifact_parse_tar_header(mender_artifact_ctx_t *ctx);
78

79
/**
80
 * @brief Read version file of the artifact
81
 * @param ctx Artifact context
82
 * @return MENDER_DONE if the data have been parsed and version verified, MENDER_OK if there is not enough data to parse, error code if an error occurred
83
 */
84
static mender_err_t artifact_read_version(mender_artifact_ctx_t *ctx);
85

86
/**
87
 * @brief Read header-info file of the artifact
88
 * @param ctx Artifact context
89
 * @param dl_data Download data for the artifact
90
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
91
 */
92
static mender_err_t artifact_read_header_info(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data);
93

94
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
95
/**
96
 * @brief Read manifest file of the artifact
97
 * @param ctx Artifact context
98
 * @return MENDER_DONE if the data have been parsed and checksums retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
99
 */
100
static mender_err_t artifact_read_manifest(mender_artifact_ctx_t *ctx);
101

102
/**
103
 * @brief Read type-info file of the artifact
104
 * @param ctx Artifact context
105
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
106
 */
107
static mender_err_t artifact_read_type_info(mender_artifact_ctx_t *ctx);
108

109
/**
110
 * @brief Parse provides/depends from JSON object
111
 * @param json_provides_depends JSON object to parse
112
 * @param provides_depends Pointer to the list of provides or depends
113
 * @return MENDER_SUCCESS if the function succeeds, MENDER_FAIL otherwise
114
 */
115
static mender_err_t artifact_parse_provides_depends(cJSON *json_provides_depends, mender_key_value_list_t **provides_depends);
116
#endif
117

118
/**
119
 * @brief Read meta-data file of the artifact
120
 * @param ctx Artifact context
121
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
122
 */
123
static mender_err_t artifact_read_meta_data(mender_artifact_ctx_t *ctx);
124

125
/**
126
 * @brief Read data file of the artifact
127
 * @param ctx Artifact context
128
 * @param dl_data Download data for the artifact
129
 * @param mdata_cache Cache with metadata for the current data being processed
130
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
131
 */
132
static mender_err_t artifact_read_data(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache);
133

134
/**
135
 * @brief Prepare a cache with metadata for the current data being processed
136
 * @param ctx Artifact context
137
 * @param dl_data Download data for the artifact
138
 * @param mdata_cache Cache to populate
139
 * @return MENDER_OK in case of success, error otherwise
140
 */
141
static mender_err_t artifact_read_data_prepare(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache);
142

143
/**
144
 * @brief Invalidate the cache with metadata for the current data being processed
145
 * @param mdata_cache Cache to invalidate
146
 */
147
static inline void
148
data_mdata_cache_invalidate(struct data_mdata_cache *mdata_cache) {
75✔
149
    assert(NULL != mdata_cache);
75✔
150

151
    FREE_AND_NULL(mdata_cache->checksum_fname);
75✔
152
    mdata_cache->valid = false;
75✔
153
}
75✔
154

155
/**
156
 * @brief Process chunk of artifact data
157
 */
158
static mender_err_t process_artifact_data_callback(const char                      *deployment_id,
159
                                                   const char                      *type,
160
                                                   const char                      *artifact_name,
161
                                                   const cJSON                     *meta_data,
162
                                                   const char                      *filename,
163
                                                   size_t                           size,
164
                                                   void                            *data,
165
                                                   size_t                           index,
166
                                                   size_t                           length,
167
                                                   mender_artifact_download_data_t *dl_data);
168

169
/**
170
 * @brief Drop content of the current file of the artifact
171
 * @param ctx Artifact context
172
 * @return MENDER_DONE if the data have been parsed and dropped, MENDER_OK if there is not enough data to parse, error code if an error occurred
173
 */
174
static mender_err_t artifact_drop_file(mender_artifact_ctx_t *ctx);
175

176
/**
177
 * @brief Shift data after parsing and update the respective checksum context
178
 * @param checksum_key Key under which the checksum for the data is calculated/checked
179
 * @param checksum_len Length of the data to include in the checksum update
180
 * @return MENDER_OK if the function succeeds, error code otherwise
181
 * @see artifact_shift_data()
182
 */
183
static mender_err_t artifact_shift_and_checksum_data(mender_artifact_ctx_t *ctx, size_t length, const char *checksum_key, size_t checksum_len);
184

185
/**
186
 * @brief Shift data after parsing
187
 * @param ctx Artifact context
188
 * @param length Length of data to shift
189
 * @return MENDER_OK if the function succeeds, error code otherwise
190
 */
191
static mender_err_t artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length);
192

193
/**
194
 * @brief Compute length rounded up to increment (usually the block size)
195
 * @param length Length
196
 * @param incr Increment
197
 * @return Rounded length
198
 */
199
static size_t artifact_round_up(size_t length, size_t incr);
200

201
/**
202
 * @brief Artifact context
203
 */
204
static mender_artifact_ctx_t *artifact_ctx = NULL;
205

206
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
207
/**
208
 * @brief Get checksum entry for a file in the context
209
 * @param ctx The mender artifact context
210
 * @param filename The name of the file in the artifact
211
 * @return The checksum entry or NULL on error.
212
 * @note Since other files may be parsed before the manifest file, we need to
213
 *       create these entries in a lazy fashion.
214
 */
215
static mender_artifact_checksum_t *
216
artifact_checksum_get_or_create(mender_artifact_ctx_t *ctx, const char *filename) {
69✔
217
    assert(NULL != ctx);
69✔
218
    assert(NULL != filename);
69✔
219

220
    /* See if we already have an entry for this file */
221
    mender_artifact_checksum_t *checksum;
222
    for (checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = checksum->next) {
111✔
223
        if (StringEqual(checksum->filename, filename)) {
88✔
224
            break;
46✔
225
        }
226
    }
227

228
    if (NULL == checksum) {
69✔
229
        /* Create new if entry not found */
230
        checksum = (mender_artifact_checksum_t *)calloc(1, sizeof(mender_artifact_checksum_t));
23✔
231
        if (NULL == checksum) {
23✔
232
            mender_log_error("Unable to allocate memory");
×
UNCOV
233
            return NULL;
×
234
        }
235
        checksum->filename = strdup(filename);
23✔
236
        if (NULL == checksum->filename) {
23✔
237
            mender_log_error("Unable to allocate memory");
×
238
            free(checksum);
×
UNCOV
239
            return NULL;
×
240
        }
241
        checksum->next               = ctx->artifact_info.checksums;
23✔
242
        ctx->artifact_info.checksums = checksum;
23✔
243

244
        /* Start SHA-256 checksum computation */
245
        if (MENDER_OK != mender_sha256_begin(&(checksum->context))) {
23✔
246
            mender_log_error("Failed to start checksum for file '%s'", filename);
×
UNCOV
247
            return NULL;
×
248
        }
249
    }
250

251
    return checksum;
69✔
252
}
253
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
254

255
mender_err_t
256
mender_artifact_check_integrity(mender_artifact_ctx_t *ctx) {
2✔
257
    assert(NULL != ctx);
2✔
258

259
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
260
    for (mender_artifact_checksum_t *checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = checksum->next) {
5✔
261
        unsigned char computed[MENDER_DIGEST_BUFFER_SIZE];
262
        mender_log_debug("Checking integrity for artifact file '%s'", checksum->filename);
263

264
        if (MENDER_OK != mender_sha256_finish(checksum->context, computed)) {
4✔
265
            mender_log_error("Failed to finish checksum for file '%s'", checksum->filename);
×
UNCOV
266
            checksum->context = NULL;
×
267
            return MENDER_FAIL;
1✔
268
        }
269
        checksum->context = NULL;
4✔
270

271
        if (0 != memcmp(checksum->manifest, computed, MENDER_DIGEST_BUFFER_SIZE)) {
4✔
272
            mender_log_error("Computed checksum for file '%s' does not match manifest", checksum->filename);
1✔
273
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_DBG
274
            /* Log the mismatching checksums for debugging */
275
            char checksum_str[(MENDER_DIGEST_BUFFER_SIZE * 2) + 1];
276

277
            for (int i = 0; i < MENDER_DIGEST_BUFFER_SIZE; i++) {
278
                if (2 != snprintf(checksum_str + (i * 2), 3, "%02hhx", checksum->manifest[i])) {
279
                    break;
280
                }
281
            }
282
            mender_log_debug("%s: '%s' (manifest)", checksum->filename, checksum_str);
283

284
            for (int i = 0; i < MENDER_DIGEST_BUFFER_SIZE; i++) {
285
                if (2 != snprintf(checksum_str + (i * 2), 3, "%02hhx", computed[i])) {
286
                    break;
287
                }
288
            }
289
            mender_log_debug("%s: '%s' (computed)", checksum->filename, checksum_str);
290
#endif /* CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_DBG */
291
            return MENDER_FAIL;
1✔
292
        }
293
    }
294
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
295
    return MENDER_OK;
1✔
296
}
297

298
mender_artifact_ctx_t *
299
mender_artifact_create_ctx(size_t buf_size) {
13✔
300

301
    mender_artifact_ctx_t *ctx;
302

303
    /* Create new context */
304
    if (NULL == (ctx = (mender_artifact_ctx_t *)calloc(1, sizeof(mender_artifact_ctx_t)))) {
13✔
305
        mender_log_error("Unable to allocate memory for artifact context");
×
UNCOV
306
        return NULL;
×
307
    }
308
    if (NULL == (ctx->input.data = malloc(buf_size))) {
13✔
309
        mender_log_error("Unable to allocate memory for artifact context buffer");
×
310
        free(ctx);
×
UNCOV
311
        return NULL;
×
312
    }
313
    ctx->input.size      = buf_size;
13✔
314
    ctx->input.orig_size = buf_size;
13✔
315

316
    /* Save context */
317
    artifact_ctx = ctx;
13✔
318

319
    return ctx;
13✔
320
}
321

322
mender_err_t
323
mender_artifact_get_ctx(mender_artifact_ctx_t **ctx) {
2✔
324

325
    assert(NULL != ctx);
2✔
326

327
    if (NULL == artifact_ctx) {
2✔
328
        return MENDER_FAIL;
1✔
329
    }
330

331
    *ctx = artifact_ctx;
1✔
332
    return MENDER_OK;
1✔
333
}
334

335
static bool
336
is_compressed(const char *filename) {
8✔
337

338
    /* Mender artifact supports `.gz`, `.xz`, and `.zst` */
339
    static const char *compression_suffixes[] = { ".gz", ".xz", ".zst", NULL };
340

341
    for (size_t i = 0; NULL != compression_suffixes[i]; i++) {
29✔
342
        if (mender_utils_strendswith(filename, compression_suffixes[i])) {
22✔
343
            return true;
1✔
344
        }
345
    }
346

347
    return false;
7✔
348
}
349

350
static struct data_mdata_cache data_mdata_cache = { 0 };
351

352
mender_err_t
353
mender_artifact_process_data(mender_artifact_ctx_t *ctx, void *input_data, size_t input_length, mender_artifact_download_data_t *dl_data) {
11✔
354

355
    assert(NULL != ctx);
11✔
356
    mender_err_t ret = MENDER_OK;
11✔
357
    void        *tmp;
358
    size_t       new_size;
359
    size_t       expected_required;
360

361
    /* Copy data to the end of the internal buffer */
362
    if ((NULL != input_data) && (0 != input_length)) {
11✔
363
        if ((ctx->input.length + input_length) > ctx->input.size) {
10✔
364
            new_size = ctx->input.length + input_length;
10✔
365
            /* data/ files are processed per block for which the original size of the buffer should
366
               be enough, but metadata is processed as whole files so there we expect we will need
367
               more, except for header.tar (and tarballs in general) which are processed
368
               transparently. */
369
            if (mender_utils_strbeginswith(ctx->file.name, "data/")) {
10✔
UNCOV
370
                expected_required = ctx->input.orig_size;
×
371
            } else if (mender_utils_strendswith(ctx->file.name, ".tar")) {
10✔
UNCOV
372
                expected_required = ctx->input.orig_size;
×
373
            } else {
374
                expected_required = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE) + MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
10✔
375
            }
376
            if (new_size > expected_required) {
377
                mender_log_debug("Reallocating artifact context buffer to %zd [SHOULD NOT BE HAPPENING!]", new_size);
378
            }
379
            /* Let's try to get what we expect we will need anyway and if we don't get that much,
380
               let's get the minimum required now, leaving us with a chance to get more later. */
381
            if (NULL == (tmp = realloc(ctx->input.data, MAX(new_size, expected_required)))) {
10✔
382
                if (NULL == (tmp = realloc(ctx->input.data, new_size))) {
2✔
383
                    /* Unable to allocate memory */
384
                    return MENDER_FAIL;
1✔
385
                }
386
                ctx->input.size = new_size;
1✔
387
            } else {
388
                ctx->input.size = MAX(new_size, expected_required);
8✔
389
            }
390
            ctx->input.data = tmp;
9✔
391
        }
392
        memcpy((void *)(((uint8_t *)ctx->input.data) + ctx->input.length), input_data, input_length);
9✔
393
        ctx->input.length += input_length;
9✔
394
    }
395

396
    /* Parse data */
397
    do {
398

399
        /* Treatment depending of the stream state */
400
        if (MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER == ctx->stream_state) {
124✔
401

402
            /* Parse TAR header */
403
            ret = artifact_parse_tar_header(ctx);
75✔
404

405
            /* Processing a new (data) file, invalidate the cache */
406
            data_mdata_cache_invalidate(&data_mdata_cache);
75✔
407

408
        } else if (MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA == ctx->stream_state) {
49✔
409

410
            /* Treatment depending of the file name */
411
            if (StringEqual(ctx->file.name, "version")) {
49✔
412

413
                /* Validate artifact version */
414
                ret = artifact_read_version(ctx);
8✔
415

416
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
417
            } else if (StringEqual(ctx->file.name, "manifest")) {
41✔
418

419
                /* Read manifest file */
420
                ret = artifact_read_manifest(ctx);
8✔
421
#endif
422
            } else if (StringEqual(ctx->file.name, "header.tar/header-info")) {
33✔
423

424
                /* Read header-info file */
425
                ret = artifact_read_header_info(ctx, dl_data);
7✔
426

427
            } else if ((true == mender_utils_strbeginswith(ctx->file.name, "header.tar/headers"))
26✔
428
                       && (true == mender_utils_strendswith(ctx->file.name, "meta-data"))) {
7✔
429

430
                /* Read meta-data file */
431
                ret = artifact_read_meta_data(ctx);
1✔
432

433
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
434
            } else if (mender_utils_strbeginswith(ctx->file.name, "header.tar/headers") && mender_utils_strendswith(ctx->file.name, "type-info")) {
25✔
435

436
                /* Read type-info file */
437
                ret = artifact_read_type_info(ctx);
6✔
438
#endif
439
            } else if ((mender_utils_strbeginswith(ctx->file.name, "data")) && (strlen(ctx->file.name) > strlen("data/xxxx.tar"))) {
19✔
440
                /* Processing data. But the first "file" is data/0000.tar which
441
                   is not a real file, it's just the beginning of the tarball
442
                   for which we don't need to do anything here. Hence the
443
                   strlen() check above. */
444
                if (!data_mdata_cache.valid) {
6✔
445
                    /* Populate the cache and do one-off things */
446
                    ret = artifact_read_data_prepare(ctx, dl_data, &data_mdata_cache);
6✔
447
                }
448

449
                if (MENDER_OK == ret) {
6✔
450
                    assert(data_mdata_cache.valid);
6✔
451
                    /* Read data */
452
                    ret = artifact_read_data(ctx, dl_data, &data_mdata_cache);
6✔
453
                }
454
            } else if (false == mender_utils_strendswith(ctx->file.name, ".tar")) {
13✔
455

456
                /* Drop data, file is not relevant */
UNCOV
457
                ret = artifact_drop_file(ctx);
×
458
            } else {
459

460
                /* Nothing to do */
461
                ret = MENDER_DONE;
13✔
462
            }
463

464
            /* Check if file have been parsed and treatment done */
465
            if (MENDER_DONE == ret) {
49✔
466

467
                /* Remove the previous file name */
468
                char *substring = mender_utils_strrstr(ctx->file.name, ".tar");
47✔
469
                if (NULL != substring) {
47✔
470
                    *(substring + strlen(".tar")) = '\0';
32✔
471
                } else {
472
                    FREE_AND_NULL(ctx->file.name);
15✔
473
                }
474
                ctx->file.size  = 0;
47✔
475
                ctx->file.index = 0;
47✔
476

477
                /* Update the stream state machine */
478
                ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER;
47✔
479
            }
480
        }
481
    } while (MENDER_DONE == ret);
124✔
482

483
    return ret;
10✔
484
}
485

486
void
487
mender_artifact_release_ctx(mender_artifact_ctx_t *ctx) {
13✔
488

489
    /* Release memory */
490
    if (NULL != ctx) {
13✔
491
        free(ctx->input.data);
13✔
492
        if (NULL != ctx->payloads.values) {
13✔
493
            for (size_t index = 0; index < ctx->payloads.size; index++) {
14✔
494
                free(ctx->payloads.values[index].type);
7✔
495
                cJSON_Delete(ctx->payloads.values[index].meta_data);
7✔
496

497
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
498
                mender_utils_key_value_list_free(ctx->payloads.values[index].provides);
7✔
499
                mender_utils_key_value_list_free(ctx->payloads.values[index].depends);
7✔
500
                for (size_t i = 0; i < ctx->payloads.values[index].clears_provides_size; i++) {
13✔
501
                    free(ctx->payloads.values[index].clears_provides[i]);
6✔
502
                }
503
                free(ctx->payloads.values[index].clears_provides);
7✔
504
#endif
505
            }
506
            free(ctx->payloads.values);
7✔
507
        }
508
        free(ctx->file.name);
13✔
509
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
510
        mender_utils_key_value_list_free(ctx->artifact_info.provides);
13✔
511
        mender_utils_key_value_list_free(ctx->artifact_info.depends);
13✔
512
        mender_artifact_checksum_t *next;
513
        for (mender_artifact_checksum_t *checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = next) {
36✔
514
            free(checksum->filename);
23✔
515
            mender_sha256_finish(checksum->context, NULL);
23✔
516
            next = checksum->next;
23✔
517
            free(checksum);
23✔
518
        }
519
        ctx->artifact_info.checksums = NULL;
13✔
520
#endif
521
        free(ctx);
13✔
522
    }
523
}
13✔
524

525
static mender_err_t
526
artifact_parse_tar_header(mender_artifact_ctx_t *ctx) {
75✔
527
    assert(NULL != ctx);
75✔
528

529
    char *tmp;
530
    bool  in_header_tar;
531

532
    /* Check if enough data are received (at least one block) */
533
    if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
75✔
534
        return MENDER_OK;
7✔
535
    }
536

537
    /* Cast block to TAR header structure */
538
    mender_artifact_tar_header_t *tar_header = (mender_artifact_tar_header_t *)ctx->input.data;
68✔
539

540
    /* Check if file name is provided, else the end of the current TAR file is reached */
541
    if ('\0' == tar_header->name[0]) {
68✔
542

543
        /* Check if enough data are received (at least 2 blocks) */
544
        if (ctx->input.length < 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE) {
18✔
UNCOV
545
            return MENDER_OK;
×
546
        }
547

548
        in_header_tar = (NULL != ctx->file.name) && StringEqual(ctx->file.name, "header.tar");
18✔
549

550
        /* Remove the TAR file name */
551
        if (NULL != ctx->file.name) {
18✔
552
            char *substring = mender_utils_strrstr(ctx->file.name, ".tar");
12✔
553
            if (NULL != substring) {
12✔
554
                *substring = '\0';
12✔
555
                substring  = mender_utils_strrstr(ctx->file.name, ".tar");
12✔
556
                if (NULL != substring) {
12✔
UNCOV
557
                    *(substring + strlen(".tar")) = '\0';
×
558
                } else {
559
                    FREE_AND_NULL(ctx->file.name);
12✔
560
                }
561
            } else {
UNCOV
562
                FREE_AND_NULL(ctx->file.name);
×
563
            }
564
        }
565

566
        /* Shift data in the buffer */
567
        /* header.tar has a checksum entry in the manifest as a whole so we need
568
           to include its empty blocks into checksum calculation */
569
        if (in_header_tar) {
18✔
570
            if (MENDER_OK
6✔
571
                != artifact_shift_and_checksum_data(ctx, 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE, "header.tar", 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
6✔
572
                mender_log_error("Unable to shift and checksum input data");
×
UNCOV
573
                return MENDER_FAIL;
×
574
            }
575
        } else {
576
            if (MENDER_OK != artifact_shift_data(ctx, 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
12✔
577
                mender_log_error("Unable to shift input data");
×
UNCOV
578
                return MENDER_FAIL;
×
579
            }
580
        }
581

582
        return MENDER_DONE;
18✔
583
    }
584

585
    /* Check magic */
586
    if (strncmp(tar_header->magic, "ustar", strlen("ustar"))) {
50✔
587
        /* Invalid magic */
588
        mender_log_error("Invalid magic");
1✔
589
        return MENDER_FAIL;
1✔
590
    }
591

592
    /* Compute the new file name */
593
    if (NULL != ctx->file.name) {
49✔
594
        size_t str_length = strlen(ctx->file.name) + strlen("/") + strlen(tar_header->name) + 1;
20✔
595
        if (NULL == (tmp = (char *)malloc(str_length))) {
20✔
596
            mender_log_error("Unable to allocate memory");
×
UNCOV
597
            return MENDER_FAIL;
×
598
        }
599
        snprintf(tmp, str_length, "%s/%s", ctx->file.name, tar_header->name);
20✔
600
        free(ctx->file.name);
20✔
601
    } else {
602
        if (NULL == (tmp = strdup(tar_header->name))) {
29✔
603
            mender_log_error("Unable to allocate memory");
×
UNCOV
604
            return MENDER_FAIL;
×
605
        }
606
    }
607
    ctx->file.name = tmp;
49✔
608

609
    /* Retrieve file size */
610
    assert(sizeof(size_t) >= sizeof(unsigned long));
611
    char *end_ptr;
612
    errno = 0; /* to distinguish between success/failure */
49✔
613

614
    ctx->file.size = strtoul(tar_header->size, &end_ptr, 8);
49✔
615
    if ((end_ptr == tar_header->size) /* no conversion */
49✔
616
        || (0 != errno)) {            /* out of range (for unsigned long) */
49✔
617
        mender_log_error("Unable to retrieve file size");
×
UNCOV
618
        return MENDER_FAIL;
×
619
    }
620

621
    ctx->file.index = 0;
49✔
622

623
    /* Shift data in the buffer */
624
    /* header.tar has a checksum entry in the manifest as a whole so we need
625
       to include its TAR header blocks into checksum calculation */
626
    in_header_tar = mender_utils_strbeginswith(ctx->file.name, "header.tar/");
49✔
627
    if (in_header_tar) {
49✔
628
        if (MENDER_OK != artifact_shift_and_checksum_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE, "header.tar", MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
14✔
629
            mender_log_error("Unable to shift and checksum input data");
×
UNCOV
630
            return MENDER_FAIL;
×
631
        }
632
    } else {
633
        if (MENDER_OK != artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
35✔
634
            mender_log_error("Unable to shift input data");
×
UNCOV
635
            return MENDER_FAIL;
×
636
        }
637
    }
638

639
    /* Update the stream state machine */
640
    ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA;
49✔
641

642
    return MENDER_DONE;
49✔
643
}
644

645
static mender_err_t
646
artifact_read_version(mender_artifact_ctx_t *ctx) {
8✔
647

648
    assert(NULL != ctx);
8✔
649
    cJSON       *object = NULL;
8✔
650
    mender_err_t ret    = MENDER_DONE;
8✔
651

652
    /* Check if all data have been received */
653
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
8✔
UNCOV
654
        return MENDER_OK;
×
655
    }
656

657
    /* Check version file */
658
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
8✔
659
        mender_log_error("Unable to allocate memory");
×
UNCOV
660
        return MENDER_FAIL;
×
661
    }
662
    cJSON *json_format = cJSON_GetObjectItemCaseSensitive(object, "format");
8✔
663
    if (true == cJSON_IsString(json_format)) {
8✔
664
        if (!StringEqual(cJSON_GetStringValue(json_format), MENDER_ARTIFACT_SUPPORTED_FORMAT)) {
8✔
665
            mender_log_error("Invalid version format");
×
666
            ret = MENDER_FAIL;
×
UNCOV
667
            goto END;
×
668
        }
669
    } else {
670
        mender_log_error("Invalid version file");
×
671
        ret = MENDER_FAIL;
×
UNCOV
672
        goto END;
×
673
    }
674
    cJSON *json_version = cJSON_GetObjectItemCaseSensitive(object, "version");
8✔
675
    if (true == cJSON_IsNumber(json_version)) {
8✔
676
        if (MENDER_ARTIFACT_SUPPORTED_VERSION != (int)cJSON_GetNumberValue(json_version)) {
8✔
677
            mender_log_error("Invalid version value");
×
678
            ret = MENDER_FAIL;
×
UNCOV
679
            goto END;
×
680
        }
681
    } else {
682
        mender_log_error("Invalid version file");
×
683
        ret = MENDER_FAIL;
×
UNCOV
684
        goto END;
×
685
    }
686
    mender_log_debug("Artifact has valid version");
687

688
    /* Shift data in the buffer */
689
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE), "version", ctx->file.size)) {
8✔
690
        mender_log_error("Unable to shift and checksum input data");
×
691
        ret = MENDER_FAIL;
×
UNCOV
692
        goto END;
×
693
    }
694

695
END:
8✔
696

697
    /* Release memory */
698
    cJSON_Delete(object);
8✔
699

700
    return ret;
8✔
701
}
702

703
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
704
mender_err_t
705
mender_artifact_get_device_type(mender_artifact_ctx_t *ctx, const char **device_type) {
2✔
706

707
    assert(NULL != ctx);
2✔
708
    assert(NULL != device_type);
2✔
709

710
    mender_key_value_list_t *item = ctx->artifact_info.depends;
2✔
711
    while (NULL != item) {
2✔
712
        if (NULL != item->key) {
1✔
713
            if (StringEqual(MENDER_ARTIFACT_DEVICE_TYPE_KEY, item->key)) {
1✔
714
                *device_type = item->value;
1✔
715
                return MENDER_OK;
1✔
716
            }
717
        }
UNCOV
718
        item = item->next;
×
719
    }
720
    return MENDER_FAIL;
1✔
721
}
722

723
static char *artifact_file = NULL;
724

725
char *
NEW
726
mender_artifact_get_filename(void) {
×
NEW
727
    return artifact_file;
×
728
}
729

730
static mender_err_t
731
artifact_read_manifest(mender_artifact_ctx_t *ctx) {
8✔
732

733
    assert(NULL != ctx);
8✔
734

735
    /* Check if all data has been received */
736
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
8✔
UNCOV
737
        return MENDER_OK;
×
738
    }
739

740
    /*  The expected format matches the output of sha256sum: sum and the name of the file separated by two spaces
741
        1d0b820130ae028ce8a79b7e217fe505a765ac394718e795d454941487c53d32  data/0000/update.ext4
742
        4d480539cdb23a4aee6330ff80673a5af92b7793eb1c57c4694532f96383b619  header.tar.gz
743
        52c76ab66947278a897c2a6df8b4d77badfa343fec7ba3b2983c2ecbbb041a35  version
744
    */
745

746
    /* Read data line by line */
747
    char *line = ctx->input.data;
8✔
748
    char *end  = line + ctx->input.length;
8✔
749
    while (line < end) {
30✔
750
        char *next = strchr(line, '\n');
30✔
751
        if (NULL == next) {
30✔
752
            break;
7✔
753
        }
754
        *next = '\0';
23✔
755

756
        /* Process line */
757
        char *separator = strstr(line, "  ");
23✔
758
        if (NULL == separator) {
23✔
UNCOV
759
            mender_log_error("Invalid manifest file");
×
UNCOV
760
            return MENDER_FAIL;
×
761
        }
762
        *separator = '\0';
23✔
763

764
        const char *checksum_str = line;
23✔
765
        const char *filename     = separator + 2;
23✔
766

767
        /* Store the filename in the artifact */
768
        artifact_file = (char *)filename;
23✔
769

770
        /* We do not support compressed artifacts */
771
        if (mender_utils_strbeginswith(filename, "header.tar") && is_compressed(filename)) {
23✔
772
            mender_log_error("Artifact compression is not supported");
1✔
773
            return MENDER_FAIL;
1✔
774
        }
775

776
        /* Useful when debugging artifact integrity check failures */
777
        mender_log_debug("%s  %s", checksum_str, filename);
778

779
        /* Make sure digest is of expected length (two hex per byte) */
780
        if ((MENDER_DIGEST_BUFFER_SIZE * 2) != strlen(checksum_str)) {
22✔
UNCOV
781
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
UNCOV
782
            return MENDER_FAIL;
×
783
        }
784

785
        /* Get checksum entry for the file (creates one if not found) */
786
        mender_artifact_checksum_t *checksum;
787
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, filename))) {
22✔
788
            /* Error already logged */
UNCOV
789
            return MENDER_FAIL;
×
790
        }
791

792
        /* Populate with manifest checksum */
793
        if (!mender_utils_hexdump_to_bytes(checksum_str, checksum->manifest, MENDER_DIGEST_BUFFER_SIZE)) {
22✔
UNCOV
794
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
UNCOV
795
            return MENDER_FAIL;
×
796
        }
797

798
        ///* Move to the next line */
799
        line = next + 1;
22✔
800
    }
801

802
    /* Shift data in the buffer */
803
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
7✔
804
        mender_log_error("Unable to shift input data");
×
UNCOV
805
        return MENDER_FAIL;
×
806
    }
807

808
    return MENDER_DONE;
7✔
809
}
810

811
static mender_err_t
812
artifact_parse_provides_depends(cJSON *json_provides_depends, mender_key_value_list_t **provides_depends) {
18✔
813

814
    assert(NULL != json_provides_depends);
18✔
815
    assert(NULL != provides_depends);
18✔
816

817
    /* Create linked-list from json object */
818
    /* The elements can either be a string or an array of strings */
819
    cJSON *json_element = NULL;
18✔
820
    cJSON_ArrayForEach(json_element, json_provides_depends) {
36✔
821
        if (cJSON_IsString(json_element)) {
18✔
822
            if (MENDER_OK != mender_utils_key_value_list_create_node(json_element->string, json_element->valuestring, provides_depends)) {
12✔
UNCOV
823
                mender_log_error("Unable to create linked list node for string element");
×
UNCOV
824
                goto ERROR;
×
825
            }
826
        } else if (cJSON_IsArray(json_element)) {
6✔
827
            cJSON *json_element_value = NULL;
6✔
828
            cJSON_ArrayForEach(json_element_value, json_element) {
12✔
829
                if (MENDER_OK != mender_utils_key_value_list_create_node(json_element->string, json_element_value->valuestring, provides_depends)) {
6✔
UNCOV
830
                    mender_log_error("Unable to create linked list node for array element");
×
UNCOV
831
                    goto ERROR;
×
832
                }
833
            }
834
        } else {
UNCOV
835
            mender_log_error("Invalid header-info file element type");
×
UNCOV
836
            goto ERROR;
×
837
        }
838
    }
839

840
    return MENDER_OK;
18✔
841

UNCOV
842
ERROR:
×
843
    /* Free linked list in case of error */
844
    mender_utils_key_value_list_free(*provides_depends);
×
845
    return MENDER_FAIL;
×
846
}
847
#endif
848

849
static mender_err_t
850
artifact_read_header_info(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data) {
7✔
851

852
    assert(NULL != ctx);
7✔
853
    cJSON       *object = NULL;
7✔
854
    mender_err_t ret    = MENDER_DONE;
7✔
855
    size_t       rounded_file_size;
856

857
    /* Check if all data have been received */
858
    if ((NULL == ctx->input.data) || (ctx->input.length < (rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)))) {
7✔
UNCOV
859
        return MENDER_OK;
×
860
    }
861

862
    /* Read header-info */
863
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
7✔
UNCOV
864
        mender_log_error("Unable to allocate memory");
×
UNCOV
865
        return MENDER_FAIL;
×
866
    }
867
    cJSON *json_payloads = cJSON_GetObjectItemCaseSensitive(object, "payloads");
7✔
868
    if (true == cJSON_IsArray(json_payloads)) {
7✔
869
        ctx->payloads.size = cJSON_GetArraySize(json_payloads);
7✔
870
        if (NULL == (ctx->payloads.values = (mender_artifact_payload_t *)calloc(ctx->payloads.size, sizeof(mender_artifact_payload_t)))) {
7✔
UNCOV
871
            mender_log_error("Unable to allocate memory");
×
UNCOV
872
            ret = MENDER_FAIL;
×
873
            goto END;
×
874
        }
875
        size_t index        = 0;
7✔
876
        cJSON *json_payload = NULL;
7✔
877
        cJSON_ArrayForEach(json_payload, json_payloads) {
13✔
878
            if (true == cJSON_IsObject(json_payload)) {
7✔
879
                cJSON *json_payload_type = cJSON_GetObjectItemCaseSensitive(json_payload, "type");
7✔
880
                if (cJSON_IsString(json_payload_type)) {
7✔
881
                    if (NULL == (ctx->payloads.values[index].type = strdup(cJSON_GetStringValue(json_payload_type)))) {
7✔
882
                        mender_log_error("Unable to allocate memory");
×
UNCOV
883
                        ret = MENDER_FAIL;
×
UNCOV
884
                        goto END;
×
885
                    }
886
                    const char *payload_type = ctx->payloads.values[index].type;
7✔
887
                    /* Choose update module */
888
                    dl_data->update_module = mender_update_module_get(payload_type);
7✔
889
                    if (NULL == dl_data->update_module) {
7✔
890
                        /* Content is not supported by the mender-mcu-client */
891
                        mender_log_error("Unable to handle artifact type '%s'", payload_type);
×
892
                        ret = MENDER_FAIL;
×
893
                        goto END;
×
894
                    }
895
                    /* Add the payload type to deployment data  */
896
                    if (MENDER_OK != mender_deployment_data_add_payload_type(dl_data->deployment, payload_type)) {
7✔
897
                        /* Error already logged */
898
                        ret = MENDER_FAIL;
1✔
899
                        goto END;
1✔
900
                    }
901
                } else {
902
                    mender_log_error("Invalid header-info file");
×
UNCOV
903
                    ret = MENDER_FAIL;
×
UNCOV
904
                    goto END;
×
905
                }
906
            } else {
UNCOV
907
                mender_log_error("Invalid header-info file");
×
UNCOV
908
                ret = MENDER_FAIL;
×
UNCOV
909
                goto END;
×
910
            }
911
            index++;
6✔
912
        }
913

914
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
915
        cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
6✔
916
        if (cJSON_IsObject(json_provides)) {
6✔
917
            if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->artifact_info.provides))) {
6✔
918
                mender_log_error("Unable to parse artifact_provides");
×
UNCOV
919
                ret = MENDER_FAIL;
×
UNCOV
920
                goto END;
×
921
            }
922
        }
923

924
        cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
6✔
925
        if (cJSON_IsObject(json_depends)) {
6✔
926
            if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->artifact_info.depends))) {
6✔
927
                mender_log_error("Unable to parse artifact_depends");
×
928
                ret = MENDER_FAIL;
×
929
                goto END;
×
930
            }
931
        }
932
#endif
933

934
    } else {
UNCOV
935
        mender_log_error("Invalid header-info file");
×
936
        ret = MENDER_FAIL;
×
937
        goto END;
×
938
    }
939

940
    /* Shift data in the buffer */
941
    /* header.tar has a checksum entry in the manifest as a whole */
942
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
6✔
UNCOV
943
        mender_log_error("Unable to shift and checksum input data");
×
944
        ret = MENDER_FAIL;
×
945
        goto END;
×
946
    }
947

948
END:
6✔
949

950
    /* Release memory */
951
    cJSON_Delete(object);
7✔
952

953
    return ret;
7✔
954
}
955

956
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
957
static mender_err_t
958
artifact_read_type_info(mender_artifact_ctx_t *ctx) {
6✔
959

960
    assert(NULL != ctx);
6✔
961
    cJSON       *object = NULL;
6✔
962
    mender_err_t ret    = MENDER_DONE;
6✔
963
    size_t       index  = 0;
6✔
964
    size_t       rounded_file_size;
965

966
    /* Check if all data have been received */
967
    if ((NULL == ctx->input.data) || (ctx->input.length < (rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)))) {
6✔
UNCOV
968
        return MENDER_OK;
×
969
    }
970

971
    /* Read type-info */
972
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
6✔
UNCOV
973
        mender_log_error("Unable to allocate memory");
×
UNCOV
974
        return MENDER_FAIL;
×
975
    }
976

977
    /* Check if payload index is valid */
978
    if (NULL == ctx->payloads.values[index].type) {
6✔
UNCOV
979
        mender_log_error("Invalid artifact format; no payload found for index %d", index);
×
UNCOV
980
        ret = MENDER_FAIL;
×
UNCOV
981
        goto END;
×
982
    }
983

984
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
985
    cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
6✔
986
    if (cJSON_IsObject(json_provides)) {
6✔
987
        if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->payloads.values[index].provides))) {
6✔
988
            mender_log_error("Unable to parse artifact_provides");
×
989
            ret = MENDER_FAIL;
×
990
            goto END;
×
991
        }
992
    }
993

994
    cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
6✔
995
    if (cJSON_IsObject(json_depends)) {
6✔
UNCOV
996
        if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->payloads.values[index].depends))) {
×
997
            mender_log_error("Unable to parse artifact_depends");
×
998
            ret = MENDER_FAIL;
×
999
            goto END;
×
1000
        }
1001
    }
1002

1003
    cJSON *json_clears_provides = cJSON_GetObjectItemCaseSensitive(object, "clears_artifact_provides");
6✔
1004
    if (cJSON_IsArray(json_clears_provides)) {
6✔
1005
        ctx->payloads.values[index].clears_provides_size = cJSON_GetArraySize(json_clears_provides);
6✔
1006
        ctx->payloads.values[index].clears_provides      = (char **)calloc(ctx->payloads.values[index].clears_provides_size, sizeof(char *));
6✔
1007
        if (NULL == ctx->payloads.values[index].clears_provides) {
6✔
1008
            mender_log_error("Unable to allocate memory");
×
UNCOV
1009
            ret = MENDER_FAIL;
×
UNCOV
1010
            goto END;
×
1011
        }
1012

1013
        size_t i                            = 0;
6✔
1014
        cJSON *json_clears_provides_element = NULL;
6✔
1015

1016
        cJSON_ArrayForEach(json_clears_provides_element, json_clears_provides) {
12✔
1017
            if (cJSON_IsString(json_clears_provides_element)) {
6✔
1018
                char *clears_provides = strdup(json_clears_provides_element->valuestring);
6✔
1019
                if (NULL == clears_provides) {
6✔
UNCOV
1020
                    mender_log_error("Unable to allocate memory");
×
UNCOV
1021
                    ret = MENDER_FAIL;
×
UNCOV
1022
                    goto END;
×
1023
                }
1024
                ctx->payloads.values[index].clears_provides[i] = clears_provides;
6✔
1025
                i++;
6✔
1026
            } else {
UNCOV
1027
                mender_log_error("Invalid header-info file");
×
UNCOV
1028
                ret = MENDER_FAIL;
×
1029
                goto END;
×
1030
            }
1031
        }
1032
    }
1033
#endif
1034

1035
    /* Shift data in the buffer */
1036
    /* header.tar has a checksum entry in the manifest as a whole */
1037
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
6✔
1038
        mender_log_error("Unable to shift and checksum input data");
×
UNCOV
1039
        ret = MENDER_FAIL;
×
UNCOV
1040
        goto END;
×
1041
    }
1042

1043
END:
6✔
1044

1045
    /* Release memory */
1046
    cJSON_Delete(object);
6✔
1047

1048
    return ret;
6✔
1049
}
1050
#endif
1051

1052
static mender_err_t
1053
artifact_read_meta_data(mender_artifact_ctx_t *ctx) {
1✔
1054
    assert(NULL != ctx);
1✔
1055
    size_t rounded_file_size;
1056

1057
    /* Retrieve payload index. We expect "header.tar/headers/%u/meta-data" where
1058
     * %u is the index. Yes sscanf(3) would be nice, but we've experienced
1059
     * unexplained segmentation faults on some hardware when using it. */
1060
    const char *const prefix = "header.tar/headers/";
1✔
1061
    if (!mender_utils_strbeginswith(ctx->file.name, prefix)) {
1✔
UNCOV
1062
        mender_log_error("Invalid artifact format");
×
UNCOV
1063
        return MENDER_FAIL;
×
1064
    }
1065

1066
    assert(sizeof(size_t) >= sizeof(unsigned long));
1067
    const char *start_ptr = ctx->file.name + strlen(prefix);
1✔
1068
    char       *end_ptr;
1069
    errno = 0; /* to distinguish between success/failure */
1✔
1070

1071
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
1✔
1072
    if ((end_ptr == start_ptr)              /* no conversion */
1✔
1073
        || (0 != errno)                     /* out of range (for unsigned long) */
1✔
1074
        || (index >= ctx->payloads.size)) { /* index out of bounds */
1✔
UNCOV
1075
        mender_log_error("Invalid artifact format");
×
UNCOV
1076
        return MENDER_FAIL;
×
1077
    }
1078

1079
    assert(NULL != end_ptr);
1✔
1080
    assert(StringEqualN(end_ptr, "/meta-data", 10)); /* just one last sanity check */
1✔
1081

1082
    /* Check size of the meta-data */
1083
    if (0 == ctx->file.size) {
1✔
1084
        /* Nothing to do */
1085
        return MENDER_DONE;
×
1086
    }
1087

1088
    /* Check if all data have been received */
1089
    rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE);
1✔
1090
    if ((NULL == ctx->input.data) || (ctx->input.length < rounded_file_size)) {
1✔
UNCOV
1091
        return MENDER_OK;
×
1092
    }
1093

1094
    /* Read meta-data */
1095
    if (NULL == (ctx->payloads.values[index].meta_data = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
1✔
UNCOV
1096
        mender_log_error("Unable to allocate memory");
×
UNCOV
1097
        return MENDER_FAIL;
×
1098
    }
1099

1100
    /* Shift data in the buffer */
1101
    /* header.tar has a checksum entry in the manifest as a whole */
1102
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
1✔
UNCOV
1103
        mender_log_error("Unable to shift and checksum input data");
×
UNCOV
1104
        return MENDER_FAIL;
×
1105
    }
1106

1107
    return MENDER_DONE;
1✔
1108
}
1109

1110
/**
1111
 * @brief Callback function to be invoked to perform the treatment of the data from the artifact
1112
 * @param deployment_id Deployment ID
1113
 * @param type Type from header-info payloads
1114
 * @param artifact_name Artifact name
1115
 * @param meta_data Meta-data from header tarball
1116
 * @param filename Artifact filename
1117
 * @param size Artifact file size
1118
 * @param data Artifact data
1119
 * @param index Artifact data index
1120
 * @param length Artifact data length
1121
 * @param dl_data Download data for the artifact
1122
 * @return MENDER_OK if the function succeeds, error code if an error occurred
1123
 */
1124
static mender_err_t
1125
process_artifact_data_callback(const char                      *deployment_id,
6✔
1126
                               const char                      *type,
1127
                               const char                      *artifact_name,
1128
                               const cJSON                     *meta_data,
1129
                               const char                      *filename,
1130
                               size_t                           size,
1131
                               void                            *data,
1132
                               size_t                           index,
1133
                               size_t                           length,
1134
                               mender_artifact_download_data_t *dl_data) {
1135

1136
    assert(NULL != type);
6✔
1137
    mender_err_t ret = MENDER_FAIL;
6✔
1138

1139
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_INF
1140
    if (size > 0) {
6✔
1141
        static size_t download_progress = 0;
1142
        /* New update */
1143
        if (0 == index) {
6✔
1144
            download_progress = 0;
6✔
1145
        }
1146

1147
        /* Update every 10% */
1148
        if (((index * 10) / size) > download_progress) {
6✔
UNCOV
1149
            download_progress = (index * 10) / size;
×
UNCOV
1150
            mender_log_info("Downloading '%s' %zu0%%... [%zu/%zu]", type, download_progress, index, size);
×
1151
        }
1152
    }
1153
#endif
1154

1155
    /* Invoke update module download callback */
1156
    struct mender_update_download_state_data_s download_state_data
6✔
1157
        = { deployment_id, artifact_name, type, meta_data, filename, size, data, index, length, false };
1158
    mender_update_state_data_t state_data = { .download_state_data = &download_state_data };
6✔
1159
    if (MENDER_OK != (ret = dl_data->update_module->callbacks[MENDER_UPDATE_STATE_DOWNLOAD](MENDER_UPDATE_STATE_DOWNLOAD, state_data))) {
6✔
UNCOV
1160
        mender_log_error("An error occurred while processing data of the artifact '%s' of type '%s'", artifact_name, type);
×
UNCOV
1161
        return ret;
×
1162
    }
1163

1164
    return MENDER_OK;
6✔
1165
}
1166

1167
static mender_err_t
1168
artifact_read_data_prepare(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache) {
6✔
1169
    /* First, retrieve payload index. We expect "data/%u.tar" where %u is the
1170
     * index. Yes sscanf(3) would be nice, but we've experienced unexplained
1171
     * segmentation faults on some hardware when using it. */
1172
    const char *const prefix = "data/";
6✔
1173
    if (!mender_utils_strbeginswith(ctx->file.name, prefix)) {
6✔
UNCOV
1174
        mender_log_error("Invalid artifact format");
×
UNCOV
1175
        return MENDER_FAIL;
×
1176
    }
1177

1178
    size_t file_name_length = strlen(ctx->file.name);
6✔
1179
    /* We check the length to make sure we only check for compression on the
1180
     * payload itself - not the files inside the payload */
1181
    if ((strlen("data/xxxx.tar.xx") == file_name_length) || (strlen("data/xxxx.tar.xxx") == file_name_length)) {
6✔
1182
        /*
1183
        * We allow compressed files _inside_ a payload:
1184
        *   'data/0000.tar/compressed.tar.gz'
1185
        * But not a compressed payload:
1186
        *   'data/0000.tar[.gz|.xz|.zst]'
1187
        **/
UNCOV
1188
        if (is_compressed(ctx->file.name)) {
×
UNCOV
1189
            mender_log_error("Artifact compression is not supported");
×
UNCOV
1190
            return MENDER_FAIL;
×
1191
        }
1192
    }
1193

1194
    assert(sizeof(size_t) >= sizeof(unsigned long));
1195
    const char *start_ptr = ctx->file.name + strlen(prefix);
6✔
1196
    char       *end_ptr;
1197
    errno = 0; /* to distinguish between success/failure */
6✔
1198

1199
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
6✔
1200
    if ((end_ptr == start_ptr)              /* no conversion */
6✔
1201
        || (0 != errno)                     /* out of range (for unsigned long) */
6✔
1202
        || (index >= ctx->payloads.size)) { /* index out of bounds */
6✔
UNCOV
1203
        mender_log_error("Invalid artifact format");
×
UNCOV
1204
        return MENDER_FAIL;
×
1205
    }
1206

1207
    assert(NULL != end_ptr);
6✔
1208
    assert(StringEqualN(end_ptr, ".tar", 4)); /* just one last sanity check */
6✔
1209

1210
    const char *payload_type = ctx->payloads.values[index].type;
6✔
1211

1212
    /* Retrieve ID and artifact name */
1213
    if (MENDER_OK != mender_deployment_data_get_id(dl_data->deployment, &(mdata_cache->deployment_id))) {
6✔
UNCOV
1214
        mender_log_error("Unable to get ID from the deployment data");
×
UNCOV
1215
        return MENDER_FAIL;
×
1216
    }
1217
    if (MENDER_OK != mender_deployment_data_get_artifact_name(dl_data->deployment, &(mdata_cache->artifact_name))) {
6✔
UNCOV
1218
        mender_log_error("Unable to get artifact name from the deployment data");
×
UNCOV
1219
        return MENDER_FAIL;
×
1220
    }
1221

1222
    mdata_cache->payload_type = payload_type;
6✔
1223
    mdata_cache->meta_data    = ctx->payloads.values[index].meta_data;
6✔
1224
    mdata_cache->filename     = strstr(ctx->file.name, ".tar") + strlen(".tar") + 1;
6✔
1225

1226
    /* The filename will be something like
1227
     * 'data/0000.tar/zephyr.signed.bin'. But the manifest will hold
1228
     * 'data/0000/zephyr.signed.bin'. Hence, we need to remove the
1229
     * '.tar' extension from the string.
1230
     */
1231
    if (NULL == (mdata_cache->checksum_fname = strdup(ctx->file.name))) {
6✔
UNCOV
1232
        mender_log_error("Unable to allocate memory");
×
UNCOV
1233
        return MENDER_FAIL;
×
1234
    }
1235
    bool done = false;
6✔
1236
    for (char *ch = strstr(mdata_cache->checksum_fname, ".tar"); (NULL != ch) && !done; ch++) {
66✔
1237
        /* Don't worry! The call to strlen() on a static string should
1238
         * be optimized out by the compiler */
1239
        done = (*ch = ch[strlen(".tar")]) == '\0';
60✔
1240
    }
1241

1242
    mdata_cache->valid = true;
6✔
1243
    return MENDER_OK;
6✔
1244
}
1245

1246
static mender_err_t
1247
artifact_read_data(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache) {
6✔
1248

1249
    assert(NULL != ctx);
6✔
1250
    mender_err_t ret;
1251

1252
    /* Check size of the data */
1253
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
6✔
1254
        /* Nothing to do */
UNCOV
1255
        return MENDER_DONE;
×
1256
    }
1257

1258
    /* Parse data until the end of the file has been reached */
1259
    do {
1260

1261
        /* Check if enough data are received (at least one block) */
1262
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
6✔
UNCOV
1263
            return MENDER_OK;
×
1264
        }
1265

1266
        /* Compute length */
1267
        size_t length
6✔
1268
            = ((ctx->file.size - ctx->file.index) > MENDER_ARTIFACT_STREAM_BLOCK_SIZE) ? MENDER_ARTIFACT_STREAM_BLOCK_SIZE : (ctx->file.size - ctx->file.index);
6✔
1269

1270
        /* Invoke the download artifact callback */
1271
        ret = process_artifact_data_callback(mdata_cache->deployment_id,
6✔
1272
                                             mdata_cache->payload_type,
1273
                                             mdata_cache->artifact_name,
1274
                                             mdata_cache->meta_data,
6✔
1275
                                             mdata_cache->filename,
1276
                                             ctx->file.size,
1277
                                             ctx->input.data,
1278
                                             ctx->file.index,
1279
                                             length,
1280
                                             dl_data);
1281
        if (MENDER_OK != ret) {
6✔
UNCOV
1282
            mender_log_error("An error occurred");
×
UNCOV
1283
            return ret;
×
1284
        }
1285

1286
        /* Update index */
1287
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
6✔
1288

1289
        /* Shift data in the buffer */
1290
        if (MENDER_OK != (ret = artifact_shift_and_checksum_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE, mdata_cache->checksum_fname, length))) {
6✔
1291
            mender_log_error("Unable to shift and checksum input data");
×
1292
            return ret;
×
1293
        }
1294

1295
    } while (ctx->file.index < ctx->file.size);
6✔
1296

1297
    return MENDER_DONE;
6✔
1298
}
1299

1300
static mender_err_t
1301
artifact_drop_file(mender_artifact_ctx_t *ctx) {
×
1302

UNCOV
1303
    assert(NULL != ctx);
×
1304
    mender_err_t ret;
1305

1306
    /* Check size of the data */
UNCOV
1307
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1308
        /* Nothing to do */
UNCOV
1309
        return MENDER_DONE;
×
1310
    }
1311

1312
    /* Parse data until the end of the file has been reached */
1313
    do {
1314

1315
        /* Check if enough data are received (at least one block) */
1316
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
1317
            return MENDER_OK;
×
1318
        }
1319

1320
        /* Update index */
UNCOV
1321
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1322

1323
        /* Shift data in the buffer */
UNCOV
1324
        if (MENDER_OK != (ret = artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
1325
            mender_log_error("Unable to shift input data");
×
1326
            return ret;
×
1327
        }
1328

UNCOV
1329
    } while (ctx->file.index < ctx->file.size);
×
1330

UNCOV
1331
    return MENDER_DONE;
×
1332
}
1333

1334
static mender_err_t
1335
artifact_shift_and_checksum_data(mender_artifact_ctx_t *ctx, size_t length, const char *checksum_key, size_t checksum_len) {
47✔
1336
    assert(NULL != ctx);
47✔
1337
    assert(ctx->input.length >= length);
47✔
1338
    assert(checksum_len <= length);
47✔
1339

1340
    if (0 == length) {
47✔
UNCOV
1341
        return MENDER_OK;
×
1342
    }
1343

1344
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
1345
    if ((NULL != checksum_key) && (0 != checksum_len)) {
47✔
1346
        mender_artifact_checksum_t *checksum;
1347
        /* Get checksum entry (create one if needed) */
1348
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, checksum_key))) {
47✔
1349
            /* Error already logged */
1350
            return MENDER_FAIL;
×
1351
        }
1352

1353
        if (MENDER_OK != mender_sha256_update(checksum->context, ctx->input.data, checksum_len)) {
47✔
UNCOV
1354
            mender_log_error("Failed to update update checksum");
×
UNCOV
1355
            return MENDER_FAIL;
×
1356
        }
1357
    }
1358
#else
1359
    /* Only to make the arguments "used" in this case. */
1360
    (void)checksum_key;
1361
    (void)checksum_len;
1362
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
1363

1364
    return artifact_shift_data(ctx, length);
47✔
1365
}
1366

1367
static mender_err_t
1368
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
101✔
1369

1370
    assert(NULL != ctx);
101✔
1371

1372
    /* Shift data */
1373
    if (length > 0) {
101✔
1374
        if (ctx->input.length > length) {
101✔
1375
            memmove(ctx->input.data, (void *)(((uint8_t *)ctx->input.data) + length), ctx->input.length - length);
95✔
1376
            ctx->input.length -= length;
95✔
1377
            /* Here we could shrink the ctx->input.data buffer, but most likely, we would need to
1378
               grow it again when we receive another batch of data so there's little point in doing
1379
               so. */
1380
        } else {
1381
            ctx->input.length = 0;
6✔
1382
        }
1383
    }
1384

1385
    return MENDER_OK;
101✔
1386
}
1387

1388
static size_t
1389
artifact_round_up(size_t length, size_t incr) {
61✔
1390
    return length + (incr - length % incr) % incr;
61✔
1391
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc