Coverage Report

Created: 2025-08-26 06:04

/src/rauc/src/manifest.c
Line
Count
Source (jump to first uncovered line)
1
#include <string.h>
2
3
#include "checksum.h"
4
#include "config_file.h"
5
#include "context.h"
6
#include "manifest.h"
7
#include "signature.h"
8
#include "utils.h"
9
10
0
#define RAUC_IMAGE_PREFIX "image"
11
12
0
#define R_MANIFEST_ERROR r_manifest_error_quark()
13
GQuark r_manifest_error_quark(void)
14
0
{
15
0
  return g_quark_from_static_string("r_manifest_error_quark");
16
0
}
17
18
static gboolean parse_image(GKeyFile *key_file, const gchar *group, RaucImage **image, GError **error)
19
9.91k
{
20
9.91k
  g_autoptr(RaucImage) iimage = r_new_image();
21
9.91k
  g_auto(GStrv) groupsplit = NULL;
22
9.91k
  gchar *value;
23
9.91k
  g_auto(GStrv) hooks = NULL;
24
9.91k
  gsize entries;
25
9.91k
  g_auto(GStrv) converted = NULL;
26
9.91k
  GError *ierror = NULL;
27
28
9.91k
  g_return_val_if_fail(key_file != NULL, FALSE);
29
9.91k
  g_return_val_if_fail(group != NULL, FALSE);
30
9.91k
  g_return_val_if_fail(image == NULL || *image == NULL, FALSE);
31
9.91k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
32
33
  /* We support several formats:
34
   * - [image.rootfs]
35
   * - [image.rootfs.product-a]
36
   * - [image.appfs/app-1]
37
   * - [image.appfs/app-1.product-a]
38
   */
39
40
9.91k
  groupsplit = g_strsplit(group, ".", 3);
41
9.91k
  g_assert_cmpint(g_strv_length(groupsplit), >=, 2);
42
9.91k
  g_assert_cmpstr(groupsplit[0], ==, "image");
43
44
9.91k
  g_auto(GStrv) targetsplit = NULL;
45
9.91k
  targetsplit = g_strsplit(groupsplit[1], "/", 2);
46
9.91k
  iimage->slotclass = g_strdup(targetsplit[0]);
47
48
  /* Do we have an artifact name for this image? */
49
9.91k
  if (g_strv_length(targetsplit) == 2)
50
977
    iimage->artifact = g_strdup(targetsplit[1]);
51
52
  /* Do we have a variant name for this image? */
53
9.91k
  if (g_strv_length(groupsplit) == 3)
54
3.28k
    iimage->variant = g_strdup(groupsplit[2]);
55
56
9.91k
  value = key_file_consume_string(key_file, group, "sha256", NULL);
57
9.91k
  if (value) {
58
101
    iimage->checksum.type = G_CHECKSUM_SHA256;
59
101
    iimage->checksum.digest = value;
60
101
  }
61
9.91k
  iimage->checksum.size = g_key_file_get_uint64(key_file,
62
9.91k
      group, "size", &ierror);
63
9.91k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
64
    /* restore size to the default of -1 */
65
9.80k
    iimage->checksum.size = -1;
66
9.80k
    g_clear_error(&ierror);
67
9.80k
  } else if (ierror) {
68
23
    g_propagate_error(error, ierror);
69
23
    return FALSE;
70
23
  }
71
9.88k
  g_key_file_remove_key(key_file, group, "size", NULL);
72
73
9.88k
  hooks = g_key_file_get_string_list(key_file, group, "hooks", &entries, NULL);
74
12.6k
  for (gsize j = 0; j < entries; j++) {
75
2.94k
    if (g_strcmp0(hooks[j], "pre-install") == 0) {
76
426
      iimage->hooks.pre_install = TRUE;
77
2.51k
    } else if (g_strcmp0(hooks[j], "install") == 0) {
78
1.88k
      iimage->hooks.install = TRUE;
79
1.88k
    } else if (g_strcmp0(hooks[j], "post-install") == 0) {
80
453
      iimage->hooks.post_install = TRUE;
81
453
    } else {
82
180
      g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE,
83
180
          "slot hook type '%s' not supported", hooks[j]);
84
180
      return FALSE;
85
180
    }
86
2.94k
  }
87
9.70k
  g_key_file_remove_key(key_file, group, "hooks", NULL);
88
89
9.70k
  iimage->filename = key_file_consume_string(key_file, group, "filename", &ierror);
90
  /* 'filename' is optional only for 'install' hooks */
91
9.70k
  if (iimage->filename == NULL) {
92
1.27k
    if (!iimage->hooks.install) {
93
332
      g_propagate_error(error, ierror);
94
332
      return FALSE;
95
944
    } else {
96
944
      g_clear_error(&ierror);
97
944
    }
98
1.27k
  }
99
100
9.37k
  g_key_file_remove_key(key_file, group, "version", NULL);
101
9.37k
  g_key_file_remove_key(key_file, group, "description", NULL);
102
9.37k
  g_key_file_remove_key(key_file, group, "build", NULL);
103
104
9.37k
  iimage->adaptive = g_key_file_get_string_list(key_file, group, "adaptive", NULL, NULL);
105
9.37k
  g_key_file_remove_key(key_file, group, "adaptive", NULL);
106
107
9.37k
  iimage->convert = g_key_file_get_string_list(key_file, group, "convert", NULL, NULL);
108
9.37k
  g_key_file_remove_key(key_file, group, "convert", NULL);
109
110
9.37k
  converted = g_key_file_get_string_list(key_file, group, "converted", NULL, NULL);
111
9.37k
  g_key_file_remove_key(key_file, group, "converted", NULL);
112
9.37k
  if (converted) {
113
0
    iimage->converted = g_ptr_array_new_with_free_func(g_free);
114
0
    r_ptr_array_addv(iimage->converted, converted, TRUE);
115
0
  }
116
117
9.37k
  if (!check_remaining_keys(key_file, group, &ierror)) {
118
102
    g_propagate_error(error, ierror);
119
102
    return FALSE;
120
102
  }
121
9.27k
  g_key_file_remove_group(key_file, group, NULL);
122
123
9.27k
  *image = g_steal_pointer(&iimage);
124
125
9.27k
  return TRUE;
126
9.37k
}
127
128
static gboolean parse_meta(GKeyFile *key_file, const gchar *group, RaucManifest *raucm, GError **error)
129
3.29k
{
130
3.29k
  g_auto(GStrv) groupsplit = NULL;
131
3.29k
  g_auto(GStrv) keys = NULL;
132
3.29k
  g_autoptr(GHashTable) kvs = NULL;
133
3.29k
  g_autofree gchar *env_section = NULL;
134
3.29k
  GError *ierror = NULL;
135
136
3.29k
  g_return_val_if_fail(key_file != NULL, FALSE);
137
3.29k
  g_return_val_if_fail(group != NULL, FALSE);
138
3.29k
  g_return_val_if_fail(raucm != NULL, FALSE);
139
3.29k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
140
141
3.29k
  groupsplit = g_strsplit(group, ".", 2);
142
3.29k
  if ((g_strv_length(groupsplit) != 2) || strchr(groupsplit[1], '.')) {
143
11
    g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE,
144
11
        "invalid metadata section name '%s' (must contain a single '.')", group);
145
11
    return FALSE;
146
11
  }
147
148
3.28k
  env_section = r_prepare_env_key(groupsplit[1], &ierror);
149
3.28k
  if (!env_section) {
150
88
    g_propagate_prefixed_error(
151
88
        error,
152
88
        ierror,
153
88
        "Invalid metadata section name '%s': ", groupsplit[1]);
154
88
    return FALSE;
155
88
  }
156
157
3.20k
  kvs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
158
159
3.20k
  keys = g_key_file_get_keys(key_file, group, NULL, NULL);
160
8.90k
  for (GStrv key = keys; *key; key++) {
161
5.85k
    g_autofree gchar *value = key_file_consume_string(key_file, group, *key, &ierror);
162
5.85k
    g_autofree gchar *env_key = NULL;
163
164
5.85k
    if (!value) {
165
92
      g_propagate_error(error, ierror);
166
92
      return FALSE;
167
92
    }
168
169
5.76k
    env_key = r_prepare_env_key(*key, &ierror);
170
5.76k
    if (!env_key) {
171
58
      g_propagate_prefixed_error(
172
58
          error,
173
58
          ierror,
174
58
          "Invalid metadata key name '%s': ", *key);
175
58
      return FALSE;
176
58
    }
177
178
5.70k
    g_hash_table_insert(kvs, g_strdup(*key), g_steal_pointer(&value));
179
5.70k
  }
180
181
3.05k
  g_hash_table_insert(raucm->meta, g_strdup(groupsplit[1]), g_steal_pointer(&kvs));
182
3.05k
  g_key_file_remove_group(key_file, group, NULL);
183
184
3.05k
  return TRUE;
185
3.20k
}
186
187
/* Parses key_file into RaucManifest structure
188
 *
189
 * key_file - input key file
190
 * manifest - address of manifest pointer, pointer must be NULL and will be set
191
 *            to point to a newly allocated RaucManifest if parsing succeeded.
192
 *            Otherwise it will remain untouched.
193
 * error    - Return location for GError
194
 *
195
 * Returns TRUE if manifest was parsed without error, otherwise FALSE
196
 */
197
static gboolean parse_manifest(GKeyFile *key_file, RaucManifest **manifest, GError **error)
198
3.06k
{
199
3.06k
  GError *ierror = NULL;
200
3.06k
  g_autoptr(RaucManifest) raucm = g_new0(RaucManifest, 1);
201
3.06k
  g_autofree gchar *tmp = NULL;
202
3.06k
  g_auto(GStrv) groups = NULL;
203
3.06k
  gsize group_count;
204
3.06k
  g_auto(GStrv) bundle_hooks = NULL;
205
3.06k
  gsize hook_entries;
206
207
3.06k
  g_return_val_if_fail(key_file != NULL, FALSE);
208
3.06k
  g_return_val_if_fail(manifest != NULL && *manifest == NULL, FALSE);
209
3.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
210
211
  /* initialize empty warnings array */
212
3.06k
  raucm->warnings = g_ptr_array_new_with_free_func(g_free);
213
214
  /* parse [update] section */
215
3.06k
  raucm->update_compatible = key_file_consume_string(key_file, "update", "compatible", &ierror);
216
3.06k
  if (!raucm->update_compatible) {
217
1.19k
    g_propagate_error(error, ierror);
218
1.19k
    return FALSE;
219
1.19k
  }
220
1.87k
  raucm->update_version = key_file_consume_string(key_file, "update", "version", NULL);
221
1.87k
  raucm->update_description = key_file_consume_string(key_file, "update", "description", NULL);
222
1.87k
  raucm->update_build = key_file_consume_string(key_file, "update", "build", NULL);
223
1.87k
  raucm->update_min_rauc_version = key_file_consume_string(key_file, "update", "min-rauc-version", NULL);
224
1.87k
  if (!check_remaining_keys(key_file, "update", &ierror)) {
225
97
    g_propagate_error(error, ierror);
226
97
    return FALSE;
227
97
  }
228
1.78k
  g_key_file_remove_group(key_file, "update", NULL);
229
230
  /* parse [bundle] section */
231
1.78k
  tmp = key_file_consume_string(key_file, "bundle", "format", NULL);
232
1.78k
  if (tmp == NULL) {
233
1.59k
    g_ptr_array_add(raucm->warnings, g_strdup(
234
1.59k
        "WARNING: The manifest does not specify a bundle format, defaulting to 'plain'."));
235
1.59k
    g_ptr_array_add(raucm->warnings, g_strdup(
236
1.59k
        "  We recommend using the 'verity' format instead, if possible."));
237
1.59k
    g_ptr_array_add(raucm->warnings, g_strdup(
238
1.59k
        "  To silence this warning, select the 'plain' format explicitly."));
239
1.59k
    g_ptr_array_add(raucm->warnings, g_strdup(
240
1.59k
        "  See https://rauc.readthedocs.io/en/latest/reference.html#sec-ref-formats for details.'"));
241
1.59k
  } else {
242
189
    raucm->bundle_format_explicit = TRUE;
243
189
  }
244
1.78k
  if (tmp == NULL || g_strcmp0(tmp, "plain") == 0) {
245
1.59k
    raucm->bundle_format = R_MANIFEST_FORMAT_PLAIN;
246
1.59k
  } else if ((g_strcmp0(tmp, "verity") == 0) || (g_strcmp0(tmp, "crypt") == 0)) {
247
    /* only SHA256 is supported for now */
248
17
    raucm->bundle_format = g_strcmp0(tmp, "crypt") == 0 ? R_MANIFEST_FORMAT_CRYPT : R_MANIFEST_FORMAT_VERITY;
249
17
    raucm->bundle_verity_hash = key_file_consume_string(key_file, "bundle", "verity-hash", NULL);
250
17
    raucm->bundle_verity_salt = key_file_consume_string(key_file, "bundle", "verity-salt", NULL);
251
17
    raucm->bundle_verity_size = g_key_file_get_uint64(key_file, "bundle", "verity-size", NULL);
252
    /* values are checked in check_manifest */
253
17
    g_key_file_remove_key(key_file, "bundle", "verity-size", NULL);
254
170
  } else {
255
170
    g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE,
256
170
        "Invalid format value '%s' in group '[bundle]'", tmp);
257
170
    return FALSE;
258
170
  }
259
  /* crypt format requires additional dm-crypt key */
260
1.61k
  if (g_strcmp0(tmp, "crypt") == 0) {
261
13
    raucm->bundle_crypt_key = key_file_consume_string(key_file, "bundle", "crypt-key", NULL);
262
13
  }
263
1.61k
  if (!check_remaining_keys(key_file, "bundle", &ierror)) {
264
7
    g_propagate_error(error, ierror);
265
7
    return FALSE;
266
7
  }
267
1.60k
  g_key_file_remove_group(key_file, "bundle", NULL);
268
269
  /* parse [handler] section */
270
1.60k
  raucm->handler_name = key_file_consume_string(key_file, "handler", "filename", NULL);
271
1.60k
  raucm->handler_args = key_file_consume_string(key_file, "handler", "args", NULL);
272
1.60k
  if (raucm->handler_args && !raucm->handler_name) {
273
2
    g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE,
274
2
        "Setting 'args' requires a full custom handler to be defined under 'filename' in group '[handler]'.");
275
2
    return FALSE;
276
2
  }
277
1.60k
  if (!check_remaining_keys(key_file, "handler", &ierror)) {
278
2
    g_propagate_error(error, ierror);
279
2
    return FALSE;
280
2
  }
281
1.59k
  g_key_file_remove_group(key_file, "handler", NULL);
282
283
  /* parse [hooks] section */
284
1.59k
  raucm->hook_name = key_file_consume_string(key_file, "hooks", "filename", NULL);
285
1.59k
  bundle_hooks = g_key_file_get_string_list(key_file, "hooks", "hooks", &hook_entries, NULL);
286
1.59k
  g_key_file_remove_key(key_file, "hooks", "hooks", NULL);
287
2.01k
  for (gsize j = 0; j < hook_entries; j++) {
288
521
    if (g_strcmp0(bundle_hooks[j], "install-check") == 0) {
289
416
      raucm->hooks.install_check = TRUE;
290
416
    } else {
291
105
      g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE,
292
105
          "install hook type '%s' not supported", bundle_hooks[j]);
293
105
      return FALSE;
294
105
    }
295
521
  }
296
297
1.49k
  if (!check_remaining_keys(key_file, "hooks", &ierror)) {
298
1
    g_propagate_error(error, ierror);
299
1
    return FALSE;
300
1
  }
301
1.49k
  g_key_file_remove_group(key_file, "hooks", NULL);
302
303
1.49k
  raucm->meta = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_destroy);
304
305
1.49k
  groups = g_key_file_get_groups(key_file, &group_count);
306
23.4k
  for (gsize i = 0; i < group_count; i++) {
307
    /* parse [image.<slotclass>] sections */
308
22.8k
    if (g_str_has_prefix(groups[i], RAUC_IMAGE_PREFIX ".")) {
309
9.91k
      RaucImage *image = NULL;
310
311
9.91k
      if (!parse_image(key_file, groups[i], &image, &ierror)) {
312
637
        g_propagate_error(error, ierror);
313
637
        return FALSE;
314
637
      }
315
316
9.27k
      raucm->images = g_list_append(raucm->images, image);
317
9.27k
    }
318
    /* parse [meta.<label>] sections */
319
22.2k
    if (g_str_has_prefix(groups[i], "meta.")) {
320
3.29k
      if (!parse_meta(key_file, groups[i], raucm, &ierror)) {
321
249
        g_propagate_error(error, ierror);
322
249
        return FALSE;
323
249
      }
324
3.29k
    }
325
22.2k
  }
326
327
  /* ignore [rollout] section for now, so that we can add hints/overrides
328
   * for rollout/polling behaviour later */
329
607
  g_key_file_remove_group(key_file, "rollout", NULL);
330
331
607
  if (!check_remaining_groups(key_file, &ierror)) {
332
230
    g_propagate_error(error, ierror);
333
230
    return FALSE;
334
230
  }
335
336
377
  *manifest = g_steal_pointer(&raucm);
337
338
377
  return TRUE;
339
607
}
340
341
gboolean load_manifest_mem(GBytes *mem, RaucManifest **manifest, GError **error)
342
5.08k
{
343
5.08k
  GError *ierror = NULL;
344
5.08k
  g_autoptr(GKeyFile) key_file = NULL;
345
5.08k
  const gchar *data;
346
5.08k
  gsize length;
347
5.08k
  g_autofree gchar *manifest_checksum = NULL;
348
349
5.08k
  g_return_val_if_fail(mem, FALSE);
350
5.08k
  g_return_val_if_fail(manifest != NULL && *manifest == NULL, FALSE);
351
5.08k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
352
353
5.08k
  data = g_bytes_get_data(mem, &length);
354
5.08k
  if (data == NULL) {
355
0
    g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_ERROR_NO_DATA, "No data available");
356
0
    return FALSE;
357
0
  }
358
359
5.08k
  manifest_checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar*) data, length);
360
361
5.08k
  key_file = g_key_file_new();
362
363
5.08k
  if (!g_key_file_load_from_data(key_file, data, length, G_KEY_FILE_NONE, &ierror)) {
364
2.01k
    g_propagate_error(error, ierror);
365
2.01k
    return FALSE;
366
2.01k
  }
367
368
3.06k
  if (!parse_manifest(key_file, manifest, &ierror)) {
369
2.69k
    g_propagate_error(error, ierror);
370
2.69k
    return FALSE;
371
2.69k
  }
372
373
377
  (*manifest)->hash = g_steal_pointer(&manifest_checksum);
374
375
377
  return TRUE;
376
3.06k
}
377
378
gboolean load_manifest_file(const gchar *filename, RaucManifest **manifest, GError **error)
379
0
{
380
0
  GError *ierror = NULL;
381
0
  g_autofree gchar *data;
382
0
  gsize length;
383
0
  g_autoptr(GKeyFile) key_file = NULL;
384
0
  g_autofree gchar *manifest_checksum = NULL;
385
386
0
  g_return_val_if_fail(filename, FALSE);
387
0
  g_return_val_if_fail(manifest != NULL && *manifest == NULL, FALSE);
388
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
389
390
0
  if (!g_file_get_contents(filename, &data, &length, &ierror)) {
391
0
    g_propagate_error(error, ierror);
392
0
    return FALSE;
393
0
  }
394
0
  manifest_checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar*) data, length);
395
396
0
  key_file = g_key_file_new();
397
398
0
  if (!g_key_file_load_from_data(key_file, data, length, G_KEY_FILE_NONE, &ierror)) {
399
0
    g_propagate_error(error, ierror);
400
0
    return FALSE;
401
0
  }
402
403
0
  if (!parse_manifest(key_file, manifest, &ierror)) {
404
0
    g_propagate_error(error, ierror);
405
0
    return FALSE;
406
0
  }
407
408
0
  (*manifest)->hash = g_steal_pointer(&manifest_checksum);
409
410
0
  return TRUE;
411
0
}
412
413
static gboolean check_manifest_common(const RaucManifest *mf, GError **error)
414
0
{
415
0
  gboolean have_hooks = FALSE;
416
417
0
  g_return_val_if_fail(mf, FALSE);
418
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
419
420
0
  if (mf->update_min_rauc_version) {
421
0
    if (!r_semver_less_equal("0", mf->update_min_rauc_version, NULL)) {
422
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
423
0
          "Failed to parse 'min-rauc-version'. Expected 'Major[.Minor[.Patch]][-pre_release]]', got '%s'",
424
0
          mf->update_min_rauc_version
425
0
          );
426
0
      return FALSE;
427
0
    }
428
0
    if (!r_semver_less_equal(mf->update_min_rauc_version, RAUC_MESON_VERSION, NULL)) {
429
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
430
0
          "Minimum RAUC version in manifest (%s) is newer than current version (%s)",
431
0
          mf->update_min_rauc_version, RAUC_MESON_VERSION
432
0
          );
433
0
      return FALSE;
434
0
    }
435
0
    if (r_semver_less_equal(mf->update_min_rauc_version, "1.13", NULL)) {
436
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
437
0
          "Minimum RAUC version field in manifest is only supported since 1.14 (not '%s')",
438
0
          mf->update_min_rauc_version
439
0
          );
440
0
      return FALSE;
441
0
    }
442
0
  }
443
444
0
  switch (mf->bundle_format) {
445
0
    case R_MANIFEST_FORMAT_PLAIN:
446
0
      break; /* no additional data needed */
447
0
    case R_MANIFEST_FORMAT_VERITY:
448
0
    case R_MANIFEST_FORMAT_CRYPT:
449
0
      break; /* data checked in _detached/_inline */
450
0
    default: {
451
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unsupported bundle format");
452
0
      return FALSE;
453
0
    }
454
0
  }
455
456
  /* Check for hook file set if hooks are enabled */
457
458
0
  if (mf->hooks.install_check == TRUE)
459
0
    have_hooks = TRUE;
460
461
0
  for (GList *l = mf->images; l != NULL; l = l->next) {
462
0
    RaucImage *image = l->data;
463
0
    if (image->hooks.pre_install == TRUE) {
464
0
      have_hooks = TRUE;
465
0
      break;
466
0
    }
467
0
    if (image->hooks.install == TRUE) {
468
0
      have_hooks = TRUE;
469
0
      break;
470
0
    }
471
0
    if (image->hooks.post_install == TRUE) {
472
0
      have_hooks = TRUE;
473
0
      break;
474
0
    }
475
0
  }
476
477
0
  if (have_hooks && !mf->hook_name) {
478
0
    g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Hooks used, but no hook 'filename' defined in [hooks] section");
479
0
    return FALSE;
480
0
  }
481
482
0
  return TRUE;
483
0
}
484
485
static gboolean check_manifest_plain(const RaucManifest *mf, GError **error)
486
0
{
487
0
  g_return_val_if_fail(mf, FALSE);
488
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
489
490
0
  g_assert(mf->bundle_format == R_MANIFEST_FORMAT_PLAIN);
491
492
0
  for (GList *elem = mf->images; elem != NULL; elem = elem->next) {
493
0
    RaucImage *image = elem->data;
494
495
    /* Check for features not supported in plain bundles */
496
0
    if (image->artifact) {
497
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Artifacts are not supported in plain bundles");
498
0
      return FALSE;
499
0
    }
500
0
    if (image->convert || (image->converted && image->converted->len)) {
501
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Image converters are not supported in plain bundles");
502
0
      return FALSE;
503
0
    }
504
0
  }
505
506
0
  return TRUE;
507
0
}
508
509
/**
510
 * Check a loaded manifest for consistency. Manifests generated by 'rauc bundle'
511
 * should pass this check if they are compatible with the running version.
512
 *
513
 * This function is called for both internal and external manifests.
514
 */
515
static gboolean check_manifest_bundled(const RaucManifest *mf, GError **error)
516
0
{
517
0
  g_return_val_if_fail(mf, FALSE);
518
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
519
520
0
  for (GList *l = mf->images; l != NULL; l = l->next) {
521
0
    RaucImage *image = l->data;
522
523
0
    g_assert(image);
524
525
    /* Having no 'filename' set is valid for 'install' hook only.
526
     * This is already ensured during manifest parsing, thus simply
527
     * skip further checks here */
528
0
    if (!image->filename)
529
0
      continue;
530
531
0
    if (image->checksum.type != G_CHECKSUM_SHA256) {
532
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unsupported checksum algorithm for image %s", image->filename);
533
0
      return FALSE;
534
0
    }
535
0
    if (!image->checksum.digest) {
536
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Missing digest for image %s", image->filename);
537
0
      return FALSE;
538
0
    }
539
0
    if (image->checksum.size < 0) {
540
      /* RAUC versions before v1.5 allowed zero-size images but did not handle this explicitly.
541
       * Thus, bundles created did have a valid 'filename=' manifest entry
542
       * but the 'size=' entry was considered as empty and not set at all.
543
       * Retain support for this case, at least for the 'install' per-slot hook use-case
544
       * where an image file can be optional. */
545
0
      if (image->hooks.install) {
546
0
        g_message("Missing size parameter for image '%s'", image->filename);
547
0
        image->checksum.size = 0;
548
0
      } else {
549
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Missing size for image %s", image->filename);
550
0
        return FALSE;
551
0
      }
552
0
    }
553
554
0
    if (image->convert) {
555
0
      guint expected_len = g_strv_length(image->convert);
556
557
0
      if (!image->converted) {
558
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Missing converted outputs for image %s", image->filename);
559
0
        return FALSE;
560
0
      }
561
562
0
      if (expected_len != image->converted->len) {
563
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Inconsistent number of converted inputs/outputs for image %s", image->filename);
564
0
        return FALSE;
565
0
      }
566
0
    }
567
0
  }
568
569
0
  return TRUE;
570
0
}
571
572
gboolean check_manifest_input(const RaucManifest *mf, GError **error)
573
0
{
574
0
  GError *ierror = NULL;
575
576
0
  g_return_val_if_fail(mf, FALSE);
577
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
578
579
0
  if (!check_manifest_common(mf, &ierror)) {
580
0
    g_propagate_error(error, ierror);
581
0
    return FALSE;
582
0
  }
583
584
0
  switch (mf->bundle_format) {
585
0
    case R_MANIFEST_FORMAT_PLAIN:
586
0
    case R_MANIFEST_FORMAT_CRYPT:
587
0
    case R_MANIFEST_FORMAT_VERITY:
588
0
      break;
589
0
    default: {
590
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unsupported bundle format in input manifest");
591
0
      return FALSE;
592
0
    }
593
0
  }
594
595
0
  if (mf->bundle_format == R_MANIFEST_FORMAT_PLAIN) {
596
0
    if (!check_manifest_plain(mf, &ierror)) {
597
0
      g_propagate_error(error, ierror);
598
0
      return FALSE;
599
0
    }
600
0
  }
601
602
0
  for (GList *l = mf->images; l != NULL; l = l->next) {
603
0
    RaucImage *image = l->data;
604
605
0
    g_assert(image);
606
607
0
    if (image->filename && strchr(image->filename, '/')) {
608
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
609
0
          "Image filename %s must not contain '/'", image->filename);
610
0
      return FALSE;
611
0
    }
612
0
    if (image->checksum.digest) {
613
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
614
0
          "Unexpected digest for image %s in input manifest", image->filename);
615
0
      return FALSE;
616
0
    }
617
0
    if (image->checksum.size != -1) {
618
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
619
0
          "Unexpected size %"G_GOFFSET_FORMAT " for image %s in input manifest", image->checksum.size, image->filename);
620
0
      return FALSE;
621
0
    }
622
0
    if (image->converted && image->converted->len) {
623
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
624
0
          "Unexpected 'converted' option in input manifest");
625
0
      return FALSE;
626
0
    }
627
0
  }
628
629
0
  return TRUE;
630
0
}
631
632
gboolean check_manifest_internal(const RaucManifest *mf, GError **error)
633
0
{
634
0
  GError *ierror = NULL;
635
0
  gboolean res = FALSE;
636
637
0
  g_return_val_if_fail(mf, FALSE);
638
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
639
640
0
  r_context_begin_step("check_manifest", "Checking manifest contents", 0);
641
642
0
  if (!check_manifest_common(mf, &ierror)) {
643
0
    g_propagate_error(error, ierror);
644
0
    goto out;
645
0
  }
646
647
0
  switch (mf->bundle_format) {
648
0
    case R_MANIFEST_FORMAT_PLAIN:
649
0
      break; /* no additional data needed */
650
0
    case R_MANIFEST_FORMAT_CRYPT:
651
0
    case R_MANIFEST_FORMAT_VERITY:
652
0
    default: {
653
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Bundle format '%s' not allowed for internal manifest", r_manifest_bundle_format_to_str(mf->bundle_format));
654
0
      goto out;
655
0
    }
656
0
  }
657
658
0
  if (!check_manifest_bundled(mf, &ierror)) {
659
0
    g_propagate_error(error, ierror);
660
0
    goto out;
661
0
  }
662
663
0
  if (!check_manifest_plain(mf, &ierror)) {
664
0
    g_propagate_error(error, ierror);
665
0
    goto out;
666
0
  }
667
668
0
  if (mf->bundle_crypt_key) {
669
0
    g_set_error_literal(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unexpected key for crypt bundle in internal manifest");
670
0
    goto out;
671
0
  }
672
0
  if (mf->bundle_verity_hash) {
673
0
    g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unexpected verity hash for %s bundle in internal manifest", r_manifest_bundle_format_to_str(mf->bundle_format));
674
0
    goto out;
675
0
  }
676
0
  if (mf->bundle_verity_salt) {
677
0
    g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unexpected verity salt for %s bundle in internal manifest", r_manifest_bundle_format_to_str(mf->bundle_format));
678
0
    goto out;
679
0
  }
680
0
  if (mf->bundle_verity_size) {
681
0
    g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unexpected verity size for %s bundle in internal manifest", r_manifest_bundle_format_to_str(mf->bundle_format));
682
0
    goto out;
683
0
  }
684
685
0
  res = TRUE;
686
0
out:
687
0
  r_context_end_step("check_manifest", res);
688
0
  return res;
689
0
}
690
691
gboolean check_manifest_external(const RaucManifest *mf, GError **error)
692
0
{
693
0
  GError *ierror = NULL;
694
0
  gboolean res = FALSE;
695
0
  const gchar *format = NULL;
696
697
0
  g_return_val_if_fail(mf, FALSE);
698
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
699
700
0
  r_context_begin_step("check_manifest", "Checking manifest contents", 0);
701
702
0
  format = r_manifest_bundle_format_to_str(mf->bundle_format);
703
0
  if (g_strcmp0(format, "invalid") == 0) {
704
0
    g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unsupported bundle format");
705
0
    goto out;
706
0
  }
707
708
0
  if (!check_manifest_common(mf, &ierror)) {
709
0
    g_propagate_error(error, ierror);
710
0
    goto out;
711
0
  }
712
713
0
  switch (mf->bundle_format) {
714
0
    case R_MANIFEST_FORMAT_PLAIN: {
715
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unsupported bundle format 'plain' for external manifest");
716
0
      goto out;
717
0
    }
718
0
    case R_MANIFEST_FORMAT_CRYPT: {
719
0
      guint8 *tmp;
720
721
0
      if (!mf->bundle_crypt_key) {
722
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Missing key for crypt bundle");
723
0
        goto out;
724
0
      }
725
0
      tmp = r_hex_decode(mf->bundle_crypt_key, 32);
726
0
      if (!tmp) {
727
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Invalid key for crypt bundle");
728
0
        goto out;
729
0
      }
730
0
      g_free(tmp);
731
0
    };
732
    /* Fallthrough */
733
0
    case R_MANIFEST_FORMAT_VERITY: {
734
0
      guint8 *tmp;
735
736
0
      if (!mf->bundle_verity_hash) {
737
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Missing hash for %s bundle", format);
738
0
        goto out;
739
0
      }
740
0
      tmp = r_hex_decode(mf->bundle_verity_hash, 32);
741
0
      if (!tmp) {
742
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Invalid hash for %s bundle", format);
743
0
        goto out;
744
0
      }
745
0
      g_free(tmp);
746
747
0
      if (!mf->bundle_verity_salt) {
748
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Missing salt for %s bundle", format);
749
0
        goto out;
750
0
      }
751
0
      tmp = r_hex_decode(mf->bundle_verity_salt, 32);
752
0
      if (!tmp) {
753
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Invalid salt for %s bundle", format);
754
0
        goto out;
755
0
      }
756
0
      g_free(tmp);
757
758
0
      if (!mf->bundle_verity_size) {
759
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Missing size for %s bundle", format);
760
0
        goto out;
761
0
      }
762
763
0
      if (mf->bundle_verity_size % 4096) {
764
0
        g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR, "Unaligned size for %s bundle", format);
765
0
        goto out;
766
0
      }
767
768
0
      break;
769
0
    };
770
0
    default: {
771
      /* should not be reached as this is checked before */
772
0
      g_error("Unsupported bundle format");
773
0
      goto out;
774
0
    }
775
0
  }
776
777
0
  if (!check_manifest_bundled(mf, &ierror)) {
778
0
    g_propagate_error(error, ierror);
779
0
    goto out;
780
0
  }
781
782
0
  res = TRUE;
783
0
out:
784
0
  r_context_end_step("check_manifest", res);
785
0
  return res;
786
0
}
787
788
gboolean check_manifest_create(const RaucManifest *mf, GError **error)
789
0
{
790
0
  g_return_val_if_fail(mf, FALSE);
791
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
792
793
0
  for (GList *l = mf->images; l != NULL; l = l->next) {
794
0
    RaucImage *image = l->data;
795
796
0
    g_assert(image);
797
798
0
    if (image->hooks.install && (image->hooks.pre_install || image->hooks.post_install)) {
799
0
      g_set_error_literal(error, R_MANIFEST_ERROR, R_MANIFEST_CHECK_ERROR,
800
0
          "An 'install' hook must not be combined with 'pre-install' or 'post-install' hooks");
801
0
      return FALSE;
802
0
    }
803
0
  }
804
805
0
  return TRUE;
806
0
}
807
808
static GKeyFile *prepare_manifest(const RaucManifest *mf)
809
0
{
810
0
  g_autoptr(GKeyFile) key_file = NULL;
811
0
  GPtrArray *hooks = g_ptr_array_new_full(3, g_free);
812
0
  GHashTableIter iter;
813
0
  GHashTable *meta_kvs;
814
0
  const gchar *meta_group;
815
816
0
  g_return_val_if_fail(mf, FALSE);
817
818
0
  key_file = g_key_file_new();
819
820
0
  if (mf->update_compatible)
821
0
    g_key_file_set_string(key_file, "update", "compatible", mf->update_compatible);
822
823
0
  if (mf->update_version)
824
0
    g_key_file_set_string(key_file, "update", "version", mf->update_version);
825
826
0
  if (mf->update_description)
827
0
    g_key_file_set_string(key_file, "update", "description", mf->update_description);
828
829
0
  if (mf->update_build)
830
0
    g_key_file_set_string(key_file, "update", "build", mf->update_build);
831
832
0
  switch (mf->bundle_format) {
833
0
    case R_MANIFEST_FORMAT_PLAIN:
834
0
      if (mf->bundle_format_explicit)
835
0
        g_key_file_set_string(key_file, "bundle", "format", "plain");
836
0
      break;
837
0
    case R_MANIFEST_FORMAT_CRYPT: {
838
0
      if (mf->bundle_crypt_key)
839
0
        g_key_file_set_string(key_file, "bundle", "crypt-key", mf->bundle_crypt_key);
840
0
    };
841
    /* Fallthrough */
842
0
    case R_MANIFEST_FORMAT_VERITY: {
843
0
      g_key_file_set_string(key_file, "bundle", "format", r_manifest_bundle_format_to_str(mf->bundle_format));
844
0
      if (mf->bundle_verity_hash)
845
0
        g_key_file_set_string(key_file, "bundle", "verity-hash", mf->bundle_verity_hash);
846
0
      if (mf->bundle_verity_salt)
847
0
        g_key_file_set_string(key_file, "bundle", "verity-salt", mf->bundle_verity_salt);
848
0
      if (mf->bundle_verity_size)
849
0
        g_key_file_set_uint64(key_file, "bundle", "verity-size", mf->bundle_verity_size);
850
851
0
      break;
852
0
    };
853
0
    default:
854
0
      break;
855
0
  }
856
857
0
  if (mf->handler_name)
858
0
    g_key_file_set_string(key_file, "handler", "filename", mf->handler_name);
859
860
0
  if (mf->handler_args)
861
0
    g_key_file_set_string(key_file, "handler", "args", mf->handler_args);
862
863
0
  if (mf->hook_name)
864
0
    g_key_file_set_string(key_file, "hooks", "filename", mf->hook_name);
865
866
0
  if (mf->hooks.install_check == TRUE) {
867
0
    g_ptr_array_add(hooks, g_strdup("install-check"));
868
0
  }
869
0
  g_ptr_array_add(hooks, NULL);
870
0
  if (hooks->pdata && *hooks->pdata) {
871
0
    g_key_file_set_string_list(key_file, "hooks", "hooks",
872
0
        (const gchar **)hooks->pdata, hooks->len);
873
0
  }
874
0
  g_ptr_array_unref(hooks);
875
876
0
  for (GList *l = mf->images; l != NULL; l = l->next) {
877
0
    g_autoptr(GPtrArray) hooklist = g_ptr_array_new_full(3, g_free);
878
0
    RaucImage *image = l->data;
879
0
    g_autofree gchar *group = NULL;
880
881
0
    if (!image || !image->slotclass)
882
0
      continue;
883
884
0
    group = g_strconcat(RAUC_IMAGE_PREFIX ".", image->slotclass, NULL);
885
886
0
    if (image->artifact) {
887
0
      gchar *tmp = group;
888
0
      group = g_strconcat(group, "/", image->artifact, NULL);
889
0
      g_free(tmp);
890
0
    }
891
892
0
    if (image->variant) {
893
0
      gchar *tmp = group;
894
0
      group = g_strconcat(group, ".", image->variant, NULL);
895
0
      g_free(tmp);
896
0
    }
897
898
0
    if (image->checksum.type == G_CHECKSUM_SHA256)
899
0
      g_key_file_set_string(key_file, group, "sha256", image->checksum.digest);
900
0
    if (image->checksum.size >= 0)
901
0
      g_key_file_set_uint64(key_file, group, "size", image->checksum.size);
902
903
0
    if (image->filename)
904
0
      g_key_file_set_string(key_file, group, "filename", image->filename);
905
906
0
    if (image->hooks.pre_install == TRUE) {
907
0
      g_ptr_array_add(hooklist, g_strdup("pre-install"));
908
0
    }
909
0
    if (image->hooks.install == TRUE) {
910
0
      g_ptr_array_add(hooklist, g_strdup("install"));
911
0
    }
912
0
    if (image->hooks.post_install == TRUE) {
913
0
      g_ptr_array_add(hooklist, g_strdup("post-install"));
914
0
    }
915
0
    g_ptr_array_add(hooklist, NULL);
916
917
0
    if (hooklist->pdata && *hooklist->pdata) {
918
0
      g_key_file_set_string_list(key_file, group, "hooks",
919
0
          (const gchar **)hooklist->pdata, hooklist->len);
920
0
    }
921
922
0
    if (image->adaptive)
923
0
      g_key_file_set_string_list(key_file, group, "adaptive",
924
0
          (const gchar * const *)image->adaptive, g_strv_length(image->adaptive));
925
926
0
    if (image->convert)
927
0
      g_key_file_set_string_list(key_file, group, "convert",
928
0
          (const gchar * const *)image->convert, g_strv_length(image->convert));
929
0
    if (image->converted && image->converted->len)
930
0
      g_key_file_set_string_list(key_file, group, "converted",
931
0
          (const gchar * const *)image->converted->pdata, image->converted->len);
932
0
  }
933
934
0
  if (mf->meta) {
935
0
    g_hash_table_iter_init(&iter, mf->meta);
936
0
    while (g_hash_table_iter_next(&iter, (gpointer*)&meta_group, (gpointer*)&meta_kvs)) {
937
0
      GHashTableIter kvs_iter;
938
0
      const gchar *key, *value;
939
940
0
      g_hash_table_iter_init(&kvs_iter, meta_kvs);
941
0
      while (g_hash_table_iter_next(&kvs_iter, (gpointer*)&key, (gpointer*)&value)) {
942
0
        g_autofree gchar *group = g_strdup_printf("meta.%s", meta_group);
943
0
        g_key_file_set_string(key_file, group, key, value);
944
0
      }
945
0
    }
946
0
  }
947
948
0
  return g_steal_pointer(&key_file);
949
0
}
950
951
gboolean save_manifest_mem(GBytes **mem, const RaucManifest *mf)
952
0
{
953
0
  g_autoptr(GKeyFile) key_file = NULL;
954
0
  guint8 *data = NULL;
955
0
  gsize length = 0;
956
957
0
  g_return_val_if_fail(mem != NULL && *mem == NULL, FALSE);
958
0
  g_return_val_if_fail(mf != NULL, FALSE);
959
960
0
  key_file = prepare_manifest(mf);
961
962
  /* according to the docs, this never fails */
963
0
  data = (guint8*)g_key_file_to_data(key_file, &length, NULL);
964
0
  g_assert(data != NULL);
965
0
  g_assert(length > 0);
966
967
0
  *mem = g_bytes_new_take(data, length);
968
969
0
  return TRUE;
970
0
}
971
972
gboolean save_manifest_file(const gchar *filename, const RaucManifest *mf, GError **error)
973
0
{
974
0
  GError *ierror = NULL;
975
0
  g_autoptr(GKeyFile) key_file = NULL;
976
0
  gboolean res = FALSE;
977
978
0
  g_return_val_if_fail(filename, FALSE);
979
0
  g_return_val_if_fail(mf, FALSE);
980
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
981
982
0
  key_file = prepare_manifest(mf);
983
984
0
  res = g_key_file_save_to_file(key_file, filename, &ierror);
985
0
  if (!res)
986
0
    g_propagate_error(error, ierror);
987
988
0
  return res;
989
0
}
990
991
GVariant* r_manifest_to_dict(const RaucManifest *manifest)
992
0
{
993
0
  GVariantDict root_dict;
994
0
  GVariantDict grp_dict;
995
0
  g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE_ARRAY);
996
0
  GHashTableIter iter;
997
0
  GHashTable *kvs;
998
0
  const gchar *group;
999
1000
0
  g_return_val_if_fail(manifest, NULL);
1001
1002
0
  g_variant_dict_init(&root_dict, NULL);
1003
1004
0
  if (manifest->hash)
1005
0
    g_variant_dict_insert(&root_dict, "manifest-hash", "s", manifest->hash);
1006
1007
  /* construct 'update' dict */
1008
0
  g_variant_dict_init(&grp_dict, NULL);
1009
0
  if (manifest->update_compatible)
1010
0
    g_variant_dict_insert(&grp_dict, "compatible", "s", manifest->update_compatible);
1011
0
  if (manifest->update_version)
1012
0
    g_variant_dict_insert(&grp_dict, "version", "s", manifest->update_version);
1013
0
  if (manifest->update_description)
1014
0
    g_variant_dict_insert(&grp_dict, "description", "s", manifest->update_description);
1015
0
  if (manifest->update_build)
1016
0
    g_variant_dict_insert(&grp_dict, "build", "s", manifest->update_build);
1017
0
  g_variant_dict_insert(&root_dict, "update", "v", g_variant_dict_end(&grp_dict));
1018
1019
  /* construct 'bundle' dict */
1020
0
  g_variant_dict_init(&grp_dict, NULL);
1021
0
  g_variant_dict_insert(&grp_dict, "format", "s", r_manifest_bundle_format_to_str(manifest->bundle_format));
1022
1023
0
  if (manifest->bundle_verity_hash)
1024
0
    g_variant_dict_insert(&grp_dict, "verity-hash", "s", manifest->bundle_verity_hash);
1025
0
  if (manifest->bundle_verity_salt)
1026
0
    g_variant_dict_insert(&grp_dict, "verity-salt", "s", manifest->bundle_verity_salt);
1027
0
  if (manifest->bundle_verity_size)
1028
0
    g_variant_dict_insert(&grp_dict, "verity-size", "t", manifest->bundle_verity_size);
1029
0
  g_variant_dict_insert(&root_dict, "bundle", "v", g_variant_dict_end(&grp_dict));
1030
1031
  /* construct 'hooks' dict */
1032
0
  if (manifest->hook_name) {
1033
0
    g_variant_dict_init(&grp_dict, NULL);
1034
0
    if (manifest->hook_name)
1035
0
      g_variant_dict_insert(&grp_dict, "filename", "s", manifest->hook_name);
1036
0
    g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1037
0
    if (manifest->hooks.install_check)
1038
0
      g_variant_builder_add(&builder, "s", "install-check");
1039
0
    g_variant_dict_insert(&grp_dict, "hooks", "v", g_variant_builder_end(&builder));
1040
0
    g_variant_dict_insert(&root_dict, "hooks", "v", g_variant_dict_end(&grp_dict));
1041
0
  }
1042
1043
  /* construct 'handler' dict */
1044
0
  if (manifest->handler_name) {
1045
0
    g_variant_dict_init(&grp_dict, NULL);
1046
0
    if (manifest->handler_name)
1047
0
      g_variant_dict_insert(&grp_dict, "filename", "s", manifest->handler_name);
1048
0
    if (manifest->handler_args)
1049
0
      g_variant_dict_insert(&grp_dict, "args", "s", manifest->handler_args);
1050
0
    g_variant_dict_insert(&root_dict, "handler", "v", g_variant_dict_end(&grp_dict));
1051
0
  }
1052
1053
  /* construct 'images' array of dicts */
1054
0
  g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
1055
0
  for (GList *l = manifest->images; l != NULL; l = l->next) {
1056
0
    const RaucImage *img = l->data;
1057
0
    g_auto(GVariantBuilder) hooks = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("as"));
1058
1059
0
    g_variant_builder_open(&builder, G_VARIANT_TYPE("a{sv}"));
1060
0
    g_variant_builder_add(&builder, "{sv}", "slot-class", g_variant_new_string(img->slotclass));
1061
0
    if (img->artifact)
1062
0
      g_variant_builder_add(&builder, "{sv}", "artifact", g_variant_new_string(img->artifact));
1063
0
    if (img->variant)
1064
0
      g_variant_builder_add(&builder, "{sv}", "variant", g_variant_new_string(img->variant));
1065
0
    if (img->filename)
1066
0
      g_variant_builder_add(&builder, "{sv}", "filename", g_variant_new_string(img->filename));
1067
0
    if (img->checksum.digest)
1068
0
      g_variant_builder_add(&builder, "{sv}", "checksum", g_variant_new_string(img->checksum.digest));
1069
0
    if (img->checksum.size)
1070
0
      g_variant_builder_add(&builder, "{sv}", "size", g_variant_new_uint64(img->checksum.size));
1071
1072
0
    if (img->hooks.pre_install)
1073
0
      g_variant_builder_add(&hooks, "s", "pre-install");
1074
0
    if (img->hooks.install)
1075
0
      g_variant_builder_add(&hooks, "s", "install");
1076
0
    if (img->hooks.post_install)
1077
0
      g_variant_builder_add(&hooks, "s", "post-install");
1078
0
    g_variant_builder_add(&builder, "{sv}", "hooks", g_variant_builder_end(&hooks));
1079
1080
0
    if (img->adaptive)
1081
0
      g_variant_builder_add(&builder, "{sv}", "adaptive", g_variant_new_strv((const gchar * const*)(img->adaptive), -1));
1082
1083
0
    if (img->convert)
1084
0
      g_variant_builder_add(&builder, "{sv}", "convert", g_variant_new_strv((const gchar * const*)(img->convert), -1));
1085
0
    if (img->converted)
1086
0
      g_variant_builder_add(&builder, "{sv}", "converted", g_variant_new_strv((const gchar * const*)(img->converted->pdata), img->converted->len));
1087
1088
0
    g_variant_builder_close(&builder);
1089
0
  }
1090
0
  g_variant_dict_insert(&root_dict, "images", "v", g_variant_builder_end(&builder));
1091
1092
  /* construct 'meta' nested dicts */
1093
0
  g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sa{ss}}"));
1094
0
  g_hash_table_iter_init(&iter, manifest->meta);
1095
0
  while (g_hash_table_iter_next(&iter, (gpointer*)&group, (gpointer*)&kvs)) {
1096
0
    GHashTableIter kvs_iter;
1097
0
    const gchar *key, *value;
1098
1099
0
    g_variant_builder_open(&builder, G_VARIANT_TYPE("{sa{ss}}"));
1100
0
    g_variant_builder_add(&builder, "s", group);
1101
1102
0
    g_variant_builder_open(&builder, G_VARIANT_TYPE("a{ss}"));
1103
0
    g_hash_table_iter_init(&kvs_iter, kvs);
1104
0
    while (g_hash_table_iter_next(&kvs_iter, (gpointer*)&key, (gpointer*)&value)) {
1105
0
      g_variant_builder_open(&builder, G_VARIANT_TYPE("{ss}"));
1106
0
      g_variant_builder_add(&builder, "s", key);
1107
0
      g_variant_builder_add(&builder, "s", value);
1108
0
      g_variant_builder_close(&builder);
1109
0
    }
1110
0
    g_variant_builder_close(&builder);
1111
1112
0
    g_variant_builder_close(&builder);
1113
0
  }
1114
0
  g_variant_dict_insert(&root_dict, "meta", "v", g_variant_builder_end(&builder));
1115
1116
0
  return g_variant_dict_end(&root_dict);
1117
0
}
1118
1119
gboolean r_manifest_has_artifact_image(const RaucManifest *manifest, const gchar *repo, const gchar *artifact)
1120
0
{
1121
0
  g_return_val_if_fail(manifest != NULL, FALSE);
1122
0
  g_return_val_if_fail((!repo && !artifact) ||
1123
0
      (repo && !artifact) ||
1124
0
      (repo && artifact), FALSE);
1125
1126
0
  for (GList *l = manifest->images; l != NULL; l = l->next) {
1127
0
    const RaucImage *img = l->data;
1128
1129
    /* skip images which are not an artifact */
1130
0
    if (!img->artifact)
1131
0
      continue;
1132
1133
    /* skip images for different repos */
1134
0
    if (repo && g_strcmp0(repo, img->slotclass) != 0)
1135
0
      continue;
1136
1137
    /* skip images with different artifact names */
1138
0
    if (artifact && g_strcmp0(artifact, img->artifact) != 0)
1139
0
      continue;
1140
1141
0
    return TRUE;
1142
0
  }
1143
1144
0
  return FALSE;
1145
0
}
1146
1147
RaucImage *r_new_image(void)
1148
9.91k
{
1149
9.91k
  RaucImage *image = g_new0(RaucImage, 1);
1150
1151
9.91k
  image->checksum.size = -1;
1152
1153
9.91k
  return image;
1154
9.91k
}
1155
1156
void r_free_image(gpointer data)
1157
9.91k
{
1158
9.91k
  RaucImage *image = (RaucImage*) data;
1159
1160
9.91k
  if (!image)
1161
0
    return;
1162
1163
9.91k
  g_free(image->slotclass);
1164
9.91k
  g_free(image->artifact);
1165
9.91k
  g_free(image->variant);
1166
9.91k
  g_free(image->checksum.digest);
1167
9.91k
  g_free(image->filename);
1168
9.91k
  g_strfreev(image->adaptive);
1169
9.91k
  g_strfreev(image->convert);
1170
9.91k
  g_clear_pointer(&image->converted, g_ptr_array_unref);
1171
9.91k
  g_free(image);
1172
9.91k
}
1173
1174
void free_manifest(RaucManifest *manifest)
1175
3.06k
{
1176
3.06k
  if (!manifest)
1177
0
    return;
1178
1179
3.06k
  g_free(manifest->update_compatible);
1180
3.06k
  g_free(manifest->update_version);
1181
3.06k
  g_free(manifest->update_description);
1182
3.06k
  g_free(manifest->update_build);
1183
3.06k
  g_free(manifest->update_min_rauc_version);
1184
3.06k
  g_free(manifest->bundle_verity_hash);
1185
3.06k
  g_free(manifest->bundle_verity_salt);
1186
3.06k
  g_free(manifest->bundle_crypt_key);
1187
3.06k
  g_free(manifest->handler_name);
1188
3.06k
  g_free(manifest->handler_args);
1189
3.06k
  g_free(manifest->hook_name);
1190
3.06k
  g_list_free_full(manifest->images, r_free_image);
1191
3.06k
  g_clear_pointer(&manifest->meta, g_hash_table_destroy);
1192
3.06k
  g_free(manifest->hash);
1193
3.06k
  g_clear_pointer(&manifest->warnings, g_ptr_array_unref);
1194
3.06k
  g_free(manifest);
1195
3.06k
}
1196
1197
/**
1198
 * Updates checksums for images listed in the manifest and found in
1199
 * the bundle directory.
1200
 *
1201
 * @param manifest pointer to the manifest
1202
 * @param dir Directory with the bundle content
1203
 * @param error return location for a GError, or NULL
1204
 *
1205
 * @return TRUE on success, FALSE if an error occurred
1206
 */
1207
static gboolean update_manifest_checksums(RaucManifest *manifest, const gchar *dir, GError **error)
1208
0
{
1209
0
  GError *ierror = NULL;
1210
0
  gboolean res = TRUE;
1211
0
  gboolean had_errors = FALSE;
1212
1213
0
  g_return_val_if_fail(manifest, FALSE);
1214
0
  g_return_val_if_fail(dir, FALSE);
1215
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1216
1217
0
  for (GList *elem = manifest->images; elem != NULL; elem = elem->next) {
1218
0
    RaucImage *image = elem->data;
1219
0
    g_autofree gchar *filename = NULL;
1220
1221
    /* If no filename is set (valid for 'install' hook) explicitly set size to -1 */
1222
0
    if (!image->filename) {
1223
0
      image->checksum.size = -1;
1224
0
      continue;
1225
0
    }
1226
1227
0
    filename = g_build_filename(dir, image->filename, NULL);
1228
0
    res = compute_checksum(&image->checksum, filename, &ierror);
1229
0
    if (!res) {
1230
0
      g_warning("Failed updating checksum: %s", ierror->message);
1231
0
      g_clear_error(&ierror);
1232
0
      had_errors = TRUE;
1233
0
      break;
1234
0
    }
1235
0
  }
1236
1237
0
  if (had_errors) {
1238
0
    res = FALSE;
1239
0
    g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_ERROR_CHECKSUM, "Failed updating all checksums");
1240
0
  }
1241
1242
0
  return res;
1243
0
}
1244
1245
gboolean sync_manifest_with_contentdir(RaucManifest *manifest, const gchar *dir, GError **error)
1246
0
{
1247
0
  g_return_val_if_fail(manifest, FALSE);
1248
0
  g_return_val_if_fail(dir, FALSE);
1249
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1250
1251
  /* Check for missing image files */
1252
0
  for (GList *elem = manifest->images; elem != NULL; elem = elem->next) {
1253
0
    RaucImage *image = elem->data;
1254
0
    g_autofree gchar *filename = g_build_filename(dir, image->filename, NULL);
1255
0
    if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
1256
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_ERROR_CHECKSUM, "image file '%s' for slot '%s' does not exist in bundle content dir (%s)", image->filename, image->slotclass, dir);
1257
0
      return FALSE;
1258
0
    }
1259
0
  }
1260
1261
  /* Check for missing hook file */
1262
0
  if (manifest->hook_name) {
1263
0
    g_autofree gchar *hookpath = NULL;
1264
0
    hookpath = g_build_filename(dir, manifest->hook_name, NULL);
1265
0
    if (!g_file_test(hookpath, G_FILE_TEST_EXISTS)) {
1266
0
      g_set_error(error, R_MANIFEST_ERROR, R_MANIFEST_ERROR_CHECKSUM, "hook file '%s' does not exist in bundle content dir (%s)", manifest->hook_name, dir);
1267
0
      return FALSE;
1268
0
    }
1269
0
  }
1270
1271
0
  return update_manifest_checksums(manifest, dir, error);
1272
0
}