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

mendersoftware / mender-mcu / 1642941481

27 Jan 2025 04:43PM UTC coverage: 25.951% (+0.3%) from 25.63%
1642941481

push

gitlab-ci

vpodzime
feat: Support platform-specific or custom allocators

And use Zephyr-specific allocation functions on Zephyr by
default, using either a separate heap (default) or the system
heap.

This means we have to use our memory management functions instead
of the standard ones everywhere, even if they can actually just
call the standard ones.

Ticket: MEN-7808
Changelog: none
Signed-off-by: Vratislav Podzimek <vratislav.podzimek@northern.tech>

70 of 185 new or added lines in 12 files covered. (37.84%)

2 existing lines in 2 files now uncovered.

737 of 2840 relevant lines covered (25.95%)

8.85 hits per line

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

68.07
/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
 * @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
92
 */
93
static mender_err_t artifact_read_header_info(mender_artifact_ctx_t *ctx);
94

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

252
    return checksum;
73✔
253
}
254
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
255

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

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

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

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

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

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

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

302
    mender_artifact_ctx_t *ctx;
303

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

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

320
    return ctx;
13✔
321
}
322

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

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

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

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

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

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

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

348
    return false;
7✔
349
}
350

351
static struct data_mdata_cache data_mdata_cache = { 0 };
352

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

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

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

397
    /* Parse data */
398
    do {
399

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

403
            /* Parse TAR header */
404
            ret = artifact_parse_tar_header(ctx);
79✔
405

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

409
        } else if (MENDER_ARTIFACT_STREAM_STATE_PARSING_DATA == ctx->stream_state) {
52✔
410

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

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

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

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

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

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

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

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

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

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

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

461
                /* Nothing to do */
462
                ret = MENDER_DONE;
14✔
463
            }
464

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

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

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

484
    return ret;
10✔
485
}
486

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

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

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

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

530
    char *tmp;
531
    bool  in_header_tar;
532

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

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

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

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

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

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

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

583
        return MENDER_DONE;
19✔
584
    }
585

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

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

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

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

622
    ctx->file.index = 0;
52✔
623

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

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

643
    return MENDER_DONE;
52✔
644
}
645

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

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

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

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

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

696
END:
8✔
697

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

701
    return ret;
8✔
702
}
703

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

799
    return MENDER_DONE;
7✔
800
}
801

802
static mender_err_t
803
artifact_parse_provides_depends(cJSON *json_provides_depends, mender_key_value_list_t **provides_depends) {
21✔
804

805
    assert(NULL != json_provides_depends);
21✔
806
    assert(NULL != provides_depends);
21✔
807

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

831
    return MENDER_OK;
21✔
832

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

840
static mender_err_t
841
artifact_read_header_info(mender_artifact_ctx_t *ctx) {
7✔
842

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

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

853
    /* Read header-info */
854
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
7✔
855
        mender_log_error("Unable to allocate memory");
×
856
        return MENDER_FAIL;
×
857
    }
858
    cJSON *json_payloads = cJSON_GetObjectItemCaseSensitive(object, "payloads");
7✔
859
    if (true == cJSON_IsArray(json_payloads)) {
7✔
860
        ctx->payloads.size = cJSON_GetArraySize(json_payloads);
7✔
861
        if (NULL == (ctx->payloads.values = (mender_artifact_payload_t *)mender_calloc(ctx->payloads.size, sizeof(mender_artifact_payload_t)))) {
7✔
862
            mender_log_error("Unable to allocate memory");
×
863
            ret = MENDER_FAIL;
×
864
            goto END;
×
865
        }
866
        size_t index        = 0;
7✔
867
        cJSON *json_payload = NULL;
7✔
868
        cJSON_ArrayForEach(json_payload, json_payloads) {
14✔
869
            if (true == cJSON_IsObject(json_payload)) {
7✔
870
                cJSON *json_payload_type = cJSON_GetObjectItemCaseSensitive(json_payload, "type");
7✔
871
                if (cJSON_IsString(json_payload_type)) {
7✔
872
                    if (NULL == (ctx->payloads.values[index].type = mender_utils_strdup(cJSON_GetStringValue(json_payload_type)))) {
7✔
873
                        mender_log_error("Unable to allocate memory");
×
874
                        ret = MENDER_FAIL;
×
875
                        goto END;
×
876
                    }
877
                } else {
878
                    mender_log_error("Invalid header-info file");
×
879
                    ret = MENDER_FAIL;
×
880
                    goto END;
×
881
                }
882
            } else {
883
                mender_log_error("Invalid header-info file");
×
884
                ret = MENDER_FAIL;
×
885
                goto END;
×
886
            }
887
            index++;
7✔
888
        }
889

890
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
891
        cJSON *json_provides = cJSON_GetObjectItemCaseSensitive(object, "artifact_provides");
7✔
892
        if (cJSON_IsObject(json_provides)) {
7✔
893
            if (MENDER_FAIL == artifact_parse_provides_depends(json_provides, &(ctx->artifact_info.provides))) {
7✔
894
                mender_log_error("Unable to parse artifact_provides");
×
895
                ret = MENDER_FAIL;
×
896
                goto END;
×
897
            }
898
        }
899

900
        cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
7✔
901
        if (cJSON_IsObject(json_depends)) {
7✔
902
            if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->artifact_info.depends))) {
7✔
903
                mender_log_error("Unable to parse artifact_depends");
×
904
                ret = MENDER_FAIL;
×
905
                goto END;
×
906
            }
907
        }
908
#endif
909

910
    } else {
911
        mender_log_error("Invalid header-info file");
×
912
        ret = MENDER_FAIL;
×
913
        goto END;
×
914
    }
915

916
    /* Shift data in the buffer */
917
    /* header.tar has a checksum entry in the manifest as a whole */
918
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
7✔
919
        mender_log_error("Unable to shift and checksum input data");
×
920
        ret = MENDER_FAIL;
×
921
        goto END;
×
922
    }
923

924
END:
7✔
925

926
    /* Release memory */
927
    cJSON_Delete(object);
7✔
928

929
    return ret;
7✔
930
}
931

932
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
933
static mender_err_t
934
artifact_read_type_info(mender_artifact_ctx_t *ctx) {
7✔
935

936
    assert(NULL != ctx);
7✔
937
    cJSON       *object = NULL;
7✔
938
    mender_err_t ret    = MENDER_DONE;
7✔
939
    size_t       index  = 0;
7✔
940
    size_t       rounded_file_size;
941

942
    /* Check if all data have been received */
943
    if ((NULL == ctx->input.data) || (ctx->input.length < (rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE)))) {
7✔
944
        return MENDER_OK;
×
945
    }
946

947
    /* Read type-info */
948
    if (NULL == (object = cJSON_ParseWithLength(ctx->input.data, ctx->file.size))) {
7✔
949
        mender_log_error("Unable to allocate memory");
×
950
        return MENDER_FAIL;
×
951
    }
952

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

969
    cJSON *json_depends = cJSON_GetObjectItemCaseSensitive(object, "artifact_depends");
7✔
970
    if (cJSON_IsObject(json_depends)) {
7✔
971
        if (MENDER_FAIL == artifact_parse_provides_depends(json_depends, &(ctx->payloads.values[index].depends))) {
×
972
            mender_log_error("Unable to parse artifact_depends");
×
973
            ret = MENDER_FAIL;
×
974
            goto END;
×
975
        }
976
    }
977

978
    cJSON *json_clears_provides = cJSON_GetObjectItemCaseSensitive(object, "clears_artifact_provides");
7✔
979
    if (cJSON_IsArray(json_clears_provides)) {
7✔
980
        ctx->payloads.values[index].clears_provides_size = cJSON_GetArraySize(json_clears_provides);
7✔
981
        ctx->payloads.values[index].clears_provides      = (char **)mender_calloc(ctx->payloads.values[index].clears_provides_size, sizeof(char *));
7✔
982
        if (NULL == ctx->payloads.values[index].clears_provides) {
7✔
983
            mender_log_error("Unable to allocate memory");
×
984
            ret = MENDER_FAIL;
×
985
            goto END;
×
986
        }
987

988
        size_t i                            = 0;
7✔
989
        cJSON *json_clears_provides_element = NULL;
7✔
990

991
        cJSON_ArrayForEach(json_clears_provides_element, json_clears_provides) {
14✔
992
            if (cJSON_IsString(json_clears_provides_element)) {
7✔
993
                char *clears_provides = mender_utils_strdup(json_clears_provides_element->valuestring);
7✔
994
                if (NULL == clears_provides) {
7✔
995
                    mender_log_error("Unable to allocate memory");
×
996
                    ret = MENDER_FAIL;
×
997
                    goto END;
×
998
                }
999
                ctx->payloads.values[index].clears_provides[i] = clears_provides;
7✔
1000
                i++;
7✔
1001
            } else {
1002
                mender_log_error("Invalid header-info file");
×
1003
                ret = MENDER_FAIL;
×
1004
                goto END;
×
1005
            }
1006
        }
1007
    }
1008
#endif
1009

1010
    /* Shift data in the buffer */
1011
    /* header.tar has a checksum entry in the manifest as a whole */
1012
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
7✔
1013
        mender_log_error("Unable to shift and checksum input data");
×
1014
        ret = MENDER_FAIL;
×
1015
        goto END;
×
1016
    }
1017

1018
END:
7✔
1019

1020
    /* Release memory */
1021
    cJSON_Delete(object);
7✔
1022

1023
    return ret;
7✔
1024
}
1025
#endif
1026

1027
static mender_err_t
1028
artifact_read_meta_data(mender_artifact_ctx_t *ctx) {
1✔
1029
    assert(NULL != ctx);
1✔
1030
    size_t rounded_file_size;
1031

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

1041
    assert(sizeof(size_t) >= sizeof(unsigned long));
1042
    const char *start_ptr = ctx->file.name + strlen(prefix);
1✔
1043
    char       *end_ptr;
1044
    errno = 0; /* to distinguish between success/failure */
1✔
1045

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

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

1057
    /* Check size of the meta-data */
1058
    if (0 == ctx->file.size) {
1✔
1059
        /* Nothing to do */
1060
        return MENDER_DONE;
×
1061
    }
1062

1063
    /* Check if all data have been received */
1064
    rounded_file_size = artifact_round_up(ctx->file.size, MENDER_ARTIFACT_STREAM_BLOCK_SIZE);
1✔
1065
    if ((NULL == ctx->input.data) || (ctx->input.length < rounded_file_size)) {
1✔
1066
        return MENDER_OK;
×
1067
    }
1068

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

1075
    /* Shift data in the buffer */
1076
    /* header.tar has a checksum entry in the manifest as a whole */
1077
    if (MENDER_OK != artifact_shift_and_checksum_data(ctx, rounded_file_size, "header.tar", rounded_file_size)) {
1✔
1078
        mender_log_error("Unable to shift and checksum input data");
×
1079
        return MENDER_FAIL;
×
1080
    }
1081

1082
    return MENDER_DONE;
1✔
1083
}
1084

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

1111
    assert(NULL != type);
6✔
1112
    mender_err_t ret = MENDER_FAIL;
6✔
1113

1114
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_INF
1115
    if (size > 0) {
6✔
1116
        static size_t download_progress = 0;
1117
        /* New update */
1118
        if (0 == index) {
6✔
1119
            download_progress = 0;
6✔
1120
        }
1121

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

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

1139
    return MENDER_OK;
6✔
1140
}
1141

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

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

1169
    assert(sizeof(size_t) >= sizeof(unsigned long));
1170
    const char *start_ptr = ctx->file.name + strlen(prefix);
7✔
1171
    char       *end_ptr;
1172
    errno = 0; /* to distinguish between success/failure */
7✔
1173

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

1182
    assert(NULL != end_ptr);
7✔
1183
    assert(StringEqualN(end_ptr, ".tar", 4)); /* just one last sanity check */
7✔
1184

1185
    const char *payload_type = ctx->payloads.values[index].type;
7✔
1186

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

1195
    /* Add the payload type to deployment data  */
1196
    if (MENDER_OK != mender_deployment_data_add_payload_type(dl_data->deployment, payload_type)) {
7✔
1197
        /* Error already logged */
1198
        return MENDER_FAIL;
1✔
1199
    }
1200

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

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

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

1231
    mdata_cache->valid = true;
6✔
1232
    return MENDER_OK;
6✔
1233
}
1234

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

1238
    assert(NULL != ctx);
6✔
1239
    mender_err_t ret;
1240

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

1247
    /* Parse data until the end of the file has been reached */
1248
    do {
1249

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

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

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

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

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

1284
    } while (ctx->file.index < ctx->file.size);
6✔
1285

1286
    return MENDER_DONE;
6✔
1287
}
1288

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

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

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

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

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

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

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

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

1320
    return MENDER_DONE;
×
1321
}
1322

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

1329
    if (0 == length) {
51✔
1330
        return MENDER_OK;
×
1331
    }
1332

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

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

1353
    return artifact_shift_data(ctx, length);
51✔
1354
}
1355

1356
static mender_err_t
1357
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
107✔
1358

1359
    assert(NULL != ctx);
107✔
1360

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

1374
    return MENDER_OK;
107✔
1375
}
1376

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