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

mendersoftware / mender-mcu / 1559779716

25 Nov 2024 06:45PM UTC coverage: 9.39% (-0.03%) from 9.418%
1559779716

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 16 new or added lines in 3 files covered. (0.0%)

472 existing lines in 5 files now uncovered.

251 of 2673 relevant lines covered (9.39%)

0.67 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
147
data_mdata_cache_invalidate(struct data_mdata_cache *mdata_cache) {
×
148
    assert(NULL != mdata_cache);
×
149

150
    FREE_AND_NULL(mdata_cache->checksum_fname);
×
151
    mdata_cache->valid = false;
×
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 *
210
artifact_checksum_get_or_create(mender_artifact_ctx_t *ctx, const char *filename) {
×
211
    assert(NULL != ctx);
×
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)) {
×
218
            break;
×
219
        }
220
    }
221

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);
×
230
        if (NULL == checksum->filename) {
×
231
            mender_log_error("Unable to allocate memory");
×
232
            free(checksum);
×
233
            return NULL;
×
234
        }
235
        checksum->next               = ctx->artifact_info.checksums;
×
236
        ctx->artifact_info.checksums = checksum;
×
237

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

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

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

253
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
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

258
        if (MENDER_OK != mender_sha256_finish(checksum->context, computed)) {
×
259
            mender_log_error("Failed to finish checksum for file '%s'", checksum->filename);
×
260
            checksum->context = NULL;
×
261
            return MENDER_FAIL;
×
262
        }
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 */
289
    return MENDER_OK;
×
290
}
291

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

295
    mender_artifact_ctx_t *ctx;
296

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

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

313
    return ctx;
×
314
}
315

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

319
    assert(NULL != ctx);
×
320

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

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

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

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

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

UNCOV
341
    return false;
×
342
}
343

344
static struct data_mdata_cache data_mdata_cache = { 0 };
345

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

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

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

387
    /* Parse data */
388
    do {
389

390
        /* Treatment depending of the stream state */
391
        if (MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER == ctx->stream_state) {
×
392

393
            /* Parse TAR header */
UNCOV
394
            ret = artifact_parse_tar_header(ctx);
×
395

396
            /* Processing a new (data) file, invalidate the cache */
UNCOV
397
            data_mdata_cache_invalidate(&data_mdata_cache);
×
398

399
        } else if (MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA == ctx->stream_state) {
×
400

401
            /* Treatment depending of the file name */
402
            if (StringEqual(ctx->file.name, "version")) {
×
403

404
                /* Validate artifact version */
UNCOV
405
                ret = artifact_read_version(ctx);
×
406

407
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
408
            } else if (StringEqual(ctx->file.name, "manifest")) {
×
409

410
                /* Read manifest file */
UNCOV
411
                ret = artifact_read_manifest(ctx);
×
412
#endif
413
            } else if (StringEqual(ctx->file.name, "header.tar/header-info")) {
×
414

415
                /* Read header-info file */
416
                ret = artifact_read_header_info(ctx);
×
417

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

421
                /* Read meta-data file */
UNCOV
422
                ret = artifact_read_meta_data(ctx);
×
423

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

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

440
                if (MENDER_OK == ret) {
×
UNCOV
441
                    assert(data_mdata_cache.valid);
×
442
                    /* Read data */
UNCOV
443
                    ret = artifact_read_data(ctx, dl_data, &data_mdata_cache);
×
444
                }
445
            } else if (false == mender_utils_strendswith(ctx->file.name, ".tar")) {
×
446

447
                /* Drop data, file is not relevant */
448
                ret = artifact_drop_file(ctx);
×
449
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
450
            } else if (StringEqual(ctx->file.name, "header.tar")) {
×
UNCOV
451
                ret = artifact_read_header(ctx);
×
452
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
453
            } else {
454

455
                /* Nothing to do */
456
                ret = MENDER_DONE;
×
457
            }
458

459
            /* Check if file have been parsed and treatment done */
UNCOV
460
            if (MENDER_DONE == ret) {
×
461

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

472
                /* Update the stream state machine */
UNCOV
473
                ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER;
×
474
            }
475
        }
UNCOV
476
    } while (MENDER_DONE == ret);
×
477

478
    return ret;
×
479
}
480

481
void
UNCOV
482
mender_artifact_release_ctx(mender_artifact_ctx_t *ctx) {
×
483

484
    /* Release memory */
UNCOV
485
    if (NULL != ctx) {
×
UNCOV
486
        free(ctx->input.data);
×
487
        if (NULL != ctx->payloads.values) {
×
UNCOV
488
            for (size_t index = 0; index < ctx->payloads.size; index++) {
×
UNCOV
489
                free(ctx->payloads.values[index].type);
×
490
                cJSON_Delete(ctx->payloads.values[index].meta_data);
×
491

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

516
static mender_err_t
517
artifact_parse_tar_header(mender_artifact_ctx_t *ctx) {
×
518

519
    assert(NULL != ctx);
×
520
    char *tmp;
521

522
    /* Check if enough data are received (at least one block) */
UNCOV
523
    if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
524
        return MENDER_OK;
×
525
    }
526

527
    /* Cast block to TAR header structure */
528
    mender_artifact_tar_header_t *tar_header = (mender_artifact_tar_header_t *)ctx->input.data;
×
529

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

533
        /* Check if enough data are received (at least 2 blocks) */
UNCOV
534
        if (ctx->input.length < 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE) {
×
UNCOV
535
            return MENDER_OK;
×
536
        }
537

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

554
        /* Shift data in the buffer */
555
        if (MENDER_OK != artifact_shift_data(ctx, 2 * MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
556
            mender_log_error("Unable to shift input data");
×
UNCOV
557
            return MENDER_FAIL;
×
558
        }
559

560
        return MENDER_DONE;
×
561
    }
562

563
    /* Check magic */
UNCOV
564
    if (strncmp(tar_header->magic, "ustar", strlen("ustar"))) {
×
565
        /* Invalid magic */
UNCOV
566
        mender_log_error("Invalid magic");
×
UNCOV
567
        return MENDER_FAIL;
×
568
    }
569

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

587
    /* Retrieve file size */
588
    assert(sizeof(size_t) >= sizeof(unsigned long));
589
    char *end_ptr;
590
    errno = 0; /* to distinguish between success/failure */
×
591

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

599
    ctx->file.index = 0;
×
600

601
    /* Shift data in the buffer */
UNCOV
602
    if (MENDER_OK != artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
603
        mender_log_error("Unable to shift input data");
×
604
        return MENDER_FAIL;
×
605
    }
606

607
    /* Update the stream state machine */
608
    ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA;
×
609

UNCOV
610
    return MENDER_DONE;
×
611
}
612

613
static mender_err_t
UNCOV
614
artifact_read_version(mender_artifact_ctx_t *ctx) {
×
615

UNCOV
616
    assert(NULL != ctx);
×
UNCOV
617
    cJSON       *object = NULL;
×
UNCOV
618
    mender_err_t ret    = MENDER_DONE;
×
619

620
    /* Check if all data have been received */
621
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
622
        return MENDER_OK;
×
623
    }
624

625
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
626
    /* Get checksum entry (create one if needed) */
627
    mender_artifact_checksum_t *checksum;
UNCOV
628
    if (NULL == (checksum = artifact_checksum_get_or_create(ctx, "version"))) {
×
629
        /* Error already logged */
UNCOV
630
        return MENDER_FAIL;
×
631
    }
632

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

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

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

678
END:
×
679

680
    /* Release memory */
UNCOV
681
    cJSON_Delete(object);
×
682

683
    return ret;
×
684
}
685

686
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
687
mender_err_t
688
mender_artifact_get_device_type(mender_artifact_ctx_t *ctx, const char **device_type) {
×
689

UNCOV
690
    assert(NULL != ctx);
×
UNCOV
691
    assert(NULL != device_type);
×
692

693
    mender_key_value_list_t *item = ctx->artifact_info.depends;
×
UNCOV
694
    while (NULL != item) {
×
695
        if (NULL != item->key) {
×
696
            if (StringEqual(MENDER_ARTIFACT_DEVICE_TYPE_KEY, item->key)) {
×
UNCOV
697
                *device_type = item->value;
×
698
                return MENDER_OK;
×
699
            }
700
        }
701
        item = item->next;
×
702
    }
703
    return MENDER_FAIL;
×
704
}
705

706
static mender_err_t
UNCOV
707
artifact_read_manifest(mender_artifact_ctx_t *ctx) {
×
708

UNCOV
709
    assert(NULL != ctx);
×
710

711
    /* Check if all data has been received */
712
    if ((NULL == ctx->input.data) || (ctx->input.length < artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
713
        return MENDER_OK;
×
714
    }
715

716
    /*  The expected format matches the output of sha256sum: sum and the name of the file separated by two spaces
717
        1d0b820130ae028ce8a79b7e217fe505a765ac394718e795d454941487c53d32  data/0000/update.ext4
718
        4d480539cdb23a4aee6330ff80673a5af92b7793eb1c57c4694532f96383b619  header.tar.gz
719
        52c76ab66947278a897c2a6df8b4d77badfa343fec7ba3b2983c2ecbbb041a35  version
720
    */
721

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

732
        /* Process line */
733
        char *separator = strstr(line, "  ");
×
UNCOV
734
        if (NULL == separator) {
×
735
            mender_log_error("Invalid manifest file");
×
UNCOV
736
            return MENDER_FAIL;
×
737
        }
738
        *separator = '\0';
×
739

740
        const char *checksum_str = line;
×
741
        const char *filename     = separator + 2;
×
742

743
        /* We do not support compressed artifacts */
UNCOV
744
        if (mender_utils_strbeginswith(filename, "header.tar") && is_compressed(filename)) {
×
745
            mender_log_error("Artifact compression is not supported");
×
746
            return MENDER_FAIL;
×
747
        }
748

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

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

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

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

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

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

UNCOV
781
    return MENDER_DONE;
×
782
}
783

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

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

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

UNCOV
813
    return MENDER_OK;
×
814

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

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

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

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

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

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

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

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

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

UNCOV
904
END:
×
905

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

UNCOV
909
    return ret;
×
910
}
911

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1023
END:
×
1024

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1085
    return MENDER_DONE;
×
1086
}
1087

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

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

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

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

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

UNCOV
1142
    return MENDER_OK;
×
1143
}
1144

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

1156
    size_t file_name_length = strlen(ctx->file.name);
×
1157
    /* We check the length to make sure we only check for compression on the
1158
     * payload itself - not the files inside the payload */
UNCOV
1159
    if ((strlen("data/xxxx.tar.xx") == file_name_length) || (strlen("data/xxxx.tar.xxx") == file_name_length)) {
×
1160
        /*
1161
        * We allow compressed files _inside_ a payload:
1162
        *   'data/0000.tar/compressed.tar.gz'
1163
        * But not a compressed payload:
1164
        *   'data/0000.tar[.gz|.xz|.zst]'
1165
        **/
UNCOV
1166
        if (is_compressed(ctx->file.name)) {
×
UNCOV
1167
            mender_log_error("Artifact compression is not supported");
×
1168
            return MENDER_FAIL;
×
1169
        }
1170
    }
1171

1172
    assert(sizeof(size_t) >= sizeof(unsigned long));
UNCOV
1173
    const char *start_ptr = ctx->file.name + strlen(prefix);
×
1174
    char       *end_ptr;
1175
    errno = 0; /* to distinguish between success/failure */
×
1176

1177
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
×
1178
    if ((end_ptr == start_ptr)              /* no conversion */
×
UNCOV
1179
        || (0 != errno)                     /* out of range (for unsigned long) */
×
UNCOV
1180
        || (index >= ctx->payloads.size)) { /* index out of bounds */
×
UNCOV
1181
        mender_log_error("Invalid artifact format");
×
1182
        return MENDER_FAIL;
×
1183
    }
1184

UNCOV
1185
    assert(NULL != end_ptr);
×
UNCOV
1186
    assert(StringEqualN(end_ptr, ".tar", 4)); /* just one last sanity check */
×
1187

1188
    const char *payload_type = ctx->payloads.values[index].type;
×
1189

1190
    /* Choose update module */
UNCOV
1191
    dl_data->update_module = mender_update_module_get(payload_type);
×
1192
    if (NULL == dl_data->update_module) {
×
1193
        /* Content is not supported by the mender-mcu-client */
1194
        mender_log_error("Unable to handle artifact type '%s'", payload_type);
×
UNCOV
1195
        return MENDER_FAIL;
×
1196
    }
1197

1198
    /* Add the payload type to deployment data  */
1199
    if (MENDER_OK != mender_deployment_data_add_payload_type(dl_data->deployment, payload_type)) {
×
1200
        /* Error already logged */
UNCOV
1201
        return MENDER_FAIL;
×
1202
    }
1203

1204
    /* Retrieve ID and artifact name */
UNCOV
1205
    if (MENDER_OK != mender_deployment_data_get_id(dl_data->deployment, &(mdata_cache->deployment_id))) {
×
1206
        mender_log_error("Unable to get ID from the deployment data");
×
1207
        return MENDER_FAIL;
×
1208
    }
UNCOV
1209
    if (MENDER_OK != mender_deployment_data_get_artifact_name(dl_data->deployment, &(mdata_cache->artifact_name))) {
×
1210
        mender_log_error("Unable to get artifact name from the deployment data");
×
UNCOV
1211
        return MENDER_FAIL;
×
1212
    }
1213

UNCOV
1214
    mdata_cache->payload_type = payload_type;
×
UNCOV
1215
    mdata_cache->meta_data    = ctx->payloads.values[index].meta_data;
×
1216
    mdata_cache->filename     = strstr(ctx->file.name, ".tar") + strlen(".tar") + 1;
×
1217

1218
    /* The filename will be something like
1219
     * 'data/0000.tar/zephyr.signed.bin'. But the manifest will hold
1220
     * 'data/0000/zephyr.signed.bin'. Hence, we need to remove the
1221
     * '.tar' extension from the string.
1222
     */
1223
    if (NULL == (mdata_cache->checksum_fname = strdup(ctx->file.name))) {
×
UNCOV
1224
        mender_log_error("Unable to allocate memory");
×
UNCOV
1225
        return MENDER_FAIL;
×
1226
    }
1227
    for (char *ch = strstr(mdata_cache->checksum_fname, ".tar"); (NULL != ch) && (*ch != '\0'); ch++) {
×
1228
        /* Don't worry! The call to strlen() on a static string should
1229
         * be optimized out by the compiler */
UNCOV
1230
        *ch = ch[strlen(".tar")];
×
1231
    }
1232

UNCOV
1233
    mdata_cache->valid = true;
×
UNCOV
1234
    return MENDER_OK;
×
1235
}
1236

1237
static mender_err_t
UNCOV
1238
artifact_read_data(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache) {
×
1239

UNCOV
1240
    assert(NULL != ctx);
×
1241
    mender_err_t ret;
1242

1243
    /* Check size of the data */
UNCOV
1244
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1245
        /* Nothing to do */
UNCOV
1246
        return MENDER_DONE;
×
1247
    }
1248

1249
    /* Parse data until the end of the file has been reached */
1250
    do {
1251

1252
        /* Check if enough data are received (at least one block) */
1253
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1254
            return MENDER_OK;
×
1255
        }
1256

1257
        /* Compute length */
UNCOV
1258
        size_t length
×
UNCOV
1259
            = ((ctx->file.size - ctx->file.index) > MENDER_ARTIFACT_STREAM_BLOCK_SIZE) ? MENDER_ARTIFACT_STREAM_BLOCK_SIZE : (ctx->file.size - ctx->file.index);
×
1260

1261
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
1262
        mender_artifact_checksum_t *checksum;
1263
        /* Get checksum entry (create one if needed) */
UNCOV
1264
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, mdata_cache->checksum_fname))) {
×
1265
            /* Error already logged */
UNCOV
1266
            return MENDER_FAIL;
×
1267
        }
1268

1269
        /* Update SHA-256 checksum */
1270
        if (MENDER_OK != mender_sha256_update(checksum->context, ctx->input.data, length)) {
×
1271
            mender_log_error("Failed to update update checksum");
×
1272
            return MENDER_FAIL;
×
1273
        }
1274
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
1275

1276
        /* Invoke the download artifact callback */
UNCOV
1277
        ret = process_artifact_data_callback(mdata_cache->deployment_id,
×
1278
                                             mdata_cache->payload_type,
1279
                                             mdata_cache->artifact_name,
1280
                                             mdata_cache->meta_data,
×
1281
                                             mdata_cache->filename,
1282
                                             ctx->file.size,
1283
                                             ctx->input.data,
1284
                                             ctx->file.index,
1285
                                             length,
1286
                                             dl_data);
UNCOV
1287
        if (MENDER_OK != ret) {
×
UNCOV
1288
            mender_log_error("An error occurred");
×
UNCOV
1289
            return ret;
×
1290
        }
1291

1292
        /* Update index */
UNCOV
1293
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1294

1295
        /* Shift data in the buffer */
1296
        if (MENDER_OK != (ret = artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
UNCOV
1297
            mender_log_error("Unable to shift input data");
×
1298
            return ret;
×
1299
        }
1300

UNCOV
1301
    } while (ctx->file.index < ctx->file.size);
×
1302

UNCOV
1303
    return MENDER_DONE;
×
1304
}
1305

1306
static mender_err_t
UNCOV
1307
artifact_drop_file(mender_artifact_ctx_t *ctx) {
×
1308

UNCOV
1309
    assert(NULL != ctx);
×
1310
    mender_err_t ret;
1311

1312
    /* Check size of the data */
1313
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
1314
        /* Nothing to do */
1315
        return MENDER_DONE;
×
1316
    }
1317

1318
    /* Parse data until the end of the file has been reached */
1319
    do {
1320

1321
        /* Check if enough data are received (at least one block) */
UNCOV
1322
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
1323
            return MENDER_OK;
×
1324
        }
1325

1326
        /* Update index */
UNCOV
1327
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1328

1329
        /* Shift data in the buffer */
1330
        if (MENDER_OK != (ret = artifact_shift_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
×
1331
            mender_log_error("Unable to shift input data");
×
1332
            return ret;
×
1333
        }
1334

UNCOV
1335
    } while (ctx->file.index < ctx->file.size);
×
1336

1337
    return MENDER_DONE;
×
1338
}
1339

1340
static mender_err_t
1341
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
×
1342

UNCOV
1343
    assert(NULL != ctx);
×
1344

1345
    /* Shift data */
1346
    if (length > 0) {
×
UNCOV
1347
        if (ctx->input.length > length) {
×
UNCOV
1348
            memmove(ctx->input.data, (void *)(((uint8_t *)ctx->input.data) + length), ctx->input.length - length);
×
UNCOV
1349
            ctx->input.length -= length;
×
1350
            /* Here we could shrink the ctx->input.data buffer, but most likely, we would need to
1351
               grow it again when we receive another batch of data so there's little point in doing
1352
               so. */
1353
        } else {
UNCOV
1354
            ctx->input.length = 0;
×
1355
        }
1356
    }
1357

UNCOV
1358
    return MENDER_OK;
×
1359
}
1360

1361
static size_t
UNCOV
1362
artifact_round_up(size_t length, size_t incr) {
×
UNCOV
1363
    return length + (incr - length % incr) % incr;
×
1364
}
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