/* * 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/fixedLayout.c#3 $ */ #include "fixedLayout.h" #include "buffer.h" #include "logger.h" #include "memoryAlloc.h" #include "header.h" #include "statusCodes.h" const BlockCount ALL_FREE_BLOCKS = (uint64_t) -1; struct fixedLayout { PhysicalBlockNumber firstFree; PhysicalBlockNumber lastFree; size_t numPartitions; Partition *head; }; struct partition { PartitionID id; // The id of this partition FixedLayout *layout; // The layout to which this partition belongs PhysicalBlockNumber offset; // The offset into the layout of this partition PhysicalBlockNumber base; // The untranslated number of the first block BlockCount count; // The number of blocks in the partition Partition *next; // A pointer to the next partition in the layout }; typedef struct { PhysicalBlockNumber firstFree; PhysicalBlockNumber lastFree; byte partitionCount; } __attribute__((packed)) Layout3_0; typedef struct { PartitionID id; PhysicalBlockNumber offset; PhysicalBlockNumber base; BlockCount count; } __attribute__((packed)) Partition3_0; static const Header LAYOUT_HEADER_3_0 = { .id = FIXED_LAYOUT, .version = { .majorVersion = 3, .minorVersion = 0, }, .size = sizeof(Layout3_0), // Minimum size (contains no partitions) }; /**********************************************************************/ int makeFixedLayout(BlockCount totalBlocks, PhysicalBlockNumber startOffset, FixedLayout **layoutPtr) { FixedLayout *layout; int result = ALLOCATE(1, FixedLayout, "fixed layout", &layout); if (result != UDS_SUCCESS) { return result; } layout->firstFree = startOffset; layout->lastFree = startOffset + totalBlocks; layout->numPartitions = 0; layout->head = NULL; *layoutPtr = layout; return VDO_SUCCESS; } /**********************************************************************/ void freeFixedLayout(FixedLayout **layoutPtr) { FixedLayout *layout = *layoutPtr; if (layout == NULL) { return; } while (layout->head != NULL) { Partition *part = layout->head; layout->head = part->next; FREE(part); } FREE(layout); *layoutPtr = NULL; } /**********************************************************************/ BlockCount getTotalFixedLayoutSize(const FixedLayout *layout) { BlockCount size = getFixedLayoutBlocksAvailable(layout); for (Partition *partition = layout->head; partition != NULL; partition = partition->next) { size += partition->count; } return size; } /**********************************************************************/ int getPartition(FixedLayout *layout, PartitionID id, Partition **partitionPtr) { for (Partition *partition = layout->head; partition != NULL; partition = partition->next) { if (partition->id == id) { if (partitionPtr != NULL) { *partitionPtr = partition; } return VDO_SUCCESS; } } return VDO_UNKNOWN_PARTITION; } /**********************************************************************/ int translateToPBN(const Partition *partition, PhysicalBlockNumber partitionBlockNumber, PhysicalBlockNumber *layerBlockNumber) { if (partition == NULL) { *layerBlockNumber = partitionBlockNumber; return VDO_SUCCESS; } if (partitionBlockNumber < partition->base) { return VDO_OUT_OF_RANGE; } PhysicalBlockNumber offsetFromBase = partitionBlockNumber - partition->base; if (offsetFromBase >= partition->count) { return VDO_OUT_OF_RANGE; } *layerBlockNumber = partition->offset + offsetFromBase; return VDO_SUCCESS; } /**********************************************************************/ int translateFromPBN(const Partition *partition, PhysicalBlockNumber layerBlockNumber, PhysicalBlockNumber *partitionBlockNumberPtr) { if (partition == NULL) { *partitionBlockNumberPtr = layerBlockNumber; return VDO_SUCCESS; } if (layerBlockNumber < partition->offset) { return VDO_OUT_OF_RANGE; } PhysicalBlockNumber partitionBlockNumber = layerBlockNumber - partition->offset; if (partitionBlockNumber >= partition->count) { return VDO_OUT_OF_RANGE; } *partitionBlockNumberPtr = partitionBlockNumber + partition->base; return VDO_SUCCESS; } /**********************************************************************/ BlockCount getFixedLayoutBlocksAvailable(const FixedLayout *layout) { return layout->lastFree - layout->firstFree; } /** * Allocate a partition. The partition will be attached to the partition * list in the layout. * * @param layout The layout containing the partition * @param id The id of the partition * @param offset The offset into the layout at which the partition begins * @param base The number of the first block for users of the partition * @param blockCount The number of blocks in the partition * * @return VDO_SUCCESS or an error **/ static int allocatePartition(FixedLayout *layout, byte id, PhysicalBlockNumber offset, PhysicalBlockNumber base, BlockCount blockCount) { Partition *partition; int result = ALLOCATE(1, Partition, "fixed layout partition", &partition); if (result != UDS_SUCCESS) { return result; } partition->id = id; partition->layout = layout; partition->offset = offset; partition->base = base; partition->count = blockCount; partition->next = layout->head; layout->head = partition; return VDO_SUCCESS; } /**********************************************************************/ int makeFixedLayoutPartition(FixedLayout *layout, PartitionID id, BlockCount blockCount, PartitionDirection direction, PhysicalBlockNumber base) { BlockCount freeBlocks = layout->lastFree - layout->firstFree; if (blockCount == ALL_FREE_BLOCKS) { if (freeBlocks == 0) { return VDO_NO_SPACE; } else { blockCount = freeBlocks; } } else if (blockCount > freeBlocks) { return VDO_NO_SPACE; } int result = getPartition(layout, id, NULL); if (result != VDO_UNKNOWN_PARTITION) { return VDO_PARTITION_EXISTS; } PhysicalBlockNumber offset = ((direction == FROM_END) ? (layout->lastFree - blockCount) : layout->firstFree); result = allocatePartition(layout, id, offset, base, blockCount); if (result != VDO_SUCCESS) { return result; } layout->numPartitions++; if (direction == FROM_END) { layout->lastFree = layout->lastFree - blockCount; } else { layout->firstFree += blockCount; } return VDO_SUCCESS; } /**********************************************************************/ BlockCount getFixedLayoutPartitionSize(const Partition *partition) { return partition->count; } /**********************************************************************/ PhysicalBlockNumber getFixedLayoutPartitionOffset(const Partition *partition) { return partition->offset; } /**********************************************************************/ PhysicalBlockNumber getFixedLayoutPartitionBase(const Partition *partition) { return partition->base; } /**********************************************************************/ static inline size_t getEncodedSize(const FixedLayout *layout) { return sizeof(Layout3_0) + (sizeof(Partition3_0) * layout->numPartitions); } /**********************************************************************/ size_t getFixedLayoutEncodedSize(const FixedLayout *layout) { return ENCODED_HEADER_SIZE + getEncodedSize(layout); } /** * Encode a null-terminated list of fixed layout partitions into a buffer * using partition format 3.0. * * @param layout The layout containing the list of partitions to encode * @param buffer A buffer positioned at the start of the encoding * * @return UDS_SUCCESS or an error code **/ static int encodePartitions_3_0(const FixedLayout *layout, Buffer *buffer) { for (const Partition *partition = layout->head; partition != NULL; partition = partition->next) { STATIC_ASSERT_SIZEOF(PartitionID, sizeof(byte)); int result = putByte(buffer, partition->id); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, partition->offset); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, partition->base); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, partition->count); if (result != UDS_SUCCESS) { return result; } } return UDS_SUCCESS; } /** * Encode the header fields of a fixed layout into a buffer using layout * format 3.0. * * @param layout The layout to encode * @param buffer A buffer positioned at the start of the encoding * * @return UDS_SUCCESS or an error code **/ static int encodeLayout_3_0(const FixedLayout *layout, Buffer *buffer) { int result = ASSERT(layout->numPartitions <= UINT8_MAX, "fixed layout partition count must fit in a byte"); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, layout->firstFree); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, layout->lastFree); if (result != UDS_SUCCESS) { return result; } return putByte(buffer, layout->numPartitions); } /**********************************************************************/ int encodeFixedLayout(const FixedLayout *layout, Buffer *buffer) { if (!ensureAvailableSpace(buffer, getFixedLayoutEncodedSize(layout))) { return UDS_BUFFER_ERROR; } Header header = LAYOUT_HEADER_3_0; header.size = getEncodedSize(layout); int result = encodeHeader(&header, buffer); if (result != UDS_SUCCESS) { return result; } size_t initialLength = contentLength(buffer); result = encodeLayout_3_0(layout, buffer); if (result != UDS_SUCCESS) { return result; } size_t encodedSize = contentLength(buffer) - initialLength; result = ASSERT(encodedSize == sizeof(Layout3_0), "encoded size of fixed layout header must match structure"); if (result != UDS_SUCCESS) { return result; } result = encodePartitions_3_0(layout, buffer); if (result != UDS_SUCCESS) { return result; } encodedSize = contentLength(buffer) - initialLength; return ASSERT(encodedSize == header.size, "encoded size of fixed layout must match header size"); } /** * Decode a sequence of fixed layout partitions from a buffer * using partition format 3.0. * * @param buffer A buffer positioned at the start of the encoding * @param layout The layout in which to allocate the decoded partitions * * @return UDS_SUCCESS or an error code **/ static int decodePartitions_3_0(Buffer *buffer, FixedLayout *layout) { for (size_t i = 0; i < layout->numPartitions; i++) { byte id; int result = getByte(buffer, &id); if (result != UDS_SUCCESS) { return result; } uint64_t offset; result = getUInt64LEFromBuffer(buffer, &offset); if (result != UDS_SUCCESS) { return result; } uint64_t base; result = getUInt64LEFromBuffer(buffer, &base); if (result != UDS_SUCCESS) { return result; } uint64_t count; result = getUInt64LEFromBuffer(buffer, &count); if (result != UDS_SUCCESS) { return result; } result = allocatePartition(layout, id, offset, base, count); if (result != VDO_SUCCESS) { return result; } } return UDS_SUCCESS; } /** * Decode the header fields of a fixed layout from a buffer using layout * format 3.0. * * @param buffer A buffer positioned at the start of the encoding * @param layout The structure to receive the decoded fields * * @return UDS_SUCCESS or an error code **/ static int decodeLayout_3_0(Buffer *buffer, Layout3_0 *layout) { size_t initialLength = contentLength(buffer); PhysicalBlockNumber firstFree; int result = getUInt64LEFromBuffer(buffer, &firstFree); if (result != UDS_SUCCESS) { return result; } PhysicalBlockNumber lastFree; result = getUInt64LEFromBuffer(buffer, &lastFree); if (result != UDS_SUCCESS) { return result; } byte partitionCount; result = getByte(buffer, &partitionCount); if (result != UDS_SUCCESS) { return result; } *layout = (Layout3_0) { .firstFree = firstFree, .lastFree = lastFree, .partitionCount = partitionCount, }; size_t decodedSize = initialLength - contentLength(buffer); return ASSERT(decodedSize == sizeof(Layout3_0), "decoded size of fixed layout header must match structure"); } /**********************************************************************/ int decodeFixedLayout(Buffer *buffer, FixedLayout **layoutPtr) { Header header; int result = decodeHeader(buffer, &header); if (result != UDS_SUCCESS) { return result; } // Layout is variable size, so only do a minimum size check here. result = validateHeader(&LAYOUT_HEADER_3_0, &header, false, __func__); if (result != VDO_SUCCESS) { return result; } Layout3_0 layoutHeader; result = decodeLayout_3_0(buffer, &layoutHeader); if (result != UDS_SUCCESS) { return result; } if (contentLength(buffer) < (sizeof(Partition3_0) * layoutHeader.partitionCount)) { return VDO_UNSUPPORTED_VERSION; } FixedLayout *layout; result = ALLOCATE(1, FixedLayout, "fixed layout", &layout); if (result != UDS_SUCCESS) { return result; } layout->firstFree = layoutHeader.firstFree; layout->lastFree = layoutHeader.lastFree; layout->numPartitions = layoutHeader.partitionCount; result = decodePartitions_3_0(buffer, layout); if (result != VDO_SUCCESS) { freeFixedLayout(&layout); return result; } *layoutPtr = layout; return VDO_SUCCESS; }