/* * Copyright (c) 2020 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/volumeGeometry.c#10 $ */ #include "volumeGeometry.h" #include "buffer.h" #include "logger.h" #include "memoryAlloc.h" #include "numeric.h" #include "permassert.h" #include "constants.h" #include "header.h" #include "physicalLayer.h" #include "releaseVersions.h" #include "statusCodes.h" #include "types.h" enum { GEOMETRY_BLOCK_LOCATION = 0, MAGIC_NUMBER_SIZE = 8, }; typedef struct { char magicNumber[MAGIC_NUMBER_SIZE]; Header header; VolumeGeometry geometry; CRC32Checksum checksum; } __attribute__((packed)) GeometryBlock; static const Header GEOMETRY_BLOCK_HEADER_4_0 = { .id = GEOMETRY_BLOCK, .version = { .majorVersion = 4, .minorVersion = 0, }, // Note: this size isn't just the payload size following the header, like it // is everywhere else in VDO. .size = sizeof(GeometryBlock), }; static const byte MAGIC_NUMBER[MAGIC_NUMBER_SIZE + 1] = "dmvdo001"; static const ReleaseVersionNumber COMPATIBLE_RELEASE_VERSIONS[] = { MAGNESIUM_RELEASE_VERSION_NUMBER, }; /** * Determine whether the supplied release version can be understood by * the VDO code. * * @param version The release version number to check * * @return True if the given version can be loaded. **/ static inline bool isLoadableReleaseVersion(ReleaseVersionNumber version) { if (version == CURRENT_RELEASE_VERSION_NUMBER) { return true; } for (unsigned int i = 0; i < COUNT_OF(COMPATIBLE_RELEASE_VERSIONS); i++) { if (version == COMPATIBLE_RELEASE_VERSIONS[i]) { return true; } } return false; } /** * Decode the on-disk representation of an index configuration from a buffer. * * @param buffer A buffer positioned at the start of the encoding * @param config The structure to receive the decoded fields * * @return UDS_SUCCESS or an error **/ static int decodeIndexConfig(Buffer *buffer, IndexConfig *config) { uint32_t mem; int result = getUInt32LEFromBuffer(buffer, &mem); if (result != VDO_SUCCESS) { return result; } uint32_t checkpointFrequency; result = getUInt32LEFromBuffer(buffer, &checkpointFrequency); if (result != VDO_SUCCESS) { return result; } bool sparse; result = getBoolean(buffer, &sparse); if (result != VDO_SUCCESS) { return result; } *config = (IndexConfig) { .mem = mem, .checkpointFrequency = checkpointFrequency, .sparse = sparse, }; return VDO_SUCCESS; } /** * Encode the on-disk representation of an index configuration into a buffer. * * @param config The index configuration to encode * @param buffer A buffer positioned at the start of the encoding * * @return UDS_SUCCESS or an error **/ static int encodeIndexConfig(const IndexConfig *config, Buffer *buffer) { int result = putUInt32LEIntoBuffer(buffer, config->mem); if (result != VDO_SUCCESS) { return result; } result = putUInt32LEIntoBuffer(buffer, config->checkpointFrequency); if (result != VDO_SUCCESS) { return result; } return putBoolean(buffer, config->sparse); } /** * Decode the on-disk representation of a volume region from a buffer. * * @param buffer A buffer positioned at the start of the encoding * @param region The structure to receive the decoded fields * * @return UDS_SUCCESS or an error **/ static int decodeVolumeRegion(Buffer *buffer, VolumeRegion *region) { VolumeRegionID id; int result = getUInt32LEFromBuffer(buffer, &id); if (result != VDO_SUCCESS) { return result; } PhysicalBlockNumber startBlock; result = getUInt64LEFromBuffer(buffer, &startBlock); if (result != VDO_SUCCESS) { return result; } *region = (VolumeRegion) { .id = id, .startBlock = startBlock, }; return VDO_SUCCESS; } /** * Encode the on-disk representation of a volume region into a buffer. * * @param region The region to encode * @param buffer A buffer positioned at the start of the encoding * * @return UDS_SUCCESS or an error **/ static int encodeVolumeRegion(const VolumeRegion *region, Buffer *buffer) { int result = putUInt32LEIntoBuffer(buffer, region->id); if (result != VDO_SUCCESS) { return result; } return putUInt64LEIntoBuffer(buffer, region->startBlock); } /** * Decode the on-disk representation of a volume geometry from a buffer. * * @param buffer A buffer positioned at the start of the encoding * @param geometry The structure to receive the decoded fields * * @return UDS_SUCCESS or an error **/ static int decodeVolumeGeometry(Buffer *buffer, VolumeGeometry *geometry) { ReleaseVersionNumber releaseVersion; int result = getUInt32LEFromBuffer(buffer, &releaseVersion); if (result != VDO_SUCCESS) { return result; } Nonce nonce; result = getUInt64LEFromBuffer(buffer, &nonce); if (result != VDO_SUCCESS) { return result; } geometry->releaseVersion = releaseVersion; geometry->nonce = nonce; result = getBytesFromBuffer(buffer, sizeof(UUID), geometry->uuid); if (result != VDO_SUCCESS) { return result; } for (VolumeRegionID id = 0; id < VOLUME_REGION_COUNT; id++) { result = decodeVolumeRegion(buffer, &geometry->regions[id]); if (result != VDO_SUCCESS) { return result; } } return decodeIndexConfig(buffer, &geometry->indexConfig); } /** * Encode the on-disk representation of a volume geometry into a buffer. * * @param geometry The geometry to encode * @param buffer A buffer positioned at the start of the encoding * * @return UDS_SUCCESS or an error **/ static int encodeVolumeGeometry(const VolumeGeometry *geometry, Buffer *buffer) { int result = putUInt32LEIntoBuffer(buffer, geometry->releaseVersion); if (result != VDO_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, geometry->nonce); if (result != VDO_SUCCESS) { return result; } result = putBytes(buffer, sizeof(UUID), geometry->uuid); if (result != VDO_SUCCESS) { return result; } for (VolumeRegionID id = 0; id < VOLUME_REGION_COUNT; id++) { result = encodeVolumeRegion(&geometry->regions[id], buffer); if (result != VDO_SUCCESS) { return result; } } return encodeIndexConfig(&geometry->indexConfig, buffer); } /** * Decode the on-disk representation of a geometry block, up to but not * including the checksum, from a buffer. * * @param buffer A buffer positioned at the start of the block * @param geometry The structure to receive the decoded volume geometry fields * * @return UDS_SUCCESS or an error **/ static int decodeGeometryBlock(Buffer *buffer, VolumeGeometry *geometry) { if (!hasSameBytes(buffer, MAGIC_NUMBER, MAGIC_NUMBER_SIZE)) { return VDO_BAD_MAGIC; } int result = skipForward(buffer, MAGIC_NUMBER_SIZE); if (result != VDO_SUCCESS) { return result; } Header header; result = decodeHeader(buffer, &header); if (result != VDO_SUCCESS) { return result; } result = validateHeader(&GEOMETRY_BLOCK_HEADER_4_0, &header, true, __func__); if (result != VDO_SUCCESS) { return result; } result = decodeVolumeGeometry(buffer, geometry); if (result != VDO_SUCCESS) { return result; } // Leave the CRC for the caller to decode and verify. return ASSERT(header.size == (uncompactedAmount(buffer) + sizeof(CRC32Checksum)), "should have decoded up to the geometry checksum"); } /** * Encode the on-disk representation of a geometry block, up to but not * including the checksum, into a buffer. * * @param geometry The volume geometry to encode into the block * @param buffer A buffer positioned at the start of the block * * @return UDS_SUCCESS or an error **/ static int encodeGeometryBlock(const VolumeGeometry *geometry, Buffer *buffer) { int result = putBytes(buffer, MAGIC_NUMBER_SIZE, MAGIC_NUMBER); if (result != VDO_SUCCESS) { return result; } result = encodeHeader(&GEOMETRY_BLOCK_HEADER_4_0, buffer); if (result != VDO_SUCCESS) { return result; } result = encodeVolumeGeometry(geometry, buffer); if (result != VDO_SUCCESS) { return result; } // Leave the CRC for the caller to compute and encode. return ASSERT(GEOMETRY_BLOCK_HEADER_4_0.size == (contentLength(buffer) + sizeof(CRC32Checksum)), "should have decoded up to the geometry checksum"); } /** * Allocate a block-size buffer to read the geometry from the physical layer, * read the block, and return the buffer. * * @param [in] layer The physical layer containing the block to read * @param [out] blockPtr A pointer to receive the allocated buffer * * @return VDO_SUCCESS or an error code **/ static int readGeometryBlock(PhysicalLayer *layer, byte **blockPtr) { int result = ASSERT(layer->reader != NULL, "Layer must have a sync reader"); if (result != VDO_SUCCESS) { return result; } char *block; result = layer->allocateIOBuffer(layer, VDO_BLOCK_SIZE, "geometry block", &block); if (result != VDO_SUCCESS) { return result; } result = layer->reader(layer, GEOMETRY_BLOCK_LOCATION, 1, block, NULL); if (result != VDO_SUCCESS) { FREE(block); return result; } *blockPtr = (byte *) block; return VDO_SUCCESS; } /**********************************************************************/ int loadVolumeGeometry(PhysicalLayer *layer, VolumeGeometry *geometry) { byte *block; int result = readGeometryBlock(layer, &block); if (result != VDO_SUCCESS) { return result; } Buffer *buffer; result = wrapBuffer(block, VDO_BLOCK_SIZE, VDO_BLOCK_SIZE, &buffer); if (result != VDO_SUCCESS) { FREE(block); return result; } result = decodeGeometryBlock(buffer, geometry); if (result != VDO_SUCCESS) { freeBuffer(&buffer); FREE(block); return result; } // Checksum everything decoded so far. CRC32Checksum checksum = layer->updateCRC32(INITIAL_CHECKSUM, block, uncompactedAmount(buffer)); CRC32Checksum savedChecksum; result = getUInt32LEFromBuffer(buffer, &savedChecksum); if (result != VDO_SUCCESS) { freeBuffer(&buffer); FREE(block); return result; } // Finished all decoding. Everything that follows is validation code. freeBuffer(&buffer); FREE(block); if (!isLoadableReleaseVersion(geometry->releaseVersion)) { return logErrorWithStringError(VDO_UNSUPPORTED_VERSION, "release version %d cannot be loaded", geometry->releaseVersion); } return ((checksum == savedChecksum) ? VDO_SUCCESS : VDO_CHECKSUM_MISMATCH); } /************************************************************************/ int computeIndexBlocks(IndexConfig *indexConfig, BlockCount *indexBlocksPtr) { UdsConfiguration udsConfiguration = NULL; int result = indexConfigToUdsConfiguration(indexConfig, &udsConfiguration); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "error creating index config"); } uint64_t indexBytes; result = udsComputeIndexSize(udsConfiguration, 0, &indexBytes); udsFreeConfiguration(udsConfiguration); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "error computing index size"); } BlockCount indexBlocks = indexBytes / VDO_BLOCK_SIZE; if ((((uint64_t) indexBlocks) * VDO_BLOCK_SIZE) != indexBytes) { return logErrorWithStringError(VDO_PARAMETER_MISMATCH, "index size must be" " a multiple of block size %d", VDO_BLOCK_SIZE); } *indexBlocksPtr = indexBlocks; return VDO_SUCCESS; } /**********************************************************************/ int initializeVolumeGeometry(Nonce nonce, UUID uuid, IndexConfig *indexConfig, VolumeGeometry *geometry) { BlockCount indexSize = 0; if (indexConfig != NULL) { int result = computeIndexBlocks(indexConfig, &indexSize); if (result != VDO_SUCCESS) { return result; } } *geometry = (VolumeGeometry) { .releaseVersion = CURRENT_RELEASE_VERSION_NUMBER, .nonce = nonce, .regions = { [INDEX_REGION] = { .id = INDEX_REGION, .startBlock = 1, }, [DATA_REGION] = { .id = DATA_REGION, .startBlock = 1 + indexSize, } } }; memcpy(geometry->uuid, uuid, sizeof(UUID)); if (indexSize > 0) { memcpy(&geometry->indexConfig, indexConfig, sizeof(IndexConfig)); } return VDO_SUCCESS; } /**********************************************************************/ int clearVolumeGeometry(PhysicalLayer *layer) { char *block; int result = layer->allocateIOBuffer(layer, VDO_BLOCK_SIZE, "geometry block", &block); if (result != VDO_SUCCESS) { return result; } result = layer->writer(layer, GEOMETRY_BLOCK_LOCATION, 1, block, NULL); FREE(block); return result; } /**********************************************************************/ int writeVolumeGeometry(PhysicalLayer *layer, VolumeGeometry *geometry) { char *block; int result = layer->allocateIOBuffer(layer, VDO_BLOCK_SIZE, "geometry block", &block); if (result != VDO_SUCCESS) { return result; } Buffer *buffer; result = wrapBuffer((byte *) block, VDO_BLOCK_SIZE, 0, &buffer); if (result != VDO_SUCCESS) { FREE(block); return result; } result = encodeGeometryBlock(geometry, buffer); if (result != VDO_SUCCESS) { freeBuffer(&buffer); FREE(block); return result; } // Checksum everything encoded so far and then encode the checksum. CRC32Checksum checksum = layer->updateCRC32(INITIAL_CHECKSUM, (byte *) block, contentLength(buffer)); result = putUInt32LEIntoBuffer(buffer, checksum); if (result != VDO_SUCCESS) { freeBuffer(&buffer); FREE(block); return result; } // Write it. result = layer->writer(layer, GEOMETRY_BLOCK_LOCATION, 1, block, NULL); freeBuffer(&buffer); FREE(block); return result; } /************************************************************************/ int indexConfigToUdsConfiguration(IndexConfig *indexConfig, UdsConfiguration *udsConfigPtr) { UdsConfiguration udsConfiguration; int result = udsInitializeConfiguration(&udsConfiguration, indexConfig->mem); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "error initializing configuration"); } udsConfigurationSetSparse(udsConfiguration, indexConfig->sparse); *udsConfigPtr = udsConfiguration; return VDO_SUCCESS; } /************************************************************************/ void indexConfigToUdsParameters(IndexConfig *indexConfig, struct uds_parameters *userParams) { userParams->checkpoint_frequency = indexConfig->checkpointFrequency; }