// Copyright 2016 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.

// This file contains types/functions and constants for platform handle wrapping
// and unwrapping APIs.
//
// Note: This header should be compilable as C.

#ifndef MOJO_PUBLIC_C_SYSTEM_PLATFORM_HANDLE_H_
#define MOJO_PUBLIC_C_SYSTEM_PLATFORM_HANDLE_H_

#include <stdint.h>

#include "mojo/public/c/system/system_export.h"
#include "mojo/public/c/system/types.h"

#ifdef __cplusplus
extern "C" {
#endif

// The type of handle value contained in a |MojoPlatformHandle| structure.
typedef uint32_t MojoPlatformHandleType;

// An invalid handle value. Other contents of the |MojoPlatformHandle| are
// ignored.
#define MOJO_PLATFORM_HANDLE_TYPE_INVALID ((MojoPlatformHandleType)0)

// The |MojoPlatformHandle| value represents a POSIX file descriptor. Only
// usable on POSIX host systems (e.g. Android, Linux, Chrome OS, Mac).
#define MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR ((MojoPlatformHandleType)1)

// The |MojoPlatformHandle| value represents a Mach port right (e.g. a value
// opaquely of type |mach_port_t|). Only usable on Mac OS X hosts.
#define MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT ((MojoPlatformHandleType)2)

// The |MojoPlatformHandle| value represents a Windows HANDLE value. Only usable
// on Windows hosts.
#define MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE ((MojoPlatformHandleType)3)

// The |MojoPlatformHandle| value represents a Fuchsia system handle. Only
// usable on Fuchsia hosts.
#define MOJO_PLATFORM_HANDLE_TYPE_FUCHSIA_HANDLE ((MojoPlatformHandleType)4)

// |MojoPlatformHandle|: A handle to a native platform object.
//
//     |uint32_t struct_size|: The size of this structure. Used for versioning
//         to allow for future extensions.
//
//     |MojoPlatformHandleType type|: The type of handle stored in |value|.
//
//     |uint64_t value|: The value of this handle. Ignored if |type| is
//         MOJO_PLATFORM_HANDLE_TYPE_INVALID. Otherwise the meaning of this
//         value depends on the value of |type|.
//

// Represents a native platform handle value for coersion to or from a wrapping
// Mojo handle.
struct MOJO_ALIGNAS(8) MojoPlatformHandle {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // The type of platform handle represented by |value|.
  MojoPlatformHandleType type;

  // An opaque representation of the native platform handle. Interpretation and
  // treatment of this value by Mojo depends on the value of |type|.
  uint64_t value;
};
MOJO_STATIC_ASSERT(sizeof(MojoPlatformHandle) == 16,
                   "MojoPlatformHandle has wrong size");

// Flags passed to |MojoWrapPlatformHandle()| via
// |MojoWrapPlatformHandleOptions|.
typedef uint32_t MojoWrapPlatformHandleFlags;

// No flags. Default behavior.
#define MOJO_WRAP_PLATFORM_HANDLE_FLAG_NONE ((MojoWrapPlatformHandleFlags)0)

// Options passed to |MojoWrapPlatformHandle()|.
struct MOJO_ALIGNAS(8) MojoWrapPlatformHandleOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoWrapPlatformHandleFlags|.
  MojoWrapPlatformHandleFlags flags;
};
MOJO_STATIC_ASSERT(sizeof(MojoWrapPlatformHandleOptions) == 8,
                   "MojoWrapPlatformHandleOptions has wrong size");

// Flags passed to |MojoUnwrapPlatformHandle()| via
// |MojoUnwrapPlatformHandleOptions|.
typedef uint32_t MojoUnwrapPlatformHandleFlags;

// No flags. Default behavior.
#define MOJO_UNWRAP_PLATFORM_HANDLE_FLAG_NONE ((MojoUnwrapPlatformHandleFlags)0)

// Options passed to |MojoUnwrapPlatformHandle()|.
struct MOJO_ALIGNAS(8) MojoUnwrapPlatformHandleOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoUnwrapPlatformHandleFlags|.
  MojoUnwrapPlatformHandleFlags flags;
};
MOJO_STATIC_ASSERT(sizeof(MojoUnwrapPlatformHandleOptions) == 8,
                   "MojoUnwrapPlatformHandleOptions has wrong size");

// A GUID value used to identify the shared memory region backing a Mojo shared
// buffer handle.
struct MOJO_ALIGNAS(8) MojoSharedBufferGuid {
  uint64_t high;
  uint64_t low;
};

// The access type of shared memory region wrapped by a Mojo shared buffer
// handle. See values defined below.
typedef uint32_t MojoPlatformSharedMemoryRegionAccessMode;

// The region is read-only, meaning there is at most one writable mapped handle
// to the region somewhere, and there are any number of handles (including this
// one) which can only be mapped read-only.
//
// WARNING: See notes in |MojoWrapPlatformSharedMemoryRegion()| about the
// meaning and usage of different access modes. This CANNOT be used to change
// a buffer's access mode; it is merely an informational value to allow Mojo
// to retain consistency between wrapping and unwrapping of buffer handles.
#define MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_READ_ONLY \
  ((MojoPlatformSharedMemoryRegionAccessMode)0)

// The region is writable, meaning there is exactly one handle to the region and
// it is mappable read/writable.
//
// WARNING: See notes in |MojoWrapPlatformSharedMemoryRegion()| about the
// meaning and usage of different access modes. This CANNOT be used to change
// a buffer's access mode; it is merely an informational value to allow Mojo
// to retain consistency between wrapping and unwrapping of buffer handles.
#define MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE \
  ((MojoPlatformSharedMemoryRegionAccessMode)1)

// The region is unsafe, meaning any number of read/writable handles may refer
// to it.
//
// WARNING: See notes in |MojoWrapPlatformSharedMemoryRegion()| about the
// meaning and usage of different access modes. This CANNOT be used to change
// a buffer's access mode; it is merely an informational value to allow Mojo
// to retain consistency between wrapping and unwrapping of buffer handles.
#define MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_UNSAFE \
  ((MojoPlatformSharedMemoryRegionAccessMode)2)

// Flags passed to |MojoWrapPlatformSharedMemoryRegion()| via
// |MojoWrapPlatformSharedMemoryRegionOptions|.
typedef uint32_t MojoWrapPlatformSharedMemoryRegionFlags;

// No flags. Default behavior.
#define MOJO_WRAP_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE \
  ((MojoWrapPlatformSharedMemoryRegionFlags)0)

// Options passed to |MojoWrapPlatformSharedMemoryRegion()|.
struct MOJO_ALIGNAS(8) MojoWrapPlatformSharedMemoryRegionOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoWrapPlatformSharedMemoryRegionFlags|.
  MojoWrapPlatformSharedMemoryRegionFlags flags;
};
MOJO_STATIC_ASSERT(sizeof(MojoWrapPlatformSharedMemoryRegionOptions) == 8,
                   "MojoWrapPlatformSharedMemoryRegionOptions has wrong size");

// Flags passed to |MojoUnwrapPlatformSharedMemoryRegion()| via
// |MojoUnwrapPlatformSharedMemoryRegionOptions|.
typedef uint32_t MojoUnwrapPlatformSharedMemoryRegionFlags;

// No flags. Default behavior.
#define MOJO_UNWRAP_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE \
  ((MojoUnwrapPlatformSharedMemoryRegionFlags)0)

// Options passed to |MojoUnwrapPlatformSharedMemoryRegion()|.
struct MOJO_ALIGNAS(8) MojoUnwrapPlatformSharedMemoryRegionOptions {
  // The size of this structure, used for versioning.
  uint32_t struct_size;

  // See |MojoUnwrapPlatformSharedMemoryRegionFlags|.
  MojoUnwrapPlatformSharedMemoryRegionFlags flags;
};
MOJO_STATIC_ASSERT(
    sizeof(MojoUnwrapPlatformSharedMemoryRegionOptions) == 8,
    "MojoUnwrapPlatformSharedMemoryRegionOptions has wrong size");

// Wraps a native platform handle as a Mojo handle which can be transferred
// over a message pipe. Takes ownership of the underlying native platform
// object. i.e. if you wrap a POSIX file descriptor or Windows HANDLE and then
// call |MojoClose()| on the resulting MojoHandle, the underlying file
// descriptor or HANDLE will be closed.
//
// |platform_handle|: The platform handle to wrap.
//
// |options| may be null.
//
// Returns:
//     |MOJO_RESULT_OK| if the handle was successfully wrapped. In this case
//         |*mojo_handle| contains the Mojo handle of the wrapped object.
//     |MOJO_RESULT_RESOURCE_EXHAUSTED| if the system is out of handles.
//     |MOJO_RESULT_INVALID_ARGUMENT| if |platform_handle| was not a valid
//          platform handle.
//
// NOTE: It is not always possible to detect if |platform_handle| is valid,
// particularly when |platform_handle->type| is valid but
// |platform_handle->value| does not represent a valid platform object.
MOJO_SYSTEM_EXPORT MojoResult
MojoWrapPlatformHandle(const struct MojoPlatformHandle* platform_handle,
                       const struct MojoWrapPlatformHandleOptions* options,
                       MojoHandle* mojo_handle);

// Unwraps a native platform handle from a Mojo handle. If this call succeeds,
// ownership of the underlying platform object is assumed by the caller. The
// The Mojo handle is always closed regardless of success or failure.
//
// |mojo_handle|: The Mojo handle from which to unwrap the native platform
//     handle.
//
// |options| may be null.
//
// Returns:
//     |MOJO_RESULT_OK| if the handle was successfully unwrapped. In this case
//         |*platform_handle| contains the unwrapped platform handle.
//     |MOJO_RESULT_INVALID_ARGUMENT| if |mojo_handle| was not a valid Mojo
//         handle wrapping a platform handle.
MOJO_SYSTEM_EXPORT MojoResult
MojoUnwrapPlatformHandle(MojoHandle mojo_handle,
                         const struct MojoUnwrapPlatformHandleOptions* options,
                         struct MojoPlatformHandle* platform_handle);

// Wraps a native platform shared memory region with a Mojo shared buffer handle
// which can be used exactly like a shared buffer handle created by
// |MojoCreateSharedBuffer()| or |MojoDuplicateBufferHandle()|.
//
// Takes ownership of the native platform shared buffer handle(s).
//
// |platform_handles|: The platform handle(s) to wrap. Must be one or more
//     native handles representing a shared memory region. On POSIX systems
//     with |access_mode| set to
//     |MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE| this must have
//     two handles, with the second one being a handle opened for read-only
//     mapping. For all other platforms and all other access modes, there should
//     be only one handle.
// |num_platform_handles|: The number of platform handles given in
//     |platform_handles|. See note above.
// |num_bytes|: The size of the shared memory region in bytes.
// |access_mode|: The current access mode of the shared memory region.
// |options|: Options to control behavior. May be null.
//
// !!WARNING!!: |access_mode| DOES NOT CONTROL ACCESS TO THE REGION. It is an
// informational field used by Mojo to ensure end-to-end consistency when
// wrapping and unwrapping region handles. The caller is responsible for
// ensuring that wrapped handles are already subject to the access constraints
// conveyed by |access_mode|.
//
// Returns:
//     |MOJO_RESULT_OK| if the handle was successfully wrapped. In this case
//         |*mojo_handle| contains a Mojo shared buffer handle.
//     |MOJO_RESULT_INVALID_ARGUMENT| if |platform_handle| was not a valid
//         platform shared buffer handle.
MOJO_SYSTEM_EXPORT MojoResult MojoWrapPlatformSharedMemoryRegion(
    const struct MojoPlatformHandle* platform_handles,
    uint32_t num_platform_handles,
    uint64_t num_bytes,
    const struct MojoSharedBufferGuid* guid,
    MojoPlatformSharedMemoryRegionAccessMode access_mode,
    const struct MojoWrapPlatformSharedMemoryRegionOptions* options,
    MojoHandle* mojo_handle);

// Unwraps a native platform shared memory region from a Mojo shared buffer
// handle. If this call succeeds, ownership of the underlying shared buffer
// object is assumed by the caller.
//
// The Mojo handle is always closed regardless of success or failure.
//
// |mojo_handle|: The Mojo shared buffer handle to unwrap.
//
// On input, |*num_platform_handles| must be non-zero, and |platform_handles|
// should point to enough memory to hold at least that many |MojoPlatformHandle|
// values. Each element in |platform_handles| must have also initialized
// |struct_size| to the caller's known |sizeof(MojoPlatformHandle)|.
//
// |platform_handles|, |num_platform_handles|, |num_bytes| and |access_mode| are
// all used to receive output values and MUST always be non-null.
//
// |options| may be null.
//
// NOTE: On POSIX systems when unwrapping regions with the
// |MOJO_PLATFORM_SHARED_MEMORY_REGION_ACCESS_MODE_WRITABLE| access mode,
// this will always unwrap two platform handles, with the first one being a
// POSIX file descriptor which can be mapped to writable memory, and the second
// one being a POSIX file descriptor which can only be mapped read-only. For all
// other access modes and all other platforms, this always unwraps to a single
// platform handle.
//
// Returns:
//    |MOJO_RESULT_OK| if the handle was successfully unwrapped. In this case
//        |*platform_handles| contains one or more platform handles to represent
//        the unwrapped region, |*num_platform_handles| contains the number of
//        platform handles actually stored in |platform_handles| on output,
//        |*num_bytes| contains the size of the shared buffer object, and
//        |*access_mode| indicates the access mode of the region.
//    |MOJO_RESULT_INVALID_ARGUMENT| if |mojo_handle| is not a valid Mojo
//        shared buffer handle or |*num_platform_handles| is not large enough
//        to hold all the handles that would have been unwrapped on success.
MOJO_SYSTEM_EXPORT MojoResult MojoUnwrapPlatformSharedMemoryRegion(
    MojoHandle mojo_handle,
    const struct MojoUnwrapPlatformSharedMemoryRegionOptions* options,
    struct MojoPlatformHandle* platform_handles,
    uint32_t* num_platform_handles,
    uint64_t* num_bytes,
    struct MojoSharedBufferGuid* guid,
    MojoPlatformSharedMemoryRegionAccessMode* access_mode);

#ifdef __cplusplus
}  // extern "C"
#endif

#endif  // MOJO_PUBLIC_C_SYSTEM_PLATFORM_HANDLE_H_