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

mendersoftware / mender-mcu / 1652801795

03 Feb 2025 08:48AM UTC coverage: 25.939% (+0.3%) from 25.682%
1652801795

push

gitlab-ci

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

Check if the aritfact has a payload file by adding a boolean that's set
when the download artifact flash callback is called.

Ticket: MEN-7804

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

739 of 2849 relevant lines covered (25.94%)

8.56 hits per line

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

68.06
/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-alloc.h"
24
#include "mender-artifact.h"
25
#include "mender-deployment-data.h"
26
#include "mender-log.h"
27
#include "mender-utils.h"
28

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

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

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

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

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

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

88
/**
89
 * @brief Read header-info file of the artifact
90
 * @param ctx Artifact context
91
 * @param dl_data Download data for the artifact
92
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
93
 */
94
static mender_err_t artifact_read_header_info(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data);
95

96
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
97
/**
98
 * @brief Read manifest file of the artifact
99
 * @param ctx Artifact context
100
 * @return MENDER_DONE if the data have been parsed and checksums retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
101
 */
102
static mender_err_t artifact_read_manifest(mender_artifact_ctx_t *ctx);
103

104
/**
105
 * @brief Read type-info file of the artifact
106
 * @param ctx Artifact context
107
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
108
 */
109
static mender_err_t artifact_read_type_info(mender_artifact_ctx_t *ctx);
110

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

120
/**
121
 * @brief Read meta-data file of the artifact
122
 * @param ctx Artifact context
123
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
124
 */
125
static mender_err_t artifact_read_meta_data(mender_artifact_ctx_t *ctx);
126

127
/**
128
 * @brief Read data file of the artifact
129
 * @param ctx Artifact context
130
 * @param dl_data Download data for the artifact
131
 * @param mdata_cache Cache with metadata for the current data being processed
132
 * @return MENDER_DONE if the data have been parsed and payloads retrieved, MENDER_OK if there is not enough data to parse, error code if an error occurred
133
 */
134
static mender_err_t artifact_read_data(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache);
135

136
/**
137
 * @brief Prepare a cache with metadata for the current data being processed
138
 * @param ctx Artifact context
139
 * @param dl_data Download data for the artifact
140
 * @param mdata_cache Cache to populate
141
 * @return MENDER_OK in case of success, error otherwise
142
 */
143
static mender_err_t artifact_read_data_prepare(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data, struct data_mdata_cache *mdata_cache);
144

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

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

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

171
/**
172
 * @brief Drop content of the current file of the artifact
173
 * @param ctx Artifact context
174
 * @return MENDER_DONE if the data have been parsed and dropped, MENDER_OK if there is not enough data to parse, error code if an error occurred
175
 */
176
static mender_err_t artifact_drop_file(mender_artifact_ctx_t *ctx);
177

178
/**
179
 * @brief Shift data after parsing and update the respective checksum context
180
 * @param checksum_key Key under which the checksum for the data is calculated/checked
181
 * @param checksum_len Length of the data to include in the checksum update
182
 * @return MENDER_OK if the function succeeds, error code otherwise
183
 * @see artifact_shift_data()
184
 */
185
static mender_err_t artifact_shift_and_checksum_data(mender_artifact_ctx_t *ctx, size_t length, const char *checksum_key, size_t checksum_len);
186

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

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

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

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

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

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

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

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

257
mender_err_t
258
mender_artifact_check_integrity(mender_artifact_ctx_t *ctx) {
2✔
259
    assert(NULL != ctx);
2✔
260

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

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

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

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

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

300
mender_artifact_ctx_t *
301
mender_artifact_create_ctx(size_t buf_size) {
13✔
302

303
    mender_artifact_ctx_t *ctx;
304

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

318
    /* Save context */
319
    artifact_ctx = ctx;
13✔
320

321
    return ctx;
13✔
322
}
323

324
mender_err_t
325
mender_artifact_get_ctx(mender_artifact_ctx_t **ctx) {
2✔
326

327
    assert(NULL != ctx);
2✔
328

329
    if (NULL == artifact_ctx) {
2✔
330
        return MENDER_FAIL;
1✔
331
    }
332

333
    *ctx = artifact_ctx;
1✔
334
    return MENDER_OK;
1✔
335
}
336

337
static bool
338
is_compressed(const char *filename) {
8✔
339

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

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

349
    return false;
7✔
350
}
351

352
static struct data_mdata_cache data_mdata_cache = { 0 };
353

354
mender_err_t
355
mender_artifact_process_data(mender_artifact_ctx_t *ctx, void *input_data, size_t input_length, mender_artifact_download_data_t *dl_data) {
11✔
356

357
    assert(NULL != ctx);
11✔
358
    mender_err_t ret = MENDER_OK;
11✔
359
    void        *tmp;
360
    size_t       new_size;
361
    size_t       expected_required;
362

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

398
    /* Parse data */
399
    do {
400

401
        /* Treatment depending of the stream state */
402
        if (MENDER_ARTIFACT_STREAM_STATE_PARSING_HEADER == ctx->stream_state) {
124✔
403

404
            /* Parse TAR header */
405
            ret = artifact_parse_tar_header(ctx);
75✔
406

407
            /* Processing a new (data) file, invalidate the cache */
408
            data_mdata_cache_invalidate(&data_mdata_cache);
75✔
409

410
        } else if (MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA == ctx->stream_state) {
49✔
411

412
            /* Treatment depending of the file name */
413
            if (StringEqual(ctx->file.name, "version")) {
49✔
414

415
                /* Validate artifact version */
416
                ret = artifact_read_version(ctx);
8✔
417

418
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
419
            } else if (StringEqual(ctx->file.name, "manifest")) {
41✔
420

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

426
                /* Read header-info file */
427
                ret = artifact_read_header_info(ctx, dl_data);
7✔
428

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

432
                /* Read meta-data file */
433
                ret = artifact_read_meta_data(ctx);
1✔
434

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

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

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

458
                /* Drop data, file is not relevant */
459
                ret = artifact_drop_file(ctx);
×
460
            } else {
461

462
                /* Nothing to do */
463
                ret = MENDER_DONE;
13✔
464
            }
465

466
            /* Check if file have been parsed and treatment done */
467
            if (MENDER_DONE == ret) {
49✔
468

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

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

485
    return ret;
10✔
486
}
487

488
void
489
mender_artifact_release_ctx(mender_artifact_ctx_t *ctx) {
13✔
490

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

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

527
static mender_err_t
528
artifact_parse_tar_header(mender_artifact_ctx_t *ctx) {
75✔
529
    assert(NULL != ctx);
75✔
530

531
    char *tmp;
532
    bool  in_header_tar;
533

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

539
    /* Cast block to TAR header structure */
540
    mender_artifact_tar_header_t *tar_header = (mender_artifact_tar_header_t *)ctx->input.data;
68✔
541

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

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

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

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

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

584
        return MENDER_DONE;
18✔
585
    }
586

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

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

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

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

623
    ctx->file.index = 0;
49✔
624

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

641
    /* Update the stream state machine */
642
    ctx->stream_state = MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA;
49✔
643

644
    return MENDER_DONE;
49✔
645
}
646

647
static mender_err_t
648
artifact_read_version(mender_artifact_ctx_t *ctx) {
8✔
649

650
    assert(NULL != ctx);
8✔
651
    cJSON       *object = NULL;
8✔
652
    mender_err_t ret    = MENDER_DONE;
8✔
653

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

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

690
    /* Shift data in the buffer */
691
    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✔
692
        mender_log_error("Unable to shift and checksum input data");
×
693
        ret = MENDER_FAIL;
×
694
        goto END;
×
695
    }
696

697
END:
8✔
698

699
    /* Release memory */
700
    cJSON_Delete(object);
8✔
701

702
    return ret;
8✔
703
}
704

705
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
706
mender_err_t
707
mender_artifact_get_device_type(mender_artifact_ctx_t *ctx, const char **device_type) {
2✔
708

709
    assert(NULL != ctx);
2✔
710
    assert(NULL != device_type);
2✔
711

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

725
static mender_err_t
726
artifact_read_manifest(mender_artifact_ctx_t *ctx) {
8✔
727

728
    assert(NULL != ctx);
8✔
729

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

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

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

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

759
        const char *checksum_str = line;
23✔
760
        const char *filename     = separator + 2;
23✔
761

762
        /* We do not support compressed artifacts */
763
        if (mender_utils_strbeginswith(filename, "header.tar") && is_compressed(filename)) {
23✔
764
            mender_log_error("Artifact compression is not supported");
1✔
765
            return MENDER_FAIL;
1✔
766
        }
767

768
        /* Useful when debugging artifact integrity check failures */
769
        mender_log_debug("%s  %s", checksum_str, filename);
770

771
        /* Make sure digest is of expected length (two hex per byte) */
772
        if ((MENDER_DIGEST_BUFFER_SIZE * 2) != strlen(checksum_str)) {
22✔
773
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
774
            return MENDER_FAIL;
×
775
        }
776

777
        /* Get checksum entry for the file (creates one if not found) */
778
        mender_artifact_checksum_t *checksum;
779
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, filename))) {
22✔
780
            /* Error already logged */
781
            return MENDER_FAIL;
×
782
        }
783

784
        /* Populate with manifest checksum */
785
        if (!mender_utils_hexdump_to_bytes(checksum_str, checksum->manifest, MENDER_DIGEST_BUFFER_SIZE)) {
22✔
786
            mender_log_error("Bad checksum '%s' in manifest for file '%s'", checksum_str, filename);
×
787
            return MENDER_FAIL;
×
788
        }
789

790
        ///* Move to the next line */
791
        line = next + 1;
22✔
792
    }
793

794
    /* Shift data in the buffer */
795
    if (MENDER_OK != artifact_shift_data(ctx, artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE))) {
7✔
796
        mender_log_error("Unable to shift input data");
×
797
        return MENDER_FAIL;
×
798
    }
799

800
    return MENDER_DONE;
7✔
801
}
802

803
static mender_err_t
804
artifact_parse_provides_depends(cJSON *json_provides_depends, mender_key_value_list_t **provides_depends) {
18✔
805

806
    assert(NULL != json_provides_depends);
18✔
807
    assert(NULL != provides_depends);
18✔
808

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

832
    return MENDER_OK;
18✔
833

834
ERROR:
×
835
    /* Free linked list in case of error */
836
    mender_utils_key_value_list_free(*provides_depends);
×
837
    return MENDER_FAIL;
×
838
}
839
#endif
840

841
static mender_err_t
842
artifact_read_header_info(mender_artifact_ctx_t *ctx, mender_artifact_download_data_t *dl_data) {
7✔
843

844
    assert(NULL != ctx);
7✔
845
    cJSON       *object = NULL;
7✔
846
    mender_err_t ret    = MENDER_DONE;
7✔
847
    size_t       rounded_file_size;
848

849
    /* Check if all data have been received */
850
    if ((NULL == ctx->input.data) || (ctx->input.length < (rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)))) {
7✔
851
        return MENDER_OK;
×
852
    }
853

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

906
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
907
        cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
6✔
908
        if (cJSON_IsObject(json_provides)) {
6✔
909
            if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->artifact_info.provides))) {
6✔
910
                mender_log_error("Unable to parse artifact_provides");
×
911
                ret = MENDER_FAIL;
×
912
                goto END;
×
913
            }
914
        }
915

916
        cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
6✔
917
        if (cJSON_IsObject(json_depends)) {
6✔
918
            if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->artifact_info.depends))) {
6✔
919
                mender_log_error("Unable to parse artifact_depends");
×
920
                ret = MENDER_FAIL;
×
921
                goto END;
×
922
            }
923
        }
924
#endif
925

926
    } else {
927
        mender_log_error("Invalid header-info file");
×
928
        ret = MENDER_FAIL;
×
929
        goto END;
×
930
    }
931

932
    /* Shift data in the buffer */
933
    /* header.tar has a checksum entry in the manifest as a whole */
934
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
6✔
935
        mender_log_error("Unable to shift and checksum input data");
×
936
        ret = MENDER_FAIL;
×
937
        goto END;
×
938
    }
939

940
END:
6✔
941

942
    /* Release memory */
943
    cJSON_Delete(object);
7✔
944

945
    return ret;
7✔
946
}
947

948
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
949
static mender_err_t
950
artifact_read_type_info(mender_artifact_ctx_t *ctx) {
6✔
951

952
    assert(NULL != ctx);
6✔
953
    cJSON       *object = NULL;
6✔
954
    mender_err_t ret    = MENDER_DONE;
6✔
955
    size_t       index  = 0;
6✔
956
    size_t       rounded_file_size;
957

958
    /* Check if all data have been received */
959
    if ((NULL == ctx->input.data) || (ctx->input.length < (rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)))) {
6✔
960
        return MENDER_OK;
×
961
    }
962

963
    /* Read type-info */
964
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
6✔
965
        mender_log_error("Unable to allocate memory");
×
966
        return MENDER_FAIL;
×
967
    }
968

969
    /* Check if payload index is valid */
970
    if (NULL == ctx->payloads.values[index].type) {
6✔
971
        mender_log_error("Invalid artifact format; no payload found for index %d", index);
×
972
        ret = MENDER_FAIL;
×
973
        goto END;
×
974
    }
975
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
976
    cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
6✔
977
    if (cJSON_IsObject(json_provides)) {
6✔
978
        if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->payloads.values[index].provides))) {
6✔
979
            mender_log_error("Unable to parse artifact_provides");
×
980
            ret = MENDER_FAIL;
×
981
            goto END;
×
982
        }
983
    }
984

985
    cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
6✔
986
    if (cJSON_IsObject(json_depends)) {
6✔
987
        if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->payloads.values[index].depends))) {
×
988
            mender_log_error("Unable to parse artifact_depends");
×
989
            ret = MENDER_FAIL;
×
990
            goto END;
×
991
        }
992
    }
993

994
    cJSON *json_clears_provides = cJSON_GetObjectItemCaseSensitive(object, "clears_artifact_provides");
6✔
995
    if (cJSON_IsArray(json_clears_provides)) {
6✔
996
        ctx->payloads.values[index].clears_provides_size = cJSON_GetArraySize(json_clears_provides);
6✔
997
        ctx->payloads.values[index].clears_provides      = (char **)mender_calloc(ctx->payloads.values[index].clears_provides_size, sizeof(char *));
6✔
998
        if (NULL == ctx->payloads.values[index].clears_provides) {
6✔
999
            mender_log_error("Unable to allocate memory");
×
1000
            ret = MENDER_FAIL;
×
1001
            goto END;
×
1002
        }
1003

1004
        size_t i                            = 0;
6✔
1005
        cJSON *json_clears_provides_element = NULL;
6✔
1006

1007
        cJSON_ArrayForEach(json_clears_provides_element, json_clears_provides) {
12✔
1008
            if (cJSON_IsString(json_clears_provides_element)) {
6✔
1009
                char *clears_provides = mender_utils_strdup(json_clears_provides_element->valuestring);
6✔
1010
                if (NULL == clears_provides) {
6✔
1011
                    mender_log_error("Unable to allocate memory");
×
1012
                    ret = MENDER_FAIL;
×
1013
                    goto END;
×
1014
                }
1015
                ctx->payloads.values[index].clears_provides[i] = clears_provides;
6✔
1016
                i++;
6✔
1017
            } else {
1018
                mender_log_error("Invalid header-info file");
×
1019
                ret = MENDER_FAIL;
×
1020
                goto END;
×
1021
            }
1022
        }
1023
    }
1024
#endif
1025

1026
    /* Shift data in the buffer */
1027
    /* header.tar has a checksum entry in the manifest as a whole */
1028
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
6✔
1029
        mender_log_error("Unable to shift and checksum input data");
×
1030
        ret = MENDER_FAIL;
×
1031
        goto END;
×
1032
    }
1033

1034
END:
6✔
1035

1036
    /* Release memory */
1037
    cJSON_Delete(object);
6✔
1038

1039
    return ret;
6✔
1040
}
1041
#endif
1042

1043
static mender_err_t
1044
artifact_read_meta_data(mender_artifact_ctx_t *ctx) {
1✔
1045
    assert(NULL != ctx);
1✔
1046
    size_t rounded_file_size;
1047

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

1057
    assert(sizeof(size_t) >= sizeof(unsigned long));
1058
    const char *start_ptr = ctx->file.name + strlen(prefix);
1✔
1059
    char       *end_ptr;
1060
    errno = 0; /* to distinguish between success/failure */
1✔
1061

1062
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
1✔
1063
    if ((end_ptr == start_ptr)              /* no conversion */
1✔
1064
        || (0 != errno)                     /* out of range (for unsigned long) */
1✔
1065
        || (index >= ctx->payloads.size)) { /* index out of bounds */
1✔
1066
        mender_log_error("Invalid artifact format");
×
1067
        return MENDER_FAIL;
×
1068
    }
1069

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

1073
    /* Check size of the meta-data */
1074
    if (0 == ctx->file.size) {
1✔
1075
        /* Nothing to do */
1076
        return MENDER_DONE;
×
1077
    }
1078

1079
    /* Check if all data have been received */
1080
    rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE);
1✔
1081
    if ((NULL == ctx->input.data) || (ctx->input.length < rounded_file_size)) {
1✔
1082
        return MENDER_OK;
×
1083
    }
1084

1085
    /* Read meta-data */
1086
    if (NULL == (ctx->payloads.values[index].meta_data = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
1✔
1087
        mender_log_error("Unable to allocate memory");
×
1088
        return MENDER_FAIL;
×
1089
    }
1090

1091
    /* Shift data in the buffer */
1092
    /* header.tar has a checksum entry in the manifest as a whole */
1093
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
1✔
1094
        mender_log_error("Unable to shift and checksum input data");
×
1095
        return MENDER_FAIL;
×
1096
    }
1097

1098
    return MENDER_DONE;
1✔
1099
}
1100

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

1127
    assert(NULL != type);
6✔
1128
    mender_err_t ret = MENDER_FAIL;
6✔
1129

1130
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_INF
1131
    if (size > 0) {
6✔
1132
        static size_t download_progress = 0;
1133
        /* New update */
1134
        if (0 == index) {
6✔
1135
            download_progress = 0;
6✔
1136
        }
1137

1138
        /* Update every 10% */
1139
        if (((index * 10) / size) > download_progress) {
6✔
1140
            download_progress = (index * 10) / size;
×
1141
            mender_log_info("Downloading '%s' %zu0%%... [%zu/%zu]", type, download_progress, index, size);
×
1142
        }
1143
    }
1144
#endif
1145

1146
    /* Invoke update module download callback */
1147
    struct mender_update_download_state_data_s download_state_data
6✔
1148
        = { deployment_id, artifact_name, type, meta_data, filename, size, data, index, length, false };
1149
    mender_update_state_data_t state_data = { .download_state_data = &download_state_data };
6✔
1150
    if (MENDER_OK != (ret = dl_data->update_module->callbacks[MENDER_UPDATE_STATE_DOWNLOAD](MENDER_UPDATE_STATE_DOWNLOAD, state_data))) {
6✔
1151
        mender_log_error("An error occurred while processing data of the artifact '%s' of type '%s'", artifact_name, type);
×
1152
        return ret;
×
1153
    }
1154

1155
    return MENDER_OK;
6✔
1156
}
1157

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

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

1185
    assert(sizeof(size_t) >= sizeof(unsigned long));
1186
    const char *start_ptr = ctx->file.name + strlen(prefix);
6✔
1187
    char       *end_ptr;
1188
    errno = 0; /* to distinguish between success/failure */
6✔
1189

1190
    const size_t index = strtoul(start_ptr, &end_ptr, 10);
6✔
1191
    if ((end_ptr == start_ptr)              /* no conversion */
6✔
1192
        || (0 != errno)                     /* out of range (for unsigned long) */
6✔
1193
        || (index >= ctx->payloads.size)) { /* index out of bounds */
6✔
1194
        mender_log_error("Invalid artifact format");
×
1195
        return MENDER_FAIL;
×
1196
    }
1197

1198
    assert(NULL != end_ptr);
6✔
1199
    assert(StringEqualN(end_ptr, ".tar", 4)); /* just one last sanity check */
6✔
1200

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

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

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

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

1233
    mdata_cache->valid = true;
6✔
1234
    return MENDER_OK;
6✔
1235
}
1236

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

1240
    assert(NULL != ctx);
6✔
1241
    mender_err_t ret;
1242

1243
    /* Check size of the data */
1244
    if (0 == artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
6✔
1245
        /* Nothing to do */
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)) {
6✔
1254
            return MENDER_OK;
×
1255
        }
1256

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

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

1277
        /* Update index */
1278
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
6✔
1279

1280
        /* Shift data in the buffer */
1281
        if (MENDER_OK != (ret = artifact_shift_and_checksum_data(ctx, MENDER_ARTIFACT_STREAM_BLOCK_SIZE, mdata_cache->checksum_fname, length))) {
6✔
1282
            mender_log_error("Unable to shift and checksum input data");
×
1283
            return ret;
×
1284
        }
1285

1286
    } while (ctx->file.index < ctx->file.size);
6✔
1287

1288
    return MENDER_DONE;
6✔
1289
}
1290

1291
static mender_err_t
1292
artifact_drop_file(mender_artifact_ctx_t *ctx) {
×
1293

1294
    assert(NULL != ctx);
×
1295
    mender_err_t ret;
1296

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

1303
    /* Parse data until the end of the file has been reached */
1304
    do {
1305

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

1311
        /* Update index */
1312
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1313

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

1320
    } while (ctx->file.index < ctx->file.size);
×
1321

1322
    return MENDER_DONE;
×
1323
}
1324

1325
static mender_err_t
1326
artifact_shift_and_checksum_data(mender_artifact_ctx_t *ctx, size_t length, const char *checksum_key, size_t checksum_len) {
47✔
1327
    assert(NULL != ctx);
47✔
1328
    assert(ctx->input.length >= length);
47✔
1329
    assert(checksum_len <= length);
47✔
1330

1331
    if (0 == length) {
47✔
1332
        return MENDER_OK;
×
1333
    }
1334

1335
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
1336
    if ((NULL != checksum_key) && (0 != checksum_len)) {
47✔
1337
        mender_artifact_checksum_t *checksum;
1338
        /* Get checksum entry (create one if needed) */
1339
        if (NULL == (checksum = artifact_checksum_get_or_create(ctx, checksum_key))) {
47✔
1340
            /* Error already logged */
1341
            return MENDER_FAIL;
×
1342
        }
1343

1344
        if (MENDER_OK != mender_sha256_update(checksum->context, ctx->input.data, checksum_len)) {
47✔
1345
            mender_log_error("Failed to update update checksum");
×
1346
            return MENDER_FAIL;
×
1347
        }
1348
    }
1349
#else
1350
    /* Only to make the arguments "used" in this case. */
1351
    (void)checksum_key;
1352
    (void)checksum_len;
1353
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
1354

1355
    return artifact_shift_data(ctx, length);
47✔
1356
}
1357

1358
static mender_err_t
1359
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
101✔
1360

1361
    assert(NULL != ctx);
101✔
1362

1363
    /* Shift data */
1364
    if (length > 0) {
101✔
1365
        if (ctx->input.length > length) {
101✔
1366
            memmove(ctx->input.data, (void *)(((uint8_t *)ctx->input.data) + length), ctx->input.length - length);
95✔
1367
            ctx->input.length -= length;
95✔
1368
            /* Here we could shrink the ctx->input.data buffer, but most likely, we would need to
1369
               grow it again when we receive another batch of data so there's little point in doing
1370
               so. */
1371
        } else {
1372
            ctx->input.length = 0;
6✔
1373
        }
1374
    }
1375

1376
    return MENDER_OK;
101✔
1377
}
1378

1379
static size_t
1380
artifact_round_up(size_t length, size_t incr) {
61✔
1381
    return length + (incr - length % incr) % incr;
61✔
1382
}
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