/* * 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/indexPageMap.c#4 $ */ #include "indexPageMap.h" #include "buffer.h" #include "bufferedWriter.h" #include "compiler.h" #include "errors.h" #include "hashUtils.h" #include "indexComponent.h" #include "logger.h" #include "memoryAlloc.h" #include "permassert.h" #include "stringUtils.h" #include "threads.h" #include "uds.h" static int readIndexPageMap(ReadPortal *portal); static int writeIndexPageMap(IndexComponent *component, BufferedWriter *writer, unsigned int zone); static const byte INDEX_PAGE_MAP_MAGIC[] = "ALBIPM02"; enum { INDEX_PAGE_MAP_MAGIC_LENGTH = sizeof(INDEX_PAGE_MAP_MAGIC) - 1, }; const IndexComponentInfo INDEX_PAGE_MAP_INFO = { .kind = RL_KIND_INDEX_PAGE_MAP, .name = "index page map", .saveOnly = false, .chapterSync = true, .multiZone = false, .ioStorage = true, .loader = readIndexPageMap, .saver = writeIndexPageMap, .incremental = NULL, }; /*****************************************************************************/ static INLINE size_t numEntries(const Geometry *geometry) { return geometry->chaptersPerVolume * (geometry->indexPagesPerChapter - 1); } /*****************************************************************************/ int makeIndexPageMap(const Geometry *geometry, IndexPageMap **mapPtr) { unsigned int deltaListsPerChapter = geometry->deltaListsPerChapter; int result = ASSERT_WITH_ERROR_CODE(((deltaListsPerChapter - 1) <= UINT16_MAX), UDS_BAD_STATE, "delta lists per chapter (%u) is too large", deltaListsPerChapter); if (result != UDS_SUCCESS) { return result; } IndexPageMap *map; result = ALLOCATE(1, IndexPageMap, "Index Page Map", &map); if (result != UDS_SUCCESS) { return result; } map->geometry = geometry; result = ALLOCATE(numEntries(geometry), IndexPageMapEntry, "Index Page Map Entries", &map->entries); if (result != UDS_SUCCESS) { freeIndexPageMap(map); return result; } *mapPtr = map; return UDS_SUCCESS; } /*****************************************************************************/ void freeIndexPageMap(IndexPageMap *map) { if (map != NULL) { FREE(map->entries); FREE(map); } } /*****************************************************************************/ uint64_t getLastUpdate(const IndexPageMap *map) { return map->lastUpdate; } /*****************************************************************************/ int updateIndexPageMap(IndexPageMap *map, uint64_t virtualChapterNumber, unsigned int chapterNumber, unsigned int indexPageNumber, unsigned int deltaListNumber) { const Geometry *geometry = map->geometry; if ((virtualChapterNumber < map->lastUpdate) || (virtualChapterNumber > map->lastUpdate + 1)) { // if the lastUpdate is 0, this is likely to be normal because we are // replaying the volume if (map->lastUpdate != 0) { logWarning("unexpected index page map update, jumping from %" PRIu64 " to %llu", map->lastUpdate, virtualChapterNumber); } } map->lastUpdate = virtualChapterNumber; if (chapterNumber >= geometry->chaptersPerVolume) { return logErrorWithStringError( UDS_INVALID_ARGUMENT, "chapter number %u exceeds maximum %u", chapterNumber, geometry->chaptersPerVolume - 1); } if (indexPageNumber >= geometry->indexPagesPerChapter) { return logErrorWithStringError( UDS_INVALID_ARGUMENT, "index page number %u exceeds maximum %u", indexPageNumber, geometry->indexPagesPerChapter - 1); } if (deltaListNumber >= geometry->deltaListsPerChapter) { return logErrorWithStringError( UDS_INVALID_ARGUMENT, "delta list number %u exceeds maximum %u", deltaListNumber, geometry->deltaListsPerChapter - 1); } if (indexPageNumber == (geometry->indexPagesPerChapter - 1)) { /* * There is no entry for the last index page of a chapter since its entry * would always be geometry->deltaListsPerChapter - 1. */ return UDS_SUCCESS; } size_t slot = (chapterNumber * (geometry->indexPagesPerChapter - 1)) + indexPageNumber; map->entries[slot] = (IndexPageMapEntry) deltaListNumber; return UDS_SUCCESS; } /*****************************************************************************/ int findIndexPageNumber(const IndexPageMap *map, const UdsChunkName *name, unsigned int chapterNumber, unsigned int *indexPageNumberPtr) { const Geometry *geometry = map->geometry; if (chapterNumber >= geometry->chaptersPerVolume) { return logErrorWithStringError( UDS_INVALID_ARGUMENT, "chapter number %u exceeds maximum %u", chapterNumber, geometry->chaptersPerVolume - 1); } unsigned int deltaListNumber = hashToChapterDeltaList(name, geometry); unsigned int slot = (chapterNumber * (geometry->indexPagesPerChapter - 1)); unsigned int limit = slot + (geometry->indexPagesPerChapter - 1); unsigned int indexPageNumber = 0; for (; slot < limit; indexPageNumber++, slot++) { if (deltaListNumber <= map->entries[slot]) { break; } } // This should be a clear post-condition of the loop above, but just in case // it's not obvious, the check is cheap. int result = ASSERT((indexPageNumber < geometry->indexPagesPerChapter), "index page number too large"); if (result != UDS_SUCCESS) { return result; } *indexPageNumberPtr = indexPageNumber; return UDS_SUCCESS; } /**********************************************************************/ int getListNumberBounds(const IndexPageMap *map, unsigned int chapterNumber, unsigned int indexPageNumber, IndexPageBounds *bounds) { const Geometry *geometry = map->geometry; int result = ASSERT((chapterNumber < geometry->chaptersPerVolume), "chapter number is valid"); if (result != UDS_SUCCESS) { return result; } result = ASSERT((indexPageNumber < geometry->indexPagesPerChapter), "index page number is valid"); if (result != UDS_SUCCESS) { return result; } unsigned int slot = chapterNumber * (geometry->indexPagesPerChapter - 1); bounds->lowestList = ((indexPageNumber == 0) ? 0 : map->entries[slot + indexPageNumber - 1] + 1); bounds->highestList = ((indexPageNumber == geometry->indexPagesPerChapter - 1) ? geometry->deltaListsPerChapter - 1 : map->entries[slot + indexPageNumber]); return UDS_SUCCESS; } /*****************************************************************************/ size_t indexPageMapSize(const Geometry *geometry) { return sizeof(IndexPageMapEntry) * numEntries(geometry); } /*****************************************************************************/ static int writeIndexPageMap(IndexComponent *component, BufferedWriter *writer, unsigned int zone) { int result = ASSERT((zone == 0), "unimplemented zone %d", zone); if (result != UDS_SUCCESS) { return result; } IndexPageMap *map = indexComponentData(component); Buffer *buffer; result = makeBuffer(INDEX_PAGE_MAP_MAGIC_LENGTH + sizeof(map->lastUpdate), &buffer); if (result != UDS_SUCCESS) { return result; } result = putBytes(buffer, INDEX_PAGE_MAP_MAGIC_LENGTH, INDEX_PAGE_MAP_MAGIC); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = putUInt64LEIntoBuffer(buffer, map->lastUpdate); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = writeToBufferedWriter(writer, getBufferContents(buffer), contentLength(buffer)); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot write index page map header"); } result = makeBuffer(indexPageMapSize(map->geometry), &buffer); if (result != UDS_SUCCESS) { return result; } result = putUInt16LEsIntoBuffer(buffer, numEntries(map->geometry), map->entries); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = writeToBufferedWriter(writer, getBufferContents(buffer), contentLength(buffer)); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot write index page map data"); } return UDS_SUCCESS; } /*****************************************************************************/ uint64_t computeIndexPageMapSaveSize(const Geometry *geometry) { return indexPageMapSize(geometry) + INDEX_PAGE_MAP_MAGIC_LENGTH + sizeof(((IndexPageMap *) 0)->lastUpdate); } /**********************************************************************/ __attribute__((warn_unused_result)) static int decodeIndexPageMap(Buffer *buffer, IndexPageMap *map) { int result = getUInt64LEFromBuffer(buffer, &map->lastUpdate); if (result != UDS_SUCCESS) { return result; } result = getUInt16LEsFromBuffer(buffer, numEntries(map->geometry), map->entries); if (result != UDS_SUCCESS) { return result; } result = ASSERT_LOG_ONLY(contentLength(buffer) == 0, "%zu bytes decoded of %zu expected", bufferLength(buffer) - contentLength(buffer), bufferLength(buffer)); return result; } /*****************************************************************************/ static int readIndexPageMap(ReadPortal *portal) { IndexPageMap *map = indexComponentData(portal->component); BufferedReader *reader = NULL; int result = getBufferedReaderForPortal(portal, 0, &reader); if (result != UDS_SUCCESS) { return result; } result = verifyBufferedData(reader, INDEX_PAGE_MAP_MAGIC, INDEX_PAGE_MAP_MAGIC_LENGTH); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "bad index page map saved magic"); } Buffer *buffer; result = makeBuffer(sizeof(map->lastUpdate) + indexPageMapSize(map->geometry), &buffer); if (result != UDS_SUCCESS) { return result; } result = readFromBufferedReader(reader, getBufferContents(buffer), bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); logErrorWithStringError(result, "cannot read index page map data"); return result; } result = resetBufferEnd(buffer, bufferLength(buffer)); if (result != UDS_SUCCESS) { freeBuffer(&buffer); return result; } result = decodeIndexPageMap(buffer, map); freeBuffer(&buffer); if (result != UDS_SUCCESS) { return result; } logDebug("read index page map, last update %llu", map->lastUpdate); return UDS_SUCCESS; }