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

mendersoftware / mender-mcu / 1617544972

09 Jan 2025 08:51AM UTC coverage: 25.63% (+0.2%) from 25.46%
1617544972

push

gitlab-ci

danielskinstad
docs: Get started README

Ticket: MEN-7871

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

712 of 2778 relevant lines covered (25.63%)

7.4 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-artifact.h"
24
#include "mender-deployment-data.h"
25
#include "mender-log.h"
26

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

300
    mender_artifact_ctx_t *ctx;
301

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

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

318
    return ctx;
13✔
319
}
320

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

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

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

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

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

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

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

346
    return false;
7✔
347
}
348

349
static struct data_mdata_cache data_mdata_cache = { 0 };
350

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

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

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

395
    /* Parse data */
396
    do {
397

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

482
    return ret;
10✔
483
}
484

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

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

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

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

528
    char *tmp;
529
    bool  in_header_tar;
530

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

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

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

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

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

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

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

581
        return MENDER_DONE;
19✔
582
    }
583

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

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

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

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

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

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

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

641
    return MENDER_DONE;
52✔
642
}
643

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

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

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

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

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

694
END:
8✔
695

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

699
    return ret;
8✔
700
}
701

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

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

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

722
static mender_err_t
723
artifact_read_manifest(mender_artifact_ctx_t *ctx) {
8✔
724

725
    assert(NULL != ctx);
8✔
726

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

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

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

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

756
        const char *checksum_str = line;
23✔
757
        const char *filename     = separator + 2;
23✔
758

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

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

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

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

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

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

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

797
    return MENDER_DONE;
7✔
798
}
799

800
static mender_err_t
801
artifact_parse_provides_depends(cJSON *json_provides_depends, mender_key_value_list_t **provides_depends) {
21✔
802

803
    assert(NULL != json_provides_depends);
21✔
804
    assert(NULL != provides_depends);
21✔
805

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

829
    return MENDER_OK;
21✔
830

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

838
static mender_err_t
839
artifact_read_header_info(mender_artifact_ctx_t *ctx) {
7✔
840

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

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

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

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

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

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

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

922
END:
7✔
923

924
    /* Release memory */
925
    cJSON_Delete(object);
7✔
926

927
    return ret;
7✔
928
}
929

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

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

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

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

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

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

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

986
        size_t i                            = 0;
7✔
987
        cJSON *json_clears_provides_element = NULL;
7✔
988

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

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

1016
END:
7✔
1017

1018
    /* Release memory */
1019
    cJSON_Delete(object);
7✔
1020

1021
    return ret;
7✔
1022
}
1023
#endif
1024

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

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

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

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

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

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

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

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

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

1080
    return MENDER_DONE;
1✔
1081
}
1082

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

1109
    assert(NULL != type);
6✔
1110
    mender_err_t ret = MENDER_FAIL;
6✔
1111

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

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

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

1137
    return MENDER_OK;
6✔
1138
}
1139

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

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

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

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

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

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

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

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

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

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

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

1229
    mdata_cache->valid = true;
6✔
1230
    return MENDER_OK;
6✔
1231
}
1232

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

1236
    assert(NULL != ctx);
6✔
1237
    mender_err_t ret;
1238

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

1245
    /* Parse data until the end of the file has been reached */
1246
    do {
1247

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

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

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

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

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

1282
    } while (ctx->file.index < ctx->file.size);
6✔
1283

1284
    return MENDER_DONE;
6✔
1285
}
1286

1287
static mender_err_t
1288
artifact_drop_file(mender_artifact_ctx_t *ctx) {
×
1289

1290
    assert(NULL != ctx);
×
1291
    mender_err_t ret;
1292

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

1299
    /* Parse data until the end of the file has been reached */
1300
    do {
1301

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

1307
        /* Update index */
1308
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1309

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

1316
    } while (ctx->file.index < ctx->file.size);
×
1317

1318
    return MENDER_DONE;
×
1319
}
1320

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

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

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

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

1351
    return artifact_shift_data(ctx, length);
51✔
1352
}
1353

1354
static mender_err_t
1355
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
107✔
1356

1357
    assert(NULL != ctx);
107✔
1358

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

1372
    return MENDER_OK;
107✔
1373
}
1374

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