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

mendersoftware / mender-mcu / 1650273018

31 Jan 2025 01:15PM UTC coverage: 25.682%. First build
1650273018

push

gitlab-ci

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

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

Ticket: MEN-7804

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

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

715 of 2784 relevant lines covered (25.68%)

7.19 hits per line

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

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

21
#include <errno.h>
22

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

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

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

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

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

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

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

86
/**
87
 * @brief Read header-info file of the artifact
88
 * @param ctx Artifact context
89
 * @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, mender_artifact_download_data_t *dl_data);
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) {
75✔
148
    assert(NULL != mdata_cache);
75✔
149

150
    FREE_AND_NULL(mdata_cache->checksum_fname);
75✔
151
    mdata_cache->valid = false;
75✔
152
}
75✔
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
/**
176
 * @brief Shift data after parsing and update the respective checksum context
177
 * @param checksum_key Key under which the checksum for the data is calculated/checked
178
 * @param checksum_len Length of the data to include in the checksum update
179
 * @return MENDER_OK if the function succeeds, error code otherwise
180
 * @see artifact_shift_data()
181
 */
182
static mender_err_t artifact_shift_and_checksum_data(mender_artifact_ctx_t *ctx, size_t length, const char *checksum_key, size_t checksum_len);
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

300
    mender_artifact_ctx_t *ctx;
301

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

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

318
    return ctx;
13✔
319
}
320

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

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

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

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

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

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

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

346
    return false;
7✔
347
}
348

349
static struct data_mdata_cache data_mdata_cache = { 0 };
350

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

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

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

395
    /* Parse data */
396
    do {
397

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

482
    return ret;
10✔
483
}
484

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

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

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

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

528
    char *tmp;
529
    bool  in_header_tar;
530

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

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

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

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

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

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

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

581
        return MENDER_DONE;
18✔
582
    }
583

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

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

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

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

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

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

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

641
    return MENDER_DONE;
49✔
642
}
643

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

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

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

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

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

694
END:
8✔
695

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

699
    return ret;
8✔
700
}
701

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

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

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

722
static char *artifact_file = NULL;
723

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

807
    return MENDER_DONE;
7✔
808
}
809

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

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

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

839
    return MENDER_OK;
18✔
840

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

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

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

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

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

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

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

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

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

947
END:
6✔
948

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

952
    return ret;
7✔
953
}
954

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

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

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

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

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

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

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

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

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

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

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

1042
END:
6✔
1043

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

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

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

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

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

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

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

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

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

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

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

1106
    return MENDER_DONE;
1✔
1107
}
1108

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

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

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

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

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

1163
    return MENDER_OK;
6✔
1164
}
1165

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1296
    return MENDER_DONE;
6✔
1297
}
1298

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

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

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

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

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

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

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

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

1330
    return MENDER_DONE;
×
1331
}
1332

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

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

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

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

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

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

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

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

1384
    return MENDER_OK;
101✔
1385
}
1386

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

© 2025 Coveralls, Inc