| // Copyright 2019 The Fuchsia Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <lib/fidl/internal.h> | 
 |  | 
 | #ifdef __Fuchsia__ | 
 | #include <zircon/syscalls.h> | 
 | #endif | 
 |  | 
 | // Coding tables for primitives are predefined and interned here. | 
 | // This file must be a .c to guarantee that these types are stored directly in | 
 | // .rodata, rather than requiring global ctors to have been run (fxbug.dev/39978). | 
 | const struct FidlCodedPrimitive fidl_internal_kBoolTable = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Bool}; | 
 | const struct FidlCodedPrimitive fidl_internal_kInt8Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int8}; | 
 | const struct FidlCodedPrimitive fidl_internal_kInt16Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int16}; | 
 | const struct FidlCodedPrimitive fidl_internal_kInt32Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int32}; | 
 | const struct FidlCodedPrimitive fidl_internal_kInt64Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int64}; | 
 | const struct FidlCodedPrimitive fidl_internal_kUint8Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint8}; | 
 | const struct FidlCodedPrimitive fidl_internal_kUint16Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint16}; | 
 | const struct FidlCodedPrimitive fidl_internal_kUint32Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint32}; | 
 | const struct FidlCodedPrimitive fidl_internal_kUint64Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint64}; | 
 | const struct FidlCodedPrimitive fidl_internal_kFloat32Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Float32}; | 
 | const struct FidlCodedPrimitive fidl_internal_kFloat64Table = { | 
 |     .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Float64}; | 
 |  | 
 | zx_rights_t subtract_rights(zx_rights_t minuend, zx_rights_t subtrahend) { | 
 |   return minuend & ~subtrahend; | 
 | } | 
 |  | 
 | #ifdef __Fuchsia__ | 
 | zx_status_t FidlEnsureHandleRights(zx_handle_t* handle_ptr, zx_obj_type_t actual_type, | 
 |                                    zx_obj_type_t actual_rights, zx_obj_type_t required_object_type, | 
 |                                    zx_rights_t required_rights, const char** error) { | 
 |   // Note: objects returned from the kernel should never have type | 
 |   // ZX_OBJ_TYPE_NONE, however this is used for backwards compatibility in | 
 |   // some places. | 
 |   if (unlikely(required_object_type != actual_type && required_object_type != ZX_OBJ_TYPE_NONE && | 
 |                actual_type != ZX_OBJ_TYPE_NONE)) { | 
 |     zx_handle_close(*handle_ptr); | 
 |     *handle_ptr = ZX_HANDLE_INVALID; | 
 |     if (error) { | 
 |       *error = "object type does not match expected type"; | 
 |     } | 
 |     return ZX_ERR_INVALID_ARGS; | 
 |   } | 
 |  | 
 |   // Special case: ZX_HANDLE_SAME_RIGHTS allows all handles through unchanged. | 
 |   // Note: objects returned from the kernel should never have rights | 
 |   // ZX_RIGHT_SAME_RIGHTS, however this is used for backwards compatibility | 
 |   // in some places. | 
 |   if (required_rights == ZX_RIGHT_SAME_RIGHTS || actual_rights == ZX_RIGHT_SAME_RIGHTS) { | 
 |     return ZX_OK; | 
 |   } | 
 |  | 
 |   // Check that |actual_rights| contain all of the |required_rights|. | 
 |   if (unlikely(subtract_rights(required_rights, actual_rights) != 0)) { | 
 |     zx_handle_close(*handle_ptr); | 
 |     *handle_ptr = ZX_HANDLE_INVALID; | 
 |     if (error) { | 
 |       *error = "missing required rights"; | 
 |     } | 
 |     return ZX_ERR_INVALID_ARGS; | 
 |   } | 
 |  | 
 |   // Check if |actual_rights| has more rights than |required_rights|. | 
 |   // If so, the rights need to be reduced. | 
 |   if (unlikely(subtract_rights(actual_rights, required_rights))) { | 
 |     zx_status_t status = zx_handle_replace(*handle_ptr, required_rights, handle_ptr); | 
 |     if (unlikely(status != ZX_OK)) { | 
 |       if (error) | 
 |         *error = "zx_handle_replace failed"; | 
 |       return status; | 
 |     } | 
 |   } | 
 |   return ZX_OK; | 
 | } | 
 | #else | 
 | zx_status_t FidlEnsureHandleRights(zx_handle_t* handle_ptr, zx_obj_type_t actual_type, | 
 |                                    zx_obj_type_t actual_rights, zx_obj_type_t required_object_type, | 
 |                                    zx_rights_t required_rights, const char** error) { | 
 |   ZX_PANIC("handles only supported on fuchsia"); | 
 | } | 
 | #endif | 
 |  | 
 | zx_status_t FidlHandleDispositionsToHandleInfos(zx_handle_disposition_t* handle_dispositions, | 
 |                                                 zx_handle_info_t* handle_infos, | 
 |                                                 uint32_t num_handles) { | 
 |   for (size_t i = 0; i < num_handles; i++) { | 
 |     zx_handle_disposition_t* handle_disposition = &handle_dispositions[i]; | 
 |     if (handle_disposition->operation != ZX_HANDLE_OP_MOVE) { | 
 |       FidlHandleDispositionCloseMany(handle_dispositions, num_handles); | 
 |       FidlHandleInfoCloseMany(handle_infos, i); | 
 |       return ZX_ERR_INVALID_ARGS; | 
 |     } | 
 |     if (handle_disposition->result != ZX_OK) { | 
 |       FidlHandleDispositionCloseMany(handle_dispositions, num_handles); | 
 |       FidlHandleInfoCloseMany(handle_infos, i); | 
 |       return ZX_ERR_INVALID_ARGS; | 
 |     } | 
 | #ifdef __Fuchsia__ | 
 |     zx_info_handle_basic_t info; | 
 |     zx_status_t status = zx_object_get_info(handle_disposition->handle, ZX_INFO_HANDLE_BASIC, &info, | 
 |                                             sizeof(info), NULL, NULL); | 
 |     if (status != ZX_OK) { | 
 |       FidlHandleDispositionCloseMany(handle_dispositions, num_handles); | 
 |       FidlHandleInfoCloseMany(handle_infos, i); | 
 |       return status; | 
 |     } | 
 |  | 
 |     zx_handle_t handle = handle_disposition->handle; | 
 |     handle_disposition->handle = ZX_HANDLE_INVALID; | 
 |     status = FidlEnsureHandleRights(&handle, info.type, info.rights, handle_disposition->type, | 
 |                                     handle_disposition->rights, NULL); | 
 |     if (status != ZX_OK) { | 
 |       FidlHandleDispositionCloseMany(handle_dispositions, num_handles); | 
 |       FidlHandleInfoCloseMany(handle_infos, i); | 
 |       return status; | 
 |     } | 
 |     handle_infos[i].handle = handle; | 
 |     handle_infos[i].type = info.type; | 
 |     handle_infos[i].rights = info.rights; | 
 | #else | 
 |     ZX_PANIC("zx_object_get_info unsupported on host"); | 
 | #endif | 
 |   } | 
 |   return ZX_OK; | 
 | } |