// Copyright 2017 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 #include #include #include #include #include #include #include #ifdef __Fuchsia__ #include #include #endif #include #include #include #include "fvm/fvm.h" #define ZXDEBUG 0 namespace { // Return true if g1 is greater than or equal to g2. // Safe against integer overflow. bool generation_ge(uint64_t g1, uint64_t g2) { if (g1 == UINT64_MAX && g2 == 0) { return false; } else if (g1 == 0 && g2 == UINT64_MAX) { return true; } return g1 >= g2; } // Validate the metadata's hash value. // Returns 'true' if it matches, 'false' otherwise. bool fvm_check_hash(const void* metadata, size_t metadata_size) { ZX_DEBUG_ASSERT(metadata_size >= sizeof(fvm::fvm_t)); const fvm::fvm_t* header = static_cast(metadata); const void* metadata_after_hash = reinterpret_cast(header->hash + sizeof(header->hash)); uint8_t empty_hash[sizeof(header->hash)]; memset(empty_hash, 0, sizeof(empty_hash)); digest::Digest digest; digest.Init(); digest.Update(metadata, offsetof(fvm::fvm_t, hash)); digest.Update(empty_hash, sizeof(empty_hash)); digest.Update(metadata_after_hash, metadata_size - (offsetof(fvm::fvm_t, hash) + sizeof(header->hash))); digest.Final(); return digest == header->hash; } } // namespace anonymous #ifdef __cplusplus uint64_t fvm::slice_entry::Vpart() const { uint64_t result = data & VPART_MASK; ZX_DEBUG_ASSERT(result < VPART_MAX); return result; } void fvm::slice_entry::SetVpart(uint64_t vpart) { ZX_DEBUG_ASSERT(vpart < VPART_MAX); data = (data & ~VPART_MASK) | (vpart & VPART_MASK); } uint64_t fvm::slice_entry::Vslice() const { uint64_t result = (data & VSLICE_MASK) >> VPART_BITS; ZX_DEBUG_ASSERT(result < VSLICE_MAX); return result; } void fvm::slice_entry::SetVslice(uint64_t vslice) { ZX_DEBUG_ASSERT(vslice < VSLICE_MAX); data = (data & ~VSLICE_MASK) | ((vslice & VSLICE_MAX) << VPART_BITS); } #endif // __cplusplus void fvm_update_hash(void* metadata, size_t metadata_size) { fvm::fvm_t* header = static_cast(metadata); memset(header->hash, 0, sizeof(header->hash)); digest::Digest digest; const uint8_t* hash = digest.Hash(metadata, metadata_size); memcpy(header->hash, hash, sizeof(header->hash)); } zx_status_t fvm_validate_header(const void* metadata, const void* backup, size_t metadata_size, const void** out) { const fvm::fvm_t* primary_header = static_cast(metadata); const fvm::fvm_t* backup_header = static_cast(backup); bool primary_valid = fvm_check_hash(metadata, metadata_size); bool backup_valid = fvm_check_hash(backup, metadata_size); // Decide if we should use the primary or the backup copy of metadata // for reading. bool use_primary; if (!primary_valid && !backup_valid) { return ZX_ERR_BAD_STATE; } else if (primary_valid && !backup_valid) { use_primary = true; } else if (!primary_valid && backup_valid) { use_primary = false; } else { use_primary = generation_ge(primary_header->generation, backup_header->generation); } const fvm::fvm_t* header = use_primary ? primary_header : backup_header; if (header->magic != FVM_MAGIC) { fprintf(stderr, "fvm: Bad magic\n"); return ZX_ERR_BAD_STATE; } if (header->version > FVM_VERSION) { fprintf(stderr, "fvm: Header Version does not match fvm driver\n"); return ZX_ERR_BAD_STATE; } // TODO(smklein): Additional validation.... if (out) { *out = use_primary ? metadata : backup; } return ZX_OK; }