Coverage Report

Created: 2025-07-18 06:26

/src/fwupd/libfwupdplugin/fu-input-stream.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2023 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuInputStream"
8
9
#include "config.h"
10
11
#include "fu-chunk-array.h"
12
#include "fu-crc-private.h"
13
#include "fu-input-stream.h"
14
#include "fu-mem-private.h"
15
#include "fu-sum.h"
16
17
/**
18
 * fu_input_stream_from_path:
19
 * @path: a filename
20
 * @error: (nullable): optional return location for an error
21
 *
22
 * Opens the file as n input stream.
23
 *
24
 * Returns: (transfer full): a #GInputStream, or %NULL on error
25
 *
26
 * Since: 2.0.0
27
 **/
28
GInputStream *
29
fu_input_stream_from_path(const gchar *path, GError **error)
30
0
{
31
0
  g_autoptr(GFile) file = NULL;
32
0
  g_autoptr(GFileInputStream) stream = NULL;
33
34
0
  g_return_val_if_fail(path != NULL, NULL);
35
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
36
37
0
  file = g_file_new_for_path(path);
38
0
  stream = g_file_read(file, NULL, error);
39
0
  if (stream == NULL) {
40
0
    fwupd_error_convert(error);
41
0
    return NULL;
42
0
  }
43
0
  return G_INPUT_STREAM(g_steal_pointer(&stream));
44
0
}
45
46
/**
47
 * fu_input_stream_read_safe:
48
 * @stream: a #GInputStream
49
 * @buf (not nullable): a buffer to read data into
50
 * @bufsz: size of @buf
51
 * @offset: offset in bytes into @buf to copy from
52
 * @seek_set: given offset to seek to
53
 * @count: the number of bytes that will be read from the stream
54
 * @error: (nullable): optional return location for an error
55
 *
56
 * Tries to read count bytes from the stream into the buffer starting at @buf.
57
 *
58
 * Returns: %TRUE for success
59
 *
60
 * Since: 2.0.0
61
 **/
62
gboolean
63
fu_input_stream_read_safe(GInputStream *stream,
64
        guint8 *buf,
65
        gsize bufsz,
66
        gsize offset,
67
        gsize seek_set,
68
        gsize count,
69
        GError **error)
70
16.7M
{
71
16.7M
  gssize rc;
72
73
16.7M
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
74
16.7M
  g_return_val_if_fail(buf != NULL, FALSE);
75
16.7M
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
76
77
16.7M
  if (!fu_memchk_write(bufsz, offset, count, error))
78
0
    return FALSE;
79
16.7M
  if (!g_seekable_seek(G_SEEKABLE(stream), seek_set, G_SEEK_SET, NULL, error)) {
80
15.7k
    g_prefix_error(error, "seek to 0x%x: ", (guint)seek_set);
81
15.7k
    return FALSE;
82
15.7k
  }
83
16.7M
  rc = g_input_stream_read(stream, buf + offset, count, NULL, error);
84
16.7M
  if (rc == -1) {
85
0
    g_prefix_error(error, "failed read of 0x%x: ", (guint)count);
86
0
    return FALSE;
87
0
  }
88
16.7M
  if ((gsize)rc != count) {
89
1.02k
    g_set_error(error,
90
1.02k
          FWUPD_ERROR,
91
1.02k
          FWUPD_ERROR_READ,
92
1.02k
          "requested 0x%x and got 0x%x",
93
1.02k
          (guint)count,
94
1.02k
          (guint)rc);
95
1.02k
    return FALSE;
96
1.02k
  }
97
16.7M
  return TRUE;
98
16.7M
}
99
100
/**
101
 * fu_input_stream_read_u8:
102
 * @stream: a #GInputStream
103
 * @offset: offset in bytes into @stream to copy from
104
 * @value: (out) (not nullable): the parsed value
105
 * @error: (nullable): optional return location for an error
106
 *
107
 * Read a value from a stream using a specified endian in a safe way.
108
 *
109
 * Returns: %TRUE if @value was set, %FALSE otherwise
110
 *
111
 * Since: 2.0.0
112
 **/
113
gboolean
114
fu_input_stream_read_u8(GInputStream *stream, gsize offset, guint8 *value, GError **error)
115
471k
{
116
471k
  guint8 buf = 0;
117
471k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
118
471k
  g_return_val_if_fail(value != NULL, FALSE);
119
471k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
120
471k
  if (!fu_input_stream_read_safe(stream, &buf, sizeof(buf), 0x0, offset, sizeof(buf), error))
121
368
    return FALSE;
122
471k
  *value = buf;
123
471k
  return TRUE;
124
471k
}
125
126
/**
127
 * fu_input_stream_read_u16:
128
 * @stream: a #GInputStream
129
 * @offset: offset in bytes into @stream to copy from
130
 * @value: (out) (not nullable): the parsed value
131
 * @endian: an endian type, e.g. %G_LITTLE_ENDIAN
132
 * @error: (nullable): optional return location for an error
133
 *
134
 * Read a value from a stream using a specified endian in a safe way.
135
 *
136
 * Returns: %TRUE if @value was set, %FALSE otherwise
137
 *
138
 * Since: 2.0.0
139
 **/
140
gboolean
141
fu_input_stream_read_u16(GInputStream *stream,
142
       gsize offset,
143
       guint16 *value,
144
       FuEndianType endian,
145
       GError **error)
146
130k
{
147
130k
  guint8 buf[2] = {0};
148
130k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
149
130k
  g_return_val_if_fail(value != NULL, FALSE);
150
130k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
151
130k
  if (!fu_input_stream_read_safe(stream, buf, sizeof(buf), 0x0, offset, sizeof(buf), error))
152
446
    return FALSE;
153
129k
  *value = fu_memread_uint16(buf, endian);
154
129k
  return TRUE;
155
130k
}
156
157
/**
158
 * fu_input_stream_read_u24:
159
 * @stream: a #GInputStream
160
 * @offset: offset in bytes into @stream to copy from
161
 * @value: (out) (not nullable): the parsed value
162
 * @endian: an endian type, e.g. %G_LITTLE_ENDIAN
163
 * @error: (nullable): optional return location for an error
164
 *
165
 * Read a value from a stream using a specified endian in a safe way.
166
 *
167
 * Returns: %TRUE if @value was set, %FALSE otherwise
168
 *
169
 * Since: 2.0.0
170
 **/
171
gboolean
172
fu_input_stream_read_u24(GInputStream *stream,
173
       gsize offset,
174
       guint32 *value,
175
       FuEndianType endian,
176
       GError **error)
177
474
{
178
474
  guint8 buf[3] = {0};
179
474
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
180
474
  g_return_val_if_fail(value != NULL, FALSE);
181
474
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
182
474
  if (!fu_input_stream_read_safe(stream, buf, sizeof(buf), 0x0, offset, sizeof(buf), error))
183
12
    return FALSE;
184
462
  *value = fu_memread_uint24(buf, endian);
185
462
  return TRUE;
186
474
}
187
188
/**
189
 * fu_input_stream_read_u32:
190
 * @stream: a #GInputStream
191
 * @offset: offset in bytes into @stream to copy from
192
 * @value: (out) (not nullable): the parsed value
193
 * @endian: an endian type, e.g. %G_LITTLE_ENDIAN
194
 * @error: (nullable): optional return location for an error
195
 *
196
 * Read a value from a stream using a specified endian in a safe way.
197
 *
198
 * Returns: %TRUE if @value was set, %FALSE otherwise
199
 *
200
 * Since: 2.0.0
201
 **/
202
gboolean
203
fu_input_stream_read_u32(GInputStream *stream,
204
       gsize offset,
205
       guint32 *value,
206
       FuEndianType endian,
207
       GError **error)
208
9.52M
{
209
9.52M
  guint8 buf[4] = {0};
210
9.52M
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
211
9.52M
  g_return_val_if_fail(value != NULL, FALSE);
212
9.52M
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
213
9.52M
  if (!fu_input_stream_read_safe(stream, buf, sizeof(buf), 0x0, offset, sizeof(buf), error))
214
574
    return FALSE;
215
9.52M
  *value = fu_memread_uint32(buf, endian);
216
9.52M
  return TRUE;
217
9.52M
}
218
219
/**
220
 * fu_input_stream_read_u64:
221
 * @stream: a #GInputStream
222
 * @offset: offset in bytes into @stream to copy from
223
 * @value: (out) (not nullable): the parsed value
224
 * @endian: an endian type, e.g. %G_LITTLE_ENDIAN
225
 * @error: (nullable): optional return location for an error
226
 *
227
 * Read a value from a stream using a specified endian in a safe way.
228
 *
229
 * Returns: %TRUE if @value was set, %FALSE otherwise
230
 *
231
 * Since: 2.0.0
232
 **/
233
gboolean
234
fu_input_stream_read_u64(GInputStream *stream,
235
       gsize offset,
236
       guint64 *value,
237
       FuEndianType endian,
238
       GError **error)
239
6.62M
{
240
6.62M
  guint8 buf[8] = {0};
241
6.62M
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
242
6.62M
  g_return_val_if_fail(value != NULL, FALSE);
243
6.62M
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
244
6.62M
  if (!fu_input_stream_read_safe(stream, buf, sizeof(buf), 0x0, offset, sizeof(buf), error))
245
15.0k
    return FALSE;
246
6.60M
  *value = fu_memread_uint64(buf, endian);
247
6.60M
  return TRUE;
248
6.62M
}
249
250
/**
251
 * fu_input_stream_read_byte_array:
252
 * @stream: a #GInputStream
253
 * @offset: offset in bytes into @stream to copy from
254
 * @count: maximum number of bytes to read
255
 * @progress: (nullable): an optional #FuProgress
256
 * @error: (nullable): optional return location for an error
257
 *
258
 * Read a byte array from a stream in a safe way.
259
 *
260
 * NOTE: The returned buffer may be smaller than @count!
261
 *
262
 * Returns: (transfer full): buffer
263
 *
264
 * Since: 2.0.0
265
 **/
266
GByteArray *
267
fu_input_stream_read_byte_array(GInputStream *stream,
268
        gsize offset,
269
        gsize count,
270
        FuProgress *progress,
271
        GError **error)
272
133M
{
273
133M
  guint8 tmp[0x8000];
274
133M
  g_autoptr(GByteArray) buf = g_byte_array_new();
275
133M
  g_autoptr(GError) error_local = NULL;
276
277
133M
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
278
133M
  g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), NULL);
279
133M
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
280
281
  /* this is invalid */
282
133M
  if (count == 0) {
283
92.3k
    g_set_error_literal(error,
284
92.3k
            FWUPD_ERROR,
285
92.3k
            FWUPD_ERROR_NOT_SUPPORTED,
286
92.3k
            "read size must be non-zero");
287
92.3k
    return NULL;
288
92.3k
  }
289
290
  /* seek back to start */
291
133M
  if (G_IS_SEEKABLE(stream) && g_seekable_can_seek(G_SEEKABLE(stream))) {
292
133M
    if (!g_seekable_seek(G_SEEKABLE(stream), offset, G_SEEK_SET, NULL, error))
293
1.84k
      return NULL;
294
133M
  }
295
296
  /* read from stream in 32kB chunks */
297
133M
  while (TRUE) {
298
133M
    gssize sz;
299
133M
    sz = g_input_stream_read(stream,
300
133M
           tmp,
301
133M
           MIN(count - buf->len, sizeof(tmp)),
302
133M
           NULL,
303
133M
           &error_local);
304
133M
    if (sz == 0)
305
161k
      break;
306
133M
    if (sz < 0) {
307
97
      g_set_error_literal(error,
308
97
              FWUPD_ERROR,
309
97
              FWUPD_ERROR_INVALID_FILE,
310
97
              error_local->message);
311
97
      return NULL;
312
97
    }
313
314
    /* update progress */
315
133M
    if (progress != NULL)
316
0
      fu_progress_set_percentage_full(progress, buf->len, count);
317
318
133M
    g_byte_array_append(buf, tmp, sz);
319
133M
    if (buf->len >= count)
320
133M
      break;
321
133M
  }
322
323
  /* no data was read */
324
133M
  if (buf->len == 0) {
325
1.83k
    g_set_error_literal(error,
326
1.83k
            FWUPD_ERROR,
327
1.83k
            FWUPD_ERROR_INVALID_FILE,
328
1.83k
            "no data could be read");
329
1.83k
    return NULL;
330
1.83k
  }
331
332
  /* success */
333
133M
  return g_steal_pointer(&buf);
334
133M
}
335
336
/**
337
 * fu_input_stream_read_bytes:
338
 * @stream: a #GInputStream
339
 * @offset: offset in bytes into @stream to copy from
340
 * @count: maximum number of bytes to read
341
 * @progress: (nullable): an optional #FuProgress
342
 * @error: (nullable): optional return location for an error
343
 *
344
 * Read a #GBytes from a stream in a safe way.
345
 *
346
 * NOTE: The returned buffer may be smaller than @count!
347
 *
348
 * Returns: (transfer full): buffer
349
 *
350
 * Since: 2.0.0
351
 **/
352
GBytes *
353
fu_input_stream_read_bytes(GInputStream *stream,
354
         gsize offset,
355
         gsize count,
356
         FuProgress *progress,
357
         GError **error)
358
1.49M
{
359
1.49M
  g_autoptr(GByteArray) buf = NULL;
360
1.49M
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
361
1.49M
  g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), NULL);
362
1.49M
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
363
1.49M
  buf = fu_input_stream_read_byte_array(stream, offset, count, progress, error);
364
1.49M
  if (buf == NULL)
365
838
    return NULL;
366
1.49M
  return g_byte_array_free_to_bytes(g_steal_pointer(&buf)); /* nocheck:blocked */
367
1.49M
}
368
369
/**
370
 * fu_input_stream_read_string:
371
 * @stream: a #GInputStream
372
 * @offset: offset in bytes into @stream to copy from
373
 * @count: maximum number of bytes to read
374
 * @error: (nullable): optional return location for an error
375
 *
376
 * Read a UTF-8 string from a stream in a safe way.
377
 *
378
 * Returns: (transfer full): string
379
 *
380
 * Since: 2.0.0
381
 **/
382
gchar *
383
fu_input_stream_read_string(GInputStream *stream, gsize offset, gsize count, GError **error)
384
6.22k
{
385
6.22k
  g_autoptr(GByteArray) buf = NULL;
386
387
6.22k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
388
6.22k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
389
390
6.22k
  buf = fu_input_stream_read_byte_array(stream, offset, count, NULL, error);
391
6.22k
  if (buf == NULL)
392
54
    return NULL;
393
6.16k
  if (!g_utf8_validate_len((const gchar *)buf->data, buf->len, NULL)) {
394
171
    g_set_error_literal(error,
395
171
            FWUPD_ERROR,
396
171
            FWUPD_ERROR_NOT_SUPPORTED,
397
171
            "non UTF-8 string");
398
171
    return NULL;
399
171
  }
400
5.99k
  return g_strndup((const gchar *)buf->data, buf->len);
401
6.16k
}
402
403
/**
404
 * fu_input_stream_size:
405
 * @stream: a #GInputStream
406
 * @val: (out): size in bytes
407
 * @error: (nullable): optional return location for an error
408
 *
409
 * Reads the total possible of the stream.
410
 *
411
 * If @stream is not seekable, %G_MAXSIZE is used as the size.
412
 *
413
 * Returns: %TRUE for success
414
 *
415
 * Since: 2.0.0
416
 **/
417
gboolean
418
fu_input_stream_size(GInputStream *stream, gsize *val, GError **error)
419
9.58M
{
420
9.58M
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
421
9.58M
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
422
423
  /* streaming from unseekable stream */
424
9.58M
  if (!G_IS_SEEKABLE(stream) || !g_seekable_can_seek(G_SEEKABLE(stream))) {
425
0
    if (val != NULL)
426
0
      *val = G_MAXSIZE;
427
0
    return TRUE;
428
0
  }
429
430
9.58M
  if (!g_seekable_seek(G_SEEKABLE(stream), 0, G_SEEK_END, NULL, error)) {
431
103
    g_prefix_error(error, "seek to end: ");
432
103
    return FALSE;
433
103
  }
434
9.58M
  if (val != NULL)
435
9.58M
    *val = g_seekable_tell(G_SEEKABLE(stream));
436
437
  /* success */
438
9.58M
  return TRUE;
439
9.58M
}
440
441
static gboolean
442
fu_input_stream_compute_checksum_cb(const guint8 *buf,
443
            gsize bufsz,
444
            gpointer user_data,
445
            GError **error)
446
0
{
447
0
  GChecksum *csum = (GChecksum *)user_data;
448
0
  g_checksum_update(csum, buf, bufsz);
449
0
  return TRUE;
450
0
}
451
452
/**
453
 * fu_input_stream_compute_checksum:
454
 * @stream: a #GInputStream
455
 * @checksum_type: a #GChecksumType
456
 * @error: (nullable): optional return location for an error
457
 *
458
 * Generates the checksum of the entire stream.
459
 *
460
 * Returns: the hexadecimal representation of the checksum, or %NULL on error
461
 *
462
 * Since: 2.0.0
463
 **/
464
gchar *
465
fu_input_stream_compute_checksum(GInputStream *stream, GChecksumType checksum_type, GError **error)
466
0
{
467
0
  g_autoptr(GChecksum) csum = g_checksum_new(checksum_type);
468
469
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
470
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
471
472
0
  if (!fu_input_stream_chunkify(stream, fu_input_stream_compute_checksum_cb, csum, error))
473
0
    return NULL;
474
0
  return g_strdup(g_checksum_get_string(csum));
475
0
}
476
477
static gboolean
478
fu_input_stream_compute_sum8_cb(const guint8 *buf, gsize bufsz, gpointer user_data, GError **error)
479
2.18k
{
480
2.18k
  guint8 *value = (guint8 *)user_data;
481
2.18k
  *value += fu_sum8(buf, bufsz);
482
2.18k
  return TRUE;
483
2.18k
}
484
485
/**
486
 * fu_input_stream_compute_sum8:
487
 * @stream: a #GInputStream
488
 * @value: (out): value
489
 * @error: (nullable): optional return location for an error
490
 *
491
 * Returns the arithmetic sum of all bytes in the stream.
492
 *
493
 * Returns: %TRUE for success
494
 *
495
 * Since: 2.0.0
496
 **/
497
gboolean
498
fu_input_stream_compute_sum8(GInputStream *stream, guint8 *value, GError **error)
499
1.77k
{
500
1.77k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
501
1.77k
  g_return_val_if_fail(value != NULL, FALSE);
502
1.77k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
503
1.77k
  return fu_input_stream_chunkify(stream, fu_input_stream_compute_sum8_cb, value, error);
504
1.77k
}
505
506
static gboolean
507
fu_input_stream_compute_sum16_cb(const guint8 *buf, gsize bufsz, gpointer user_data, GError **error)
508
8.56k
{
509
8.56k
  guint16 *value = (guint16 *)user_data;
510
8.56k
  if (bufsz % sizeof(*value) != 0) {
511
0
    g_set_error(error,
512
0
          FWUPD_ERROR,
513
0
          FWUPD_ERROR_READ,
514
0
          "not aligned to %u bytes, got 0x%x",
515
0
          (guint)sizeof(*value),
516
0
          (guint)bufsz);
517
0
    return FALSE;
518
0
  }
519
8.56k
  *value += fu_sum16(buf, bufsz);
520
8.56k
  return TRUE;
521
8.56k
}
522
523
/**
524
 * fu_input_stream_compute_sum16:
525
 * @stream: a #GInputStream
526
 * @value: (out): value
527
 * @error: (nullable): optional return location for an error
528
 *
529
 * Returns the arithmetic sum of all bytes in the stream.
530
 *
531
 * Returns: %TRUE for success
532
 *
533
 * Since: 2.0.0
534
 **/
535
gboolean
536
fu_input_stream_compute_sum16(GInputStream *stream, guint16 *value, GError **error)
537
8.45k
{
538
8.45k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
539
8.45k
  g_return_val_if_fail(value != NULL, FALSE);
540
8.45k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
541
8.45k
  return fu_input_stream_chunkify(stream, fu_input_stream_compute_sum16_cb, value, error);
542
8.45k
}
543
544
static gboolean
545
fu_input_stream_compute_sum32_cb(const guint8 *buf, gsize bufsz, gpointer user_data, GError **error)
546
0
{
547
0
  guint32 *value = (guint32 *)user_data;
548
0
  if (bufsz % sizeof(*value) != 0) {
549
0
    g_set_error(error,
550
0
          FWUPD_ERROR,
551
0
          FWUPD_ERROR_READ,
552
0
          "not aligned to %u bytes, got 0x%x",
553
0
          (guint)sizeof(*value),
554
0
          (guint)bufsz);
555
0
    return FALSE;
556
0
  }
557
0
  *value += fu_sum32(buf, bufsz);
558
0
  return TRUE;
559
0
}
560
561
/**
562
 * fu_input_stream_compute_sum32:
563
 * @stream: a #GInputStream
564
 * @value: (out): value
565
 * @error: (nullable): optional return location for an error
566
 *
567
 * Returns the arithmetic sum of all bytes in the stream.
568
 *
569
 * Returns: %TRUE for success
570
 *
571
 * Since: 2.0.1
572
 **/
573
gboolean
574
fu_input_stream_compute_sum32(GInputStream *stream, guint32 *value, GError **error)
575
0
{
576
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
577
0
  g_return_val_if_fail(value != NULL, FALSE);
578
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
579
0
  return fu_input_stream_chunkify(stream, fu_input_stream_compute_sum32_cb, value, error);
580
0
}
581
582
typedef struct {
583
  FuCrcKind kind;
584
  guint32 crc;
585
} FuInputStreamComputeCrc32Helper;
586
587
static gboolean
588
fu_input_stream_compute_crc32_cb(const guint8 *buf, gsize bufsz, gpointer user_data, GError **error)
589
3.35k
{
590
3.35k
  FuInputStreamComputeCrc32Helper *helper = (FuInputStreamComputeCrc32Helper *)user_data;
591
3.35k
  helper->crc = fu_crc32_step(helper->kind, buf, bufsz, helper->crc);
592
3.35k
  return TRUE;
593
3.35k
}
594
595
/**
596
 * fu_input_stream_compute_crc32:
597
 * @stream: a #GInputStream
598
 * @kind: a #FuCrcKind, typically %FU_CRC_KIND_B32_STANDARD
599
 * @crc: (inout): initial and final CRC value
600
 * @error: (nullable): optional return location for an error
601
 *
602
 * Returns the cyclic redundancy check value for the given memory buffer.
603
 *
604
 * NOTE: The initial @crc differs from fu_crc32_step() in that it is inverted (to make it
605
 * symmetrical, and chainable), so for most uses you want to use the value of 0x0, not 0xFFFFFFFF.
606
 *
607
 * Returns: %TRUE for success
608
 *
609
 * Since: 2.0.0
610
 **/
611
gboolean
612
fu_input_stream_compute_crc32(GInputStream *stream, FuCrcKind kind, guint32 *crc, GError **error)
613
3.10k
{
614
3.10k
  FuInputStreamComputeCrc32Helper helper = {.crc = *crc, .kind = kind};
615
3.10k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
616
3.10k
  g_return_val_if_fail(crc != NULL, FALSE);
617
3.10k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
618
3.10k
  if (!fu_input_stream_chunkify(stream, fu_input_stream_compute_crc32_cb, &helper, error))
619
0
    return FALSE;
620
3.10k
  *crc = fu_crc32_done(kind, helper.crc);
621
3.10k
  return TRUE;
622
3.10k
}
623
624
typedef struct {
625
  FuCrcKind kind;
626
  guint16 crc;
627
} FuInputStreamComputeCrc16Helper;
628
629
static gboolean
630
fu_input_stream_compute_crc16_cb(const guint8 *buf, gsize bufsz, gpointer user_data, GError **error)
631
0
{
632
0
  FuInputStreamComputeCrc16Helper *helper = (FuInputStreamComputeCrc16Helper *)user_data;
633
0
  helper->crc = fu_crc16_step(helper->kind, buf, bufsz, helper->crc);
634
0
  return TRUE;
635
0
}
636
637
/**
638
 * fu_input_stream_compute_crc16:
639
 * @stream: a #GInputStream
640
 * @kind: a #FuCrcKind, typically %FU_CRC_KIND_B16_XMODEM
641
 * @crc: (inout): initial and final CRC value
642
 * @error: (nullable): optional return location for an error
643
 *
644
 * Returns the cyclic redundancy check value for the given memory buffer.
645
 *
646
 * NOTE: The initial @crc differs from fu_crc16() in that it is inverted (to make it
647
 * symmetrical, and chainable), so for most uses you want to use the value of 0x0, not 0xFFFF.
648
 *
649
 * Returns: %TRUE for success
650
 *
651
 * Since: 2.0.0
652
 **/
653
gboolean
654
fu_input_stream_compute_crc16(GInputStream *stream, FuCrcKind kind, guint16 *crc, GError **error)
655
0
{
656
0
  FuInputStreamComputeCrc16Helper helper = {.crc = *crc, .kind = kind};
657
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
658
0
  g_return_val_if_fail(crc != NULL, FALSE);
659
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
660
0
  if (!fu_input_stream_chunkify(stream, fu_input_stream_compute_crc16_cb, &helper, error))
661
0
    return FALSE;
662
0
  *crc = fu_crc16_done(kind, helper.crc);
663
0
  return TRUE;
664
0
}
665
666
/**
667
 * fu_input_stream_chunkify:
668
 * @stream: a #GInputStream
669
 * @func_cb: (scope async): function to call with chunks
670
 * @user_data: user data to pass to @func_cb
671
 * @error: (nullable): optional return location for an error
672
 *
673
 * Split the stream into blocks and calls a function on each chunk.
674
 *
675
 * Returns: %TRUE for success
676
 *
677
 * Since: 2.0.0
678
 **/
679
gboolean
680
fu_input_stream_chunkify(GInputStream *stream,
681
       FuInputStreamChunkifyFunc func_cb,
682
       gpointer user_data,
683
       GError **error)
684
15.5k
{
685
15.5k
  g_autoptr(FuChunkArray) chunks = NULL;
686
687
15.5k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
688
15.5k
  g_return_val_if_fail(func_cb != NULL, FALSE);
689
15.5k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
690
691
15.5k
  chunks = fu_chunk_array_new_from_stream(stream,
692
15.5k
            FU_CHUNK_ADDR_OFFSET_NONE,
693
15.5k
            FU_CHUNK_PAGESZ_NONE,
694
15.5k
            0x8000,
695
15.5k
            error);
696
15.5k
  if (chunks == NULL)
697
0
    return FALSE;
698
32.1k
  for (gsize i = 0; i < fu_chunk_array_length(chunks); i++) {
699
16.5k
    g_autoptr(FuChunk) chk = NULL;
700
16.5k
    chk = fu_chunk_array_index(chunks, i, error);
701
16.5k
    if (chk == NULL)
702
0
      return FALSE;
703
16.5k
    if (!func_cb(fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), user_data, error))
704
0
      return FALSE;
705
16.5k
  }
706
15.5k
  return TRUE;
707
15.5k
}
708
709
/**
710
 * fu_input_stream_find:
711
 * @stream: a #GInputStream
712
 * @buf: input buffer to look for
713
 * @bufsz: size of @buf
714
 * @offset: (nullable): found offset
715
 * @error: (nullable): optional return location for an error
716
 *
717
 * Find a memory buffer within an input stream, without loading the entire stream into a buffer.
718
 *
719
 * Returns: %TRUE if @buf was found
720
 *
721
 * Since: 2.0.0
722
 **/
723
gboolean
724
fu_input_stream_find(GInputStream *stream,
725
         const guint8 *buf,
726
         gsize bufsz,
727
         gsize *offset,
728
         GError **error)
729
1.11k
{
730
1.11k
  g_autoptr(GByteArray) buf_acc = g_byte_array_new();
731
1.11k
  const gsize blocksz = 0x10000;
732
1.11k
  gsize offset_add = 0;
733
1.11k
  gsize offset_cur = 0;
734
735
1.11k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
736
1.11k
  g_return_val_if_fail(buf != NULL, FALSE);
737
1.11k
  g_return_val_if_fail(bufsz != 0, FALSE);
738
1.11k
  g_return_val_if_fail(bufsz < blocksz, FALSE);
739
1.11k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
740
741
1.41k
  while (offset_cur < bufsz) {
742
1.13k
    g_autoptr(GByteArray) buf_tmp = NULL;
743
1.13k
    g_autoptr(GError) error_local = NULL;
744
745
    /* read more data */
746
1.13k
    buf_tmp = fu_input_stream_read_byte_array(stream,
747
1.13k
                offset_cur,
748
1.13k
                blocksz,
749
1.13k
                NULL,
750
1.13k
                &error_local);
751
1.13k
    if (buf_tmp == NULL) {
752
24
      if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE))
753
24
        break;
754
0
      g_propagate_error(error, g_steal_pointer(&error_local));
755
0
      return FALSE;
756
24
    }
757
1.11k
    g_byte_array_append(buf_acc, buf_tmp->data, buf_tmp->len);
758
759
    /* we found something */
760
1.11k
    if (fu_memmem_safe(buf_acc->data, buf_acc->len, buf, bufsz, offset, NULL)) {
761
804
      if (offset != NULL)
762
804
        *offset += offset_add;
763
804
      return TRUE;
764
804
    }
765
766
    /* truncate the buffer */
767
306
    if (buf_acc->len > bufsz) {
768
280
      offset_add += buf_acc->len - bufsz;
769
280
      g_byte_array_remove_range(buf_acc, 0, buf_acc->len - bufsz);
770
280
    }
771
772
    /* move the offset */
773
306
    offset_cur += buf_tmp->len;
774
306
  }
775
306
  g_set_error(error,
776
306
        FWUPD_ERROR,
777
306
        FWUPD_ERROR_NOT_FOUND,
778
306
        "failed to find buffer of size 0x%x",
779
306
        (guint)bufsz);
780
306
  return FALSE;
781
1.11k
}