[libcamera-devel,v3,17/17] libcamera: base: Rename FileDescriptor to SharedFD
diff mbox series

Message ID 20211128235752.10836-18-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • libcamera: Introduce UniqueFD
Related show

Commit Message

Laurent Pinchart Nov. 28, 2021, 11:57 p.m. UTC
Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.
Rename it to SharedFD.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/libcamera/base/file.h                 |   4 +-
 include/libcamera/base/meson.build            |   2 +-
 .../base/{file_descriptor.h => shared_fd.h}   |  20 +-
 include/libcamera/framebuffer.h               |   4 +-
 .../libcamera/internal/ipa_data_serializer.h  |  40 +--
 include/libcamera/internal/ipc_pipe.h         |   8 +-
 include/libcamera/ipa/core.mojom              |   6 +-
 include/libcamera/ipa/raspberrypi.mojom       |   2 +-
 src/android/camera_device.cpp                 |   2 +-
 src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-
 src/libcamera/base/file.cpp                   |   6 +-
 src/libcamera/base/file_descriptor.cpp        | 266 ------------------
 src/libcamera/base/meson.build                |   2 +-
 src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++
 src/libcamera/framebuffer.cpp                 |   6 +-
 src/libcamera/ipa_data_serializer.cpp         | 100 +++----
 src/libcamera/ipc_pipe.cpp                    |   4 +-
 .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-
 src/libcamera/v4l2_videodevice.cpp            |   6 +-
 src/v4l2/v4l2_camera.h                        |   2 +-
 test/meson.build                              |   2 +-
 .../ipa_data_serializer_test.cpp              |  14 +-
 test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--
 .../module_ipa_proxy.cpp.tmpl                 |   2 +-
 .../module_ipa_proxy.h.tmpl                   |   2 +-
 .../libcamera_templates/proxy_functions.tmpl  |   2 +-
 .../libcamera_templates/serializer.tmpl       |  22 +-
 .../generators/mojom_libcamera_generator.py   |   6 +-
 28 files changed, 422 insertions(+), 426 deletions(-)
 rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)
 delete mode 100644 src/libcamera/base/file_descriptor.cpp
 create mode 100644 src/libcamera/base/shared_fd.cpp
 rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)

Comments

Hirokazu Honda Nov. 29, 2021, 2:04 p.m. UTC | #1
Hi Laurent,

On Mon, Nov 29, 2021 at 8:58 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.
> Rename it to SharedFD.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Hirokazu Honda <hiroh@chromium.org>
> ---
>  include/libcamera/base/file.h                 |   4 +-
>  include/libcamera/base/meson.build            |   2 +-
>  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-
>  include/libcamera/framebuffer.h               |   4 +-
>  .../libcamera/internal/ipa_data_serializer.h  |  40 +--
>  include/libcamera/internal/ipc_pipe.h         |   8 +-
>  include/libcamera/ipa/core.mojom              |   6 +-
>  include/libcamera/ipa/raspberrypi.mojom       |   2 +-
>  src/android/camera_device.cpp                 |   2 +-
>  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-
>  src/libcamera/base/file.cpp                   |   6 +-
>  src/libcamera/base/file_descriptor.cpp        | 266 ------------------
>  src/libcamera/base/meson.build                |   2 +-
>  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++
>  src/libcamera/framebuffer.cpp                 |   6 +-
>  src/libcamera/ipa_data_serializer.cpp         | 100 +++----
>  src/libcamera/ipc_pipe.cpp                    |   4 +-
>  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-
>  src/libcamera/v4l2_videodevice.cpp            |   6 +-
>  src/v4l2/v4l2_camera.h                        |   2 +-
>  test/meson.build                              |   2 +-
>  .../ipa_data_serializer_test.cpp              |  14 +-
>  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--
>  .../module_ipa_proxy.cpp.tmpl                 |   2 +-
>  .../module_ipa_proxy.h.tmpl                   |   2 +-
>  .../libcamera_templates/proxy_functions.tmpl  |   2 +-
>  .../libcamera_templates/serializer.tmpl       |  22 +-
>  .../generators/mojom_libcamera_generator.py   |   6 +-
>  28 files changed, 422 insertions(+), 426 deletions(-)
>  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)
>  delete mode 100644 src/libcamera/base/file_descriptor.cpp
>  create mode 100644 src/libcamera/base/shared_fd.cpp
>  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)
>
> diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h
> index 47769da7abc2..691b52d6ab2d 100644
> --- a/include/libcamera/base/file.h
> +++ b/include/libcamera/base/file.h
> @@ -21,7 +21,7 @@
>
>  namespace libcamera {
>
> -class FileDescriptor;
> +class SharedFD;
>
>  class File
>  {
> @@ -69,7 +69,7 @@ public:
>         bool unmap(uint8_t *addr);
>
>         static bool exists(const std::string &name);
> -       static ino_t inode(const FileDescriptor &fd);
> +       static ino_t inode(const SharedFD &fd);
>
>  private:
>         LIBCAMERA_DISABLE_COPY(File)
> diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
> index cca374a769cc..112420dab225 100644
> --- a/include/libcamera/base/meson.build
> +++ b/include/libcamera/base/meson.build
> @@ -11,13 +11,13 @@ libcamera_base_headers = files([
>      'event_dispatcher_poll.h',
>      'event_notifier.h',
>      'file.h',
> -    'file_descriptor.h',
>      'flags.h',
>      'log.h',
>      'message.h',
>      'object.h',
>      'private.h',
>      'semaphore.h',
> +    'shared_fd.h',
>      'signal.h',
>      'span.h',
>      'thread.h',
> diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h
> similarity index 55%
> rename from include/libcamera/base/file_descriptor.h
> rename to include/libcamera/base/shared_fd.h
> index 12a43f95d414..a786885ceb32 100644
> --- a/include/libcamera/base/file_descriptor.h
> +++ b/include/libcamera/base/shared_fd.h
> @@ -2,7 +2,7 @@
>  /*
>   * Copyright (C) 2019, Google Inc.
>   *
> - * file_descriptor.h - File descriptor wrapper
> + * shared_fd.h - File descriptor wrapper with shared ownership
>   */
>
>  #pragma once
> @@ -13,18 +13,18 @@ namespace libcamera {
>
>  class UniqueFD;
>
> -class FileDescriptor final
> +class SharedFD final
>  {
>  public:
> -       explicit FileDescriptor(const int &fd = -1);
> -       explicit FileDescriptor(int &&fd);
> -       explicit FileDescriptor(UniqueFD fd);
> -       FileDescriptor(const FileDescriptor &other);
> -       FileDescriptor(FileDescriptor &&other);
> -       ~FileDescriptor();
> +       explicit SharedFD(const int &fd = -1);
> +       explicit SharedFD(int &&fd);
> +       explicit SharedFD(UniqueFD fd);
> +       SharedFD(const SharedFD &other);
> +       SharedFD(SharedFD &&other);
> +       ~SharedFD();
>
> -       FileDescriptor &operator=(const FileDescriptor &other);
> -       FileDescriptor &operator=(FileDescriptor &&other);
> +       SharedFD &operator=(const SharedFD &other);
> +       SharedFD &operator=(SharedFD &&other);
>
>         bool isValid() const { return fd_ != nullptr; }
>         int fd() const { return fd_ ? fd_->fd() : -1; }
> diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h
> index 2fbea9c5be16..357bbe189551 100644
> --- a/include/libcamera/framebuffer.h
> +++ b/include/libcamera/framebuffer.h
> @@ -13,7 +13,7 @@
>  #include <vector>
>
>  #include <libcamera/base/class.h>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/span.h>
>
>  namespace libcamera {
> @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible
>  public:
>         struct Plane {
>                 static constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();
> -               FileDescriptor fd;
> +               SharedFD fd;
>                 unsigned int offset = kInvalidOffset;
>                 unsigned int length;
>         };
> diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
> index c2f602d5b7de..a87449c9be48 100644
> --- a/include/libcamera/internal/ipa_data_serializer.h
> +++ b/include/libcamera/internal/ipa_data_serializer.h
> @@ -66,7 +66,7 @@ template<typename T>
>  class IPADataSerializer
>  {
>  public:
> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>         serialize(const T &data, ControlSerializer *cs = nullptr);
>
>         static T deserialize(const std::vector<uint8_t> &data,
> @@ -76,12 +76,12 @@ public:
>                              ControlSerializer *cs = nullptr);
>
>         static T deserialize(const std::vector<uint8_t> &data,
> -                            const std::vector<FileDescriptor> &fds,
> +                            const std::vector<SharedFD> &fds,
>                              ControlSerializer *cs = nullptr);
>         static T deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                              std::vector<uint8_t>::const_iterator dataEnd,
> -                            std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                            std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                            std::vector<SharedFD>::const_iterator fdsBegin,
> +                            std::vector<SharedFD>::const_iterator fdsEnd,
>                              ControlSerializer *cs = nullptr);
>  };
>
> @@ -104,11 +104,11 @@ template<typename V>
>  class IPADataSerializer<std::vector<V>>
>  {
>  public:
> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>         serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
>         {
>                 std::vector<uint8_t> dataVec;
> -               std::vector<FileDescriptor> fdsVec;
> +               std::vector<SharedFD> fdsVec;
>
>                 /* Serialize the length. */
>                 uint32_t vecLen = data.size();
> @@ -117,7 +117,7 @@ public:
>                 /* Serialize the members. */
>                 for (auto const &it : data) {
>                         std::vector<uint8_t> dvec;
> -                       std::vector<FileDescriptor> fvec;
> +                       std::vector<SharedFD> fvec;
>
>                         std::tie(dvec, fvec) =
>                                 IPADataSerializer<V>::serialize(it, cs);
> @@ -141,11 +141,11 @@ public:
>                                           std::vector<uint8_t>::const_iterator dataEnd,
>                                           ControlSerializer *cs = nullptr)
>         {
> -               std::vector<FileDescriptor> fds;
> +               std::vector<SharedFD> fds;
>                 return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
>         }
>
> -       static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> +       static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
>                                           ControlSerializer *cs = nullptr)
>         {
>                 return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> @@ -153,15 +153,15 @@ public:
>
>         static std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                                           std::vector<uint8_t>::const_iterator dataEnd,
> -                                         std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                                         std::vector<SharedFD>::const_iterator fdsBegin,
> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>                                           ControlSerializer *cs = nullptr)
>         {
>                 uint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
>                 std::vector<V> ret(vecLen);
>
>                 std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> -               std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> +               std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
>                 for (uint32_t i = 0; i < vecLen; i++) {
>                         uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
>                         uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> @@ -201,11 +201,11 @@ template<typename K, typename V>
>  class IPADataSerializer<std::map<K, V>>
>  {
>  public:
> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>         serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
>         {
>                 std::vector<uint8_t> dataVec;
> -               std::vector<FileDescriptor> fdsVec;
> +               std::vector<SharedFD> fdsVec;
>
>                 /* Serialize the length. */
>                 uint32_t mapLen = data.size();
> @@ -214,7 +214,7 @@ public:
>                 /* Serialize the members. */
>                 for (auto const &it : data) {
>                         std::vector<uint8_t> dvec;
> -                       std::vector<FileDescriptor> fvec;
> +                       std::vector<SharedFD> fvec;
>
>                         std::tie(dvec, fvec) =
>                                 IPADataSerializer<K>::serialize(it.first, cs);
> @@ -247,11 +247,11 @@ public:
>                                           std::vector<uint8_t>::const_iterator dataEnd,
>                                           ControlSerializer *cs = nullptr)
>         {
> -               std::vector<FileDescriptor> fds;
> +               std::vector<SharedFD> fds;
>                 return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
>         }
>
> -       static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> +       static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
>                                           ControlSerializer *cs = nullptr)
>         {
>                 return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> @@ -259,8 +259,8 @@ public:
>
>         static std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                                           std::vector<uint8_t>::const_iterator dataEnd,
> -                                         std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                                         std::vector<SharedFD>::const_iterator fdsBegin,
> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>                                           ControlSerializer *cs = nullptr)
>         {
>                 std::map<K, V> ret;
> @@ -268,7 +268,7 @@ public:
>                 uint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
>
>                 std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> -               std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> +               std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
>                 for (uint32_t i = 0; i < mapLen; i++) {
>                         uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
>                         uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h
> index 986f8d886fa6..ab5dd67c3813 100644
> --- a/include/libcamera/internal/ipc_pipe.h
> +++ b/include/libcamera/internal/ipc_pipe.h
> @@ -9,7 +9,7 @@
>
>  #include <vector>
>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/signal.h>
>
>  #include "libcamera/internal/ipc_unixsocket.h"
> @@ -33,17 +33,17 @@ public:
>
>         Header &header() { return header_; }
>         std::vector<uint8_t> &data() { return data_; }
> -       std::vector<FileDescriptor> &fds() { return fds_; }
> +       std::vector<SharedFD> &fds() { return fds_; }
>
>         const Header &header() const { return header_; }
>         const std::vector<uint8_t> &data() const { return data_; }
> -       const std::vector<FileDescriptor> &fds() const { return fds_; }
> +       const std::vector<SharedFD> &fds() const { return fds_; }
>
>  private:
>         Header header_;
>
>         std::vector<uint8_t> data_;
> -       std::vector<FileDescriptor> fds_;
> +       std::vector<SharedFD> fds_;
>  };
>
>  class IPCPipe
> diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom
> index f7eff0c7ab8c..74f3339e56f2 100644
> --- a/include/libcamera/ipa/core.mojom
> +++ b/include/libcamera/ipa/core.mojom
> @@ -32,7 +32,7 @@ module libcamera;
>   *   - This attribute instructs the build system that a (de)serializer is
>   *     available for the type and there's no need to generate one
>   * - hasFd - struct fields or empty structs only
> - *   - Designate that this field or empty struct contains a FileDescriptor
> + *   - Designate that this field or empty struct contains a SharedFD
>   *
>   * Rules:
>   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is
> @@ -60,7 +60,7 @@ module libcamera;
>   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array
>   *       member
>   * - [skipHeader] and [skipSerdes] only work here in core.mojom
> - * - If a field in a struct has a FileDescriptor, but is not explicitly
> + * - If a field in a struct has a SharedFD, but is not explicitly
>   *   defined so in mojom, then the field must be marked with the [hasFd]
>   *   attribute
>   *
> @@ -71,7 +71,7 @@ module libcamera;
>   */
>  [skipSerdes, skipHeader] struct ControlInfoMap {};
>  [skipSerdes, skipHeader] struct ControlList {};
> -[skipSerdes, skipHeader] struct FileDescriptor {};
> +[skipSerdes, skipHeader] struct SharedFD {};
>
>  [skipHeader] struct Point {
>         int32 x;
> diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
> index e453d46cb14f..acd3cafe6c91 100644
> --- a/include/libcamera/ipa/raspberrypi.mojom
> +++ b/include/libcamera/ipa/raspberrypi.mojom
> @@ -35,7 +35,7 @@ struct ISPConfig {
>
>  struct IPAConfig {
>         uint32 transform;
> -       libcamera.FileDescriptor lsTableHandle;
> +       libcamera.SharedFD lsTableHandle;
>  };
>
>  struct StartConfig {
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index f2e0bdbdbbf6..1938b10509fa 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,
>
>         std::vector<FrameBuffer::Plane> planes(buf.numPlanes());
>         for (size_t i = 0; i < buf.numPlanes(); ++i) {
> -               FileDescriptor fd{ camera3buffer->data[i] };
> +               SharedFD fd{ camera3buffer->data[i] };
>                 if (!fd.isValid()) {
>                         LOG(HAL, Fatal) << "No valid fd";
>                         return nullptr;
> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
> index c6aec09046f7..aaf629eeb3fc 100644
> --- a/src/ipa/raspberrypi/raspberrypi.cpp
> +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> @@ -15,8 +15,8 @@
>
>  #include <linux/bcm2835-isp.h>
>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/log.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/span.h>
>
>  #include <libcamera/control_ids.h>
> @@ -164,7 +164,7 @@ private:
>         bool processPending_;
>
>         /* LS table allocation passed in from the pipeline handler. */
> -       FileDescriptor lsTableHandle_;
> +       SharedFD lsTableHandle_;
>         void *lsTable_;
>
>         /* Distinguish the first camera start from others. */
> diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp
> index 66c73c406198..3ca9839bb989 100644
> --- a/src/libcamera/base/file.cpp
> +++ b/src/libcamera/base/file.cpp
> @@ -14,8 +14,8 @@
>  #include <sys/types.h>
>  #include <unistd.h>
>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/log.h>
> +#include <libcamera/base/shared_fd.h>
>
>  /**
>   * \file base/file.h
> @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)
>  }
>
>  /**
> - * \brief Retrieve the inode of a FileDescriptor
> + * \brief Retrieve the inode of a SharedFD
>   *
>   * \return The file descriptor inode on success, or 0 on error
>   */
> -ino_t File::inode(const FileDescriptor &fd)
> +ino_t File::inode(const SharedFD &fd)
>  {
>         if (!fd.isValid())
>                 return 0;
> diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp
> deleted file mode 100644
> index a83bf52c31e6..000000000000
> --- a/src/libcamera/base/file_descriptor.cpp
> +++ /dev/null
> @@ -1,266 +0,0 @@
> -/* SPDX-License-Identifier: LGPL-2.1-or-later */
> -/*
> - * Copyright (C) 2019, Google Inc.
> - *
> - * file_descriptor.cpp - File descriptor wrapper
> - */
> -
> -#include <libcamera/base/file_descriptor.h>
> -
> -#include <string.h>
> -#include <sys/types.h>
> -#include <unistd.h>
> -#include <utility>
> -
> -#include <libcamera/base/log.h>
> -#include <libcamera/base/unique_fd.h>
> -
> -/**
> - * \file base/file_descriptor.h
> - * \brief File descriptor wrapper
> - */
> -
> -namespace libcamera {
> -
> -LOG_DEFINE_CATEGORY(FileDescriptor)
> -
> -/**
> - * \class FileDescriptor
> - * \brief RAII-style wrapper for file descriptors
> - *
> - * The FileDescriptor class provides RAII-style lifetime management of file
> - * descriptors with an efficient mechanism for ownership sharing. At its core,
> - * an internal Descriptor object wraps a file descriptor (expressed as a signed
> - * integer) with an RAII-style interface. The Descriptor is then implicitly
> - * shared with all FileDescriptor instances constructed as copies.
> - *
> - * When constructed from a numerical file descriptor, the FileDescriptor
> - * instance either duplicates or takes over the file descriptor:
> - *
> - * - The FileDescriptor(const int &) constructor duplicates the numerical file
> - *   descriptor and wraps the duplicate in a Descriptor. The caller is
> - *   responsible for closing the original file descriptor, and the value
> - *   returned by fd() will be different from the value passed to the
> - *   constructor.
> - *
> - * - The FileDescriptor(int &&) constructor takes over the numerical file
> - *   descriptor and wraps it in a Descriptor. The caller shall not touch the
> - *   original file descriptor once the function returns, and the value returned
> - *   by fd() will be identical to the value passed to the constructor.
> - *
> - * The copy constructor and assignment operator create copies that share the
> - * Descriptor, while the move versions of those functions additionally make the
> - * other FileDescriptor invalid. When the last FileDescriptor that references a
> - * Descriptor is destroyed, the file descriptor is closed.
> - *
> - * The numerical file descriptor is available through the fd() function. All
> - * FileDescriptor instances created as copies of a FileDescriptor will report
> - * the same fd() value. Callers can perform operations on the fd(), but shall
> - * never close it manually.
> - */
> -
> -/**
> - * \brief Create a FileDescriptor copying a given \a fd
> - * \param[in] fd File descriptor
> - *
> - * Construct a FileDescriptor from a numerical file descriptor by duplicating
> - * the \a fd, and take ownership of the copy. The original \a fd is left
> - * untouched, and the caller is responsible for closing it when appropriate.
> - * The duplicated file descriptor will be closed automatically when all
> - * FileDescriptor instances that reference it are destroyed.
> - *
> - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> - * the fd() function will return -1.
> - */
> -FileDescriptor::FileDescriptor(const int &fd)
> -{
> -       if (fd < 0)
> -               return;
> -
> -       fd_ = std::make_shared<Descriptor>(fd, true);
> -       if (fd_->fd() < 0)
> -               fd_.reset();
> -}
> -
> -/**
> - * \brief Create a FileDescriptor taking ownership of a given \a fd
> - * \param[in] fd File descriptor
> - *
> - * Construct a FileDescriptor from a numerical file descriptor by taking
> - * ownership of the \a fd. The original \a fd is set to -1 and shall not be
> - * touched by the caller anymore. In particular, the caller shall not close the
> - * original \a fd manually. The duplicated file descriptor will be closed
> - * automatically when all FileDescriptor instances that reference it are
> - * destroyed.
> - *
> - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> - * the fd() function will return -1.
> - */
> -FileDescriptor::FileDescriptor(int &&fd)
> -{
> -       if (fd < 0)
> -               return;
> -
> -       fd_ = std::make_shared<Descriptor>(fd, false);
> -       /*
> -        * The Descriptor constructor can't have failed here, as it took over
> -        * the fd without duplicating it. Just set the original fd to -1 to
> -        * implement move semantics.
> -        */
> -       fd = -1;
> -}
> -
> -/**
> - * \brief Create a FileDescriptor taking ownership of a given UniqueFD \a fd
> - * \param[in] fd UniqueFD
> - *
> - * Construct a FileDescriptor from UniqueFD by taking ownership of the \a fd.
> - * The original \a fd becomes invalid.
> - */
> -FileDescriptor::FileDescriptor(UniqueFD fd)
> -       : FileDescriptor(fd.release())
> -{
> -}
> -
> -/**
> - * \brief Copy constructor, create a FileDescriptor from a copy of \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Copying a FileDescriptor implicitly shares ownership of the wrapped file
> - * descriptor. The original FileDescriptor is left untouched, and the caller is
> - * responsible for destroying it when appropriate. The wrapped file descriptor
> - * will be closed automatically when all FileDescriptor instances that
> - * reference it are destroyed.
> - */
> -FileDescriptor::FileDescriptor(const FileDescriptor &other)
> -       : fd_(other.fd_)
> -{
> -}
> -
> -/**
> - * \brief Move constructor, create a FileDescriptor by taking over \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> - * by \a other to the new FileDescriptor. The \a other FileDescriptor is
> - * invalidated and its fd() function will return -1. The wrapped file descriptor
> - * will be closed automatically when all FileDescriptor instances that
> - * reference it are destroyed.
> - */
> -FileDescriptor::FileDescriptor(FileDescriptor &&other)
> -       : fd_(std::move(other.fd_))
> -{
> -}
> -
> -/**
> - * \brief Destroy the FileDescriptor instance
> - *
> - * Destroying a FileDescriptor instance releases its reference to the wrapped
> - * descriptor, if any. When the last instance that references a wrapped
> - * descriptor is destroyed, the file descriptor is automatically closed.
> - */
> -FileDescriptor::~FileDescriptor()
> -{
> -}
> -
> -/**
> - * \brief Copy assignment operator, replace the wrapped file descriptor with a
> - * copy of \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Copying a FileDescriptor creates a new reference to the wrapped file
> - * descriptor owner by \a other. If \a other is invalid, *this will also be
> - * invalid. The original FileDescriptor is left untouched, and the caller is
> - * responsible for destroying it when appropriate. The wrapped file descriptor
> - * will be closed automatically when all FileDescriptor instances that
> - * reference it are destroyed.
> - *
> - * \return A reference to this FileDescriptor
> - */
> -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)
> -{
> -       fd_ = other.fd_;
> -
> -       return *this;
> -}
> -
> -/**
> - * \brief Move assignment operator, replace the wrapped file descriptor by
> - * taking over \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> - * by \a other to the new FileDescriptor. If \a other is invalid, *this will
> - * also be invalid. The \a other FileDescriptor is invalidated and its fd()
> - * function will return -1. The wrapped file descriptor will be closed
> - * automatically when all FileDescriptor instances that reference it are
> - * destroyed.
> - *
> - * \return A reference to this FileDescriptor
> - */
> -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)
> -{
> -       fd_ = std::move(other.fd_);
> -
> -       return *this;
> -}
> -
> -/**
> - * \fn FileDescriptor::isValid()
> - * \brief Check if the FileDescriptor instance is valid
> - * \return True if the FileDescriptor is valid, false otherwise
> - */
> -
> -/**
> - * \fn FileDescriptor::fd()
> - * \brief Retrieve the numerical file descriptor
> - * \return The numerical file descriptor, which may be -1 if the FileDescriptor
> - * instance is invalid
> - */
> -
> -/**
> - * \brief Duplicate a FileDescriptor
> - *
> - * Duplicating a FileDescriptor creates a duplicate of the wrapped file
> - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function
> - * of the original and the get() function of the duplicate will return different
> - * values. The duplicate instance will not be affected by destruction of the
> - * original instance or its copies.
> - *
> - * \return A UniqueFD owning a duplicate of the original file descriptor
> - */
> -UniqueFD FileDescriptor::dup() const
> -{
> -       int dupFd = ::dup(fd());
> -       if (dupFd == -1) {
> -               int ret = -errno;
> -               LOG(FileDescriptor, Error)
> -                       << "Failed to dup() fd: " << strerror(-ret);
> -       }
> -
> -       return UniqueFD(dupFd);
> -}
> -
> -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)
> -{
> -       if (!duplicate) {
> -               fd_ = fd;
> -               return;
> -       }
> -
> -       /* Failing to dup() a fd should not happen and is fatal. */
> -       fd_ = ::dup(fd);
> -       if (fd_ == -1) {
> -               int ret = -errno;
> -               LOG(FileDescriptor, Fatal)
> -                       << "Failed to dup() fd: " << strerror(-ret);
> -       }
> -}
> -
> -FileDescriptor::Descriptor::~Descriptor()
> -{
> -       if (fd_ != -1)
> -               close(fd_);
> -}
> -
> -} /* namespace libcamera */
> diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build
> index b0d85bc19245..ccb746c27466 100644
> --- a/src/libcamera/base/meson.build
> +++ b/src/libcamera/base/meson.build
> @@ -8,12 +8,12 @@ libcamera_base_sources = files([
>      'event_dispatcher_poll.cpp',
>      'event_notifier.cpp',
>      'file.cpp',
> -    'file_descriptor.cpp',
>      'flags.cpp',
>      'log.cpp',
>      'message.cpp',
>      'object.cpp',
>      'semaphore.cpp',
> +    'shared_fd.cpp',
>      'signal.cpp',
>      'thread.cpp',
>      'timer.cpp',
> diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp
> new file mode 100644
> index 000000000000..05b6892f7e19
> --- /dev/null
> +++ b/src/libcamera/base/shared_fd.cpp
> @@ -0,0 +1,262 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * shared_fd.cpp - File descriptor wrapper with shared ownership
> + */
> +
> +#include <libcamera/base/shared_fd.h>
> +
> +#include <string.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <utility>
> +
> +#include <libcamera/base/log.h>
> +#include <libcamera/base/unique_fd.h>
> +
> +/**
> + * \file base/shared_fd.h
> + * \brief File descriptor wrapper
> + */
> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(SharedFD)
> +
> +/**
> + * \class SharedFD
> + * \brief RAII-style wrapper for file descriptors
> + *
> + * The SharedFD class provides RAII-style lifetime management of file
> + * descriptors with an efficient mechanism for ownership sharing. At its core,
> + * an internal Descriptor object wraps a file descriptor (expressed as a signed
> + * integer) with an RAII-style interface. The Descriptor is then implicitly
> + * shared with all SharedFD instances constructed as copies.
> + *
> + * When constructed from a numerical file descriptor, the SharedFD instance
> + * either duplicates or takes over the file descriptor:
> + *
> + * - The SharedFD(const int &) constructor duplicates the numerical file
> + *   descriptor and wraps the duplicate in a Descriptor. The caller is
> + *   responsible for closing the original file descriptor, and the value
> + *   returned by fd() will be different from the value passed to the
> + *   constructor.
> + *
> + * - The SharedFD(int &&) constructor takes over the numerical file descriptor
> + *   and wraps it in a Descriptor. The caller shall not touch the original file
> + *   descriptor once the function returns, and the value returned by fd() will
> + *   be identical to the value passed to the constructor.
> + *
> + * The copy constructor and assignment operator create copies that share the
> + * Descriptor, while the move versions of those functions additionally make the
> + * other SharedFD invalid. When the last SharedFD that references a Descriptor
> + * is destroyed, the file descriptor is closed.
> + *
> + * The numerical file descriptor is available through the fd() function. All
> + * SharedFD instances created as copies of a SharedFD will report the same fd()
> + * value. Callers can perform operations on the fd(), but shall never close it
> + * manually.
> + */
> +
> +/**
> + * \brief Create a SharedFD copying a given \a fd
> + * \param[in] fd File descriptor
> + *
> + * Construct a SharedFD from a numerical file descriptor by duplicating the
> + * \a fd, and take ownership of the copy. The original \a fd is left untouched,
> + * and the caller is responsible for closing it when appropriate. The duplicated
> + * file descriptor will be closed automatically when all SharedFD instances that
> + * reference it are destroyed.
> + *
> + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> + * function will return -1.
> + */
> +SharedFD::SharedFD(const int &fd)
> +{
> +       if (fd < 0)
> +               return;
> +
> +       fd_ = std::make_shared<Descriptor>(fd, true);
> +       if (fd_->fd() < 0)
> +               fd_.reset();
> +}
> +
> +/**
> + * \brief Create a SharedFD taking ownership of a given \a fd
> + * \param[in] fd File descriptor
> + *
> + * Construct a SharedFD from a numerical file descriptor by taking ownership of
> + * the \a fd. The original \a fd is set to -1 and shall not be touched by the
> + * caller anymore. In particular, the caller shall not close the original \a fd
> + * manually. The duplicated file descriptor will be closed automatically when
> + * all SharedFD instances that reference it are destroyed.
> + *
> + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> + * function will return -1.
> + */
> +SharedFD::SharedFD(int &&fd)
> +{
> +       if (fd < 0)
> +               return;
> +
> +       fd_ = std::make_shared<Descriptor>(fd, false);
> +       /*
> +        * The Descriptor constructor can't have failed here, as it took over
> +        * the fd without duplicating it. Just set the original fd to -1 to
> +        * implement move semantics.
> +        */
> +       fd = -1;
> +}
> +
> +/**
> + * \brief Create a SharedFD taking ownership of a given UniqueFD \a fd
> + * \param[in] fd UniqueFD
> + *
> + * Construct a SharedFD from UniqueFD by taking ownership of the \a fd. The
> + * original \a fd becomes invalid.
> + */
> +SharedFD::SharedFD(UniqueFD fd)
> +       : SharedFD(fd.release())
> +{
> +}
> +
> +/**
> + * \brief Copy constructor, create a SharedFD from a copy of \a other
> + * \param[in] other The other SharedFD
> + *
> + * Copying a SharedFD implicitly shares ownership of the wrapped file
> + * descriptor. The original SharedFD is left untouched, and the caller is
> + * responsible for destroying it when appropriate. The wrapped file descriptor
> + * will be closed automatically when all SharedFD instances that reference it
> + * are destroyed.
> + */
> +SharedFD::SharedFD(const SharedFD &other)
> +       : fd_(other.fd_)
> +{
> +}
> +
> +/**
> + * \brief Move constructor, create a SharedFD by taking over \a other
> + * \param[in] other The other SharedFD
> + *
> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> + * \a other to the new SharedFD. The \a other SharedFD is invalidated and its
> + * fd() function will return -1. The wrapped file descriptor will be closed
> + * automatically when all SharedFD instances that reference it are destroyed.
> + */
> +SharedFD::SharedFD(SharedFD &&other)
> +       : fd_(std::move(other.fd_))
> +{
> +}
> +
> +/**
> + * \brief Destroy the SharedFD instance
> + *
> + * Destroying a SharedFD instance releases its reference to the wrapped
> + * descriptor, if any. When the last instance that references a wrapped
> + * descriptor is destroyed, the file descriptor is automatically closed.
> + */
> +SharedFD::~SharedFD()
> +{
> +}
> +
> +/**
> + * \brief Copy assignment operator, replace the wrapped file descriptor with a
> + * copy of \a other
> + * \param[in] other The other SharedFD
> + *
> + * Copying a SharedFD creates a new reference to the wrapped file descriptor
> + * owner by \a other. If \a other is invalid, *this will also be invalid. The
> + * original SharedFD is left untouched, and the caller is responsible for
> + * destroying it when appropriate. The wrapped file descriptor will be closed
> + * automatically when all SharedFD instances that reference it are destroyed.
> + *
> + * \return A reference to this SharedFD
> + */
> +SharedFD &SharedFD::operator=(const SharedFD &other)
> +{
> +       fd_ = other.fd_;
> +
> +       return *this;
> +}
> +
> +/**
> + * \brief Move assignment operator, replace the wrapped file descriptor by
> + * taking over \a other
> + * \param[in] other The other SharedFD
> + *
> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> + * \a other to the new SharedFD. If \a other is invalid, *this will also be
> + * invalid. The \a other SharedFD is invalidated and its fd() function will
> + * return -1. The wrapped file descriptor will be closed automatically when
> + * all SharedFD instances that reference it are destroyed.
> + *
> + * \return A reference to this SharedFD
> + */
> +SharedFD &SharedFD::operator=(SharedFD &&other)
> +{
> +       fd_ = std::move(other.fd_);
> +
> +       return *this;
> +}
> +
> +/**
> + * \fn SharedFD::isValid()
> + * \brief Check if the SharedFD instance is valid
> + * \return True if the SharedFD is valid, false otherwise
> + */
> +
> +/**
> + * \fn SharedFD::fd()
> + * \brief Retrieve the numerical file descriptor
> + * \return The numerical file descriptor, which may be -1 if the SharedFD
> + * instance is invalid
> + */
> +
> +/**
> + * \brief Duplicate a SharedFD
> + *
> + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and
> + * returns a UniqueFD that owns the duplicate. The fd() function of the original
> + * and the get() function of the duplicate will return different values. The
> + * duplicate instance will not be affected by destruction of the original
> + * instance or its copies.
> + *
> + * \return A UniqueFD owning a duplicate of the original file descriptor
> + */
> +UniqueFD SharedFD::dup() const
> +{
> +       int dupFd = ::dup(fd());
> +       if (dupFd == -1) {
> +               int ret = -errno;
> +               LOG(SharedFD, Error)
> +                       << "Failed to dup() fd: " << strerror(-ret);
> +       }
> +
> +       return UniqueFD(dupFd);
> +}
> +
> +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)
> +{
> +       if (!duplicate) {
> +               fd_ = fd;
> +               return;
> +       }
> +
> +       /* Failing to dup() a fd should not happen and is fatal. */
> +       fd_ = ::dup(fd);
> +       if (fd_ == -1) {
> +               int ret = -errno;
> +               LOG(SharedFD, Fatal)
> +                       << "Failed to dup() fd: " << strerror(-ret);
> +       }
> +}
> +
> +SharedFD::Descriptor::~Descriptor()
> +{
> +       if (fd_ != -1)
> +               close(fd_);
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp
> index f5bcf107d7aa..0a5bf7fdbeb7 100644
> --- a/src/libcamera/framebuffer.cpp
> +++ b/src/libcamera/framebuffer.cpp
> @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()
>   * offset and length.
>   *
>   * To support DMA access, planes are associated with dmabuf objects represented
> - * by FileDescriptor handles. The Plane class doesn't handle mapping of the
> - * memory to the CPU, but applications and IPAs may use the dmabuf file
> - * descriptors to map the plane memory with mmap() and access its contents.
> + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to
> + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map
> + * the plane memory with mmap() and access its contents.
>   *
>   * \todo Specify how an application shall decide whether to use a single or
>   * multiple dmabufs, based on the camera requirements.
> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
> index 82ec9b20a411..0a259305afa2 100644
> --- a/src/libcamera/ipa_data_serializer.cpp
> +++ b/src/libcamera/ipa_data_serializer.cpp
> @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)
>   *
>   * \todo Harden the vector and map deserializer
>   *
> - * \todo For FileDescriptors, instead of storing a validity flag, store an
> + * \todo For SharedFDs, instead of storing a validity flag, store an
>   * index into the fd array. This will allow us to use views instead of copying.
>   */
>
> @@ -112,7 +112,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() can be used if the object type \a T and its
> - * members don't have any FileDescriptor.
> + * members don't have any SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -132,7 +132,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() can be used if the object type \a T and its
> - * members don't have any FileDescriptor.
> + * members don't have any SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -143,7 +143,7 @@ namespace {
>  /**
>   * \fn template<typename T> IPADataSerializer<T>::deserialize(
>   *     const std::vector<uint8_t> &data,
> - *     const std::vector<FileDescriptor> &fds,
> + *     const std::vector<SharedFD> &fds,
>   *     ControlSerializer *cs = nullptr)
>   * \brief Deserialize byte vector and fd vector into an object
>   * \tparam T Type of object to deserialize to
> @@ -152,7 +152,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() (or the iterator version) must be used if
> - * the object type \a T or its members contain FileDescriptor.
> + * the object type \a T or its members contain SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -164,8 +164,8 @@ namespace {
>   * \fn template<typename T> IPADataSerializer::deserialize(
>   *     std::vector<uint8_t>::const_iterator dataBegin,
>   *     std::vector<uint8_t>::const_iterator dataEnd,
> - *     std::vector<FileDescriptor>::const_iterator fdsBegin,
> - *     std::vector<FileDescriptor>::const_iterator fdsEnd,
> + *     std::vector<SharedFD>::const_iterator fdsBegin,
> + *     std::vector<SharedFD>::const_iterator fdsEnd,
>   *     ControlSerializer *cs = nullptr)
>   * \brief Deserialize byte vector and fd vector into an object
>   * \tparam T Type of object to deserialize to
> @@ -176,7 +176,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() (or the vector version) must be used if
> - * the object type \a T or its members contain FileDescriptor.
> + * the object type \a T or its members contain SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -189,7 +189,7 @@ namespace {
>  #define DEFINE_POD_SERIALIZER(type)                                    \
>                                                                         \
>  template<>                                                             \
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>          \
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>                \
>  IPADataSerializer<type>::serialize(const type &data,                   \
>                                   [[maybe_unused]] ControlSerializer *cs) \
>  {                                                                      \
> @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
>                                                                         \
>  template<>                                                             \
>  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> -                                         [[maybe_unused]] const std::vector<FileDescriptor> &fds, \
> +                                         [[maybe_unused]] const std::vector<SharedFD> &fds, \
>                                           ControlSerializer *cs)        \
>  {                                                                      \
>         return deserialize(data.cbegin(), data.end(), cs);              \
> @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
>  template<>                                                             \
>  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \
>                                           std::vector<uint8_t>::const_iterator dataEnd, \
> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \
> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \
> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \
> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \
>                                           ControlSerializer *cs)        \
>  {                                                                      \
>         return deserialize(dataBegin, dataEnd, cs);                     \
> @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)
>   * function parameter serdes).
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<std::string>::serialize(const std::string &data,
>                                           [[maybe_unused]] ControlSerializer *cs)
>  {
> @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
>  template<>
>  std::string
>  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,
> -                                           [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> +                                           [[maybe_unused]] const std::vector<SharedFD> &fds,
>                                             [[maybe_unused]] ControlSerializer *cs)
>  {
>         return { data.cbegin(), data.cend() };
> @@ -288,8 +288,8 @@ template<>
>  std::string
>  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                                             std::vector<uint8_t>::const_iterator dataEnd,
> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>                                             [[maybe_unused]] ControlSerializer *cs)
>  {
>         return { dataBegin, dataEnd };
> @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
>   * be used. The serialized ControlInfoMap will have zero length.
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)
>  {
>         if (!cs)
> @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
>  template<>
>  ControlList
>  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
> -                                           [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> +                                           [[maybe_unused]] const std::vector<SharedFD> &fds,
>                                             ControlSerializer *cs)
>  {
>         return deserialize(data.cbegin(), data.end(), cs);
> @@ -417,8 +417,8 @@ template<>
>  ControlList
>  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                                             std::vector<uint8_t>::const_iterator dataEnd,
> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>                                             ControlSerializer *cs)
>  {
>         return deserialize(dataBegin, dataEnd, cs);
> @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator
>   * X bytes - Serialized ControlInfoMap (using ControlSerializer)
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,
>                                              ControlSerializer *cs)
>  {
> @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
>  template<>
>  ControlInfoMap
>  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
> -                                              [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> +                                              [[maybe_unused]] const std::vector<SharedFD> &fds,
>                                                ControlSerializer *cs)
>  {
>         return deserialize(data.cbegin(), data.end(), cs);
> @@ -503,30 +503,30 @@ template<>
>  ControlInfoMap
>  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                                                std::vector<uint8_t>::const_iterator dataEnd,
> -                                              [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                                              [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                                              [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +                                              [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>                                                ControlSerializer *cs)
>  {
>         return deserialize(dataBegin, dataEnd, cs);
>  }
>
>  /*
> - * FileDescriptors are serialized into four bytes that tells if the
> - * FileDescriptor is valid or not. If it is valid, then for serialization
> - * the fd will be written to the fd vector, or for deserialization the
> - * fd vector const_iterator will be valid.
> + * SharedFD instances are serialized into four bytes that tells if the SharedFD
> + * is valid or not. If it is valid, then for serialization the fd will be
> + * written to the fd vector, or for deserialization the fd vector const_iterator
> + * will be valid.
>   *
>   * This validity is necessary so that we don't send -1 fd over sendmsg(). It
>   * also allows us to simply send the entire fd vector into the deserializer
>   * and it will be recursively consumed as necessary.
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
> -                                            [[maybe_unused]] ControlSerializer *cs)
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,
> +                                      [[maybe_unused]] ControlSerializer *cs)
>  {
>         std::vector<uint8_t> dataVec;
> -       std::vector<FileDescriptor> fdVec;
> +       std::vector<SharedFD> fdVec;
>
>         /*
>          * Store as uint32_t to prepare for conversion from validity flag
> @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
>  }
>
>  template<>
> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> -                                                             [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> -                                                             std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                                                             std::vector<FileDescriptor>::const_iterator fdsEnd,
> -                                                             [[maybe_unused]] ControlSerializer *cs)
> +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> +                                                 [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> +                                                 std::vector<SharedFD>::const_iterator fdsBegin,
> +                                                 std::vector<SharedFD>::const_iterator fdsEnd,
> +                                                 [[maybe_unused]] ControlSerializer *cs)
>  {
>         ASSERT(std::distance(dataBegin, dataEnd) >= 4);
>
> @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s
>
>         ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));
>
> -       return valid ? *fdsBegin : FileDescriptor();
> +       return valid ? *fdsBegin : SharedFD();
>  }
>
>  template<>
> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,
> -                                                             const std::vector<FileDescriptor> &fds,
> -                                                             [[maybe_unused]] ControlSerializer *cs)
> +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,
> +                                                 const std::vector<SharedFD> &fds,
> +                                                 [[maybe_unused]] ControlSerializer *cs)
>  {
>         return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());
>  }
> @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<
>  /*
>   * FrameBuffer::Plane is serialized as:
>   *
> - * 4 byte  - FileDescriptor
> + * 4 byte  - SharedFD
>   * 4 bytes - uint32_t Offset
>   * 4 bytes - uint32_t Length
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,
>                                                  [[maybe_unused]] ControlSerializer *cs)
>  {
>         std::vector<uint8_t> dataVec;
> -       std::vector<FileDescriptor> fdsVec;
> +       std::vector<SharedFD> fdsVec;
>
>         std::vector<uint8_t> fdBuf;
> -       std::vector<FileDescriptor> fdFds;
> +       std::vector<SharedFD> fdFds;
>         std::tie(fdBuf, fdFds) =
> -               IPADataSerializer<FileDescriptor>::serialize(data.fd);
> +               IPADataSerializer<SharedFD>::serialize(data.fd);
>         dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());
>         fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());
>
> @@ -597,13 +597,13 @@ template<>
>  FrameBuffer::Plane
>  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                                                    std::vector<uint8_t>::const_iterator dataEnd,
> -                                                  std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                                                  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                                                  std::vector<SharedFD>::const_iterator fdsBegin,
> +                                                  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>                                                    [[maybe_unused]] ControlSerializer *cs)
>  {
>         FrameBuffer::Plane ret;
>
> -       ret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,
> +       ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,
>                                                                 fdsBegin, fdsBegin + 1);
>         ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);
>         ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);
> @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i
>  template<>
>  FrameBuffer::Plane
>  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,
> -                                                  const std::vector<FileDescriptor> &fds,
> +                                                  const std::vector<SharedFD> &fds,
>                                                    ControlSerializer *cs)
>  {
>         return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp
> index ad870fd4137f..3b47032de0a2 100644
> --- a/src/libcamera/ipc_pipe.cpp
> +++ b/src/libcamera/ipc_pipe.cpp
> @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)
>         data_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),
>                                      payload.data.end());
>         for (int32_t &fd : payload.fds)
> -               fds_.push_back(FileDescriptor(std::move(fd)));
> +               fds_.push_back(SharedFD(std::move(fd)));
>  }
>
>  /**
> @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const
>                        data_.data(), data_.size());
>         }
>
> -       for (const FileDescriptor &fd : fds_)
> +       for (const SharedFD &fd : fds_)
>                 payload.fds.push_back(fd.fd());
>
>         return payload;
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index ffa51a0c65ca..ea8243912a29 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -12,7 +12,7 @@
>  #include <queue>
>  #include <unordered_set>
>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>
>  #include <libcamera/camera.h>
>  #include <libcamera/control_ids.h>
> @@ -228,7 +228,7 @@ public:
>
>         /* DMAHEAP allocation helper. */
>         RPi::DmaHeap dmaHeap_;
> -       FileDescriptor lsTable_;
> +       SharedFD lsTable_;
>
>         std::unique_ptr<DelayedControls> delayedCtrls_;
>         bool sensorMetadata_;
> @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)
>                 if (!fd.isValid())
>                         return -ENOMEM;
>
> -               lsTable_ = FileDescriptor(std::move(fd));
> +               lsTable_ = SharedFD(std::move(fd));
>
>                 /* Allow the IPA to mmap the LS table via the file descriptor. */
>                 /*
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 3966483a365f..97d431071def 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -22,8 +22,8 @@
>  #include <linux/version.h>
>
>  #include <libcamera/base/event_notifier.h>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/log.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/unique_fd.h>
>  #include <libcamera/base/utils.h>
>
> @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
>                         return nullptr;
>
>                 FrameBuffer::Plane plane;
> -               plane.fd = FileDescriptor(std::move(fd));
> +               plane.fd = SharedFD(std::move(fd));
>                 /*
>                  * V4L2 API doesn't provide dmabuf offset information of plane.
>                  * Set 0 as a placeholder offset.
> @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
>                 ASSERT(numPlanes == 1u);
>
>                 planes.resize(formatInfo_->numPlanes());
> -               const FileDescriptor &fd = planes[0].fd;
> +               const SharedFD &fd = planes[0].fd;
>                 size_t offset = 0;
>
>                 for (auto [i, plane] : utils::enumerate(planes)) {
> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
> index 9817fd393d59..586347829845 100644
> --- a/src/v4l2/v4l2_camera.h
> +++ b/src/v4l2/v4l2_camera.h
> @@ -11,8 +11,8 @@
>  #include <mutex>
>  #include <utility>
>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/semaphore.h>
> +#include <libcamera/base/shared_fd.h>
>
>  #include <libcamera/camera.h>
>  #include <libcamera/framebuffer.h>
> diff --git a/test/meson.build b/test/meson.build
> index 42dfbc1f8ee9..daaa3862cdd6 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -40,7 +40,6 @@ internal_tests = [
>      ['event-dispatcher',                'event-dispatcher.cpp'],
>      ['event-thread',                    'event-thread.cpp'],
>      ['file',                            'file.cpp'],
> -    ['file-descriptor',                 'file-descriptor.cpp'],
>      ['flags',                           'flags.cpp'],
>      ['hotplug-cameras',                 'hotplug-cameras.cpp'],
>      ['mapped-buffer',                   'mapped-buffer.cpp'],
> @@ -49,6 +48,7 @@ internal_tests = [
>      ['object-delete',                   'object-delete.cpp'],
>      ['object-invoke',                   'object-invoke.cpp'],
>      ['pixel-format',                    'pixel-format.cpp'],
> +    ['shared-fd',                       'shared-fd.cpp'],
>      ['signal-threads',                  'signal-threads.cpp'],
>      ['threads',                         'threads.cpp'],
>      ['timer',                           'timer.cpp'],
> diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
> index 5fcdcb8eae92..d2050a868b38 100644
> --- a/test/serialization/ipa_data_serializer_test.cpp
> +++ b/test/serialization/ipa_data_serializer_test.cpp
> @@ -53,7 +53,7 @@ template<typename T>
>  int testPodSerdes(T in)
>  {
>         std::vector<uint8_t> buf;
> -       std::vector<FileDescriptor> fds;
> +       std::vector<SharedFD> fds;
>
>         std::tie(buf, fds) = IPADataSerializer<T>::serialize(in);
>         T out = IPADataSerializer<T>::deserialize(buf, fds);
> @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,
>                      ControlSerializer *cs = nullptr)
>  {
>         std::vector<uint8_t> buf;
> -       std::vector<FileDescriptor> fds;
> +       std::vector<SharedFD> fds;
>
>         std::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);
>         std::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);
> @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,
>                   ControlSerializer *cs = nullptr)
>  {
>         std::vector<uint8_t> buf;
> -       std::vector<FileDescriptor> fds;
> +       std::vector<SharedFD> fds;
>
>         std::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);
>         std::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);
> @@ -198,7 +198,7 @@ private:
>                 ControlSerializer cs(ControlSerializer::Role::Proxy);
>
>                 /*
> -                * We don't test FileDescriptor serdes because it dup()s, so we
> +                * We don't test SharedFD serdes because it dup()s, so we
>                  * can't check for equality.
>                  */
>                 std::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };
> @@ -219,7 +219,7 @@ private:
>                 };
>
>                 std::vector<uint8_t> buf;
> -               std::vector<FileDescriptor> fds;
> +               std::vector<SharedFD> fds;
>
>                 if (testVectorSerdes(vecUint8) != TestPass)
>                         return TestFail;
> @@ -291,7 +291,7 @@ private:
>                         { { "a", { 1, 2, 3 } }, { "b", { 4, 5, 6 } }, { "c", { 7, 8, 9 } } };
>
>                 std::vector<uint8_t> buf;
> -               std::vector<FileDescriptor> fds;
> +               std::vector<SharedFD> fds;
>
>                 if (testMapSerdes(mapUintStr) != TestPass)
>                         return TestFail;
> @@ -359,7 +359,7 @@ private:
>                 std::string strEmpty = "";
>
>                 std::vector<uint8_t> buf;
> -               std::vector<FileDescriptor> fds;
> +               std::vector<SharedFD> fds;
>
>                 if (testPodSerdes(u32min) != TestPass)
>                         return TestFail;
> diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp
> similarity index 80%
> rename from test/file-descriptor.cpp
> rename to test/shared-fd.cpp
> index 76badc4c5fad..60e5d0aaa395 100644
> --- a/test/file-descriptor.cpp
> +++ b/test/shared-fd.cpp
> @@ -2,7 +2,7 @@
>  /*
>   * Copyright (C) 2019, Google Inc.
>   *
> - * file_descriptor.cpp - FileDescriptor test
> + * shared_fd.cpp - SharedFD test
>   */
>
>  #include <fcntl.h>
> @@ -11,7 +11,7 @@
>  #include <sys/types.h>
>  #include <unistd.h>
>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/utils.h>
>
>  #include "test.h"
> @@ -19,7 +19,7 @@
>  using namespace libcamera;
>  using namespace std;
>
> -class FileDescriptorTest : public Test
> +class SharedFDTest : public Test
>  {
>  protected:
>         int init()
> @@ -43,8 +43,8 @@ protected:
>
>         int run()
>         {
> -               /* Test creating empty FileDescriptor. */
> -               desc1_ = new FileDescriptor();
> +               /* Test creating empty SharedFD. */
> +               desc1_ = new SharedFD();
>
>                 if (desc1_->fd() != -1) {
>                         std::cout << "Failed fd numerical check (default constructor)"
> @@ -56,10 +56,10 @@ protected:
>                 desc1_ = nullptr;
>
>                 /*
> -                * Test creating FileDescriptor by copying numerical file
> +                * Test creating SharedFD by copying numerical file
>                  * descriptor.
>                  */
> -               desc1_ = new FileDescriptor(fd_);
> +               desc1_ = new SharedFD(fd_);
>                 if (desc1_->fd() == fd_) {
>                         std::cout << "Failed fd numerical check (lvalue ref constructor)"
>                                   << std::endl;
> @@ -84,13 +84,13 @@ protected:
>                 }
>
>                 /*
> -                * Test creating FileDescriptor by taking ownership of
> +                * Test creating SharedFD by taking ownership of
>                  * numerical file descriptor.
>                  */
>                 int dupFd = dup(fd_);
>                 int dupFdCopy = dupFd;
>
> -               desc1_ = new FileDescriptor(std::move(dupFd));
> +               desc1_ = new SharedFD(std::move(dupFd));
>                 if (desc1_->fd() != dupFdCopy) {
>                         std::cout << "Failed fd numerical check (rvalue ref constructor)"
>                                   << std::endl;
> @@ -114,9 +114,9 @@ protected:
>                         return TestFail;
>                 }
>
> -               /* Test creating FileDescriptor from other FileDescriptor. */
> -               desc1_ = new FileDescriptor(fd_);
> -               desc2_ = new FileDescriptor(*desc1_);
> +               /* Test creating SharedFD from other SharedFD. */
> +               desc1_ = new SharedFD(fd_);
> +               desc2_ = new SharedFD(*desc1_);
>
>                 if (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {
>                         std::cout << "Failed fd numerical check (copy constructor)"
> @@ -142,10 +142,10 @@ protected:
>                 delete desc2_;
>                 desc2_ = nullptr;
>
> -               /* Test creating FileDescriptor by taking over other FileDescriptor. */
> -               desc1_ = new FileDescriptor(fd_);
> +               /* Test creating SharedFD by taking over other SharedFD. */
> +               desc1_ = new SharedFD(fd_);
>                 fd = desc1_->fd();
> -               desc2_ = new FileDescriptor(std::move(*desc1_));
> +               desc2_ = new SharedFD(std::move(*desc1_));
>
>                 if (desc1_->fd() != -1 || desc2_->fd() != fd) {
>                         std::cout << "Failed fd numerical check (move constructor)"
> @@ -164,9 +164,9 @@ protected:
>                 delete desc2_;
>                 desc2_ = nullptr;
>
> -               /* Test creating FileDescriptor by copy assignment. */
> -               desc1_ = new FileDescriptor();
> -               desc2_ = new FileDescriptor(fd_);
> +               /* Test creating SharedFD by copy assignment. */
> +               desc1_ = new SharedFD();
> +               desc2_ = new SharedFD(fd_);
>
>                 fd = desc2_->fd();
>                 *desc1_ = *desc2_;
> @@ -188,9 +188,9 @@ protected:
>                 delete desc2_;
>                 desc2_ = nullptr;
>
> -               /* Test creating FileDescriptor by move assignment. */
> -               desc1_ = new FileDescriptor();
> -               desc2_ = new FileDescriptor(fd_);
> +               /* Test creating SharedFD by move assignment. */
> +               desc1_ = new SharedFD();
> +               desc2_ = new SharedFD(fd_);
>
>                 fd = desc2_->fd();
>                 *desc1_ = std::move(*desc2_);
> @@ -237,7 +237,7 @@ private:
>
>         int fd_;
>         ino_t inodeNr_;
> -       FileDescriptor *desc1_, *desc2_;
> +       SharedFD *desc1_, *desc2_;
>  };
>
> -TEST_REGISTER(FileDescriptorTest)
> +TEST_REGISTER(SharedFDTest)
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> index d856339aa9ee..c37c4941b528 100644
> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)
>  void {{proxy_name}}::{{method.mojom_name}}IPC(
>         std::vector<uint8_t>::const_iterator data,
>         size_t dataSize,
> -       [[maybe_unused]] const std::vector<FileDescriptor> &fds)
> +       [[maybe_unused]] const std::vector<SharedFD> &fds)
>  {
>  {%- for param in method.parameters %}
>         {{param|name}} {{param.mojom_name}};
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> index ce396c183d0c..c308dd10c7e5 100644
> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> @@ -64,7 +64,7 @@ private:
>         void {{method.mojom_name}}IPC(
>                 std::vector<uint8_t>::const_iterator data,
>                 size_t dataSize,
> -               const std::vector<FileDescriptor> &fds);
> +               const std::vector<SharedFD> &fds);
>  {% endfor %}
>
>         /* Helper class to invoke async functions in another thread. */
> diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> index ebcd2aaaafae..bac826a74c2d 100644
> --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> @@ -54,7 +54,7 @@
>  {%- for param in params %}
>         std::vector<uint8_t> {{param.mojom_name}}Buf;
>  {%- if param|has_fd %}
> -       std::vector<FileDescriptor> {{param.mojom_name}}Fds;
> +       std::vector<SharedFD> {{param.mojom_name}}Fds;
>         std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =
>  {%- else %}
>         std::tie({{param.mojom_name}}Buf, std::ignore) =
> diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> index b8ef8e7b974e..77bae36fe6b7 100644
> --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> @@ -40,7 +40,7 @@
>                 retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
>  {%- elif field|is_fd %}
>                 std::vector<uint8_t> {{field.mojom_name}};
> -               std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> +               std::vector<SharedFD> {{field.mojom_name}}Fds;
>                 std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
>                         IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
>                 retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> @@ -58,7 +58,7 @@
>  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
>                 std::vector<uint8_t> {{field.mojom_name}};
>         {%- if field|has_fd %}
> -               std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> +               std::vector<SharedFD> {{field.mojom_name}}Fds;
>                 std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
>         {%- else %}
>                 std::tie({{field.mojom_name}}, std::ignore) =
> @@ -177,7 +177,7 @@
>   # \a struct.
>   #}
>  {%- macro serializer(struct, namespace) %}
> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>         serialize(const {{struct|name_full}} &data,
>  {%- if struct|needs_control_serializer %}
>                   ControlSerializer *cs)
> @@ -187,7 +187,7 @@
>         {
>                 std::vector<uint8_t> retData;
>  {%- if struct|has_fd %}
> -               std::vector<FileDescriptor> retFds;
> +               std::vector<SharedFD> retFds;
>  {%- endif %}
>  {%- for field in struct.fields %}
>  {{serializer_field(field, namespace, loop)}}
> @@ -210,7 +210,7 @@
>  {%- macro deserializer_fd(struct, namespace) %}
>         static {{struct|name_full}}
>         deserialize(std::vector<uint8_t> &data,
> -                   std::vector<FileDescriptor> &fds,
> +                   std::vector<SharedFD> &fds,
>  {%- if struct|needs_control_serializer %}
>                     ControlSerializer *cs)
>  {%- else %}
> @@ -224,8 +224,8 @@
>         static {{struct|name_full}}
>         deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                     std::vector<uint8_t>::const_iterator dataEnd,
> -                   std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                   std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                   std::vector<SharedFD>::const_iterator fdsBegin,
> +                   std::vector<SharedFD>::const_iterator fdsEnd,
>  {%- if struct|needs_control_serializer %}
>                     ControlSerializer *cs)
>  {%- else %}
> @@ -234,7 +234,7 @@
>         {
>                 {{struct|name_full}} ret;
>                 std::vector<uint8_t>::const_iterator m = dataBegin;
> -               std::vector<FileDescriptor>::const_iterator n = fdsBegin;
> +               std::vector<SharedFD>::const_iterator n = fdsBegin;
>
>                 size_t dataSize = std::distance(dataBegin, dataEnd);
>                 [[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
> @@ -255,7 +255,7 @@
>  {%- macro deserializer_fd_simple(struct, namespace) %}
>         static {{struct|name_full}}
>         deserialize(std::vector<uint8_t> &data,
> -                   [[maybe_unused]] std::vector<FileDescriptor> &fds,
> +                   [[maybe_unused]] std::vector<SharedFD> &fds,
>                     ControlSerializer *cs = nullptr)
>         {
>                 return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);
> @@ -264,8 +264,8 @@
>         static {{struct|name_full}}
>         deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>                     std::vector<uint8_t>::const_iterator dataEnd,
> -                   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -                   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +                   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +                   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>                     ControlSerializer *cs = nullptr)
>         {
>                 return IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);
> diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py
> index c609f4e5c062..753bfc734e56 100644
> --- a/utils/ipc/generators/mojom_libcamera_generator.py
> +++ b/utils/ipc/generators/mojom_libcamera_generator.py
> @@ -77,7 +77,7 @@ def GetDefaultValue(element):
>      if mojom.IsEnumKind(element.kind):
>          return f'static_cast<{element.kind.mojom_name}>(0)'
>      if isinstance(element.kind, mojom.Struct) and \
> -       element.kind.mojom_name == 'FileDescriptor':
> +       element.kind.mojom_name == 'SharedFD':
>          return '-1'
>      return ''
>
> @@ -140,7 +140,7 @@ def HasFd(element):
>          types = GetAllTypes(element)
>      else:
>          types = GetAllTypes(element.kind)
> -    return "FileDescriptor" in types or (attrs is not None and "hasFd" in attrs)
> +    return "SharedFD" in types or (attrs is not None and "hasFd" in attrs)
>
>  def WithDefaultValues(element):
>      return [x for x in element if HasDefaultValue(x)]
> @@ -221,7 +221,7 @@ def IsEnum(element):
>      return mojom.IsEnumKind(element.kind)
>
>  def IsFd(element):
> -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "FileDescriptor"
> +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "SharedFD"
>
>  def IsMap(element):
>      return mojom.IsMapKind(element.kind)
> --
> Regards,
>
> Laurent Pinchart
>
Jacopo Mondi Nov. 29, 2021, 4 p.m. UTC | #2
Hi Laurent

On Mon, Nov 29, 2021 at 01:57:52AM +0200, Laurent Pinchart wrote:
> Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.
> Rename it to SharedFD.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

makes sense!

Thanks
  j

> ---
>  include/libcamera/base/file.h                 |   4 +-
>  include/libcamera/base/meson.build            |   2 +-
>  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-
>  include/libcamera/framebuffer.h               |   4 +-
>  .../libcamera/internal/ipa_data_serializer.h  |  40 +--
>  include/libcamera/internal/ipc_pipe.h         |   8 +-
>  include/libcamera/ipa/core.mojom              |   6 +-
>  include/libcamera/ipa/raspberrypi.mojom       |   2 +-
>  src/android/camera_device.cpp                 |   2 +-
>  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-
>  src/libcamera/base/file.cpp                   |   6 +-
>  src/libcamera/base/file_descriptor.cpp        | 266 ------------------
>  src/libcamera/base/meson.build                |   2 +-
>  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++
>  src/libcamera/framebuffer.cpp                 |   6 +-
>  src/libcamera/ipa_data_serializer.cpp         | 100 +++----
>  src/libcamera/ipc_pipe.cpp                    |   4 +-
>  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-
>  src/libcamera/v4l2_videodevice.cpp            |   6 +-
>  src/v4l2/v4l2_camera.h                        |   2 +-
>  test/meson.build                              |   2 +-
>  .../ipa_data_serializer_test.cpp              |  14 +-
>  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--
>  .../module_ipa_proxy.cpp.tmpl                 |   2 +-
>  .../module_ipa_proxy.h.tmpl                   |   2 +-
>  .../libcamera_templates/proxy_functions.tmpl  |   2 +-
>  .../libcamera_templates/serializer.tmpl       |  22 +-
>  .../generators/mojom_libcamera_generator.py   |   6 +-
>  28 files changed, 422 insertions(+), 426 deletions(-)
>  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)
>  delete mode 100644 src/libcamera/base/file_descriptor.cpp
>  create mode 100644 src/libcamera/base/shared_fd.cpp
>  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)
>
> diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h
> index 47769da7abc2..691b52d6ab2d 100644
> --- a/include/libcamera/base/file.h
> +++ b/include/libcamera/base/file.h
> @@ -21,7 +21,7 @@
>
>  namespace libcamera {
>
> -class FileDescriptor;
> +class SharedFD;
>
>  class File
>  {
> @@ -69,7 +69,7 @@ public:
>  	bool unmap(uint8_t *addr);
>
>  	static bool exists(const std::string &name);
> -	static ino_t inode(const FileDescriptor &fd);
> +	static ino_t inode(const SharedFD &fd);
>
>  private:
>  	LIBCAMERA_DISABLE_COPY(File)
> diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
> index cca374a769cc..112420dab225 100644
> --- a/include/libcamera/base/meson.build
> +++ b/include/libcamera/base/meson.build
> @@ -11,13 +11,13 @@ libcamera_base_headers = files([
>      'event_dispatcher_poll.h',
>      'event_notifier.h',
>      'file.h',
> -    'file_descriptor.h',
>      'flags.h',
>      'log.h',
>      'message.h',
>      'object.h',
>      'private.h',
>      'semaphore.h',
> +    'shared_fd.h',
>      'signal.h',
>      'span.h',
>      'thread.h',
> diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h
> similarity index 55%
> rename from include/libcamera/base/file_descriptor.h
> rename to include/libcamera/base/shared_fd.h
> index 12a43f95d414..a786885ceb32 100644
> --- a/include/libcamera/base/file_descriptor.h
> +++ b/include/libcamera/base/shared_fd.h
> @@ -2,7 +2,7 @@
>  /*
>   * Copyright (C) 2019, Google Inc.
>   *
> - * file_descriptor.h - File descriptor wrapper
> + * shared_fd.h - File descriptor wrapper with shared ownership
>   */
>
>  #pragma once
> @@ -13,18 +13,18 @@ namespace libcamera {
>
>  class UniqueFD;
>
> -class FileDescriptor final
> +class SharedFD final
>  {
>  public:
> -	explicit FileDescriptor(const int &fd = -1);
> -	explicit FileDescriptor(int &&fd);
> -	explicit FileDescriptor(UniqueFD fd);
> -	FileDescriptor(const FileDescriptor &other);
> -	FileDescriptor(FileDescriptor &&other);
> -	~FileDescriptor();
> +	explicit SharedFD(const int &fd = -1);
> +	explicit SharedFD(int &&fd);
> +	explicit SharedFD(UniqueFD fd);
> +	SharedFD(const SharedFD &other);
> +	SharedFD(SharedFD &&other);
> +	~SharedFD();
>
> -	FileDescriptor &operator=(const FileDescriptor &other);
> -	FileDescriptor &operator=(FileDescriptor &&other);
> +	SharedFD &operator=(const SharedFD &other);
> +	SharedFD &operator=(SharedFD &&other);
>
>  	bool isValid() const { return fd_ != nullptr; }
>  	int fd() const { return fd_ ? fd_->fd() : -1; }
> diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h
> index 2fbea9c5be16..357bbe189551 100644
> --- a/include/libcamera/framebuffer.h
> +++ b/include/libcamera/framebuffer.h
> @@ -13,7 +13,7 @@
>  #include <vector>
>
>  #include <libcamera/base/class.h>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/span.h>
>
>  namespace libcamera {
> @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible
>  public:
>  	struct Plane {
>  		static constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();
> -		FileDescriptor fd;
> +		SharedFD fd;
>  		unsigned int offset = kInvalidOffset;
>  		unsigned int length;
>  	};
> diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
> index c2f602d5b7de..a87449c9be48 100644
> --- a/include/libcamera/internal/ipa_data_serializer.h
> +++ b/include/libcamera/internal/ipa_data_serializer.h
> @@ -66,7 +66,7 @@ template<typename T>
>  class IPADataSerializer
>  {
>  public:
> -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  	serialize(const T &data, ControlSerializer *cs = nullptr);
>
>  	static T deserialize(const std::vector<uint8_t> &data,
> @@ -76,12 +76,12 @@ public:
>  			     ControlSerializer *cs = nullptr);
>
>  	static T deserialize(const std::vector<uint8_t> &data,
> -			     const std::vector<FileDescriptor> &fds,
> +			     const std::vector<SharedFD> &fds,
>  			     ControlSerializer *cs = nullptr);
>  	static T deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  			     std::vector<uint8_t>::const_iterator dataEnd,
> -			     std::vector<FileDescriptor>::const_iterator fdsBegin,
> -			     std::vector<FileDescriptor>::const_iterator fdsEnd,
> +			     std::vector<SharedFD>::const_iterator fdsBegin,
> +			     std::vector<SharedFD>::const_iterator fdsEnd,
>  			     ControlSerializer *cs = nullptr);
>  };
>
> @@ -104,11 +104,11 @@ template<typename V>
>  class IPADataSerializer<std::vector<V>>
>  {
>  public:
> -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
>  	{
>  		std::vector<uint8_t> dataVec;
> -		std::vector<FileDescriptor> fdsVec;
> +		std::vector<SharedFD> fdsVec;
>
>  		/* Serialize the length. */
>  		uint32_t vecLen = data.size();
> @@ -117,7 +117,7 @@ public:
>  		/* Serialize the members. */
>  		for (auto const &it : data) {
>  			std::vector<uint8_t> dvec;
> -			std::vector<FileDescriptor> fvec;
> +			std::vector<SharedFD> fvec;
>
>  			std::tie(dvec, fvec) =
>  				IPADataSerializer<V>::serialize(it, cs);
> @@ -141,11 +141,11 @@ public:
>  					  std::vector<uint8_t>::const_iterator dataEnd,
>  					  ControlSerializer *cs = nullptr)
>  	{
> -		std::vector<FileDescriptor> fds;
> +		std::vector<SharedFD> fds;
>  		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
>  	}
>
> -	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> +	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
>  					  ControlSerializer *cs = nullptr)
>  	{
>  		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> @@ -153,15 +153,15 @@ public:
>
>  	static std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  					  std::vector<uint8_t>::const_iterator dataEnd,
> -					  std::vector<FileDescriptor>::const_iterator fdsBegin,
> -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +					  std::vector<SharedFD>::const_iterator fdsBegin,
> +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>  					  ControlSerializer *cs = nullptr)
>  	{
>  		uint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
>  		std::vector<V> ret(vecLen);
>
>  		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> -		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> +		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
>  		for (uint32_t i = 0; i < vecLen; i++) {
>  			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
>  			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> @@ -201,11 +201,11 @@ template<typename K, typename V>
>  class IPADataSerializer<std::map<K, V>>
>  {
>  public:
> -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
>  	{
>  		std::vector<uint8_t> dataVec;
> -		std::vector<FileDescriptor> fdsVec;
> +		std::vector<SharedFD> fdsVec;
>
>  		/* Serialize the length. */
>  		uint32_t mapLen = data.size();
> @@ -214,7 +214,7 @@ public:
>  		/* Serialize the members. */
>  		for (auto const &it : data) {
>  			std::vector<uint8_t> dvec;
> -			std::vector<FileDescriptor> fvec;
> +			std::vector<SharedFD> fvec;
>
>  			std::tie(dvec, fvec) =
>  				IPADataSerializer<K>::serialize(it.first, cs);
> @@ -247,11 +247,11 @@ public:
>  					  std::vector<uint8_t>::const_iterator dataEnd,
>  					  ControlSerializer *cs = nullptr)
>  	{
> -		std::vector<FileDescriptor> fds;
> +		std::vector<SharedFD> fds;
>  		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
>  	}
>
> -	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
>  					  ControlSerializer *cs = nullptr)
>  	{
>  		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> @@ -259,8 +259,8 @@ public:
>
>  	static std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  					  std::vector<uint8_t>::const_iterator dataEnd,
> -					  std::vector<FileDescriptor>::const_iterator fdsBegin,
> -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +					  std::vector<SharedFD>::const_iterator fdsBegin,
> +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>  					  ControlSerializer *cs = nullptr)
>  	{
>  		std::map<K, V> ret;
> @@ -268,7 +268,7 @@ public:
>  		uint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
>
>  		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> -		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> +		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
>  		for (uint32_t i = 0; i < mapLen; i++) {
>  			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
>  			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h
> index 986f8d886fa6..ab5dd67c3813 100644
> --- a/include/libcamera/internal/ipc_pipe.h
> +++ b/include/libcamera/internal/ipc_pipe.h
> @@ -9,7 +9,7 @@
>
>  #include <vector>
>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/signal.h>
>
>  #include "libcamera/internal/ipc_unixsocket.h"
> @@ -33,17 +33,17 @@ public:
>
>  	Header &header() { return header_; }
>  	std::vector<uint8_t> &data() { return data_; }
> -	std::vector<FileDescriptor> &fds() { return fds_; }
> +	std::vector<SharedFD> &fds() { return fds_; }
>
>  	const Header &header() const { return header_; }
>  	const std::vector<uint8_t> &data() const { return data_; }
> -	const std::vector<FileDescriptor> &fds() const { return fds_; }
> +	const std::vector<SharedFD> &fds() const { return fds_; }
>
>  private:
>  	Header header_;
>
>  	std::vector<uint8_t> data_;
> -	std::vector<FileDescriptor> fds_;
> +	std::vector<SharedFD> fds_;
>  };
>
>  class IPCPipe
> diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom
> index f7eff0c7ab8c..74f3339e56f2 100644
> --- a/include/libcamera/ipa/core.mojom
> +++ b/include/libcamera/ipa/core.mojom
> @@ -32,7 +32,7 @@ module libcamera;
>   *   - This attribute instructs the build system that a (de)serializer is
>   *     available for the type and there's no need to generate one
>   * - hasFd - struct fields or empty structs only
> - *   - Designate that this field or empty struct contains a FileDescriptor
> + *   - Designate that this field or empty struct contains a SharedFD
>   *
>   * Rules:
>   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is
> @@ -60,7 +60,7 @@ module libcamera;
>   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array
>   *       member
>   * - [skipHeader] and [skipSerdes] only work here in core.mojom
> - * - If a field in a struct has a FileDescriptor, but is not explicitly
> + * - If a field in a struct has a SharedFD, but is not explicitly
>   *   defined so in mojom, then the field must be marked with the [hasFd]
>   *   attribute
>   *
> @@ -71,7 +71,7 @@ module libcamera;
>   */
>  [skipSerdes, skipHeader] struct ControlInfoMap {};
>  [skipSerdes, skipHeader] struct ControlList {};
> -[skipSerdes, skipHeader] struct FileDescriptor {};
> +[skipSerdes, skipHeader] struct SharedFD {};
>
>  [skipHeader] struct Point {
>  	int32 x;
> diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
> index e453d46cb14f..acd3cafe6c91 100644
> --- a/include/libcamera/ipa/raspberrypi.mojom
> +++ b/include/libcamera/ipa/raspberrypi.mojom
> @@ -35,7 +35,7 @@ struct ISPConfig {
>
>  struct IPAConfig {
>  	uint32 transform;
> -	libcamera.FileDescriptor lsTableHandle;
> +	libcamera.SharedFD lsTableHandle;
>  };
>
>  struct StartConfig {
> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> index f2e0bdbdbbf6..1938b10509fa 100644
> --- a/src/android/camera_device.cpp
> +++ b/src/android/camera_device.cpp
> @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,
>
>  	std::vector<FrameBuffer::Plane> planes(buf.numPlanes());
>  	for (size_t i = 0; i < buf.numPlanes(); ++i) {
> -		FileDescriptor fd{ camera3buffer->data[i] };
> +		SharedFD fd{ camera3buffer->data[i] };
>  		if (!fd.isValid()) {
>  			LOG(HAL, Fatal) << "No valid fd";
>  			return nullptr;
> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
> index c6aec09046f7..aaf629eeb3fc 100644
> --- a/src/ipa/raspberrypi/raspberrypi.cpp
> +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> @@ -15,8 +15,8 @@
>
>  #include <linux/bcm2835-isp.h>
>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/log.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/span.h>
>
>  #include <libcamera/control_ids.h>
> @@ -164,7 +164,7 @@ private:
>  	bool processPending_;
>
>  	/* LS table allocation passed in from the pipeline handler. */
> -	FileDescriptor lsTableHandle_;
> +	SharedFD lsTableHandle_;
>  	void *lsTable_;
>
>  	/* Distinguish the first camera start from others. */
> diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp
> index 66c73c406198..3ca9839bb989 100644
> --- a/src/libcamera/base/file.cpp
> +++ b/src/libcamera/base/file.cpp
> @@ -14,8 +14,8 @@
>  #include <sys/types.h>
>  #include <unistd.h>
>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/log.h>
> +#include <libcamera/base/shared_fd.h>
>
>  /**
>   * \file base/file.h
> @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)
>  }
>
>  /**
> - * \brief Retrieve the inode of a FileDescriptor
> + * \brief Retrieve the inode of a SharedFD
>   *
>   * \return The file descriptor inode on success, or 0 on error
>   */
> -ino_t File::inode(const FileDescriptor &fd)
> +ino_t File::inode(const SharedFD &fd)
>  {
>  	if (!fd.isValid())
>  		return 0;
> diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp
> deleted file mode 100644
> index a83bf52c31e6..000000000000
> --- a/src/libcamera/base/file_descriptor.cpp
> +++ /dev/null
> @@ -1,266 +0,0 @@
> -/* SPDX-License-Identifier: LGPL-2.1-or-later */
> -/*
> - * Copyright (C) 2019, Google Inc.
> - *
> - * file_descriptor.cpp - File descriptor wrapper
> - */
> -
> -#include <libcamera/base/file_descriptor.h>
> -
> -#include <string.h>
> -#include <sys/types.h>
> -#include <unistd.h>
> -#include <utility>
> -
> -#include <libcamera/base/log.h>
> -#include <libcamera/base/unique_fd.h>
> -
> -/**
> - * \file base/file_descriptor.h
> - * \brief File descriptor wrapper
> - */
> -
> -namespace libcamera {
> -
> -LOG_DEFINE_CATEGORY(FileDescriptor)
> -
> -/**
> - * \class FileDescriptor
> - * \brief RAII-style wrapper for file descriptors
> - *
> - * The FileDescriptor class provides RAII-style lifetime management of file
> - * descriptors with an efficient mechanism for ownership sharing. At its core,
> - * an internal Descriptor object wraps a file descriptor (expressed as a signed
> - * integer) with an RAII-style interface. The Descriptor is then implicitly
> - * shared with all FileDescriptor instances constructed as copies.
> - *
> - * When constructed from a numerical file descriptor, the FileDescriptor
> - * instance either duplicates or takes over the file descriptor:
> - *
> - * - The FileDescriptor(const int &) constructor duplicates the numerical file
> - *   descriptor and wraps the duplicate in a Descriptor. The caller is
> - *   responsible for closing the original file descriptor, and the value
> - *   returned by fd() will be different from the value passed to the
> - *   constructor.
> - *
> - * - The FileDescriptor(int &&) constructor takes over the numerical file
> - *   descriptor and wraps it in a Descriptor. The caller shall not touch the
> - *   original file descriptor once the function returns, and the value returned
> - *   by fd() will be identical to the value passed to the constructor.
> - *
> - * The copy constructor and assignment operator create copies that share the
> - * Descriptor, while the move versions of those functions additionally make the
> - * other FileDescriptor invalid. When the last FileDescriptor that references a
> - * Descriptor is destroyed, the file descriptor is closed.
> - *
> - * The numerical file descriptor is available through the fd() function. All
> - * FileDescriptor instances created as copies of a FileDescriptor will report
> - * the same fd() value. Callers can perform operations on the fd(), but shall
> - * never close it manually.
> - */
> -
> -/**
> - * \brief Create a FileDescriptor copying a given \a fd
> - * \param[in] fd File descriptor
> - *
> - * Construct a FileDescriptor from a numerical file descriptor by duplicating
> - * the \a fd, and take ownership of the copy. The original \a fd is left
> - * untouched, and the caller is responsible for closing it when appropriate.
> - * The duplicated file descriptor will be closed automatically when all
> - * FileDescriptor instances that reference it are destroyed.
> - *
> - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> - * the fd() function will return -1.
> - */
> -FileDescriptor::FileDescriptor(const int &fd)
> -{
> -	if (fd < 0)
> -		return;
> -
> -	fd_ = std::make_shared<Descriptor>(fd, true);
> -	if (fd_->fd() < 0)
> -		fd_.reset();
> -}
> -
> -/**
> - * \brief Create a FileDescriptor taking ownership of a given \a fd
> - * \param[in] fd File descriptor
> - *
> - * Construct a FileDescriptor from a numerical file descriptor by taking
> - * ownership of the \a fd. The original \a fd is set to -1 and shall not be
> - * touched by the caller anymore. In particular, the caller shall not close the
> - * original \a fd manually. The duplicated file descriptor will be closed
> - * automatically when all FileDescriptor instances that reference it are
> - * destroyed.
> - *
> - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> - * the fd() function will return -1.
> - */
> -FileDescriptor::FileDescriptor(int &&fd)
> -{
> -	if (fd < 0)
> -		return;
> -
> -	fd_ = std::make_shared<Descriptor>(fd, false);
> -	/*
> -	 * The Descriptor constructor can't have failed here, as it took over
> -	 * the fd without duplicating it. Just set the original fd to -1 to
> -	 * implement move semantics.
> -	 */
> -	fd = -1;
> -}
> -
> -/**
> - * \brief Create a FileDescriptor taking ownership of a given UniqueFD \a fd
> - * \param[in] fd UniqueFD
> - *
> - * Construct a FileDescriptor from UniqueFD by taking ownership of the \a fd.
> - * The original \a fd becomes invalid.
> - */
> -FileDescriptor::FileDescriptor(UniqueFD fd)
> -	: FileDescriptor(fd.release())
> -{
> -}
> -
> -/**
> - * \brief Copy constructor, create a FileDescriptor from a copy of \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Copying a FileDescriptor implicitly shares ownership of the wrapped file
> - * descriptor. The original FileDescriptor is left untouched, and the caller is
> - * responsible for destroying it when appropriate. The wrapped file descriptor
> - * will be closed automatically when all FileDescriptor instances that
> - * reference it are destroyed.
> - */
> -FileDescriptor::FileDescriptor(const FileDescriptor &other)
> -	: fd_(other.fd_)
> -{
> -}
> -
> -/**
> - * \brief Move constructor, create a FileDescriptor by taking over \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> - * by \a other to the new FileDescriptor. The \a other FileDescriptor is
> - * invalidated and its fd() function will return -1. The wrapped file descriptor
> - * will be closed automatically when all FileDescriptor instances that
> - * reference it are destroyed.
> - */
> -FileDescriptor::FileDescriptor(FileDescriptor &&other)
> -	: fd_(std::move(other.fd_))
> -{
> -}
> -
> -/**
> - * \brief Destroy the FileDescriptor instance
> - *
> - * Destroying a FileDescriptor instance releases its reference to the wrapped
> - * descriptor, if any. When the last instance that references a wrapped
> - * descriptor is destroyed, the file descriptor is automatically closed.
> - */
> -FileDescriptor::~FileDescriptor()
> -{
> -}
> -
> -/**
> - * \brief Copy assignment operator, replace the wrapped file descriptor with a
> - * copy of \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Copying a FileDescriptor creates a new reference to the wrapped file
> - * descriptor owner by \a other. If \a other is invalid, *this will also be
> - * invalid. The original FileDescriptor is left untouched, and the caller is
> - * responsible for destroying it when appropriate. The wrapped file descriptor
> - * will be closed automatically when all FileDescriptor instances that
> - * reference it are destroyed.
> - *
> - * \return A reference to this FileDescriptor
> - */
> -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)
> -{
> -	fd_ = other.fd_;
> -
> -	return *this;
> -}
> -
> -/**
> - * \brief Move assignment operator, replace the wrapped file descriptor by
> - * taking over \a other
> - * \param[in] other The other FileDescriptor
> - *
> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> - * by \a other to the new FileDescriptor. If \a other is invalid, *this will
> - * also be invalid. The \a other FileDescriptor is invalidated and its fd()
> - * function will return -1. The wrapped file descriptor will be closed
> - * automatically when all FileDescriptor instances that reference it are
> - * destroyed.
> - *
> - * \return A reference to this FileDescriptor
> - */
> -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)
> -{
> -	fd_ = std::move(other.fd_);
> -
> -	return *this;
> -}
> -
> -/**
> - * \fn FileDescriptor::isValid()
> - * \brief Check if the FileDescriptor instance is valid
> - * \return True if the FileDescriptor is valid, false otherwise
> - */
> -
> -/**
> - * \fn FileDescriptor::fd()
> - * \brief Retrieve the numerical file descriptor
> - * \return The numerical file descriptor, which may be -1 if the FileDescriptor
> - * instance is invalid
> - */
> -
> -/**
> - * \brief Duplicate a FileDescriptor
> - *
> - * Duplicating a FileDescriptor creates a duplicate of the wrapped file
> - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function
> - * of the original and the get() function of the duplicate will return different
> - * values. The duplicate instance will not be affected by destruction of the
> - * original instance or its copies.
> - *
> - * \return A UniqueFD owning a duplicate of the original file descriptor
> - */
> -UniqueFD FileDescriptor::dup() const
> -{
> -	int dupFd = ::dup(fd());
> -	if (dupFd == -1) {
> -		int ret = -errno;
> -		LOG(FileDescriptor, Error)
> -			<< "Failed to dup() fd: " << strerror(-ret);
> -	}
> -
> -	return UniqueFD(dupFd);
> -}
> -
> -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)
> -{
> -	if (!duplicate) {
> -		fd_ = fd;
> -		return;
> -	}
> -
> -	/* Failing to dup() a fd should not happen and is fatal. */
> -	fd_ = ::dup(fd);
> -	if (fd_ == -1) {
> -		int ret = -errno;
> -		LOG(FileDescriptor, Fatal)
> -			<< "Failed to dup() fd: " << strerror(-ret);
> -	}
> -}
> -
> -FileDescriptor::Descriptor::~Descriptor()
> -{
> -	if (fd_ != -1)
> -		close(fd_);
> -}
> -
> -} /* namespace libcamera */
> diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build
> index b0d85bc19245..ccb746c27466 100644
> --- a/src/libcamera/base/meson.build
> +++ b/src/libcamera/base/meson.build
> @@ -8,12 +8,12 @@ libcamera_base_sources = files([
>      'event_dispatcher_poll.cpp',
>      'event_notifier.cpp',
>      'file.cpp',
> -    'file_descriptor.cpp',
>      'flags.cpp',
>      'log.cpp',
>      'message.cpp',
>      'object.cpp',
>      'semaphore.cpp',
> +    'shared_fd.cpp',
>      'signal.cpp',
>      'thread.cpp',
>      'timer.cpp',
> diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp
> new file mode 100644
> index 000000000000..05b6892f7e19
> --- /dev/null
> +++ b/src/libcamera/base/shared_fd.cpp
> @@ -0,0 +1,262 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * shared_fd.cpp - File descriptor wrapper with shared ownership
> + */
> +
> +#include <libcamera/base/shared_fd.h>
> +
> +#include <string.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <utility>
> +
> +#include <libcamera/base/log.h>
> +#include <libcamera/base/unique_fd.h>
> +
> +/**
> + * \file base/shared_fd.h
> + * \brief File descriptor wrapper
> + */
> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(SharedFD)
> +
> +/**
> + * \class SharedFD
> + * \brief RAII-style wrapper for file descriptors
> + *
> + * The SharedFD class provides RAII-style lifetime management of file
> + * descriptors with an efficient mechanism for ownership sharing. At its core,
> + * an internal Descriptor object wraps a file descriptor (expressed as a signed
> + * integer) with an RAII-style interface. The Descriptor is then implicitly
> + * shared with all SharedFD instances constructed as copies.
> + *
> + * When constructed from a numerical file descriptor, the SharedFD instance
> + * either duplicates or takes over the file descriptor:
> + *
> + * - The SharedFD(const int &) constructor duplicates the numerical file
> + *   descriptor and wraps the duplicate in a Descriptor. The caller is
> + *   responsible for closing the original file descriptor, and the value
> + *   returned by fd() will be different from the value passed to the
> + *   constructor.
> + *
> + * - The SharedFD(int &&) constructor takes over the numerical file descriptor
> + *   and wraps it in a Descriptor. The caller shall not touch the original file
> + *   descriptor once the function returns, and the value returned by fd() will
> + *   be identical to the value passed to the constructor.
> + *
> + * The copy constructor and assignment operator create copies that share the
> + * Descriptor, while the move versions of those functions additionally make the
> + * other SharedFD invalid. When the last SharedFD that references a Descriptor
> + * is destroyed, the file descriptor is closed.
> + *
> + * The numerical file descriptor is available through the fd() function. All
> + * SharedFD instances created as copies of a SharedFD will report the same fd()
> + * value. Callers can perform operations on the fd(), but shall never close it
> + * manually.
> + */
> +
> +/**
> + * \brief Create a SharedFD copying a given \a fd
> + * \param[in] fd File descriptor
> + *
> + * Construct a SharedFD from a numerical file descriptor by duplicating the
> + * \a fd, and take ownership of the copy. The original \a fd is left untouched,
> + * and the caller is responsible for closing it when appropriate. The duplicated
> + * file descriptor will be closed automatically when all SharedFD instances that
> + * reference it are destroyed.
> + *
> + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> + * function will return -1.
> + */
> +SharedFD::SharedFD(const int &fd)
> +{
> +	if (fd < 0)
> +		return;
> +
> +	fd_ = std::make_shared<Descriptor>(fd, true);
> +	if (fd_->fd() < 0)
> +		fd_.reset();
> +}
> +
> +/**
> + * \brief Create a SharedFD taking ownership of a given \a fd
> + * \param[in] fd File descriptor
> + *
> + * Construct a SharedFD from a numerical file descriptor by taking ownership of
> + * the \a fd. The original \a fd is set to -1 and shall not be touched by the
> + * caller anymore. In particular, the caller shall not close the original \a fd
> + * manually. The duplicated file descriptor will be closed automatically when
> + * all SharedFD instances that reference it are destroyed.
> + *
> + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> + * function will return -1.
> + */
> +SharedFD::SharedFD(int &&fd)
> +{
> +	if (fd < 0)
> +		return;
> +
> +	fd_ = std::make_shared<Descriptor>(fd, false);
> +	/*
> +	 * The Descriptor constructor can't have failed here, as it took over
> +	 * the fd without duplicating it. Just set the original fd to -1 to
> +	 * implement move semantics.
> +	 */
> +	fd = -1;
> +}
> +
> +/**
> + * \brief Create a SharedFD taking ownership of a given UniqueFD \a fd
> + * \param[in] fd UniqueFD
> + *
> + * Construct a SharedFD from UniqueFD by taking ownership of the \a fd. The
> + * original \a fd becomes invalid.
> + */
> +SharedFD::SharedFD(UniqueFD fd)
> +	: SharedFD(fd.release())
> +{
> +}
> +
> +/**
> + * \brief Copy constructor, create a SharedFD from a copy of \a other
> + * \param[in] other The other SharedFD
> + *
> + * Copying a SharedFD implicitly shares ownership of the wrapped file
> + * descriptor. The original SharedFD is left untouched, and the caller is
> + * responsible for destroying it when appropriate. The wrapped file descriptor
> + * will be closed automatically when all SharedFD instances that reference it
> + * are destroyed.
> + */
> +SharedFD::SharedFD(const SharedFD &other)
> +	: fd_(other.fd_)
> +{
> +}
> +
> +/**
> + * \brief Move constructor, create a SharedFD by taking over \a other
> + * \param[in] other The other SharedFD
> + *
> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> + * \a other to the new SharedFD. The \a other SharedFD is invalidated and its
> + * fd() function will return -1. The wrapped file descriptor will be closed
> + * automatically when all SharedFD instances that reference it are destroyed.
> + */
> +SharedFD::SharedFD(SharedFD &&other)
> +	: fd_(std::move(other.fd_))
> +{
> +}
> +
> +/**
> + * \brief Destroy the SharedFD instance
> + *
> + * Destroying a SharedFD instance releases its reference to the wrapped
> + * descriptor, if any. When the last instance that references a wrapped
> + * descriptor is destroyed, the file descriptor is automatically closed.
> + */
> +SharedFD::~SharedFD()
> +{
> +}
> +
> +/**
> + * \brief Copy assignment operator, replace the wrapped file descriptor with a
> + * copy of \a other
> + * \param[in] other The other SharedFD
> + *
> + * Copying a SharedFD creates a new reference to the wrapped file descriptor
> + * owner by \a other. If \a other is invalid, *this will also be invalid. The
> + * original SharedFD is left untouched, and the caller is responsible for
> + * destroying it when appropriate. The wrapped file descriptor will be closed
> + * automatically when all SharedFD instances that reference it are destroyed.
> + *
> + * \return A reference to this SharedFD
> + */
> +SharedFD &SharedFD::operator=(const SharedFD &other)
> +{
> +	fd_ = other.fd_;
> +
> +	return *this;
> +}
> +
> +/**
> + * \brief Move assignment operator, replace the wrapped file descriptor by
> + * taking over \a other
> + * \param[in] other The other SharedFD
> + *
> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> + * \a other to the new SharedFD. If \a other is invalid, *this will also be
> + * invalid. The \a other SharedFD is invalidated and its fd() function will
> + * return -1. The wrapped file descriptor will be closed automatically when
> + * all SharedFD instances that reference it are destroyed.
> + *
> + * \return A reference to this SharedFD
> + */
> +SharedFD &SharedFD::operator=(SharedFD &&other)
> +{
> +	fd_ = std::move(other.fd_);
> +
> +	return *this;
> +}
> +
> +/**
> + * \fn SharedFD::isValid()
> + * \brief Check if the SharedFD instance is valid
> + * \return True if the SharedFD is valid, false otherwise
> + */
> +
> +/**
> + * \fn SharedFD::fd()
> + * \brief Retrieve the numerical file descriptor
> + * \return The numerical file descriptor, which may be -1 if the SharedFD
> + * instance is invalid
> + */
> +
> +/**
> + * \brief Duplicate a SharedFD
> + *
> + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and
> + * returns a UniqueFD that owns the duplicate. The fd() function of the original
> + * and the get() function of the duplicate will return different values. The
> + * duplicate instance will not be affected by destruction of the original
> + * instance or its copies.
> + *
> + * \return A UniqueFD owning a duplicate of the original file descriptor
> + */
> +UniqueFD SharedFD::dup() const
> +{
> +	int dupFd = ::dup(fd());
> +	if (dupFd == -1) {
> +		int ret = -errno;
> +		LOG(SharedFD, Error)
> +			<< "Failed to dup() fd: " << strerror(-ret);
> +	}
> +
> +	return UniqueFD(dupFd);
> +}
> +
> +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)
> +{
> +	if (!duplicate) {
> +		fd_ = fd;
> +		return;
> +	}
> +
> +	/* Failing to dup() a fd should not happen and is fatal. */
> +	fd_ = ::dup(fd);
> +	if (fd_ == -1) {
> +		int ret = -errno;
> +		LOG(SharedFD, Fatal)
> +			<< "Failed to dup() fd: " << strerror(-ret);
> +	}
> +}
> +
> +SharedFD::Descriptor::~Descriptor()
> +{
> +	if (fd_ != -1)
> +		close(fd_);
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp
> index f5bcf107d7aa..0a5bf7fdbeb7 100644
> --- a/src/libcamera/framebuffer.cpp
> +++ b/src/libcamera/framebuffer.cpp
> @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()
>   * offset and length.
>   *
>   * To support DMA access, planes are associated with dmabuf objects represented
> - * by FileDescriptor handles. The Plane class doesn't handle mapping of the
> - * memory to the CPU, but applications and IPAs may use the dmabuf file
> - * descriptors to map the plane memory with mmap() and access its contents.
> + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to
> + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map
> + * the plane memory with mmap() and access its contents.
>   *
>   * \todo Specify how an application shall decide whether to use a single or
>   * multiple dmabufs, based on the camera requirements.
> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
> index 82ec9b20a411..0a259305afa2 100644
> --- a/src/libcamera/ipa_data_serializer.cpp
> +++ b/src/libcamera/ipa_data_serializer.cpp
> @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)
>   *
>   * \todo Harden the vector and map deserializer
>   *
> - * \todo For FileDescriptors, instead of storing a validity flag, store an
> + * \todo For SharedFDs, instead of storing a validity flag, store an
>   * index into the fd array. This will allow us to use views instead of copying.
>   */
>
> @@ -112,7 +112,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() can be used if the object type \a T and its
> - * members don't have any FileDescriptor.
> + * members don't have any SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -132,7 +132,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() can be used if the object type \a T and its
> - * members don't have any FileDescriptor.
> + * members don't have any SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -143,7 +143,7 @@ namespace {
>  /**
>   * \fn template<typename T> IPADataSerializer<T>::deserialize(
>   * 	const std::vector<uint8_t> &data,
> - * 	const std::vector<FileDescriptor> &fds,
> + * 	const std::vector<SharedFD> &fds,
>   * 	ControlSerializer *cs = nullptr)
>   * \brief Deserialize byte vector and fd vector into an object
>   * \tparam T Type of object to deserialize to
> @@ -152,7 +152,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() (or the iterator version) must be used if
> - * the object type \a T or its members contain FileDescriptor.
> + * the object type \a T or its members contain SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -164,8 +164,8 @@ namespace {
>   * \fn template<typename T> IPADataSerializer::deserialize(
>   * 	std::vector<uint8_t>::const_iterator dataBegin,
>   * 	std::vector<uint8_t>::const_iterator dataEnd,
> - * 	std::vector<FileDescriptor>::const_iterator fdsBegin,
> - * 	std::vector<FileDescriptor>::const_iterator fdsEnd,
> + * 	std::vector<SharedFD>::const_iterator fdsBegin,
> + * 	std::vector<SharedFD>::const_iterator fdsEnd,
>   * 	ControlSerializer *cs = nullptr)
>   * \brief Deserialize byte vector and fd vector into an object
>   * \tparam T Type of object to deserialize to
> @@ -176,7 +176,7 @@ namespace {
>   * \param[in] cs ControlSerializer
>   *
>   * This version of deserialize() (or the vector version) must be used if
> - * the object type \a T or its members contain FileDescriptor.
> + * the object type \a T or its members contain SharedFD.
>   *
>   * \a cs is only necessary if the object type \a T or its members contain
>   * ControlList or ControlInfoMap.
> @@ -189,7 +189,7 @@ namespace {
>  #define DEFINE_POD_SERIALIZER(type)					\
>  									\
>  template<>								\
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>		\
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>		\
>  IPADataSerializer<type>::serialize(const type &data,			\
>  				  [[maybe_unused]] ControlSerializer *cs) \
>  {									\
> @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
>  									\
>  template<>								\
>  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> -					  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \
> +					  [[maybe_unused]] const std::vector<SharedFD> &fds, \
>  					  ControlSerializer *cs)	\
>  {									\
>  	return deserialize(data.cbegin(), data.end(), cs);		\
> @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
>  template<>								\
>  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \
>  					  std::vector<uint8_t>::const_iterator dataEnd, \
> -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \
> -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \
> +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \
> +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \
>  					  ControlSerializer *cs)	\
>  {									\
>  	return deserialize(dataBegin, dataEnd, cs);			\
> @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)
>   * function parameter serdes).
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<std::string>::serialize(const std::string &data,
>  					  [[maybe_unused]] ControlSerializer *cs)
>  {
> @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
>  template<>
>  std::string
>  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,
> -					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> +					    [[maybe_unused]] const std::vector<SharedFD> &fds,
>  					    [[maybe_unused]] ControlSerializer *cs)
>  {
>  	return { data.cbegin(), data.cend() };
> @@ -288,8 +288,8 @@ template<>
>  std::string
>  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  					    std::vector<uint8_t>::const_iterator dataEnd,
> -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>  					    [[maybe_unused]] ControlSerializer *cs)
>  {
>  	return { dataBegin, dataEnd };
> @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
>   * be used. The serialized ControlInfoMap will have zero length.
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)
>  {
>  	if (!cs)
> @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
>  template<>
>  ControlList
>  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
> -					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> +					    [[maybe_unused]] const std::vector<SharedFD> &fds,
>  					    ControlSerializer *cs)
>  {
>  	return deserialize(data.cbegin(), data.end(), cs);
> @@ -417,8 +417,8 @@ template<>
>  ControlList
>  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  					    std::vector<uint8_t>::const_iterator dataEnd,
> -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>  					    ControlSerializer *cs)
>  {
>  	return deserialize(dataBegin, dataEnd, cs);
> @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator
>   * X bytes - Serialized ControlInfoMap (using ControlSerializer)
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,
>  					     ControlSerializer *cs)
>  {
> @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
>  template<>
>  ControlInfoMap
>  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
> -					       [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> +					       [[maybe_unused]] const std::vector<SharedFD> &fds,
>  					       ControlSerializer *cs)
>  {
>  	return deserialize(data.cbegin(), data.end(), cs);
> @@ -503,30 +503,30 @@ template<>
>  ControlInfoMap
>  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  					       std::vector<uint8_t>::const_iterator dataEnd,
> -					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>  					       ControlSerializer *cs)
>  {
>  	return deserialize(dataBegin, dataEnd, cs);
>  }
>
>  /*
> - * FileDescriptors are serialized into four bytes that tells if the
> - * FileDescriptor is valid or not. If it is valid, then for serialization
> - * the fd will be written to the fd vector, or for deserialization the
> - * fd vector const_iterator will be valid.
> + * SharedFD instances are serialized into four bytes that tells if the SharedFD
> + * is valid or not. If it is valid, then for serialization the fd will be
> + * written to the fd vector, or for deserialization the fd vector const_iterator
> + * will be valid.
>   *
>   * This validity is necessary so that we don't send -1 fd over sendmsg(). It
>   * also allows us to simply send the entire fd vector into the deserializer
>   * and it will be recursively consumed as necessary.
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
> -					     [[maybe_unused]] ControlSerializer *cs)
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,
> +				       [[maybe_unused]] ControlSerializer *cs)
>  {
>  	std::vector<uint8_t> dataVec;
> -	std::vector<FileDescriptor> fdVec;
> +	std::vector<SharedFD> fdVec;
>
>  	/*
>  	 * Store as uint32_t to prepare for conversion from validity flag
> @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
>  }
>
>  template<>
> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> -							      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> -							      std::vector<FileDescriptor>::const_iterator fdsBegin,
> -							      std::vector<FileDescriptor>::const_iterator fdsEnd,
> -							      [[maybe_unused]] ControlSerializer *cs)
> +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> +						  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> +						  std::vector<SharedFD>::const_iterator fdsBegin,
> +						  std::vector<SharedFD>::const_iterator fdsEnd,
> +						  [[maybe_unused]] ControlSerializer *cs)
>  {
>  	ASSERT(std::distance(dataBegin, dataEnd) >= 4);
>
> @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s
>
>  	ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));
>
> -	return valid ? *fdsBegin : FileDescriptor();
> +	return valid ? *fdsBegin : SharedFD();
>  }
>
>  template<>
> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,
> -							      const std::vector<FileDescriptor> &fds,
> -							      [[maybe_unused]] ControlSerializer *cs)
> +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,
> +						  const std::vector<SharedFD> &fds,
> +						  [[maybe_unused]] ControlSerializer *cs)
>  {
>  	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());
>  }
> @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<
>  /*
>   * FrameBuffer::Plane is serialized as:
>   *
> - * 4 byte  - FileDescriptor
> + * 4 byte  - SharedFD
>   * 4 bytes - uint32_t Offset
>   * 4 bytes - uint32_t Length
>   */
>  template<>
> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,
>  						 [[maybe_unused]] ControlSerializer *cs)
>  {
>  	std::vector<uint8_t> dataVec;
> -	std::vector<FileDescriptor> fdsVec;
> +	std::vector<SharedFD> fdsVec;
>
>  	std::vector<uint8_t> fdBuf;
> -	std::vector<FileDescriptor> fdFds;
> +	std::vector<SharedFD> fdFds;
>  	std::tie(fdBuf, fdFds) =
> -		IPADataSerializer<FileDescriptor>::serialize(data.fd);
> +		IPADataSerializer<SharedFD>::serialize(data.fd);
>  	dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());
>  	fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());
>
> @@ -597,13 +597,13 @@ template<>
>  FrameBuffer::Plane
>  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  						   std::vector<uint8_t>::const_iterator dataEnd,
> -						   std::vector<FileDescriptor>::const_iterator fdsBegin,
> -						   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +						   std::vector<SharedFD>::const_iterator fdsBegin,
> +						   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>  						   [[maybe_unused]] ControlSerializer *cs)
>  {
>  	FrameBuffer::Plane ret;
>
> -	ret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,
> +	ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,
>  								fdsBegin, fdsBegin + 1);
>  	ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);
>  	ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);
> @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i
>  template<>
>  FrameBuffer::Plane
>  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,
> -						   const std::vector<FileDescriptor> &fds,
> +						   const std::vector<SharedFD> &fds,
>  						   ControlSerializer *cs)
>  {
>  	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp
> index ad870fd4137f..3b47032de0a2 100644
> --- a/src/libcamera/ipc_pipe.cpp
> +++ b/src/libcamera/ipc_pipe.cpp
> @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)
>  	data_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),
>  				     payload.data.end());
>  	for (int32_t &fd : payload.fds)
> -		fds_.push_back(FileDescriptor(std::move(fd)));
> +		fds_.push_back(SharedFD(std::move(fd)));
>  }
>
>  /**
> @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const
>  		       data_.data(), data_.size());
>  	}
>
> -	for (const FileDescriptor &fd : fds_)
> +	for (const SharedFD &fd : fds_)
>  		payload.fds.push_back(fd.fd());
>
>  	return payload;
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index ffa51a0c65ca..ea8243912a29 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -12,7 +12,7 @@
>  #include <queue>
>  #include <unordered_set>
>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>
>  #include <libcamera/camera.h>
>  #include <libcamera/control_ids.h>
> @@ -228,7 +228,7 @@ public:
>
>  	/* DMAHEAP allocation helper. */
>  	RPi::DmaHeap dmaHeap_;
> -	FileDescriptor lsTable_;
> +	SharedFD lsTable_;
>
>  	std::unique_ptr<DelayedControls> delayedCtrls_;
>  	bool sensorMetadata_;
> @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)
>  		if (!fd.isValid())
>  			return -ENOMEM;
>
> -		lsTable_ = FileDescriptor(std::move(fd));
> +		lsTable_ = SharedFD(std::move(fd));
>
>  		/* Allow the IPA to mmap the LS table via the file descriptor. */
>  		/*
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 3966483a365f..97d431071def 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -22,8 +22,8 @@
>  #include <linux/version.h>
>
>  #include <libcamera/base/event_notifier.h>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/log.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/unique_fd.h>
>  #include <libcamera/base/utils.h>
>
> @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
>  			return nullptr;
>
>  		FrameBuffer::Plane plane;
> -		plane.fd = FileDescriptor(std::move(fd));
> +		plane.fd = SharedFD(std::move(fd));
>  		/*
>  		 * V4L2 API doesn't provide dmabuf offset information of plane.
>  		 * Set 0 as a placeholder offset.
> @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
>  		ASSERT(numPlanes == 1u);
>
>  		planes.resize(formatInfo_->numPlanes());
> -		const FileDescriptor &fd = planes[0].fd;
> +		const SharedFD &fd = planes[0].fd;
>  		size_t offset = 0;
>
>  		for (auto [i, plane] : utils::enumerate(planes)) {
> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
> index 9817fd393d59..586347829845 100644
> --- a/src/v4l2/v4l2_camera.h
> +++ b/src/v4l2/v4l2_camera.h
> @@ -11,8 +11,8 @@
>  #include <mutex>
>  #include <utility>
>
> -#include <libcamera/base/file_descriptor.h>
>  #include <libcamera/base/semaphore.h>
> +#include <libcamera/base/shared_fd.h>
>
>  #include <libcamera/camera.h>
>  #include <libcamera/framebuffer.h>
> diff --git a/test/meson.build b/test/meson.build
> index 42dfbc1f8ee9..daaa3862cdd6 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -40,7 +40,6 @@ internal_tests = [
>      ['event-dispatcher',                'event-dispatcher.cpp'],
>      ['event-thread',                    'event-thread.cpp'],
>      ['file',                            'file.cpp'],
> -    ['file-descriptor',                 'file-descriptor.cpp'],
>      ['flags',                           'flags.cpp'],
>      ['hotplug-cameras',                 'hotplug-cameras.cpp'],
>      ['mapped-buffer',                   'mapped-buffer.cpp'],
> @@ -49,6 +48,7 @@ internal_tests = [
>      ['object-delete',                   'object-delete.cpp'],
>      ['object-invoke',                   'object-invoke.cpp'],
>      ['pixel-format',                    'pixel-format.cpp'],
> +    ['shared-fd',                       'shared-fd.cpp'],
>      ['signal-threads',                  'signal-threads.cpp'],
>      ['threads',                         'threads.cpp'],
>      ['timer',                           'timer.cpp'],
> diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
> index 5fcdcb8eae92..d2050a868b38 100644
> --- a/test/serialization/ipa_data_serializer_test.cpp
> +++ b/test/serialization/ipa_data_serializer_test.cpp
> @@ -53,7 +53,7 @@ template<typename T>
>  int testPodSerdes(T in)
>  {
>  	std::vector<uint8_t> buf;
> -	std::vector<FileDescriptor> fds;
> +	std::vector<SharedFD> fds;
>
>  	std::tie(buf, fds) = IPADataSerializer<T>::serialize(in);
>  	T out = IPADataSerializer<T>::deserialize(buf, fds);
> @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,
>  		     ControlSerializer *cs = nullptr)
>  {
>  	std::vector<uint8_t> buf;
> -	std::vector<FileDescriptor> fds;
> +	std::vector<SharedFD> fds;
>
>  	std::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);
>  	std::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);
> @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,
>  		  ControlSerializer *cs = nullptr)
>  {
>  	std::vector<uint8_t> buf;
> -	std::vector<FileDescriptor> fds;
> +	std::vector<SharedFD> fds;
>
>  	std::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);
>  	std::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);
> @@ -198,7 +198,7 @@ private:
>  		ControlSerializer cs(ControlSerializer::Role::Proxy);
>
>  		/*
> -		 * We don't test FileDescriptor serdes because it dup()s, so we
> +		 * We don't test SharedFD serdes because it dup()s, so we
>  		 * can't check for equality.
>  		 */
>  		std::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };
> @@ -219,7 +219,7 @@ private:
>  		};
>
>  		std::vector<uint8_t> buf;
> -		std::vector<FileDescriptor> fds;
> +		std::vector<SharedFD> fds;
>
>  		if (testVectorSerdes(vecUint8) != TestPass)
>  			return TestFail;
> @@ -291,7 +291,7 @@ private:
>  			{ { "a", { 1, 2, 3 } }, { "b", { 4, 5, 6 } }, { "c", { 7, 8, 9 } } };
>
>  		std::vector<uint8_t> buf;
> -		std::vector<FileDescriptor> fds;
> +		std::vector<SharedFD> fds;
>
>  		if (testMapSerdes(mapUintStr) != TestPass)
>  			return TestFail;
> @@ -359,7 +359,7 @@ private:
>  		std::string strEmpty = "";
>
>  		std::vector<uint8_t> buf;
> -		std::vector<FileDescriptor> fds;
> +		std::vector<SharedFD> fds;
>
>  		if (testPodSerdes(u32min) != TestPass)
>  			return TestFail;
> diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp
> similarity index 80%
> rename from test/file-descriptor.cpp
> rename to test/shared-fd.cpp
> index 76badc4c5fad..60e5d0aaa395 100644
> --- a/test/file-descriptor.cpp
> +++ b/test/shared-fd.cpp
> @@ -2,7 +2,7 @@
>  /*
>   * Copyright (C) 2019, Google Inc.
>   *
> - * file_descriptor.cpp - FileDescriptor test
> + * shared_fd.cpp - SharedFD test
>   */
>
>  #include <fcntl.h>
> @@ -11,7 +11,7 @@
>  #include <sys/types.h>
>  #include <unistd.h>
>
> -#include <libcamera/base/file_descriptor.h>
> +#include <libcamera/base/shared_fd.h>
>  #include <libcamera/base/utils.h>
>
>  #include "test.h"
> @@ -19,7 +19,7 @@
>  using namespace libcamera;
>  using namespace std;
>
> -class FileDescriptorTest : public Test
> +class SharedFDTest : public Test
>  {
>  protected:
>  	int init()
> @@ -43,8 +43,8 @@ protected:
>
>  	int run()
>  	{
> -		/* Test creating empty FileDescriptor. */
> -		desc1_ = new FileDescriptor();
> +		/* Test creating empty SharedFD. */
> +		desc1_ = new SharedFD();
>
>  		if (desc1_->fd() != -1) {
>  			std::cout << "Failed fd numerical check (default constructor)"
> @@ -56,10 +56,10 @@ protected:
>  		desc1_ = nullptr;
>
>  		/*
> -		 * Test creating FileDescriptor by copying numerical file
> +		 * Test creating SharedFD by copying numerical file
>  		 * descriptor.
>  		 */
> -		desc1_ = new FileDescriptor(fd_);
> +		desc1_ = new SharedFD(fd_);
>  		if (desc1_->fd() == fd_) {
>  			std::cout << "Failed fd numerical check (lvalue ref constructor)"
>  				  << std::endl;
> @@ -84,13 +84,13 @@ protected:
>  		}
>
>  		/*
> -		 * Test creating FileDescriptor by taking ownership of
> +		 * Test creating SharedFD by taking ownership of
>  		 * numerical file descriptor.
>  		 */
>  		int dupFd = dup(fd_);
>  		int dupFdCopy = dupFd;
>
> -		desc1_ = new FileDescriptor(std::move(dupFd));
> +		desc1_ = new SharedFD(std::move(dupFd));
>  		if (desc1_->fd() != dupFdCopy) {
>  			std::cout << "Failed fd numerical check (rvalue ref constructor)"
>  				  << std::endl;
> @@ -114,9 +114,9 @@ protected:
>  			return TestFail;
>  		}
>
> -		/* Test creating FileDescriptor from other FileDescriptor. */
> -		desc1_ = new FileDescriptor(fd_);
> -		desc2_ = new FileDescriptor(*desc1_);
> +		/* Test creating SharedFD from other SharedFD. */
> +		desc1_ = new SharedFD(fd_);
> +		desc2_ = new SharedFD(*desc1_);
>
>  		if (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {
>  			std::cout << "Failed fd numerical check (copy constructor)"
> @@ -142,10 +142,10 @@ protected:
>  		delete desc2_;
>  		desc2_ = nullptr;
>
> -		/* Test creating FileDescriptor by taking over other FileDescriptor. */
> -		desc1_ = new FileDescriptor(fd_);
> +		/* Test creating SharedFD by taking over other SharedFD. */
> +		desc1_ = new SharedFD(fd_);
>  		fd = desc1_->fd();
> -		desc2_ = new FileDescriptor(std::move(*desc1_));
> +		desc2_ = new SharedFD(std::move(*desc1_));
>
>  		if (desc1_->fd() != -1 || desc2_->fd() != fd) {
>  			std::cout << "Failed fd numerical check (move constructor)"
> @@ -164,9 +164,9 @@ protected:
>  		delete desc2_;
>  		desc2_ = nullptr;
>
> -		/* Test creating FileDescriptor by copy assignment. */
> -		desc1_ = new FileDescriptor();
> -		desc2_ = new FileDescriptor(fd_);
> +		/* Test creating SharedFD by copy assignment. */
> +		desc1_ = new SharedFD();
> +		desc2_ = new SharedFD(fd_);
>
>  		fd = desc2_->fd();
>  		*desc1_ = *desc2_;
> @@ -188,9 +188,9 @@ protected:
>  		delete desc2_;
>  		desc2_ = nullptr;
>
> -		/* Test creating FileDescriptor by move assignment. */
> -		desc1_ = new FileDescriptor();
> -		desc2_ = new FileDescriptor(fd_);
> +		/* Test creating SharedFD by move assignment. */
> +		desc1_ = new SharedFD();
> +		desc2_ = new SharedFD(fd_);
>
>  		fd = desc2_->fd();
>  		*desc1_ = std::move(*desc2_);
> @@ -237,7 +237,7 @@ private:
>
>  	int fd_;
>  	ino_t inodeNr_;
> -	FileDescriptor *desc1_, *desc2_;
> +	SharedFD *desc1_, *desc2_;
>  };
>
> -TEST_REGISTER(FileDescriptorTest)
> +TEST_REGISTER(SharedFDTest)
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> index d856339aa9ee..c37c4941b528 100644
> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)
>  void {{proxy_name}}::{{method.mojom_name}}IPC(
>  	std::vector<uint8_t>::const_iterator data,
>  	size_t dataSize,
> -	[[maybe_unused]] const std::vector<FileDescriptor> &fds)
> +	[[maybe_unused]] const std::vector<SharedFD> &fds)
>  {
>  {%- for param in method.parameters %}
>  	{{param|name}} {{param.mojom_name}};
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> index ce396c183d0c..c308dd10c7e5 100644
> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> @@ -64,7 +64,7 @@ private:
>  	void {{method.mojom_name}}IPC(
>  		std::vector<uint8_t>::const_iterator data,
>  		size_t dataSize,
> -		const std::vector<FileDescriptor> &fds);
> +		const std::vector<SharedFD> &fds);
>  {% endfor %}
>
>  	/* Helper class to invoke async functions in another thread. */
> diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> index ebcd2aaaafae..bac826a74c2d 100644
> --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> @@ -54,7 +54,7 @@
>  {%- for param in params %}
>  	std::vector<uint8_t> {{param.mojom_name}}Buf;
>  {%- if param|has_fd %}
> -	std::vector<FileDescriptor> {{param.mojom_name}}Fds;
> +	std::vector<SharedFD> {{param.mojom_name}}Fds;
>  	std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =
>  {%- else %}
>  	std::tie({{param.mojom_name}}Buf, std::ignore) =
> diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> index b8ef8e7b974e..77bae36fe6b7 100644
> --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl
> +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> @@ -40,7 +40,7 @@
>  		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
>  {%- elif field|is_fd %}
>  		std::vector<uint8_t> {{field.mojom_name}};
> -		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> +		std::vector<SharedFD> {{field.mojom_name}}Fds;
>  		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
>  			IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
>  		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> @@ -58,7 +58,7 @@
>  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
>  		std::vector<uint8_t> {{field.mojom_name}};
>  	{%- if field|has_fd %}
> -		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> +		std::vector<SharedFD> {{field.mojom_name}}Fds;
>  		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
>  	{%- else %}
>  		std::tie({{field.mojom_name}}, std::ignore) =
> @@ -177,7 +177,7 @@
>   # \a struct.
>   #}
>  {%- macro serializer(struct, namespace) %}
> -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
>  	serialize(const {{struct|name_full}} &data,
>  {%- if struct|needs_control_serializer %}
>  		  ControlSerializer *cs)
> @@ -187,7 +187,7 @@
>  	{
>  		std::vector<uint8_t> retData;
>  {%- if struct|has_fd %}
> -		std::vector<FileDescriptor> retFds;
> +		std::vector<SharedFD> retFds;
>  {%- endif %}
>  {%- for field in struct.fields %}
>  {{serializer_field(field, namespace, loop)}}
> @@ -210,7 +210,7 @@
>  {%- macro deserializer_fd(struct, namespace) %}
>  	static {{struct|name_full}}
>  	deserialize(std::vector<uint8_t> &data,
> -		    std::vector<FileDescriptor> &fds,
> +		    std::vector<SharedFD> &fds,
>  {%- if struct|needs_control_serializer %}
>  		    ControlSerializer *cs)
>  {%- else %}
> @@ -224,8 +224,8 @@
>  	static {{struct|name_full}}
>  	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  		    std::vector<uint8_t>::const_iterator dataEnd,
> -		    std::vector<FileDescriptor>::const_iterator fdsBegin,
> -		    std::vector<FileDescriptor>::const_iterator fdsEnd,
> +		    std::vector<SharedFD>::const_iterator fdsBegin,
> +		    std::vector<SharedFD>::const_iterator fdsEnd,
>  {%- if struct|needs_control_serializer %}
>  		    ControlSerializer *cs)
>  {%- else %}
> @@ -234,7 +234,7 @@
>  	{
>  		{{struct|name_full}} ret;
>  		std::vector<uint8_t>::const_iterator m = dataBegin;
> -		std::vector<FileDescriptor>::const_iterator n = fdsBegin;
> +		std::vector<SharedFD>::const_iterator n = fdsBegin;
>
>  		size_t dataSize = std::distance(dataBegin, dataEnd);
>  		[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
> @@ -255,7 +255,7 @@
>  {%- macro deserializer_fd_simple(struct, namespace) %}
>  	static {{struct|name_full}}
>  	deserialize(std::vector<uint8_t> &data,
> -		    [[maybe_unused]] std::vector<FileDescriptor> &fds,
> +		    [[maybe_unused]] std::vector<SharedFD> &fds,
>  		    ControlSerializer *cs = nullptr)
>  	{
>  		return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);
> @@ -264,8 +264,8 @@
>  	static {{struct|name_full}}
>  	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
>  		    std::vector<uint8_t>::const_iterator dataEnd,
> -		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> -		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> +		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> +		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
>  		    ControlSerializer *cs = nullptr)
>  	{
>  		return IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);
> diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py
> index c609f4e5c062..753bfc734e56 100644
> --- a/utils/ipc/generators/mojom_libcamera_generator.py
> +++ b/utils/ipc/generators/mojom_libcamera_generator.py
> @@ -77,7 +77,7 @@ def GetDefaultValue(element):
>      if mojom.IsEnumKind(element.kind):
>          return f'static_cast<{element.kind.mojom_name}>(0)'
>      if isinstance(element.kind, mojom.Struct) and \
> -       element.kind.mojom_name == 'FileDescriptor':
> +       element.kind.mojom_name == 'SharedFD':
>          return '-1'
>      return ''
>
> @@ -140,7 +140,7 @@ def HasFd(element):
>          types = GetAllTypes(element)
>      else:
>          types = GetAllTypes(element.kind)
> -    return "FileDescriptor" in types or (attrs is not None and "hasFd" in attrs)
> +    return "SharedFD" in types or (attrs is not None and "hasFd" in attrs)
>
>  def WithDefaultValues(element):
>      return [x for x in element if HasDefaultValue(x)]
> @@ -221,7 +221,7 @@ def IsEnum(element):
>      return mojom.IsEnumKind(element.kind)
>
>  def IsFd(element):
> -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "FileDescriptor"
> +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "SharedFD"
>
>  def IsMap(element):
>      return mojom.IsMapKind(element.kind)
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart Nov. 29, 2021, 5:24 p.m. UTC | #3
Hi Jacopo,

On Mon, Nov 29, 2021 at 05:00:55PM +0100, Jacopo Mondi wrote:
> On Mon, Nov 29, 2021 at 01:57:52AM +0200, Laurent Pinchart wrote:
> > Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.
> > Rename it to SharedFD.
> >
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> makes sense!

Does this mean Rb ? :-)

> > ---
> >  include/libcamera/base/file.h                 |   4 +-
> >  include/libcamera/base/meson.build            |   2 +-
> >  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-
> >  include/libcamera/framebuffer.h               |   4 +-
> >  .../libcamera/internal/ipa_data_serializer.h  |  40 +--
> >  include/libcamera/internal/ipc_pipe.h         |   8 +-
> >  include/libcamera/ipa/core.mojom              |   6 +-
> >  include/libcamera/ipa/raspberrypi.mojom       |   2 +-
> >  src/android/camera_device.cpp                 |   2 +-
> >  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-
> >  src/libcamera/base/file.cpp                   |   6 +-
> >  src/libcamera/base/file_descriptor.cpp        | 266 ------------------
> >  src/libcamera/base/meson.build                |   2 +-
> >  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++
> >  src/libcamera/framebuffer.cpp                 |   6 +-
> >  src/libcamera/ipa_data_serializer.cpp         | 100 +++----
> >  src/libcamera/ipc_pipe.cpp                    |   4 +-
> >  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-
> >  src/libcamera/v4l2_videodevice.cpp            |   6 +-
> >  src/v4l2/v4l2_camera.h                        |   2 +-
> >  test/meson.build                              |   2 +-
> >  .../ipa_data_serializer_test.cpp              |  14 +-
> >  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--
> >  .../module_ipa_proxy.cpp.tmpl                 |   2 +-
> >  .../module_ipa_proxy.h.tmpl                   |   2 +-
> >  .../libcamera_templates/proxy_functions.tmpl  |   2 +-
> >  .../libcamera_templates/serializer.tmpl       |  22 +-
> >  .../generators/mojom_libcamera_generator.py   |   6 +-
> >  28 files changed, 422 insertions(+), 426 deletions(-)
> >  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)
> >  delete mode 100644 src/libcamera/base/file_descriptor.cpp
> >  create mode 100644 src/libcamera/base/shared_fd.cpp
> >  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)
> >
> > diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h
> > index 47769da7abc2..691b52d6ab2d 100644
> > --- a/include/libcamera/base/file.h
> > +++ b/include/libcamera/base/file.h
> > @@ -21,7 +21,7 @@
> >
> >  namespace libcamera {
> >
> > -class FileDescriptor;
> > +class SharedFD;
> >
> >  class File
> >  {
> > @@ -69,7 +69,7 @@ public:
> >  	bool unmap(uint8_t *addr);
> >
> >  	static bool exists(const std::string &name);
> > -	static ino_t inode(const FileDescriptor &fd);
> > +	static ino_t inode(const SharedFD &fd);
> >
> >  private:
> >  	LIBCAMERA_DISABLE_COPY(File)
> > diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
> > index cca374a769cc..112420dab225 100644
> > --- a/include/libcamera/base/meson.build
> > +++ b/include/libcamera/base/meson.build
> > @@ -11,13 +11,13 @@ libcamera_base_headers = files([
> >      'event_dispatcher_poll.h',
> >      'event_notifier.h',
> >      'file.h',
> > -    'file_descriptor.h',
> >      'flags.h',
> >      'log.h',
> >      'message.h',
> >      'object.h',
> >      'private.h',
> >      'semaphore.h',
> > +    'shared_fd.h',
> >      'signal.h',
> >      'span.h',
> >      'thread.h',
> > diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h
> > similarity index 55%
> > rename from include/libcamera/base/file_descriptor.h
> > rename to include/libcamera/base/shared_fd.h
> > index 12a43f95d414..a786885ceb32 100644
> > --- a/include/libcamera/base/file_descriptor.h
> > +++ b/include/libcamera/base/shared_fd.h
> > @@ -2,7 +2,7 @@
> >  /*
> >   * Copyright (C) 2019, Google Inc.
> >   *
> > - * file_descriptor.h - File descriptor wrapper
> > + * shared_fd.h - File descriptor wrapper with shared ownership
> >   */
> >
> >  #pragma once
> > @@ -13,18 +13,18 @@ namespace libcamera {
> >
> >  class UniqueFD;
> >
> > -class FileDescriptor final
> > +class SharedFD final
> >  {
> >  public:
> > -	explicit FileDescriptor(const int &fd = -1);
> > -	explicit FileDescriptor(int &&fd);
> > -	explicit FileDescriptor(UniqueFD fd);
> > -	FileDescriptor(const FileDescriptor &other);
> > -	FileDescriptor(FileDescriptor &&other);
> > -	~FileDescriptor();
> > +	explicit SharedFD(const int &fd = -1);
> > +	explicit SharedFD(int &&fd);
> > +	explicit SharedFD(UniqueFD fd);
> > +	SharedFD(const SharedFD &other);
> > +	SharedFD(SharedFD &&other);
> > +	~SharedFD();
> >
> > -	FileDescriptor &operator=(const FileDescriptor &other);
> > -	FileDescriptor &operator=(FileDescriptor &&other);
> > +	SharedFD &operator=(const SharedFD &other);
> > +	SharedFD &operator=(SharedFD &&other);
> >
> >  	bool isValid() const { return fd_ != nullptr; }
> >  	int fd() const { return fd_ ? fd_->fd() : -1; }
> > diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h
> > index 2fbea9c5be16..357bbe189551 100644
> > --- a/include/libcamera/framebuffer.h
> > +++ b/include/libcamera/framebuffer.h
> > @@ -13,7 +13,7 @@
> >  #include <vector>
> >
> >  #include <libcamera/base/class.h>
> > -#include <libcamera/base/file_descriptor.h>
> > +#include <libcamera/base/shared_fd.h>
> >  #include <libcamera/base/span.h>
> >
> >  namespace libcamera {
> > @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible
> >  public:
> >  	struct Plane {
> >  		static constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();
> > -		FileDescriptor fd;
> > +		SharedFD fd;
> >  		unsigned int offset = kInvalidOffset;
> >  		unsigned int length;
> >  	};
> > diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
> > index c2f602d5b7de..a87449c9be48 100644
> > --- a/include/libcamera/internal/ipa_data_serializer.h
> > +++ b/include/libcamera/internal/ipa_data_serializer.h
> > @@ -66,7 +66,7 @@ template<typename T>
> >  class IPADataSerializer
> >  {
> >  public:
> > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  	serialize(const T &data, ControlSerializer *cs = nullptr);
> >
> >  	static T deserialize(const std::vector<uint8_t> &data,
> > @@ -76,12 +76,12 @@ public:
> >  			     ControlSerializer *cs = nullptr);
> >
> >  	static T deserialize(const std::vector<uint8_t> &data,
> > -			     const std::vector<FileDescriptor> &fds,
> > +			     const std::vector<SharedFD> &fds,
> >  			     ControlSerializer *cs = nullptr);
> >  	static T deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  			     std::vector<uint8_t>::const_iterator dataEnd,
> > -			     std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -			     std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +			     std::vector<SharedFD>::const_iterator fdsBegin,
> > +			     std::vector<SharedFD>::const_iterator fdsEnd,
> >  			     ControlSerializer *cs = nullptr);
> >  };
> >
> > @@ -104,11 +104,11 @@ template<typename V>
> >  class IPADataSerializer<std::vector<V>>
> >  {
> >  public:
> > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
> >  	{
> >  		std::vector<uint8_t> dataVec;
> > -		std::vector<FileDescriptor> fdsVec;
> > +		std::vector<SharedFD> fdsVec;
> >
> >  		/* Serialize the length. */
> >  		uint32_t vecLen = data.size();
> > @@ -117,7 +117,7 @@ public:
> >  		/* Serialize the members. */
> >  		for (auto const &it : data) {
> >  			std::vector<uint8_t> dvec;
> > -			std::vector<FileDescriptor> fvec;
> > +			std::vector<SharedFD> fvec;
> >
> >  			std::tie(dvec, fvec) =
> >  				IPADataSerializer<V>::serialize(it, cs);
> > @@ -141,11 +141,11 @@ public:
> >  					  std::vector<uint8_t>::const_iterator dataEnd,
> >  					  ControlSerializer *cs = nullptr)
> >  	{
> > -		std::vector<FileDescriptor> fds;
> > +		std::vector<SharedFD> fds;
> >  		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
> >  	}
> >
> > -	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> > +	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
> >  					  ControlSerializer *cs = nullptr)
> >  	{
> >  		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> > @@ -153,15 +153,15 @@ public:
> >
> >  	static std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  					  std::vector<uint8_t>::const_iterator dataEnd,
> > -					  std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +					  std::vector<SharedFD>::const_iterator fdsBegin,
> > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> >  					  ControlSerializer *cs = nullptr)
> >  	{
> >  		uint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> >  		std::vector<V> ret(vecLen);
> >
> >  		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> > -		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> > +		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
> >  		for (uint32_t i = 0; i < vecLen; i++) {
> >  			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
> >  			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> > @@ -201,11 +201,11 @@ template<typename K, typename V>
> >  class IPADataSerializer<std::map<K, V>>
> >  {
> >  public:
> > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
> >  	{
> >  		std::vector<uint8_t> dataVec;
> > -		std::vector<FileDescriptor> fdsVec;
> > +		std::vector<SharedFD> fdsVec;
> >
> >  		/* Serialize the length. */
> >  		uint32_t mapLen = data.size();
> > @@ -214,7 +214,7 @@ public:
> >  		/* Serialize the members. */
> >  		for (auto const &it : data) {
> >  			std::vector<uint8_t> dvec;
> > -			std::vector<FileDescriptor> fvec;
> > +			std::vector<SharedFD> fvec;
> >
> >  			std::tie(dvec, fvec) =
> >  				IPADataSerializer<K>::serialize(it.first, cs);
> > @@ -247,11 +247,11 @@ public:
> >  					  std::vector<uint8_t>::const_iterator dataEnd,
> >  					  ControlSerializer *cs = nullptr)
> >  	{
> > -		std::vector<FileDescriptor> fds;
> > +		std::vector<SharedFD> fds;
> >  		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
> >  	}
> >
> > -	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> > +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
> >  					  ControlSerializer *cs = nullptr)
> >  	{
> >  		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> > @@ -259,8 +259,8 @@ public:
> >
> >  	static std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  					  std::vector<uint8_t>::const_iterator dataEnd,
> > -					  std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +					  std::vector<SharedFD>::const_iterator fdsBegin,
> > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> >  					  ControlSerializer *cs = nullptr)
> >  	{
> >  		std::map<K, V> ret;
> > @@ -268,7 +268,7 @@ public:
> >  		uint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> >
> >  		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> > -		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> > +		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
> >  		for (uint32_t i = 0; i < mapLen; i++) {
> >  			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
> >  			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> > diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h
> > index 986f8d886fa6..ab5dd67c3813 100644
> > --- a/include/libcamera/internal/ipc_pipe.h
> > +++ b/include/libcamera/internal/ipc_pipe.h
> > @@ -9,7 +9,7 @@
> >
> >  #include <vector>
> >
> > -#include <libcamera/base/file_descriptor.h>
> > +#include <libcamera/base/shared_fd.h>
> >  #include <libcamera/base/signal.h>
> >
> >  #include "libcamera/internal/ipc_unixsocket.h"
> > @@ -33,17 +33,17 @@ public:
> >
> >  	Header &header() { return header_; }
> >  	std::vector<uint8_t> &data() { return data_; }
> > -	std::vector<FileDescriptor> &fds() { return fds_; }
> > +	std::vector<SharedFD> &fds() { return fds_; }
> >
> >  	const Header &header() const { return header_; }
> >  	const std::vector<uint8_t> &data() const { return data_; }
> > -	const std::vector<FileDescriptor> &fds() const { return fds_; }
> > +	const std::vector<SharedFD> &fds() const { return fds_; }
> >
> >  private:
> >  	Header header_;
> >
> >  	std::vector<uint8_t> data_;
> > -	std::vector<FileDescriptor> fds_;
> > +	std::vector<SharedFD> fds_;
> >  };
> >
> >  class IPCPipe
> > diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom
> > index f7eff0c7ab8c..74f3339e56f2 100644
> > --- a/include/libcamera/ipa/core.mojom
> > +++ b/include/libcamera/ipa/core.mojom
> > @@ -32,7 +32,7 @@ module libcamera;
> >   *   - This attribute instructs the build system that a (de)serializer is
> >   *     available for the type and there's no need to generate one
> >   * - hasFd - struct fields or empty structs only
> > - *   - Designate that this field or empty struct contains a FileDescriptor
> > + *   - Designate that this field or empty struct contains a SharedFD
> >   *
> >   * Rules:
> >   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is
> > @@ -60,7 +60,7 @@ module libcamera;
> >   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array
> >   *       member
> >   * - [skipHeader] and [skipSerdes] only work here in core.mojom
> > - * - If a field in a struct has a FileDescriptor, but is not explicitly
> > + * - If a field in a struct has a SharedFD, but is not explicitly
> >   *   defined so in mojom, then the field must be marked with the [hasFd]
> >   *   attribute
> >   *
> > @@ -71,7 +71,7 @@ module libcamera;
> >   */
> >  [skipSerdes, skipHeader] struct ControlInfoMap {};
> >  [skipSerdes, skipHeader] struct ControlList {};
> > -[skipSerdes, skipHeader] struct FileDescriptor {};
> > +[skipSerdes, skipHeader] struct SharedFD {};
> >
> >  [skipHeader] struct Point {
> >  	int32 x;
> > diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
> > index e453d46cb14f..acd3cafe6c91 100644
> > --- a/include/libcamera/ipa/raspberrypi.mojom
> > +++ b/include/libcamera/ipa/raspberrypi.mojom
> > @@ -35,7 +35,7 @@ struct ISPConfig {
> >
> >  struct IPAConfig {
> >  	uint32 transform;
> > -	libcamera.FileDescriptor lsTableHandle;
> > +	libcamera.SharedFD lsTableHandle;
> >  };
> >
> >  struct StartConfig {
> > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > index f2e0bdbdbbf6..1938b10509fa 100644
> > --- a/src/android/camera_device.cpp
> > +++ b/src/android/camera_device.cpp
> > @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,
> >
> >  	std::vector<FrameBuffer::Plane> planes(buf.numPlanes());
> >  	for (size_t i = 0; i < buf.numPlanes(); ++i) {
> > -		FileDescriptor fd{ camera3buffer->data[i] };
> > +		SharedFD fd{ camera3buffer->data[i] };
> >  		if (!fd.isValid()) {
> >  			LOG(HAL, Fatal) << "No valid fd";
> >  			return nullptr;
> > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
> > index c6aec09046f7..aaf629eeb3fc 100644
> > --- a/src/ipa/raspberrypi/raspberrypi.cpp
> > +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> > @@ -15,8 +15,8 @@
> >
> >  #include <linux/bcm2835-isp.h>
> >
> > -#include <libcamera/base/file_descriptor.h>
> >  #include <libcamera/base/log.h>
> > +#include <libcamera/base/shared_fd.h>
> >  #include <libcamera/base/span.h>
> >
> >  #include <libcamera/control_ids.h>
> > @@ -164,7 +164,7 @@ private:
> >  	bool processPending_;
> >
> >  	/* LS table allocation passed in from the pipeline handler. */
> > -	FileDescriptor lsTableHandle_;
> > +	SharedFD lsTableHandle_;
> >  	void *lsTable_;
> >
> >  	/* Distinguish the first camera start from others. */
> > diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp
> > index 66c73c406198..3ca9839bb989 100644
> > --- a/src/libcamera/base/file.cpp
> > +++ b/src/libcamera/base/file.cpp
> > @@ -14,8 +14,8 @@
> >  #include <sys/types.h>
> >  #include <unistd.h>
> >
> > -#include <libcamera/base/file_descriptor.h>
> >  #include <libcamera/base/log.h>
> > +#include <libcamera/base/shared_fd.h>
> >
> >  /**
> >   * \file base/file.h
> > @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)
> >  }
> >
> >  /**
> > - * \brief Retrieve the inode of a FileDescriptor
> > + * \brief Retrieve the inode of a SharedFD
> >   *
> >   * \return The file descriptor inode on success, or 0 on error
> >   */
> > -ino_t File::inode(const FileDescriptor &fd)
> > +ino_t File::inode(const SharedFD &fd)
> >  {
> >  	if (!fd.isValid())
> >  		return 0;
> > diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp
> > deleted file mode 100644
> > index a83bf52c31e6..000000000000
> > --- a/src/libcamera/base/file_descriptor.cpp
> > +++ /dev/null
> > @@ -1,266 +0,0 @@
> > -/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > -/*
> > - * Copyright (C) 2019, Google Inc.
> > - *
> > - * file_descriptor.cpp - File descriptor wrapper
> > - */
> > -
> > -#include <libcamera/base/file_descriptor.h>
> > -
> > -#include <string.h>
> > -#include <sys/types.h>
> > -#include <unistd.h>
> > -#include <utility>
> > -
> > -#include <libcamera/base/log.h>
> > -#include <libcamera/base/unique_fd.h>
> > -
> > -/**
> > - * \file base/file_descriptor.h
> > - * \brief File descriptor wrapper
> > - */
> > -
> > -namespace libcamera {
> > -
> > -LOG_DEFINE_CATEGORY(FileDescriptor)
> > -
> > -/**
> > - * \class FileDescriptor
> > - * \brief RAII-style wrapper for file descriptors
> > - *
> > - * The FileDescriptor class provides RAII-style lifetime management of file
> > - * descriptors with an efficient mechanism for ownership sharing. At its core,
> > - * an internal Descriptor object wraps a file descriptor (expressed as a signed
> > - * integer) with an RAII-style interface. The Descriptor is then implicitly
> > - * shared with all FileDescriptor instances constructed as copies.
> > - *
> > - * When constructed from a numerical file descriptor, the FileDescriptor
> > - * instance either duplicates or takes over the file descriptor:
> > - *
> > - * - The FileDescriptor(const int &) constructor duplicates the numerical file
> > - *   descriptor and wraps the duplicate in a Descriptor. The caller is
> > - *   responsible for closing the original file descriptor, and the value
> > - *   returned by fd() will be different from the value passed to the
> > - *   constructor.
> > - *
> > - * - The FileDescriptor(int &&) constructor takes over the numerical file
> > - *   descriptor and wraps it in a Descriptor. The caller shall not touch the
> > - *   original file descriptor once the function returns, and the value returned
> > - *   by fd() will be identical to the value passed to the constructor.
> > - *
> > - * The copy constructor and assignment operator create copies that share the
> > - * Descriptor, while the move versions of those functions additionally make the
> > - * other FileDescriptor invalid. When the last FileDescriptor that references a
> > - * Descriptor is destroyed, the file descriptor is closed.
> > - *
> > - * The numerical file descriptor is available through the fd() function. All
> > - * FileDescriptor instances created as copies of a FileDescriptor will report
> > - * the same fd() value. Callers can perform operations on the fd(), but shall
> > - * never close it manually.
> > - */
> > -
> > -/**
> > - * \brief Create a FileDescriptor copying a given \a fd
> > - * \param[in] fd File descriptor
> > - *
> > - * Construct a FileDescriptor from a numerical file descriptor by duplicating
> > - * the \a fd, and take ownership of the copy. The original \a fd is left
> > - * untouched, and the caller is responsible for closing it when appropriate.
> > - * The duplicated file descriptor will be closed automatically when all
> > - * FileDescriptor instances that reference it are destroyed.
> > - *
> > - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> > - * the fd() function will return -1.
> > - */
> > -FileDescriptor::FileDescriptor(const int &fd)
> > -{
> > -	if (fd < 0)
> > -		return;
> > -
> > -	fd_ = std::make_shared<Descriptor>(fd, true);
> > -	if (fd_->fd() < 0)
> > -		fd_.reset();
> > -}
> > -
> > -/**
> > - * \brief Create a FileDescriptor taking ownership of a given \a fd
> > - * \param[in] fd File descriptor
> > - *
> > - * Construct a FileDescriptor from a numerical file descriptor by taking
> > - * ownership of the \a fd. The original \a fd is set to -1 and shall not be
> > - * touched by the caller anymore. In particular, the caller shall not close the
> > - * original \a fd manually. The duplicated file descriptor will be closed
> > - * automatically when all FileDescriptor instances that reference it are
> > - * destroyed.
> > - *
> > - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> > - * the fd() function will return -1.
> > - */
> > -FileDescriptor::FileDescriptor(int &&fd)
> > -{
> > -	if (fd < 0)
> > -		return;
> > -
> > -	fd_ = std::make_shared<Descriptor>(fd, false);
> > -	/*
> > -	 * The Descriptor constructor can't have failed here, as it took over
> > -	 * the fd without duplicating it. Just set the original fd to -1 to
> > -	 * implement move semantics.
> > -	 */
> > -	fd = -1;
> > -}
> > -
> > -/**
> > - * \brief Create a FileDescriptor taking ownership of a given UniqueFD \a fd
> > - * \param[in] fd UniqueFD
> > - *
> > - * Construct a FileDescriptor from UniqueFD by taking ownership of the \a fd.
> > - * The original \a fd becomes invalid.
> > - */
> > -FileDescriptor::FileDescriptor(UniqueFD fd)
> > -	: FileDescriptor(fd.release())
> > -{
> > -}
> > -
> > -/**
> > - * \brief Copy constructor, create a FileDescriptor from a copy of \a other
> > - * \param[in] other The other FileDescriptor
> > - *
> > - * Copying a FileDescriptor implicitly shares ownership of the wrapped file
> > - * descriptor. The original FileDescriptor is left untouched, and the caller is
> > - * responsible for destroying it when appropriate. The wrapped file descriptor
> > - * will be closed automatically when all FileDescriptor instances that
> > - * reference it are destroyed.
> > - */
> > -FileDescriptor::FileDescriptor(const FileDescriptor &other)
> > -	: fd_(other.fd_)
> > -{
> > -}
> > -
> > -/**
> > - * \brief Move constructor, create a FileDescriptor by taking over \a other
> > - * \param[in] other The other FileDescriptor
> > - *
> > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> > - * by \a other to the new FileDescriptor. The \a other FileDescriptor is
> > - * invalidated and its fd() function will return -1. The wrapped file descriptor
> > - * will be closed automatically when all FileDescriptor instances that
> > - * reference it are destroyed.
> > - */
> > -FileDescriptor::FileDescriptor(FileDescriptor &&other)
> > -	: fd_(std::move(other.fd_))
> > -{
> > -}
> > -
> > -/**
> > - * \brief Destroy the FileDescriptor instance
> > - *
> > - * Destroying a FileDescriptor instance releases its reference to the wrapped
> > - * descriptor, if any. When the last instance that references a wrapped
> > - * descriptor is destroyed, the file descriptor is automatically closed.
> > - */
> > -FileDescriptor::~FileDescriptor()
> > -{
> > -}
> > -
> > -/**
> > - * \brief Copy assignment operator, replace the wrapped file descriptor with a
> > - * copy of \a other
> > - * \param[in] other The other FileDescriptor
> > - *
> > - * Copying a FileDescriptor creates a new reference to the wrapped file
> > - * descriptor owner by \a other. If \a other is invalid, *this will also be
> > - * invalid. The original FileDescriptor is left untouched, and the caller is
> > - * responsible for destroying it when appropriate. The wrapped file descriptor
> > - * will be closed automatically when all FileDescriptor instances that
> > - * reference it are destroyed.
> > - *
> > - * \return A reference to this FileDescriptor
> > - */
> > -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)
> > -{
> > -	fd_ = other.fd_;
> > -
> > -	return *this;
> > -}
> > -
> > -/**
> > - * \brief Move assignment operator, replace the wrapped file descriptor by
> > - * taking over \a other
> > - * \param[in] other The other FileDescriptor
> > - *
> > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> > - * by \a other to the new FileDescriptor. If \a other is invalid, *this will
> > - * also be invalid. The \a other FileDescriptor is invalidated and its fd()
> > - * function will return -1. The wrapped file descriptor will be closed
> > - * automatically when all FileDescriptor instances that reference it are
> > - * destroyed.
> > - *
> > - * \return A reference to this FileDescriptor
> > - */
> > -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)
> > -{
> > -	fd_ = std::move(other.fd_);
> > -
> > -	return *this;
> > -}
> > -
> > -/**
> > - * \fn FileDescriptor::isValid()
> > - * \brief Check if the FileDescriptor instance is valid
> > - * \return True if the FileDescriptor is valid, false otherwise
> > - */
> > -
> > -/**
> > - * \fn FileDescriptor::fd()
> > - * \brief Retrieve the numerical file descriptor
> > - * \return The numerical file descriptor, which may be -1 if the FileDescriptor
> > - * instance is invalid
> > - */
> > -
> > -/**
> > - * \brief Duplicate a FileDescriptor
> > - *
> > - * Duplicating a FileDescriptor creates a duplicate of the wrapped file
> > - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function
> > - * of the original and the get() function of the duplicate will return different
> > - * values. The duplicate instance will not be affected by destruction of the
> > - * original instance or its copies.
> > - *
> > - * \return A UniqueFD owning a duplicate of the original file descriptor
> > - */
> > -UniqueFD FileDescriptor::dup() const
> > -{
> > -	int dupFd = ::dup(fd());
> > -	if (dupFd == -1) {
> > -		int ret = -errno;
> > -		LOG(FileDescriptor, Error)
> > -			<< "Failed to dup() fd: " << strerror(-ret);
> > -	}
> > -
> > -	return UniqueFD(dupFd);
> > -}
> > -
> > -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)
> > -{
> > -	if (!duplicate) {
> > -		fd_ = fd;
> > -		return;
> > -	}
> > -
> > -	/* Failing to dup() a fd should not happen and is fatal. */
> > -	fd_ = ::dup(fd);
> > -	if (fd_ == -1) {
> > -		int ret = -errno;
> > -		LOG(FileDescriptor, Fatal)
> > -			<< "Failed to dup() fd: " << strerror(-ret);
> > -	}
> > -}
> > -
> > -FileDescriptor::Descriptor::~Descriptor()
> > -{
> > -	if (fd_ != -1)
> > -		close(fd_);
> > -}
> > -
> > -} /* namespace libcamera */
> > diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build
> > index b0d85bc19245..ccb746c27466 100644
> > --- a/src/libcamera/base/meson.build
> > +++ b/src/libcamera/base/meson.build
> > @@ -8,12 +8,12 @@ libcamera_base_sources = files([
> >      'event_dispatcher_poll.cpp',
> >      'event_notifier.cpp',
> >      'file.cpp',
> > -    'file_descriptor.cpp',
> >      'flags.cpp',
> >      'log.cpp',
> >      'message.cpp',
> >      'object.cpp',
> >      'semaphore.cpp',
> > +    'shared_fd.cpp',
> >      'signal.cpp',
> >      'thread.cpp',
> >      'timer.cpp',
> > diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp
> > new file mode 100644
> > index 000000000000..05b6892f7e19
> > --- /dev/null
> > +++ b/src/libcamera/base/shared_fd.cpp
> > @@ -0,0 +1,262 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Google Inc.
> > + *
> > + * shared_fd.cpp - File descriptor wrapper with shared ownership
> > + */
> > +
> > +#include <libcamera/base/shared_fd.h>
> > +
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <unistd.h>
> > +#include <utility>
> > +
> > +#include <libcamera/base/log.h>
> > +#include <libcamera/base/unique_fd.h>
> > +
> > +/**
> > + * \file base/shared_fd.h
> > + * \brief File descriptor wrapper
> > + */
> > +
> > +namespace libcamera {
> > +
> > +LOG_DEFINE_CATEGORY(SharedFD)
> > +
> > +/**
> > + * \class SharedFD
> > + * \brief RAII-style wrapper for file descriptors
> > + *
> > + * The SharedFD class provides RAII-style lifetime management of file
> > + * descriptors with an efficient mechanism for ownership sharing. At its core,
> > + * an internal Descriptor object wraps a file descriptor (expressed as a signed
> > + * integer) with an RAII-style interface. The Descriptor is then implicitly
> > + * shared with all SharedFD instances constructed as copies.
> > + *
> > + * When constructed from a numerical file descriptor, the SharedFD instance
> > + * either duplicates or takes over the file descriptor:
> > + *
> > + * - The SharedFD(const int &) constructor duplicates the numerical file
> > + *   descriptor and wraps the duplicate in a Descriptor. The caller is
> > + *   responsible for closing the original file descriptor, and the value
> > + *   returned by fd() will be different from the value passed to the
> > + *   constructor.
> > + *
> > + * - The SharedFD(int &&) constructor takes over the numerical file descriptor
> > + *   and wraps it in a Descriptor. The caller shall not touch the original file
> > + *   descriptor once the function returns, and the value returned by fd() will
> > + *   be identical to the value passed to the constructor.
> > + *
> > + * The copy constructor and assignment operator create copies that share the
> > + * Descriptor, while the move versions of those functions additionally make the
> > + * other SharedFD invalid. When the last SharedFD that references a Descriptor
> > + * is destroyed, the file descriptor is closed.
> > + *
> > + * The numerical file descriptor is available through the fd() function. All
> > + * SharedFD instances created as copies of a SharedFD will report the same fd()
> > + * value. Callers can perform operations on the fd(), but shall never close it
> > + * manually.
> > + */
> > +
> > +/**
> > + * \brief Create a SharedFD copying a given \a fd
> > + * \param[in] fd File descriptor
> > + *
> > + * Construct a SharedFD from a numerical file descriptor by duplicating the
> > + * \a fd, and take ownership of the copy. The original \a fd is left untouched,
> > + * and the caller is responsible for closing it when appropriate. The duplicated
> > + * file descriptor will be closed automatically when all SharedFD instances that
> > + * reference it are destroyed.
> > + *
> > + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> > + * function will return -1.
> > + */
> > +SharedFD::SharedFD(const int &fd)
> > +{
> > +	if (fd < 0)
> > +		return;
> > +
> > +	fd_ = std::make_shared<Descriptor>(fd, true);
> > +	if (fd_->fd() < 0)
> > +		fd_.reset();
> > +}
> > +
> > +/**
> > + * \brief Create a SharedFD taking ownership of a given \a fd
> > + * \param[in] fd File descriptor
> > + *
> > + * Construct a SharedFD from a numerical file descriptor by taking ownership of
> > + * the \a fd. The original \a fd is set to -1 and shall not be touched by the
> > + * caller anymore. In particular, the caller shall not close the original \a fd
> > + * manually. The duplicated file descriptor will be closed automatically when
> > + * all SharedFD instances that reference it are destroyed.
> > + *
> > + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> > + * function will return -1.
> > + */
> > +SharedFD::SharedFD(int &&fd)
> > +{
> > +	if (fd < 0)
> > +		return;
> > +
> > +	fd_ = std::make_shared<Descriptor>(fd, false);
> > +	/*
> > +	 * The Descriptor constructor can't have failed here, as it took over
> > +	 * the fd without duplicating it. Just set the original fd to -1 to
> > +	 * implement move semantics.
> > +	 */
> > +	fd = -1;
> > +}
> > +
> > +/**
> > + * \brief Create a SharedFD taking ownership of a given UniqueFD \a fd
> > + * \param[in] fd UniqueFD
> > + *
> > + * Construct a SharedFD from UniqueFD by taking ownership of the \a fd. The
> > + * original \a fd becomes invalid.
> > + */
> > +SharedFD::SharedFD(UniqueFD fd)
> > +	: SharedFD(fd.release())
> > +{
> > +}
> > +
> > +/**
> > + * \brief Copy constructor, create a SharedFD from a copy of \a other
> > + * \param[in] other The other SharedFD
> > + *
> > + * Copying a SharedFD implicitly shares ownership of the wrapped file
> > + * descriptor. The original SharedFD is left untouched, and the caller is
> > + * responsible for destroying it when appropriate. The wrapped file descriptor
> > + * will be closed automatically when all SharedFD instances that reference it
> > + * are destroyed.
> > + */
> > +SharedFD::SharedFD(const SharedFD &other)
> > +	: fd_(other.fd_)
> > +{
> > +}
> > +
> > +/**
> > + * \brief Move constructor, create a SharedFD by taking over \a other
> > + * \param[in] other The other SharedFD
> > + *
> > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> > + * \a other to the new SharedFD. The \a other SharedFD is invalidated and its
> > + * fd() function will return -1. The wrapped file descriptor will be closed
> > + * automatically when all SharedFD instances that reference it are destroyed.
> > + */
> > +SharedFD::SharedFD(SharedFD &&other)
> > +	: fd_(std::move(other.fd_))
> > +{
> > +}
> > +
> > +/**
> > + * \brief Destroy the SharedFD instance
> > + *
> > + * Destroying a SharedFD instance releases its reference to the wrapped
> > + * descriptor, if any. When the last instance that references a wrapped
> > + * descriptor is destroyed, the file descriptor is automatically closed.
> > + */
> > +SharedFD::~SharedFD()
> > +{
> > +}
> > +
> > +/**
> > + * \brief Copy assignment operator, replace the wrapped file descriptor with a
> > + * copy of \a other
> > + * \param[in] other The other SharedFD
> > + *
> > + * Copying a SharedFD creates a new reference to the wrapped file descriptor
> > + * owner by \a other. If \a other is invalid, *this will also be invalid. The
> > + * original SharedFD is left untouched, and the caller is responsible for
> > + * destroying it when appropriate. The wrapped file descriptor will be closed
> > + * automatically when all SharedFD instances that reference it are destroyed.
> > + *
> > + * \return A reference to this SharedFD
> > + */
> > +SharedFD &SharedFD::operator=(const SharedFD &other)
> > +{
> > +	fd_ = other.fd_;
> > +
> > +	return *this;
> > +}
> > +
> > +/**
> > + * \brief Move assignment operator, replace the wrapped file descriptor by
> > + * taking over \a other
> > + * \param[in] other The other SharedFD
> > + *
> > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> > + * \a other to the new SharedFD. If \a other is invalid, *this will also be
> > + * invalid. The \a other SharedFD is invalidated and its fd() function will
> > + * return -1. The wrapped file descriptor will be closed automatically when
> > + * all SharedFD instances that reference it are destroyed.
> > + *
> > + * \return A reference to this SharedFD
> > + */
> > +SharedFD &SharedFD::operator=(SharedFD &&other)
> > +{
> > +	fd_ = std::move(other.fd_);
> > +
> > +	return *this;
> > +}
> > +
> > +/**
> > + * \fn SharedFD::isValid()
> > + * \brief Check if the SharedFD instance is valid
> > + * \return True if the SharedFD is valid, false otherwise
> > + */
> > +
> > +/**
> > + * \fn SharedFD::fd()
> > + * \brief Retrieve the numerical file descriptor
> > + * \return The numerical file descriptor, which may be -1 if the SharedFD
> > + * instance is invalid
> > + */
> > +
> > +/**
> > + * \brief Duplicate a SharedFD
> > + *
> > + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and
> > + * returns a UniqueFD that owns the duplicate. The fd() function of the original
> > + * and the get() function of the duplicate will return different values. The
> > + * duplicate instance will not be affected by destruction of the original
> > + * instance or its copies.
> > + *
> > + * \return A UniqueFD owning a duplicate of the original file descriptor
> > + */
> > +UniqueFD SharedFD::dup() const
> > +{
> > +	int dupFd = ::dup(fd());
> > +	if (dupFd == -1) {
> > +		int ret = -errno;
> > +		LOG(SharedFD, Error)
> > +			<< "Failed to dup() fd: " << strerror(-ret);
> > +	}
> > +
> > +	return UniqueFD(dupFd);
> > +}
> > +
> > +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)
> > +{
> > +	if (!duplicate) {
> > +		fd_ = fd;
> > +		return;
> > +	}
> > +
> > +	/* Failing to dup() a fd should not happen and is fatal. */
> > +	fd_ = ::dup(fd);
> > +	if (fd_ == -1) {
> > +		int ret = -errno;
> > +		LOG(SharedFD, Fatal)
> > +			<< "Failed to dup() fd: " << strerror(-ret);
> > +	}
> > +}
> > +
> > +SharedFD::Descriptor::~Descriptor()
> > +{
> > +	if (fd_ != -1)
> > +		close(fd_);
> > +}
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp
> > index f5bcf107d7aa..0a5bf7fdbeb7 100644
> > --- a/src/libcamera/framebuffer.cpp
> > +++ b/src/libcamera/framebuffer.cpp
> > @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()
> >   * offset and length.
> >   *
> >   * To support DMA access, planes are associated with dmabuf objects represented
> > - * by FileDescriptor handles. The Plane class doesn't handle mapping of the
> > - * memory to the CPU, but applications and IPAs may use the dmabuf file
> > - * descriptors to map the plane memory with mmap() and access its contents.
> > + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to
> > + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map
> > + * the plane memory with mmap() and access its contents.
> >   *
> >   * \todo Specify how an application shall decide whether to use a single or
> >   * multiple dmabufs, based on the camera requirements.
> > diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
> > index 82ec9b20a411..0a259305afa2 100644
> > --- a/src/libcamera/ipa_data_serializer.cpp
> > +++ b/src/libcamera/ipa_data_serializer.cpp
> > @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)
> >   *
> >   * \todo Harden the vector and map deserializer
> >   *
> > - * \todo For FileDescriptors, instead of storing a validity flag, store an
> > + * \todo For SharedFDs, instead of storing a validity flag, store an
> >   * index into the fd array. This will allow us to use views instead of copying.
> >   */
> >
> > @@ -112,7 +112,7 @@ namespace {
> >   * \param[in] cs ControlSerializer
> >   *
> >   * This version of deserialize() can be used if the object type \a T and its
> > - * members don't have any FileDescriptor.
> > + * members don't have any SharedFD.
> >   *
> >   * \a cs is only necessary if the object type \a T or its members contain
> >   * ControlList or ControlInfoMap.
> > @@ -132,7 +132,7 @@ namespace {
> >   * \param[in] cs ControlSerializer
> >   *
> >   * This version of deserialize() can be used if the object type \a T and its
> > - * members don't have any FileDescriptor.
> > + * members don't have any SharedFD.
> >   *
> >   * \a cs is only necessary if the object type \a T or its members contain
> >   * ControlList or ControlInfoMap.
> > @@ -143,7 +143,7 @@ namespace {
> >  /**
> >   * \fn template<typename T> IPADataSerializer<T>::deserialize(
> >   * 	const std::vector<uint8_t> &data,
> > - * 	const std::vector<FileDescriptor> &fds,
> > + * 	const std::vector<SharedFD> &fds,
> >   * 	ControlSerializer *cs = nullptr)
> >   * \brief Deserialize byte vector and fd vector into an object
> >   * \tparam T Type of object to deserialize to
> > @@ -152,7 +152,7 @@ namespace {
> >   * \param[in] cs ControlSerializer
> >   *
> >   * This version of deserialize() (or the iterator version) must be used if
> > - * the object type \a T or its members contain FileDescriptor.
> > + * the object type \a T or its members contain SharedFD.
> >   *
> >   * \a cs is only necessary if the object type \a T or its members contain
> >   * ControlList or ControlInfoMap.
> > @@ -164,8 +164,8 @@ namespace {
> >   * \fn template<typename T> IPADataSerializer::deserialize(
> >   * 	std::vector<uint8_t>::const_iterator dataBegin,
> >   * 	std::vector<uint8_t>::const_iterator dataEnd,
> > - * 	std::vector<FileDescriptor>::const_iterator fdsBegin,
> > - * 	std::vector<FileDescriptor>::const_iterator fdsEnd,
> > + * 	std::vector<SharedFD>::const_iterator fdsBegin,
> > + * 	std::vector<SharedFD>::const_iterator fdsEnd,
> >   * 	ControlSerializer *cs = nullptr)
> >   * \brief Deserialize byte vector and fd vector into an object
> >   * \tparam T Type of object to deserialize to
> > @@ -176,7 +176,7 @@ namespace {
> >   * \param[in] cs ControlSerializer
> >   *
> >   * This version of deserialize() (or the vector version) must be used if
> > - * the object type \a T or its members contain FileDescriptor.
> > + * the object type \a T or its members contain SharedFD.
> >   *
> >   * \a cs is only necessary if the object type \a T or its members contain
> >   * ControlList or ControlInfoMap.
> > @@ -189,7 +189,7 @@ namespace {
> >  #define DEFINE_POD_SERIALIZER(type)					\
> >  									\
> >  template<>								\
> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>		\
> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>		\
> >  IPADataSerializer<type>::serialize(const type &data,			\
> >  				  [[maybe_unused]] ControlSerializer *cs) \
> >  {									\
> > @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> >  									\
> >  template<>								\
> >  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> > -					  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \
> > +					  [[maybe_unused]] const std::vector<SharedFD> &fds, \
> >  					  ControlSerializer *cs)	\
> >  {									\
> >  	return deserialize(data.cbegin(), data.end(), cs);		\
> > @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> >  template<>								\
> >  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \
> >  					  std::vector<uint8_t>::const_iterator dataEnd, \
> > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \
> > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \
> > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \
> > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \
> >  					  ControlSerializer *cs)	\
> >  {									\
> >  	return deserialize(dataBegin, dataEnd, cs);			\
> > @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)
> >   * function parameter serdes).
> >   */
> >  template<>
> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  IPADataSerializer<std::string>::serialize(const std::string &data,
> >  					  [[maybe_unused]] ControlSerializer *cs)
> >  {
> > @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
> >  template<>
> >  std::string
> >  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,
> > -					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> > +					    [[maybe_unused]] const std::vector<SharedFD> &fds,
> >  					    [[maybe_unused]] ControlSerializer *cs)
> >  {
> >  	return { data.cbegin(), data.cend() };
> > @@ -288,8 +288,8 @@ template<>
> >  std::string
> >  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  					    std::vector<uint8_t>::const_iterator dataEnd,
> > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> >  					    [[maybe_unused]] ControlSerializer *cs)
> >  {
> >  	return { dataBegin, dataEnd };
> > @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
> >   * be used. The serialized ControlInfoMap will have zero length.
> >   */
> >  template<>
> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)
> >  {
> >  	if (!cs)
> > @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
> >  template<>
> >  ControlList
> >  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
> > -					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> > +					    [[maybe_unused]] const std::vector<SharedFD> &fds,
> >  					    ControlSerializer *cs)
> >  {
> >  	return deserialize(data.cbegin(), data.end(), cs);
> > @@ -417,8 +417,8 @@ template<>
> >  ControlList
> >  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  					    std::vector<uint8_t>::const_iterator dataEnd,
> > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> >  					    ControlSerializer *cs)
> >  {
> >  	return deserialize(dataBegin, dataEnd, cs);
> > @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator
> >   * X bytes - Serialized ControlInfoMap (using ControlSerializer)
> >   */
> >  template<>
> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,
> >  					     ControlSerializer *cs)
> >  {
> > @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
> >  template<>
> >  ControlInfoMap
> >  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
> > -					       [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> > +					       [[maybe_unused]] const std::vector<SharedFD> &fds,
> >  					       ControlSerializer *cs)
> >  {
> >  	return deserialize(data.cbegin(), data.end(), cs);
> > @@ -503,30 +503,30 @@ template<>
> >  ControlInfoMap
> >  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  					       std::vector<uint8_t>::const_iterator dataEnd,
> > -					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > +					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> >  					       ControlSerializer *cs)
> >  {
> >  	return deserialize(dataBegin, dataEnd, cs);
> >  }
> >
> >  /*
> > - * FileDescriptors are serialized into four bytes that tells if the
> > - * FileDescriptor is valid or not. If it is valid, then for serialization
> > - * the fd will be written to the fd vector, or for deserialization the
> > - * fd vector const_iterator will be valid.
> > + * SharedFD instances are serialized into four bytes that tells if the SharedFD
> > + * is valid or not. If it is valid, then for serialization the fd will be
> > + * written to the fd vector, or for deserialization the fd vector const_iterator
> > + * will be valid.
> >   *
> >   * This validity is necessary so that we don't send -1 fd over sendmsg(). It
> >   * also allows us to simply send the entire fd vector into the deserializer
> >   * and it will be recursively consumed as necessary.
> >   */
> >  template<>
> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
> > -					     [[maybe_unused]] ControlSerializer *cs)
> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,
> > +				       [[maybe_unused]] ControlSerializer *cs)
> >  {
> >  	std::vector<uint8_t> dataVec;
> > -	std::vector<FileDescriptor> fdVec;
> > +	std::vector<SharedFD> fdVec;
> >
> >  	/*
> >  	 * Store as uint32_t to prepare for conversion from validity flag
> > @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
> >  }
> >
> >  template<>
> > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> > -							      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> > -							      std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -							      std::vector<FileDescriptor>::const_iterator fdsEnd,
> > -							      [[maybe_unused]] ControlSerializer *cs)
> > +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> > +						  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> > +						  std::vector<SharedFD>::const_iterator fdsBegin,
> > +						  std::vector<SharedFD>::const_iterator fdsEnd,
> > +						  [[maybe_unused]] ControlSerializer *cs)
> >  {
> >  	ASSERT(std::distance(dataBegin, dataEnd) >= 4);
> >
> > @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s
> >
> >  	ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));
> >
> > -	return valid ? *fdsBegin : FileDescriptor();
> > +	return valid ? *fdsBegin : SharedFD();
> >  }
> >
> >  template<>
> > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,
> > -							      const std::vector<FileDescriptor> &fds,
> > -							      [[maybe_unused]] ControlSerializer *cs)
> > +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,
> > +						  const std::vector<SharedFD> &fds,
> > +						  [[maybe_unused]] ControlSerializer *cs)
> >  {
> >  	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());
> >  }
> > @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<
> >  /*
> >   * FrameBuffer::Plane is serialized as:
> >   *
> > - * 4 byte  - FileDescriptor
> > + * 4 byte  - SharedFD
> >   * 4 bytes - uint32_t Offset
> >   * 4 bytes - uint32_t Length
> >   */
> >  template<>
> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,
> >  						 [[maybe_unused]] ControlSerializer *cs)
> >  {
> >  	std::vector<uint8_t> dataVec;
> > -	std::vector<FileDescriptor> fdsVec;
> > +	std::vector<SharedFD> fdsVec;
> >
> >  	std::vector<uint8_t> fdBuf;
> > -	std::vector<FileDescriptor> fdFds;
> > +	std::vector<SharedFD> fdFds;
> >  	std::tie(fdBuf, fdFds) =
> > -		IPADataSerializer<FileDescriptor>::serialize(data.fd);
> > +		IPADataSerializer<SharedFD>::serialize(data.fd);
> >  	dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());
> >  	fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());
> >
> > @@ -597,13 +597,13 @@ template<>
> >  FrameBuffer::Plane
> >  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  						   std::vector<uint8_t>::const_iterator dataEnd,
> > -						   std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -						   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +						   std::vector<SharedFD>::const_iterator fdsBegin,
> > +						   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> >  						   [[maybe_unused]] ControlSerializer *cs)
> >  {
> >  	FrameBuffer::Plane ret;
> >
> > -	ret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,
> > +	ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,
> >  								fdsBegin, fdsBegin + 1);
> >  	ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);
> >  	ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);
> > @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i
> >  template<>
> >  FrameBuffer::Plane
> >  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,
> > -						   const std::vector<FileDescriptor> &fds,
> > +						   const std::vector<SharedFD> &fds,
> >  						   ControlSerializer *cs)
> >  {
> >  	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> > diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp
> > index ad870fd4137f..3b47032de0a2 100644
> > --- a/src/libcamera/ipc_pipe.cpp
> > +++ b/src/libcamera/ipc_pipe.cpp
> > @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)
> >  	data_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),
> >  				     payload.data.end());
> >  	for (int32_t &fd : payload.fds)
> > -		fds_.push_back(FileDescriptor(std::move(fd)));
> > +		fds_.push_back(SharedFD(std::move(fd)));
> >  }
> >
> >  /**
> > @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const
> >  		       data_.data(), data_.size());
> >  	}
> >
> > -	for (const FileDescriptor &fd : fds_)
> > +	for (const SharedFD &fd : fds_)
> >  		payload.fds.push_back(fd.fd());
> >
> >  	return payload;
> > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > index ffa51a0c65ca..ea8243912a29 100644
> > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > @@ -12,7 +12,7 @@
> >  #include <queue>
> >  #include <unordered_set>
> >
> > -#include <libcamera/base/file_descriptor.h>
> > +#include <libcamera/base/shared_fd.h>
> >
> >  #include <libcamera/camera.h>
> >  #include <libcamera/control_ids.h>
> > @@ -228,7 +228,7 @@ public:
> >
> >  	/* DMAHEAP allocation helper. */
> >  	RPi::DmaHeap dmaHeap_;
> > -	FileDescriptor lsTable_;
> > +	SharedFD lsTable_;
> >
> >  	std::unique_ptr<DelayedControls> delayedCtrls_;
> >  	bool sensorMetadata_;
> > @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)
> >  		if (!fd.isValid())
> >  			return -ENOMEM;
> >
> > -		lsTable_ = FileDescriptor(std::move(fd));
> > +		lsTable_ = SharedFD(std::move(fd));
> >
> >  		/* Allow the IPA to mmap the LS table via the file descriptor. */
> >  		/*
> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> > index 3966483a365f..97d431071def 100644
> > --- a/src/libcamera/v4l2_videodevice.cpp
> > +++ b/src/libcamera/v4l2_videodevice.cpp
> > @@ -22,8 +22,8 @@
> >  #include <linux/version.h>
> >
> >  #include <libcamera/base/event_notifier.h>
> > -#include <libcamera/base/file_descriptor.h>
> >  #include <libcamera/base/log.h>
> > +#include <libcamera/base/shared_fd.h>
> >  #include <libcamera/base/unique_fd.h>
> >  #include <libcamera/base/utils.h>
> >
> > @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
> >  			return nullptr;
> >
> >  		FrameBuffer::Plane plane;
> > -		plane.fd = FileDescriptor(std::move(fd));
> > +		plane.fd = SharedFD(std::move(fd));
> >  		/*
> >  		 * V4L2 API doesn't provide dmabuf offset information of plane.
> >  		 * Set 0 as a placeholder offset.
> > @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
> >  		ASSERT(numPlanes == 1u);
> >
> >  		planes.resize(formatInfo_->numPlanes());
> > -		const FileDescriptor &fd = planes[0].fd;
> > +		const SharedFD &fd = planes[0].fd;
> >  		size_t offset = 0;
> >
> >  		for (auto [i, plane] : utils::enumerate(planes)) {
> > diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
> > index 9817fd393d59..586347829845 100644
> > --- a/src/v4l2/v4l2_camera.h
> > +++ b/src/v4l2/v4l2_camera.h
> > @@ -11,8 +11,8 @@
> >  #include <mutex>
> >  #include <utility>
> >
> > -#include <libcamera/base/file_descriptor.h>
> >  #include <libcamera/base/semaphore.h>
> > +#include <libcamera/base/shared_fd.h>
> >
> >  #include <libcamera/camera.h>
> >  #include <libcamera/framebuffer.h>
> > diff --git a/test/meson.build b/test/meson.build
> > index 42dfbc1f8ee9..daaa3862cdd6 100644
> > --- a/test/meson.build
> > +++ b/test/meson.build
> > @@ -40,7 +40,6 @@ internal_tests = [
> >      ['event-dispatcher',                'event-dispatcher.cpp'],
> >      ['event-thread',                    'event-thread.cpp'],
> >      ['file',                            'file.cpp'],
> > -    ['file-descriptor',                 'file-descriptor.cpp'],
> >      ['flags',                           'flags.cpp'],
> >      ['hotplug-cameras',                 'hotplug-cameras.cpp'],
> >      ['mapped-buffer',                   'mapped-buffer.cpp'],
> > @@ -49,6 +48,7 @@ internal_tests = [
> >      ['object-delete',                   'object-delete.cpp'],
> >      ['object-invoke',                   'object-invoke.cpp'],
> >      ['pixel-format',                    'pixel-format.cpp'],
> > +    ['shared-fd',                       'shared-fd.cpp'],
> >      ['signal-threads',                  'signal-threads.cpp'],
> >      ['threads',                         'threads.cpp'],
> >      ['timer',                           'timer.cpp'],
> > diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
> > index 5fcdcb8eae92..d2050a868b38 100644
> > --- a/test/serialization/ipa_data_serializer_test.cpp
> > +++ b/test/serialization/ipa_data_serializer_test.cpp
> > @@ -53,7 +53,7 @@ template<typename T>
> >  int testPodSerdes(T in)
> >  {
> >  	std::vector<uint8_t> buf;
> > -	std::vector<FileDescriptor> fds;
> > +	std::vector<SharedFD> fds;
> >
> >  	std::tie(buf, fds) = IPADataSerializer<T>::serialize(in);
> >  	T out = IPADataSerializer<T>::deserialize(buf, fds);
> > @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,
> >  		     ControlSerializer *cs = nullptr)
> >  {
> >  	std::vector<uint8_t> buf;
> > -	std::vector<FileDescriptor> fds;
> > +	std::vector<SharedFD> fds;
> >
> >  	std::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);
> >  	std::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);
> > @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,
> >  		  ControlSerializer *cs = nullptr)
> >  {
> >  	std::vector<uint8_t> buf;
> > -	std::vector<FileDescriptor> fds;
> > +	std::vector<SharedFD> fds;
> >
> >  	std::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);
> >  	std::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);
> > @@ -198,7 +198,7 @@ private:
> >  		ControlSerializer cs(ControlSerializer::Role::Proxy);
> >
> >  		/*
> > -		 * We don't test FileDescriptor serdes because it dup()s, so we
> > +		 * We don't test SharedFD serdes because it dup()s, so we
> >  		 * can't check for equality.
> >  		 */
> >  		std::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };
> > @@ -219,7 +219,7 @@ private:
> >  		};
> >
> >  		std::vector<uint8_t> buf;
> > -		std::vector<FileDescriptor> fds;
> > +		std::vector<SharedFD> fds;
> >
> >  		if (testVectorSerdes(vecUint8) != TestPass)
> >  			return TestFail;
> > @@ -291,7 +291,7 @@ private:
> >  			{ { "a", { 1, 2, 3 } }, { "b", { 4, 5, 6 } }, { "c", { 7, 8, 9 } } };
> >
> >  		std::vector<uint8_t> buf;
> > -		std::vector<FileDescriptor> fds;
> > +		std::vector<SharedFD> fds;
> >
> >  		if (testMapSerdes(mapUintStr) != TestPass)
> >  			return TestFail;
> > @@ -359,7 +359,7 @@ private:
> >  		std::string strEmpty = "";
> >
> >  		std::vector<uint8_t> buf;
> > -		std::vector<FileDescriptor> fds;
> > +		std::vector<SharedFD> fds;
> >
> >  		if (testPodSerdes(u32min) != TestPass)
> >  			return TestFail;
> > diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp
> > similarity index 80%
> > rename from test/file-descriptor.cpp
> > rename to test/shared-fd.cpp
> > index 76badc4c5fad..60e5d0aaa395 100644
> > --- a/test/file-descriptor.cpp
> > +++ b/test/shared-fd.cpp
> > @@ -2,7 +2,7 @@
> >  /*
> >   * Copyright (C) 2019, Google Inc.
> >   *
> > - * file_descriptor.cpp - FileDescriptor test
> > + * shared_fd.cpp - SharedFD test
> >   */
> >
> >  #include <fcntl.h>
> > @@ -11,7 +11,7 @@
> >  #include <sys/types.h>
> >  #include <unistd.h>
> >
> > -#include <libcamera/base/file_descriptor.h>
> > +#include <libcamera/base/shared_fd.h>
> >  #include <libcamera/base/utils.h>
> >
> >  #include "test.h"
> > @@ -19,7 +19,7 @@
> >  using namespace libcamera;
> >  using namespace std;
> >
> > -class FileDescriptorTest : public Test
> > +class SharedFDTest : public Test
> >  {
> >  protected:
> >  	int init()
> > @@ -43,8 +43,8 @@ protected:
> >
> >  	int run()
> >  	{
> > -		/* Test creating empty FileDescriptor. */
> > -		desc1_ = new FileDescriptor();
> > +		/* Test creating empty SharedFD. */
> > +		desc1_ = new SharedFD();
> >
> >  		if (desc1_->fd() != -1) {
> >  			std::cout << "Failed fd numerical check (default constructor)"
> > @@ -56,10 +56,10 @@ protected:
> >  		desc1_ = nullptr;
> >
> >  		/*
> > -		 * Test creating FileDescriptor by copying numerical file
> > +		 * Test creating SharedFD by copying numerical file
> >  		 * descriptor.
> >  		 */
> > -		desc1_ = new FileDescriptor(fd_);
> > +		desc1_ = new SharedFD(fd_);
> >  		if (desc1_->fd() == fd_) {
> >  			std::cout << "Failed fd numerical check (lvalue ref constructor)"
> >  				  << std::endl;
> > @@ -84,13 +84,13 @@ protected:
> >  		}
> >
> >  		/*
> > -		 * Test creating FileDescriptor by taking ownership of
> > +		 * Test creating SharedFD by taking ownership of
> >  		 * numerical file descriptor.
> >  		 */
> >  		int dupFd = dup(fd_);
> >  		int dupFdCopy = dupFd;
> >
> > -		desc1_ = new FileDescriptor(std::move(dupFd));
> > +		desc1_ = new SharedFD(std::move(dupFd));
> >  		if (desc1_->fd() != dupFdCopy) {
> >  			std::cout << "Failed fd numerical check (rvalue ref constructor)"
> >  				  << std::endl;
> > @@ -114,9 +114,9 @@ protected:
> >  			return TestFail;
> >  		}
> >
> > -		/* Test creating FileDescriptor from other FileDescriptor. */
> > -		desc1_ = new FileDescriptor(fd_);
> > -		desc2_ = new FileDescriptor(*desc1_);
> > +		/* Test creating SharedFD from other SharedFD. */
> > +		desc1_ = new SharedFD(fd_);
> > +		desc2_ = new SharedFD(*desc1_);
> >
> >  		if (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {
> >  			std::cout << "Failed fd numerical check (copy constructor)"
> > @@ -142,10 +142,10 @@ protected:
> >  		delete desc2_;
> >  		desc2_ = nullptr;
> >
> > -		/* Test creating FileDescriptor by taking over other FileDescriptor. */
> > -		desc1_ = new FileDescriptor(fd_);
> > +		/* Test creating SharedFD by taking over other SharedFD. */
> > +		desc1_ = new SharedFD(fd_);
> >  		fd = desc1_->fd();
> > -		desc2_ = new FileDescriptor(std::move(*desc1_));
> > +		desc2_ = new SharedFD(std::move(*desc1_));
> >
> >  		if (desc1_->fd() != -1 || desc2_->fd() != fd) {
> >  			std::cout << "Failed fd numerical check (move constructor)"
> > @@ -164,9 +164,9 @@ protected:
> >  		delete desc2_;
> >  		desc2_ = nullptr;
> >
> > -		/* Test creating FileDescriptor by copy assignment. */
> > -		desc1_ = new FileDescriptor();
> > -		desc2_ = new FileDescriptor(fd_);
> > +		/* Test creating SharedFD by copy assignment. */
> > +		desc1_ = new SharedFD();
> > +		desc2_ = new SharedFD(fd_);
> >
> >  		fd = desc2_->fd();
> >  		*desc1_ = *desc2_;
> > @@ -188,9 +188,9 @@ protected:
> >  		delete desc2_;
> >  		desc2_ = nullptr;
> >
> > -		/* Test creating FileDescriptor by move assignment. */
> > -		desc1_ = new FileDescriptor();
> > -		desc2_ = new FileDescriptor(fd_);
> > +		/* Test creating SharedFD by move assignment. */
> > +		desc1_ = new SharedFD();
> > +		desc2_ = new SharedFD(fd_);
> >
> >  		fd = desc2_->fd();
> >  		*desc1_ = std::move(*desc2_);
> > @@ -237,7 +237,7 @@ private:
> >
> >  	int fd_;
> >  	ino_t inodeNr_;
> > -	FileDescriptor *desc1_, *desc2_;
> > +	SharedFD *desc1_, *desc2_;
> >  };
> >
> > -TEST_REGISTER(FileDescriptorTest)
> > +TEST_REGISTER(SharedFDTest)
> > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> > index d856339aa9ee..c37c4941b528 100644
> > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> > @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)
> >  void {{proxy_name}}::{{method.mojom_name}}IPC(
> >  	std::vector<uint8_t>::const_iterator data,
> >  	size_t dataSize,
> > -	[[maybe_unused]] const std::vector<FileDescriptor> &fds)
> > +	[[maybe_unused]] const std::vector<SharedFD> &fds)
> >  {
> >  {%- for param in method.parameters %}
> >  	{{param|name}} {{param.mojom_name}};
> > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> > index ce396c183d0c..c308dd10c7e5 100644
> > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> > @@ -64,7 +64,7 @@ private:
> >  	void {{method.mojom_name}}IPC(
> >  		std::vector<uint8_t>::const_iterator data,
> >  		size_t dataSize,
> > -		const std::vector<FileDescriptor> &fds);
> > +		const std::vector<SharedFD> &fds);
> >  {% endfor %}
> >
> >  	/* Helper class to invoke async functions in another thread. */
> > diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> > index ebcd2aaaafae..bac826a74c2d 100644
> > --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> > +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> > @@ -54,7 +54,7 @@
> >  {%- for param in params %}
> >  	std::vector<uint8_t> {{param.mojom_name}}Buf;
> >  {%- if param|has_fd %}
> > -	std::vector<FileDescriptor> {{param.mojom_name}}Fds;
> > +	std::vector<SharedFD> {{param.mojom_name}}Fds;
> >  	std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =
> >  {%- else %}
> >  	std::tie({{param.mojom_name}}Buf, std::ignore) =
> > diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > index b8ef8e7b974e..77bae36fe6b7 100644
> > --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > @@ -40,7 +40,7 @@
> >  		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> >  {%- elif field|is_fd %}
> >  		std::vector<uint8_t> {{field.mojom_name}};
> > -		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> > +		std::vector<SharedFD> {{field.mojom_name}}Fds;
> >  		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> >  			IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
> >  		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> > @@ -58,7 +58,7 @@
> >  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
> >  		std::vector<uint8_t> {{field.mojom_name}};
> >  	{%- if field|has_fd %}
> > -		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> > +		std::vector<SharedFD> {{field.mojom_name}}Fds;
> >  		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> >  	{%- else %}
> >  		std::tie({{field.mojom_name}}, std::ignore) =
> > @@ -177,7 +177,7 @@
> >   # \a struct.
> >   #}
> >  {%- macro serializer(struct, namespace) %}
> > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> >  	serialize(const {{struct|name_full}} &data,
> >  {%- if struct|needs_control_serializer %}
> >  		  ControlSerializer *cs)
> > @@ -187,7 +187,7 @@
> >  	{
> >  		std::vector<uint8_t> retData;
> >  {%- if struct|has_fd %}
> > -		std::vector<FileDescriptor> retFds;
> > +		std::vector<SharedFD> retFds;
> >  {%- endif %}
> >  {%- for field in struct.fields %}
> >  {{serializer_field(field, namespace, loop)}}
> > @@ -210,7 +210,7 @@
> >  {%- macro deserializer_fd(struct, namespace) %}
> >  	static {{struct|name_full}}
> >  	deserialize(std::vector<uint8_t> &data,
> > -		    std::vector<FileDescriptor> &fds,
> > +		    std::vector<SharedFD> &fds,
> >  {%- if struct|needs_control_serializer %}
> >  		    ControlSerializer *cs)
> >  {%- else %}
> > @@ -224,8 +224,8 @@
> >  	static {{struct|name_full}}
> >  	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  		    std::vector<uint8_t>::const_iterator dataEnd,
> > -		    std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -		    std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +		    std::vector<SharedFD>::const_iterator fdsBegin,
> > +		    std::vector<SharedFD>::const_iterator fdsEnd,
> >  {%- if struct|needs_control_serializer %}
> >  		    ControlSerializer *cs)
> >  {%- else %}
> > @@ -234,7 +234,7 @@
> >  	{
> >  		{{struct|name_full}} ret;
> >  		std::vector<uint8_t>::const_iterator m = dataBegin;
> > -		std::vector<FileDescriptor>::const_iterator n = fdsBegin;
> > +		std::vector<SharedFD>::const_iterator n = fdsBegin;
> >
> >  		size_t dataSize = std::distance(dataBegin, dataEnd);
> >  		[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
> > @@ -255,7 +255,7 @@
> >  {%- macro deserializer_fd_simple(struct, namespace) %}
> >  	static {{struct|name_full}}
> >  	deserialize(std::vector<uint8_t> &data,
> > -		    [[maybe_unused]] std::vector<FileDescriptor> &fds,
> > +		    [[maybe_unused]] std::vector<SharedFD> &fds,
> >  		    ControlSerializer *cs = nullptr)
> >  	{
> >  		return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);
> > @@ -264,8 +264,8 @@
> >  	static {{struct|name_full}}
> >  	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> >  		    std::vector<uint8_t>::const_iterator dataEnd,
> > -		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > -		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > +		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > +		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> >  		    ControlSerializer *cs = nullptr)
> >  	{
> >  		return IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);
> > diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py
> > index c609f4e5c062..753bfc734e56 100644
> > --- a/utils/ipc/generators/mojom_libcamera_generator.py
> > +++ b/utils/ipc/generators/mojom_libcamera_generator.py
> > @@ -77,7 +77,7 @@ def GetDefaultValue(element):
> >      if mojom.IsEnumKind(element.kind):
> >          return f'static_cast<{element.kind.mojom_name}>(0)'
> >      if isinstance(element.kind, mojom.Struct) and \
> > -       element.kind.mojom_name == 'FileDescriptor':
> > +       element.kind.mojom_name == 'SharedFD':
> >          return '-1'
> >      return ''
> >
> > @@ -140,7 +140,7 @@ def HasFd(element):
> >          types = GetAllTypes(element)
> >      else:
> >          types = GetAllTypes(element.kind)
> > -    return "FileDescriptor" in types or (attrs is not None and "hasFd" in attrs)
> > +    return "SharedFD" in types or (attrs is not None and "hasFd" in attrs)
> >
> >  def WithDefaultValues(element):
> >      return [x for x in element if HasDefaultValue(x)]
> > @@ -221,7 +221,7 @@ def IsEnum(element):
> >      return mojom.IsEnumKind(element.kind)
> >
> >  def IsFd(element):
> > -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "FileDescriptor"
> > +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "SharedFD"
> >
> >  def IsMap(element):
> >      return mojom.IsMapKind(element.kind)
Jacopo Mondi Nov. 29, 2021, 5:30 p.m. UTC | #4
Hi Laurent

On Mon, Nov 29, 2021 at 07:24:47PM +0200, Laurent Pinchart wrote:
> Hi Jacopo,
>
> On Mon, Nov 29, 2021 at 05:00:55PM +0100, Jacopo Mondi wrote:
> > On Mon, Nov 29, 2021 at 01:57:52AM +0200, Laurent Pinchart wrote:
> > > Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.
> > > Rename it to SharedFD.
> > >
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >
> > makes sense!
>
> Does this mean Rb ? :-)
>

ups :)
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

It does!

> > > ---
> > >  include/libcamera/base/file.h                 |   4 +-
> > >  include/libcamera/base/meson.build            |   2 +-
> > >  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-
> > >  include/libcamera/framebuffer.h               |   4 +-
> > >  .../libcamera/internal/ipa_data_serializer.h  |  40 +--
> > >  include/libcamera/internal/ipc_pipe.h         |   8 +-
> > >  include/libcamera/ipa/core.mojom              |   6 +-
> > >  include/libcamera/ipa/raspberrypi.mojom       |   2 +-
> > >  src/android/camera_device.cpp                 |   2 +-
> > >  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-
> > >  src/libcamera/base/file.cpp                   |   6 +-
> > >  src/libcamera/base/file_descriptor.cpp        | 266 ------------------
> > >  src/libcamera/base/meson.build                |   2 +-
> > >  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++
> > >  src/libcamera/framebuffer.cpp                 |   6 +-
> > >  src/libcamera/ipa_data_serializer.cpp         | 100 +++----
> > >  src/libcamera/ipc_pipe.cpp                    |   4 +-
> > >  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-
> > >  src/libcamera/v4l2_videodevice.cpp            |   6 +-
> > >  src/v4l2/v4l2_camera.h                        |   2 +-
> > >  test/meson.build                              |   2 +-
> > >  .../ipa_data_serializer_test.cpp              |  14 +-
> > >  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--
> > >  .../module_ipa_proxy.cpp.tmpl                 |   2 +-
> > >  .../module_ipa_proxy.h.tmpl                   |   2 +-
> > >  .../libcamera_templates/proxy_functions.tmpl  |   2 +-
> > >  .../libcamera_templates/serializer.tmpl       |  22 +-
> > >  .../generators/mojom_libcamera_generator.py   |   6 +-
> > >  28 files changed, 422 insertions(+), 426 deletions(-)
> > >  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)
> > >  delete mode 100644 src/libcamera/base/file_descriptor.cpp
> > >  create mode 100644 src/libcamera/base/shared_fd.cpp
> > >  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)
> > >
> > > diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h
> > > index 47769da7abc2..691b52d6ab2d 100644
> > > --- a/include/libcamera/base/file.h
> > > +++ b/include/libcamera/base/file.h
> > > @@ -21,7 +21,7 @@
> > >
> > >  namespace libcamera {
> > >
> > > -class FileDescriptor;
> > > +class SharedFD;
> > >
> > >  class File
> > >  {
> > > @@ -69,7 +69,7 @@ public:
> > >  	bool unmap(uint8_t *addr);
> > >
> > >  	static bool exists(const std::string &name);
> > > -	static ino_t inode(const FileDescriptor &fd);
> > > +	static ino_t inode(const SharedFD &fd);
> > >
> > >  private:
> > >  	LIBCAMERA_DISABLE_COPY(File)
> > > diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
> > > index cca374a769cc..112420dab225 100644
> > > --- a/include/libcamera/base/meson.build
> > > +++ b/include/libcamera/base/meson.build
> > > @@ -11,13 +11,13 @@ libcamera_base_headers = files([
> > >      'event_dispatcher_poll.h',
> > >      'event_notifier.h',
> > >      'file.h',
> > > -    'file_descriptor.h',
> > >      'flags.h',
> > >      'log.h',
> > >      'message.h',
> > >      'object.h',
> > >      'private.h',
> > >      'semaphore.h',
> > > +    'shared_fd.h',
> > >      'signal.h',
> > >      'span.h',
> > >      'thread.h',
> > > diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h
> > > similarity index 55%
> > > rename from include/libcamera/base/file_descriptor.h
> > > rename to include/libcamera/base/shared_fd.h
> > > index 12a43f95d414..a786885ceb32 100644
> > > --- a/include/libcamera/base/file_descriptor.h
> > > +++ b/include/libcamera/base/shared_fd.h
> > > @@ -2,7 +2,7 @@
> > >  /*
> > >   * Copyright (C) 2019, Google Inc.
> > >   *
> > > - * file_descriptor.h - File descriptor wrapper
> > > + * shared_fd.h - File descriptor wrapper with shared ownership
> > >   */
> > >
> > >  #pragma once
> > > @@ -13,18 +13,18 @@ namespace libcamera {
> > >
> > >  class UniqueFD;
> > >
> > > -class FileDescriptor final
> > > +class SharedFD final
> > >  {
> > >  public:
> > > -	explicit FileDescriptor(const int &fd = -1);
> > > -	explicit FileDescriptor(int &&fd);
> > > -	explicit FileDescriptor(UniqueFD fd);
> > > -	FileDescriptor(const FileDescriptor &other);
> > > -	FileDescriptor(FileDescriptor &&other);
> > > -	~FileDescriptor();
> > > +	explicit SharedFD(const int &fd = -1);
> > > +	explicit SharedFD(int &&fd);
> > > +	explicit SharedFD(UniqueFD fd);
> > > +	SharedFD(const SharedFD &other);
> > > +	SharedFD(SharedFD &&other);
> > > +	~SharedFD();
> > >
> > > -	FileDescriptor &operator=(const FileDescriptor &other);
> > > -	FileDescriptor &operator=(FileDescriptor &&other);
> > > +	SharedFD &operator=(const SharedFD &other);
> > > +	SharedFD &operator=(SharedFD &&other);
> > >
> > >  	bool isValid() const { return fd_ != nullptr; }
> > >  	int fd() const { return fd_ ? fd_->fd() : -1; }
> > > diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h
> > > index 2fbea9c5be16..357bbe189551 100644
> > > --- a/include/libcamera/framebuffer.h
> > > +++ b/include/libcamera/framebuffer.h
> > > @@ -13,7 +13,7 @@
> > >  #include <vector>
> > >
> > >  #include <libcamera/base/class.h>
> > > -#include <libcamera/base/file_descriptor.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >  #include <libcamera/base/span.h>
> > >
> > >  namespace libcamera {
> > > @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible
> > >  public:
> > >  	struct Plane {
> > >  		static constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();
> > > -		FileDescriptor fd;
> > > +		SharedFD fd;
> > >  		unsigned int offset = kInvalidOffset;
> > >  		unsigned int length;
> > >  	};
> > > diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
> > > index c2f602d5b7de..a87449c9be48 100644
> > > --- a/include/libcamera/internal/ipa_data_serializer.h
> > > +++ b/include/libcamera/internal/ipa_data_serializer.h
> > > @@ -66,7 +66,7 @@ template<typename T>
> > >  class IPADataSerializer
> > >  {
> > >  public:
> > > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  	serialize(const T &data, ControlSerializer *cs = nullptr);
> > >
> > >  	static T deserialize(const std::vector<uint8_t> &data,
> > > @@ -76,12 +76,12 @@ public:
> > >  			     ControlSerializer *cs = nullptr);
> > >
> > >  	static T deserialize(const std::vector<uint8_t> &data,
> > > -			     const std::vector<FileDescriptor> &fds,
> > > +			     const std::vector<SharedFD> &fds,
> > >  			     ControlSerializer *cs = nullptr);
> > >  	static T deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  			     std::vector<uint8_t>::const_iterator dataEnd,
> > > -			     std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -			     std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +			     std::vector<SharedFD>::const_iterator fdsBegin,
> > > +			     std::vector<SharedFD>::const_iterator fdsEnd,
> > >  			     ControlSerializer *cs = nullptr);
> > >  };
> > >
> > > @@ -104,11 +104,11 @@ template<typename V>
> > >  class IPADataSerializer<std::vector<V>>
> > >  {
> > >  public:
> > > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
> > >  	{
> > >  		std::vector<uint8_t> dataVec;
> > > -		std::vector<FileDescriptor> fdsVec;
> > > +		std::vector<SharedFD> fdsVec;
> > >
> > >  		/* Serialize the length. */
> > >  		uint32_t vecLen = data.size();
> > > @@ -117,7 +117,7 @@ public:
> > >  		/* Serialize the members. */
> > >  		for (auto const &it : data) {
> > >  			std::vector<uint8_t> dvec;
> > > -			std::vector<FileDescriptor> fvec;
> > > +			std::vector<SharedFD> fvec;
> > >
> > >  			std::tie(dvec, fvec) =
> > >  				IPADataSerializer<V>::serialize(it, cs);
> > > @@ -141,11 +141,11 @@ public:
> > >  					  std::vector<uint8_t>::const_iterator dataEnd,
> > >  					  ControlSerializer *cs = nullptr)
> > >  	{
> > > -		std::vector<FileDescriptor> fds;
> > > +		std::vector<SharedFD> fds;
> > >  		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
> > >  	}
> > >
> > > -	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> > > +	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
> > >  					  ControlSerializer *cs = nullptr)
> > >  	{
> > >  		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> > > @@ -153,15 +153,15 @@ public:
> > >
> > >  	static std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  					  std::vector<uint8_t>::const_iterator dataEnd,
> > > -					  std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +					  std::vector<SharedFD>::const_iterator fdsBegin,
> > > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> > >  					  ControlSerializer *cs = nullptr)
> > >  	{
> > >  		uint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> > >  		std::vector<V> ret(vecLen);
> > >
> > >  		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> > > -		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> > > +		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
> > >  		for (uint32_t i = 0; i < vecLen; i++) {
> > >  			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
> > >  			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> > > @@ -201,11 +201,11 @@ template<typename K, typename V>
> > >  class IPADataSerializer<std::map<K, V>>
> > >  {
> > >  public:
> > > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
> > >  	{
> > >  		std::vector<uint8_t> dataVec;
> > > -		std::vector<FileDescriptor> fdsVec;
> > > +		std::vector<SharedFD> fdsVec;
> > >
> > >  		/* Serialize the length. */
> > >  		uint32_t mapLen = data.size();
> > > @@ -214,7 +214,7 @@ public:
> > >  		/* Serialize the members. */
> > >  		for (auto const &it : data) {
> > >  			std::vector<uint8_t> dvec;
> > > -			std::vector<FileDescriptor> fvec;
> > > +			std::vector<SharedFD> fvec;
> > >
> > >  			std::tie(dvec, fvec) =
> > >  				IPADataSerializer<K>::serialize(it.first, cs);
> > > @@ -247,11 +247,11 @@ public:
> > >  					  std::vector<uint8_t>::const_iterator dataEnd,
> > >  					  ControlSerializer *cs = nullptr)
> > >  	{
> > > -		std::vector<FileDescriptor> fds;
> > > +		std::vector<SharedFD> fds;
> > >  		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
> > >  	}
> > >
> > > -	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
> > > +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
> > >  					  ControlSerializer *cs = nullptr)
> > >  	{
> > >  		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> > > @@ -259,8 +259,8 @@ public:
> > >
> > >  	static std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  					  std::vector<uint8_t>::const_iterator dataEnd,
> > > -					  std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +					  std::vector<SharedFD>::const_iterator fdsBegin,
> > > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> > >  					  ControlSerializer *cs = nullptr)
> > >  	{
> > >  		std::map<K, V> ret;
> > > @@ -268,7 +268,7 @@ public:
> > >  		uint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> > >
> > >  		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
> > > -		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
> > > +		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
> > >  		for (uint32_t i = 0; i < mapLen; i++) {
> > >  			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
> > >  			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> > > diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h
> > > index 986f8d886fa6..ab5dd67c3813 100644
> > > --- a/include/libcamera/internal/ipc_pipe.h
> > > +++ b/include/libcamera/internal/ipc_pipe.h
> > > @@ -9,7 +9,7 @@
> > >
> > >  #include <vector>
> > >
> > > -#include <libcamera/base/file_descriptor.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >  #include <libcamera/base/signal.h>
> > >
> > >  #include "libcamera/internal/ipc_unixsocket.h"
> > > @@ -33,17 +33,17 @@ public:
> > >
> > >  	Header &header() { return header_; }
> > >  	std::vector<uint8_t> &data() { return data_; }
> > > -	std::vector<FileDescriptor> &fds() { return fds_; }
> > > +	std::vector<SharedFD> &fds() { return fds_; }
> > >
> > >  	const Header &header() const { return header_; }
> > >  	const std::vector<uint8_t> &data() const { return data_; }
> > > -	const std::vector<FileDescriptor> &fds() const { return fds_; }
> > > +	const std::vector<SharedFD> &fds() const { return fds_; }
> > >
> > >  private:
> > >  	Header header_;
> > >
> > >  	std::vector<uint8_t> data_;
> > > -	std::vector<FileDescriptor> fds_;
> > > +	std::vector<SharedFD> fds_;
> > >  };
> > >
> > >  class IPCPipe
> > > diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom
> > > index f7eff0c7ab8c..74f3339e56f2 100644
> > > --- a/include/libcamera/ipa/core.mojom
> > > +++ b/include/libcamera/ipa/core.mojom
> > > @@ -32,7 +32,7 @@ module libcamera;
> > >   *   - This attribute instructs the build system that a (de)serializer is
> > >   *     available for the type and there's no need to generate one
> > >   * - hasFd - struct fields or empty structs only
> > > - *   - Designate that this field or empty struct contains a FileDescriptor
> > > + *   - Designate that this field or empty struct contains a SharedFD
> > >   *
> > >   * Rules:
> > >   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is
> > > @@ -60,7 +60,7 @@ module libcamera;
> > >   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array
> > >   *       member
> > >   * - [skipHeader] and [skipSerdes] only work here in core.mojom
> > > - * - If a field in a struct has a FileDescriptor, but is not explicitly
> > > + * - If a field in a struct has a SharedFD, but is not explicitly
> > >   *   defined so in mojom, then the field must be marked with the [hasFd]
> > >   *   attribute
> > >   *
> > > @@ -71,7 +71,7 @@ module libcamera;
> > >   */
> > >  [skipSerdes, skipHeader] struct ControlInfoMap {};
> > >  [skipSerdes, skipHeader] struct ControlList {};
> > > -[skipSerdes, skipHeader] struct FileDescriptor {};
> > > +[skipSerdes, skipHeader] struct SharedFD {};
> > >
> > >  [skipHeader] struct Point {
> > >  	int32 x;
> > > diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
> > > index e453d46cb14f..acd3cafe6c91 100644
> > > --- a/include/libcamera/ipa/raspberrypi.mojom
> > > +++ b/include/libcamera/ipa/raspberrypi.mojom
> > > @@ -35,7 +35,7 @@ struct ISPConfig {
> > >
> > >  struct IPAConfig {
> > >  	uint32 transform;
> > > -	libcamera.FileDescriptor lsTableHandle;
> > > +	libcamera.SharedFD lsTableHandle;
> > >  };
> > >
> > >  struct StartConfig {
> > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
> > > index f2e0bdbdbbf6..1938b10509fa 100644
> > > --- a/src/android/camera_device.cpp
> > > +++ b/src/android/camera_device.cpp
> > > @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,
> > >
> > >  	std::vector<FrameBuffer::Plane> planes(buf.numPlanes());
> > >  	for (size_t i = 0; i < buf.numPlanes(); ++i) {
> > > -		FileDescriptor fd{ camera3buffer->data[i] };
> > > +		SharedFD fd{ camera3buffer->data[i] };
> > >  		if (!fd.isValid()) {
> > >  			LOG(HAL, Fatal) << "No valid fd";
> > >  			return nullptr;
> > > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
> > > index c6aec09046f7..aaf629eeb3fc 100644
> > > --- a/src/ipa/raspberrypi/raspberrypi.cpp
> > > +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> > > @@ -15,8 +15,8 @@
> > >
> > >  #include <linux/bcm2835-isp.h>
> > >
> > > -#include <libcamera/base/file_descriptor.h>
> > >  #include <libcamera/base/log.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >  #include <libcamera/base/span.h>
> > >
> > >  #include <libcamera/control_ids.h>
> > > @@ -164,7 +164,7 @@ private:
> > >  	bool processPending_;
> > >
> > >  	/* LS table allocation passed in from the pipeline handler. */
> > > -	FileDescriptor lsTableHandle_;
> > > +	SharedFD lsTableHandle_;
> > >  	void *lsTable_;
> > >
> > >  	/* Distinguish the first camera start from others. */
> > > diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp
> > > index 66c73c406198..3ca9839bb989 100644
> > > --- a/src/libcamera/base/file.cpp
> > > +++ b/src/libcamera/base/file.cpp
> > > @@ -14,8 +14,8 @@
> > >  #include <sys/types.h>
> > >  #include <unistd.h>
> > >
> > > -#include <libcamera/base/file_descriptor.h>
> > >  #include <libcamera/base/log.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >
> > >  /**
> > >   * \file base/file.h
> > > @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)
> > >  }
> > >
> > >  /**
> > > - * \brief Retrieve the inode of a FileDescriptor
> > > + * \brief Retrieve the inode of a SharedFD
> > >   *
> > >   * \return The file descriptor inode on success, or 0 on error
> > >   */
> > > -ino_t File::inode(const FileDescriptor &fd)
> > > +ino_t File::inode(const SharedFD &fd)
> > >  {
> > >  	if (!fd.isValid())
> > >  		return 0;
> > > diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp
> > > deleted file mode 100644
> > > index a83bf52c31e6..000000000000
> > > --- a/src/libcamera/base/file_descriptor.cpp
> > > +++ /dev/null
> > > @@ -1,266 +0,0 @@
> > > -/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > -/*
> > > - * Copyright (C) 2019, Google Inc.
> > > - *
> > > - * file_descriptor.cpp - File descriptor wrapper
> > > - */
> > > -
> > > -#include <libcamera/base/file_descriptor.h>
> > > -
> > > -#include <string.h>
> > > -#include <sys/types.h>
> > > -#include <unistd.h>
> > > -#include <utility>
> > > -
> > > -#include <libcamera/base/log.h>
> > > -#include <libcamera/base/unique_fd.h>
> > > -
> > > -/**
> > > - * \file base/file_descriptor.h
> > > - * \brief File descriptor wrapper
> > > - */
> > > -
> > > -namespace libcamera {
> > > -
> > > -LOG_DEFINE_CATEGORY(FileDescriptor)
> > > -
> > > -/**
> > > - * \class FileDescriptor
> > > - * \brief RAII-style wrapper for file descriptors
> > > - *
> > > - * The FileDescriptor class provides RAII-style lifetime management of file
> > > - * descriptors with an efficient mechanism for ownership sharing. At its core,
> > > - * an internal Descriptor object wraps a file descriptor (expressed as a signed
> > > - * integer) with an RAII-style interface. The Descriptor is then implicitly
> > > - * shared with all FileDescriptor instances constructed as copies.
> > > - *
> > > - * When constructed from a numerical file descriptor, the FileDescriptor
> > > - * instance either duplicates or takes over the file descriptor:
> > > - *
> > > - * - The FileDescriptor(const int &) constructor duplicates the numerical file
> > > - *   descriptor and wraps the duplicate in a Descriptor. The caller is
> > > - *   responsible for closing the original file descriptor, and the value
> > > - *   returned by fd() will be different from the value passed to the
> > > - *   constructor.
> > > - *
> > > - * - The FileDescriptor(int &&) constructor takes over the numerical file
> > > - *   descriptor and wraps it in a Descriptor. The caller shall not touch the
> > > - *   original file descriptor once the function returns, and the value returned
> > > - *   by fd() will be identical to the value passed to the constructor.
> > > - *
> > > - * The copy constructor and assignment operator create copies that share the
> > > - * Descriptor, while the move versions of those functions additionally make the
> > > - * other FileDescriptor invalid. When the last FileDescriptor that references a
> > > - * Descriptor is destroyed, the file descriptor is closed.
> > > - *
> > > - * The numerical file descriptor is available through the fd() function. All
> > > - * FileDescriptor instances created as copies of a FileDescriptor will report
> > > - * the same fd() value. Callers can perform operations on the fd(), but shall
> > > - * never close it manually.
> > > - */
> > > -
> > > -/**
> > > - * \brief Create a FileDescriptor copying a given \a fd
> > > - * \param[in] fd File descriptor
> > > - *
> > > - * Construct a FileDescriptor from a numerical file descriptor by duplicating
> > > - * the \a fd, and take ownership of the copy. The original \a fd is left
> > > - * untouched, and the caller is responsible for closing it when appropriate.
> > > - * The duplicated file descriptor will be closed automatically when all
> > > - * FileDescriptor instances that reference it are destroyed.
> > > - *
> > > - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> > > - * the fd() function will return -1.
> > > - */
> > > -FileDescriptor::FileDescriptor(const int &fd)
> > > -{
> > > -	if (fd < 0)
> > > -		return;
> > > -
> > > -	fd_ = std::make_shared<Descriptor>(fd, true);
> > > -	if (fd_->fd() < 0)
> > > -		fd_.reset();
> > > -}
> > > -
> > > -/**
> > > - * \brief Create a FileDescriptor taking ownership of a given \a fd
> > > - * \param[in] fd File descriptor
> > > - *
> > > - * Construct a FileDescriptor from a numerical file descriptor by taking
> > > - * ownership of the \a fd. The original \a fd is set to -1 and shall not be
> > > - * touched by the caller anymore. In particular, the caller shall not close the
> > > - * original \a fd manually. The duplicated file descriptor will be closed
> > > - * automatically when all FileDescriptor instances that reference it are
> > > - * destroyed.
> > > - *
> > > - * If the \a fd is negative, the FileDescriptor is constructed as invalid and
> > > - * the fd() function will return -1.
> > > - */
> > > -FileDescriptor::FileDescriptor(int &&fd)
> > > -{
> > > -	if (fd < 0)
> > > -		return;
> > > -
> > > -	fd_ = std::make_shared<Descriptor>(fd, false);
> > > -	/*
> > > -	 * The Descriptor constructor can't have failed here, as it took over
> > > -	 * the fd without duplicating it. Just set the original fd to -1 to
> > > -	 * implement move semantics.
> > > -	 */
> > > -	fd = -1;
> > > -}
> > > -
> > > -/**
> > > - * \brief Create a FileDescriptor taking ownership of a given UniqueFD \a fd
> > > - * \param[in] fd UniqueFD
> > > - *
> > > - * Construct a FileDescriptor from UniqueFD by taking ownership of the \a fd.
> > > - * The original \a fd becomes invalid.
> > > - */
> > > -FileDescriptor::FileDescriptor(UniqueFD fd)
> > > -	: FileDescriptor(fd.release())
> > > -{
> > > -}
> > > -
> > > -/**
> > > - * \brief Copy constructor, create a FileDescriptor from a copy of \a other
> > > - * \param[in] other The other FileDescriptor
> > > - *
> > > - * Copying a FileDescriptor implicitly shares ownership of the wrapped file
> > > - * descriptor. The original FileDescriptor is left untouched, and the caller is
> > > - * responsible for destroying it when appropriate. The wrapped file descriptor
> > > - * will be closed automatically when all FileDescriptor instances that
> > > - * reference it are destroyed.
> > > - */
> > > -FileDescriptor::FileDescriptor(const FileDescriptor &other)
> > > -	: fd_(other.fd_)
> > > -{
> > > -}
> > > -
> > > -/**
> > > - * \brief Move constructor, create a FileDescriptor by taking over \a other
> > > - * \param[in] other The other FileDescriptor
> > > - *
> > > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> > > - * by \a other to the new FileDescriptor. The \a other FileDescriptor is
> > > - * invalidated and its fd() function will return -1. The wrapped file descriptor
> > > - * will be closed automatically when all FileDescriptor instances that
> > > - * reference it are destroyed.
> > > - */
> > > -FileDescriptor::FileDescriptor(FileDescriptor &&other)
> > > -	: fd_(std::move(other.fd_))
> > > -{
> > > -}
> > > -
> > > -/**
> > > - * \brief Destroy the FileDescriptor instance
> > > - *
> > > - * Destroying a FileDescriptor instance releases its reference to the wrapped
> > > - * descriptor, if any. When the last instance that references a wrapped
> > > - * descriptor is destroyed, the file descriptor is automatically closed.
> > > - */
> > > -FileDescriptor::~FileDescriptor()
> > > -{
> > > -}
> > > -
> > > -/**
> > > - * \brief Copy assignment operator, replace the wrapped file descriptor with a
> > > - * copy of \a other
> > > - * \param[in] other The other FileDescriptor
> > > - *
> > > - * Copying a FileDescriptor creates a new reference to the wrapped file
> > > - * descriptor owner by \a other. If \a other is invalid, *this will also be
> > > - * invalid. The original FileDescriptor is left untouched, and the caller is
> > > - * responsible for destroying it when appropriate. The wrapped file descriptor
> > > - * will be closed automatically when all FileDescriptor instances that
> > > - * reference it are destroyed.
> > > - *
> > > - * \return A reference to this FileDescriptor
> > > - */
> > > -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)
> > > -{
> > > -	fd_ = other.fd_;
> > > -
> > > -	return *this;
> > > -}
> > > -
> > > -/**
> > > - * \brief Move assignment operator, replace the wrapped file descriptor by
> > > - * taking over \a other
> > > - * \param[in] other The other FileDescriptor
> > > - *
> > > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
> > > - * by \a other to the new FileDescriptor. If \a other is invalid, *this will
> > > - * also be invalid. The \a other FileDescriptor is invalidated and its fd()
> > > - * function will return -1. The wrapped file descriptor will be closed
> > > - * automatically when all FileDescriptor instances that reference it are
> > > - * destroyed.
> > > - *
> > > - * \return A reference to this FileDescriptor
> > > - */
> > > -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)
> > > -{
> > > -	fd_ = std::move(other.fd_);
> > > -
> > > -	return *this;
> > > -}
> > > -
> > > -/**
> > > - * \fn FileDescriptor::isValid()
> > > - * \brief Check if the FileDescriptor instance is valid
> > > - * \return True if the FileDescriptor is valid, false otherwise
> > > - */
> > > -
> > > -/**
> > > - * \fn FileDescriptor::fd()
> > > - * \brief Retrieve the numerical file descriptor
> > > - * \return The numerical file descriptor, which may be -1 if the FileDescriptor
> > > - * instance is invalid
> > > - */
> > > -
> > > -/**
> > > - * \brief Duplicate a FileDescriptor
> > > - *
> > > - * Duplicating a FileDescriptor creates a duplicate of the wrapped file
> > > - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function
> > > - * of the original and the get() function of the duplicate will return different
> > > - * values. The duplicate instance will not be affected by destruction of the
> > > - * original instance or its copies.
> > > - *
> > > - * \return A UniqueFD owning a duplicate of the original file descriptor
> > > - */
> > > -UniqueFD FileDescriptor::dup() const
> > > -{
> > > -	int dupFd = ::dup(fd());
> > > -	if (dupFd == -1) {
> > > -		int ret = -errno;
> > > -		LOG(FileDescriptor, Error)
> > > -			<< "Failed to dup() fd: " << strerror(-ret);
> > > -	}
> > > -
> > > -	return UniqueFD(dupFd);
> > > -}
> > > -
> > > -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)
> > > -{
> > > -	if (!duplicate) {
> > > -		fd_ = fd;
> > > -		return;
> > > -	}
> > > -
> > > -	/* Failing to dup() a fd should not happen and is fatal. */
> > > -	fd_ = ::dup(fd);
> > > -	if (fd_ == -1) {
> > > -		int ret = -errno;
> > > -		LOG(FileDescriptor, Fatal)
> > > -			<< "Failed to dup() fd: " << strerror(-ret);
> > > -	}
> > > -}
> > > -
> > > -FileDescriptor::Descriptor::~Descriptor()
> > > -{
> > > -	if (fd_ != -1)
> > > -		close(fd_);
> > > -}
> > > -
> > > -} /* namespace libcamera */
> > > diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build
> > > index b0d85bc19245..ccb746c27466 100644
> > > --- a/src/libcamera/base/meson.build
> > > +++ b/src/libcamera/base/meson.build
> > > @@ -8,12 +8,12 @@ libcamera_base_sources = files([
> > >      'event_dispatcher_poll.cpp',
> > >      'event_notifier.cpp',
> > >      'file.cpp',
> > > -    'file_descriptor.cpp',
> > >      'flags.cpp',
> > >      'log.cpp',
> > >      'message.cpp',
> > >      'object.cpp',
> > >      'semaphore.cpp',
> > > +    'shared_fd.cpp',
> > >      'signal.cpp',
> > >      'thread.cpp',
> > >      'timer.cpp',
> > > diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp
> > > new file mode 100644
> > > index 000000000000..05b6892f7e19
> > > --- /dev/null
> > > +++ b/src/libcamera/base/shared_fd.cpp
> > > @@ -0,0 +1,262 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2019, Google Inc.
> > > + *
> > > + * shared_fd.cpp - File descriptor wrapper with shared ownership
> > > + */
> > > +
> > > +#include <libcamera/base/shared_fd.h>
> > > +
> > > +#include <string.h>
> > > +#include <sys/types.h>
> > > +#include <unistd.h>
> > > +#include <utility>
> > > +
> > > +#include <libcamera/base/log.h>
> > > +#include <libcamera/base/unique_fd.h>
> > > +
> > > +/**
> > > + * \file base/shared_fd.h
> > > + * \brief File descriptor wrapper
> > > + */
> > > +
> > > +namespace libcamera {
> > > +
> > > +LOG_DEFINE_CATEGORY(SharedFD)
> > > +
> > > +/**
> > > + * \class SharedFD
> > > + * \brief RAII-style wrapper for file descriptors
> > > + *
> > > + * The SharedFD class provides RAII-style lifetime management of file
> > > + * descriptors with an efficient mechanism for ownership sharing. At its core,
> > > + * an internal Descriptor object wraps a file descriptor (expressed as a signed
> > > + * integer) with an RAII-style interface. The Descriptor is then implicitly
> > > + * shared with all SharedFD instances constructed as copies.
> > > + *
> > > + * When constructed from a numerical file descriptor, the SharedFD instance
> > > + * either duplicates or takes over the file descriptor:
> > > + *
> > > + * - The SharedFD(const int &) constructor duplicates the numerical file
> > > + *   descriptor and wraps the duplicate in a Descriptor. The caller is
> > > + *   responsible for closing the original file descriptor, and the value
> > > + *   returned by fd() will be different from the value passed to the
> > > + *   constructor.
> > > + *
> > > + * - The SharedFD(int &&) constructor takes over the numerical file descriptor
> > > + *   and wraps it in a Descriptor. The caller shall not touch the original file
> > > + *   descriptor once the function returns, and the value returned by fd() will
> > > + *   be identical to the value passed to the constructor.
> > > + *
> > > + * The copy constructor and assignment operator create copies that share the
> > > + * Descriptor, while the move versions of those functions additionally make the
> > > + * other SharedFD invalid. When the last SharedFD that references a Descriptor
> > > + * is destroyed, the file descriptor is closed.
> > > + *
> > > + * The numerical file descriptor is available through the fd() function. All
> > > + * SharedFD instances created as copies of a SharedFD will report the same fd()
> > > + * value. Callers can perform operations on the fd(), but shall never close it
> > > + * manually.
> > > + */
> > > +
> > > +/**
> > > + * \brief Create a SharedFD copying a given \a fd
> > > + * \param[in] fd File descriptor
> > > + *
> > > + * Construct a SharedFD from a numerical file descriptor by duplicating the
> > > + * \a fd, and take ownership of the copy. The original \a fd is left untouched,
> > > + * and the caller is responsible for closing it when appropriate. The duplicated
> > > + * file descriptor will be closed automatically when all SharedFD instances that
> > > + * reference it are destroyed.
> > > + *
> > > + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> > > + * function will return -1.
> > > + */
> > > +SharedFD::SharedFD(const int &fd)
> > > +{
> > > +	if (fd < 0)
> > > +		return;
> > > +
> > > +	fd_ = std::make_shared<Descriptor>(fd, true);
> > > +	if (fd_->fd() < 0)
> > > +		fd_.reset();
> > > +}
> > > +
> > > +/**
> > > + * \brief Create a SharedFD taking ownership of a given \a fd
> > > + * \param[in] fd File descriptor
> > > + *
> > > + * Construct a SharedFD from a numerical file descriptor by taking ownership of
> > > + * the \a fd. The original \a fd is set to -1 and shall not be touched by the
> > > + * caller anymore. In particular, the caller shall not close the original \a fd
> > > + * manually. The duplicated file descriptor will be closed automatically when
> > > + * all SharedFD instances that reference it are destroyed.
> > > + *
> > > + * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
> > > + * function will return -1.
> > > + */
> > > +SharedFD::SharedFD(int &&fd)
> > > +{
> > > +	if (fd < 0)
> > > +		return;
> > > +
> > > +	fd_ = std::make_shared<Descriptor>(fd, false);
> > > +	/*
> > > +	 * The Descriptor constructor can't have failed here, as it took over
> > > +	 * the fd without duplicating it. Just set the original fd to -1 to
> > > +	 * implement move semantics.
> > > +	 */
> > > +	fd = -1;
> > > +}
> > > +
> > > +/**
> > > + * \brief Create a SharedFD taking ownership of a given UniqueFD \a fd
> > > + * \param[in] fd UniqueFD
> > > + *
> > > + * Construct a SharedFD from UniqueFD by taking ownership of the \a fd. The
> > > + * original \a fd becomes invalid.
> > > + */
> > > +SharedFD::SharedFD(UniqueFD fd)
> > > +	: SharedFD(fd.release())
> > > +{
> > > +}
> > > +
> > > +/**
> > > + * \brief Copy constructor, create a SharedFD from a copy of \a other
> > > + * \param[in] other The other SharedFD
> > > + *
> > > + * Copying a SharedFD implicitly shares ownership of the wrapped file
> > > + * descriptor. The original SharedFD is left untouched, and the caller is
> > > + * responsible for destroying it when appropriate. The wrapped file descriptor
> > > + * will be closed automatically when all SharedFD instances that reference it
> > > + * are destroyed.
> > > + */
> > > +SharedFD::SharedFD(const SharedFD &other)
> > > +	: fd_(other.fd_)
> > > +{
> > > +}
> > > +
> > > +/**
> > > + * \brief Move constructor, create a SharedFD by taking over \a other
> > > + * \param[in] other The other SharedFD
> > > + *
> > > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> > > + * \a other to the new SharedFD. The \a other SharedFD is invalidated and its
> > > + * fd() function will return -1. The wrapped file descriptor will be closed
> > > + * automatically when all SharedFD instances that reference it are destroyed.
> > > + */
> > > +SharedFD::SharedFD(SharedFD &&other)
> > > +	: fd_(std::move(other.fd_))
> > > +{
> > > +}
> > > +
> > > +/**
> > > + * \brief Destroy the SharedFD instance
> > > + *
> > > + * Destroying a SharedFD instance releases its reference to the wrapped
> > > + * descriptor, if any. When the last instance that references a wrapped
> > > + * descriptor is destroyed, the file descriptor is automatically closed.
> > > + */
> > > +SharedFD::~SharedFD()
> > > +{
> > > +}
> > > +
> > > +/**
> > > + * \brief Copy assignment operator, replace the wrapped file descriptor with a
> > > + * copy of \a other
> > > + * \param[in] other The other SharedFD
> > > + *
> > > + * Copying a SharedFD creates a new reference to the wrapped file descriptor
> > > + * owner by \a other. If \a other is invalid, *this will also be invalid. The
> > > + * original SharedFD is left untouched, and the caller is responsible for
> > > + * destroying it when appropriate. The wrapped file descriptor will be closed
> > > + * automatically when all SharedFD instances that reference it are destroyed.
> > > + *
> > > + * \return A reference to this SharedFD
> > > + */
> > > +SharedFD &SharedFD::operator=(const SharedFD &other)
> > > +{
> > > +	fd_ = other.fd_;
> > > +
> > > +	return *this;
> > > +}
> > > +
> > > +/**
> > > + * \brief Move assignment operator, replace the wrapped file descriptor by
> > > + * taking over \a other
> > > + * \param[in] other The other SharedFD
> > > + *
> > > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by
> > > + * \a other to the new SharedFD. If \a other is invalid, *this will also be
> > > + * invalid. The \a other SharedFD is invalidated and its fd() function will
> > > + * return -1. The wrapped file descriptor will be closed automatically when
> > > + * all SharedFD instances that reference it are destroyed.
> > > + *
> > > + * \return A reference to this SharedFD
> > > + */
> > > +SharedFD &SharedFD::operator=(SharedFD &&other)
> > > +{
> > > +	fd_ = std::move(other.fd_);
> > > +
> > > +	return *this;
> > > +}
> > > +
> > > +/**
> > > + * \fn SharedFD::isValid()
> > > + * \brief Check if the SharedFD instance is valid
> > > + * \return True if the SharedFD is valid, false otherwise
> > > + */
> > > +
> > > +/**
> > > + * \fn SharedFD::fd()
> > > + * \brief Retrieve the numerical file descriptor
> > > + * \return The numerical file descriptor, which may be -1 if the SharedFD
> > > + * instance is invalid
> > > + */
> > > +
> > > +/**
> > > + * \brief Duplicate a SharedFD
> > > + *
> > > + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and
> > > + * returns a UniqueFD that owns the duplicate. The fd() function of the original
> > > + * and the get() function of the duplicate will return different values. The
> > > + * duplicate instance will not be affected by destruction of the original
> > > + * instance or its copies.
> > > + *
> > > + * \return A UniqueFD owning a duplicate of the original file descriptor
> > > + */
> > > +UniqueFD SharedFD::dup() const
> > > +{
> > > +	int dupFd = ::dup(fd());
> > > +	if (dupFd == -1) {
> > > +		int ret = -errno;
> > > +		LOG(SharedFD, Error)
> > > +			<< "Failed to dup() fd: " << strerror(-ret);
> > > +	}
> > > +
> > > +	return UniqueFD(dupFd);
> > > +}
> > > +
> > > +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)
> > > +{
> > > +	if (!duplicate) {
> > > +		fd_ = fd;
> > > +		return;
> > > +	}
> > > +
> > > +	/* Failing to dup() a fd should not happen and is fatal. */
> > > +	fd_ = ::dup(fd);
> > > +	if (fd_ == -1) {
> > > +		int ret = -errno;
> > > +		LOG(SharedFD, Fatal)
> > > +			<< "Failed to dup() fd: " << strerror(-ret);
> > > +	}
> > > +}
> > > +
> > > +SharedFD::Descriptor::~Descriptor()
> > > +{
> > > +	if (fd_ != -1)
> > > +		close(fd_);
> > > +}
> > > +
> > > +} /* namespace libcamera */
> > > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp
> > > index f5bcf107d7aa..0a5bf7fdbeb7 100644
> > > --- a/src/libcamera/framebuffer.cpp
> > > +++ b/src/libcamera/framebuffer.cpp
> > > @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()
> > >   * offset and length.
> > >   *
> > >   * To support DMA access, planes are associated with dmabuf objects represented
> > > - * by FileDescriptor handles. The Plane class doesn't handle mapping of the
> > > - * memory to the CPU, but applications and IPAs may use the dmabuf file
> > > - * descriptors to map the plane memory with mmap() and access its contents.
> > > + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to
> > > + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map
> > > + * the plane memory with mmap() and access its contents.
> > >   *
> > >   * \todo Specify how an application shall decide whether to use a single or
> > >   * multiple dmabufs, based on the camera requirements.
> > > diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
> > > index 82ec9b20a411..0a259305afa2 100644
> > > --- a/src/libcamera/ipa_data_serializer.cpp
> > > +++ b/src/libcamera/ipa_data_serializer.cpp
> > > @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)
> > >   *
> > >   * \todo Harden the vector and map deserializer
> > >   *
> > > - * \todo For FileDescriptors, instead of storing a validity flag, store an
> > > + * \todo For SharedFDs, instead of storing a validity flag, store an
> > >   * index into the fd array. This will allow us to use views instead of copying.
> > >   */
> > >
> > > @@ -112,7 +112,7 @@ namespace {
> > >   * \param[in] cs ControlSerializer
> > >   *
> > >   * This version of deserialize() can be used if the object type \a T and its
> > > - * members don't have any FileDescriptor.
> > > + * members don't have any SharedFD.
> > >   *
> > >   * \a cs is only necessary if the object type \a T or its members contain
> > >   * ControlList or ControlInfoMap.
> > > @@ -132,7 +132,7 @@ namespace {
> > >   * \param[in] cs ControlSerializer
> > >   *
> > >   * This version of deserialize() can be used if the object type \a T and its
> > > - * members don't have any FileDescriptor.
> > > + * members don't have any SharedFD.
> > >   *
> > >   * \a cs is only necessary if the object type \a T or its members contain
> > >   * ControlList or ControlInfoMap.
> > > @@ -143,7 +143,7 @@ namespace {
> > >  /**
> > >   * \fn template<typename T> IPADataSerializer<T>::deserialize(
> > >   * 	const std::vector<uint8_t> &data,
> > > - * 	const std::vector<FileDescriptor> &fds,
> > > + * 	const std::vector<SharedFD> &fds,
> > >   * 	ControlSerializer *cs = nullptr)
> > >   * \brief Deserialize byte vector and fd vector into an object
> > >   * \tparam T Type of object to deserialize to
> > > @@ -152,7 +152,7 @@ namespace {
> > >   * \param[in] cs ControlSerializer
> > >   *
> > >   * This version of deserialize() (or the iterator version) must be used if
> > > - * the object type \a T or its members contain FileDescriptor.
> > > + * the object type \a T or its members contain SharedFD.
> > >   *
> > >   * \a cs is only necessary if the object type \a T or its members contain
> > >   * ControlList or ControlInfoMap.
> > > @@ -164,8 +164,8 @@ namespace {
> > >   * \fn template<typename T> IPADataSerializer::deserialize(
> > >   * 	std::vector<uint8_t>::const_iterator dataBegin,
> > >   * 	std::vector<uint8_t>::const_iterator dataEnd,
> > > - * 	std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > - * 	std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > + * 	std::vector<SharedFD>::const_iterator fdsBegin,
> > > + * 	std::vector<SharedFD>::const_iterator fdsEnd,
> > >   * 	ControlSerializer *cs = nullptr)
> > >   * \brief Deserialize byte vector and fd vector into an object
> > >   * \tparam T Type of object to deserialize to
> > > @@ -176,7 +176,7 @@ namespace {
> > >   * \param[in] cs ControlSerializer
> > >   *
> > >   * This version of deserialize() (or the vector version) must be used if
> > > - * the object type \a T or its members contain FileDescriptor.
> > > + * the object type \a T or its members contain SharedFD.
> > >   *
> > >   * \a cs is only necessary if the object type \a T or its members contain
> > >   * ControlList or ControlInfoMap.
> > > @@ -189,7 +189,7 @@ namespace {
> > >  #define DEFINE_POD_SERIALIZER(type)					\
> > >  									\
> > >  template<>								\
> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>		\
> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>		\
> > >  IPADataSerializer<type>::serialize(const type &data,			\
> > >  				  [[maybe_unused]] ControlSerializer *cs) \
> > >  {									\
> > > @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> > >  									\
> > >  template<>								\
> > >  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> > > -					  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \
> > > +					  [[maybe_unused]] const std::vector<SharedFD> &fds, \
> > >  					  ControlSerializer *cs)	\
> > >  {									\
> > >  	return deserialize(data.cbegin(), data.end(), cs);		\
> > > @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
> > >  template<>								\
> > >  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \
> > >  					  std::vector<uint8_t>::const_iterator dataEnd, \
> > > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \
> > > -					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \
> > > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \
> > > +					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \
> > >  					  ControlSerializer *cs)	\
> > >  {									\
> > >  	return deserialize(dataBegin, dataEnd, cs);			\
> > > @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)
> > >   * function parameter serdes).
> > >   */
> > >  template<>
> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  IPADataSerializer<std::string>::serialize(const std::string &data,
> > >  					  [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > > @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
> > >  template<>
> > >  std::string
> > >  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,
> > > -					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> > > +					    [[maybe_unused]] const std::vector<SharedFD> &fds,
> > >  					    [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > >  	return { data.cbegin(), data.cend() };
> > > @@ -288,8 +288,8 @@ template<>
> > >  std::string
> > >  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  					    std::vector<uint8_t>::const_iterator dataEnd,
> > > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> > >  					    [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > >  	return { dataBegin, dataEnd };
> > > @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
> > >   * be used. The serialized ControlInfoMap will have zero length.
> > >   */
> > >  template<>
> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)
> > >  {
> > >  	if (!cs)
> > > @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
> > >  template<>
> > >  ControlList
> > >  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
> > > -					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> > > +					    [[maybe_unused]] const std::vector<SharedFD> &fds,
> > >  					    ControlSerializer *cs)
> > >  {
> > >  	return deserialize(data.cbegin(), data.end(), cs);
> > > @@ -417,8 +417,8 @@ template<>
> > >  ControlList
> > >  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  					    std::vector<uint8_t>::const_iterator dataEnd,
> > > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > > +					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> > >  					    ControlSerializer *cs)
> > >  {
> > >  	return deserialize(dataBegin, dataEnd, cs);
> > > @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator
> > >   * X bytes - Serialized ControlInfoMap (using ControlSerializer)
> > >   */
> > >  template<>
> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,
> > >  					     ControlSerializer *cs)
> > >  {
> > > @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
> > >  template<>
> > >  ControlInfoMap
> > >  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
> > > -					       [[maybe_unused]] const std::vector<FileDescriptor> &fds,
> > > +					       [[maybe_unused]] const std::vector<SharedFD> &fds,
> > >  					       ControlSerializer *cs)
> > >  {
> > >  	return deserialize(data.cbegin(), data.end(), cs);
> > > @@ -503,30 +503,30 @@ template<>
> > >  ControlInfoMap
> > >  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  					       std::vector<uint8_t>::const_iterator dataEnd,
> > > -					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > > +					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> > >  					       ControlSerializer *cs)
> > >  {
> > >  	return deserialize(dataBegin, dataEnd, cs);
> > >  }
> > >
> > >  /*
> > > - * FileDescriptors are serialized into four bytes that tells if the
> > > - * FileDescriptor is valid or not. If it is valid, then for serialization
> > > - * the fd will be written to the fd vector, or for deserialization the
> > > - * fd vector const_iterator will be valid.
> > > + * SharedFD instances are serialized into four bytes that tells if the SharedFD
> > > + * is valid or not. If it is valid, then for serialization the fd will be
> > > + * written to the fd vector, or for deserialization the fd vector const_iterator
> > > + * will be valid.
> > >   *
> > >   * This validity is necessary so that we don't send -1 fd over sendmsg(). It
> > >   * also allows us to simply send the entire fd vector into the deserializer
> > >   * and it will be recursively consumed as necessary.
> > >   */
> > >  template<>
> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
> > > -					     [[maybe_unused]] ControlSerializer *cs)
> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > > +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,
> > > +				       [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > >  	std::vector<uint8_t> dataVec;
> > > -	std::vector<FileDescriptor> fdVec;
> > > +	std::vector<SharedFD> fdVec;
> > >
> > >  	/*
> > >  	 * Store as uint32_t to prepare for conversion from validity flag
> > > @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
> > >  }
> > >
> > >  template<>
> > > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> > > -							      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> > > -							      std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -							      std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > -							      [[maybe_unused]] ControlSerializer *cs)
> > > +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
> > > +						  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
> > > +						  std::vector<SharedFD>::const_iterator fdsBegin,
> > > +						  std::vector<SharedFD>::const_iterator fdsEnd,
> > > +						  [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > >  	ASSERT(std::distance(dataBegin, dataEnd) >= 4);
> > >
> > > @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s
> > >
> > >  	ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));
> > >
> > > -	return valid ? *fdsBegin : FileDescriptor();
> > > +	return valid ? *fdsBegin : SharedFD();
> > >  }
> > >
> > >  template<>
> > > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,
> > > -							      const std::vector<FileDescriptor> &fds,
> > > -							      [[maybe_unused]] ControlSerializer *cs)
> > > +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,
> > > +						  const std::vector<SharedFD> &fds,
> > > +						  [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > >  	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());
> > >  }
> > > @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<
> > >  /*
> > >   * FrameBuffer::Plane is serialized as:
> > >   *
> > > - * 4 byte  - FileDescriptor
> > > + * 4 byte  - SharedFD
> > >   * 4 bytes - uint32_t Offset
> > >   * 4 bytes - uint32_t Length
> > >   */
> > >  template<>
> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,
> > >  						 [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > >  	std::vector<uint8_t> dataVec;
> > > -	std::vector<FileDescriptor> fdsVec;
> > > +	std::vector<SharedFD> fdsVec;
> > >
> > >  	std::vector<uint8_t> fdBuf;
> > > -	std::vector<FileDescriptor> fdFds;
> > > +	std::vector<SharedFD> fdFds;
> > >  	std::tie(fdBuf, fdFds) =
> > > -		IPADataSerializer<FileDescriptor>::serialize(data.fd);
> > > +		IPADataSerializer<SharedFD>::serialize(data.fd);
> > >  	dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());
> > >  	fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());
> > >
> > > @@ -597,13 +597,13 @@ template<>
> > >  FrameBuffer::Plane
> > >  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  						   std::vector<uint8_t>::const_iterator dataEnd,
> > > -						   std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -						   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +						   std::vector<SharedFD>::const_iterator fdsBegin,
> > > +						   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> > >  						   [[maybe_unused]] ControlSerializer *cs)
> > >  {
> > >  	FrameBuffer::Plane ret;
> > >
> > > -	ret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,
> > > +	ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,
> > >  								fdsBegin, fdsBegin + 1);
> > >  	ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);
> > >  	ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);
> > > @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i
> > >  template<>
> > >  FrameBuffer::Plane
> > >  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,
> > > -						   const std::vector<FileDescriptor> &fds,
> > > +						   const std::vector<SharedFD> &fds,
> > >  						   ControlSerializer *cs)
> > >  {
> > >  	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
> > > diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp
> > > index ad870fd4137f..3b47032de0a2 100644
> > > --- a/src/libcamera/ipc_pipe.cpp
> > > +++ b/src/libcamera/ipc_pipe.cpp
> > > @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)
> > >  	data_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),
> > >  				     payload.data.end());
> > >  	for (int32_t &fd : payload.fds)
> > > -		fds_.push_back(FileDescriptor(std::move(fd)));
> > > +		fds_.push_back(SharedFD(std::move(fd)));
> > >  }
> > >
> > >  /**
> > > @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const
> > >  		       data_.data(), data_.size());
> > >  	}
> > >
> > > -	for (const FileDescriptor &fd : fds_)
> > > +	for (const SharedFD &fd : fds_)
> > >  		payload.fds.push_back(fd.fd());
> > >
> > >  	return payload;
> > > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > index ffa51a0c65ca..ea8243912a29 100644
> > > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > @@ -12,7 +12,7 @@
> > >  #include <queue>
> > >  #include <unordered_set>
> > >
> > > -#include <libcamera/base/file_descriptor.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >
> > >  #include <libcamera/camera.h>
> > >  #include <libcamera/control_ids.h>
> > > @@ -228,7 +228,7 @@ public:
> > >
> > >  	/* DMAHEAP allocation helper. */
> > >  	RPi::DmaHeap dmaHeap_;
> > > -	FileDescriptor lsTable_;
> > > +	SharedFD lsTable_;
> > >
> > >  	std::unique_ptr<DelayedControls> delayedCtrls_;
> > >  	bool sensorMetadata_;
> > > @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)
> > >  		if (!fd.isValid())
> > >  			return -ENOMEM;
> > >
> > > -		lsTable_ = FileDescriptor(std::move(fd));
> > > +		lsTable_ = SharedFD(std::move(fd));
> > >
> > >  		/* Allow the IPA to mmap the LS table via the file descriptor. */
> > >  		/*
> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> > > index 3966483a365f..97d431071def 100644
> > > --- a/src/libcamera/v4l2_videodevice.cpp
> > > +++ b/src/libcamera/v4l2_videodevice.cpp
> > > @@ -22,8 +22,8 @@
> > >  #include <linux/version.h>
> > >
> > >  #include <libcamera/base/event_notifier.h>
> > > -#include <libcamera/base/file_descriptor.h>
> > >  #include <libcamera/base/log.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >  #include <libcamera/base/unique_fd.h>
> > >  #include <libcamera/base/utils.h>
> > >
> > > @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
> > >  			return nullptr;
> > >
> > >  		FrameBuffer::Plane plane;
> > > -		plane.fd = FileDescriptor(std::move(fd));
> > > +		plane.fd = SharedFD(std::move(fd));
> > >  		/*
> > >  		 * V4L2 API doesn't provide dmabuf offset information of plane.
> > >  		 * Set 0 as a placeholder offset.
> > > @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
> > >  		ASSERT(numPlanes == 1u);
> > >
> > >  		planes.resize(formatInfo_->numPlanes());
> > > -		const FileDescriptor &fd = planes[0].fd;
> > > +		const SharedFD &fd = planes[0].fd;
> > >  		size_t offset = 0;
> > >
> > >  		for (auto [i, plane] : utils::enumerate(planes)) {
> > > diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
> > > index 9817fd393d59..586347829845 100644
> > > --- a/src/v4l2/v4l2_camera.h
> > > +++ b/src/v4l2/v4l2_camera.h
> > > @@ -11,8 +11,8 @@
> > >  #include <mutex>
> > >  #include <utility>
> > >
> > > -#include <libcamera/base/file_descriptor.h>
> > >  #include <libcamera/base/semaphore.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >
> > >  #include <libcamera/camera.h>
> > >  #include <libcamera/framebuffer.h>
> > > diff --git a/test/meson.build b/test/meson.build
> > > index 42dfbc1f8ee9..daaa3862cdd6 100644
> > > --- a/test/meson.build
> > > +++ b/test/meson.build
> > > @@ -40,7 +40,6 @@ internal_tests = [
> > >      ['event-dispatcher',                'event-dispatcher.cpp'],
> > >      ['event-thread',                    'event-thread.cpp'],
> > >      ['file',                            'file.cpp'],
> > > -    ['file-descriptor',                 'file-descriptor.cpp'],
> > >      ['flags',                           'flags.cpp'],
> > >      ['hotplug-cameras',                 'hotplug-cameras.cpp'],
> > >      ['mapped-buffer',                   'mapped-buffer.cpp'],
> > > @@ -49,6 +48,7 @@ internal_tests = [
> > >      ['object-delete',                   'object-delete.cpp'],
> > >      ['object-invoke',                   'object-invoke.cpp'],
> > >      ['pixel-format',                    'pixel-format.cpp'],
> > > +    ['shared-fd',                       'shared-fd.cpp'],
> > >      ['signal-threads',                  'signal-threads.cpp'],
> > >      ['threads',                         'threads.cpp'],
> > >      ['timer',                           'timer.cpp'],
> > > diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
> > > index 5fcdcb8eae92..d2050a868b38 100644
> > > --- a/test/serialization/ipa_data_serializer_test.cpp
> > > +++ b/test/serialization/ipa_data_serializer_test.cpp
> > > @@ -53,7 +53,7 @@ template<typename T>
> > >  int testPodSerdes(T in)
> > >  {
> > >  	std::vector<uint8_t> buf;
> > > -	std::vector<FileDescriptor> fds;
> > > +	std::vector<SharedFD> fds;
> > >
> > >  	std::tie(buf, fds) = IPADataSerializer<T>::serialize(in);
> > >  	T out = IPADataSerializer<T>::deserialize(buf, fds);
> > > @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,
> > >  		     ControlSerializer *cs = nullptr)
> > >  {
> > >  	std::vector<uint8_t> buf;
> > > -	std::vector<FileDescriptor> fds;
> > > +	std::vector<SharedFD> fds;
> > >
> > >  	std::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);
> > >  	std::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);
> > > @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,
> > >  		  ControlSerializer *cs = nullptr)
> > >  {
> > >  	std::vector<uint8_t> buf;
> > > -	std::vector<FileDescriptor> fds;
> > > +	std::vector<SharedFD> fds;
> > >
> > >  	std::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);
> > >  	std::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);
> > > @@ -198,7 +198,7 @@ private:
> > >  		ControlSerializer cs(ControlSerializer::Role::Proxy);
> > >
> > >  		/*
> > > -		 * We don't test FileDescriptor serdes because it dup()s, so we
> > > +		 * We don't test SharedFD serdes because it dup()s, so we
> > >  		 * can't check for equality.
> > >  		 */
> > >  		std::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };
> > > @@ -219,7 +219,7 @@ private:
> > >  		};
> > >
> > >  		std::vector<uint8_t> buf;
> > > -		std::vector<FileDescriptor> fds;
> > > +		std::vector<SharedFD> fds;
> > >
> > >  		if (testVectorSerdes(vecUint8) != TestPass)
> > >  			return TestFail;
> > > @@ -291,7 +291,7 @@ private:
> > >  			{ { "a", { 1, 2, 3 } }, { "b", { 4, 5, 6 } }, { "c", { 7, 8, 9 } } };
> > >
> > >  		std::vector<uint8_t> buf;
> > > -		std::vector<FileDescriptor> fds;
> > > +		std::vector<SharedFD> fds;
> > >
> > >  		if (testMapSerdes(mapUintStr) != TestPass)
> > >  			return TestFail;
> > > @@ -359,7 +359,7 @@ private:
> > >  		std::string strEmpty = "";
> > >
> > >  		std::vector<uint8_t> buf;
> > > -		std::vector<FileDescriptor> fds;
> > > +		std::vector<SharedFD> fds;
> > >
> > >  		if (testPodSerdes(u32min) != TestPass)
> > >  			return TestFail;
> > > diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp
> > > similarity index 80%
> > > rename from test/file-descriptor.cpp
> > > rename to test/shared-fd.cpp
> > > index 76badc4c5fad..60e5d0aaa395 100644
> > > --- a/test/file-descriptor.cpp
> > > +++ b/test/shared-fd.cpp
> > > @@ -2,7 +2,7 @@
> > >  /*
> > >   * Copyright (C) 2019, Google Inc.
> > >   *
> > > - * file_descriptor.cpp - FileDescriptor test
> > > + * shared_fd.cpp - SharedFD test
> > >   */
> > >
> > >  #include <fcntl.h>
> > > @@ -11,7 +11,7 @@
> > >  #include <sys/types.h>
> > >  #include <unistd.h>
> > >
> > > -#include <libcamera/base/file_descriptor.h>
> > > +#include <libcamera/base/shared_fd.h>
> > >  #include <libcamera/base/utils.h>
> > >
> > >  #include "test.h"
> > > @@ -19,7 +19,7 @@
> > >  using namespace libcamera;
> > >  using namespace std;
> > >
> > > -class FileDescriptorTest : public Test
> > > +class SharedFDTest : public Test
> > >  {
> > >  protected:
> > >  	int init()
> > > @@ -43,8 +43,8 @@ protected:
> > >
> > >  	int run()
> > >  	{
> > > -		/* Test creating empty FileDescriptor. */
> > > -		desc1_ = new FileDescriptor();
> > > +		/* Test creating empty SharedFD. */
> > > +		desc1_ = new SharedFD();
> > >
> > >  		if (desc1_->fd() != -1) {
> > >  			std::cout << "Failed fd numerical check (default constructor)"
> > > @@ -56,10 +56,10 @@ protected:
> > >  		desc1_ = nullptr;
> > >
> > >  		/*
> > > -		 * Test creating FileDescriptor by copying numerical file
> > > +		 * Test creating SharedFD by copying numerical file
> > >  		 * descriptor.
> > >  		 */
> > > -		desc1_ = new FileDescriptor(fd_);
> > > +		desc1_ = new SharedFD(fd_);
> > >  		if (desc1_->fd() == fd_) {
> > >  			std::cout << "Failed fd numerical check (lvalue ref constructor)"
> > >  				  << std::endl;
> > > @@ -84,13 +84,13 @@ protected:
> > >  		}
> > >
> > >  		/*
> > > -		 * Test creating FileDescriptor by taking ownership of
> > > +		 * Test creating SharedFD by taking ownership of
> > >  		 * numerical file descriptor.
> > >  		 */
> > >  		int dupFd = dup(fd_);
> > >  		int dupFdCopy = dupFd;
> > >
> > > -		desc1_ = new FileDescriptor(std::move(dupFd));
> > > +		desc1_ = new SharedFD(std::move(dupFd));
> > >  		if (desc1_->fd() != dupFdCopy) {
> > >  			std::cout << "Failed fd numerical check (rvalue ref constructor)"
> > >  				  << std::endl;
> > > @@ -114,9 +114,9 @@ protected:
> > >  			return TestFail;
> > >  		}
> > >
> > > -		/* Test creating FileDescriptor from other FileDescriptor. */
> > > -		desc1_ = new FileDescriptor(fd_);
> > > -		desc2_ = new FileDescriptor(*desc1_);
> > > +		/* Test creating SharedFD from other SharedFD. */
> > > +		desc1_ = new SharedFD(fd_);
> > > +		desc2_ = new SharedFD(*desc1_);
> > >
> > >  		if (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {
> > >  			std::cout << "Failed fd numerical check (copy constructor)"
> > > @@ -142,10 +142,10 @@ protected:
> > >  		delete desc2_;
> > >  		desc2_ = nullptr;
> > >
> > > -		/* Test creating FileDescriptor by taking over other FileDescriptor. */
> > > -		desc1_ = new FileDescriptor(fd_);
> > > +		/* Test creating SharedFD by taking over other SharedFD. */
> > > +		desc1_ = new SharedFD(fd_);
> > >  		fd = desc1_->fd();
> > > -		desc2_ = new FileDescriptor(std::move(*desc1_));
> > > +		desc2_ = new SharedFD(std::move(*desc1_));
> > >
> > >  		if (desc1_->fd() != -1 || desc2_->fd() != fd) {
> > >  			std::cout << "Failed fd numerical check (move constructor)"
> > > @@ -164,9 +164,9 @@ protected:
> > >  		delete desc2_;
> > >  		desc2_ = nullptr;
> > >
> > > -		/* Test creating FileDescriptor by copy assignment. */
> > > -		desc1_ = new FileDescriptor();
> > > -		desc2_ = new FileDescriptor(fd_);
> > > +		/* Test creating SharedFD by copy assignment. */
> > > +		desc1_ = new SharedFD();
> > > +		desc2_ = new SharedFD(fd_);
> > >
> > >  		fd = desc2_->fd();
> > >  		*desc1_ = *desc2_;
> > > @@ -188,9 +188,9 @@ protected:
> > >  		delete desc2_;
> > >  		desc2_ = nullptr;
> > >
> > > -		/* Test creating FileDescriptor by move assignment. */
> > > -		desc1_ = new FileDescriptor();
> > > -		desc2_ = new FileDescriptor(fd_);
> > > +		/* Test creating SharedFD by move assignment. */
> > > +		desc1_ = new SharedFD();
> > > +		desc2_ = new SharedFD(fd_);
> > >
> > >  		fd = desc2_->fd();
> > >  		*desc1_ = std::move(*desc2_);
> > > @@ -237,7 +237,7 @@ private:
> > >
> > >  	int fd_;
> > >  	ino_t inodeNr_;
> > > -	FileDescriptor *desc1_, *desc2_;
> > > +	SharedFD *desc1_, *desc2_;
> > >  };
> > >
> > > -TEST_REGISTER(FileDescriptorTest)
> > > +TEST_REGISTER(SharedFDTest)
> > > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> > > index d856339aa9ee..c37c4941b528 100644
> > > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> > > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> > > @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)
> > >  void {{proxy_name}}::{{method.mojom_name}}IPC(
> > >  	std::vector<uint8_t>::const_iterator data,
> > >  	size_t dataSize,
> > > -	[[maybe_unused]] const std::vector<FileDescriptor> &fds)
> > > +	[[maybe_unused]] const std::vector<SharedFD> &fds)
> > >  {
> > >  {%- for param in method.parameters %}
> > >  	{{param|name}} {{param.mojom_name}};
> > > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> > > index ce396c183d0c..c308dd10c7e5 100644
> > > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> > > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> > > @@ -64,7 +64,7 @@ private:
> > >  	void {{method.mojom_name}}IPC(
> > >  		std::vector<uint8_t>::const_iterator data,
> > >  		size_t dataSize,
> > > -		const std::vector<FileDescriptor> &fds);
> > > +		const std::vector<SharedFD> &fds);
> > >  {% endfor %}
> > >
> > >  	/* Helper class to invoke async functions in another thread. */
> > > diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> > > index ebcd2aaaafae..bac826a74c2d 100644
> > > --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> > > +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> > > @@ -54,7 +54,7 @@
> > >  {%- for param in params %}
> > >  	std::vector<uint8_t> {{param.mojom_name}}Buf;
> > >  {%- if param|has_fd %}
> > > -	std::vector<FileDescriptor> {{param.mojom_name}}Fds;
> > > +	std::vector<SharedFD> {{param.mojom_name}}Fds;
> > >  	std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =
> > >  {%- else %}
> > >  	std::tie({{param.mojom_name}}Buf, std::ignore) =
> > > diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > > index b8ef8e7b974e..77bae36fe6b7 100644
> > > --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > > +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> > > @@ -40,7 +40,7 @@
> > >  		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> > >  {%- elif field|is_fd %}
> > >  		std::vector<uint8_t> {{field.mojom_name}};
> > > -		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> > > +		std::vector<SharedFD> {{field.mojom_name}}Fds;
> > >  		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> > >  			IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
> > >  		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> > > @@ -58,7 +58,7 @@
> > >  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
> > >  		std::vector<uint8_t> {{field.mojom_name}};
> > >  	{%- if field|has_fd %}
> > > -		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
> > > +		std::vector<SharedFD> {{field.mojom_name}}Fds;
> > >  		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> > >  	{%- else %}
> > >  		std::tie({{field.mojom_name}}, std::ignore) =
> > > @@ -177,7 +177,7 @@
> > >   # \a struct.
> > >   #}
> > >  {%- macro serializer(struct, namespace) %}
> > > -	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
> > >  	serialize(const {{struct|name_full}} &data,
> > >  {%- if struct|needs_control_serializer %}
> > >  		  ControlSerializer *cs)
> > > @@ -187,7 +187,7 @@
> > >  	{
> > >  		std::vector<uint8_t> retData;
> > >  {%- if struct|has_fd %}
> > > -		std::vector<FileDescriptor> retFds;
> > > +		std::vector<SharedFD> retFds;
> > >  {%- endif %}
> > >  {%- for field in struct.fields %}
> > >  {{serializer_field(field, namespace, loop)}}
> > > @@ -210,7 +210,7 @@
> > >  {%- macro deserializer_fd(struct, namespace) %}
> > >  	static {{struct|name_full}}
> > >  	deserialize(std::vector<uint8_t> &data,
> > > -		    std::vector<FileDescriptor> &fds,
> > > +		    std::vector<SharedFD> &fds,
> > >  {%- if struct|needs_control_serializer %}
> > >  		    ControlSerializer *cs)
> > >  {%- else %}
> > > @@ -224,8 +224,8 @@
> > >  	static {{struct|name_full}}
> > >  	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  		    std::vector<uint8_t>::const_iterator dataEnd,
> > > -		    std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -		    std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +		    std::vector<SharedFD>::const_iterator fdsBegin,
> > > +		    std::vector<SharedFD>::const_iterator fdsEnd,
> > >  {%- if struct|needs_control_serializer %}
> > >  		    ControlSerializer *cs)
> > >  {%- else %}
> > > @@ -234,7 +234,7 @@
> > >  	{
> > >  		{{struct|name_full}} ret;
> > >  		std::vector<uint8_t>::const_iterator m = dataBegin;
> > > -		std::vector<FileDescriptor>::const_iterator n = fdsBegin;
> > > +		std::vector<SharedFD>::const_iterator n = fdsBegin;
> > >
> > >  		size_t dataSize = std::distance(dataBegin, dataEnd);
> > >  		[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
> > > @@ -255,7 +255,7 @@
> > >  {%- macro deserializer_fd_simple(struct, namespace) %}
> > >  	static {{struct|name_full}}
> > >  	deserialize(std::vector<uint8_t> &data,
> > > -		    [[maybe_unused]] std::vector<FileDescriptor> &fds,
> > > +		    [[maybe_unused]] std::vector<SharedFD> &fds,
> > >  		    ControlSerializer *cs = nullptr)
> > >  	{
> > >  		return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);
> > > @@ -264,8 +264,8 @@
> > >  	static {{struct|name_full}}
> > >  	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
> > >  		    std::vector<uint8_t>::const_iterator dataEnd,
> > > -		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
> > > -		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
> > > +		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
> > > +		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
> > >  		    ControlSerializer *cs = nullptr)
> > >  	{
> > >  		return IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);
> > > diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py
> > > index c609f4e5c062..753bfc734e56 100644
> > > --- a/utils/ipc/generators/mojom_libcamera_generator.py
> > > +++ b/utils/ipc/generators/mojom_libcamera_generator.py
> > > @@ -77,7 +77,7 @@ def GetDefaultValue(element):
> > >      if mojom.IsEnumKind(element.kind):
> > >          return f'static_cast<{element.kind.mojom_name}>(0)'
> > >      if isinstance(element.kind, mojom.Struct) and \
> > > -       element.kind.mojom_name == 'FileDescriptor':
> > > +       element.kind.mojom_name == 'SharedFD':
> > >          return '-1'
> > >      return ''
> > >
> > > @@ -140,7 +140,7 @@ def HasFd(element):
> > >          types = GetAllTypes(element)
> > >      else:
> > >          types = GetAllTypes(element.kind)
> > > -    return "FileDescriptor" in types or (attrs is not None and "hasFd" in attrs)
> > > +    return "SharedFD" in types or (attrs is not None and "hasFd" in attrs)
> > >
> > >  def WithDefaultValues(element):
> > >      return [x for x in element if HasDefaultValue(x)]
> > > @@ -221,7 +221,7 @@ def IsEnum(element):
> > >      return mojom.IsEnumKind(element.kind)
> > >
> > >  def IsFd(element):
> > > -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "FileDescriptor"
> > > +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "SharedFD"
> > >
> > >  def IsMap(element):
> > >      return mojom.IsMapKind(element.kind)
>
> --
> Regards,
>
> Laurent Pinchart

Patch
diff mbox series

diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h
index 47769da7abc2..691b52d6ab2d 100644
--- a/include/libcamera/base/file.h
+++ b/include/libcamera/base/file.h
@@ -21,7 +21,7 @@ 
 
 namespace libcamera {
 
-class FileDescriptor;
+class SharedFD;
 
 class File
 {
@@ -69,7 +69,7 @@  public:
 	bool unmap(uint8_t *addr);
 
 	static bool exists(const std::string &name);
-	static ino_t inode(const FileDescriptor &fd);
+	static ino_t inode(const SharedFD &fd);
 
 private:
 	LIBCAMERA_DISABLE_COPY(File)
diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
index cca374a769cc..112420dab225 100644
--- a/include/libcamera/base/meson.build
+++ b/include/libcamera/base/meson.build
@@ -11,13 +11,13 @@  libcamera_base_headers = files([
     'event_dispatcher_poll.h',
     'event_notifier.h',
     'file.h',
-    'file_descriptor.h',
     'flags.h',
     'log.h',
     'message.h',
     'object.h',
     'private.h',
     'semaphore.h',
+    'shared_fd.h',
     'signal.h',
     'span.h',
     'thread.h',
diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h
similarity index 55%
rename from include/libcamera/base/file_descriptor.h
rename to include/libcamera/base/shared_fd.h
index 12a43f95d414..a786885ceb32 100644
--- a/include/libcamera/base/file_descriptor.h
+++ b/include/libcamera/base/shared_fd.h
@@ -2,7 +2,7 @@ 
 /*
  * Copyright (C) 2019, Google Inc.
  *
- * file_descriptor.h - File descriptor wrapper
+ * shared_fd.h - File descriptor wrapper with shared ownership
  */
 
 #pragma once
@@ -13,18 +13,18 @@  namespace libcamera {
 
 class UniqueFD;
 
-class FileDescriptor final
+class SharedFD final
 {
 public:
-	explicit FileDescriptor(const int &fd = -1);
-	explicit FileDescriptor(int &&fd);
-	explicit FileDescriptor(UniqueFD fd);
-	FileDescriptor(const FileDescriptor &other);
-	FileDescriptor(FileDescriptor &&other);
-	~FileDescriptor();
+	explicit SharedFD(const int &fd = -1);
+	explicit SharedFD(int &&fd);
+	explicit SharedFD(UniqueFD fd);
+	SharedFD(const SharedFD &other);
+	SharedFD(SharedFD &&other);
+	~SharedFD();
 
-	FileDescriptor &operator=(const FileDescriptor &other);
-	FileDescriptor &operator=(FileDescriptor &&other);
+	SharedFD &operator=(const SharedFD &other);
+	SharedFD &operator=(SharedFD &&other);
 
 	bool isValid() const { return fd_ != nullptr; }
 	int fd() const { return fd_ ? fd_->fd() : -1; }
diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h
index 2fbea9c5be16..357bbe189551 100644
--- a/include/libcamera/framebuffer.h
+++ b/include/libcamera/framebuffer.h
@@ -13,7 +13,7 @@ 
 #include <vector>
 
 #include <libcamera/base/class.h>
-#include <libcamera/base/file_descriptor.h>
+#include <libcamera/base/shared_fd.h>
 #include <libcamera/base/span.h>
 
 namespace libcamera {
@@ -51,7 +51,7 @@  class FrameBuffer final : public Extensible
 public:
 	struct Plane {
 		static constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();
-		FileDescriptor fd;
+		SharedFD fd;
 		unsigned int offset = kInvalidOffset;
 		unsigned int length;
 	};
diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
index c2f602d5b7de..a87449c9be48 100644
--- a/include/libcamera/internal/ipa_data_serializer.h
+++ b/include/libcamera/internal/ipa_data_serializer.h
@@ -66,7 +66,7 @@  template<typename T>
 class IPADataSerializer
 {
 public:
-	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 	serialize(const T &data, ControlSerializer *cs = nullptr);
 
 	static T deserialize(const std::vector<uint8_t> &data,
@@ -76,12 +76,12 @@  public:
 			     ControlSerializer *cs = nullptr);
 
 	static T deserialize(const std::vector<uint8_t> &data,
-			     const std::vector<FileDescriptor> &fds,
+			     const std::vector<SharedFD> &fds,
 			     ControlSerializer *cs = nullptr);
 	static T deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 			     std::vector<uint8_t>::const_iterator dataEnd,
-			     std::vector<FileDescriptor>::const_iterator fdsBegin,
-			     std::vector<FileDescriptor>::const_iterator fdsEnd,
+			     std::vector<SharedFD>::const_iterator fdsBegin,
+			     std::vector<SharedFD>::const_iterator fdsEnd,
 			     ControlSerializer *cs = nullptr);
 };
 
@@ -104,11 +104,11 @@  template<typename V>
 class IPADataSerializer<std::vector<V>>
 {
 public:
-	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
 	{
 		std::vector<uint8_t> dataVec;
-		std::vector<FileDescriptor> fdsVec;
+		std::vector<SharedFD> fdsVec;
 
 		/* Serialize the length. */
 		uint32_t vecLen = data.size();
@@ -117,7 +117,7 @@  public:
 		/* Serialize the members. */
 		for (auto const &it : data) {
 			std::vector<uint8_t> dvec;
-			std::vector<FileDescriptor> fvec;
+			std::vector<SharedFD> fvec;
 
 			std::tie(dvec, fvec) =
 				IPADataSerializer<V>::serialize(it, cs);
@@ -141,11 +141,11 @@  public:
 					  std::vector<uint8_t>::const_iterator dataEnd,
 					  ControlSerializer *cs = nullptr)
 	{
-		std::vector<FileDescriptor> fds;
+		std::vector<SharedFD> fds;
 		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
 	}
 
-	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
+	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
 					  ControlSerializer *cs = nullptr)
 	{
 		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
@@ -153,15 +153,15 @@  public:
 
 	static std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 					  std::vector<uint8_t>::const_iterator dataEnd,
-					  std::vector<FileDescriptor>::const_iterator fdsBegin,
-					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
+					  std::vector<SharedFD>::const_iterator fdsBegin,
+					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
 					  ControlSerializer *cs = nullptr)
 	{
 		uint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
 		std::vector<V> ret(vecLen);
 
 		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
-		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
+		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
 		for (uint32_t i = 0; i < vecLen; i++) {
 			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
 			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
@@ -201,11 +201,11 @@  template<typename K, typename V>
 class IPADataSerializer<std::map<K, V>>
 {
 public:
-	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
 	{
 		std::vector<uint8_t> dataVec;
-		std::vector<FileDescriptor> fdsVec;
+		std::vector<SharedFD> fdsVec;
 
 		/* Serialize the length. */
 		uint32_t mapLen = data.size();
@@ -214,7 +214,7 @@  public:
 		/* Serialize the members. */
 		for (auto const &it : data) {
 			std::vector<uint8_t> dvec;
-			std::vector<FileDescriptor> fvec;
+			std::vector<SharedFD> fvec;
 
 			std::tie(dvec, fvec) =
 				IPADataSerializer<K>::serialize(it.first, cs);
@@ -247,11 +247,11 @@  public:
 					  std::vector<uint8_t>::const_iterator dataEnd,
 					  ControlSerializer *cs = nullptr)
 	{
-		std::vector<FileDescriptor> fds;
+		std::vector<SharedFD> fds;
 		return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);
 	}
 
-	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,
+	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,
 					  ControlSerializer *cs = nullptr)
 	{
 		return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
@@ -259,8 +259,8 @@  public:
 
 	static std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 					  std::vector<uint8_t>::const_iterator dataEnd,
-					  std::vector<FileDescriptor>::const_iterator fdsBegin,
-					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
+					  std::vector<SharedFD>::const_iterator fdsBegin,
+					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
 					  ControlSerializer *cs = nullptr)
 	{
 		std::map<K, V> ret;
@@ -268,7 +268,7 @@  public:
 		uint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
 
 		std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;
-		std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;
+		std::vector<SharedFD>::const_iterator fdIter = fdsBegin;
 		for (uint32_t i = 0; i < mapLen; i++) {
 			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
 			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h
index 986f8d886fa6..ab5dd67c3813 100644
--- a/include/libcamera/internal/ipc_pipe.h
+++ b/include/libcamera/internal/ipc_pipe.h
@@ -9,7 +9,7 @@ 
 
 #include <vector>
 
-#include <libcamera/base/file_descriptor.h>
+#include <libcamera/base/shared_fd.h>
 #include <libcamera/base/signal.h>
 
 #include "libcamera/internal/ipc_unixsocket.h"
@@ -33,17 +33,17 @@  public:
 
 	Header &header() { return header_; }
 	std::vector<uint8_t> &data() { return data_; }
-	std::vector<FileDescriptor> &fds() { return fds_; }
+	std::vector<SharedFD> &fds() { return fds_; }
 
 	const Header &header() const { return header_; }
 	const std::vector<uint8_t> &data() const { return data_; }
-	const std::vector<FileDescriptor> &fds() const { return fds_; }
+	const std::vector<SharedFD> &fds() const { return fds_; }
 
 private:
 	Header header_;
 
 	std::vector<uint8_t> data_;
-	std::vector<FileDescriptor> fds_;
+	std::vector<SharedFD> fds_;
 };
 
 class IPCPipe
diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom
index f7eff0c7ab8c..74f3339e56f2 100644
--- a/include/libcamera/ipa/core.mojom
+++ b/include/libcamera/ipa/core.mojom
@@ -32,7 +32,7 @@  module libcamera;
  *   - This attribute instructs the build system that a (de)serializer is
  *     available for the type and there's no need to generate one
  * - hasFd - struct fields or empty structs only
- *   - Designate that this field or empty struct contains a FileDescriptor
+ *   - Designate that this field or empty struct contains a SharedFD
  *
  * Rules:
  * - If the type is defined in a libcamera C++ header *and* a (de)serializer is
@@ -60,7 +60,7 @@  module libcamera;
  *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array
  *       member
  * - [skipHeader] and [skipSerdes] only work here in core.mojom
- * - If a field in a struct has a FileDescriptor, but is not explicitly
+ * - If a field in a struct has a SharedFD, but is not explicitly
  *   defined so in mojom, then the field must be marked with the [hasFd]
  *   attribute
  *
@@ -71,7 +71,7 @@  module libcamera;
  */
 [skipSerdes, skipHeader] struct ControlInfoMap {};
 [skipSerdes, skipHeader] struct ControlList {};
-[skipSerdes, skipHeader] struct FileDescriptor {};
+[skipSerdes, skipHeader] struct SharedFD {};
 
 [skipHeader] struct Point {
 	int32 x;
diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
index e453d46cb14f..acd3cafe6c91 100644
--- a/include/libcamera/ipa/raspberrypi.mojom
+++ b/include/libcamera/ipa/raspberrypi.mojom
@@ -35,7 +35,7 @@  struct ISPConfig {
 
 struct IPAConfig {
 	uint32 transform;
-	libcamera.FileDescriptor lsTableHandle;
+	libcamera.SharedFD lsTableHandle;
 };
 
 struct StartConfig {
diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp
index f2e0bdbdbbf6..1938b10509fa 100644
--- a/src/android/camera_device.cpp
+++ b/src/android/camera_device.cpp
@@ -725,7 +725,7 @@  CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,
 
 	std::vector<FrameBuffer::Plane> planes(buf.numPlanes());
 	for (size_t i = 0; i < buf.numPlanes(); ++i) {
-		FileDescriptor fd{ camera3buffer->data[i] };
+		SharedFD fd{ camera3buffer->data[i] };
 		if (!fd.isValid()) {
 			LOG(HAL, Fatal) << "No valid fd";
 			return nullptr;
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index c6aec09046f7..aaf629eeb3fc 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -15,8 +15,8 @@ 
 
 #include <linux/bcm2835-isp.h>
 
-#include <libcamera/base/file_descriptor.h>
 #include <libcamera/base/log.h>
+#include <libcamera/base/shared_fd.h>
 #include <libcamera/base/span.h>
 
 #include <libcamera/control_ids.h>
@@ -164,7 +164,7 @@  private:
 	bool processPending_;
 
 	/* LS table allocation passed in from the pipeline handler. */
-	FileDescriptor lsTableHandle_;
+	SharedFD lsTableHandle_;
 	void *lsTable_;
 
 	/* Distinguish the first camera start from others. */
diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp
index 66c73c406198..3ca9839bb989 100644
--- a/src/libcamera/base/file.cpp
+++ b/src/libcamera/base/file.cpp
@@ -14,8 +14,8 @@ 
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <libcamera/base/file_descriptor.h>
 #include <libcamera/base/log.h>
+#include <libcamera/base/shared_fd.h>
 
 /**
  * \file base/file.h
@@ -473,11 +473,11 @@  bool File::exists(const std::string &name)
 }
 
 /**
- * \brief Retrieve the inode of a FileDescriptor
+ * \brief Retrieve the inode of a SharedFD
  *
  * \return The file descriptor inode on success, or 0 on error
  */
-ino_t File::inode(const FileDescriptor &fd)
+ino_t File::inode(const SharedFD &fd)
 {
 	if (!fd.isValid())
 		return 0;
diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp
deleted file mode 100644
index a83bf52c31e6..000000000000
--- a/src/libcamera/base/file_descriptor.cpp
+++ /dev/null
@@ -1,266 +0,0 @@ 
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2019, Google Inc.
- *
- * file_descriptor.cpp - File descriptor wrapper
- */
-
-#include <libcamera/base/file_descriptor.h>
-
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <utility>
-
-#include <libcamera/base/log.h>
-#include <libcamera/base/unique_fd.h>
-
-/**
- * \file base/file_descriptor.h
- * \brief File descriptor wrapper
- */
-
-namespace libcamera {
-
-LOG_DEFINE_CATEGORY(FileDescriptor)
-
-/**
- * \class FileDescriptor
- * \brief RAII-style wrapper for file descriptors
- *
- * The FileDescriptor class provides RAII-style lifetime management of file
- * descriptors with an efficient mechanism for ownership sharing. At its core,
- * an internal Descriptor object wraps a file descriptor (expressed as a signed
- * integer) with an RAII-style interface. The Descriptor is then implicitly
- * shared with all FileDescriptor instances constructed as copies.
- *
- * When constructed from a numerical file descriptor, the FileDescriptor
- * instance either duplicates or takes over the file descriptor:
- *
- * - The FileDescriptor(const int &) constructor duplicates the numerical file
- *   descriptor and wraps the duplicate in a Descriptor. The caller is
- *   responsible for closing the original file descriptor, and the value
- *   returned by fd() will be different from the value passed to the
- *   constructor.
- *
- * - The FileDescriptor(int &&) constructor takes over the numerical file
- *   descriptor and wraps it in a Descriptor. The caller shall not touch the
- *   original file descriptor once the function returns, and the value returned
- *   by fd() will be identical to the value passed to the constructor.
- *
- * The copy constructor and assignment operator create copies that share the
- * Descriptor, while the move versions of those functions additionally make the
- * other FileDescriptor invalid. When the last FileDescriptor that references a
- * Descriptor is destroyed, the file descriptor is closed.
- *
- * The numerical file descriptor is available through the fd() function. All
- * FileDescriptor instances created as copies of a FileDescriptor will report
- * the same fd() value. Callers can perform operations on the fd(), but shall
- * never close it manually.
- */
-
-/**
- * \brief Create a FileDescriptor copying a given \a fd
- * \param[in] fd File descriptor
- *
- * Construct a FileDescriptor from a numerical file descriptor by duplicating
- * the \a fd, and take ownership of the copy. The original \a fd is left
- * untouched, and the caller is responsible for closing it when appropriate.
- * The duplicated file descriptor will be closed automatically when all
- * FileDescriptor instances that reference it are destroyed.
- *
- * If the \a fd is negative, the FileDescriptor is constructed as invalid and
- * the fd() function will return -1.
- */
-FileDescriptor::FileDescriptor(const int &fd)
-{
-	if (fd < 0)
-		return;
-
-	fd_ = std::make_shared<Descriptor>(fd, true);
-	if (fd_->fd() < 0)
-		fd_.reset();
-}
-
-/**
- * \brief Create a FileDescriptor taking ownership of a given \a fd
- * \param[in] fd File descriptor
- *
- * Construct a FileDescriptor from a numerical file descriptor by taking
- * ownership of the \a fd. The original \a fd is set to -1 and shall not be
- * touched by the caller anymore. In particular, the caller shall not close the
- * original \a fd manually. The duplicated file descriptor will be closed
- * automatically when all FileDescriptor instances that reference it are
- * destroyed.
- *
- * If the \a fd is negative, the FileDescriptor is constructed as invalid and
- * the fd() function will return -1.
- */
-FileDescriptor::FileDescriptor(int &&fd)
-{
-	if (fd < 0)
-		return;
-
-	fd_ = std::make_shared<Descriptor>(fd, false);
-	/*
-	 * The Descriptor constructor can't have failed here, as it took over
-	 * the fd without duplicating it. Just set the original fd to -1 to
-	 * implement move semantics.
-	 */
-	fd = -1;
-}
-
-/**
- * \brief Create a FileDescriptor taking ownership of a given UniqueFD \a fd
- * \param[in] fd UniqueFD
- *
- * Construct a FileDescriptor from UniqueFD by taking ownership of the \a fd.
- * The original \a fd becomes invalid.
- */
-FileDescriptor::FileDescriptor(UniqueFD fd)
-	: FileDescriptor(fd.release())
-{
-}
-
-/**
- * \brief Copy constructor, create a FileDescriptor from a copy of \a other
- * \param[in] other The other FileDescriptor
- *
- * Copying a FileDescriptor implicitly shares ownership of the wrapped file
- * descriptor. The original FileDescriptor is left untouched, and the caller is
- * responsible for destroying it when appropriate. The wrapped file descriptor
- * will be closed automatically when all FileDescriptor instances that
- * reference it are destroyed.
- */
-FileDescriptor::FileDescriptor(const FileDescriptor &other)
-	: fd_(other.fd_)
-{
-}
-
-/**
- * \brief Move constructor, create a FileDescriptor by taking over \a other
- * \param[in] other The other FileDescriptor
- *
- * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
- * by \a other to the new FileDescriptor. The \a other FileDescriptor is
- * invalidated and its fd() function will return -1. The wrapped file descriptor
- * will be closed automatically when all FileDescriptor instances that
- * reference it are destroyed.
- */
-FileDescriptor::FileDescriptor(FileDescriptor &&other)
-	: fd_(std::move(other.fd_))
-{
-}
-
-/**
- * \brief Destroy the FileDescriptor instance
- *
- * Destroying a FileDescriptor instance releases its reference to the wrapped
- * descriptor, if any. When the last instance that references a wrapped
- * descriptor is destroyed, the file descriptor is automatically closed.
- */
-FileDescriptor::~FileDescriptor()
-{
-}
-
-/**
- * \brief Copy assignment operator, replace the wrapped file descriptor with a
- * copy of \a other
- * \param[in] other The other FileDescriptor
- *
- * Copying a FileDescriptor creates a new reference to the wrapped file
- * descriptor owner by \a other. If \a other is invalid, *this will also be
- * invalid. The original FileDescriptor is left untouched, and the caller is
- * responsible for destroying it when appropriate. The wrapped file descriptor
- * will be closed automatically when all FileDescriptor instances that
- * reference it are destroyed.
- *
- * \return A reference to this FileDescriptor
- */
-FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)
-{
-	fd_ = other.fd_;
-
-	return *this;
-}
-
-/**
- * \brief Move assignment operator, replace the wrapped file descriptor by
- * taking over \a other
- * \param[in] other The other FileDescriptor
- *
- * Moving a FileDescriptor moves the reference to the wrapped descriptor owned
- * by \a other to the new FileDescriptor. If \a other is invalid, *this will
- * also be invalid. The \a other FileDescriptor is invalidated and its fd()
- * function will return -1. The wrapped file descriptor will be closed
- * automatically when all FileDescriptor instances that reference it are
- * destroyed.
- *
- * \return A reference to this FileDescriptor
- */
-FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)
-{
-	fd_ = std::move(other.fd_);
-
-	return *this;
-}
-
-/**
- * \fn FileDescriptor::isValid()
- * \brief Check if the FileDescriptor instance is valid
- * \return True if the FileDescriptor is valid, false otherwise
- */
-
-/**
- * \fn FileDescriptor::fd()
- * \brief Retrieve the numerical file descriptor
- * \return The numerical file descriptor, which may be -1 if the FileDescriptor
- * instance is invalid
- */
-
-/**
- * \brief Duplicate a FileDescriptor
- *
- * Duplicating a FileDescriptor creates a duplicate of the wrapped file
- * descriptor and returns a UniqueFD that owns the duplicate. The fd() function
- * of the original and the get() function of the duplicate will return different
- * values. The duplicate instance will not be affected by destruction of the
- * original instance or its copies.
- *
- * \return A UniqueFD owning a duplicate of the original file descriptor
- */
-UniqueFD FileDescriptor::dup() const
-{
-	int dupFd = ::dup(fd());
-	if (dupFd == -1) {
-		int ret = -errno;
-		LOG(FileDescriptor, Error)
-			<< "Failed to dup() fd: " << strerror(-ret);
-	}
-
-	return UniqueFD(dupFd);
-}
-
-FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)
-{
-	if (!duplicate) {
-		fd_ = fd;
-		return;
-	}
-
-	/* Failing to dup() a fd should not happen and is fatal. */
-	fd_ = ::dup(fd);
-	if (fd_ == -1) {
-		int ret = -errno;
-		LOG(FileDescriptor, Fatal)
-			<< "Failed to dup() fd: " << strerror(-ret);
-	}
-}
-
-FileDescriptor::Descriptor::~Descriptor()
-{
-	if (fd_ != -1)
-		close(fd_);
-}
-
-} /* namespace libcamera */
diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build
index b0d85bc19245..ccb746c27466 100644
--- a/src/libcamera/base/meson.build
+++ b/src/libcamera/base/meson.build
@@ -8,12 +8,12 @@  libcamera_base_sources = files([
     'event_dispatcher_poll.cpp',
     'event_notifier.cpp',
     'file.cpp',
-    'file_descriptor.cpp',
     'flags.cpp',
     'log.cpp',
     'message.cpp',
     'object.cpp',
     'semaphore.cpp',
+    'shared_fd.cpp',
     'signal.cpp',
     'thread.cpp',
     'timer.cpp',
diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp
new file mode 100644
index 000000000000..05b6892f7e19
--- /dev/null
+++ b/src/libcamera/base/shared_fd.cpp
@@ -0,0 +1,262 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * shared_fd.cpp - File descriptor wrapper with shared ownership
+ */
+
+#include <libcamera/base/shared_fd.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utility>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/unique_fd.h>
+
+/**
+ * \file base/shared_fd.h
+ * \brief File descriptor wrapper
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(SharedFD)
+
+/**
+ * \class SharedFD
+ * \brief RAII-style wrapper for file descriptors
+ *
+ * The SharedFD class provides RAII-style lifetime management of file
+ * descriptors with an efficient mechanism for ownership sharing. At its core,
+ * an internal Descriptor object wraps a file descriptor (expressed as a signed
+ * integer) with an RAII-style interface. The Descriptor is then implicitly
+ * shared with all SharedFD instances constructed as copies.
+ *
+ * When constructed from a numerical file descriptor, the SharedFD instance
+ * either duplicates or takes over the file descriptor:
+ *
+ * - The SharedFD(const int &) constructor duplicates the numerical file
+ *   descriptor and wraps the duplicate in a Descriptor. The caller is
+ *   responsible for closing the original file descriptor, and the value
+ *   returned by fd() will be different from the value passed to the
+ *   constructor.
+ *
+ * - The SharedFD(int &&) constructor takes over the numerical file descriptor
+ *   and wraps it in a Descriptor. The caller shall not touch the original file
+ *   descriptor once the function returns, and the value returned by fd() will
+ *   be identical to the value passed to the constructor.
+ *
+ * The copy constructor and assignment operator create copies that share the
+ * Descriptor, while the move versions of those functions additionally make the
+ * other SharedFD invalid. When the last SharedFD that references a Descriptor
+ * is destroyed, the file descriptor is closed.
+ *
+ * The numerical file descriptor is available through the fd() function. All
+ * SharedFD instances created as copies of a SharedFD will report the same fd()
+ * value. Callers can perform operations on the fd(), but shall never close it
+ * manually.
+ */
+
+/**
+ * \brief Create a SharedFD copying a given \a fd
+ * \param[in] fd File descriptor
+ *
+ * Construct a SharedFD from a numerical file descriptor by duplicating the
+ * \a fd, and take ownership of the copy. The original \a fd is left untouched,
+ * and the caller is responsible for closing it when appropriate. The duplicated
+ * file descriptor will be closed automatically when all SharedFD instances that
+ * reference it are destroyed.
+ *
+ * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
+ * function will return -1.
+ */
+SharedFD::SharedFD(const int &fd)
+{
+	if (fd < 0)
+		return;
+
+	fd_ = std::make_shared<Descriptor>(fd, true);
+	if (fd_->fd() < 0)
+		fd_.reset();
+}
+
+/**
+ * \brief Create a SharedFD taking ownership of a given \a fd
+ * \param[in] fd File descriptor
+ *
+ * Construct a SharedFD from a numerical file descriptor by taking ownership of
+ * the \a fd. The original \a fd is set to -1 and shall not be touched by the
+ * caller anymore. In particular, the caller shall not close the original \a fd
+ * manually. The duplicated file descriptor will be closed automatically when
+ * all SharedFD instances that reference it are destroyed.
+ *
+ * If the \a fd is negative, the SharedFD is constructed as invalid and the fd()
+ * function will return -1.
+ */
+SharedFD::SharedFD(int &&fd)
+{
+	if (fd < 0)
+		return;
+
+	fd_ = std::make_shared<Descriptor>(fd, false);
+	/*
+	 * The Descriptor constructor can't have failed here, as it took over
+	 * the fd without duplicating it. Just set the original fd to -1 to
+	 * implement move semantics.
+	 */
+	fd = -1;
+}
+
+/**
+ * \brief Create a SharedFD taking ownership of a given UniqueFD \a fd
+ * \param[in] fd UniqueFD
+ *
+ * Construct a SharedFD from UniqueFD by taking ownership of the \a fd. The
+ * original \a fd becomes invalid.
+ */
+SharedFD::SharedFD(UniqueFD fd)
+	: SharedFD(fd.release())
+{
+}
+
+/**
+ * \brief Copy constructor, create a SharedFD from a copy of \a other
+ * \param[in] other The other SharedFD
+ *
+ * Copying a SharedFD implicitly shares ownership of the wrapped file
+ * descriptor. The original SharedFD is left untouched, and the caller is
+ * responsible for destroying it when appropriate. The wrapped file descriptor
+ * will be closed automatically when all SharedFD instances that reference it
+ * are destroyed.
+ */
+SharedFD::SharedFD(const SharedFD &other)
+	: fd_(other.fd_)
+{
+}
+
+/**
+ * \brief Move constructor, create a SharedFD by taking over \a other
+ * \param[in] other The other SharedFD
+ *
+ * Moving a SharedFD moves the reference to the wrapped descriptor owned by
+ * \a other to the new SharedFD. The \a other SharedFD is invalidated and its
+ * fd() function will return -1. The wrapped file descriptor will be closed
+ * automatically when all SharedFD instances that reference it are destroyed.
+ */
+SharedFD::SharedFD(SharedFD &&other)
+	: fd_(std::move(other.fd_))
+{
+}
+
+/**
+ * \brief Destroy the SharedFD instance
+ *
+ * Destroying a SharedFD instance releases its reference to the wrapped
+ * descriptor, if any. When the last instance that references a wrapped
+ * descriptor is destroyed, the file descriptor is automatically closed.
+ */
+SharedFD::~SharedFD()
+{
+}
+
+/**
+ * \brief Copy assignment operator, replace the wrapped file descriptor with a
+ * copy of \a other
+ * \param[in] other The other SharedFD
+ *
+ * Copying a SharedFD creates a new reference to the wrapped file descriptor
+ * owner by \a other. If \a other is invalid, *this will also be invalid. The
+ * original SharedFD is left untouched, and the caller is responsible for
+ * destroying it when appropriate. The wrapped file descriptor will be closed
+ * automatically when all SharedFD instances that reference it are destroyed.
+ *
+ * \return A reference to this SharedFD
+ */
+SharedFD &SharedFD::operator=(const SharedFD &other)
+{
+	fd_ = other.fd_;
+
+	return *this;
+}
+
+/**
+ * \brief Move assignment operator, replace the wrapped file descriptor by
+ * taking over \a other
+ * \param[in] other The other SharedFD
+ *
+ * Moving a SharedFD moves the reference to the wrapped descriptor owned by
+ * \a other to the new SharedFD. If \a other is invalid, *this will also be
+ * invalid. The \a other SharedFD is invalidated and its fd() function will
+ * return -1. The wrapped file descriptor will be closed automatically when
+ * all SharedFD instances that reference it are destroyed.
+ *
+ * \return A reference to this SharedFD
+ */
+SharedFD &SharedFD::operator=(SharedFD &&other)
+{
+	fd_ = std::move(other.fd_);
+
+	return *this;
+}
+
+/**
+ * \fn SharedFD::isValid()
+ * \brief Check if the SharedFD instance is valid
+ * \return True if the SharedFD is valid, false otherwise
+ */
+
+/**
+ * \fn SharedFD::fd()
+ * \brief Retrieve the numerical file descriptor
+ * \return The numerical file descriptor, which may be -1 if the SharedFD
+ * instance is invalid
+ */
+
+/**
+ * \brief Duplicate a SharedFD
+ *
+ * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and
+ * returns a UniqueFD that owns the duplicate. The fd() function of the original
+ * and the get() function of the duplicate will return different values. The
+ * duplicate instance will not be affected by destruction of the original
+ * instance or its copies.
+ *
+ * \return A UniqueFD owning a duplicate of the original file descriptor
+ */
+UniqueFD SharedFD::dup() const
+{
+	int dupFd = ::dup(fd());
+	if (dupFd == -1) {
+		int ret = -errno;
+		LOG(SharedFD, Error)
+			<< "Failed to dup() fd: " << strerror(-ret);
+	}
+
+	return UniqueFD(dupFd);
+}
+
+SharedFD::Descriptor::Descriptor(int fd, bool duplicate)
+{
+	if (!duplicate) {
+		fd_ = fd;
+		return;
+	}
+
+	/* Failing to dup() a fd should not happen and is fatal. */
+	fd_ = ::dup(fd);
+	if (fd_ == -1) {
+		int ret = -errno;
+		LOG(SharedFD, Fatal)
+			<< "Failed to dup() fd: " << strerror(-ret);
+	}
+}
+
+SharedFD::Descriptor::~Descriptor()
+{
+	if (fd_ != -1)
+		close(fd_);
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp
index f5bcf107d7aa..0a5bf7fdbeb7 100644
--- a/src/libcamera/framebuffer.cpp
+++ b/src/libcamera/framebuffer.cpp
@@ -180,9 +180,9 @@  FrameBuffer::Private::Private()
  * offset and length.
  *
  * To support DMA access, planes are associated with dmabuf objects represented
- * by FileDescriptor handles. The Plane class doesn't handle mapping of the
- * memory to the CPU, but applications and IPAs may use the dmabuf file
- * descriptors to map the plane memory with mmap() and access its contents.
+ * by SharedFD handles. The Plane class doesn't handle mapping of the memory to
+ * the CPU, but applications and IPAs may use the dmabuf file descriptors to map
+ * the plane memory with mmap() and access its contents.
  *
  * \todo Specify how an application shall decide whether to use a single or
  * multiple dmabufs, based on the camera requirements.
diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
index 82ec9b20a411..0a259305afa2 100644
--- a/src/libcamera/ipa_data_serializer.cpp
+++ b/src/libcamera/ipa_data_serializer.cpp
@@ -31,7 +31,7 @@  LOG_DEFINE_CATEGORY(IPADataSerializer)
  *
  * \todo Harden the vector and map deserializer
  *
- * \todo For FileDescriptors, instead of storing a validity flag, store an
+ * \todo For SharedFDs, instead of storing a validity flag, store an
  * index into the fd array. This will allow us to use views instead of copying.
  */
 
@@ -112,7 +112,7 @@  namespace {
  * \param[in] cs ControlSerializer
  *
  * This version of deserialize() can be used if the object type \a T and its
- * members don't have any FileDescriptor.
+ * members don't have any SharedFD.
  *
  * \a cs is only necessary if the object type \a T or its members contain
  * ControlList or ControlInfoMap.
@@ -132,7 +132,7 @@  namespace {
  * \param[in] cs ControlSerializer
  *
  * This version of deserialize() can be used if the object type \a T and its
- * members don't have any FileDescriptor.
+ * members don't have any SharedFD.
  *
  * \a cs is only necessary if the object type \a T or its members contain
  * ControlList or ControlInfoMap.
@@ -143,7 +143,7 @@  namespace {
 /**
  * \fn template<typename T> IPADataSerializer<T>::deserialize(
  * 	const std::vector<uint8_t> &data,
- * 	const std::vector<FileDescriptor> &fds,
+ * 	const std::vector<SharedFD> &fds,
  * 	ControlSerializer *cs = nullptr)
  * \brief Deserialize byte vector and fd vector into an object
  * \tparam T Type of object to deserialize to
@@ -152,7 +152,7 @@  namespace {
  * \param[in] cs ControlSerializer
  *
  * This version of deserialize() (or the iterator version) must be used if
- * the object type \a T or its members contain FileDescriptor.
+ * the object type \a T or its members contain SharedFD.
  *
  * \a cs is only necessary if the object type \a T or its members contain
  * ControlList or ControlInfoMap.
@@ -164,8 +164,8 @@  namespace {
  * \fn template<typename T> IPADataSerializer::deserialize(
  * 	std::vector<uint8_t>::const_iterator dataBegin,
  * 	std::vector<uint8_t>::const_iterator dataEnd,
- * 	std::vector<FileDescriptor>::const_iterator fdsBegin,
- * 	std::vector<FileDescriptor>::const_iterator fdsEnd,
+ * 	std::vector<SharedFD>::const_iterator fdsBegin,
+ * 	std::vector<SharedFD>::const_iterator fdsEnd,
  * 	ControlSerializer *cs = nullptr)
  * \brief Deserialize byte vector and fd vector into an object
  * \tparam T Type of object to deserialize to
@@ -176,7 +176,7 @@  namespace {
  * \param[in] cs ControlSerializer
  *
  * This version of deserialize() (or the vector version) must be used if
- * the object type \a T or its members contain FileDescriptor.
+ * the object type \a T or its members contain SharedFD.
  *
  * \a cs is only necessary if the object type \a T or its members contain
  * ControlList or ControlInfoMap.
@@ -189,7 +189,7 @@  namespace {
 #define DEFINE_POD_SERIALIZER(type)					\
 									\
 template<>								\
-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>		\
+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>		\
 IPADataSerializer<type>::serialize(const type &data,			\
 				  [[maybe_unused]] ControlSerializer *cs) \
 {									\
@@ -217,7 +217,7 @@  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
 									\
 template<>								\
 type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
-					  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \
+					  [[maybe_unused]] const std::vector<SharedFD> &fds, \
 					  ControlSerializer *cs)	\
 {									\
 	return deserialize(data.cbegin(), data.end(), cs);		\
@@ -226,8 +226,8 @@  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
 template<>								\
 type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \
 					  std::vector<uint8_t>::const_iterator dataEnd, \
-					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \
-					  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \
+					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \
+					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \
 					  ControlSerializer *cs)	\
 {									\
 	return deserialize(dataBegin, dataEnd, cs);			\
@@ -251,7 +251,7 @@  DEFINE_POD_SERIALIZER(double)
  * function parameter serdes).
  */
 template<>
-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 IPADataSerializer<std::string>::serialize(const std::string &data,
 					  [[maybe_unused]] ControlSerializer *cs)
 {
@@ -278,7 +278,7 @@  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
 template<>
 std::string
 IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,
-					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
+					    [[maybe_unused]] const std::vector<SharedFD> &fds,
 					    [[maybe_unused]] ControlSerializer *cs)
 {
 	return { data.cbegin(), data.cend() };
@@ -288,8 +288,8 @@  template<>
 std::string
 IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 					    std::vector<uint8_t>::const_iterator dataEnd,
-					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
-					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
+					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
+					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
 					    [[maybe_unused]] ControlSerializer *cs)
 {
 	return { dataBegin, dataEnd };
@@ -307,7 +307,7 @@  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator
  * be used. The serialized ControlInfoMap will have zero length.
  */
 template<>
-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)
 {
 	if (!cs)
@@ -407,7 +407,7 @@  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
 template<>
 ControlList
 IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
-					    [[maybe_unused]] const std::vector<FileDescriptor> &fds,
+					    [[maybe_unused]] const std::vector<SharedFD> &fds,
 					    ControlSerializer *cs)
 {
 	return deserialize(data.cbegin(), data.end(), cs);
@@ -417,8 +417,8 @@  template<>
 ControlList
 IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 					    std::vector<uint8_t>::const_iterator dataEnd,
-					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
-					    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
+					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
+					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
 					    ControlSerializer *cs)
 {
 	return deserialize(dataBegin, dataEnd, cs);
@@ -431,7 +431,7 @@  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator
  * X bytes - Serialized ControlInfoMap (using ControlSerializer)
  */
 template<>
-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,
 					     ControlSerializer *cs)
 {
@@ -493,7 +493,7 @@  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
 template<>
 ControlInfoMap
 IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
-					       [[maybe_unused]] const std::vector<FileDescriptor> &fds,
+					       [[maybe_unused]] const std::vector<SharedFD> &fds,
 					       ControlSerializer *cs)
 {
 	return deserialize(data.cbegin(), data.end(), cs);
@@ -503,30 +503,30 @@  template<>
 ControlInfoMap
 IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 					       std::vector<uint8_t>::const_iterator dataEnd,
-					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
-					       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
+					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
+					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
 					       ControlSerializer *cs)
 {
 	return deserialize(dataBegin, dataEnd, cs);
 }
 
 /*
- * FileDescriptors are serialized into four bytes that tells if the
- * FileDescriptor is valid or not. If it is valid, then for serialization
- * the fd will be written to the fd vector, or for deserialization the
- * fd vector const_iterator will be valid.
+ * SharedFD instances are serialized into four bytes that tells if the SharedFD
+ * is valid or not. If it is valid, then for serialization the fd will be
+ * written to the fd vector, or for deserialization the fd vector const_iterator
+ * will be valid.
  *
  * This validity is necessary so that we don't send -1 fd over sendmsg(). It
  * also allows us to simply send the entire fd vector into the deserializer
  * and it will be recursively consumed as necessary.
  */
 template<>
-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
-IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
-					     [[maybe_unused]] ControlSerializer *cs)
+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
+IPADataSerializer<SharedFD>::serialize(const SharedFD &data,
+				       [[maybe_unused]] ControlSerializer *cs)
 {
 	std::vector<uint8_t> dataVec;
-	std::vector<FileDescriptor> fdVec;
+	std::vector<SharedFD> fdVec;
 
 	/*
 	 * Store as uint32_t to prepare for conversion from validity flag
@@ -542,11 +542,11 @@  IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,
 }
 
 template<>
-FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
-							      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
-							      std::vector<FileDescriptor>::const_iterator fdsBegin,
-							      std::vector<FileDescriptor>::const_iterator fdsEnd,
-							      [[maybe_unused]] ControlSerializer *cs)
+SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
+						  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
+						  std::vector<SharedFD>::const_iterator fdsBegin,
+						  std::vector<SharedFD>::const_iterator fdsEnd,
+						  [[maybe_unused]] ControlSerializer *cs)
 {
 	ASSERT(std::distance(dataBegin, dataEnd) >= 4);
 
@@ -554,13 +554,13 @@  FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s
 
 	ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));
 
-	return valid ? *fdsBegin : FileDescriptor();
+	return valid ? *fdsBegin : SharedFD();
 }
 
 template<>
-FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,
-							      const std::vector<FileDescriptor> &fds,
-							      [[maybe_unused]] ControlSerializer *cs)
+SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,
+						  const std::vector<SharedFD> &fds,
+						  [[maybe_unused]] ControlSerializer *cs)
 {
 	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());
 }
@@ -568,22 +568,22 @@  FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<
 /*
  * FrameBuffer::Plane is serialized as:
  *
- * 4 byte  - FileDescriptor
+ * 4 byte  - SharedFD
  * 4 bytes - uint32_t Offset
  * 4 bytes - uint32_t Length
  */
 template<>
-std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,
 						 [[maybe_unused]] ControlSerializer *cs)
 {
 	std::vector<uint8_t> dataVec;
-	std::vector<FileDescriptor> fdsVec;
+	std::vector<SharedFD> fdsVec;
 
 	std::vector<uint8_t> fdBuf;
-	std::vector<FileDescriptor> fdFds;
+	std::vector<SharedFD> fdFds;
 	std::tie(fdBuf, fdFds) =
-		IPADataSerializer<FileDescriptor>::serialize(data.fd);
+		IPADataSerializer<SharedFD>::serialize(data.fd);
 	dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());
 	fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());
 
@@ -597,13 +597,13 @@  template<>
 FrameBuffer::Plane
 IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 						   std::vector<uint8_t>::const_iterator dataEnd,
-						   std::vector<FileDescriptor>::const_iterator fdsBegin,
-						   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
+						   std::vector<SharedFD>::const_iterator fdsBegin,
+						   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
 						   [[maybe_unused]] ControlSerializer *cs)
 {
 	FrameBuffer::Plane ret;
 
-	ret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,
+	ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,
 								fdsBegin, fdsBegin + 1);
 	ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);
 	ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);
@@ -614,7 +614,7 @@  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i
 template<>
 FrameBuffer::Plane
 IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,
-						   const std::vector<FileDescriptor> &fds,
+						   const std::vector<SharedFD> &fds,
 						   ControlSerializer *cs)
 {
 	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp
index ad870fd4137f..3b47032de0a2 100644
--- a/src/libcamera/ipc_pipe.cpp
+++ b/src/libcamera/ipc_pipe.cpp
@@ -87,7 +87,7 @@  IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)
 	data_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),
 				     payload.data.end());
 	for (int32_t &fd : payload.fds)
-		fds_.push_back(FileDescriptor(std::move(fd)));
+		fds_.push_back(SharedFD(std::move(fd)));
 }
 
 /**
@@ -112,7 +112,7 @@  IPCUnixSocket::Payload IPCMessage::payload() const
 		       data_.data(), data_.size());
 	}
 
-	for (const FileDescriptor &fd : fds_)
+	for (const SharedFD &fd : fds_)
 		payload.fds.push_back(fd.fd());
 
 	return payload;
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index ffa51a0c65ca..ea8243912a29 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -12,7 +12,7 @@ 
 #include <queue>
 #include <unordered_set>
 
-#include <libcamera/base/file_descriptor.h>
+#include <libcamera/base/shared_fd.h>
 
 #include <libcamera/camera.h>
 #include <libcamera/control_ids.h>
@@ -228,7 +228,7 @@  public:
 
 	/* DMAHEAP allocation helper. */
 	RPi::DmaHeap dmaHeap_;
-	FileDescriptor lsTable_;
+	SharedFD lsTable_;
 
 	std::unique_ptr<DelayedControls> delayedCtrls_;
 	bool sensorMetadata_;
@@ -1365,7 +1365,7 @@  int RPiCameraData::configureIPA(const CameraConfiguration *config)
 		if (!fd.isValid())
 			return -ENOMEM;
 
-		lsTable_ = FileDescriptor(std::move(fd));
+		lsTable_ = SharedFD(std::move(fd));
 
 		/* Allow the IPA to mmap the LS table via the file descriptor. */
 		/*
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 3966483a365f..97d431071def 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -22,8 +22,8 @@ 
 #include <linux/version.h>
 
 #include <libcamera/base/event_notifier.h>
-#include <libcamera/base/file_descriptor.h>
 #include <libcamera/base/log.h>
+#include <libcamera/base/shared_fd.h>
 #include <libcamera/base/unique_fd.h>
 #include <libcamera/base/utils.h>
 
@@ -1325,7 +1325,7 @@  std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
 			return nullptr;
 
 		FrameBuffer::Plane plane;
-		plane.fd = FileDescriptor(std::move(fd));
+		plane.fd = SharedFD(std::move(fd));
 		/*
 		 * V4L2 API doesn't provide dmabuf offset information of plane.
 		 * Set 0 as a placeholder offset.
@@ -1354,7 +1354,7 @@  std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)
 		ASSERT(numPlanes == 1u);
 
 		planes.resize(formatInfo_->numPlanes());
-		const FileDescriptor &fd = planes[0].fd;
+		const SharedFD &fd = planes[0].fd;
 		size_t offset = 0;
 
 		for (auto [i, plane] : utils::enumerate(planes)) {
diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
index 9817fd393d59..586347829845 100644
--- a/src/v4l2/v4l2_camera.h
+++ b/src/v4l2/v4l2_camera.h
@@ -11,8 +11,8 @@ 
 #include <mutex>
 #include <utility>
 
-#include <libcamera/base/file_descriptor.h>
 #include <libcamera/base/semaphore.h>
+#include <libcamera/base/shared_fd.h>
 
 #include <libcamera/camera.h>
 #include <libcamera/framebuffer.h>
diff --git a/test/meson.build b/test/meson.build
index 42dfbc1f8ee9..daaa3862cdd6 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -40,7 +40,6 @@  internal_tests = [
     ['event-dispatcher',                'event-dispatcher.cpp'],
     ['event-thread',                    'event-thread.cpp'],
     ['file',                            'file.cpp'],
-    ['file-descriptor',                 'file-descriptor.cpp'],
     ['flags',                           'flags.cpp'],
     ['hotplug-cameras',                 'hotplug-cameras.cpp'],
     ['mapped-buffer',                   'mapped-buffer.cpp'],
@@ -49,6 +48,7 @@  internal_tests = [
     ['object-delete',                   'object-delete.cpp'],
     ['object-invoke',                   'object-invoke.cpp'],
     ['pixel-format',                    'pixel-format.cpp'],
+    ['shared-fd',                       'shared-fd.cpp'],
     ['signal-threads',                  'signal-threads.cpp'],
     ['threads',                         'threads.cpp'],
     ['timer',                           'timer.cpp'],
diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
index 5fcdcb8eae92..d2050a868b38 100644
--- a/test/serialization/ipa_data_serializer_test.cpp
+++ b/test/serialization/ipa_data_serializer_test.cpp
@@ -53,7 +53,7 @@  template<typename T>
 int testPodSerdes(T in)
 {
 	std::vector<uint8_t> buf;
-	std::vector<FileDescriptor> fds;
+	std::vector<SharedFD> fds;
 
 	std::tie(buf, fds) = IPADataSerializer<T>::serialize(in);
 	T out = IPADataSerializer<T>::deserialize(buf, fds);
@@ -72,7 +72,7 @@  int testVectorSerdes(const std::vector<T> &in,
 		     ControlSerializer *cs = nullptr)
 {
 	std::vector<uint8_t> buf;
-	std::vector<FileDescriptor> fds;
+	std::vector<SharedFD> fds;
 
 	std::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);
 	std::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);
@@ -92,7 +92,7 @@  int testMapSerdes(const std::map<K, V> &in,
 		  ControlSerializer *cs = nullptr)
 {
 	std::vector<uint8_t> buf;
-	std::vector<FileDescriptor> fds;
+	std::vector<SharedFD> fds;
 
 	std::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);
 	std::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);
@@ -198,7 +198,7 @@  private:
 		ControlSerializer cs(ControlSerializer::Role::Proxy);
 
 		/*
-		 * We don't test FileDescriptor serdes because it dup()s, so we
+		 * We don't test SharedFD serdes because it dup()s, so we
 		 * can't check for equality.
 		 */
 		std::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };
@@ -219,7 +219,7 @@  private:
 		};
 
 		std::vector<uint8_t> buf;
-		std::vector<FileDescriptor> fds;
+		std::vector<SharedFD> fds;
 
 		if (testVectorSerdes(vecUint8) != TestPass)
 			return TestFail;
@@ -291,7 +291,7 @@  private:
 			{ { "a", { 1, 2, 3 } }, { "b", { 4, 5, 6 } }, { "c", { 7, 8, 9 } } };
 
 		std::vector<uint8_t> buf;
-		std::vector<FileDescriptor> fds;
+		std::vector<SharedFD> fds;
 
 		if (testMapSerdes(mapUintStr) != TestPass)
 			return TestFail;
@@ -359,7 +359,7 @@  private:
 		std::string strEmpty = "";
 
 		std::vector<uint8_t> buf;
-		std::vector<FileDescriptor> fds;
+		std::vector<SharedFD> fds;
 
 		if (testPodSerdes(u32min) != TestPass)
 			return TestFail;
diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp
similarity index 80%
rename from test/file-descriptor.cpp
rename to test/shared-fd.cpp
index 76badc4c5fad..60e5d0aaa395 100644
--- a/test/file-descriptor.cpp
+++ b/test/shared-fd.cpp
@@ -2,7 +2,7 @@ 
 /*
  * Copyright (C) 2019, Google Inc.
  *
- * file_descriptor.cpp - FileDescriptor test
+ * shared_fd.cpp - SharedFD test
  */
 
 #include <fcntl.h>
@@ -11,7 +11,7 @@ 
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <libcamera/base/file_descriptor.h>
+#include <libcamera/base/shared_fd.h>
 #include <libcamera/base/utils.h>
 
 #include "test.h"
@@ -19,7 +19,7 @@ 
 using namespace libcamera;
 using namespace std;
 
-class FileDescriptorTest : public Test
+class SharedFDTest : public Test
 {
 protected:
 	int init()
@@ -43,8 +43,8 @@  protected:
 
 	int run()
 	{
-		/* Test creating empty FileDescriptor. */
-		desc1_ = new FileDescriptor();
+		/* Test creating empty SharedFD. */
+		desc1_ = new SharedFD();
 
 		if (desc1_->fd() != -1) {
 			std::cout << "Failed fd numerical check (default constructor)"
@@ -56,10 +56,10 @@  protected:
 		desc1_ = nullptr;
 
 		/*
-		 * Test creating FileDescriptor by copying numerical file
+		 * Test creating SharedFD by copying numerical file
 		 * descriptor.
 		 */
-		desc1_ = new FileDescriptor(fd_);
+		desc1_ = new SharedFD(fd_);
 		if (desc1_->fd() == fd_) {
 			std::cout << "Failed fd numerical check (lvalue ref constructor)"
 				  << std::endl;
@@ -84,13 +84,13 @@  protected:
 		}
 
 		/*
-		 * Test creating FileDescriptor by taking ownership of
+		 * Test creating SharedFD by taking ownership of
 		 * numerical file descriptor.
 		 */
 		int dupFd = dup(fd_);
 		int dupFdCopy = dupFd;
 
-		desc1_ = new FileDescriptor(std::move(dupFd));
+		desc1_ = new SharedFD(std::move(dupFd));
 		if (desc1_->fd() != dupFdCopy) {
 			std::cout << "Failed fd numerical check (rvalue ref constructor)"
 				  << std::endl;
@@ -114,9 +114,9 @@  protected:
 			return TestFail;
 		}
 
-		/* Test creating FileDescriptor from other FileDescriptor. */
-		desc1_ = new FileDescriptor(fd_);
-		desc2_ = new FileDescriptor(*desc1_);
+		/* Test creating SharedFD from other SharedFD. */
+		desc1_ = new SharedFD(fd_);
+		desc2_ = new SharedFD(*desc1_);
 
 		if (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {
 			std::cout << "Failed fd numerical check (copy constructor)"
@@ -142,10 +142,10 @@  protected:
 		delete desc2_;
 		desc2_ = nullptr;
 
-		/* Test creating FileDescriptor by taking over other FileDescriptor. */
-		desc1_ = new FileDescriptor(fd_);
+		/* Test creating SharedFD by taking over other SharedFD. */
+		desc1_ = new SharedFD(fd_);
 		fd = desc1_->fd();
-		desc2_ = new FileDescriptor(std::move(*desc1_));
+		desc2_ = new SharedFD(std::move(*desc1_));
 
 		if (desc1_->fd() != -1 || desc2_->fd() != fd) {
 			std::cout << "Failed fd numerical check (move constructor)"
@@ -164,9 +164,9 @@  protected:
 		delete desc2_;
 		desc2_ = nullptr;
 
-		/* Test creating FileDescriptor by copy assignment. */
-		desc1_ = new FileDescriptor();
-		desc2_ = new FileDescriptor(fd_);
+		/* Test creating SharedFD by copy assignment. */
+		desc1_ = new SharedFD();
+		desc2_ = new SharedFD(fd_);
 
 		fd = desc2_->fd();
 		*desc1_ = *desc2_;
@@ -188,9 +188,9 @@  protected:
 		delete desc2_;
 		desc2_ = nullptr;
 
-		/* Test creating FileDescriptor by move assignment. */
-		desc1_ = new FileDescriptor();
-		desc2_ = new FileDescriptor(fd_);
+		/* Test creating SharedFD by move assignment. */
+		desc1_ = new SharedFD();
+		desc2_ = new SharedFD(fd_);
 
 		fd = desc2_->fd();
 		*desc1_ = std::move(*desc2_);
@@ -237,7 +237,7 @@  private:
 
 	int fd_;
 	ino_t inodeNr_;
-	FileDescriptor *desc1_, *desc2_;
+	SharedFD *desc1_, *desc2_;
 };
 
-TEST_REGISTER(FileDescriptorTest)
+TEST_REGISTER(SharedFDTest)
diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
index d856339aa9ee..c37c4941b528 100644
--- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
+++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
@@ -237,7 +237,7 @@  void {{proxy_name}}::recvMessage(const IPCMessage &data)
 void {{proxy_name}}::{{method.mojom_name}}IPC(
 	std::vector<uint8_t>::const_iterator data,
 	size_t dataSize,
-	[[maybe_unused]] const std::vector<FileDescriptor> &fds)
+	[[maybe_unused]] const std::vector<SharedFD> &fds)
 {
 {%- for param in method.parameters %}
 	{{param|name}} {{param.mojom_name}};
diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
index ce396c183d0c..c308dd10c7e5 100644
--- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
+++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
@@ -64,7 +64,7 @@  private:
 	void {{method.mojom_name}}IPC(
 		std::vector<uint8_t>::const_iterator data,
 		size_t dataSize,
-		const std::vector<FileDescriptor> &fds);
+		const std::vector<SharedFD> &fds);
 {% endfor %}
 
 	/* Helper class to invoke async functions in another thread. */
diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
index ebcd2aaaafae..bac826a74c2d 100644
--- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
+++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
@@ -54,7 +54,7 @@ 
 {%- for param in params %}
 	std::vector<uint8_t> {{param.mojom_name}}Buf;
 {%- if param|has_fd %}
-	std::vector<FileDescriptor> {{param.mojom_name}}Fds;
+	std::vector<SharedFD> {{param.mojom_name}}Fds;
 	std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =
 {%- else %}
 	std::tie({{param.mojom_name}}Buf, std::ignore) =
diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl
index b8ef8e7b974e..77bae36fe6b7 100644
--- a/utils/ipc/generators/libcamera_templates/serializer.tmpl
+++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl
@@ -40,7 +40,7 @@ 
 		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
 {%- elif field|is_fd %}
 		std::vector<uint8_t> {{field.mojom_name}};
-		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
+		std::vector<SharedFD> {{field.mojom_name}}Fds;
 		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
 			IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
 		retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
@@ -58,7 +58,7 @@ 
 {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
 		std::vector<uint8_t> {{field.mojom_name}};
 	{%- if field|has_fd %}
-		std::vector<FileDescriptor> {{field.mojom_name}}Fds;
+		std::vector<SharedFD> {{field.mojom_name}}Fds;
 		std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
 	{%- else %}
 		std::tie({{field.mojom_name}}, std::ignore) =
@@ -177,7 +177,7 @@ 
  # \a struct.
  #}
 {%- macro serializer(struct, namespace) %}
-	static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>
+	static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
 	serialize(const {{struct|name_full}} &data,
 {%- if struct|needs_control_serializer %}
 		  ControlSerializer *cs)
@@ -187,7 +187,7 @@ 
 	{
 		std::vector<uint8_t> retData;
 {%- if struct|has_fd %}
-		std::vector<FileDescriptor> retFds;
+		std::vector<SharedFD> retFds;
 {%- endif %}
 {%- for field in struct.fields %}
 {{serializer_field(field, namespace, loop)}}
@@ -210,7 +210,7 @@ 
 {%- macro deserializer_fd(struct, namespace) %}
 	static {{struct|name_full}}
 	deserialize(std::vector<uint8_t> &data,
-		    std::vector<FileDescriptor> &fds,
+		    std::vector<SharedFD> &fds,
 {%- if struct|needs_control_serializer %}
 		    ControlSerializer *cs)
 {%- else %}
@@ -224,8 +224,8 @@ 
 	static {{struct|name_full}}
 	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 		    std::vector<uint8_t>::const_iterator dataEnd,
-		    std::vector<FileDescriptor>::const_iterator fdsBegin,
-		    std::vector<FileDescriptor>::const_iterator fdsEnd,
+		    std::vector<SharedFD>::const_iterator fdsBegin,
+		    std::vector<SharedFD>::const_iterator fdsEnd,
 {%- if struct|needs_control_serializer %}
 		    ControlSerializer *cs)
 {%- else %}
@@ -234,7 +234,7 @@ 
 	{
 		{{struct|name_full}} ret;
 		std::vector<uint8_t>::const_iterator m = dataBegin;
-		std::vector<FileDescriptor>::const_iterator n = fdsBegin;
+		std::vector<SharedFD>::const_iterator n = fdsBegin;
 
 		size_t dataSize = std::distance(dataBegin, dataEnd);
 		[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
@@ -255,7 +255,7 @@ 
 {%- macro deserializer_fd_simple(struct, namespace) %}
 	static {{struct|name_full}}
 	deserialize(std::vector<uint8_t> &data,
-		    [[maybe_unused]] std::vector<FileDescriptor> &fds,
+		    [[maybe_unused]] std::vector<SharedFD> &fds,
 		    ControlSerializer *cs = nullptr)
 	{
 		return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);
@@ -264,8 +264,8 @@ 
 	static {{struct|name_full}}
 	deserialize(std::vector<uint8_t>::const_iterator dataBegin,
 		    std::vector<uint8_t>::const_iterator dataEnd,
-		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,
-		    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,
+		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
+		    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
 		    ControlSerializer *cs = nullptr)
 	{
 		return IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);
diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py
index c609f4e5c062..753bfc734e56 100644
--- a/utils/ipc/generators/mojom_libcamera_generator.py
+++ b/utils/ipc/generators/mojom_libcamera_generator.py
@@ -77,7 +77,7 @@  def GetDefaultValue(element):
     if mojom.IsEnumKind(element.kind):
         return f'static_cast<{element.kind.mojom_name}>(0)'
     if isinstance(element.kind, mojom.Struct) and \
-       element.kind.mojom_name == 'FileDescriptor':
+       element.kind.mojom_name == 'SharedFD':
         return '-1'
     return ''
 
@@ -140,7 +140,7 @@  def HasFd(element):
         types = GetAllTypes(element)
     else:
         types = GetAllTypes(element.kind)
-    return "FileDescriptor" in types or (attrs is not None and "hasFd" in attrs)
+    return "SharedFD" in types or (attrs is not None and "hasFd" in attrs)
 
 def WithDefaultValues(element):
     return [x for x in element if HasDefaultValue(x)]
@@ -221,7 +221,7 @@  def IsEnum(element):
     return mojom.IsEnumKind(element.kind)
 
 def IsFd(element):
-    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "FileDescriptor"
+    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "SharedFD"
 
 def IsMap(element):
     return mojom.IsMapKind(element.kind)