/* * 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/uds-releases/jasper/src/uds/indexLayout.c#19 $ */ #include "indexLayout.h" #include "buffer.h" #include "compiler.h" #include "config.h" #include "indexConfig.h" #include "layoutRegion.h" #include "logger.h" #include "masterIndexOps.h" #include "memoryAlloc.h" #include "nonce.h" #include "openChapter.h" /* * Overall layout of an index on disk: * * The layout is divided into a number of fixed-size regions, the sizes of * which are computed when the index is created. Every header and region * begins on 4K block boundary. Save regions are further sub-divided into * regions of their own. * * Each region has a kind and an instance number. Some kinds only have one * instance and therefore use RL_SOLE_INSTANCE (-1) as the instance number. * The RL_KIND_INDEX uses instances to represent sub-indices, where used. * A save region can either hold a checkpoint or a clean shutdown (determined * by the type). The instances determine which available save slot is used. * The RL_KIND_MASTER_INDEX uses instances to record which zone is being saved. * * +-+-+--------+--------+--------+-----+--- -+-+ * | | | I N D E X 0 101, 0 | ... | | * |H|C+--------+--------+--------+-----+--- -+S| * |D|f| Volume | Save | Save | | |e| * |R|g| Region | Region | Region | ... | ... |a| * | | | 201 -1 | 202 0 | 202 1 | | |l| * +-+-+--------+--------+--------+-----+--- -+-+ * * The header contains the encoded regional layout table as well as * the saved index configuration record. The sub-index regions and their * subdivisions are maintained in the same table. * * There are at least two save regions per sub-index to preserve the old * state should the saving of a state be incomplete. They are used in * a round-robin fashion. * * Anatomy of a save region: * * +-+-----+------+------+-----+ -+-----+ * |H| IPM | MI | MI | | | OC | * |D| | zone | zone | ... | | | * |R| 301 | 302 | 302 | | | 303 | * | | -1 | 0 | 1 | | | -1 | * +-+-----+------+------+-----+ -+-----+ * * Every region header has a type (and version). In save regions, * the open chapter only appears in RL_TYPE_SAVE not RL_TYPE_CHECKPOINT, * although the same space is reserved for both. * * The header contains the encoded regional layout table as well as the * index state record for that save or checkpoint. Each save or checkpoint * has a unique generation number and nonce which is used to seed the * checksums of those regions. */ typedef struct indexSaveData_v1 { uint64_t timestamp; // ms since epoch... uint64_t nonce; uint32_t version; // 1 uint32_t unused__; } IndexSaveData; typedef struct indexSaveLayout { LayoutRegion indexSave; LayoutRegion header; unsigned int numZones; LayoutRegion indexPageMap; LayoutRegion freeSpace; LayoutRegion *masterIndexZones; LayoutRegion *openChapter; IndexSaveType saveType; IndexSaveData saveData; Buffer *indexStateBuffer; bool read; bool written; } IndexSaveLayout; typedef struct subIndexLayout { LayoutRegion subIndex; uint64_t nonce; LayoutRegion volume; IndexSaveLayout *saves; } SubIndexLayout; typedef struct superBlockData_v1 { byte magicLabel[32]; byte nonceInfo[32]; uint64_t nonce; uint32_t version; // 2 uint32_t blockSize; // for verification uint16_t numIndexes; // 1 uint16_t maxSaves; uint64_t openChapterBlocks; uint64_t pageMapBlocks; } SuperBlockData; struct indexLayout { IOFactory *factory; off_t offset; struct index_version indexVersion; SuperBlockData super; LayoutRegion header; LayoutRegion config; SubIndexLayout index; LayoutRegion seal; uint64_t totalBlocks; int refCount; }; /** * Structure used to compute single file layout sizes. * * Note that the masterIndexBlocks represent all zones and are sized for * the maximum number of blocks that would be needed regardless of the number * of zones (up to the maximum value) that are used at run time. * * Similarly, the number of saves is sized for the minimum safe value * assuming checkpointing is enabled, since that is also a run-time parameter. **/ typedef struct saveLayoutSizes { Configuration config; // this is a captive copy Geometry geometry; // this is a captive copy unsigned int numSaves; // per sub-index size_t blockSize; // in bytes uint64_t volumeBlocks; // per sub-index uint64_t masterIndexBlocks; // per save uint64_t pageMapBlocks; // per save uint64_t openChapterBlocks; // per save uint64_t saveBlocks; // per sub-index uint64_t subIndexBlocks; // per sub-index uint64_t totalBlocks; // for whole layout } SaveLayoutSizes; enum { INDEX_STATE_BUFFER_SIZE = 512, MAX_SAVES = 5, }; static const byte SINGLE_FILE_MAGIC_1[32] = "*ALBIREO*SINGLE*FILE*LAYOUT*001*"; enum { SINGLE_FILE_MAGIC_1_LENGTH = sizeof(SINGLE_FILE_MAGIC_1), }; static int reconstituteSingleFileLayout(IndexLayout *layout, SuperBlockData *super, RegionTable *table, uint64_t firstBlock) __attribute__((warn_unused_result)); static int writeIndexSaveLayout(IndexLayout *layout, IndexSaveLayout *isl) __attribute__((warn_unused_result)); /*****************************************************************************/ static INLINE uint64_t blockCount(uint64_t bytes, uint32_t blockSize) { uint64_t blocks = bytes / blockSize; if (bytes % blockSize > 0) { ++blocks; } return blocks; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int computeSizes(SaveLayoutSizes *sls, const UdsConfiguration config, size_t blockSize, unsigned int numCheckpoints) { if (config->bytesPerPage % blockSize != 0) { return logErrorWithStringError(UDS_INCORRECT_ALIGNMENT, "page size not a multiple of block size"); } Configuration *cfg = NULL; int result = makeConfiguration(config, &cfg); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot compute layout size"); } memset(sls, 0, sizeof(*sls)); // internalize the configuration and geometry... sls->geometry = *cfg->geometry; sls->config = *cfg; sls->config.geometry = &sls->geometry; freeConfiguration(cfg); sls->numSaves = 2 + numCheckpoints; sls->blockSize = blockSize; sls->volumeBlocks = sls->geometry.bytesPerVolume / blockSize; result = computeMasterIndexSaveBlocks(&sls->config, blockSize, &sls->masterIndexBlocks); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot compute index save size"); } sls->pageMapBlocks = blockCount(computeIndexPageMapSaveSize(&sls->geometry), blockSize); sls->openChapterBlocks = blockCount(computeSavedOpenChapterSize(&sls->geometry), blockSize); sls->saveBlocks = 1 + (sls->masterIndexBlocks + sls->pageMapBlocks + sls->openChapterBlocks); sls->subIndexBlocks = sls->volumeBlocks + (sls->numSaves * sls->saveBlocks); sls->totalBlocks = 3 + sls->subIndexBlocks; return UDS_SUCCESS; } /*****************************************************************************/ int udsComputeIndexSize(const UdsConfiguration config, unsigned int numCheckpoints, uint64_t *indexSize) { SaveLayoutSizes sizes; int result = computeSizes(&sizes, config, UDS_BLOCK_SIZE, numCheckpoints); if (result != UDS_SUCCESS) { return result; } if (indexSize != NULL) { *indexSize = sizes.totalBlocks * sizes.blockSize; } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int openLayoutReader(IndexLayout *layout, LayoutRegion *lr, BufferedReader **readerPtr) { off_t start = lr->startBlock * layout->super.blockSize; size_t size = lr->numBlocks * layout->super.blockSize; return openBufferedReader(layout->factory, start, size, readerPtr); } /*****************************************************************************/ __attribute__((warn_unused_result)) static int openLayoutWriter(IndexLayout *layout, LayoutRegion *lr, BufferedWriter **writerPtr) { off_t start = lr->startBlock * layout->super.blockSize; size_t size = lr->numBlocks * layout->super.blockSize; return openBufferedWriter(layout->factory, start, size, writerPtr); } /*****************************************************************************/ __attribute__((warn_unused_result)) static int decodeIndexSaveData(Buffer *buffer, IndexSaveData *saveData) { int result = getUInt64LEFromBuffer(buffer, &saveData->timestamp); if (result != UDS_SUCCESS) { return result; } result = getUInt64LEFromBuffer(buffer, &saveData->nonce); if (result != UDS_SUCCESS) { return result; } result = getUInt32LEFromBuffer(buffer, &saveData->version); if (result != UDS_SUCCESS) { return result; } result = getUInt32LEFromBuffer(buffer, &saveData->unused__); if (result != UDS_SUCCESS) { return result; } // The unused padding has to be zeroed for correct nonce calculation if (saveData->unused__ != 0) { return UDS_CORRUPT_COMPONENT; } result = ASSERT_LOG_ONLY(contentLength(buffer) == 0, "%zu bytes decoded of %zu expected", bufferLength(buffer), sizeof(*saveData)); if (result != UDS_SUCCESS) { return UDS_CORRUPT_COMPONENT; } return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int decodeRegionHeader(Buffer *buffer, RegionHeader *header) { int result = getUInt64LEFromBuffer(buffer, &header->magic); if (result != UDS_SUCCESS) { return result; } result = getUInt64LEFromBuffer(buffer, &header->regionBlocks); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, &header->type); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, &header->version); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, &header->numRegions); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, &header->payload); if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(contentLength(buffer) == 0, "%zu bytes decoded of %zu expected", bufferLength(buffer), sizeof(*header)); if (result != UDS_SUCCESS) { return UDS_CORRUPT_COMPONENT; } return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int decodeLayoutRegion(Buffer *buffer, LayoutRegion *region) { size_t cl1 = contentLength(buffer); int result = getUInt64LEFromBuffer(buffer, ®ion->startBlock); if (result != UDS_SUCCESS) { return result; } result = getUInt64LEFromBuffer(buffer, ®ion->numBlocks); if (result != UDS_SUCCESS) { return result; } result = getUInt32LEFromBuffer(buffer, ®ion->checksum); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, ®ion->kind); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, ®ion->instance); if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(cl1 - contentLength(buffer) == sizeof(*region), "%zu bytes decoded, of %zu expected", cl1 - contentLength(buffer), sizeof(*region)); if (result != UDS_SUCCESS) { return UDS_CORRUPT_COMPONENT; } return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int loadRegionTable(BufferedReader *reader, RegionTable **tablePtr) { Buffer *buffer; int result = makeBuffer(sizeof(RegionHeader), &buffer); if (result != UDS_SUCCESS) { return result; } result = readFromBufferedReader(reader, getBufferContents(buffer), bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return logErrorWithStringError(result, "cannot read region table header"); } result = resetBufferEnd(buffer, bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } RegionHeader header; result = decodeRegionHeader(buffer, &header); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return result; } if (header.magic != REGION_MAGIC) { return UDS_NO_INDEX; } if (header.version != 1) { return logErrorWithStringError(UDS_UNSUPPORTED_VERSION, "unknown region table version %" PRIu16, header.version); } RegionTable *table; result = ALLOCATE_EXTENDED(RegionTable, header.numRegions, LayoutRegion, "single file layout region table", &table); if (result != UDS_SUCCESS) { return result; } table->header = header; result = makeBuffer(header.numRegions * sizeof(LayoutRegion), &buffer); if (result != UDS_SUCCESS) { FREE(table); return result; } result = readFromBufferedReader(reader, getBufferContents(buffer), bufferLength(buffer)); if (result != UDS_SUCCESS) { FREE(table); freeBuffer(&buffer); return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "cannot read region table layouts"); } result = resetBufferEnd(buffer, bufferLength(buffer)); if (result != UDS_SUCCESS) { FREE(table); freeBuffer(&buffer); return result; } unsigned int i; for (i = 0; i < header.numRegions; i++){ result = decodeLayoutRegion(buffer, &table->regions[i]); if (result != UDS_SUCCESS) { FREE(table); freeBuffer(&buffer); return result; } } freeBuffer(&buffer); *tablePtr = table; return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int decodeSuperBlockData(Buffer *buffer, SuperBlockData *super) { int result = getBytesFromBuffer(buffer, 32, super->magicLabel); if (result != UDS_SUCCESS) { return result; } result = getBytesFromBuffer(buffer, 32, super->nonceInfo); if (result != UDS_SUCCESS) { return result; } result = getUInt64LEFromBuffer(buffer, &super->nonce); if (result != UDS_SUCCESS) { return result; } result = getUInt32LEFromBuffer(buffer, &super->version); if (result != UDS_SUCCESS) { return result; } result = getUInt32LEFromBuffer(buffer, &super->blockSize); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, &super->numIndexes); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEFromBuffer(buffer, &super->maxSaves); if (result != UDS_SUCCESS) { return result; } result = skipForward(buffer, 4); // aligment if (result != UDS_SUCCESS) { return result; } result = getUInt64LEFromBuffer(buffer, &super->openChapterBlocks); if (result != UDS_SUCCESS) { return result; } result = getUInt64LEFromBuffer(buffer, &super->pageMapBlocks); if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(contentLength(buffer) == 0, "%zu bytes decoded of %zu expected", bufferLength(buffer), sizeof(*super)); if (result != UDS_SUCCESS) { return UDS_CORRUPT_COMPONENT; } return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int readSuperBlockData(BufferedReader *reader, SuperBlockData *super, size_t savedSize) { if (savedSize != sizeof(SuperBlockData)) { return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "unexpected super block data size %zu", savedSize); } if (sizeof(super->magicLabel) != SINGLE_FILE_MAGIC_1_LENGTH) { return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "super block magic label size incorrect"); } Buffer *buffer; int result = makeBuffer(savedSize, &buffer); if (result != UDS_SUCCESS) { return result; } result = readFromBufferedReader(reader, getBufferContents(buffer), bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return logErrorWithStringError(result, "cannot read region table header"); } result = resetBufferEnd(buffer, bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = decodeSuperBlockData(buffer, super); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot read super block data"); } if (memcmp(super->magicLabel, SINGLE_FILE_MAGIC_1, SINGLE_FILE_MAGIC_1_LENGTH) != 0) { return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "unknown superblock magic label"); } if ((super->version < SUPER_VERSION_MINIMUM) || (super->version > SUPER_VERSION_MAXIMUM)) { return logErrorWithStringError(UDS_UNSUPPORTED_VERSION, "unknown superblock version number %" PRIu32, super->version); } // We dropped the usage of multiple subindices before we ever ran UDS code in // the kernel. We do not have code that will handle multiple subindices. if (super->numIndexes != 1) { return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "invalid subindex count %" PRIu32, super->numIndexes); } if (generateMasterNonce(super->nonceInfo, sizeof(super->nonceInfo)) != super->nonce) { return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "inconsistent superblock nonce"); } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int allocateSingleFileParts(IndexLayout *layout, SuperBlockData *super) { int result = ALLOCATE(super->maxSaves, IndexSaveLayout, __func__, &layout->index.saves); if (result != UDS_SUCCESS) { return result; } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int loadSuperBlock(IndexLayout *layout, size_t blockSize, uint64_t firstBlock, BufferedReader *reader) { RegionTable *table = NULL; int result = loadRegionTable(reader, &table); if (result != UDS_SUCCESS) { return result; } if (table->header.type != RH_TYPE_SUPER) { FREE(table); return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "not a superblock region table"); } SuperBlockData superBlockData; result = readSuperBlockData(reader, &superBlockData, table->header.payload); if (result != UDS_SUCCESS) { FREE(table); return logErrorWithStringError(result, "unknown superblock format"); } if (superBlockData.blockSize != blockSize) { FREE(table); return logErrorWithStringError(UDS_WRONG_INDEX_CONFIG, "superblock saved blockSize %" PRIu32 " differs from supplied blockSize %zu", superBlockData.blockSize, blockSize); } initializeIndexVersion(&layout->indexVersion, superBlockData.version); result = allocateSingleFileParts(layout, &superBlockData); if (result != UDS_SUCCESS) { FREE(table); return result; } result = reconstituteSingleFileLayout(layout, &superBlockData, table, firstBlock); FREE(table); return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int readIndexSaveData(BufferedReader *reader, IndexSaveData *saveData, size_t savedSize, Buffer **bufferPtr) { int result = UDS_SUCCESS; if (savedSize == 0) { memset(saveData, 0, sizeof(*saveData)); } else { if (savedSize < sizeof(IndexSaveData)) { return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "unexpected index save data size %zu", savedSize); } Buffer *buffer; result = makeBuffer(sizeof(*saveData), &buffer); if (result != UDS_SUCCESS) { return result; } result = readFromBufferedReader(reader, getBufferContents(buffer), bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return logErrorWithStringError(result, "cannot read index save data"); } result = resetBufferEnd(buffer, bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = decodeIndexSaveData(buffer, saveData); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return result; } savedSize -= sizeof(IndexSaveData); if (saveData->version > 1) { return logErrorWithStringError(UDS_UNSUPPORTED_VERSION, "unkown index save verion number %" PRIu32, saveData->version); } if (savedSize > INDEX_STATE_BUFFER_SIZE) { return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "unexpected index state buffer size %zu", savedSize); } } Buffer *buffer = NULL; if (saveData->version != 0) { result = makeBuffer(INDEX_STATE_BUFFER_SIZE, &buffer); if (result != UDS_SUCCESS) { return result; } if (savedSize > 0) { result = readFromBufferedReader(reader, getBufferContents(buffer), savedSize); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = resetBufferEnd(buffer, savedSize); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } } } *bufferPtr = buffer; return UDS_SUCCESS; } /*****************************************************************************/ typedef struct { LayoutRegion *nextRegion; LayoutRegion *lastRegion; uint64_t nextBlock; int result; } RegionIterator; /*****************************************************************************/ __attribute__((format(printf, 2, 3))) static void iterError(RegionIterator *iter, const char *fmt, ...) { va_list args; va_start(args, fmt); int r = vLogWithStringError(LOG_ERR, UDS_UNEXPECTED_RESULT, fmt, args); va_end(args); if (iter->result == UDS_SUCCESS) { iter->result = r; } } /** * Set the next layout region in the layout according to a region table * iterator, unless the iterator already contains an error * * @param expect whether to record an error or return false * @param lr the layout region field to set * @param iter the region iterator, which also holds the cumulative * result * @param numBlocks if non-zero, the expected number of blocks * @param kind the expected kind of the region * @param instance the expected instance number of the region * * @return true if we meet expectations, false if we do not **/ static bool expectLayout(bool expect, LayoutRegion *lr, RegionIterator *iter, uint64_t numBlocks, RegionKind kind, unsigned int instance) { if (iter->result != UDS_SUCCESS) { return false; } if (iter->nextRegion == iter->lastRegion) { if (expect) { iterError(iter, "ran out of layout regions in region table"); } return false; } if (iter->nextRegion->startBlock != iter->nextBlock) { iterError(iter, "layout region not at expected offset"); return false; } if (iter->nextRegion->kind != kind) { if (expect) { iterError(iter, "layout region has incorrect kind"); } return false; } if (iter->nextRegion->instance != instance) { iterError(iter, "layout region has incorrect instance"); return false; } if (numBlocks > 0 && iter->nextRegion->numBlocks != numBlocks) { iterError(iter, "layout region size is incorrect"); return false; } if (lr != NULL) { *lr = *iter->nextRegion; } iter->nextBlock += iter->nextRegion->numBlocks; iter->nextRegion++; return true; } /*****************************************************************************/ static void setupLayout(LayoutRegion *lr, uint64_t *nextAddrPtr, uint64_t regionSize, unsigned int kind, unsigned int instance) { *lr = (LayoutRegion) { .startBlock = *nextAddrPtr, .numBlocks = regionSize, .checksum = 0, .kind = kind, .instance = instance, }; *nextAddrPtr += regionSize; } /*****************************************************************************/ static void populateIndexSaveLayout(IndexSaveLayout *isl, SuperBlockData *super, unsigned int numZones, IndexSaveType saveType) { uint64_t nextBlock = isl->indexSave.startBlock; setupLayout(&isl->header, &nextBlock, 1, RL_KIND_HEADER, RL_SOLE_INSTANCE); setupLayout(&isl->indexPageMap, &nextBlock, super->pageMapBlocks, RL_KIND_INDEX_PAGE_MAP, RL_SOLE_INSTANCE); uint64_t blocksAvail = (isl->indexSave.numBlocks - (nextBlock - isl->indexSave.startBlock) - super->openChapterBlocks); if (numZones > 0) { uint64_t miBlockCount = blocksAvail / numZones; unsigned int z; for (z = 0; z < numZones; ++z) { LayoutRegion *miz = &isl->masterIndexZones[z]; setupLayout(miz, &nextBlock, miBlockCount, RL_KIND_MASTER_INDEX, z); } } if (saveType == IS_SAVE && isl->openChapter != NULL) { setupLayout(isl->openChapter, &nextBlock, super->openChapterBlocks, RL_KIND_OPEN_CHAPTER, RL_SOLE_INSTANCE); } setupLayout(&isl->freeSpace, &nextBlock, (isl->indexSave.numBlocks - (nextBlock - isl->indexSave.startBlock)), RL_KIND_SCRATCH, RL_SOLE_INSTANCE); } /*****************************************************************************/ __attribute__((warn_unused_result)) static int reconstructIndexSave(IndexSaveLayout *isl, IndexSaveData *saveData, SuperBlockData *super, RegionTable *table) { isl->numZones = 0; isl->saveData = *saveData; isl->read = false; isl->written = false; if (table->header.type == RH_TYPE_SAVE) { isl->saveType = IS_SAVE; } else if (table->header.type == RH_TYPE_CHECKPOINT) { isl->saveType = IS_CHECKPOINT; } else { isl->saveType = NO_SAVE; } if ((table->header.numRegions == 0) || ((table->header.numRegions == 1) && (table->regions[0].kind == RL_KIND_SCRATCH))) { populateIndexSaveLayout(isl, super, 0, NO_SAVE); return UDS_SUCCESS; } RegionIterator iter = { .nextRegion = table->regions, .lastRegion = table->regions + table->header.numRegions, .nextBlock = isl->indexSave.startBlock, .result = UDS_SUCCESS, }; expectLayout(true, &isl->header, &iter, 1, RL_KIND_HEADER, RL_SOLE_INSTANCE); expectLayout(true, &isl->indexPageMap, &iter, 0, RL_KIND_INDEX_PAGE_MAP, RL_SOLE_INSTANCE); unsigned int n = 0; RegionIterator tmpIter; for (tmpIter = iter; expectLayout(false, NULL, &tmpIter, 0, RL_KIND_MASTER_INDEX, n); ++n) ; isl->numZones = n; int result = UDS_SUCCESS; if (isl->numZones > 0) { result = ALLOCATE(n, LayoutRegion, "master index layout regions", &isl->masterIndexZones); if (result != UDS_SUCCESS) { return result; } } if (isl->saveType == IS_SAVE) { result = ALLOCATE(1, LayoutRegion, "open chapter layout region", &isl->openChapter); if (result != UDS_SUCCESS) { FREE(isl->masterIndexZones); return result; } } unsigned int z; for (z = 0; z < isl->numZones; ++z) { expectLayout(true, &isl->masterIndexZones[z], &iter, 0, RL_KIND_MASTER_INDEX, z); } if (isl->saveType == IS_SAVE) { expectLayout(true, isl->openChapter, &iter, 0, RL_KIND_OPEN_CHAPTER, RL_SOLE_INSTANCE); } if (!expectLayout(false, &isl->freeSpace, &iter, 0, RL_KIND_SCRATCH, RL_SOLE_INSTANCE)) { isl->freeSpace = (LayoutRegion) { .startBlock = iter.nextBlock, .numBlocks = (isl->indexSave.startBlock + isl->indexSave.numBlocks) - iter.nextBlock, .checksum = 0, .kind = RL_KIND_SCRATCH, .instance = RL_SOLE_INSTANCE, }; iter.nextBlock = isl->freeSpace.startBlock + isl->freeSpace.numBlocks; } if (iter.result != UDS_SUCCESS) { return iter.result; } if (iter.nextRegion != iter.lastRegion) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "expected %ld additional regions", iter.lastRegion - iter.nextRegion); } if (iter.nextBlock != isl->indexSave.startBlock + isl->indexSave.numBlocks) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "index save layout table incomplete"); } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int loadIndexSave(IndexSaveLayout *isl, SuperBlockData *super, BufferedReader *reader, unsigned int saveId) { RegionTable *table = NULL; int result = loadRegionTable(reader, &table); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot read index 0 save %u header", saveId); } if (table->header.regionBlocks != isl->indexSave.numBlocks) { uint64_t regionBlocks = table->header.regionBlocks; FREE(table); return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "unexpected index 0 save %u " "region block count %llu", saveId, regionBlocks); } if (table->header.type != RH_TYPE_SAVE && table->header.type != RH_TYPE_CHECKPOINT && table->header.type != RH_TYPE_UNSAVED) { unsigned int type = table->header.type; FREE(table); return logErrorWithStringError(UDS_CORRUPT_COMPONENT, "unexpected" " index 0 save %u header type %u", saveId, type); } IndexSaveData indexSaveData; result = readIndexSaveData(reader, &indexSaveData, table->header.payload, &isl->indexStateBuffer); if (result != UDS_SUCCESS) { FREE(table); return logErrorWithStringError(result, "unknown index 0 save %u data format", saveId); } result = reconstructIndexSave(isl, &indexSaveData, super, table); FREE(table); if (result != UDS_SUCCESS) { freeBuffer(&isl->indexStateBuffer); return logErrorWithStringError(result, "cannot reconstruct index 0 save %u", saveId); } isl->read = true; return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int loadSubIndexRegions(IndexLayout *layout) { unsigned int j; for (j = 0; j < layout->super.maxSaves; ++j) { IndexSaveLayout *isl = &layout->index.saves[j]; BufferedReader *reader; int result = openLayoutReader(layout, &isl->indexSave, &reader); if (result != UDS_SUCCESS) { logErrorWithStringError(result, "cannot get reader for index 0 save %u", j); while (j-- > 0) { IndexSaveLayout *isl = &layout->index.saves[j]; FREE(isl->masterIndexZones); FREE(isl->openChapter); freeBuffer(&isl->indexStateBuffer); } return result; } result = loadIndexSave(isl, &layout->super, reader, j); freeBufferedReader(reader); if (result != UDS_SUCCESS) { while (j-- > 0) { IndexSaveLayout *isl = &layout->index.saves[j]; FREE(isl->masterIndexZones); FREE(isl->openChapter); freeBuffer(&isl->indexStateBuffer); } return result; } } return UDS_SUCCESS; } /*****************************************************************************/ static int loadIndexLayout(IndexLayout *layout) { BufferedReader *reader; int result = openBufferedReader(layout->factory, layout->offset, UDS_BLOCK_SIZE, &reader); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "unable to read superblock"); } result = loadSuperBlock(layout, UDS_BLOCK_SIZE, layout->offset / UDS_BLOCK_SIZE, reader); freeBufferedReader(reader); if (result != UDS_SUCCESS) { FREE(layout->index.saves); layout->index.saves = NULL; return result; } result = loadSubIndexRegions(layout); if (result != UDS_SUCCESS) { FREE(layout->index.saves); layout->index.saves = NULL; return result; } return UDS_SUCCESS; } /*****************************************************************************/ static void generateSuperBlockData(size_t blockSize, unsigned int maxSaves, uint64_t openChapterBlocks, uint64_t pageMapBlocks, SuperBlockData *super) { memset(super, 0, sizeof(*super)); memcpy(super->magicLabel, SINGLE_FILE_MAGIC_1, SINGLE_FILE_MAGIC_1_LENGTH); createUniqueNonceData(super->nonceInfo, sizeof(super->nonceInfo)); super->nonce = generateMasterNonce(super->nonceInfo, sizeof(super->nonceInfo)); super->version = SUPER_VERSION_CURRENT; super->blockSize = blockSize; super->numIndexes = 1; super->maxSaves = maxSaves; super->openChapterBlocks = openChapterBlocks; super->pageMapBlocks = pageMapBlocks; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int resetIndexSaveLayout(IndexSaveLayout *isl, uint64_t *nextBlockPtr, uint64_t saveBlocks, uint64_t pageMapBlocks, unsigned int instance) { uint64_t startBlock = *nextBlockPtr; if (isl->masterIndexZones) { FREE(isl->masterIndexZones); } if (isl->openChapter) { FREE(isl->openChapter); } if (isl->indexStateBuffer) { freeBuffer(&isl->indexStateBuffer); } memset(isl, 0, sizeof(*isl)); isl->saveType = NO_SAVE; setupLayout(&isl->indexSave, &startBlock, saveBlocks, RL_KIND_SAVE, instance); setupLayout(&isl->header, nextBlockPtr, 1, RL_KIND_HEADER, RL_SOLE_INSTANCE); setupLayout(&isl->indexPageMap, nextBlockPtr, pageMapBlocks, RL_KIND_INDEX_PAGE_MAP, RL_SOLE_INSTANCE); uint64_t remaining = startBlock - *nextBlockPtr; setupLayout(&isl->freeSpace, nextBlockPtr, remaining, RL_KIND_SCRATCH, RL_SOLE_INSTANCE); // number of zones is a save-time parameter // presence of open chapter is a save-time parameter return UDS_SUCCESS; } /*****************************************************************************/ static void defineSubIndexNonce(SubIndexLayout *sil, uint64_t masterNonce, unsigned int indexId) { struct subIndexNonceData { uint64_t offset; uint16_t indexId; }; byte buffer[sizeof(struct subIndexNonceData)] = { 0 }; size_t offset = 0; encodeUInt64LE(buffer, &offset, sil->subIndex.startBlock); encodeUInt16LE(buffer, &offset, indexId); sil->nonce = generateSecondaryNonce(masterNonce, buffer, sizeof(buffer)); if (sil->nonce == 0) { sil->nonce = generateSecondaryNonce(~masterNonce + 1, buffer, sizeof(buffer)); } } /*****************************************************************************/ __attribute__((warn_unused_result)) static int setupSubIndex(SubIndexLayout *sil, uint64_t *nextBlockPtr, SaveLayoutSizes *sls, unsigned int instance, uint64_t masterNonce) { uint64_t startBlock = *nextBlockPtr; setupLayout(&sil->subIndex, &startBlock, sls->subIndexBlocks, RL_KIND_INDEX, instance); setupLayout(&sil->volume, nextBlockPtr, sls->volumeBlocks, RL_KIND_VOLUME, RL_SOLE_INSTANCE); unsigned int i; for (i = 0; i < sls->numSaves; ++i) { int result = resetIndexSaveLayout(&sil->saves[i], nextBlockPtr, sls->saveBlocks, sls->pageMapBlocks, i); if (result != UDS_SUCCESS) { return result; } } if (startBlock != *nextBlockPtr) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "sub index layout regions don't agree"); } defineSubIndexNonce(sil, masterNonce, instance); return UDS_SUCCESS; } /*****************************************************************************/ /** * Initialize a single file layout using the save layout sizes specified. * * @param layout the layout to initialize * @param offset the offset in bytes from the start of the backing storage * @param size the size in bytes of the backing storage * @param sls a populated SaveLayoutSizes object * * @return UDS_SUCCESS or an error code, potentially * UDS_INSUFFICIENT_INDEX_SPACE if the size of the backing store * is not sufficient for the index configuration, * UDS_BAD_INDEX_ALIGNMENT if the offset specified does not * align properly with the index block and page sizes] * various other errors **/ __attribute__((warn_unused_result)) static int initSingleFileLayout(IndexLayout *layout, uint64_t offset, uint64_t size, SaveLayoutSizes *sls) { layout->totalBlocks = sls->totalBlocks; if (size < sls->totalBlocks * sls->blockSize) { return logErrorWithStringError(UDS_INSUFFICIENT_INDEX_SPACE, "not enough space for index as configured"); } generateSuperBlockData(sls->blockSize, sls->numSaves, sls->openChapterBlocks, sls->pageMapBlocks, &layout->super); initializeIndexVersion(&layout->indexVersion, SUPER_VERSION_CURRENT); int result = allocateSingleFileParts(layout, &layout->super); if (result != UDS_SUCCESS) { return result; } uint64_t nextBlock = offset / sls->blockSize; setupLayout(&layout->header, &nextBlock, 1, RL_KIND_HEADER, RL_SOLE_INSTANCE); setupLayout(&layout->config, &nextBlock, 1, RL_KIND_CONFIG, RL_SOLE_INSTANCE); result = setupSubIndex(&layout->index, &nextBlock, sls, 0, layout->super.nonce); if (result != UDS_SUCCESS) { return result; } setupLayout(&layout->seal, &nextBlock, 1, RL_KIND_SEAL, RL_SOLE_INSTANCE); if (nextBlock * sls->blockSize > offset + size) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "layout does not fit as expected"); } return UDS_SUCCESS; } /*****************************************************************************/ static void expectSubIndex(SubIndexLayout *sil, RegionIterator *iter, SuperBlockData *super, unsigned int instance) { if (iter->result != UDS_SUCCESS) { return; } uint64_t startBlock = iter->nextBlock; expectLayout(true, &sil->subIndex, iter, 0, RL_KIND_INDEX, instance); uint64_t endBlock = iter->nextBlock; iter->nextBlock = startBlock; expectLayout(true, &sil->volume, iter, 0, RL_KIND_VOLUME, RL_SOLE_INSTANCE); unsigned int i; for (i = 0; i < super->maxSaves; ++i) { IndexSaveLayout *isl = &sil->saves[i]; expectLayout(true, &isl->indexSave, iter, 0, RL_KIND_SAVE, i); } if (iter->nextBlock != endBlock) { iterError(iter, "sub index region does not span all saves"); } defineSubIndexNonce(sil, super->nonce, instance); } /*****************************************************************************/ /** * Initialize a single file layout from the region table and super block data * stored in stable storage. * * @param layout the layout to initialize * @param region the IO region for this layout * @param super the super block data read from the superblock * @param table the region table read from the superblock * @param firstBlock the first block number in the region * * @return UDS_SUCCESS or an error code **/ __attribute__((warn_unused_result)) static int reconstituteSingleFileLayout(IndexLayout *layout, SuperBlockData *super, RegionTable *table, uint64_t firstBlock) { layout->super = *super; layout->totalBlocks = table->header.regionBlocks; RegionIterator iter = { .nextRegion = table->regions, .lastRegion = table->regions + table->header.numRegions, .nextBlock = firstBlock, .result = UDS_SUCCESS }; expectLayout(true, &layout->header, &iter, 1, RL_KIND_HEADER, RL_SOLE_INSTANCE); expectLayout(true, &layout->config, &iter, 1, RL_KIND_CONFIG, RL_SOLE_INSTANCE); expectSubIndex(&layout->index, &iter, &layout->super, 0); expectLayout(true, &layout->seal, &iter, 1, RL_KIND_SEAL, RL_SOLE_INSTANCE); if (iter.result != UDS_SUCCESS) { return iter.result; } if (iter.nextBlock != firstBlock + layout->totalBlocks) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "layout table does not span total blocks"); } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int saveSubIndexRegions(IndexLayout *layout) { SubIndexLayout *sil = &layout->index; unsigned int j; for (j = 0; j < layout->super.maxSaves; ++j) { IndexSaveLayout *isl = &sil->saves[j]; int result = writeIndexSaveLayout(layout, isl); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "unable to format index %u save 0 layout", j); } } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int makeSingleFileRegionTable(IndexLayout *layout, unsigned int *numRegionsPtr, RegionTable **tablePtr) { unsigned int numRegions = 1 + // header 1 + // config 1 + // index 1 + // volume layout->super.maxSaves + // saves 1; // seal RegionTable *table; int result = ALLOCATE_EXTENDED(RegionTable, numRegions, LayoutRegion, "layout region table", &table); if (result != UDS_SUCCESS) { return result; } LayoutRegion *lr = &table->regions[0]; *lr++ = layout->header; *lr++ = layout->config; SubIndexLayout *sil = &layout->index; *lr++ = sil->subIndex; *lr++ = sil->volume; unsigned int j; for (j = 0; j < layout->super.maxSaves; ++j) { *lr++ = sil->saves[j].indexSave; } *lr++ = layout->seal; result = ASSERT((lr == &table->regions[numRegions]), "incorrect number of regions"); if (result != UDS_SUCCESS) { return result; } *numRegionsPtr = numRegions; *tablePtr = table; return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int encodeIndexSaveData(Buffer *buffer, IndexSaveData *saveData) { int result = putUInt64LEIntoBuffer(buffer, saveData->timestamp); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, saveData->nonce); if (result != UDS_SUCCESS) { return result; } result = putUInt32LEIntoBuffer(buffer, saveData->version); if (result != UDS_SUCCESS) { return result; } result = zeroBytes(buffer, 4); /* padding */ if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(contentLength(buffer) == sizeof *saveData, "%zu bytes encoded of %zu expected", contentLength(buffer), sizeof(*saveData)); return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int encodeRegionHeader(Buffer *buffer, RegionHeader *header) { size_t startingLength = contentLength(buffer); int result = putUInt64LEIntoBuffer(buffer, REGION_MAGIC); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, header->regionBlocks); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, header->type); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, header->version); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, header->numRegions); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, header->payload); if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(contentLength(buffer) - startingLength == sizeof(*header), "%zu bytes encoded, of %zu expected", contentLength(buffer) - startingLength, sizeof(*header)); return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int encodeLayoutRegion(Buffer *buffer, LayoutRegion *region) { size_t startingLength = contentLength(buffer); int result = putUInt64LEIntoBuffer(buffer, region->startBlock); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, region->numBlocks); if (result != UDS_SUCCESS) { return result; } result = putUInt32LEIntoBuffer(buffer, region->checksum); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, region->kind); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, region->instance); if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(contentLength(buffer) - startingLength == sizeof(*region), "%zu bytes encoded, of %zu expected", contentLength(buffer) - startingLength, sizeof(*region)); return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int encodeSuperBlockData(Buffer *buffer, SuperBlockData *super) { int result = putBytes(buffer, 32, &super->magicLabel); if (result != UDS_SUCCESS) { return result; } result = putBytes(buffer, 32, &super->nonceInfo); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, super->nonce); if (result != UDS_SUCCESS) { return result; } result = putUInt32LEIntoBuffer(buffer, super->version); if (result != UDS_SUCCESS) { return result; } result = putUInt32LEIntoBuffer(buffer, super->blockSize); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, super->numIndexes); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEIntoBuffer(buffer, super->maxSaves); if (result != UDS_SUCCESS) { return result; } result = zeroBytes(buffer, 4); // aligment if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, super->openChapterBlocks); if (result != UDS_SUCCESS) { return result; } result = putUInt64LEIntoBuffer(buffer, super->pageMapBlocks); if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(contentLength(buffer) == sizeof(SuperBlockData), "%zu bytes encoded, of %zu expected", contentLength(buffer), sizeof(SuperBlockData)); return result; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int writeSingleFileHeader(IndexLayout *layout, RegionTable *table, unsigned int numRegions, BufferedWriter *writer) { table->header = (RegionHeader) { .magic = REGION_MAGIC, .regionBlocks = layout->totalBlocks, .type = RH_TYPE_SUPER, .version = 1, .numRegions = numRegions, .payload = sizeof(layout->super), }; size_t tableSize = sizeof(RegionTable) + numRegions * sizeof(LayoutRegion); Buffer *buffer; int result = makeBuffer(tableSize, &buffer); if (result != UDS_SUCCESS) { return result; } result = encodeRegionHeader(buffer, &table->header); unsigned int i; for (i = 0; i < numRegions; i++) { if (result == UDS_SUCCESS) { result = encodeLayoutRegion(buffer, &table->regions[i]); } } if (result == UDS_SUCCESS) { result = writeToBufferedWriter(writer, getBufferContents(buffer), contentLength(buffer)); } freeBuffer(&buffer); if (result != UDS_SUCCESS) { return result; } result = makeBuffer(sizeof(layout->super), &buffer); if (result != UDS_SUCCESS) { return result; } result = encodeSuperBlockData(buffer, &layout->super); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = writeToBufferedWriter(writer, getBufferContents(buffer), contentLength(buffer)); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return result; } return flushBufferedWriter(writer); } /*****************************************************************************/ __attribute__((warn_unused_result)) static int saveSingleFileConfiguration(IndexLayout *layout) { int result = saveSubIndexRegions(layout); if (result != UDS_SUCCESS) { return result; } RegionTable *table; unsigned int numRegions; result = makeSingleFileRegionTable(layout, &numRegions, &table); if (result != UDS_SUCCESS) { return result; } BufferedWriter *writer = NULL; result = openLayoutWriter(layout, &layout->header, &writer); if (result != UDS_SUCCESS) { FREE(table); return result; } result = writeSingleFileHeader(layout, table, numRegions, writer); FREE(table); freeBufferedWriter(writer); return result; } /*****************************************************************************/ void putIndexLayout(IndexLayout **layoutPtr) { if (layoutPtr == NULL) { return; } IndexLayout *layout = *layoutPtr; *layoutPtr = NULL; if ((layout == NULL) || (--layout->refCount > 0)) { return; } SubIndexLayout *sil = &layout->index; if (sil->saves != NULL) { unsigned int j; for (j = 0; j < layout->super.maxSaves; ++j) { IndexSaveLayout *isl = &sil->saves[j]; FREE(isl->masterIndexZones); FREE(isl->openChapter); freeBuffer(&isl->indexStateBuffer); } } FREE(sil->saves); if (layout->factory != NULL) { putIOFactory(layout->factory); } FREE(layout); } /*****************************************************************************/ void getIndexLayout(IndexLayout *layout, IndexLayout **layoutPtr) { ++layout->refCount; *layoutPtr = layout; } /*****************************************************************************/ const struct index_version *getIndexVersion(IndexLayout *layout) { return &layout->indexVersion; } /*****************************************************************************/ int writeIndexConfig(IndexLayout *layout, UdsConfiguration config) { BufferedWriter *writer = NULL; int result = openLayoutWriter(layout, &layout->config, &writer); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "failed to open config region"); } result = writeConfigContents(writer, config); if (result != UDS_SUCCESS) { freeBufferedWriter(writer); return logErrorWithStringError(result, "failed to write config region"); } result = flushBufferedWriter(writer); if (result != UDS_SUCCESS) { freeBufferedWriter(writer); return logErrorWithStringError(result, "cannot flush config writer"); } freeBufferedWriter(writer); return UDS_SUCCESS; } /*****************************************************************************/ int verifyIndexConfig(IndexLayout *layout, UdsConfiguration config) { BufferedReader *reader = NULL; int result = openLayoutReader(layout, &layout->config, &reader); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "failed to open config reader"); } struct udsConfiguration storedConfig; result = readConfigContents(reader, &storedConfig); if (result != UDS_SUCCESS) { freeBufferedReader(reader); return logErrorWithStringError(result, "failed to read config region"); } freeBufferedReader(reader); return (areUdsConfigurationsEqual(&storedConfig, config) ? UDS_SUCCESS : UDS_NO_INDEX); } #ifdef __KERNEL__ /*****************************************************************************/ int openVolumeBufio(IndexLayout *layout, size_t blockSize, unsigned int reservedBuffers, struct dm_bufio_client **clientPtr) { off_t offset = layout->index.volume.startBlock * layout->super.blockSize; return makeBufio(layout->factory, offset, blockSize, reservedBuffers, clientPtr); } #else /*****************************************************************************/ int openVolumeRegion(IndexLayout *layout, IORegion **regionPtr) { LayoutRegion *lr = &layout->index.volume; off_t start = lr->startBlock * layout->super.blockSize; size_t size = lr->numBlocks * layout->super.blockSize; int result = makeIORegion(layout->factory, start, size, regionPtr); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot access index volume region"); } return UDS_SUCCESS; } #endif /*****************************************************************************/ uint64_t getVolumeNonce(IndexLayout *layout) { return layout->index.nonce; } /*****************************************************************************/ static uint64_t generateIndexSaveNonce(uint64_t volumeNonce, IndexSaveLayout *isl) { struct SaveNonceData { IndexSaveData data; uint64_t offset; } nonceData; nonceData.data = isl->saveData; nonceData.data.nonce = 0; nonceData.offset = isl->indexSave.startBlock; byte buffer[sizeof(nonceData)]; size_t offset = 0; encodeUInt64LE(buffer, &offset, nonceData.data.timestamp); encodeUInt64LE(buffer, &offset, nonceData.data.nonce); encodeUInt32LE(buffer, &offset, nonceData.data.version); encodeUInt32LE(buffer, &offset, 0U); // padding encodeUInt64LE(buffer, &offset, nonceData.offset); ASSERT_LOG_ONLY(offset == sizeof(nonceData), "%zu bytes encoded of %zu expected", offset, sizeof(nonceData)); return generateSecondaryNonce(volumeNonce, buffer, sizeof(buffer)); } /*****************************************************************************/ static int validateIndexSaveLayout(IndexSaveLayout *isl, uint64_t volumeNonce, uint64_t *saveTimePtr) { if (isl->saveType == NO_SAVE || isl->numZones == 0 || isl->saveData.timestamp == 0) { return UDS_BAD_STATE; } if (isl->saveData.nonce != generateIndexSaveNonce(volumeNonce, isl)) { return UDS_BAD_STATE; } if (saveTimePtr != NULL) { *saveTimePtr = isl->saveData.timestamp; } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int selectOldestIndexSaveLayout(SubIndexLayout *sil, unsigned int maxSaves, IndexSaveLayout **islPtr) { IndexSaveLayout *oldest = NULL; uint64_t oldestTime = 0; // find the oldest valid or first invalid slot IndexSaveLayout *isl; for (isl = sil->saves; isl < sil->saves + maxSaves; ++isl) { uint64_t saveTime = 0; int result = validateIndexSaveLayout(isl, sil->nonce, &saveTime); if (result != UDS_SUCCESS) { saveTime = 0; } if (oldest == NULL || saveTime < oldestTime) { oldest = isl; oldestTime = saveTime; } } int result = ASSERT((oldest != NULL), "no oldest or free save slot"); if (result != UDS_SUCCESS) { return result; } *islPtr = oldest; return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int selectLatestIndexSaveLayout(SubIndexLayout *sil, unsigned int maxSaves, IndexSaveLayout **islPtr) { IndexSaveLayout *latest = NULL; uint64_t latestTime = 0; // find the latest valid save slot IndexSaveLayout *isl; for (isl = sil->saves; isl < sil->saves + maxSaves; ++isl) { uint64_t saveTime = 0; int result = validateIndexSaveLayout(isl, sil->nonce, &saveTime); if (result != UDS_SUCCESS) { continue; } if (saveTime > latestTime) { latest = isl; latestTime = saveTime; } } if (latest == NULL) { return UDS_INDEX_NOT_SAVED_CLEANLY; } *islPtr = latest; return UDS_SUCCESS; } /*****************************************************************************/ static uint64_t getTimeMS(AbsTime time) { time_t t = asTimeT(time); RelTime r = timeDifference(time, fromTimeT(t)); return (uint64_t) t * 1000 + relTimeToMilliseconds(r); } /*****************************************************************************/ __attribute__((warn_unused_result)) static int instantiateIndexSaveLayout(IndexSaveLayout *isl, SuperBlockData *super, uint64_t volumeNonce, unsigned int numZones, IndexSaveType saveType) { int result = UDS_SUCCESS; if (isl->openChapter && saveType == IS_CHECKPOINT) { FREE(isl->openChapter); isl->openChapter = NULL; } else if (isl->openChapter == NULL && saveType == IS_SAVE) { result = ALLOCATE(1, LayoutRegion, "open chapter layout", &isl->openChapter); if (result != UDS_SUCCESS) { return result; } } if (numZones != isl->numZones) { if (isl->masterIndexZones != NULL) { FREE(isl->masterIndexZones); } result = ALLOCATE(numZones, LayoutRegion, "master index zone layouts", &isl->masterIndexZones); if (result != UDS_SUCCESS) { return result; } isl->numZones = numZones; } populateIndexSaveLayout(isl, super, numZones, saveType); result = makeBuffer(INDEX_STATE_BUFFER_SIZE, &isl->indexStateBuffer); if (result != UDS_SUCCESS) { return result; } isl->read = isl->written = false; isl->saveType = saveType; memset(&isl->saveData, 0, sizeof(isl->saveData)); isl->saveData.timestamp = getTimeMS(currentTime(CLOCK_REALTIME)); isl->saveData.version = 1; isl->saveData.nonce = generateIndexSaveNonce(volumeNonce, isl); return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int invalidateOldSave(IndexLayout *layout, IndexSaveLayout *isl) { uint64_t startBlock = isl->indexSave.startBlock; uint64_t saveBlocks = isl->indexSave.numBlocks; unsigned int save = isl->indexSave.instance; int result = resetIndexSaveLayout(isl, &startBlock, saveBlocks, layout->super.pageMapBlocks, save); if (result != UDS_SUCCESS) { return result; } return writeIndexSaveLayout(layout, isl); } /*****************************************************************************/ int setupIndexSaveSlot(IndexLayout *layout, unsigned int numZones, IndexSaveType saveType, unsigned int *saveSlotPtr) { SubIndexLayout *sil = &layout->index; IndexSaveLayout *isl = NULL; int result = selectOldestIndexSaveLayout(sil, layout->super.maxSaves, &isl); if (result != UDS_SUCCESS) { return result; } result = invalidateOldSave(layout, isl); if (result != UDS_SUCCESS) { return result; } result = instantiateIndexSaveLayout(isl, &layout->super, sil->nonce, numZones, saveType); if (result != UDS_SUCCESS) { return result; } *saveSlotPtr = isl - sil->saves; return UDS_SUCCESS; } /*****************************************************************************/ int findLatestIndexSaveSlot(IndexLayout *layout, unsigned int *numZonesPtr, unsigned int *slotPtr) { SubIndexLayout *sil = &layout->index; IndexSaveLayout *isl = NULL; int result = selectLatestIndexSaveLayout(sil, layout->super.maxSaves, &isl); if (result != UDS_SUCCESS) { return result; } if (numZonesPtr != NULL) { *numZonesPtr = isl->numZones; } if (slotPtr != NULL) { *slotPtr = isl - sil->saves; } return UDS_SUCCESS; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int makeIndexSaveRegionTable(IndexSaveLayout *isl, unsigned int *numRegionsPtr, RegionTable **tablePtr) { unsigned int numRegions = 1 + // header 1 + // index page map isl->numZones + // master index zones (bool) isl->openChapter; // open chapter if needed if (isl->freeSpace.numBlocks > 0) { numRegions++; } RegionTable *table; int result = ALLOCATE_EXTENDED(RegionTable, numRegions, LayoutRegion, "layout region table for ISL", &table); if (result != UDS_SUCCESS) { return result; } LayoutRegion *lr = &table->regions[0]; *lr++ = isl->header; *lr++ = isl->indexPageMap; unsigned int z; for (z = 0; z < isl->numZones; ++z) { *lr++ = isl->masterIndexZones[z]; } if (isl->openChapter) { *lr++ = *isl->openChapter; } if (isl->freeSpace.numBlocks > 0) { *lr++ = isl->freeSpace; } result = ASSERT((lr == &table->regions[numRegions]), "incorrect number of ISL regions"); if (result != UDS_SUCCESS) { return result; } *numRegionsPtr = numRegions; *tablePtr = table; return UDS_SUCCESS; } /*****************************************************************************/ static unsigned int regionTypeForSaveType(IndexSaveType saveType) { switch (saveType) { case IS_SAVE: return RH_TYPE_SAVE; case IS_CHECKPOINT: return RH_TYPE_CHECKPOINT; default: break; } return RH_TYPE_UNSAVED; } /*****************************************************************************/ __attribute__((warn_unused_result)) static int writeIndexSaveHeader(IndexSaveLayout *isl, RegionTable *table, unsigned int numRegions, BufferedWriter *writer) { size_t payload = sizeof(isl->saveData); if (isl->indexStateBuffer != NULL) { payload += contentLength(isl->indexStateBuffer); } table->header = (RegionHeader) { .magic = REGION_MAGIC, .regionBlocks = isl->indexSave.numBlocks, .type = regionTypeForSaveType(isl->saveType), .version = 1, .numRegions = numRegions, .payload = payload, }; size_t tableSize = sizeof(RegionTable) + numRegions * sizeof(LayoutRegion); Buffer *buffer; int result = makeBuffer(tableSize, &buffer); if (result != UDS_SUCCESS) { return result; } result = encodeRegionHeader(buffer, &table->header); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } unsigned int i; for (i = 0; i < numRegions; i++) { result = encodeLayoutRegion(buffer, &table->regions[i]); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } } result = ASSERT_LOG_ONLY(contentLength(buffer) == tableSize, "%zu bytes encoded of %zu expected", contentLength(buffer), tableSize); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = writeToBufferedWriter(writer, getBufferContents(buffer), contentLength(buffer)); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return result; } result = makeBuffer(sizeof(isl->saveData), &buffer); if (result != UDS_SUCCESS) { return result; } result = encodeIndexSaveData(buffer, &isl->saveData); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = writeToBufferedWriter(writer, getBufferContents(buffer), contentLength(buffer)); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return result; } if (isl->indexStateBuffer != NULL) { result = writeToBufferedWriter(writer, getBufferContents(isl->indexStateBuffer), contentLength(isl->indexStateBuffer)); if (result != UDS_SUCCESS) { return result; } } return flushBufferedWriter(writer); } /*****************************************************************************/ static int writeIndexSaveLayout(IndexLayout *layout, IndexSaveLayout *isl) { unsigned int numRegions; RegionTable *table; int result = makeIndexSaveRegionTable(isl, &numRegions, &table); if (result != UDS_SUCCESS) { return result; } BufferedWriter *writer = NULL; result = openLayoutWriter(layout, &isl->header, &writer); if (result != UDS_SUCCESS) { FREE(table); return result; } result = writeIndexSaveHeader(isl, table, numRegions, writer); FREE(table); freeBufferedWriter(writer); isl->written = true; return result; } /*****************************************************************************/ int commitIndexSave(IndexLayout *layout, unsigned int saveSlot) { int result = ASSERT((saveSlot < layout->super.maxSaves), "save slot out of range"); if (result != UDS_SUCCESS) { return result; } IndexSaveLayout *isl = &layout->index.saves[saveSlot]; if (bufferUsed(isl->indexStateBuffer) == 0) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "%s: no index state data saved", __func__); } return writeIndexSaveLayout(layout, isl); } /*****************************************************************************/ static void mutilateIndexSaveInfo(IndexSaveLayout *isl) { memset(&isl->saveData, 0, sizeof(isl->saveData)); isl->read = isl->written = 0; isl->saveType = NO_SAVE; isl->numZones = 0; freeBuffer(&isl->indexStateBuffer); } /*****************************************************************************/ int cancelIndexSave(IndexLayout *layout, unsigned int saveSlot) { int result = ASSERT((saveSlot < layout->super.maxSaves), "save slot out of range"); if (result != UDS_SUCCESS) { return result; } mutilateIndexSaveInfo(&layout->index.saves[saveSlot]); return UDS_SUCCESS; } /*****************************************************************************/ int discardIndexSaves(IndexLayout *layout, bool all) { int result = UDS_SUCCESS; SubIndexLayout *sil = &layout->index; if (all) { unsigned int i; for (i = 0; i < layout->super.maxSaves; ++i) { IndexSaveLayout *isl = &sil->saves[i]; result = firstError(result, invalidateOldSave(layout, isl)); } } else { IndexSaveLayout *isl; result = selectLatestIndexSaveLayout(sil, layout->super.maxSaves, &isl); if (result == UDS_SUCCESS) { result = invalidateOldSave(layout, isl); } } return result; } /*****************************************************************************/ static int createIndexLayout(IndexLayout *layout, uint64_t size, const UdsConfiguration config) { if (config == NULL) { return UDS_CONF_PTR_REQUIRED; } SaveLayoutSizes sizes; int result = computeSizes(&sizes, config, UDS_BLOCK_SIZE, 0); if (result != UDS_SUCCESS) { return result; } if (size < sizes.totalBlocks * sizes.blockSize) { return logErrorWithStringError(UDS_INSUFFICIENT_INDEX_SPACE, "layout requires at least %" PRIu64 " bytes", sizes.totalBlocks * sizes.blockSize); } result = initSingleFileLayout(layout, layout->offset, size, &sizes); if (result != UDS_SUCCESS) { return result; } result = saveSingleFileConfiguration(layout); if (result != UDS_SUCCESS) { return result; } return UDS_SUCCESS; } /*****************************************************************************/ Buffer *getIndexStateBuffer(IndexLayout *layout, unsigned int slot) { return layout->index.saves[slot].indexStateBuffer; } /*****************************************************************************/ static int findLayoutRegion(IndexLayout *layout, unsigned int slot, const char *operation, RegionKind kind, unsigned int zone, LayoutRegion **lrPtr) { int result = ASSERT((slot < layout->super.maxSaves), "%s not started", operation); if (result != UDS_SUCCESS) { return result; } IndexSaveLayout *isl = &layout->index.saves[slot]; LayoutRegion *lr = NULL; switch (kind) { case RL_KIND_INDEX_PAGE_MAP: lr = &isl->indexPageMap; break; case RL_KIND_OPEN_CHAPTER: if (isl->openChapter == NULL) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "%s: %s has no open chapter", __func__, operation); } lr = isl->openChapter; break; case RL_KIND_MASTER_INDEX: if (isl->masterIndexZones == NULL || zone >= isl->numZones) { return logErrorWithStringError(UDS_UNEXPECTED_RESULT, "%s: %s has no master index zone %u", __func__, operation, zone); } lr = &isl->masterIndexZones[zone]; break; default: return logErrorWithStringError(UDS_INVALID_ARGUMENT, "%s: unexpected kind %u", __func__, kind); } *lrPtr = lr; return UDS_SUCCESS; } /*****************************************************************************/ int openIndexBufferedReader(IndexLayout *layout, unsigned int slot, RegionKind kind, unsigned int zone, BufferedReader **readerPtr) { LayoutRegion *lr = NULL; int result = findLayoutRegion(layout, slot, "load", kind, zone, &lr); if (result != UDS_SUCCESS) { return result; } return openLayoutReader(layout, lr, readerPtr); } /*****************************************************************************/ int openIndexBufferedWriter(IndexLayout *layout, unsigned int slot, RegionKind kind, unsigned int zone, BufferedWriter **writerPtr) { LayoutRegion *lr = NULL; int result = findLayoutRegion(layout, slot, "save", kind, zone, &lr); if (result != UDS_SUCCESS) { return result; } return openLayoutWriter(layout, lr, writerPtr); } /*****************************************************************************/ int makeIndexLayoutFromFactory(IOFactory *factory, off_t offset, uint64_t namedSize, bool newLayout, const UdsConfiguration config, IndexLayout **layoutPtr) { // Get the device size and round it down to a multiple of UDS_BLOCK_SIZE. size_t size = getWritableSize(factory) & -UDS_BLOCK_SIZE; if (namedSize > size) { return logErrorWithStringError(UDS_INSUFFICIENT_INDEX_SPACE, "index storage (%zu) is smaller than the" " requested size %llu", size, namedSize); } if ((namedSize > 0) && (namedSize < size)) { size = namedSize; } // Get the index size according the the config uint64_t configSize; int result = udsComputeIndexSize(config, 0, &configSize); if (result != UDS_SUCCESS) { return result; } if (size < configSize) { return logErrorWithStringError(UDS_INSUFFICIENT_INDEX_SPACE, "index storage (%zu) is smaller than the" " required size %llu", size, configSize); } size = configSize; IndexLayout *layout = NULL; result = ALLOCATE(1, IndexLayout, __func__, &layout); if (result != UDS_SUCCESS) { return result; } layout->refCount = 1; getIOFactory(factory); layout->factory = factory; layout->offset = offset; if (newLayout) { // Populate the layout from the UDSConfiguration result = createIndexLayout(layout, size, config); } else { // Populate the layout from the saved index. result = loadIndexLayout(layout); } if (result != UDS_SUCCESS) { putIndexLayout(&layout); return result; } *layoutPtr = layout; return UDS_SUCCESS; }