void gdbWriteHeader(GDatabase *db) { char version[2]; char type; if (db == NULL || db->fp == NULL) return; version[0] = DB_MAJOR_VER; version[1] = DB_MINOR_VER; type = (char)db->type; fseek(db->fp, 0, SEEK_SET); /* Write the magic string. */ fputs(DB_MAGIC, db->fp); fwrite(version, sizeof(char), 2, db->fp); fwrite(&type, sizeof(char), 1, db->fp); if (DB_HEADER_BLOCK_SIZE > DB_HEADER_DATA_SIZE) gdbPad(db->fp, DB_HEADER_BLOCK_SIZE - DB_HEADER_DATA_SIZE); fflush(db->fp); }
void btreeEraseNode(BTreeNode *node) { GdbBlock *block; if (node == NULL || node->block->offset == 0) { return; } block = node->block; gdbFreeBlock(block->db, block->offset, block->type); #if 0 rawFileSeek(block->db->fp, block->offset, SEEK_SET); gdbPad(block->db->fp, block->size); #endif }
GDatabase * gdbCreate(const char *filename, GdbType type) { GDatabase *db; FILE *fp; cxReturnValueUnless(filename != NULL, NULL); fp = fopen(filename, "w+"); if (fp == NULL) { pmError(PM_ERROR_WARNING, _("GNUpdate DB: " "Unable to open database %s for reading/writing.\n"), filename); return NULL; } MEM_CHECK(db = (GDatabase *)malloc(sizeof(GDatabase))); memset(db, 0, sizeof(GDatabase)); __setupDatabase(db); db->filename = strdup(filename); db->type = type; db->fp = fp; gdbWriteHeader(db); /* Leave enough room for the free block list. */ fseek(db->fp, DB_FREE_BLOCK_LIST_OFFSET, SEEK_SET); gdbPad(db->fp, DB_FREE_BLOCK_LIST_SIZE); db->mainTree = btreeCreate(db, 5); return db; }
void gdbWriteHeader(GDatabase *db) { uint8_t version[2]; uint8_t type; offset_t offset; if (db == NULL || db->idxRawFile == NULL) { return; } version[0] = DB_MAJOR_VER; version[1] = DB_MINOR_VER; type = db->type; rawFileSeek(db->idxRawFile, 0, SEEK_SET); offset = 0; /* Write the magic string. */ rawFilePuts(db->idxRawFile, offset, DB_MAGIC); offset += strlen(DB_MAGIC); rawFileWrite(db->idxRawFile, offset, version, sizeof(uint8_t), 2, LOC_DB_0011); offset += (sizeof(uint8_t) * 2); rawFileWrite(db->idxRawFile, offset, &type, sizeof(uint8_t), 1, LOC_DB_0012); offset += sizeof(uint8_t); if (DB_HEADER_BLOCK_SIZE > DB_HEADER_DATA_SIZE) { gdbPad(db->idxRawFile, offset, DB_HEADER_BLOCK_SIZE - DB_HEADER_DATA_SIZE); } }
offset_t * gdbReserveBlockChain(GDatabase *db, unsigned short count, blocktype_t blockType) { GdbFreeBlock *freeBlocks, *newFreeBlocks; offset_t *chain; offset_t offset; unsigned short blockSize; long blockCount, fillCount, newListCount; long i, j, result; if (db == NULL || count == 0 || !GDB_VALID_BLOCK_TYPE(blockType)) return NULL; /* Get the block size for this type. */ blockSize = blockTypeInfo[blockType - 1].multiple; /* Create the chain. */ MEM_CHECK(chain = (offset_t *)malloc(count * sizeof(offset_t))); /* Lock the free block list. */ gdbLockFreeBlockList(db, DB_WRITE_LOCK); /* Get the free block list. */ result = gdbGetFreeBlockList(db, &freeBlocks, &blockCount); if (result == 0) { gdbUnlockFreeBlockList(db); gdbFreeBlockList(freeBlocks); fseek(db->fp, 0L, SEEK_END); offset = ftell(db->fp); /* Fill in the chain with the reserved offsets. */ for (i = 0; i < count; i++) chain[i] = offset + (i * blockSize); gdbPad(db->fp, count * blockSize); return chain; } fillCount = 0; j = 0; /* Create the new array of free blocks. */ MEM_CHECK(newFreeBlocks = (GdbFreeBlock *)malloc(blockCount * sizeof(GdbFreeBlock))); memset(newFreeBlocks, 0, blockCount * sizeof(GdbFreeBlock)); for (i = 0; i < blockCount; i++) { if (fillCount < count && freeBlocks[i].size == blockSize) { chain[fillCount++] = freeBlocks[i].offset; } else { newFreeBlocks[j].offset = freeBlocks[i].offset; newFreeBlocks[j].size = freeBlocks[i].size; j++; } } newListCount = j; if (fillCount != count) { if (fillCount > 0) gdbWriteFreeBlockList(db, newFreeBlocks, newListCount); gdbUnlockFreeBlockList(db); gdbFreeBlockList(newFreeBlocks); gdbFreeBlockList(freeBlocks); fseek(db->fp, 0L, SEEK_END); offset = ftell(db->fp); /* Fill in the chain with the reserved offsets. */ for (i = fillCount, j = 0; i < count; i++, j++) chain[i] = offset + (j * blockSize); gdbPad(db->fp, (count - fillCount) * blockSize); qsort(chain, count, sizeof(offset_t), __offsetCompare); return chain; } /* Write the new list to disk. */ gdbWriteFreeBlockList(db, newFreeBlocks, newListCount); /* Unlock the list. */ gdbUnlockFreeBlockList(db); /* Free up the memory for the lists. */ gdbFreeBlockList(newFreeBlocks); gdbFreeBlockList(freeBlocks); /* Sort it. */ qsort(chain, count, sizeof(offset_t), __offsetCompare); return chain; }
void gdbWriteBlock(GdbBlock *block) { GDatabase *db; char *buffer; offset_t *oldChain; blocktype_t typeIndex; unsigned int oldChainCount, oldDataSize; unsigned int i, pos; if (block == NULL || !GDB_IS_DIRTY(block)) return; /* Set a couple of vars we'll be using. */ db = block->db; typeIndex = block->type - 1; /* Save the old data. */ oldDataSize = block->dataSize; oldChainCount = block->chainCount; oldChain = block->chain; /* See if there is a write function assigned. */ if (blockTypeInfo[typeIndex].writeBlock != NULL) { /* Write the block info to a buffer. */ blockTypeInfo[typeIndex].writeBlock(block, &buffer, &block->dataSize); } else { buffer = (char *)block->detail; } if (buffer == NULL) { pmError(PM_ERROR_FATAL, _("GNUpdate DB: buffer == NULL in %s, line %d\n"), __FILE__, __LINE__); exit(1); } /* Get the number of needed blocks. */ block->chainCount = gdbGetNeededBlockCount(block->dataSize, block->multiple); if (oldChainCount == 0) { /* Reserve new blocks. */ block->chain = gdbReserveBlockChain(db, block->chainCount, block->type); } else if (block->chainCount < oldChainCount) { /* The number of needed blocks is shorter than before. */ MEM_CHECK(block->chain = (offset_t *)malloc(block->chainCount * sizeof(offset_t))); memcpy(block->chain, oldChain, block->chainCount * sizeof(offset_t)); } else if (block->chainCount > oldChainCount) { offset_t *newChain; int j; /* The number of needed blocks is longer than before. */ MEM_CHECK(block->chain = (offset_t *)malloc(block->chainCount * sizeof(offset_t))); newChain = gdbReserveBlockChain(db, block->chainCount - oldChainCount, block->type); memcpy(block->chain, oldChain, oldChainCount * sizeof(offset_t)); for (i = oldChainCount, j = 0; i < block->chainCount; i++, j++) block->chain[i] = newChain[j]; free(newChain); } /* * Set the offset and next block, if this spills over into * additional blocks. */ block->offset = block->chain[0]; if (block->chainCount > 1) block->next = block->chain[1]; else block->next = 0; /* Write the first block header */ gdbWriteBlockHeader(block); /* Write the first block. */ fwrite(buffer, 1, (block->dataSize < block->multiple - GDB_BLOCK_HEADER_SIZE ? block->dataSize : block->multiple - GDB_BLOCK_HEADER_SIZE), db->fp); if (block->dataSize < block->multiple - GDB_BLOCK_HEADER_SIZE) { gdbPad(db->fp, block->multiple - GDB_BLOCK_HEADER_SIZE - block->dataSize); } else { char *blockBuffer; MEM_CHECK(blockBuffer = (char *)malloc(block->multiple)); pos = block->multiple - GDB_BLOCK_HEADER_SIZE; /* Write any overflow blocks. */ for (i = 1; i < block->chainCount; i++) { offset_t nextOffset; unsigned long relPos; nextOffset = ((i + 1 < block->chainCount) ? block->chain[i + 1] : 0); relPos = block->dataSize - pos; /* Reset the block buffer. */ memset(blockBuffer, 0, block->multiple); /* Write to it. */ nextOffset = htonl(nextOffset); memcpy(blockBuffer, &nextOffset, sizeof(offset_t)); memcpy(blockBuffer + sizeof(offset_t), buffer + pos, (relPos < block->multiple - sizeof(offset_t) ? relPos : block->multiple - sizeof(offset_t))); /* Write the block buffer. */ if (block->chain[i - 1] + block->multiple != block->chain[i]) fseek(db->fp, block->chain[i], SEEK_SET); fwrite(blockBuffer, 1, block->multiple, db->fp); pos += block->multiple - sizeof(offset_t); } free(blockBuffer); } if (oldChainCount != 0) { /* If the chain shrunk, free up the discarded blocks. */ if (block->chainCount < oldChainCount) { gdbFreeBlockChain(db, &oldChain[block->chainCount], oldChainCount - block->chainCount, block->type); } if (oldChainCount != block->chainCount) free(oldChain); } fflush(db->fp); if (buffer != block->detail) free(buffer); }
GdbStatus gdbAddTree(GDatabase *db, BTree *tree, const char *key, BTree **newTree) { GdbStatus status; offset_t offset; short blockSize; blocktype_t type; if (db == NULL || tree == NULL || db->fp == NULL || key == NULL || newTree == NULL) { return GDB_ERROR; } *newTree = btreeCreate(db, 5); offset = (*newTree)->block->offset; blockSize = (*newTree)->block->multiple; type = (*newTree)->block->type; if (*newTree == NULL) { *newTree = NULL; return GDB_ERROR; } status = btreeInsert(tree, key, offset, 0); if (status == GDB_DUPLICATE) { /* Return the existing newTree. */ btreeClose(*newTree); gdbFreeBlock(db, offset, type); offset = btreeSearch(tree, key); if (offset == 0) { /* I doubt this will ever happen. */ pmError(PM_ERROR_FATAL, _("GNUpdate DB: Possible database corruption! Back up " "your database and contact a developer.\n")); exit(1); } *newTree = btreeOpen(db, offset); status = GDB_SUCCESS; } else if (status == GDB_ERROR) { btreeClose(*newTree); fseek(db->fp, offset, SEEK_SET); #if 0 gdbPad(db->fp, blockSize); #endif gdbFreeBlock(db, offset, type); *newTree = NULL; } return status; }