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

mendersoftware / mender-mcu / 2180234949

26 Nov 2025 08:37AM UTC coverage: 60.837%. Remained the same
2180234949

Pull #209

gitlab-ci

mender-test-bot
chore(main): release 0.10.0

Signed-off-by: mender-test-bot <mender@northern.tech>
Pull Request #209: chore(main): release 0.10.0

2456 of 4037 relevant lines covered (60.84%)

70.49 hits per line

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

71.48
/src/core/artifact.c
1
/**
2
 * @file      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 "alloc.h"
24
#include "artifact.h"
25
#include "deployment-data.h"
26
#include "log.h"
27
#include "utils.h"
28

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

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

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

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

74
/**
75
 * @brief Parse header of TAR file
76
 * @param ctx Artifact context
77
 * @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
78
 */
79
static mender_err_t artifact_parse_tar_header(mender_artifact_ctx_t *ctx);
80

81
/**
82
 * @brief Read version file of the artifact
83
 * @param ctx Artifact context
84
 * @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
85
 */
86
static mender_err_t artifact_read_version(mender_artifact_ctx_t *ctx);
87

88
/**
89
 * @brief Read header-info file of the artifact
90
 * @param ctx Artifact context
91
 * @param dl_data Download data for the artifact
92
 * @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
93
 */
94
static mender_err_t artifact_read_header_info(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data);
95

96
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
97
/**
98
 * @brief Read manifest file of the artifact
99
 * @param ctx Artifact context
100
 * @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
101
 */
102
static mender_err_t artifact_read_manifest(mender_artifact_ctx_t *ctx);
103

104
/**
105
 * @brief Read type-info file of the artifact
106
 * @param ctx Artifact context
107
 * @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
108
 */
109
static mender_err_t artifact_read_type_info(mender_artifact_ctx_t *ctx);
110

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

120
/**
121
 * @brief Read meta-data file of the artifact
122
 * @param ctx Artifact context
123
 * @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
124
 */
125
static mender_err_t artifact_read_meta_data(mender_artifact_ctx_t *ctx);
126

127
/**
128
 * @brief Read data file of the artifact
129
 * @param ctx Artifact context
130
 * @param dl_data Download data for the artifact
131
 * @param mdata_cache Cache with metadata for the current data being processed
132
 * @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
133
 */
134
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);
135

136
/**
137
 * @brief Prepare a cache with metadata for the current data being processed
138
 * @param ctx Artifact context
139
 * @param dl_data Download data for the artifact
140
 * @param mdata_cache Cache to populate
141
 * @return MENDER_OK in case of success, error otherwise
142
 */
143
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);
144

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

153
    FREE_AND_NULL(mdata_cache->checksum_fname);
412✔
154
    mdata_cache->valid = false;
412✔
155
}
412✔
156

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

171
/**
172
 * @brief Drop content of the current file of the artifact
173
 * @param ctx Artifact context
174
 * @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
175
 */
176
static mender_err_t artifact_drop_file(mender_artifact_ctx_t *ctx);
177

178
/**
179
 * @brief Shift data after parsing and update the respective checksum context
180
 * @param checksum_key Key under which the checksum for the data is calculated/checked
181
 * @param checksum_len Length of the data to include in the checksum update
182
 * @return MENDER_OK if the function succeeds, error code otherwise
183
 * @see artifact_shift_data()
184
 */
185
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);
186

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

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

203
/**
204
 * @brief Artifact context
205
 */
206
static mender_artifact_ctx_t *artifact_ctx = NULL;
207

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

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

230
    if (NULL == checksum) {
221✔
231
        /* Create new if entry not found */
232
        checksum = (mender_artifact_checksum_t *)mender_calloc(1, sizeof(mender_artifact_checksum_t));
71✔
233
        if (NULL == checksum) {
71✔
234
            mender_log_error("Unable to allocate memory");
×
235
            return NULL;
×
236
        }
237
        checksum->filename = mender_utils_strdup(filename);
71✔
238
        if (NULL == checksum->filename) {
71✔
239
            mender_log_error("Unable to allocate memory");
×
240
            mender_free(checksum);
×
241
            return NULL;
×
242
        }
243
        checksum->next               = ctx->artifact_info.checksums;
71✔
244
        ctx->artifact_info.checksums = checksum;
71✔
245

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

253
    return checksum;
221✔
254
}
255
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
256

257
static mender_err_t
258
is_checksum_valid(mender_artifact_checksum_t *checksum) {
58✔
259
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
260
    unsigned char computed[MENDER_DIGEST_BUFFER_SIZE];
261
    mender_log_debug("Checking integrity for artifact file '%s'", checksum->filename);
35✔
262

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

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

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

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

296
mender_err_t
297
mender_artifact_check_integrity_and_remove_item(mender_artifact_ctx_t *ctx, const char *filename) {
45✔
298
    assert(NULL != ctx);
45✔
299

300
    mender_err_t                 ret          = MENDER_FAIL;
45✔
301
    mender_artifact_checksum_t **checksum_ptr = &ctx->artifact_info.checksums;
45✔
302
    mender_artifact_checksum_t  *checksum     = *checksum_ptr;
45✔
303
    while (NULL != checksum) {
157✔
304
        if (StringEqual(filename, checksum->filename)) {
112✔
305
            ret = is_checksum_valid(checksum);
44✔
306

307
            // Remove the node from the list and free the data
308
            *checksum_ptr = checksum->next;
44✔
309
            mender_free(checksum->filename);
44✔
310
            mender_sha256_finish(checksum->context, NULL);
44✔
311
            mender_free(checksum);
44✔
312
            checksum = *checksum_ptr;
44✔
313
        } else {
314
            checksum_ptr = &checksum->next;
68✔
315
            checksum     = checksum->next;
68✔
316
        }
317
    }
318
    return ret;
45✔
319
}
320

321
mender_err_t
322
mender_artifact_check_integrity_remaining(mender_artifact_ctx_t *ctx) {
14✔
323
    assert(NULL != ctx);
14✔
324
    for (mender_artifact_checksum_t *checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = checksum->next) {
26✔
325
        if (MENDER_OK != is_checksum_valid(checksum)) {
14✔
326
            return MENDER_FAIL;
2✔
327
        }
328
    }
329
    return MENDER_OK;
12✔
330
}
331

332
mender_artifact_ctx_t *
333
mender_artifact_create_ctx(size_t buf_size) {
29✔
334

335
    mender_artifact_ctx_t *ctx;
336

337
    /* Create new context */
338
    if (NULL == (ctx = (mender_artifact_ctx_t *)mender_calloc(1, sizeof(mender_artifact_ctx_t)))) {
29✔
339
        mender_log_error("Unable to allocate memory for artifact context");
×
340
        return NULL;
×
341
    }
342
    if (NULL == (ctx->input.data = mender_malloc(buf_size))) {
29✔
343
        mender_log_error("Unable to allocate memory for artifact context buffer");
×
344
        mender_free(ctx);
×
345
        return NULL;
×
346
    }
347
    ctx->input.size      = buf_size;
29✔
348
    ctx->input.orig_size = buf_size;
29✔
349

350
    /* Save context */
351
    artifact_ctx = ctx;
29✔
352

353
    return ctx;
29✔
354
}
355

356
mender_err_t
357
mender_artifact_get_ctx(mender_artifact_ctx_t **ctx) {
273✔
358

359
    assert(NULL != ctx);
273✔
360

361
    if (NULL == artifact_ctx) {
273✔
362
        return MENDER_FAIL;
1✔
363
    }
364

365
    *ctx = artifact_ctx;
272✔
366
    return MENDER_OK;
272✔
367
}
368

369
static bool
370
is_compressed(const char *filename) {
24✔
371

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

375
    for (size_t i = 0; NULL != compression_suffixes[i]; i++) {
93✔
376
        if (mender_utils_strendswith(filename, compression_suffixes[i])) {
70✔
377
            return true;
1✔
378
        }
379
    }
380

381
    return false;
23✔
382
}
383

384
static struct data_mdata_cache data_mdata_cache = { 0 };
385

386
mender_err_t
387
mender_artifact_process_data(mender_artifact_ctx_t *ctx, void *input_data, size_t input_length, mender_artifact_download_data_t *dl_data) {
274✔
388

389
    assert(NULL != ctx);
274✔
390
    mender_err_t ret = MENDER_OK;
274✔
391
    void        *tmp;
392
    size_t       new_size;
393
    size_t       expected_required;
394

395
    /* Copy data to the end of the internal buffer */
396
    if ((NULL != input_data) && (0 != input_length)) {
274✔
397
        if ((ctx->input.length + input_length) > ctx->input.size) {
273✔
398
            new_size = ctx->input.length + input_length;
14✔
399
            /* data/ files are processed per block for which the original size of the buffer should
400
               be enough, but metadata is processed as whole files so there we expect we will need
401
               more, except for header.tar (and tarballs in general) which are processed
402
               transparently. */
403
            if (mender_utils_strbeginswith(ctx->file.name, "data/")) {
14✔
404
                expected_required = ctx->input.orig_size;
×
405
            } else if (mender_utils_strendswith(ctx->file.name, ".tar")) {
14✔
406
                expected_required = ctx->input.orig_size;
×
407
            } else {
408
                expected_required = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE) + MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
14✔
409
            }
410
            if (new_size > expected_required) {
411
                mender_log_debug("Reallocating artifact context buffer to %zd [SHOULD NOT BE HAPPENING!]", new_size);
412
            }
413
            /* Let's try to get what we expect we will need anyway and if we don't get that much,
414
               let's get the minimum required now, leaving us with a chance to get more later. */
415
            if (NULL == (tmp = mender_realloc(ctx->input.data, MAX(new_size, expected_required)))) {
14✔
416
                if (NULL == (tmp = mender_realloc(ctx->input.data, new_size))) {
2✔
417
                    /* Unable to allocate memory */
418
                    return MENDER_FAIL;
1✔
419
                }
420
                ctx->input.size = new_size;
1✔
421
            } else {
422
                ctx->input.size = MAX(new_size, expected_required);
12✔
423
            }
424
            ctx->input.data = tmp;
13✔
425
        }
426
        memcpy((void *)(((uint8_t *)ctx->input.data) + ctx->input.length), input_data, input_length);
272✔
427
        ctx->input.length += input_length;
272✔
428
    }
429

430
    /* Parse data */
431
    do {
432

433
        /* Treatment depending of the stream state */
434
        if (MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER == ctx->stream_state) {
640✔
435

436
            /* Parse TAR header */
437
            ret = artifact_parse_tar_header(ctx);
412✔
438

439
            /* Processing a new (data) file, invalidate the cache */
440
            data_mdata_cache_invalidate(&data_mdata_cache);
412✔
441

442
        } else if (MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA == ctx->stream_state) {
228✔
443

444
            /* Treatment depending of the file name */
445
            if (StringEqual(ctx->file.name, "version")) {
228✔
446

447
                /* Validate artifact version */
448
                ret = artifact_read_version(ctx);
48✔
449

450
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
451
            } else if (StringEqual(ctx->file.name, "manifest")) {
180✔
452

453
                /* Read manifest file */
454
                ret = artifact_read_manifest(ctx);
36✔
455

456
                /* Early integrity check for version file */
457
                if ((MENDER_DONE == ret) && (MENDER_OK != mender_artifact_check_integrity_and_remove_item(ctx, "version"))) {
36✔
458
                    mender_log_error("Integrity check failed for version file");
1✔
459
                    ret = MENDER_FAIL;
1✔
460
                }
461
#endif
462
            } else if (StringEqual(ctx->file.name, "header.tar/header-info")) {
144✔
463

464
                /* Read header-info file */
465
                ret = artifact_read_header_info(ctx, dl_data);
34✔
466

467
            } else if ((true == mender_utils_strbeginswith(ctx->file.name, "header.tar/headers"))
110✔
468
                       && (true == mender_utils_strendswith(ctx->file.name, "meta-data"))) {
34✔
469

470
                /* Read meta-data file */
471
                ret = artifact_read_meta_data(ctx);
1✔
472

473
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
474
            } else if (mender_utils_strbeginswith(ctx->file.name, "header.tar/headers") && mender_utils_strendswith(ctx->file.name, "type-info")) {
109✔
475

476
                /* Read type-info file */
477
                ret = artifact_read_type_info(ctx);
33✔
478
#endif
479
            } else if ((mender_utils_strbeginswith(ctx->file.name, "data")) && (strlen(ctx->file.name) > strlen("data/xxxx.tar"))) {
76✔
480
                /* Processing data. But the first "file" is data/0000.tar which
481
                   is not a real file, it's just the beginning of the tarball
482
                   for which we don't need to do anything here. Hence the
483
                   strlen() check above. */
484
                if (!data_mdata_cache.valid) {
33✔
485

486
                    /* Early integrity check for header.tar */
487
                    if (MENDER_OK != mender_artifact_check_integrity_and_remove_item(ctx, "header.tar")) {
21✔
488
                        mender_log_error("Integrity check failed for header.tar");
1✔
489
                        ret = MENDER_FAIL;
1✔
490
                    } else {
491
                        /* Populate the cache and do one-off things */
492
                        ret = artifact_read_data_prepare(ctx, dl_data, &data_mdata_cache);
20✔
493
                    }
494
                }
495

496
                if (MENDER_OK == ret) {
33✔
497
                    assert(data_mdata_cache.valid);
32✔
498
                    /* Read data */
499
                    ret = artifact_read_data(ctx, dl_data, &data_mdata_cache);
32✔
500
                }
501
            } else if (false == mender_utils_strendswith(ctx->file.name, ".tar")) {
43✔
502

503
                /* Drop data, file is not relevant */
504
                ret = artifact_drop_file(ctx);
×
505
            } else {
506

507
                /* Nothing to do */
508
                ret = MENDER_DONE;
43✔
509
            }
510

511
            /* Check if file have been parsed and treatment done */
512
            if (MENDER_DONE == ret) {
228✔
513

514
                /* Remove the previous file name */
515
                char *substring = mender_utils_strrstr(ctx->file.name, ".tar");
151✔
516
                if (NULL != substring) {
151✔
517
                    *(substring + strlen(".tar")) = '\0';
105✔
518
                } else {
519
                    FREE_AND_NULL(ctx->file.name);
46✔
520
                }
521
                ctx->file.size  = 0;
151✔
522
                ctx->file.index = 0;
151✔
523

524
                /* Update the stream state machine */
525
                ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER;
151✔
526
            }
527
        }
528
    } while (MENDER_DONE == ret);
640✔
529

530
    return ret;
273✔
531
}
532

533
void
534
mender_artifact_compact_ctx(mender_artifact_ctx_t *ctx) {
12✔
535
    if (NULL == ctx) {
12✔
536
        return;
×
537
    }
538
    FREE_AND_NULL(ctx->input.data);
12✔
539
    ctx->input.length = 0;
12✔
540
    ctx->input.size   = 0;
12✔
541
    FREE_AND_NULL(ctx->file.name);
12✔
542
}
543

544
void
545
mender_artifact_release_ctx(mender_artifact_ctx_t *ctx) {
65✔
546

547
    /* Release memory */
548
    if (NULL != ctx) {
65✔
549
        mender_free(ctx->input.data);
27✔
550
        if (NULL != ctx->payloads.values) {
27✔
551
            for (size_t index = 0; index < ctx->payloads.size; index++) {
40✔
552
                mender_free(ctx->payloads.values[index].type);
20✔
553
                cJSON_Delete(ctx->payloads.values[index].meta_data);
20✔
554

555
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
556
                mender_utils_key_value_list_free(ctx->payloads.values[index].provides);
20✔
557
                mender_utils_key_value_list_free(ctx->payloads.values[index].depends);
20✔
558
                for (size_t i = 0; i < ctx->payloads.values[index].clears_provides_size; i++) {
39✔
559
                    mender_free(ctx->payloads.values[index].clears_provides[i]);
19✔
560
                }
561
                mender_free(ctx->payloads.values[index].clears_provides);
20✔
562
#endif
563
            }
564
            mender_free(ctx->payloads.values);
20✔
565
        }
566
        mender_free(ctx->file.name);
27✔
567
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
568
        mender_utils_key_value_list_free(ctx->artifact_info.provides);
27✔
569
        mender_utils_key_value_list_free(ctx->artifact_info.depends);
27✔
570
        mender_artifact_checksum_t *next;
571
        for (mender_artifact_checksum_t *checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = next) {
52✔
572
            mender_free(checksum->filename);
25✔
573
            mender_sha256_finish(checksum->context, NULL);
25✔
574
            next = checksum->next;
25✔
575
            mender_free(checksum);
25✔
576
        }
577
        ctx->artifact_info.checksums = NULL;
27✔
578
#endif
579
        if (artifact_ctx == ctx) {
27✔
580
            artifact_ctx = NULL;
26✔
581
        }
582

583
        mender_free(ctx);
27✔
584
    }
585
}
65✔
586

587
static mender_err_t
588
artifact_parse_tar_header(mender_artifact_ctx_t *ctx) {
412✔
589
    assert(NULL != ctx);
412✔
590

591
    char *tmp;
592
    bool  in_header_tar;
593

594
    /* Check if enough data are received (at least one block) */
595
    if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
412✔
596
        return MENDER_OK;
150✔
597
    }
598

599
    /* Cast block to TAR header structure */
600
    mender_artifact_tar_header_t *tar_header = (mender_artifact_tar_header_t *)ctx->input.data;
262✔
601

602
    /* Check if file name is provided, else the end of the current TAR file is reached */
603
    if ('\0' == tar_header->name[0]) {
262✔
604

605
        /* Check if enough data are received (at least 2 blocks) */
606
        if (ctx->input.length < 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE) {
105✔
607
            return MENDER_OK;
45✔
608
        }
609

610
        in_header_tar = (NULL != ctx->file.name) && StringEqual(ctx->file.name, "header.tar");
60✔
611

612
        /* Remove the TAR file name */
613
        if (NULL != ctx->file.name) {
60✔
614
            char *substring = mender_utils_strrstr(ctx->file.name, ".tar");
40✔
615
            if (NULL != substring) {
40✔
616
                *substring = '\0';
40✔
617
                substring  = mender_utils_strrstr(ctx->file.name, ".tar");
40✔
618
                if (NULL != substring) {
40✔
619
                    *(substring + strlen(".tar")) = '\0';
×
620
                } else {
621
                    FREE_AND_NULL(ctx->file.name);
40✔
622
                }
623
            } else {
624
                FREE_AND_NULL(ctx->file.name);
×
625
            }
626
        }
627

628
        /* Shift data in the buffer */
629
        /* header.tar has a checksum entry in the manifest as a whole so we need
630
           to include its empty blocks into checksum calculation */
631
        if (in_header_tar) {
60✔
632
            if (MENDER_OK
21✔
633
                != artifact_shift_and_checksum_data(ctx, 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE, "header.tar", 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
21✔
634
                mender_log_error("Unable to shift and checksum input data");
×
635
                return MENDER_FAIL;
×
636
            }
637
        } else {
638
            if (MENDER_OK != artifact_shift_data(ctx, 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
39✔
639
                mender_log_error("Unable to shift input data");
×
640
                return MENDER_FAIL;
×
641
            }
642
        }
643

644
        return MENDER_DONE;
60✔
645
    }
646

647
    /* Check magic */
648
    if (strncmp(tar_header->magic, "ustar", strlen("ustar"))) {
157✔
649
        /* Invalid magic */
650
        mender_log_error("Invalid magic");
1✔
651
        return MENDER_FAIL;
1✔
652
    }
653

654
    /* Compute the new file name */
655
    if (NULL != ctx->file.name) {
156✔
656
        size_t str_length = strlen(ctx->file.name) + strlen("/") + strlen(tar_header->name) + 1;
65✔
657
        if (NULL == (tmp = (char *)mender_malloc(str_length))) {
65✔
658
            mender_log_error("Unable to allocate memory");
×
659
            return MENDER_FAIL;
×
660
        }
661
        snprintf(tmp, str_length, "%s/%s", ctx->file.name, tar_header->name);
65✔
662
        mender_free(ctx->file.name);
65✔
663
    } else {
664
        if (NULL == (tmp = mender_utils_strdup(tar_header->name))) {
91✔
665
            mender_log_error("Unable to allocate memory");
×
666
            return MENDER_FAIL;
×
667
        }
668
    }
669
    ctx->file.name = tmp;
156✔
670

671
    /* Retrieve file size */
672
    assert(sizeof(size_t) >= sizeof(unsigned long));
673
    char *end_ptr;
674
    errno = 0; /* to distinguish between success/failure */
156✔
675

676
    ctx->file.size = strtoul(tar_header->size, &end_ptr, 8);
156✔
677
    if ((end_ptr == tar_header->size) /* no conversion */
156✔
678
        || (0 != errno)) {            /* out of range (for unsigned long) */
156✔
679
        mender_log_error("Unable to retrieve file size");
×
680
        return MENDER_FAIL;
×
681
    }
682

683
    ctx->file.index = 0;
156✔
684

685
    /* Shift data in the buffer */
686
    /* header.tar has a checksum entry in the manifest as a whole so we need
687
       to include its TAR header blocks into checksum calculation */
688
    in_header_tar = mender_utils_strbeginswith(ctx->file.name, "header.tar/");
156✔
689
    if (in_header_tar) {
156✔
690
        if (MENDER_OK != artifact_shift_and_checksum_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE, "header.tar", MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
44✔
691
            mender_log_error("Unable to shift and checksum input data");
×
692
            return MENDER_FAIL;
×
693
        }
694
    } else {
695
        if (MENDER_OK != artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
112✔
696
            mender_log_error("Unable to shift input data");
×
697
            return MENDER_FAIL;
×
698
        }
699
    }
700

701
    /* Update the stream state machine */
702
    ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA;
156✔
703

704
    return MENDER_DONE;
156✔
705
}
706

707
static mender_err_t
708
artifact_read_version(mender_artifact_ctx_t *ctx) {
48✔
709

710
    assert(NULL != ctx);
48✔
711
    cJSON       *object = NULL;
48✔
712
    mender_err_t ret    = MENDER_DONE;
48✔
713

714
    /* Check if all data have been received */
715
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
48✔
716
        return MENDER_OK;
24✔
717
    }
718

719
    /* Check version file */
720
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
24✔
721
        mender_log_error("Unable to allocate memory");
×
722
        return MENDER_FAIL;
×
723
    }
724
    cJSON *json_format = cJSON_GetObjectItemCaseSensitive(object, "format");
24✔
725
    if (true == cJSON_IsString(json_format)) {
24✔
726
        if (!StringEqual(cJSON_GetStringValue(json_format), MENDER_ARTIFACT_SUPPORTED_FORMAT)) {
24✔
727
            mender_log_error("Invalid version format");
×
728
            ret = MENDER_FAIL;
×
729
            goto END;
×
730
        }
731
    } else {
732
        mender_log_error("Invalid version file");
×
733
        ret = MENDER_FAIL;
×
734
        goto END;
×
735
    }
736
    cJSON *json_version = cJSON_GetObjectItemCaseSensitive(object, "version");
24✔
737
    if (true == cJSON_IsNumber(json_version)) {
24✔
738
        if (MENDER_ARTIFACT_SUPPORTED_VERSION != (int)cJSON_GetNumberValue(json_version)) {
24✔
739
            mender_log_error("Invalid version value");
×
740
            ret = MENDER_FAIL;
×
741
            goto END;
×
742
        }
743
    } else {
744
        mender_log_error("Invalid version file");
×
745
        ret = MENDER_FAIL;
×
746
        goto END;
×
747
    }
748
    mender_log_debug("Artifact has valid version");
12✔
749

750
    /* Shift data in the buffer */
751
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE), "version", ctx->file.size)) {
24✔
752
        mender_log_error("Unable to shift and checksum input data");
×
753
        ret = MENDER_FAIL;
×
754
        goto END;
×
755
    }
756

757
END:
24✔
758

759
    /* Release memory */
760
    cJSON_Delete(object);
24✔
761

762
    return ret;
24✔
763
}
764

765
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
766
mender_err_t
767
mender_artifact_get_device_type(mender_artifact_ctx_t *ctx, const char **device_type) {
13✔
768

769
    assert(NULL != ctx);
13✔
770
    assert(NULL != device_type);
13✔
771

772
    mender_key_value_list_t *item = ctx->artifact_info.depends;
13✔
773
    while (NULL != item) {
13✔
774
        if (NULL != item->key) {
12✔
775
            if (StringEqual(MENDER_ARTIFACT_DEVICE_TYPE_KEY, item->key)) {
12✔
776
                *device_type = item->value;
12✔
777
                return MENDER_OK;
12✔
778
            }
779
        }
780
        item = item->next;
×
781
    }
782
    return MENDER_FAIL;
1✔
783
}
784

785
static mender_err_t
786
artifact_read_manifest(mender_artifact_ctx_t *ctx) {
36✔
787

788
    assert(NULL != ctx);
36✔
789

790
    /* Check if all data has been received */
791
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
36✔
792
        return MENDER_OK;
12✔
793
    }
794

795
    /*  The expected format matches the output of sha256sum: sum and the name of the file separated by two spaces
796
        1d0b820130ae028ce8a79b7e217fe505a765ac394718e795d454941487c53d32  data/0000/update.ext4
797
        4d480539cdb23a4aee6330ff80673a5af92b7793eb1c57c4694532f96383b619  header.tar.gz
798
        52c76ab66947278a897c2a6df8b4d77badfa343fec7ba3b2983c2ecbbb041a35  version
799
    */
800

801
    /* Read data line by line */
802
    char *line = ctx->input.data;
24✔
803
    char *end  = line + ctx->input.length;
24✔
804
    while (line < end) {
94✔
805
        char *next = strchr(line, '\n');
94✔
806
        if (NULL == next) {
94✔
807
            break;
23✔
808
        }
809
        *next = '\0';
71✔
810

811
        /* Process line */
812
        char *separator = strstr(line, "  ");
71✔
813
        if (NULL == separator) {
71✔
814
            mender_log_error("Invalid manifest file");
×
815
            return MENDER_FAIL;
×
816
        }
817
        *separator = '\0';
71✔
818

819
        const char *checksum_str = line;
71✔
820
        const char *filename     = separator + 2;
71✔
821

822
        /* We do not support compressed artifacts */
823
        if (mender_utils_strbeginswith(filename, "header.tar") && is_compressed(filename)) {
71✔
824
            mender_log_error("Artifact compression is not supported");
1✔
825
            return MENDER_FAIL;
1✔
826
        }
827

828
        /* Useful when debugging artifact integrity check failures */
829
        mender_log_debug("%s  %s", checksum_str, filename);
36✔
830

831
        /* Make sure digest is of expected length (two hex per byte) */
832
        if ((MENDER_DIGEST_BUFFER_SIZE * 2) != strlen(checksum_str)) {
70✔
833
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
834
            return MENDER_FAIL;
×
835
        }
836

837
        /* Get checksum entry for the file (creates one if not found) */
838
        mender_artifact_checksum_t *checksum;
839
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, filename))) {
70✔
840
            /* Error already logged */
841
            return MENDER_FAIL;
×
842
        }
843

844
        /* Populate with manifest checksum */
845
        if (!mender_utils_hexdump_to_bytes(checksum_str, checksum->manifest, MENDER_DIGEST_BUFFER_SIZE)) {
70✔
846
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
847
            return MENDER_FAIL;
×
848
        }
849

850
        ///* Move to the next line */
851
        line = next + 1;
70✔
852
    }
853

854
    /* Shift data in the buffer */
855
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
23✔
856
        mender_log_error("Unable to shift input data");
×
857
        return MENDER_FAIL;
×
858
    }
859

860
    return MENDER_DONE;
23✔
861
}
862

863
static mender_err_t
864
artifact_parse_provides_depends(cJSON *json_provides_depends, mender_key_value_list_t **provides_depends) {
63✔
865

866
    assert(NULL != json_provides_depends);
63✔
867
    assert(NULL != provides_depends);
63✔
868

869
    /* Create linked-list from json object */
870
    /* The elements can either be a string or an array of strings */
871
    cJSON *json_element = NULL;
63✔
872
    cJSON_ArrayForEach(json_element, json_provides_depends) {
126✔
873
        if (cJSON_IsString(json_element)) {
63✔
874
            if (MENDER_OK != mender_utils_key_value_list_create_node(json_element->string, json_element->valuestring, provides_depends)) {
42✔
875
                mender_log_error("Unable to create linked list node for string element");
×
876
                goto ERROR;
×
877
            }
878
        } else if (cJSON_IsArray(json_element)) {
21✔
879
            cJSON *json_element_value = NULL;
21✔
880
            cJSON_ArrayForEach(json_element_value, json_element) {
42✔
881
                if (MENDER_OK != mender_utils_key_value_list_create_node(json_element->string, json_element_value->valuestring, provides_depends)) {
21✔
882
                    mender_log_error("Unable to create linked list node for array element");
×
883
                    goto ERROR;
×
884
                }
885
            }
886
        } else {
887
            mender_log_error("Invalid header-info file element type");
×
888
            goto ERROR;
×
889
        }
890
    }
891

892
    return MENDER_OK;
63✔
893

894
ERROR:
×
895
    /* Free linked list in case of error */
896
    mender_utils_key_value_list_free(*provides_depends);
×
897
    return MENDER_FAIL;
×
898
}
899
#endif
900

901
static mender_err_t
902
artifact_read_header_info(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data) {
34✔
903

904
    assert(NULL != ctx);
34✔
905
    cJSON       *object = NULL;
34✔
906
    mender_err_t ret    = MENDER_DONE;
34✔
907
    size_t       rounded_file_size;
908

909
    /* Check if all data have been received */
910
    if ((NULL == ctx->input.data) || (ctx->input.length < (rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)))) {
34✔
911
        return MENDER_OK;
12✔
912
    }
913

914
    /* Read header-info */
915
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
22✔
916
        mender_log_error("Unable to allocate memory");
×
917
        return MENDER_FAIL;
×
918
    }
919
    cJSON *json_payloads = cJSON_GetObjectItemCaseSensitive(object, "payloads");
22✔
920
    if (true == cJSON_IsArray(json_payloads)) {
22✔
921
        ctx->payloads.size = cJSON_GetArraySize(json_payloads);
22✔
922
        if (NULL == (ctx->payloads.values = (mender_artifact_payload_t *)mender_calloc(ctx->payloads.size, sizeof(mender_artifact_payload_t)))) {
22✔
923
            mender_log_error("Unable to allocate memory");
×
924
            ret = MENDER_FAIL;
×
925
            goto END;
×
926
        }
927
        size_t index        = 0;
22✔
928
        cJSON *json_payload = NULL;
22✔
929
        cJSON_ArrayForEach(json_payload, json_payloads) {
43✔
930
            if (true == cJSON_IsObject(json_payload)) {
22✔
931
                cJSON *json_payload_type = cJSON_GetObjectItemCaseSensitive(json_payload, "type");
22✔
932
                if (cJSON_IsString(json_payload_type)) {
22✔
933
                    if (NULL == (ctx->payloads.values[index].type = mender_utils_strdup(cJSON_GetStringValue(json_payload_type)))) {
22✔
934
                        mender_log_error("Unable to allocate memory");
×
935
                        ret = MENDER_FAIL;
×
936
                        goto END;
×
937
                    }
938
                    const char *payload_type = ctx->payloads.values[index].type;
22✔
939
                    /* Choose update module */
940
                    dl_data->update_module = mender_update_module_get(payload_type);
22✔
941
                    if (NULL == dl_data->update_module) {
22✔
942
                        /* Content is not supported by the mender-mcu-client */
943
                        mender_log_error("Unable to handle artifact type '%s'", payload_type);
×
944
                        ret = MENDER_FAIL;
×
945
                        goto END;
×
946
                    }
947
                    /* Add the payload type to deployment data  */
948
                    if (MENDER_OK != mender_deployment_data_add_payload_type(dl_data->deployment, payload_type)) {
22✔
949
                        /* Error already logged */
950
                        ret = MENDER_FAIL;
1✔
951
                        goto END;
1✔
952
                    }
953
                } else {
954
                    mender_log_error("Invalid header-info file");
×
955
                    ret = MENDER_FAIL;
×
956
                    goto END;
×
957
                }
958
            } else {
959
                mender_log_error("Invalid header-info file");
×
960
                ret = MENDER_FAIL;
×
961
                goto END;
×
962
            }
963
            index++;
21✔
964
        }
965

966
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
967
        cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
21✔
968
        if (cJSON_IsObject(json_provides)) {
21✔
969
            if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->artifact_info.provides))) {
21✔
970
                mender_log_error("Unable to parse artifact_provides");
×
971
                ret = MENDER_FAIL;
×
972
                goto END;
×
973
            }
974
        }
975

976
        cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
21✔
977
        if (cJSON_IsObject(json_depends)) {
21✔
978
            if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->artifact_info.depends))) {
21✔
979
                mender_log_error("Unable to parse artifact_depends");
×
980
                ret = MENDER_FAIL;
×
981
                goto END;
×
982
            }
983
        }
984
#endif
985

986
    } else {
987
        mender_log_error("Invalid header-info file");
×
988
        ret = MENDER_FAIL;
×
989
        goto END;
×
990
    }
991

992
    /* Shift data in the buffer */
993
    /* header.tar has a checksum entry in the manifest as a whole */
994
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
21✔
995
        mender_log_error("Unable to shift and checksum input data");
×
996
        ret = MENDER_FAIL;
×
997
        goto END;
×
998
    }
999

1000
END:
21✔
1001

1002
    /* Release memory */
1003
    cJSON_Delete(object);
22✔
1004

1005
    return ret;
22✔
1006
}
1007

1008
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
1009
static mender_err_t
1010
artifact_read_type_info(mender_artifact_ctx_t *ctx) {
33✔
1011

1012
    assert(NULL != ctx);
33✔
1013
    cJSON       *object = NULL;
33✔
1014
    mender_err_t ret    = MENDER_DONE;
33✔
1015
    size_t       index  = 0;
33✔
1016
    size_t       rounded_file_size;
1017

1018
    /* Check if all data have been received */
1019
    if ((NULL == ctx->input.data) || (ctx->input.length < (rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)))) {
33✔
1020
        return MENDER_OK;
12✔
1021
    }
1022

1023
    /* Read type-info */
1024
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
21✔
1025
        mender_log_error("Unable to allocate memory");
×
1026
        return MENDER_FAIL;
×
1027
    }
1028

1029
    /* Check if payload index is valid */
1030
    if (NULL == ctx->payloads.values[index].type) {
21✔
1031
        mender_log_error("Invalid artifact format; no payload found for index %d", index);
×
1032
        ret = MENDER_FAIL;
×
1033
        goto END;
×
1034
    }
1035

1036
    cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
21✔
1037
    if (cJSON_IsObject(json_provides)) {
21✔
1038
        if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->payloads.values[index].provides))) {
21✔
1039
            mender_log_error("Unable to parse artifact_provides");
×
1040
            ret = MENDER_FAIL;
×
1041
            goto END;
×
1042
        }
1043
    }
1044

1045
    cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
21✔
1046
    if (cJSON_IsObject(json_depends)) {
21✔
1047
        if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->payloads.values[index].depends))) {
×
1048
            mender_log_error("Unable to parse artifact_depends");
×
1049
            ret = MENDER_FAIL;
×
1050
            goto END;
×
1051
        }
1052
    }
1053

1054
    cJSON *json_clears_provides = cJSON_GetObjectItemCaseSensitive(object, "clears_artifact_provides");
21✔
1055
    if (cJSON_IsArray(json_clears_provides)) {
21✔
1056
        ctx->payloads.values[index].clears_provides_size = cJSON_GetArraySize(json_clears_provides);
21✔
1057
        ctx->payloads.values[index].clears_provides      = (char **)mender_calloc(ctx->payloads.values[index].clears_provides_size, sizeof(char *));
21✔
1058
        if (NULL == ctx->payloads.values[index].clears_provides) {
21✔
1059
            mender_log_error("Unable to allocate memory");
×
1060
            ret = MENDER_FAIL;
×
1061
            goto END;
×
1062
        }
1063

1064
        size_t i                            = 0;
21✔
1065
        cJSON *json_clears_provides_element = NULL;
21✔
1066

1067
        cJSON_ArrayForEach(json_clears_provides_element, json_clears_provides) {
42✔
1068
            if (cJSON_IsString(json_clears_provides_element)) {
21✔
1069
                char *clears_provides = mender_utils_strdup(json_clears_provides_element->valuestring);
21✔
1070
                if (NULL == clears_provides) {
21✔
1071
                    mender_log_error("Unable to allocate memory");
×
1072
                    ret = MENDER_FAIL;
×
1073
                    goto END;
×
1074
                }
1075
                ctx->payloads.values[index].clears_provides[i] = clears_provides;
21✔
1076
                i++;
21✔
1077
            } else {
1078
                mender_log_error("Invalid header-info file");
×
1079
                ret = MENDER_FAIL;
×
1080
                goto END;
×
1081
            }
1082
        }
1083
    }
1084

1085
    /* Shift data in the buffer */
1086
    /* header.tar has a checksum entry in the manifest as a whole */
1087
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
21✔
1088
        mender_log_error("Unable to shift and checksum input data");
×
1089
        ret = MENDER_FAIL;
×
1090
        goto END;
×
1091
    }
1092

1093
END:
21✔
1094

1095
    /* Release memory */
1096
    cJSON_Delete(object);
21✔
1097

1098
    return ret;
21✔
1099
}
1100
#endif
1101

1102
static mender_err_t
1103
artifact_read_meta_data(mender_artifact_ctx_t *ctx) {
1✔
1104
    assert(NULL != ctx);
1✔
1105
    size_t rounded_file_size;
1106

1107
    /* Retrieve payload index. We expect "header.tar/headers/%u/meta-data" where
1108
     * %u is the index. Yes sscanf(3) would be nice, but we've experienced
1109
     * unexplained segmentation faults on some hardware when using it. */
1110
    const char *const prefix = "header.tar/headers/";
1✔
1111
    if (!mender_utils_strbeginswith(ctx->file.name, prefix)) {
1✔
1112
        mender_log_error("Invalid artifact format");
×
1113
        return MENDER_FAIL;
×
1114
    }
1115

1116
    assert(sizeof(size_t) >= sizeof(unsigned long));
1117
    const char *start_ptr = ctx->file.name + strlen(prefix);
1✔
1118
    char       *end_ptr;
1119
    errno = 0; /* to distinguish between success/failure */
1✔
1120

1121
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
1✔
1122
    if ((end_ptr == start_ptr)              /* no conversion */
1✔
1123
        || (0 != errno)                     /* out of range (for unsigned long) */
1✔
1124
        || (index >= ctx->payloads.size)) { /* index out of bounds */
1✔
1125
        mender_log_error("Invalid artifact format");
×
1126
        return MENDER_FAIL;
×
1127
    }
1128

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

1132
    /* Check size of the meta-data */
1133
    if (0 == ctx->file.size) {
1✔
1134
        /* Nothing to do */
1135
        return MENDER_DONE;
×
1136
    }
1137

1138
    /* Check if all data have been received */
1139
    rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE);
1✔
1140
    if ((NULL == ctx->input.data) || (ctx->input.length < rounded_file_size)) {
1✔
1141
        return MENDER_OK;
×
1142
    }
1143

1144
    /* Read meta-data */
1145
    if (NULL == (ctx->payloads.values[index].meta_data = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
1✔
1146
        mender_log_error("Unable to allocate memory");
×
1147
        return MENDER_FAIL;
×
1148
    }
1149

1150
    /* Shift data in the buffer */
1151
    /* header.tar has a checksum entry in the manifest as a whole */
1152
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
1✔
1153
        mender_log_error("Unable to shift and checksum input data");
×
1154
        return MENDER_FAIL;
×
1155
    }
1156

1157
    return MENDER_DONE;
1✔
1158
}
1159

1160
/**
1161
 * @brief Callback function to be invoked to perform the treatment of the data from the artifact
1162
 * @param deployment_id Deployment ID
1163
 * @param type Type from header-info payloads
1164
 * @param artifact_name Artifact name
1165
 * @param meta_data Meta-data from header tarball
1166
 * @param filename Artifact filename
1167
 * @param size Artifact file size
1168
 * @param data Artifact data
1169
 * @param index Artifact data index
1170
 * @param length Artifact data length
1171
 * @param dl_data Download data for the artifact
1172
 * @return MENDER_OK if the function succeeds, error code if an error occurred
1173
 */
1174
static mender_err_t
1175
process_artifact_data_callback(const char                      *deployment_id,
20✔
1176
                               const char                      *type,
1177
                               const char                      *artifact_name,
1178
                               const cJSON                     *meta_data,
1179
                               const char                      *filename,
1180
                               size_t                           size,
1181
                               void                            *data,
1182
                               size_t                           index,
1183
                               size_t                           length,
1184
                               mender_artifact_download_data_t *dl_data) {
1185

1186
    assert(NULL != type);
20✔
1187
    mender_err_t ret = MENDER_FAIL;
20✔
1188

1189
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_INF
1190
    if (size > 0) {
20✔
1191
        static size_t download_progress = 0;
1192
        /* New update */
1193
        if (0 == index) {
20✔
1194
            download_progress = 0;
20✔
1195
        }
1196

1197
        /* Update every 10% */
1198
        if (((index * 10) / size) > download_progress) {
20✔
1199
            download_progress = (index * 10) / size;
×
1200
            mender_log_info("Downloading '%s' %zu0%%... [%zu/%zu]", type, download_progress, index, size);
×
1201
        }
1202
    }
1203
#endif
1204

1205
    /* Invoke update module download callback */
1206
    struct mender_update_download_state_data_s download_state_data
20✔
1207
        = { deployment_id, artifact_name, type, meta_data, filename, size, data, index, length, false };
1208
    mender_update_state_data_t state_data = { .download_state_data = &download_state_data };
20✔
1209
    if (MENDER_OK != (ret = dl_data->update_module->callbacks[MENDER_UPDATE_STATE_DOWNLOAD](MENDER_UPDATE_STATE_DOWNLOAD, state_data))) {
20✔
1210
        mender_log_error("An error occurred while processing data of the artifact '%s' of type '%s'", artifact_name, type);
1✔
1211
        return ret;
1✔
1212
    }
1213

1214
    return MENDER_OK;
19✔
1215
}
1216

1217
static mender_err_t
1218
artifact_read_data_prepare(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache) {
20✔
1219
    /* First, retrieve payload index. We expect "data/%u.tar" where %u is the
1220
     * index. Yes sscanf(3) would be nice, but we've experienced unexplained
1221
     * segmentation faults on some hardware when using it. */
1222
    const char *const prefix = "data/";
20✔
1223
    if (!mender_utils_strbeginswith(ctx->file.name, prefix)) {
20✔
1224
        mender_log_error("Invalid artifact format");
×
1225
        return MENDER_FAIL;
×
1226
    }
1227

1228
    size_t file_name_length = strlen(ctx->file.name);
20✔
1229
    /* We check the length to make sure we only check for compression on the
1230
     * payload itself - not the files inside the payload */
1231
    if ((strlen("data/xxxx.tar.xx") == file_name_length) || (strlen("data/xxxx.tar.xxx") == file_name_length)) {
20✔
1232
        /*
1233
        * We allow compressed files _inside_ a payload:
1234
        *   'data/0000.tar/compressed.tar.gz'
1235
        * But not a compressed payload:
1236
        *   'data/0000.tar[.gz|.xz|.zst]'
1237
        **/
1238
        if (is_compressed(ctx->file.name)) {
×
1239
            mender_log_error("Artifact compression is not supported");
×
1240
            return MENDER_FAIL;
×
1241
        }
1242
    }
1243

1244
    assert(sizeof(size_t) >= sizeof(unsigned long));
1245
    const char *start_ptr = ctx->file.name + strlen(prefix);
20✔
1246
    char       *end_ptr;
1247
    errno = 0; /* to distinguish between success/failure */
20✔
1248

1249
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
20✔
1250
    if ((end_ptr == start_ptr)              /* no conversion */
20✔
1251
        || (0 != errno)                     /* out of range (for unsigned long) */
20✔
1252
        || (index >= ctx->payloads.size)) { /* index out of bounds */
20✔
1253
        mender_log_error("Invalid artifact format");
×
1254
        return MENDER_FAIL;
×
1255
    }
1256

1257
    assert(NULL != end_ptr);
20✔
1258
    assert(StringEqualN(end_ptr, ".tar", 4)); /* just one last sanity check */
20✔
1259

1260
    const char *payload_type = ctx->payloads.values[index].type;
20✔
1261

1262
    /* Retrieve ID and artifact name */
1263
    if (MENDER_OK != mender_deployment_data_get_id(dl_data->deployment, &(mdata_cache->deployment_id))) {
20✔
1264
        mender_log_error("Unable to get ID from the deployment data");
×
1265
        return MENDER_FAIL;
×
1266
    }
1267
    if (MENDER_OK != mender_deployment_data_get_artifact_name(dl_data->deployment, &(mdata_cache->artifact_name))) {
20✔
1268
        mender_log_error("Unable to get artifact name from the deployment data");
×
1269
        return MENDER_FAIL;
×
1270
    }
1271

1272
    mdata_cache->payload_type = payload_type;
20✔
1273
    mdata_cache->meta_data    = ctx->payloads.values[index].meta_data;
20✔
1274
    mdata_cache->filename     = strstr(ctx->file.name, ".tar") + strlen(".tar") + 1;
20✔
1275

1276
    /* The filename will be something like
1277
     * 'data/0000.tar/zephyr.signed.bin'. But the manifest will hold
1278
     * 'data/0000/zephyr.signed.bin'. Hence, we need to remove the
1279
     * '.tar' extension from the string.
1280
     */
1281
    if (NULL == (mdata_cache->checksum_fname = mender_utils_strdup(ctx->file.name))) {
20✔
1282
        mender_log_error("Unable to allocate memory");
×
1283
        return MENDER_FAIL;
×
1284
    }
1285
    bool done = false;
20✔
1286
    for (char *ch = strstr(mdata_cache->checksum_fname, ".tar"); (NULL != ch) && !done; ch++) {
256✔
1287
        /* Don't worry! The call to strlen() on a static string should
1288
         * be optimized out by the compiler */
1289
        done = (*ch = ch[strlen(".tar")]) == '\0';
236✔
1290
    }
1291

1292
    mdata_cache->valid = true;
20✔
1293
    return MENDER_OK;
20✔
1294
}
1295

1296
static mender_err_t
1297
artifact_read_data(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache) {
32✔
1298

1299
    assert(NULL != ctx);
32✔
1300
    mender_err_t ret;
1301

1302
    /* Check size of the data */
1303
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
32✔
1304
        /* Nothing to do */
1305
        return MENDER_DONE;
×
1306
    }
1307

1308
    /* Parse data until the end of the file has been reached */
1309
    do {
1310

1311
        /* Check if enough data are received (at least one block) */
1312
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
32✔
1313
            return MENDER_OK;
12✔
1314
        }
1315

1316
        /* Compute length */
1317
        size_t length
20✔
1318
            = ((ctx->file.size - ctx->file.index) > MENDER_ARTIFACT_STREAM_BLOCK_SIZE) ? MENDER_ARTIFACT_STREAM_BLOCK_SIZE : (ctx->file.size - ctx->file.index);
20✔
1319

1320
        /* Invoke the download artifact callback */
1321
        ret = process_artifact_data_callback(mdata_cache->deployment_id,
20✔
1322
                                             mdata_cache->payload_type,
1323
                                             mdata_cache->artifact_name,
1324
                                             mdata_cache->meta_data,
20✔
1325
                                             mdata_cache->filename,
1326
                                             ctx->file.size,
1327
                                             ctx->input.data,
1328
                                             ctx->file.index,
1329
                                             length,
1330
                                             dl_data);
1331
        if (MENDER_OK != ret) {
20✔
1332
            mender_log_error("An error occurred");
1✔
1333
            return ret;
1✔
1334
        }
1335

1336
        /* Update index */
1337
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
19✔
1338

1339
        /* Shift data in the buffer */
1340
        if (MENDER_OK != (ret = artifact_shift_and_checksum_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE, mdata_cache->checksum_fname, length))) {
19✔
1341
            mender_log_error("Unable to shift and checksum input data");
×
1342
            return ret;
×
1343
        }
1344

1345
    } while (ctx->file.index < ctx->file.size);
19✔
1346

1347
    return MENDER_DONE;
19✔
1348
}
1349

1350
static mender_err_t
1351
artifact_drop_file(mender_artifact_ctx_t *ctx) {
×
1352

1353
    assert(NULL != ctx);
×
1354
    mender_err_t ret;
1355

1356
    /* Check size of the data */
1357
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1358
        /* Nothing to do */
1359
        return MENDER_DONE;
×
1360
    }
1361

1362
    /* Parse data until the end of the file has been reached */
1363
    do {
1364

1365
        /* Check if enough data are received (at least one block) */
1366
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1367
            return MENDER_OK;
×
1368
        }
1369

1370
        /* Update index */
1371
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1372

1373
        /* Shift data in the buffer */
1374
        if (MENDER_OK != (ret = artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
1375
            mender_log_error("Unable to shift input data");
×
1376
            return ret;
×
1377
        }
1378

1379
    } while (ctx->file.index < ctx->file.size);
×
1380

1381
    return MENDER_DONE;
×
1382
}
1383

1384
static mender_err_t
1385
artifact_shift_and_checksum_data(mender_artifact_ctx_t *ctx, size_t length, const char *checksum_key, size_t checksum_len) {
151✔
1386
    assert(NULL != ctx);
151✔
1387
    assert(ctx->input.length >= length);
151✔
1388
    assert(checksum_len <= length);
151✔
1389

1390
    if (0 == length) {
151✔
1391
        return MENDER_OK;
×
1392
    }
1393

1394
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
1395
    if ((NULL != checksum_key) && (0 != checksum_len)) {
151✔
1396
        mender_artifact_checksum_t *checksum;
1397
        /* Get checksum entry (create one if needed) */
1398
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, checksum_key))) {
151✔
1399
            /* Error already logged */
1400
            return MENDER_FAIL;
×
1401
        }
1402

1403
        if (MENDER_OK != mender_sha256_update(checksum->context, ctx->input.data, checksum_len)) {
151✔
1404
            mender_log_error("Failed to update update checksum");
×
1405
            return MENDER_FAIL;
×
1406
        }
1407
    }
1408
#else
1409
    /* Only to make the arguments "used" in this case. */
1410
    (void)checksum_key;
1411
    (void)checksum_len;
1412
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
1413

1414
    return artifact_shift_data(ctx, length);
151✔
1415
}
1416

1417
static mender_err_t
1418
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
325✔
1419

1420
    assert(NULL != ctx);
325✔
1421

1422
    /* Shift data */
1423
    if (length > 0) {
325✔
1424
        if (ctx->input.length > length) {
325✔
1425
            memmove(ctx->input.data, (void *)(((uint8_t *)ctx->input.data) + length), ctx->input.length - length);
306✔
1426
            ctx->input.length -= length;
306✔
1427
            /* Here we could shrink the ctx->input.data buffer, but most likely, we would need to
1428
               grow it again when we receive another batch of data so there's little point in doing
1429
               so. */
1430
        } else {
1431
            ctx->input.length = 0;
19✔
1432
        }
1433
    }
1434

1435
    return MENDER_OK;
325✔
1436
}
1437

1438
static size_t
1439
artifact_round_up(size_t length, size_t incr) {
245✔
1440
    return length + (incr - length % incr) % incr;
245✔
1441
}
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