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

mendersoftware / mender-mcu / 1650288124

31 Jan 2025 01:23PM UTC coverage: 25.682%. Remained the same
1650288124

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%)

92 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");
×
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);
×
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);
×
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);
×
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");
×
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);
×
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✔
370
                expected_required = ctx->input.orig_size;
×
371
            } else if (mender_utils_strendswith(ctx->file.name, ".tar")) {
10✔
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 */
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✔
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✔
557
                    *(substring + strlen(".tar")) = '\0';
×
558
                } else {
559
                    FREE_AND_NULL(ctx->file.name);
12✔
560
                }
561
            } else {
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");
×
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");
×
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");
×
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");
×
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");
×
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");
×
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");
×
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✔
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");
×
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;
×
667
            goto END;
×
668
        }
669
    } else {
670
        mender_log_error("Invalid version file");
×
671
        ret = MENDER_FAIL;
×
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;
×
679
            goto END;
×
680
        }
681
    } else {
682
        mender_log_error("Invalid version file");
×
683
        ret = MENDER_FAIL;
×
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;
×
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
        }
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");
×
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 */
UNCOV
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;
×
UNCOV
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");
×
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 */
UNCOV
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");
×
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");
×
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✔
UNCOV
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");
×
UNCOV
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");
×
UNCOV
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
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
984
    cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
6✔
985
    if (cJSON_IsObject(json_provides)) {
6✔
986
        if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->payloads.values[index].provides))) {
6✔
UNCOV
987
            mender_log_error("Unable to parse artifact_provides");
×
UNCOV
988
            ret = MENDER_FAIL;
×
989
            goto END;
×
990
        }
991
    }
992

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

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

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

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

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

1042
END:
6✔
1043

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

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

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

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

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

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

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

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

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

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

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

1106
    return MENDER_DONE;
1✔
1107
}
1108

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

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

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

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

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

1163
    return MENDER_OK;
6✔
1164
}
1165

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1296
    return MENDER_DONE;
6✔
1297
}
1298

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

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

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

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

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

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

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

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

UNCOV
1330
    return MENDER_DONE;
×
1331
}
1332

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

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

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

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

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

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

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

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

1384
    return MENDER_OK;
101✔
1385
}
1386

1387
static size_t
1388
artifact_round_up(size_t length, size_t incr) {
61✔
1389
    return length + (incr - length % incr) % incr;
61✔
1390
}
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