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

mendersoftware / mender-mcu / 1536215907

11 Nov 2024 10:07AM UTC coverage: 9.418% (-0.03%) from 9.447%
1536215907

push

gitlab-ci

danielskinstad
fix: fail deployment when it's aborted

Changelog: Title
Ticket: MEN-7693

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

0 of 13 new or added lines in 3 files covered. (0.0%)

499 existing lines in 4 files now uncovered.

251 of 2665 relevant lines covered (9.42%)

0.68 hits per line

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

0.0
/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
 * @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
90
 */
91
static mender_err_t artifact_read_header_info(mender_artifact_ctx_t *ctx);
92

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

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

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

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

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

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

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

UNCOV
150
    FREE_AND_NULL(mdata_cache->checksum_fname);
×
UNCOV
151
    mdata_cache->valid = false;
×
UNCOV
152
}
×
153

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

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

175
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
176
static mender_err_t artifact_read_header(mender_artifact_ctx_t *ctx);
177
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
178

179
/**
180
 * @brief Shift data after parsing
181
 * @param ctx Artifact context
182
 * @param length Length of data to shift
183
 * @return MENDER_OK if the function succeeds, error code otherwise
184
 */
185
static mender_err_t artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length);
186

187
/**
188
 * @brief Compute length rounded up to increment (usually the block size)
189
 * @param length Length
190
 * @param incr Increment
191
 * @return Rounded length
192
 */
193
static size_t artifact_round_up(size_t length, size_t incr);
194

195
/**
196
 * @brief Artifact context
197
 */
198
static mender_artifact_ctx_t *artifact_ctx = NULL;
199

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

214
    /* See if we already have an entry for this file */
215
    mender_artifact_checksum_t *checksum;
216
    for (checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = checksum->next) {
×
217
        if (StringEqual(checksum->filename, filename)) {
×
UNCOV
218
            break;
×
219
        }
220
    }
221

UNCOV
222
    if (NULL == checksum) {
×
223
        /* Create new if entry not found */
224
        checksum = (mender_artifact_checksum_t *)calloc(1, sizeof(mender_artifact_checksum_t));
×
225
        if (NULL == checksum) {
×
226
            mender_log_error("Unable to allocate memory");
×
227
            return NULL;
×
228
        }
229
        checksum->filename = strdup(filename);
×
UNCOV
230
        if (NULL == checksum->filename) {
×
231
            mender_log_error("Unable to allocate memory");
×
232
            free(checksum);
×
UNCOV
233
            return NULL;
×
234
        }
UNCOV
235
        checksum->next               = ctx->artifact_info.checksums;
×
UNCOV
236
        ctx->artifact_info.checksums = checksum;
×
237

238
        /* Start SHA-256 checksum computation */
UNCOV
239
        if (MENDER_OK != mender_sha256_begin(&(checksum->context))) {
×
UNCOV
240
            mender_log_error("Failed to start checksum for file '%s'", filename);
×
UNCOV
241
            return NULL;
×
242
        }
243
    }
244

UNCOV
245
    return checksum;
×
246
}
247
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
248

249
mender_err_t
UNCOV
250
mender_artifact_check_integrity(mender_artifact_ctx_t *ctx) {
×
251
    assert(NULL != ctx);
×
252

253
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
254
    for (mender_artifact_checksum_t *checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = checksum->next) {
×
255
        unsigned char computed[MENDER_DIGEST_BUFFER_SIZE];
256
        mender_log_debug("Checking integrity for artifact file '%s'", checksum->filename);
257

UNCOV
258
        if (MENDER_OK != mender_sha256_finish(checksum->context, computed)) {
×
259
            mender_log_error("Failed to finish checksum for file '%s'", checksum->filename);
×
UNCOV
260
            checksum->context = NULL;
×
UNCOV
261
            return MENDER_FAIL;
×
262
        }
UNCOV
263
        checksum->context = NULL;
×
264

265
        if (0 != memcmp(checksum->manifest, computed, MENDER_DIGEST_BUFFER_SIZE)) {
×
266
            mender_log_error("Computed checksum for file '%s' does not match manifest", checksum->filename);
×
267
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_DBG
268
            /* Log the mismatching checksums for debugging */
269
            char checksum_str[(MENDER_DIGEST_BUFFER_SIZE * 2) + 1];
270

271
            for (int i = 0; i < MENDER_DIGEST_BUFFER_SIZE; i++) {
272
                if (2 != snprintf(checksum_str + (i * 2), 3, "%02hhx", checksum->manifest[i])) {
273
                    break;
274
                }
275
            }
276
            mender_log_debug("%s: '%s' (manifest)", checksum->filename, checksum_str);
277

278
            for (int i = 0; i < MENDER_DIGEST_BUFFER_SIZE; i++) {
279
                if (2 != snprintf(checksum_str + (i * 2), 3, "%02hhx", computed[i])) {
280
                    break;
281
                }
282
            }
283
            mender_log_debug("%s: '%s' (computed)", checksum->filename, checksum_str);
284
#endif /* CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_DBG */
285
            return MENDER_FAIL;
×
286
        }
287
    }
288
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
UNCOV
289
    return MENDER_OK;
×
290
}
291

292
mender_artifact_ctx_t *
UNCOV
293
mender_artifact_create_ctx(size_t buf_size) {
×
294

295
    mender_artifact_ctx_t *ctx;
296

297
    /* Create new context */
UNCOV
298
    if (NULL == (ctx = (mender_artifact_ctx_t *)calloc(1, sizeof(mender_artifact_ctx_t)))) {
×
UNCOV
299
        mender_log_error("Unable to allocate memory for artifact context");
×
300
        return NULL;
×
301
    }
302
    if (NULL == (ctx->input.data = malloc(buf_size))) {
×
UNCOV
303
        mender_log_error("Unable to allocate memory for artifact context buffer");
×
UNCOV
304
        free(ctx);
×
UNCOV
305
        return NULL;
×
306
    }
UNCOV
307
    ctx->input.size      = buf_size;
×
UNCOV
308
    ctx->input.orig_size = buf_size;
×
309

310
    /* Save context */
UNCOV
311
    artifact_ctx = ctx;
×
312

313
    return ctx;
×
314
}
315

316
mender_err_t
UNCOV
317
mender_artifact_get_ctx(mender_artifact_ctx_t **ctx) {
×
318

319
    assert(NULL != ctx);
×
320

321
    if (NULL == artifact_ctx) {
×
UNCOV
322
        return MENDER_FAIL;
×
323
    }
324

325
    *ctx = artifact_ctx;
×
326
    return MENDER_OK;
×
327
}
328

329
static bool
UNCOV
330
is_compressed(const char *filename) {
×
331

332
    static const char *compression_suffixes[] = { ".gz", ".xz", ".zst", NULL };
333

UNCOV
334
    for (size_t i = 0; NULL != compression_suffixes[i]; i++) {
×
335
        if (mender_utils_strendswith(filename, compression_suffixes[i])) {
×
336
            return true;
×
337
        }
338
    }
339

340
    return false;
×
341
}
342

343
static struct data_mdata_cache data_mdata_cache = { 0 };
344

345
mender_err_t
346
mender_artifact_process_data(mender_artifact_ctx_t *ctx, void *input_data, size_t input_length, mender_artifact_download_data_t *dl_data) {
×
347

UNCOV
348
    assert(NULL != ctx);
×
UNCOV
349
    mender_err_t ret = MENDER_OK;
×
350
    void        *tmp;
351
    size_t       new_size;
352
    size_t       expected_required;
353

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

386
    /* Parse data */
387
    do {
388

389
        /* We do not support compressed artifacts */
UNCOV
390
        if (is_compressed(ctx->file.name)) {
×
391
            mender_log_error("Artifact compression is not supported");
×
UNCOV
392
            return MENDER_FAIL;
×
393
        }
394

395
        /* Treatment depending of the stream state */
396
        if (MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER == ctx->stream_state) {
×
397

398
            /* Parse TAR header */
399
            ret = artifact_parse_tar_header(ctx);
×
400

401
            /* Processing a new (data) file, invalidate the cache */
UNCOV
402
            data_mdata_cache_invalidate(&data_mdata_cache);
×
403

404
        } else if (MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA == ctx->stream_state) {
×
405

406
            /* Treatment depending of the file name */
407
            if (StringEqual(ctx->file.name, "version")) {
×
408

409
                /* Validate artifact version */
UNCOV
410
                ret = artifact_read_version(ctx);
×
411

412
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
413
            } else if (StringEqual(ctx->file.name, "manifest")) {
×
414

415
                /* Read manifest file */
416
                ret = artifact_read_manifest(ctx);
×
417
#endif
UNCOV
418
            } else if (StringEqual(ctx->file.name, "header.tar/header-info")) {
×
419

420
                /* Read header-info file */
421
                ret = artifact_read_header_info(ctx);
×
422

423
            } else if ((true == mender_utils_strbeginswith(ctx->file.name, "header.tar/headers"))
×
UNCOV
424
                       && (true == mender_utils_strendswith(ctx->file.name, "meta-data"))) {
×
425

426
                /* Read meta-data file */
UNCOV
427
                ret = artifact_read_meta_data(ctx);
×
428

429
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
430
            } else if (mender_utils_strbeginswith(ctx->file.name, "header.tar/headers") && mender_utils_strendswith(ctx->file.name, "type-info")) {
×
431

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

445
                if (MENDER_OK == ret) {
×
446
                    assert(data_mdata_cache.valid);
×
447
                    /* Read data */
UNCOV
448
                    ret = artifact_read_data(ctx, dl_data, &data_mdata_cache);
×
449
                }
450
            } else if (false == mender_utils_strendswith(ctx->file.name, ".tar")) {
×
451

452
                /* Drop data, file is not relevant */
UNCOV
453
                ret = artifact_drop_file(ctx);
×
454
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
455
            } else if (StringEqual(ctx->file.name, "header.tar")) {
×
UNCOV
456
                ret = artifact_read_header(ctx);
×
457
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
458
            } else {
459

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

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

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

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

UNCOV
483
    return ret;
×
484
}
485

486
void
487
mender_artifact_release_ctx(mender_artifact_ctx_t *ctx) {
×
488

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

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

521
static mender_err_t
522
artifact_parse_tar_header(mender_artifact_ctx_t *ctx) {
×
523

UNCOV
524
    assert(NULL != ctx);
×
525
    char *tmp;
526

527
    /* Check if enough data are received (at least one block) */
528
    if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
529
        return MENDER_OK;
×
530
    }
531

532
    /* Cast block to TAR header structure */
533
    mender_artifact_tar_header_t *tar_header = (mender_artifact_tar_header_t *)ctx->input.data;
×
534

535
    /* Check if file name is provided, else the end of the current TAR file is reached */
536
    if ('\0' == tar_header->name[0]) {
×
537

538
        /* Check if enough data are received (at least 2 blocks) */
UNCOV
539
        if (ctx->input.length < 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE) {
×
UNCOV
540
            return MENDER_OK;
×
541
        }
542

543
        /* Remove the TAR file name */
UNCOV
544
        if (NULL != ctx->file.name) {
×
UNCOV
545
            char *substring = mender_utils_strrstr(ctx->file.name, ".tar");
×
546
            if (NULL != substring) {
×
UNCOV
547
                *substring = '\0';
×
548
                substring  = mender_utils_strrstr(ctx->file.name, ".tar");
×
549
                if (NULL != substring) {
×
550
                    *(substring + strlen(".tar")) = '\0';
×
551
                } else {
552
                    FREE_AND_NULL(ctx->file.name);
×
553
                }
554
            } else {
555
                FREE_AND_NULL(ctx->file.name);
×
556
            }
557
        }
558

559
        /* Shift data in the buffer */
560
        if (MENDER_OK != artifact_shift_data(ctx, 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
561
            mender_log_error("Unable to shift input data");
×
UNCOV
562
            return MENDER_FAIL;
×
563
        }
564

UNCOV
565
        return MENDER_DONE;
×
566
    }
567

568
    /* Check magic */
UNCOV
569
    if (strncmp(tar_header->magic, "ustar", strlen("ustar"))) {
×
570
        /* Invalid magic */
UNCOV
571
        mender_log_error("Invalid magic");
×
572
        return MENDER_FAIL;
×
573
    }
574

575
    /* Compute the new file name */
UNCOV
576
    if (NULL != ctx->file.name) {
×
577
        size_t str_length = strlen(ctx->file.name) + strlen("/") + strlen(tar_header->name) + 1;
×
578
        if (NULL == (tmp = (char *)malloc(str_length))) {
×
UNCOV
579
            mender_log_error("Unable to allocate memory");
×
UNCOV
580
            return MENDER_FAIL;
×
581
        }
UNCOV
582
        snprintf(tmp, str_length, "%s/%s", ctx->file.name, tar_header->name);
×
UNCOV
583
        free(ctx->file.name);
×
584
    } else {
UNCOV
585
        if (NULL == (tmp = strdup(tar_header->name))) {
×
586
            mender_log_error("Unable to allocate memory");
×
UNCOV
587
            return MENDER_FAIL;
×
588
        }
589
    }
590
    ctx->file.name = tmp;
×
591

592
    /* Retrieve file size */
593
    assert(sizeof(size_t) >= sizeof(unsigned long));
594
    char *end_ptr;
UNCOV
595
    errno = 0; /* to distinguish between success/failure */
×
596

597
    ctx->file.size = strtoul(tar_header->size, &end_ptr, 8);
×
598
    if ((end_ptr == tar_header->size) /* no conversion */
×
599
        || (0 != errno)) {            /* out of range (for unsigned long) */
×
UNCOV
600
        mender_log_error("Unable to retrieve file size");
×
601
        return MENDER_FAIL;
×
602
    }
603

604
    ctx->file.index = 0;
×
605

606
    /* Shift data in the buffer */
UNCOV
607
    if (MENDER_OK != artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
608
        mender_log_error("Unable to shift input data");
×
609
        return MENDER_FAIL;
×
610
    }
611

612
    /* Update the stream state machine */
613
    ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA;
×
614

615
    return MENDER_DONE;
×
616
}
617

618
static mender_err_t
UNCOV
619
artifact_read_version(mender_artifact_ctx_t *ctx) {
×
620

621
    assert(NULL != ctx);
×
622
    cJSON       *object = NULL;
×
623
    mender_err_t ret    = MENDER_DONE;
×
624

625
    /* Check if all data have been received */
UNCOV
626
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
627
        return MENDER_OK;
×
628
    }
629

630
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
631
    /* Get checksum entry (create one if needed) */
632
    mender_artifact_checksum_t *checksum;
UNCOV
633
    if (NULL == (checksum = artifact_checksum_get_or_create(ctx, "version"))) {
×
634
        /* Error already logged */
UNCOV
635
        return MENDER_FAIL;
×
636
    }
637

638
    /* Update SHA-256 checksum */
639
    if (MENDER_OK != mender_sha256_update(checksum->context, ctx->input.data, ctx->file.size)) {
×
UNCOV
640
        mender_log_error("Failed to update update checksum");
×
UNCOV
641
        return MENDER_FAIL;
×
642
    }
643
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
644

645
    /* Check version file */
646
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
×
647
        mender_log_error("Unable to allocate memory");
×
UNCOV
648
        return MENDER_FAIL;
×
649
    }
650
    cJSON *json_format = cJSON_GetObjectItemCaseSensitive(object, "format");
×
651
    if (true == cJSON_IsString(json_format)) {
×
652
        if (!StringEqual(cJSON_GetStringValue(json_format), MENDER_ARTIFACT_SUPPORTED_FORMAT)) {
×
653
            mender_log_error("Invalid version format");
×
654
            ret = MENDER_FAIL;
×
UNCOV
655
            goto END;
×
656
        }
657
    } else {
UNCOV
658
        mender_log_error("Invalid version file");
×
659
        ret = MENDER_FAIL;
×
UNCOV
660
        goto END;
×
661
    }
UNCOV
662
    cJSON *json_version = cJSON_GetObjectItemCaseSensitive(object, "version");
×
663
    if (true == cJSON_IsNumber(json_version)) {
×
UNCOV
664
        if (MENDER_ARTIFACT_SUPPORTED_VERSION != (int)cJSON_GetNumberValue(json_version)) {
×
665
            mender_log_error("Invalid version value");
×
UNCOV
666
            ret = MENDER_FAIL;
×
UNCOV
667
            goto END;
×
668
        }
669
    } else {
UNCOV
670
        mender_log_error("Invalid version file");
×
UNCOV
671
        ret = MENDER_FAIL;
×
UNCOV
672
        goto END;
×
673
    }
674
    mender_log_debug("Artifact has valid version");
675

676
    /* Shift data in the buffer */
UNCOV
677
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
678
        mender_log_error("Unable to shift input data");
×
679
        ret = MENDER_FAIL;
×
680
        goto END;
×
681
    }
682

683
END:
×
684

685
    /* Release memory */
686
    cJSON_Delete(object);
×
687

UNCOV
688
    return ret;
×
689
}
690

691
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
692
mender_err_t
UNCOV
693
mender_artifact_get_device_type(mender_artifact_ctx_t *ctx, const char **device_type) {
×
694

UNCOV
695
    assert(NULL != ctx);
×
696
    assert(NULL != device_type);
×
697

UNCOV
698
    mender_key_value_list_t *item = ctx->artifact_info.depends;
×
UNCOV
699
    while (NULL != item) {
×
UNCOV
700
        if (NULL != item->key) {
×
UNCOV
701
            if (StringEqual(MENDER_ARTIFACT_DEVICE_TYPE_KEY, item->key)) {
×
UNCOV
702
                *device_type = item->value;
×
703
                return MENDER_OK;
×
704
            }
705
        }
UNCOV
706
        item = item->next;
×
707
    }
UNCOV
708
    return MENDER_FAIL;
×
709
}
710

711
static mender_err_t
712
artifact_read_manifest(mender_artifact_ctx_t *ctx) {
×
713

UNCOV
714
    assert(NULL != ctx);
×
715

716
    /* Check if all data has been received */
717
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
718
        return MENDER_OK;
×
719
    }
720

721
    /*  The expected format matches the output of sha256sum: sum and the name of the file separated by two spaces
722
        1d0b820130ae028ce8a79b7e217fe505a765ac394718e795d454941487c53d32  data/0000/update.ext4
723
        4d480539cdb23a4aee6330ff80673a5af92b7793eb1c57c4694532f96383b619  header.tar.gz
724
        52c76ab66947278a897c2a6df8b4d77badfa343fec7ba3b2983c2ecbbb041a35  version
725
    */
726

727
    /* Read data line by line */
728
    char *line = ctx->input.data;
×
UNCOV
729
    char *end  = line + ctx->input.length;
×
UNCOV
730
    while (line < end) {
×
731
        char *next = strchr(line, '\n');
×
UNCOV
732
        if (NULL == next) {
×
UNCOV
733
            break;
×
734
        }
735
        *next = '\0';
×
736

737
        /* Process line */
738
        char *separator = strstr(line, "  ");
×
UNCOV
739
        if (NULL == separator) {
×
UNCOV
740
            mender_log_error("Invalid manifest file");
×
UNCOV
741
            return MENDER_FAIL;
×
742
        }
743
        *separator = '\0';
×
744

745
        const char *checksum_str = line;
×
746
        const char *filename     = separator + 2;
×
747

748
        /* Useful when debugging artifact integrity check failures */
749
        mender_log_debug("%s  %s", checksum_str, filename);
750

751
        /* Make sure digest is of expected length (two hex per byte) */
752
        if ((MENDER_DIGEST_BUFFER_SIZE * 2) != strlen(checksum_str)) {
×
753
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
754
            return MENDER_FAIL;
×
755
        }
756

757
        /* Get checksum entry for the file (creates one if not found) */
758
        mender_artifact_checksum_t *checksum;
759
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, filename))) {
×
760
            /* Error already logged */
UNCOV
761
            return MENDER_FAIL;
×
762
        }
763

764
        /* Populate with manifest checksum */
765
        if (!mender_utils_hexdump_to_bytes(checksum_str, checksum->manifest, MENDER_DIGEST_BUFFER_SIZE)) {
×
UNCOV
766
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
767
            return MENDER_FAIL;
×
768
        }
769

770
        ///* Move to the next line */
UNCOV
771
        line = next + 1;
×
772
    }
773

774
    /* Shift data in the buffer */
775
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
776
        mender_log_error("Unable to shift input data");
×
777
        return MENDER_FAIL;
×
778
    }
779

780
    return MENDER_DONE;
×
781
}
782

783
static mender_err_t
UNCOV
784
artifact_parse_provides_depends(cJSON *json_provides_depends, mender_key_value_list_t **provides_depends) {
×
785

786
    assert(NULL != json_provides_depends);
×
787
    assert(NULL != provides_depends);
×
788

789
    /* Create linked-list from json object */
790
    /* The elements can either be a string or an array of strings */
791
    cJSON *json_element = NULL;
×
792
    cJSON_ArrayForEach(json_element, json_provides_depends) {
×
793
        if (cJSON_IsString(json_element)) {
×
794
            if (MENDER_OK != mender_utils_key_value_list_create_node(json_element->string, json_element->valuestring, provides_depends)) {
×
795
                mender_log_error("Unable to create linked list node for string element");
×
UNCOV
796
                goto ERROR;
×
797
            }
798
        } else if (cJSON_IsArray(json_element)) {
×
799
            cJSON *json_element_value = NULL;
×
800
            cJSON_ArrayForEach(json_element_value, json_element) {
×
801
                if (MENDER_OK != mender_utils_key_value_list_create_node(json_element->string, json_element_value->valuestring, provides_depends)) {
×
802
                    mender_log_error("Unable to create linked list node for array element");
×
803
                    goto ERROR;
×
804
                }
805
            }
806
        } else {
UNCOV
807
            mender_log_error("Invalid header-info file element type");
×
UNCOV
808
            goto ERROR;
×
809
        }
810
    }
811

UNCOV
812
    return MENDER_OK;
×
813

814
ERROR:
×
815
    /* Free linked list in case of error */
816
    mender_utils_key_value_list_free(*provides_depends);
×
UNCOV
817
    return MENDER_FAIL;
×
818
}
819
#endif
820

821
static mender_err_t
822
artifact_read_header_info(mender_artifact_ctx_t *ctx) {
×
823

824
    assert(NULL != ctx);
×
825
    cJSON       *object = NULL;
×
826
    mender_err_t ret    = MENDER_DONE;
×
827

828
    /* Check if all data have been received */
UNCOV
829
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
830
        return MENDER_OK;
×
831
    }
832

833
    /* Read header-info */
834
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
×
835
        mender_log_error("Unable to allocate memory");
×
836
        return MENDER_FAIL;
×
837
    }
UNCOV
838
    cJSON *json_payloads = cJSON_GetObjectItemCaseSensitive(object, "payloads");
×
UNCOV
839
    if (true == cJSON_IsArray(json_payloads)) {
×
UNCOV
840
        ctx->payloads.size = cJSON_GetArraySize(json_payloads);
×
UNCOV
841
        if (NULL == (ctx->payloads.values = (mender_artifact_payload_t *)calloc(ctx->payloads.size, sizeof(mender_artifact_payload_t)))) {
×
842
            mender_log_error("Unable to allocate memory");
×
843
            ret = MENDER_FAIL;
×
844
            goto END;
×
845
        }
UNCOV
846
        size_t index        = 0;
×
UNCOV
847
        cJSON *json_payload = NULL;
×
848
        cJSON_ArrayForEach(json_payload, json_payloads) {
×
849
            if (true == cJSON_IsObject(json_payload)) {
×
850
                cJSON *json_payload_type = cJSON_GetObjectItemCaseSensitive(json_payload, "type");
×
851
                if (cJSON_IsString(json_payload_type)) {
×
UNCOV
852
                    if (NULL == (ctx->payloads.values[index].type = strdup(cJSON_GetStringValue(json_payload_type)))) {
×
UNCOV
853
                        mender_log_error("Unable to allocate memory");
×
854
                        ret = MENDER_FAIL;
×
UNCOV
855
                        goto END;
×
856
                    }
857
                } else {
UNCOV
858
                    mender_log_error("Invalid header-info file");
×
859
                    ret = MENDER_FAIL;
×
UNCOV
860
                    goto END;
×
861
                }
862
            } else {
UNCOV
863
                mender_log_error("Invalid header-info file");
×
864
                ret = MENDER_FAIL;
×
865
                goto END;
×
866
            }
UNCOV
867
            index++;
×
868
        }
869

870
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
871
        cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
×
UNCOV
872
        if (cJSON_IsObject(json_provides)) {
×
UNCOV
873
            if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->artifact_info.provides))) {
×
874
                mender_log_error("Unable to parse artifact_provides");
×
UNCOV
875
                ret = MENDER_FAIL;
×
876
                goto END;
×
877
            }
878
        }
879

880
        cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
×
881
        if (cJSON_IsObject(json_depends)) {
×
882
            if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->artifact_info.depends))) {
×
UNCOV
883
                mender_log_error("Unable to parse artifact_depends");
×
UNCOV
884
                ret = MENDER_FAIL;
×
885
                goto END;
×
886
            }
887
        }
888
#endif
889

890
    } else {
891
        mender_log_error("Invalid header-info file");
×
UNCOV
892
        ret = MENDER_FAIL;
×
893
        goto END;
×
894
    }
895

896
    /* Shift data in the buffer */
UNCOV
897
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
898
        mender_log_error("Unable to shift input data");
×
899
        ret = MENDER_FAIL;
×
900
        goto END;
×
901
    }
902

UNCOV
903
END:
×
904

905
    /* Release memory */
906
    cJSON_Delete(object);
×
907

UNCOV
908
    return ret;
×
909
}
910

911
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
912
static mender_err_t
913
artifact_read_header(mender_artifact_ctx_t *ctx) {
×
UNCOV
914
    assert(NULL != ctx);
×
915

916
    /* Check if all data have been received */
917
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
918
        return MENDER_OK;
×
919
    }
920

921
    /* Get checksum entry (create one if needed) */
922
    mender_artifact_checksum_t *checksum;
UNCOV
923
    if (NULL == (checksum = artifact_checksum_get_or_create(ctx, "header.tar"))) {
×
924
        /* Error already logged */
925
        return MENDER_FAIL;
×
926
    }
927

928
    /* Update SHA-256 checksum */
929
    if (MENDER_OK != mender_sha256_update(checksum->context, ctx->input.data, ctx->file.size)) {
×
930
        mender_log_error("Failed to update update checksum");
×
UNCOV
931
        return MENDER_FAIL;
×
932
    }
933

934
    return MENDER_DONE;
×
935
}
936
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
937

938
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
939
static mender_err_t
940
artifact_read_type_info(mender_artifact_ctx_t *ctx) {
×
941

UNCOV
942
    assert(NULL != ctx);
×
UNCOV
943
    cJSON       *object = NULL;
×
944
    mender_err_t ret    = MENDER_DONE;
×
945
    size_t       index  = 0;
×
946

947
    /* Check if all data have been received */
948
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
949
        return MENDER_OK;
×
950
    }
951

952
    /* Read type-info */
953
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
×
UNCOV
954
        mender_log_error("Unable to allocate memory");
×
955
        return MENDER_FAIL;
×
956
    }
957

958
    /* Check if payload index is valid */
959
    if (NULL == ctx->payloads.values[index].type) {
×
960
        mender_log_error("Invalid artifact format; no payload found for index %d", index);
×
UNCOV
961
        ret = MENDER_FAIL;
×
UNCOV
962
        goto END;
×
963
    }
964
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
965
    cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
×
UNCOV
966
    if (cJSON_IsObject(json_provides)) {
×
967
        if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->payloads.values[index].provides))) {
×
968
            mender_log_error("Unable to parse artifact_provides");
×
969
            ret = MENDER_FAIL;
×
970
            goto END;
×
971
        }
972
    }
973

UNCOV
974
    cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
×
UNCOV
975
    if (cJSON_IsObject(json_depends)) {
×
976
        if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->payloads.values[index].depends))) {
×
UNCOV
977
            mender_log_error("Unable to parse artifact_depends");
×
978
            ret = MENDER_FAIL;
×
UNCOV
979
            goto END;
×
980
        }
981
    }
982

983
    cJSON *json_clears_provides = cJSON_GetObjectItemCaseSensitive(object, "clears_artifact_provides");
×
UNCOV
984
    if (cJSON_IsArray(json_clears_provides)) {
×
985
        ctx->payloads.values[index].clears_provides_size = cJSON_GetArraySize(json_clears_provides);
×
UNCOV
986
        ctx->payloads.values[index].clears_provides      = (char **)calloc(ctx->payloads.values[index].clears_provides_size, sizeof(char *));
×
UNCOV
987
        if (NULL == ctx->payloads.values[index].clears_provides) {
×
UNCOV
988
            mender_log_error("Unable to allocate memory");
×
UNCOV
989
            ret = MENDER_FAIL;
×
990
            goto END;
×
991
        }
992

993
        size_t i                            = 0;
×
UNCOV
994
        cJSON *json_clears_provides_element = NULL;
×
995

UNCOV
996
        cJSON_ArrayForEach(json_clears_provides_element, json_clears_provides) {
×
997
            if (cJSON_IsString(json_clears_provides_element)) {
×
UNCOV
998
                char *clears_provides = strdup(json_clears_provides_element->valuestring);
×
999
                if (NULL == clears_provides) {
×
UNCOV
1000
                    mender_log_error("Unable to allocate memory");
×
1001
                    ret = MENDER_FAIL;
×
1002
                    goto END;
×
1003
                }
1004
                ctx->payloads.values[index].clears_provides[i] = clears_provides;
×
1005
                i++;
×
1006
            } else {
UNCOV
1007
                mender_log_error("Invalid header-info file");
×
UNCOV
1008
                ret = MENDER_FAIL;
×
1009
                goto END;
×
1010
            }
1011
        }
1012
    }
1013
#endif
1014

1015
    /* Shift data in the buffer */
UNCOV
1016
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
1017
        mender_log_error("Unable to shift input data");
×
UNCOV
1018
        ret = MENDER_FAIL;
×
1019
        goto END;
×
1020
    }
1021

UNCOV
1022
END:
×
1023

1024
    /* Release memory */
1025
    cJSON_Delete(object);
×
1026

UNCOV
1027
    return ret;
×
1028
}
1029
#endif
1030

1031
static mender_err_t
1032
artifact_read_meta_data(mender_artifact_ctx_t *ctx) {
×
1033

UNCOV
1034
    assert(NULL != ctx);
×
1035

1036
    /* Retrieve payload index. We expect "header.tar/headers/%u/meta-data" where
1037
     * %u is the index. Yes sscanf(3) would be nice, but we've experienced
1038
     * unexplained segmentation faults on some hardware when using it. */
UNCOV
1039
    const char *const prefix = "header.tar/headers/";
×
UNCOV
1040
    if (!mender_utils_strbeginswith(ctx->file.name, prefix)) {
×
UNCOV
1041
        mender_log_error("Invalid artifact format");
×
UNCOV
1042
        return MENDER_FAIL;
×
1043
    }
1044

1045
    assert(sizeof(size_t) >= sizeof(unsigned long));
UNCOV
1046
    const char *start_ptr = ctx->file.name + strlen(prefix);
×
1047
    char       *end_ptr;
UNCOV
1048
    errno = 0; /* to distinguish between success/failure */
×
1049

UNCOV
1050
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
×
1051
    if ((end_ptr == start_ptr)              /* no conversion */
×
UNCOV
1052
        || (0 != errno)                     /* out of range (for unsigned long) */
×
UNCOV
1053
        || (index >= ctx->payloads.size)) { /* index out of bounds */
×
UNCOV
1054
        mender_log_error("Invalid artifact format");
×
UNCOV
1055
        return MENDER_FAIL;
×
1056
    }
1057

UNCOV
1058
    assert(NULL != end_ptr);
×
UNCOV
1059
    assert(StringEqualN(end_ptr, "/meta-data", 10)); /* just one last sanity check */
×
1060

1061
    /* Check size of the meta-data */
UNCOV
1062
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1063
        /* Nothing to do */
1064
        return MENDER_DONE;
×
1065
    }
1066

1067
    /* Check if all data have been received */
1068
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
1069
        return MENDER_OK;
×
1070
    }
1071

1072
    /* Read meta-data */
1073
    if (NULL == (ctx->payloads.values[index].meta_data = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
×
1074
        mender_log_error("Unable to allocate memory");
×
UNCOV
1075
        return MENDER_FAIL;
×
1076
    }
1077

1078
    /* Shift data in the buffer */
1079
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
1080
        mender_log_error("Unable to shift input data");
×
UNCOV
1081
        return MENDER_FAIL;
×
1082
    }
1083

UNCOV
1084
    return MENDER_DONE;
×
1085
}
1086

1087
/**
1088
 * @brief Callback function to be invoked to perform the treatment of the data from the artifact
1089
 * @param deployment_id Deployment ID
1090
 * @param type Type from header-info payloads
1091
 * @param artifact_name Artifact name
1092
 * @param meta_data Meta-data from header tarball
1093
 * @param filename Artifact filename
1094
 * @param size Artifact file size
1095
 * @param data Artifact data
1096
 * @param index Artifact data index
1097
 * @param length Artifact data length
1098
 * @param dl_data Download data for the artifact
1099
 * @return MENDER_OK if the function succeeds, error code if an error occurred
1100
 */
1101
static mender_err_t
1102
process_artifact_data_callback(const char                      *deployment_id,
×
1103
                               const char                      *type,
1104
                               const char                      *artifact_name,
1105
                               const cJSON                     *meta_data,
1106
                               const char                      *filename,
1107
                               size_t                           size,
1108
                               void                            *data,
1109
                               size_t                           index,
1110
                               size_t                           length,
1111
                               mender_artifact_download_data_t *dl_data) {
1112

UNCOV
1113
    assert(NULL != type);
×
UNCOV
1114
    mender_err_t ret = MENDER_FAIL;
×
1115

1116
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_INF
UNCOV
1117
    if (size > 0) {
×
1118
        static size_t download_progress = 0;
1119
        /* New update */
UNCOV
1120
        if (0 == index) {
×
1121
            download_progress = 0;
×
1122
        }
1123

1124
        /* Update every 10% */
UNCOV
1125
        if (((index * 10) / size) > download_progress) {
×
UNCOV
1126
            download_progress = (index * 10) / size;
×
1127
            mender_log_info("Downloading '%s' %zu0%%... [%zu/%zu]", type, download_progress, index, size);
×
1128
        }
1129
    }
1130
#endif
1131

1132
    /* Invoke update module download callback */
UNCOV
1133
    struct mender_update_download_state_data_s download_state_data
×
1134
        = { deployment_id, artifact_name, type, meta_data, filename, size, data, index, length, false };
UNCOV
1135
    mender_update_state_data_t state_data = { .download_state_data = &download_state_data };
×
1136
    if (MENDER_OK != (ret = dl_data->update_module->callbacks[MENDER_UPDATE_STATE_DOWNLOAD](MENDER_UPDATE_STATE_DOWNLOAD, state_data))) {
×
UNCOV
1137
        mender_log_error("An error occurred while processing data of the artifact '%s' of type '%s'", artifact_name, type);
×
1138
        return ret;
×
1139
    }
1140

1141
    return MENDER_OK;
×
1142
}
1143

1144
static mender_err_t
UNCOV
1145
artifact_read_data_prepare(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache) {
×
1146
    /* First, retrieve payload index. We expect "data/%u.tar" where %u is the
1147
     * index. Yes sscanf(3) would be nice, but we've experienced unexplained
1148
     * segmentation faults on some hardware when using it. */
UNCOV
1149
    const char *const prefix = "data/";
×
1150
    if (!mender_utils_strbeginswith(ctx->file.name, prefix)) {
×
UNCOV
1151
        mender_log_error("Invalid artifact format");
×
UNCOV
1152
        return MENDER_FAIL;
×
1153
    }
1154

1155
    assert(sizeof(size_t) >= sizeof(unsigned long));
1156
    const char *start_ptr = ctx->file.name + strlen(prefix);
×
1157
    char       *end_ptr;
UNCOV
1158
    errno = 0; /* to distinguish between success/failure */
×
1159

UNCOV
1160
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
×
UNCOV
1161
    if ((end_ptr == start_ptr)              /* no conversion */
×
UNCOV
1162
        || (0 != errno)                     /* out of range (for unsigned long) */
×
1163
        || (index >= ctx->payloads.size)) { /* index out of bounds */
×
UNCOV
1164
        mender_log_error("Invalid artifact format");
×
1165
        return MENDER_FAIL;
×
1166
    }
1167

UNCOV
1168
    assert(NULL != end_ptr);
×
UNCOV
1169
    assert(StringEqualN(end_ptr, ".tar", 4)); /* just one last sanity check */
×
1170

UNCOV
1171
    const char *payload_type = ctx->payloads.values[index].type;
×
1172

1173
    /* Choose update module */
UNCOV
1174
    dl_data->update_module = mender_update_module_get(payload_type);
×
UNCOV
1175
    if (NULL == dl_data->update_module) {
×
1176
        /* Content is not supported by the mender-mcu-client */
1177
        mender_log_error("Unable to handle artifact type '%s'", payload_type);
×
1178
        return MENDER_FAIL;
×
1179
    }
1180

1181
    /* Add the payload type to deployment data  */
1182
    if (MENDER_OK != mender_deployment_data_add_payload_type(dl_data->deployment, payload_type)) {
×
1183
        /* Error already logged */
UNCOV
1184
        return MENDER_FAIL;
×
1185
    }
1186

1187
    /* Retrieve ID and artifact name */
1188
    if (MENDER_OK != mender_deployment_data_get_id(dl_data->deployment, &(mdata_cache->deployment_id))) {
×
1189
        mender_log_error("Unable to get ID from the deployment data");
×
UNCOV
1190
        return MENDER_FAIL;
×
1191
    }
UNCOV
1192
    if (MENDER_OK != mender_deployment_data_get_artifact_name(dl_data->deployment, &(mdata_cache->artifact_name))) {
×
UNCOV
1193
        mender_log_error("Unable to get artifact name from the deployment data");
×
1194
        return MENDER_FAIL;
×
1195
    }
1196

UNCOV
1197
    mdata_cache->payload_type = payload_type;
×
1198
    mdata_cache->meta_data    = ctx->payloads.values[index].meta_data;
×
UNCOV
1199
    mdata_cache->filename     = strstr(ctx->file.name, ".tar") + strlen(".tar") + 1;
×
1200

1201
    /* The filename will be something like
1202
     * 'data/0000.tar/zephyr.signed.bin'. But the manifest will hold
1203
     * 'data/0000/zephyr.signed.bin'. Hence, we need to remove the
1204
     * '.tar' extension from the string.
1205
     */
1206
    if (NULL == (mdata_cache->checksum_fname = strdup(ctx->file.name))) {
×
1207
        mender_log_error("Unable to allocate memory");
×
UNCOV
1208
        return MENDER_FAIL;
×
1209
    }
UNCOV
1210
    for (char *ch = strstr(mdata_cache->checksum_fname, ".tar"); (NULL != ch) && (*ch != '\0'); ch++) {
×
1211
        /* Don't worry! The call to strlen() on a static string should
1212
         * be optimized out by the compiler */
1213
        *ch = ch[strlen(".tar")];
×
1214
    }
1215

UNCOV
1216
    mdata_cache->valid = true;
×
UNCOV
1217
    return MENDER_OK;
×
1218
}
1219

1220
static mender_err_t
1221
artifact_read_data(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache) {
×
1222

UNCOV
1223
    assert(NULL != ctx);
×
1224
    mender_err_t ret;
1225

1226
    /* Check size of the data */
UNCOV
1227
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1228
        /* Nothing to do */
1229
        return MENDER_DONE;
×
1230
    }
1231

1232
    /* Parse data until the end of the file has been reached */
1233
    do {
1234

1235
        /* Check if enough data are received (at least one block) */
1236
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
1237
            return MENDER_OK;
×
1238
        }
1239

1240
        /* Compute length */
UNCOV
1241
        size_t length
×
1242
            = ((ctx->file.size - ctx->file.index) > MENDER_ARTIFACT_STREAM_BLOCK_SIZE) ? MENDER_ARTIFACT_STREAM_BLOCK_SIZE : (ctx->file.size - ctx->file.index);
×
1243

1244
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
1245
        mender_artifact_checksum_t *checksum;
1246
        /* Get checksum entry (create one if needed) */
UNCOV
1247
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, mdata_cache->checksum_fname))) {
×
1248
            /* Error already logged */
UNCOV
1249
            return MENDER_FAIL;
×
1250
        }
1251

1252
        /* Update SHA-256 checksum */
UNCOV
1253
        if (MENDER_OK != mender_sha256_update(checksum->context, ctx->input.data, length)) {
×
UNCOV
1254
            mender_log_error("Failed to update update checksum");
×
1255
            return MENDER_FAIL;
×
1256
        }
1257
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
1258

1259
        /* Invoke the download artifact callback */
1260
        ret = process_artifact_data_callback(mdata_cache->deployment_id,
×
1261
                                             mdata_cache->payload_type,
1262
                                             mdata_cache->artifact_name,
1263
                                             mdata_cache->meta_data,
×
1264
                                             mdata_cache->filename,
1265
                                             ctx->file.size,
1266
                                             ctx->input.data,
1267
                                             ctx->file.index,
1268
                                             length,
1269
                                             dl_data);
1270
        if (MENDER_OK != ret) {
×
UNCOV
1271
            mender_log_error("An error occurred");
×
UNCOV
1272
            return ret;
×
1273
        }
1274

1275
        /* Update index */
1276
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1277

1278
        /* Shift data in the buffer */
1279
        if (MENDER_OK != (ret = artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
1280
            mender_log_error("Unable to shift input data");
×
1281
            return ret;
×
1282
        }
1283

UNCOV
1284
    } while (ctx->file.index < ctx->file.size);
×
1285

UNCOV
1286
    return MENDER_DONE;
×
1287
}
1288

1289
static mender_err_t
UNCOV
1290
artifact_drop_file(mender_artifact_ctx_t *ctx) {
×
1291

UNCOV
1292
    assert(NULL != ctx);
×
1293
    mender_err_t ret;
1294

1295
    /* Check size of the data */
1296
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1297
        /* Nothing to do */
UNCOV
1298
        return MENDER_DONE;
×
1299
    }
1300

1301
    /* Parse data until the end of the file has been reached */
1302
    do {
1303

1304
        /* Check if enough data are received (at least one block) */
UNCOV
1305
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
1306
            return MENDER_OK;
×
1307
        }
1308

1309
        /* Update index */
UNCOV
1310
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1311

1312
        /* Shift data in the buffer */
UNCOV
1313
        if (MENDER_OK != (ret = artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
1314
            mender_log_error("Unable to shift input data");
×
UNCOV
1315
            return ret;
×
1316
        }
1317

UNCOV
1318
    } while (ctx->file.index < ctx->file.size);
×
1319

UNCOV
1320
    return MENDER_DONE;
×
1321
}
1322

1323
static mender_err_t
UNCOV
1324
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
×
1325

UNCOV
1326
    assert(NULL != ctx);
×
1327

1328
    /* Shift data */
UNCOV
1329
    if (length > 0) {
×
UNCOV
1330
        if (ctx->input.length > length) {
×
UNCOV
1331
            memmove(ctx->input.data, (void *)(((uint8_t *)ctx->input.data) + length), ctx->input.length - length);
×
UNCOV
1332
            ctx->input.length -= length;
×
1333
            /* Here we could shrink the ctx->input.data buffer, but most likely, we would need to
1334
               grow it again when we receive another batch of data so there's little point in doing
1335
               so. */
1336
        } else {
UNCOV
1337
            ctx->input.length = 0;
×
1338
        }
1339
    }
1340

UNCOV
1341
    return MENDER_OK;
×
1342
}
1343

1344
static size_t
UNCOV
1345
artifact_round_up(size_t length, size_t incr) {
×
UNCOV
1346
    return length + (incr - length % incr) % incr;
×
1347
}
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