Coverage Report

Created: 2026-01-25 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-io-channel.c
Line
Count
Source
1
/*
2
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuIOChannel"
8
9
#include "config.h"
10
11
#include <errno.h>
12
#include <fcntl.h>
13
#include <gio/gio.h>
14
#include <glib/gstdio.h>
15
#ifdef HAVE_POLL_H
16
#include <poll.h>
17
#endif
18
#include <string.h>
19
#include <sys/stat.h>
20
#ifdef HAVE_MEMFD_CREATE
21
#include <sys/mman.h>
22
#endif
23
24
#include "fwupd-error.h"
25
26
#include "fu-input-stream.h"
27
#include "fu-io-channel.h"
28
29
/**
30
 * FuIOChannel:
31
 *
32
 * A bidirectional IO channel which can be read from and written to.
33
 */
34
35
struct _FuIOChannel {
36
  GObject parent_instance;
37
  gint fd;
38
};
39
40
0
G_DEFINE_TYPE(FuIOChannel, fu_io_channel, G_TYPE_OBJECT)
41
0
42
0
/**
43
0
 * fu_io_channel_unix_get_fd:
44
0
 * @self: a #FuIOChannel
45
0
 *
46
0
 * Gets the file descriptor for the device.
47
0
 *
48
0
 * Returns: fd, or -1 for not open.
49
0
 *
50
0
 * Since: 1.2.2
51
0
 **/
52
0
gint
53
0
fu_io_channel_unix_get_fd(FuIOChannel *self)
54
0
{
55
0
  g_return_val_if_fail(FU_IS_IO_CHANNEL(self), -1);
56
0
  return self->fd;
57
0
}
58
59
/**
60
 * fu_io_channel_shutdown:
61
 * @self: a #FuIOChannel
62
 * @error: (nullable): optional return location for an error
63
 *
64
 * Closes the file descriptor for the device if open.
65
 *
66
 * Returns: %TRUE if all the FD was closed.
67
 *
68
 * Since: 1.2.2
69
 **/
70
gboolean
71
fu_io_channel_shutdown(FuIOChannel *self, GError **error)
72
0
{
73
0
  g_return_val_if_fail(FU_IS_IO_CHANNEL(self), FALSE);
74
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
75
76
0
  if (self->fd != -1) {
77
0
    if (!g_close(self->fd, error))
78
0
      return FALSE;
79
0
    self->fd = -1;
80
0
  }
81
0
  return TRUE;
82
0
}
83
84
/**
85
 * fu_io_channel_seek:
86
 * @self: a #FuIOChannel
87
 * @offset: an absolute offset in bytes
88
 * @error: (nullable): optional return location for an error
89
 *
90
 * Seeks the file descriptor to a specific offset.
91
 *
92
 * Returns: %TRUE if all the seek worked.
93
 *
94
 * Since: 2.0.0
95
 **/
96
gboolean
97
fu_io_channel_seek(FuIOChannel *self, gsize offset, GError **error)
98
0
{
99
0
  g_return_val_if_fail(FU_IS_IO_CHANNEL(self), FALSE);
100
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
101
102
0
  if (self->fd == -1) {
103
0
    g_set_error_literal(error,
104
0
            FWUPD_ERROR,
105
0
            FWUPD_ERROR_NOT_SUPPORTED,
106
0
            "channel is not open");
107
0
    return FALSE;
108
0
  }
109
0
  if (lseek(self->fd, offset, SEEK_SET) < 0) {
110
0
    g_set_error(error,
111
0
          G_IO_ERROR, /* nocheck:error */
112
#ifdef HAVE_ERRNO_H
113
          g_io_error_from_errno(errno),
114
#else
115
0
          G_IO_ERROR_FAILED, /* nocheck:blocked */
116
0
#endif
117
0
          "failed to seek to 0x%04x: %s",
118
0
          (guint)offset,
119
0
          fwupd_strerror(errno));
120
0
    fwupd_error_convert(error);
121
0
    return FALSE;
122
0
  }
123
124
  /* success */
125
0
  return TRUE;
126
0
}
127
128
static gboolean
129
fu_io_channel_flush_input(FuIOChannel *self, GError **error)
130
0
{
131
0
  GPollFD poll = {
132
0
      .fd = self->fd,
133
0
      .events = G_IO_IN | G_IO_ERR,
134
0
  };
135
0
  while (g_poll(&poll, 1, 0) > 0) {
136
0
    gchar c;
137
0
    gint r = read(self->fd, &c, 1);
138
0
    if (r < 0 && errno != EINTR)
139
0
      break;
140
0
  }
141
0
  return TRUE;
142
0
}
143
144
/**
145
 * fu_io_channel_write_bytes:
146
 * @self: a #FuIOChannel
147
 * @bytes: buffer to write
148
 * @timeout_ms: timeout in ms
149
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
150
 * @error: (nullable): optional return location for an error
151
 *
152
 * Writes bytes to the TTY, that will fail if exceeding @timeout_ms.
153
 *
154
 * Returns: %TRUE if all the bytes was written
155
 *
156
 * Since: 1.2.2
157
 **/
158
gboolean
159
fu_io_channel_write_bytes(FuIOChannel *self,
160
        GBytes *bytes,
161
        guint timeout_ms,
162
        FuIoChannelFlags flags,
163
        GError **error)
164
0
{
165
0
  gsize bufsz = 0;
166
0
  const guint8 *buf = g_bytes_get_data(bytes, &bufsz);
167
0
  return fu_io_channel_write_raw(self, buf, bufsz, timeout_ms, flags, error);
168
0
}
169
170
typedef struct {
171
  FuIOChannel *self;
172
  guint timeout_ms;
173
  FuIoChannelFlags flags;
174
} FuIOChannelWriteStreamHelper;
175
176
static gboolean
177
fu_io_channel_write_stream_cb(const guint8 *buf, gsize bufsz, gpointer user_data, GError **error)
178
0
{
179
0
  FuIOChannelWriteStreamHelper *helper = (FuIOChannelWriteStreamHelper *)user_data;
180
0
  return fu_io_channel_write_raw(helper->self,
181
0
               buf,
182
0
               bufsz,
183
0
               helper->timeout_ms,
184
0
               helper->flags,
185
0
               error);
186
0
}
187
188
/**
189
 * fu_io_channel_write_stream:
190
 * @self: a #FuIOChannel
191
 * @stream: #GInputStream to write
192
 * @timeout_ms: timeout in ms
193
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
194
 * @error: (nullable): optional return location for an error
195
 *
196
 * Writes the stream to the fd, chucking when required.
197
 *
198
 * Returns: %TRUE if all the bytes was written
199
 *
200
 * Since: 2.0.0
201
 **/
202
gboolean
203
fu_io_channel_write_stream(FuIOChannel *self,
204
         GInputStream *stream,
205
         guint timeout_ms,
206
         FuIoChannelFlags flags,
207
         GError **error)
208
0
{
209
0
  FuIOChannelWriteStreamHelper helper = {.self = self,
210
0
                 .timeout_ms = timeout_ms,
211
0
                 .flags = flags};
212
0
  g_return_val_if_fail(FU_IS_IO_CHANNEL(self), FALSE);
213
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
214
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
215
0
  return fu_input_stream_chunkify(stream, fu_io_channel_write_stream_cb, &helper, error);
216
0
}
217
218
/**
219
 * fu_io_channel_write_byte_array:
220
 * @self: a #FuIOChannel
221
 * @buf: buffer to write
222
 * @timeout_ms: timeout in ms
223
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
224
 * @error: (nullable): optional return location for an error
225
 *
226
 * Writes bytes to the TTY, that will fail if exceeding @timeout_ms.
227
 *
228
 * Returns: %TRUE if all the bytes was written
229
 *
230
 * Since: 1.3.2
231
 **/
232
gboolean
233
fu_io_channel_write_byte_array(FuIOChannel *self,
234
             GByteArray *buf,
235
             guint timeout_ms,
236
             FuIoChannelFlags flags,
237
             GError **error)
238
0
{
239
0
  return fu_io_channel_write_raw(self, buf->data, buf->len, timeout_ms, flags, error);
240
0
}
241
242
/**
243
 * fu_io_channel_write_raw:
244
 * @self: a #FuIOChannel
245
 * @data: buffer to write
246
 * @datasz: size of @data
247
 * @timeout_ms: timeout in ms
248
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
249
 * @error: (nullable): optional return location for an error
250
 *
251
 * Writes bytes to the TTY, that will fail if exceeding @timeout_ms.
252
 *
253
 * Returns: %TRUE if all the bytes was written
254
 *
255
 * Since: 1.2.2
256
 **/
257
gboolean
258
fu_io_channel_write_raw(FuIOChannel *self,
259
      const guint8 *data,
260
      gsize datasz,
261
      guint timeout_ms,
262
      FuIoChannelFlags flags,
263
      GError **error)
264
0
{
265
0
  gsize idx = 0;
266
267
0
  g_return_val_if_fail(FU_IS_IO_CHANNEL(self), FALSE);
268
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
269
270
  /* flush pending reads */
271
0
  if (flags & FU_IO_CHANNEL_FLAG_FLUSH_INPUT) {
272
0
    if (!fu_io_channel_flush_input(self, error))
273
0
      return FALSE;
274
0
  }
275
276
  /* blocking IO */
277
0
  if (flags & FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO) {
278
0
    gssize wrote = write(self->fd, data, datasz);
279
0
    if (wrote != (gssize)datasz) {
280
0
      if (errno == EPROTO) {
281
0
        g_set_error(error,
282
0
              FWUPD_ERROR,
283
0
              FWUPD_ERROR_NOT_FOUND,
284
0
              "failed to write: %s",
285
0
              fwupd_strerror(errno));
286
0
        return FALSE;
287
0
      }
288
0
      g_set_error(error,
289
0
            FWUPD_ERROR,
290
0
            FWUPD_ERROR_WRITE,
291
0
            "failed to write: "
292
0
            "wrote %" G_GSSIZE_FORMAT " of %" G_GSIZE_FORMAT,
293
0
            wrote,
294
0
            datasz);
295
0
      return FALSE;
296
0
    }
297
0
    return TRUE;
298
0
  }
299
300
  /* nonblocking IO */
301
0
  while (idx < datasz) {
302
0
    gint rc;
303
0
    GPollFD fds = {
304
0
        .fd = self->fd,
305
0
        .events = G_IO_OUT | G_IO_ERR,
306
0
    };
307
308
    /* wait for data to be allowed to write without blocking */
309
0
    rc = g_poll(&fds, 1, (gint)timeout_ms);
310
0
    if (rc == 0)
311
0
      break;
312
0
    if (rc < 0) {
313
0
      g_set_error(error,
314
0
            FWUPD_ERROR,
315
0
            FWUPD_ERROR_READ,
316
0
            "failed to poll %i",
317
0
            self->fd);
318
0
      return FALSE;
319
0
    }
320
321
    /* we can write data */
322
0
    if (fds.revents & G_IO_OUT) {
323
0
      gssize len = write(self->fd, data + idx, datasz - idx);
324
0
      if (len < 0) {
325
0
        if (errno == EAGAIN) {
326
0
          g_debug("got EAGAIN, trying harder");
327
0
          continue;
328
0
        }
329
0
        if (errno == EPROTO) {
330
0
          g_set_error(error,
331
0
                FWUPD_ERROR,
332
0
                FWUPD_ERROR_NOT_FOUND,
333
0
                "failed to write: %s",
334
0
                fwupd_strerror(errno));
335
0
          return FALSE;
336
0
        }
337
0
        g_set_error(error,
338
0
              FWUPD_ERROR,
339
0
              FWUPD_ERROR_WRITE,
340
0
              "failed to write %" G_GSIZE_FORMAT " bytes to %i: %s",
341
0
              datasz,
342
0
              self->fd,
343
0
              fwupd_strerror(errno));
344
0
        return FALSE;
345
0
      }
346
0
      if (flags & FU_IO_CHANNEL_FLAG_SINGLE_SHOT)
347
0
        break;
348
0
      idx += len;
349
0
    }
350
0
  }
351
352
0
  return TRUE;
353
0
}
354
355
/**
356
 * fu_io_channel_read_bytes:
357
 * @self: a #FuIOChannel
358
 * @count: number of bytes to read, or -1 for no limit
359
 * @timeout_ms: timeout in ms
360
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
361
 * @error: (nullable): optional return location for an error
362
 *
363
 * Reads bytes from the TTY, that will fail if exceeding @timeout_ms.
364
 *
365
 * Returns: a #GBytes (which may be bigger than @count), or %NULL for error
366
 *
367
 * Since: 1.2.2
368
 **/
369
GBytes *
370
fu_io_channel_read_bytes(FuIOChannel *self,
371
       gssize count,
372
       guint timeout_ms,
373
       FuIoChannelFlags flags,
374
       GError **error)
375
0
{
376
0
  g_autoptr(GByteArray) buf =
377
0
      fu_io_channel_read_byte_array(self, count, timeout_ms, flags, error);
378
0
  if (buf == NULL)
379
0
    return NULL;
380
0
  return g_bytes_new(buf->data, buf->len);
381
0
}
382
383
/**
384
 * fu_io_channel_read_byte_array:
385
 * @self: a #FuIOChannel
386
 * @count: number of bytes to read, or -1 for no limit
387
 * @timeout_ms: timeout in ms
388
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
389
 * @error: (nullable): optional return location for an error
390
 *
391
 * Reads bytes from the TTY, that will fail if exceeding @timeout_ms.
392
 *
393
 * Returns: (transfer full): a #GByteArray (which may be bigger than @count), or %NULL for error
394
 *
395
 * Since: 1.3.2
396
 **/
397
GByteArray *
398
fu_io_channel_read_byte_array(FuIOChannel *self,
399
            gssize count,
400
            guint timeout_ms,
401
            FuIoChannelFlags flags,
402
            GError **error)
403
0
{
404
0
  GPollFD fds = {
405
0
      .fd = self->fd,
406
0
      .events = G_IO_IN | G_IO_PRI | G_IO_ERR,
407
0
  };
408
0
  g_autoptr(GByteArray) buf = g_byte_array_new();
409
0
  g_autoptr(GByteArray) buf_tmp = g_byte_array_new();
410
411
0
  g_return_val_if_fail(FU_IS_IO_CHANNEL(self), NULL);
412
413
  /* a temp buf of 1k or smaller size */
414
0
  g_byte_array_set_size(buf_tmp, count >= 0 ? MIN(count, 1024) : 1024);
415
416
  /* blocking IO */
417
0
  if (flags & FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO) {
418
0
    do {
419
0
      gssize len = read(self->fd, buf_tmp->data, buf_tmp->len);
420
0
      if (len < 0) {
421
0
        g_set_error(error,
422
0
              G_IO_ERROR, /* nocheck:error */
423
#ifdef HAVE_ERRNO_H
424
              g_io_error_from_errno(errno),
425
#else
426
0
              G_IO_ERROR_FAILED, /* nocheck:blocked */
427
0
#endif
428
0
              "failed to read %i: %s",
429
0
              self->fd,
430
0
              fwupd_strerror(errno));
431
0
        fwupd_error_convert(error);
432
0
        return NULL;
433
0
      }
434
0
      if (len == 0)
435
0
        break;
436
0
      if (flags & FU_IO_CHANNEL_FLAG_SINGLE_SHOT)
437
0
        break;
438
0
      g_byte_array_append(buf, buf_tmp->data, len);
439
0
    } while (count < 0 || buf->len < (gsize)count);
440
0
    return g_steal_pointer(&buf);
441
0
  }
442
443
  /* nonblocking IO */
444
0
  while (TRUE) {
445
    /* wait for data to appear */
446
0
    gint rc = g_poll(&fds, 1, (gint)timeout_ms);
447
0
    if (rc == 0) {
448
0
      g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "timeout");
449
0
      return NULL;
450
0
    }
451
0
    if (rc < 0) {
452
0
      if (errno == EINTR)
453
0
        continue;
454
0
      g_set_error(error,
455
0
            FWUPD_ERROR,
456
0
            FWUPD_ERROR_READ,
457
0
            "failed to poll %i",
458
0
            self->fd);
459
0
      return NULL;
460
0
    }
461
462
    /* we have data to read */
463
0
    if (fds.revents & G_IO_IN) {
464
0
      gssize len = read(self->fd, buf_tmp->data, buf_tmp->len);
465
0
      if (len < 0) {
466
0
        if (errno == EINTR)
467
0
          continue;
468
0
        if (errno == EAGAIN)
469
0
          continue;
470
0
        g_set_error(error,
471
0
              G_IO_ERROR, /* nocheck:error */
472
#ifdef HAVE_ERRNO_H
473
              g_io_error_from_errno(errno),
474
#else
475
0
              G_IO_ERROR_FAILED, /* nocheck:blocked */
476
0
#endif
477
0
              "failed to read %i: %s",
478
0
              self->fd,
479
0
              fwupd_strerror(errno));
480
0
        fwupd_error_convert(error);
481
0
        return NULL;
482
0
      }
483
0
      if (len == 0)
484
0
        break;
485
0
      if (len > 0)
486
0
        g_byte_array_append(buf, buf_tmp->data, len);
487
488
      /* check maximum size */
489
0
      if (count > 0 && buf->len >= (guint)count)
490
0
        break;
491
0
      if (flags & FU_IO_CHANNEL_FLAG_SINGLE_SHOT)
492
0
        break;
493
0
      continue;
494
0
    }
495
0
    if (fds.revents & G_IO_ERR) {
496
0
      g_set_error_literal(error,
497
0
              FWUPD_ERROR,
498
0
              FWUPD_ERROR_READ,
499
0
              "error condition");
500
0
      return NULL;
501
0
    }
502
0
    if (fds.revents & G_IO_HUP) {
503
0
      g_set_error_literal(error,
504
0
              FWUPD_ERROR,
505
0
              FWUPD_ERROR_READ,
506
0
              "connection hung up");
507
0
      return NULL;
508
0
    }
509
0
    if (fds.revents & G_IO_NVAL) {
510
0
      g_set_error_literal(error,
511
0
              FWUPD_ERROR,
512
0
              FWUPD_ERROR_READ,
513
0
              "invalid request");
514
0
      return NULL;
515
0
    }
516
0
  }
517
518
  /* no data */
519
0
  if (buf->len == 0) {
520
0
    g_set_error(error,
521
0
          FWUPD_ERROR,
522
0
          FWUPD_ERROR_TIMED_OUT,
523
0
          "no data received from device in %ums",
524
0
          timeout_ms);
525
0
    return NULL;
526
0
  }
527
528
  /* return blob */
529
0
  return g_steal_pointer(&buf);
530
0
}
531
532
/**
533
 * fu_io_channel_read_raw:
534
 * @self: a #FuIOChannel
535
 * @buf: (nullable): optional buffer
536
 * @bufsz: size of @buf
537
 * @bytes_read: (out) (nullable): data written to @buf
538
 * @timeout_ms: timeout in ms
539
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
540
 * @error: (nullable): optional return location for an error
541
 *
542
 * Reads bytes from the TTY, that will fail if exceeding @timeout_ms.
543
 *
544
 * Returns: a #GBytes, or %NULL for error
545
 *
546
 * Since: 1.2.2
547
 **/
548
gboolean
549
fu_io_channel_read_raw(FuIOChannel *self,
550
           guint8 *buf,
551
           gsize bufsz,
552
           gsize *bytes_read,
553
           guint timeout_ms,
554
           FuIoChannelFlags flags,
555
           GError **error)
556
0
{
557
0
  g_autoptr(GByteArray) tmp = NULL;
558
559
0
  g_return_val_if_fail(FU_IS_IO_CHANNEL(self), FALSE);
560
561
0
  tmp = fu_io_channel_read_byte_array(self, bufsz, timeout_ms, flags, error);
562
0
  if (tmp == NULL)
563
0
    return FALSE;
564
0
  if (buf != NULL)
565
0
    memcpy(buf, tmp->data, MIN(tmp->len, bufsz)); /* nocheck:blocked */
566
0
  if (bytes_read != NULL)
567
0
    *bytes_read = tmp->len;
568
0
  return TRUE;
569
0
}
570
571
static void
572
fu_io_channel_finalize(GObject *object)
573
0
{
574
0
  FuIOChannel *self = FU_IO_CHANNEL(object);
575
0
  if (self->fd != -1)
576
0
    g_close(self->fd, NULL);
577
0
  G_OBJECT_CLASS(fu_io_channel_parent_class)->finalize(object);
578
0
}
579
580
static void
581
fu_io_channel_class_init(FuIOChannelClass *klass)
582
0
{
583
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
584
0
  object_class->finalize = fu_io_channel_finalize;
585
0
}
586
587
static void
588
fu_io_channel_init(FuIOChannel *self)
589
0
{
590
0
  self->fd = -1;
591
0
}
592
593
/**
594
 * fu_io_channel_unix_new:
595
 * @fd: file descriptor
596
 *
597
 * Creates a new object to write and read from.
598
 *
599
 * Returns: a #FuIOChannel
600
 *
601
 * Since: 1.2.2
602
 **/
603
FuIOChannel *
604
fu_io_channel_unix_new(gint fd)
605
0
{
606
0
  FuIOChannel *self;
607
0
  self = g_object_new(FU_TYPE_IO_CHANNEL, NULL);
608
0
  self->fd = fd;
609
0
  return FU_IO_CHANNEL(self);
610
0
}
611
612
/**
613
 * fu_io_channel_new_file:
614
 * @filename: device file
615
 * @open_flags: some #FuIoChannelOpenFlags typically %FU_IO_CHANNEL_OPEN_FLAG_READ
616
 * @error: (nullable): optional return location for an error
617
 *
618
 * Creates a new object to write and/or read from.
619
 *
620
 * Returns: a #FuIOChannel
621
 *
622
 * Since: 2.0.0
623
 **/
624
FuIOChannel *
625
fu_io_channel_new_file(const gchar *filename, FuIoChannelOpenFlags open_flags, GError **error)
626
0
{
627
0
  gint fd;
628
0
  int flags = 0;
629
630
0
  g_return_val_if_fail(filename != NULL, NULL);
631
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
632
633
0
  if (open_flags & FU_IO_CHANNEL_OPEN_FLAG_READ &&
634
0
      open_flags & FU_IO_CHANNEL_OPEN_FLAG_WRITE) {
635
0
    flags |= O_RDWR;
636
0
  } else if (open_flags & FU_IO_CHANNEL_OPEN_FLAG_READ) {
637
0
    flags |= O_RDONLY;
638
0
  } else if (open_flags & FU_IO_CHANNEL_OPEN_FLAG_WRITE) {
639
0
    flags |= O_WRONLY;
640
0
  }
641
0
#ifdef O_NONBLOCK
642
0
  if (open_flags & FU_IO_CHANNEL_OPEN_FLAG_NONBLOCK)
643
0
    flags |= O_NONBLOCK;
644
0
#endif
645
0
#ifdef O_SYNC
646
0
  if (open_flags & FU_IO_CHANNEL_OPEN_FLAG_SYNC)
647
0
    flags |= O_SYNC;
648
0
#endif
649
0
  fd = g_open(filename, flags, S_IRWXU);
650
0
  if (fd < 0) {
651
0
    g_set_error(error,
652
0
          G_IO_ERROR, /* nocheck:error */
653
#ifdef HAVE_ERRNO_H
654
          g_io_error_from_errno(errno),
655
#else
656
0
          G_IO_ERROR_FAILED, /* nocheck:blocked */
657
0
#endif
658
0
          "failed to open %s: %s",
659
0
          filename,
660
0
          fwupd_strerror(errno));
661
0
    fwupd_error_convert(error);
662
0
    return NULL;
663
0
  }
664
0
  return fu_io_channel_unix_new(fd);
665
0
}
666
667
/**
668
 * fu_io_channel_virtual_new:
669
 * @name: (not nullable): memfd name
670
 * @error: (nullable): optional return location for an error
671
 *
672
 * Creates a new virtual object to write and/or read from.
673
 *
674
 * Returns: a #FuIOChannel
675
 *
676
 * Since: 2.0.0
677
 **/
678
FuIOChannel *
679
fu_io_channel_virtual_new(const gchar *name, GError **error)
680
0
{
681
#ifdef HAVE_MEMFD_CREATE
682
  gint fd;
683
684
  g_return_val_if_fail(name != NULL, NULL);
685
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
686
687
  fd = memfd_create(name, MFD_CLOEXEC);
688
  if (fd < 0) {
689
    g_set_error(error,
690
          G_IO_ERROR, /* nocheck:error */
691
#ifdef HAVE_ERRNO_H
692
          g_io_error_from_errno(errno),
693
#else
694
          G_IO_ERROR_FAILED, /* nocheck:blocked */
695
#endif
696
          "failed to create %s: %s",
697
          name,
698
          fwupd_strerror(errno));
699
    fwupd_error_convert(error);
700
    return NULL;
701
  }
702
  return fu_io_channel_unix_new(fd);
703
#else
704
0
  g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "memfd not supported");
705
  return NULL;
706
0
#endif
707
0
}