Coverage Report

Created: 2026-04-12 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-linear-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2022 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-byte-array.h"
10
#include "fu-bytes.h"
11
#include "fu-common.h"
12
#include "fu-input-stream.h"
13
#include "fu-linear-firmware.h"
14
#include "fu-partial-input-stream.h"
15
16
/**
17
 * FuLinearFirmware:
18
 *
19
 * A firmware made up of concatenated blobs of a different firmware type.
20
 *
21
 * Parsed firmware images can set `FU_FIRMWARE_FLAG_IS_LAST_IMAGE` to abort processing.
22
 *
23
 * NOTE: All the child images will be of the specified `GType`.
24
 *
25
 * See also: [class@FuFirmware]
26
 */
27
28
typedef struct {
29
  GType image_gtype;
30
} FuLinearFirmwarePrivate;
31
32
5.42k
G_DEFINE_TYPE_WITH_PRIVATE(FuLinearFirmware, fu_linear_firmware, FU_TYPE_FIRMWARE)
33
5.42k
#define GET_PRIVATE(o) (fu_linear_firmware_get_instance_private(o))
34
35
enum { PROP_0, PROP_IMAGE_GTYPE, PROP_LAST };
36
37
static void
38
fu_linear_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
39
0
{
40
0
  FuLinearFirmware *self = FU_LINEAR_FIRMWARE(firmware);
41
0
  FuLinearFirmwarePrivate *priv = GET_PRIVATE(self);
42
0
  fu_xmlb_builder_insert_kv(bn, "image_gtype", g_type_name(priv->image_gtype));
43
0
}
44
45
/**
46
 * fu_linear_firmware_get_image_gtype:
47
 * @self: a #FuLinearFirmware
48
 *
49
 * Gets the image #GType to use when parsing a byte buffer.
50
 *
51
 * Returns: integer
52
 *
53
 * Since: 1.8.2
54
 **/
55
GType
56
fu_linear_firmware_get_image_gtype(FuLinearFirmware *self)
57
0
{
58
0
  FuLinearFirmwarePrivate *priv = GET_PRIVATE(self);
59
0
  g_return_val_if_fail(FU_IS_LINEAR_FIRMWARE(self), G_TYPE_INVALID);
60
0
  return priv->image_gtype;
61
0
}
62
63
static gboolean
64
fu_linear_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
65
0
{
66
0
  FuLinearFirmware *self = FU_LINEAR_FIRMWARE(firmware);
67
0
  FuLinearFirmwarePrivate *priv = GET_PRIVATE(self);
68
0
  const gchar *tmp;
69
70
  /* simple properties */
71
0
  tmp = xb_node_query_text(n, "image_gtype", NULL);
72
0
  if (tmp != NULL) {
73
0
    priv->image_gtype = g_type_from_name(tmp);
74
0
    if (priv->image_gtype == G_TYPE_INVALID) {
75
0
      g_set_error(error,
76
0
            FWUPD_ERROR,
77
0
            FWUPD_ERROR_NOT_FOUND,
78
0
            "GType %s not registered",
79
0
            tmp);
80
0
      return FALSE;
81
0
    }
82
0
    fu_firmware_add_image_gtype(firmware, priv->image_gtype);
83
0
  }
84
85
  /* success */
86
0
  return TRUE;
87
0
}
88
89
static gboolean
90
fu_linear_firmware_parse(FuFirmware *firmware,
91
       GInputStream *stream,
92
       FuFirmwareParseFlags flags,
93
       GError **error)
94
1.80k
{
95
1.80k
  FuLinearFirmware *self = FU_LINEAR_FIRMWARE(firmware);
96
1.80k
  FuLinearFirmwarePrivate *priv = GET_PRIVATE(self);
97
1.80k
  gsize offset = 0;
98
1.80k
  gsize streamsz = 0;
99
100
1.80k
  if (!fu_input_stream_size(stream, &streamsz, error))
101
0
    return FALSE;
102
1.89k
  while (offset < streamsz) {
103
1.87k
    g_autoptr(FuFirmware) img = g_object_new(priv->image_gtype, NULL);
104
1.87k
    g_autoptr(GInputStream) stream_tmp = NULL;
105
106
1.87k
    stream_tmp = fu_partial_input_stream_new(stream, offset, streamsz - offset, error);
107
1.87k
    if (stream_tmp == NULL) {
108
0
      g_prefix_error_literal(error, "failed to cut linear image: ");
109
0
      return FALSE;
110
0
    }
111
1.87k
    if (!fu_firmware_parse_stream(img,
112
1.87k
                stream_tmp,
113
1.87k
                0x0,
114
1.87k
                flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
115
1.87k
                error)) {
116
1.78k
      g_prefix_error(error, "failed to parse at 0x%x: ", (guint)offset);
117
1.78k
      return FALSE;
118
1.78k
    }
119
90
    fu_firmware_set_offset(img, offset);
120
90
    if (!fu_firmware_add_image(firmware, img, error))
121
0
      return FALSE;
122
90
    if (fu_firmware_get_size(img) == 0) {
123
0
      g_set_error_literal(error,
124
0
              FWUPD_ERROR,
125
0
              FWUPD_ERROR_INVALID_DATA,
126
0
              "child image had no defined size");
127
0
      return FALSE;
128
0
    }
129
130
    /* next! */
131
90
    if (!fu_size_checked_inc(&offset, fu_firmware_get_size(img), error))
132
0
      return FALSE;
133
134
    /* skip any padding */
135
90
    if (fu_firmware_has_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE))
136
0
      break;
137
90
  }
138
139
  /* this might be less than streamsz if padded */
140
14
  fu_firmware_set_size(firmware, offset);
141
142
  /* success */
143
14
  return TRUE;
144
1.80k
}
145
146
static GByteArray *
147
fu_linear_firmware_write(FuFirmware *firmware, GError **error)
148
0
{
149
0
  g_autoptr(GByteArray) buf = g_byte_array_new();
150
0
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
151
152
  /* add each file */
153
0
  for (guint i = 0; i < images->len; i++) {
154
0
    FuFirmware *img = g_ptr_array_index(images, i);
155
0
    g_autoptr(GBytes) blob = NULL;
156
0
    if (i == images->len - 1)
157
0
      fu_firmware_add_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE);
158
0
    fu_firmware_set_offset(img, buf->len);
159
0
    blob = fu_firmware_write(img, error);
160
0
    if (blob == NULL)
161
0
      return NULL;
162
0
    fu_byte_array_append_bytes(buf, blob);
163
0
  }
164
165
  /* success */
166
0
  return g_steal_pointer(&buf);
167
0
}
168
169
static void
170
fu_linear_firmware_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
171
0
{
172
0
  FuLinearFirmware *self = FU_LINEAR_FIRMWARE(object);
173
0
  FuLinearFirmwarePrivate *priv = GET_PRIVATE(self);
174
0
  switch (prop_id) {
175
0
  case PROP_IMAGE_GTYPE:
176
0
    g_value_set_gtype(value, priv->image_gtype);
177
0
    break;
178
0
  default:
179
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
180
0
    break;
181
0
  }
182
0
}
183
184
static void
185
fu_linear_firmware_set_property(GObject *object,
186
        guint prop_id,
187
        const GValue *value,
188
        GParamSpec *pspec)
189
1.81k
{
190
1.81k
  FuLinearFirmware *self = FU_LINEAR_FIRMWARE(object);
191
1.81k
  FuLinearFirmwarePrivate *priv = GET_PRIVATE(self);
192
1.81k
  switch (prop_id) {
193
1.81k
  case PROP_IMAGE_GTYPE:
194
1.81k
    priv->image_gtype = g_value_get_gtype(value);
195
1.81k
    fu_firmware_add_image_gtype(FU_FIRMWARE(self), priv->image_gtype);
196
1.81k
    break;
197
0
  default:
198
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
199
0
    break;
200
1.81k
  }
201
1.81k
}
202
203
static void
204
fu_linear_firmware_init(FuLinearFirmware *self)
205
1.81k
{
206
1.81k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 1024);
207
1.81k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION);
208
1.81k
  fu_firmware_set_size_max(FU_FIRMWARE(self), 1 * FU_GB);
209
1.81k
}
210
211
static void
212
fu_linear_firmware_class_init(FuLinearFirmwareClass *klass)
213
1
{
214
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
215
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
216
1
  GParamSpec *pspec;
217
218
1
  object_class->get_property = fu_linear_firmware_get_property;
219
1
  object_class->set_property = fu_linear_firmware_set_property;
220
1
  firmware_class->parse = fu_linear_firmware_parse;
221
1
  firmware_class->write = fu_linear_firmware_write;
222
1
  firmware_class->export = fu_linear_firmware_export;
223
1
  firmware_class->build = fu_linear_firmware_build;
224
225
  /**
226
   * FuLinearFirmware:image-gtype:
227
   *
228
   * The image #GType
229
   *
230
   * Since: 1.8.2
231
   */
232
1
  pspec =
233
1
      g_param_spec_gtype("image-gtype",
234
1
             NULL,
235
1
             NULL,
236
1
             FU_TYPE_FIRMWARE,
237
1
             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME);
238
1
  g_object_class_install_property(object_class, PROP_IMAGE_GTYPE, pspec);
239
1
}
240
241
/**
242
 * fu_linear_firmware_new:
243
 * @image_gtype: a #GType, e.g. %FU_TYPE_OPROM_FIRMWARE
244
 *
245
 * Creates a new #FuFirmware made up of concatenated images.
246
 *
247
 * Since: 1.8.2
248
 **/
249
FuFirmware *
250
fu_linear_firmware_new(GType image_gtype)
251
1.81k
{
252
1.81k
  return g_object_new(FU_TYPE_LINEAR_FIRMWARE, "image-gtype", image_gtype, NULL);
253
1.81k
}