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

mendersoftware / mender-mcu / 1536144243

11 Nov 2024 09:15AM UTC coverage: 9.433% (-0.03%) from 9.461%
1536144243

push

gitlab-ci

danielskinstad
fix: allow compressed files inside a payload

* We don't care if the files _inside_ a payload are compressed, we only
  want to check for compressed artifacts, i.e. that the payload is not
  compressed.

* Moved the check to `artifact_read_manifest` and `artifact_read_data_prepare` to
  avoid having to call it for every file.

* Check the return value of `artifact_download_callback`

Changelog: Title
Ticket: MEN-7594

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

0 of 7 new or added lines in 1 file covered. (0.0%)

290 existing lines in 1 file now uncovered.

251 of 2661 relevant lines covered (9.43%)

0.68 hits per line

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

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

21
#include <errno.h>
22

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

175
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
176
static mender_err_t artifact_read_header(mender_artifact_ctx_t *ctx);
177
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
178

179
/**
180
 * @brief Shift data after parsing
181
 * @param ctx Artifact context
182
 * @param length Length of data to shift
183
 * @return MENDER_OK if the function succeeds, error code otherwise
184
 */
185
static mender_err_t artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length);
186

187
/**
188
 * @brief Compute length rounded up to increment (usually the block size)
189
 * @param length Length
190
 * @param incr Increment
191
 * @return Rounded length
192
 */
193
static size_t artifact_round_up(size_t length, size_t incr);
194

195
/**
196
 * @brief Artifact context
197
 */
198
static mender_artifact_ctx_t *artifact_ctx = NULL;
199

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

214
    /* See if we already have an entry for this file */
215
    mender_artifact_checksum_t *checksum;
216
    for (checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = checksum->next) {
×
217
        if (StringEqual(checksum->filename, filename)) {
×
UNCOV
218
            break;
×
219
        }
220
    }
221

UNCOV
222
    if (NULL == checksum) {
×
223
        /* Create new if entry not found */
224
        checksum = (mender_artifact_checksum_t *)calloc(1, sizeof(mender_artifact_checksum_t));
×
225
        if (NULL == checksum) {
×
226
            mender_log_error("Unable to allocate memory");
×
227
            return NULL;
×
228
        }
229
        checksum->filename = strdup(filename);
×
UNCOV
230
        if (NULL == checksum->filename) {
×
231
            mender_log_error("Unable to allocate memory");
×
232
            free(checksum);
×
UNCOV
233
            return NULL;
×
234
        }
UNCOV
235
        checksum->next               = ctx->artifact_info.checksums;
×
UNCOV
236
        ctx->artifact_info.checksums = checksum;
×
237

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

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

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

253
#ifdef CONFIG_MENDER_FULL_PARSE_ARTIFACT
UNCOV
254
    for (mender_artifact_checksum_t *checksum = ctx->artifact_info.checksums; NULL != checksum; checksum = checksum->next) {
×
255
        unsigned char computed[MENDER_DIGEST_BUFFER_SIZE];
256
        mender_log_debug("Checking integrity for artifact file '%s'", checksum->filename);
257

UNCOV
258
        if (MENDER_OK != mender_sha256_finish(checksum->context, computed)) {
×
259
            mender_log_error("Failed to finish checksum for file '%s'", checksum->filename);
×
UNCOV
260
            checksum->context = NULL;
×
UNCOV
261
            return MENDER_FAIL;
×
262
        }
UNCOV
263
        checksum->context = NULL;
×
264

265
        if (0 != memcmp(checksum->manifest, computed, MENDER_DIGEST_BUFFER_SIZE)) {
×
266
            mender_log_error("Computed checksum for file '%s' does not match manifest", checksum->filename);
×
267
#if CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_DBG
268
            /* Log the mismatching checksums for debugging */
269
            char checksum_str[(MENDER_DIGEST_BUFFER_SIZE * 2) + 1];
270

271
            for (int i = 0; i < MENDER_DIGEST_BUFFER_SIZE; i++) {
272
                if (2 != snprintf(checksum_str + (i * 2), 3, "%02hhx", checksum->manifest[i])) {
273
                    break;
274
                }
275
            }
276
            mender_log_debug("%s: '%s' (manifest)", checksum->filename, checksum_str);
277

278
            for (int i = 0; i < MENDER_DIGEST_BUFFER_SIZE; i++) {
279
                if (2 != snprintf(checksum_str + (i * 2), 3, "%02hhx", computed[i])) {
280
                    break;
281
                }
282
            }
283
            mender_log_debug("%s: '%s' (computed)", checksum->filename, checksum_str);
284
#endif /* CONFIG_MENDER_LOG_LEVEL >= MENDER_LOG_LEVEL_DBG */
285
            return MENDER_FAIL;
×
286
        }
287
    }
288
#endif /* CONFIG_MENDER_FULL_PARSE_ARTIFACT */
UNCOV
289
    return MENDER_OK;
×
290
}
291

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

295
    mender_artifact_ctx_t *ctx;
296

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

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

313
    return ctx;
×
314
}
315

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

319
    assert(NULL != ctx);
×
320

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

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

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

332
    static const char *compression_suffixes[] = { ".gz", ".xz", ".zst", NULL };
333

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

340
    return false;
×
341
}
342

343
static struct data_mdata_cache data_mdata_cache = { 0 };
344

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

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

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

386
    /* Parse data */
387
    do {
388

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
477
    return ret;
×
478
}
479

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

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

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

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

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

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

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

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

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

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

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

UNCOV
559
        return MENDER_DONE;
×
560
    }
561

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

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

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

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

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

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

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

609
    return MENDER_DONE;
×
610
}
611

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

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

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

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

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

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

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

677
END:
×
678

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

682
    return ret;
×
683
}
684

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

780
    return MENDER_DONE;
×
781
}
782

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

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

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

UNCOV
812
    return MENDER_OK;
×
813

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

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

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

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

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

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

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

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

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

UNCOV
903
END:
×
904

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

UNCOV
908
    return ret;
×
909
}
910

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1022
END:
×
1023

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1084
    return MENDER_DONE;
×
1085
}
1086

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

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

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

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

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

1141
    return MENDER_OK;
×
1142
}
1143

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

NEW
1155
    size_t file_name_length = strlen(ctx->file.name);
×
NEW
1156
    if ((strlen("data/xxxx.tar.xx") == file_name_length) || (strlen("data/xxxx.tar.xxx") == file_name_length)) {
×
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
        **/
NEW
1163
        if (is_compressed(ctx->file.name)) {
×
NEW
1164
            return MENDER_FAIL;
×
1165
        }
1166
    }
1167

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

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

UNCOV
1181
    assert(NULL != end_ptr);
×
1182
    assert(StringEqualN(end_ptr, ".tar", 4)); /* just one last sanity check */
×
1183

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

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

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

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

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

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

UNCOV
1229
    mdata_cache->valid = true;
×
UNCOV
1230
    return MENDER_OK;
×
1231
}
1232

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

UNCOV
1236
    assert(NULL != ctx);
×
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)) {
×
1241
        /* Nothing to do */
UNCOV
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) */
UNCOV
1249
        if ((NULL == ctx->input.data) || (ctx->input.length < MENDER_ARTIFACT_STREAM_BLOCK_SIZE)) {
×
UNCOV
1250
            return MENDER_OK;
×
1251
        }
1252

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

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

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

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

1288
        /* Update index */
1289
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1290

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

UNCOV
1297
    } while (ctx->file.index < ctx->file.size);
×
1298

UNCOV
1299
    return MENDER_DONE;
×
1300
}
1301

1302
static mender_err_t
UNCOV
1303
artifact_drop_file(mender_artifact_ctx_t *ctx) {
×
1304

1305
    assert(NULL != ctx);
×
1306
    mender_err_t ret;
1307

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

1314
    /* Parse data until the end of the file has been reached */
1315
    do {
1316

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

1322
        /* Update index */
UNCOV
1323
        ctx->file.index += MENDER_ARTIFACT_STREAM_BLOCK_SIZE;
×
1324

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

UNCOV
1331
    } while (ctx->file.index < ctx->file.size);
×
1332

UNCOV
1333
    return MENDER_DONE;
×
1334
}
1335

1336
static mender_err_t
UNCOV
1337
artifact_shift_data(mender_artifact_ctx_t *ctx, size_t length) {
×
1338

UNCOV
1339
    assert(NULL != ctx);
×
1340

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

UNCOV
1354
    return MENDER_OK;
×
1355
}
1356

1357
static size_t
UNCOV
1358
artifact_round_up(size_t length, size_t incr) {
×
UNCOV
1359
    return length + (incr - length % incr) % incr;
×
1360
}
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