// Write DataVault to Disk eEsifError DataVault_WriteVault(DataVaultPtr self) { eEsifError rc = ESIF_OK; DataCacheEntryPtr pairPtr = NULL; IOStreamPtr diskStreamPtr = NULL; IOStreamPtr memStreamPtr = NULL; BytePtr memStreamBuffer = NULL; size_t memStreamBufSize = 0; UInt32 idx; DataCachePtr cacheClonePtr = NULL; if (FLAGS_TEST(self->flags, ESIF_SERVICE_CONFIG_STATIC | ESIF_SERVICE_CONFIG_READONLY)) { rc = ESIF_E_READONLY; goto exit; } if (self->stream->file.name == NULL || self->cache == NULL) { rc = ESIF_E_PARAMETER_IS_NULL; goto exit; } // // As the cache data can be modified while writing the data vault (due to // non-cached values), we create a clone of the current cache so that we can // fall back to it in case of a failure. // cacheClonePtr = DataCache_Clone(self->cache); if ((NULL == cacheClonePtr) && (self->cache != NULL)) { rc = ESIF_E_NO_MEMORY; goto exit; } // // Use a memory stream for creation before writing the final output to a file // memStreamPtr = IOStream_Create(); if (!memStreamPtr) { rc = ESIF_E_NO_MEMORY; goto exit; } if (IOStream_SetMemory(memStreamPtr, NULL, 0) != EOK) { rc = ESIF_E_NO_MEMORY; goto exit; } // // If any rows contain NOCACHE PERSIST values, we need to read them from the // original file before we overwrite it // diskStreamPtr = IOStream_Create(); if (diskStreamPtr == NULL) { rc = ESIF_E_NO_MEMORY; goto exit; } if (IOStream_Seek(memStreamPtr, sizeof(DataVaultHeader), SEEK_CUR) != EOK) { rc = ESIF_E_IO_ERROR; goto exit; } // // Write the key/value pairs to the memory stream for each data category // Fill in the data header for each category after the key/value pairs are written // Note: Only write persisted data // // Write All Persisted Rows from Sorted List to DataVault for (idx = 0; rc == ESIF_OK && idx < self->cache->size; idx++) { pairPtr = &self->cache->elements[idx]; if (!(pairPtr->flags & ESIF_SERVICE_CONFIG_PERSIST)) { continue; } rc = DataVault_WriteKeyValuePair(self, pairPtr, memStreamPtr); if (rc != ESIF_OK) { goto exit; } } // // Now go back and complete the headers // Fill in and write the main header // if (IOStream_Open(memStreamPtr) != EOK) { rc = ESIF_E_IO_ERROR; goto exit; } DataVaultHeader header = { 0 }; esif_ccb_memcpy(&header.signature, ESIFDV_SIGNATURE, sizeof(header.signature)); header.headersize = sizeof(header); header.version = ESIFDV_VERSION(1, 0, ESIFDV_REVISION); header.flags = self->flags; if (IOStream_Write(memStreamPtr, &header, sizeof(header)) != sizeof(header)) { rc = ESIF_E_IO_ERROR; goto exit; } memStreamBuffer = IOStream_GetMemoryBuffer(memStreamPtr); memStreamBufSize = IOStream_GetSize(memStreamPtr); // // Now write the memory stream to the disk // if (rc == ESIF_OK && IOStream_OpenFile(diskStreamPtr, self->stream->file.name, "wb") != EOK) { rc = ESIF_E_IO_OPEN_FAILED; goto exit; } if (rc == ESIF_OK && (IOStream_Write(diskStreamPtr, memStreamBuffer, memStreamBufSize) != memStreamBufSize)) { rc = ESIF_E_IO_ERROR; goto exit; } exit: IOStream_Destroy(diskStreamPtr); IOStream_Destroy(memStreamPtr); if (rc != ESIF_OK && cacheClonePtr != NULL) { DataCache_Destroy(self->cache); self->cache = cacheClonePtr; } else { DataCache_Destroy(cacheClonePtr); } return rc; }
static eEsifError DataVault_WriteKeyValuePair( DataVaultPtr self, DataCacheEntryPtr pairPtr, IOStreamPtr destStreamPtr ) { eEsifError rc = ESIF_OK; IOStreamPtr diskStreamPtr = NULL; UInt8 *buffer = NULL; UInt32 buffer_len = 0; UInt32 byte = 0; size_t orgOffset = 0; size_t newOffset = 0; ESIF_ASSERT(self != NULL); ESIF_ASSERT(pairPtr != NULL); ESIF_ASSERT(destStreamPtr != NULL); // Expected to be open diskStreamPtr = self->stream; // Write Flags: <flags> if (IOStream_Write(destStreamPtr, &pairPtr->flags, sizeof(pairPtr->flags)) != sizeof(pairPtr->flags)) { rc = ESIF_E_IO_ERROR; goto exit; } // Write Key: <len><value...> if (IOStream_Write(destStreamPtr, &pairPtr->key.data_len, sizeof(pairPtr->key.data_len)) != sizeof(pairPtr->key.data_len)) { rc = ESIF_E_IO_ERROR; goto exit; } if (IOStream_Write(destStreamPtr, pairPtr->key.buf_ptr, pairPtr->key.data_len) != pairPtr->key.data_len) { rc = ESIF_E_IO_ERROR; goto exit; } // Write Value: <type><len><value...> if (IOStream_Write(destStreamPtr, &pairPtr->value.type, sizeof(pairPtr->value.type)) != sizeof(pairPtr->value.type)) { rc = ESIF_E_IO_ERROR; goto exit; } if (IOStream_Write(destStreamPtr, &pairPtr->value.data_len, sizeof(pairPtr->value.data_len)) != sizeof(pairPtr->value.data_len)) { rc = ESIF_E_IO_ERROR; goto exit; } // Read NOCACHE Entries from disk file if (pairPtr->flags & ESIF_SERVICE_CONFIG_NOCACHE) { newOffset = IOStream_GetOffset(destStreamPtr); // Read Block from disk if (pairPtr->value.buf_len == 0) { orgOffset = (size_t)pairPtr->value.buf_ptr; buffer = (UInt8*)esif_ccb_malloc(pairPtr->value.data_len); buffer_len = pairPtr->value.data_len; if (!buffer) { rc = ESIF_E_NO_MEMORY; goto exit; } rc = DataVault_GetFromSource(self, buffer, buffer_len, orgOffset); if (rc != ESIF_OK) { goto exit; } } // Convert internal storage to NOCACHE else { buffer = (UInt8*)pairPtr->value.buf_ptr; buffer_len = pairPtr->value.data_len; pairPtr->value.buf_len = 0;// Set to 0 so we don't free twice } // Update pair with offset in new file pairPtr->value.buf_ptr = (void*)newOffset; } // Scramble Data? if (pairPtr->flags & ESIF_SERVICE_CONFIG_SCRAMBLE) { if (!buffer) { buffer = (UInt8*)esif_ccb_malloc(pairPtr->value.data_len); buffer_len = pairPtr->value.data_len; if (!buffer) { rc = ESIF_E_NO_MEMORY; goto exit; } } for (byte = 0; byte < pairPtr->value.data_len; byte++) buffer[byte] = ~((UInt8*)(pairPtr->value.buf_ptr))[byte]; } if (buffer) { if (IOStream_Write(destStreamPtr, buffer, buffer_len) != buffer_len) rc = ESIF_E_IO_ERROR; } else { if (IOStream_Write(destStreamPtr, pairPtr->value.buf_ptr, pairPtr->value.data_len) != pairPtr->value.data_len) rc = ESIF_E_IO_ERROR; } exit: esif_ccb_free(buffer); return rc; }
// Write DataVault to Disk eEsifError DataVault_WriteVault (DataVaultPtr self) { eEsifError rc = ESIF_E_NOT_FOUND; DataVaultHeader header; struct esif_ccb_file dv_file = {0}; struct esif_ccb_file dv_filebak = {0}; IOStreamPtr vault = 0; IOStreamPtr vaultBak = 0; u32 idx; if (FLAGS_TEST(self->flags, ESIF_SERVICE_CONFIG_STATIC | ESIF_SERVICE_CONFIG_READONLY)) { return ESIF_E_READONLY; } // TODO: Locking vault = IOStream_Create(); if (!vault) { return ESIF_E_NO_MEMORY; } // If any rows contain NOCACHE PERSIST values, we need to make a copy the original DataVault while creating the new one // esif_ccb_sprintf(MAX_PATH, dv_file.filename, "%s%s", esif_build_path(dv_file.filename, MAX_PATH, NULL, self->name), ESIFDV_FILEEXT); esif_ccb_sprintf(MAX_PATH, dv_file.filename, "%s%s%s", ESIFDV_DIR, self->name, ESIFDV_FILEEXT); for (idx = 0; idx < self->cache->size; idx++) if (FLAGS_TESTALL(self->cache->elements[idx].flags, ESIF_SERVICE_CONFIG_NOCACHE | ESIF_SERVICE_CONFIG_PERSIST) && self->cache->elements[idx].value.buf_len == 0) { struct stat filebak_stat = {0}; esif_ccb_sprintf(MAX_PATH, dv_filebak.filename, "%s%s%s", ESIFDV_DIR, self->name, ESIFDV_BAKFILEEXT); // Delete BAK file if it exists if (esif_ccb_stat(dv_filebak.filename, &filebak_stat) == 0) { esif_ccb_unlink(dv_filebak.filename); } if (esif_ccb_rename(dv_file.filename, dv_filebak.filename) == 0) { if ((vaultBak = IOStream_Create()) == NULL) { rc = ESIF_E_NO_MEMORY; } if (!vaultBak || IOStream_OpenFile(vaultBak, dv_filebak.filename, "rb") != 0) { IOStream_Destroy(vault); IOStream_Destroy(vaultBak); return rc; } } break; } // Create DataVault, Overwrite if necessary IOStream_SetFile(vault, self->stream->file.name, "wb"); if (IOStream_Open(vault) != 0) { if (vaultBak) { IOStream_Destroy(vaultBak); esif_ccb_unlink(dv_filebak.filename); } IOStream_Destroy(vault); return rc; } // Create File Header memset(&header, 0, sizeof(header)); esif_ccb_memcpy(&header.signature, ESIFDV_SIGNATURE, sizeof(header.signature)); header.headersize = sizeof(header); header.version = ESIFDV_VERSION(ESIFDV_MAJOR_VERSION, ESIFDV_MINOR_VERSION, ESIFDV_REVISION); header.flags = 0; // TODO: get from self->flags // Write File Header IOStream_Seek(vault, 0, SEEK_SET); IOStream_Write(vault, &header, sizeof(header)); rc = ESIF_OK; // Write All Persisted Rows from Sorted List to DataVault for (idx = 0; idx < self->cache->size; idx++) { DataCacheEntryPtr keypair = &self->cache->elements[idx]; if (keypair->flags & ESIF_SERVICE_CONFIG_PERSIST) { UInt8 *buffer = 0; UInt32 buffer_len = 0; UInt32 byte = 0; IOStream_Write(vault, &keypair->flags, sizeof(keypair->flags)); IOStream_Write(vault, &keypair->key.data_len, sizeof(keypair->key.data_len)); IOStream_Write(vault, keypair->key.buf_ptr, keypair->key.data_len); IOStream_Write(vault, &keypair->value.type, sizeof(keypair->value.type)); IOStream_Write(vault, &keypair->value.data_len, sizeof(keypair->value.data_len)); // Read NOCACHE Entries from Backup file if (keypair->flags & ESIF_SERVICE_CONFIG_NOCACHE) { size_t offset = IOStream_GetOffset(vault); // Read Block from BAK file if (keypair->value.buf_len == 0) { size_t bakoffset = (size_t)keypair->value.buf_ptr; buffer = (UInt8*)esif_ccb_malloc(keypair->value.data_len); buffer_len = keypair->value.data_len; if (!buffer) { rc = ESIF_E_NO_MEMORY; break; } if (IOStream_Seek(vaultBak, bakoffset, SEEK_SET) != 0 || IOStream_Read(vaultBak, buffer, buffer_len) != buffer_len) { esif_ccb_free(buffer); rc = ESIF_E_UNSPECIFIED;// TODO: ESIF_E_IOERROR; break; } keypair->value.buf_ptr = (void*)offset; } // Convert internal storage to NOCACHE else { buffer = (UInt8*)keypair->value.buf_ptr; buffer_len = keypair->value.data_len; keypair->value.buf_ptr = (void*)offset; keypair->value.buf_len = 0; } } // Encrypt Data? if (keypair->flags & ESIF_SERVICE_CONFIG_ENCRYPT) { if (!buffer) { buffer = (UInt8*)esif_ccb_malloc(keypair->value.data_len); buffer_len = keypair->value.data_len; if (!buffer) { rc = ESIF_E_NO_MEMORY; break; } } for (byte = 0; byte < keypair->value.data_len; byte++) buffer[byte] = ~((UInt8*)(keypair->value.buf_ptr))[byte]; } if (buffer) { IOStream_Write(vault, buffer, buffer_len); esif_ccb_free(buffer); } else { IOStream_Write(vault, keypair->value.buf_ptr, keypair->value.data_len); } } } // Rollback on Error if (rc != ESIF_OK) { IOStream_Destroy(vaultBak); IOStream_Destroy(vault); esif_ccb_unlink(dv_file.filename); IGNORE_RESULT(esif_ccb_rename(dv_filebak.filename, dv_file.filename)); return rc; } // Remove BAK file and Commit if (vaultBak) { IOStream_Close(vaultBak); esif_ccb_unlink(dv_filebak.filename); IOStream_Destroy(vaultBak); } IOStream_Close(vault); IOStream_Destroy(vault); return rc; }