// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_MEMORY_SHARED_MEMORY_HANDLE_H_
#define BASE_MEMORY_SHARED_MEMORY_HANDLE_H_

#include <stddef.h>

#include "base/unguessable_token.h"
#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#include "base/process/process_handle.h"
#elif defined(OS_MACOSX) && !defined(OS_IOS)
#include <mach/mach.h>
#include "base/base_export.h"
#include "base/file_descriptor_posix.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#elif defined(OS_POSIX)
#include <sys/types.h>
#include "base/file_descriptor_posix.h"
#endif

namespace base {

// SharedMemoryHandle is the smallest possible IPC-transportable "reference" to
// a shared memory OS resource. A "reference" can be consumed exactly once [by
// base::SharedMemory] to map the shared memory OS resource into the virtual
// address space of the current process.
// TODO(erikchen): This class should have strong ownership semantics to prevent
// leaks of the underlying OS resource. https://crbug.com/640840.
class BASE_EXPORT SharedMemoryHandle {
 public:
  // The default constructor returns an invalid SharedMemoryHandle.
  SharedMemoryHandle();

  // Standard copy constructor. The new instance shares the underlying OS
  // primitives.
  SharedMemoryHandle(const SharedMemoryHandle& handle);

  // Standard assignment operator. The updated instance shares the underlying
  // OS primitives.
  SharedMemoryHandle& operator=(const SharedMemoryHandle& handle);

  // Closes the underlying OS resource.
  // The fact that this method needs to be "const" is an artifact of the
  // original interface for base::SharedMemory::CloseHandle.
  // TODO(erikchen): This doesn't clear the underlying reference, which seems
  // like a bug, but is how this class has always worked. Fix this:
  // https://crbug.com/716072.
  void Close() const;

  // Whether ownership of the underlying OS resource is implicitly passed to
  // the IPC subsystem during serialization.
  void SetOwnershipPassesToIPC(bool ownership_passes);
  bool OwnershipPassesToIPC() const;

  // Whether the underlying OS resource is valid.
  bool IsValid() const;

  // Duplicates the underlying OS resource. Using the return value as a
  // parameter to an IPC message will cause the IPC subsystem to consume the OS
  // resource.
  SharedMemoryHandle Duplicate() const;

  // Uniques identifies the shared memory region that the underlying OS resource
  // points to. Multiple SharedMemoryHandles that point to the same shared
  // memory region will have the same GUID. Preserved across IPC.
  base::UnguessableToken GetGUID() const;

  // Returns the size of the memory region that SharedMemoryHandle points to.
  size_t GetSize() const;

#if defined(OS_MACOSX) && !defined(OS_IOS)
  enum Type {
    // The SharedMemoryHandle is backed by a POSIX fd.
    POSIX,
    // The SharedMemoryHandle is backed by the Mach primitive "memory object".
    MACH,
  };

  // Constructs a SharedMemoryHandle backed by the components of a
  // FileDescriptor. The newly created instance has the same ownership semantics
  // as base::FileDescriptor. This typically means that the SharedMemoryHandle
  // takes ownership of the |fd| if |auto_close| is true. Unfortunately, it's
  // common for existing code to make shallow copies of SharedMemoryHandle, and
  // the one that is finally passed into a base::SharedMemory is the one that
  // "consumes" the fd.
  // |guid| uniquely identifies the shared memory region pointed to by the
  // underlying OS resource. If |file_descriptor| is associated with another
  // SharedMemoryHandle, the caller must pass the |guid| of that
  // SharedMemoryHandle. Otherwise, the caller should generate a new
  // UnguessableToken.
  // |size| refers to the size of the memory region pointed to by
  // file_descriptor.fd. Passing the wrong |size| has no immediate consequence,
  // but may cause errors when trying to map the SharedMemoryHandle at a later
  // point in time.
  SharedMemoryHandle(const base::FileDescriptor& file_descriptor,
                     size_t size,
                     const base::UnguessableToken& guid);

  // Makes a Mach-based SharedMemoryHandle of the given size. On error,
  // subsequent calls to IsValid() return false.
  // Passing the wrong |size| has no immediate consequence, but may cause errors
  // when trying to map the SharedMemoryHandle at a later point in time.
  SharedMemoryHandle(mach_vm_size_t size, const base::UnguessableToken& guid);

  // Makes a Mach-based SharedMemoryHandle from |memory_object|, a named entry
  // in the current task. The memory region has size |size|.
  // Passing the wrong |size| has no immediate consequence, but may cause errors
  // when trying to map the SharedMemoryHandle at a later point in time.
  SharedMemoryHandle(mach_port_t memory_object,
                     mach_vm_size_t size,
                     const base::UnguessableToken& guid);

  // Exposed so that the SharedMemoryHandle can be transported between
  // processes.
  mach_port_t GetMemoryObject() const;

  // The SharedMemoryHandle must be valid.
  // Returns whether the SharedMemoryHandle was successfully mapped into memory.
  // On success, |memory| is an output variable that contains the start of the
  // mapped memory.
  bool MapAt(off_t offset, size_t bytes, void** memory, bool read_only);
#elif defined(OS_FUCHSIA)
  // Takes implicit ownership of |h|.
  // |guid| uniquely identifies the shared memory region pointed to by the
  // underlying OS resource. If the mx_handle_t is associated with another
  // SharedMemoryHandle, the caller must pass the |guid| of that
  // SharedMemoryHandle. Otherwise, the caller should generate a new
  // UnguessableToken.
  // Passing the wrong |size| has no immediate consequence, but may cause errors
  // when trying to map the SharedMemoryHandle at a later point in time.
  SharedMemoryHandle(mx_handle_t h,
                     size_t size,
                     const base::UnguessableToken& guid);
  mx_handle_t GetHandle() const;
#elif defined(OS_WIN)
  // Takes implicit ownership of |h|.
  // |guid| uniquely identifies the shared memory region pointed to by the
  // underlying OS resource. If the HANDLE is associated with another
  // SharedMemoryHandle, the caller must pass the |guid| of that
  // SharedMemoryHandle. Otherwise, the caller should generate a new
  // UnguessableToken.
  // Passing the wrong |size| has no immediate consequence, but may cause errors
  // when trying to map the SharedMemoryHandle at a later point in time.
  SharedMemoryHandle(HANDLE h, size_t size, const base::UnguessableToken& guid);
  HANDLE GetHandle() const;
#else
  // |guid| uniquely identifies the shared memory region pointed to by the
  // underlying OS resource. If |file_descriptor| is associated with another
  // SharedMemoryHandle, the caller must pass the |guid| of that
  // SharedMemoryHandle. Otherwise, the caller should generate a new
  // UnguessableToken.
  // Passing the wrong |size| has no immediate consequence, but may cause errors
  // when trying to map the SharedMemoryHandle at a later point in time.
  SharedMemoryHandle(const base::FileDescriptor& file_descriptor,
                     size_t size,
                     const base::UnguessableToken& guid);

  // Creates a SharedMemoryHandle from an |fd| supplied from an external
  // service.
  // Passing the wrong |size| has no immediate consequence, but may cause errors
  // when trying to map the SharedMemoryHandle at a later point in time.
  static SharedMemoryHandle ImportHandle(int fd, size_t size);

  // Returns the underlying OS resource.
  int GetHandle() const;

  // Invalidates [but doesn't close] the underlying OS resource. This will leak
  // unless the caller is careful.
  int Release();
#endif

 private:
#if defined(OS_MACOSX) && !defined(OS_IOS)
  friend class SharedMemory;

  Type type_ = MACH;

  // Each instance of a SharedMemoryHandle is backed either by a POSIX fd or a
  // mach port. |type_| determines the backing member.
  union {
    FileDescriptor file_descriptor_;

    struct {
      mach_port_t memory_object_ = MACH_PORT_NULL;

      // Whether passing this object as a parameter to an IPC message passes
      // ownership of |memory_object_| to the IPC stack. This is meant to mimic
      // the behavior of the |auto_close| parameter of FileDescriptor.
      // Defaults to |false|.
      bool ownership_passes_to_ipc_ = false;
    };
  };
#elif defined(OS_FUCHSIA)
  mx_handle_t handle_ = MX_HANDLE_INVALID;
  bool ownership_passes_to_ipc_ = false;
#elif defined(OS_WIN)
  HANDLE handle_ = nullptr;

  // Whether passing this object as a parameter to an IPC message passes
  // ownership of |handle_| to the IPC stack. This is meant to mimic the
  // behavior of the |auto_close| parameter of FileDescriptor. This member only
  // affects attachment-brokered SharedMemoryHandles.
  // Defaults to |false|.
  bool ownership_passes_to_ipc_ = false;
#else
  FileDescriptor file_descriptor_;
#endif

  base::UnguessableToken guid_;

  // The size of the region referenced by the SharedMemoryHandle.
  size_t size_ = 0;
};

}  // namespace base

#endif  // BASE_MEMORY_SHARED_MEMORY_HANDLE_H_
