From a6fb0a6560a89b2fdf16595254335481eb7e0d3a Mon Sep 17 00:00:00 2001 From: "Michael J. Chudobiak" Date: Wed, 22 Aug 2012 07:55:11 -0400 Subject: first attempt at flash persistence --- CMakeLists.txt | 3 +- flash.c | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 269 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cf0643..c02b3ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(Instrument) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) find_package(Glib) find_package(GIO) +find_package(Mhash) include_directories(${GLIB_PKG_INCLUDE_DIRS}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -funsigned-char -Wall") add_executable(instr-daemon instr-daemon.c @@ -23,7 +24,7 @@ add_executable(instr-daemon instr-daemon.c ) add_executable(instr-client instr-client.c) -target_link_libraries(instr-daemon gio-2.0 gobject-2.0 glib-2.0) +target_link_libraries(instr-daemon gio-2.0 gobject-2.0 glib-2.0 mhash) target_link_libraries(instr-client gio-2.0 gobject-2.0 glib-2.0) INSTALL(TARGETS instr-daemon instr-client diff --git a/flash.c b/flash.c index 500ad96..3113388 100644 --- a/flash.c +++ b/flash.c @@ -1,48 +1,287 @@ #include "globals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -int readUserBlock(FlashStruct *mem) -{ - // read the /root/flash.copy file into the mem struct - // and return the number of bytes read in +#define MAINFILE "/root/flash.copy" +#define BACKUPFILE "/root/flash.bup" - // if the file does not exist, then "return 0"; +#define PERSIST_ERR_COULDNTREADHDR 1 +#define PERSIST_ERR_HDRLENMISMATCH 2 +#define PERSIST_ERR_VERSIONMISMATCH 3 +#define PERSIST_ERR_COULDNTREADDATA 4 +#define PERSIST_ERR_BADCRC32 5 +#define PERSIST_ERR_BADARGS 6 +#define PERSIST_ERR_COULDNTALLOCATEBUFFER 7 +#define PERSIST_ERR_COULDNTSEEK 8 +#define PERSIST_ERR_COULDNTWRITE 9 +#define PERSIST_ERR_COUNDNTOPENFILE 10 + +// crc32 routine + +static uint32_t crc32(uint8_t* buf, int count) { + MHASH context = mhash_init(MHASH_CRC32B); + mhash(context, buf, count); + uint32_t hash; + mhash_deinit(context, &hash); + return hash; +} + +// header to prepend to stashed objects +typedef struct { + uint32_t version; + uint32_t length; + uint32_t crc32; +} persistancehdr; + +static void persistance_printheader(persistancehdr* hdr) { +printf("Frozen struct has version %d, is %d bytes long and has the CRC32 0x%08"PRIx32"\n", hdr->version, + hdr->length, hdr->crc32); +} + +// copy a file from one place to another.. this is not portable, linux only +bool persistance_copyfile(char* source, char* dest) { + mode_t filemode = S_IRUSR | S_IWUSR; + int src = open(source, O_RDONLY); + int dst = open(dest, O_SYNC | O_RDWR | O_CREAT, filemode); + + if (src < 0 || dst < 0) + return false; + + struct stat s; + fstat(src, &s); + + ftruncate(dst, 0); + sendfile(dst, src, NULL, s.st_size); + close(src); + close(dst); + return true; +} + +// store an object +bool persistance_freeze(char* dest, void* data, unsigned int offset, unsigned int len, unsigned int total, + uint32_t version) { + + // don't write past the end of the file.. + if (offset + len > total) { + errno = PERSIST_ERR_BADARGS; + return false; + } + + bool newfile = false; + uint32_t crc; + + // open the target file with O_SYNC so that write blocks until it's on disk + // fingers crossed the FS actually does what it's told.. + int fd = open(dest, O_SYNC | O_RDWR); + if (fd < 0) { + // this is to catch if the file didn't exist and if it needed to be created + mode_t filemode = S_IRUSR | S_IWUSR; + fd = open(dest, O_SYNC | O_RDWR | O_CREAT, filemode); + if (fd < 0) { + errno = PERSIST_ERR_COUNDNTOPENFILE; + return false; + } + newfile = true; + } + + // if this is a new file or we're overwriting everything we can just calculate the CRC from the data passed in + if (newfile || len == total) { + // if this a new file we want to write everything irrespective of the offset and len passed in + if (newfile) { + offset = 0; + len = total; + } + crc = crc32((uint8_t*) data, total); + } + + // this is a modification within an existing file so we need to merge the existing data with the + // new data to calculate the new crc for the file because it seems the struct getting passed in + // only contains the changed data. + else if (len != total) { + // create a buffer for the existing data + void* payload = malloc(total); + if (payload == NULL) { + errno = PERSIST_ERR_COULDNTALLOCATEBUFFER; + return false; + } + + // get the header + persistancehdr hdr; + if (read(fd, &hdr, sizeof(persistancehdr)) != sizeof(persistancehdr)) { + errno = PERSIST_ERR_COULDNTREADHDR; + return false; + } + + // load the data + persistance_printheader(&hdr); + if (read(fd, payload, total) != total) { + errno = PERSIST_ERR_COULDNTREADDATA; + return false; + } + + // check the existing data isn't already corrupt. + uint32_t calculatedcrc32 = crc32((uint8_t*) payload, hdr.length); + if (calculatedcrc32 != hdr.crc32) { + errno = PERSIST_ERR_BADCRC32; + return false; + } + // overlay the payload with the existing data + memcpy(((char*) payload) + offset, ((char*) data) + offset, len); + crc = crc32((uint8_t*) payload, total); + free(payload); + lseek(fd, 0, SEEK_SET); // rewind + } + + // build the header + persistancehdr hdr; + hdr.version = version; + hdr.length = total; + hdr.crc32 = crc; + + persistance_printheader(&hdr); + + // write the data to disk + ftruncate(fd, sizeof(hdr) + total); // not really needed but if we did have a file thats bigger than it + // should be put a stop to that + + // write the header + if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + errno = PERSIST_ERR_COULDNTWRITE; + return false; + } + + // seek to the offset.. which could mean not seeking at all + if (lseek(fd, offset, SEEK_CUR) < 0) { + errno = PERSIST_ERR_COULDNTSEEK; // shouldn't ever happen really because if we're actually + // seeking the file should already be the total size. + return false; + } + + // write the data out to disk + if (write(fd, ((char*) data) + offset, len) != len) { + errno = PERSIST_ERR_COULDNTWRITE; + return false; + } + + // pack up and go home + close(fd); + + return true; + +} + +// try to load an object from disk +bool persistance_unfreeze(char* dest, void* result, unsigned int len, uint32_t version) { + + int fd = open(dest, O_RDONLY); + + // get the header + persistancehdr hdr; + if (read(fd, &hdr, sizeof(persistancehdr)) != sizeof(persistancehdr)) { + errno = PERSIST_ERR_COULDNTREADHDR; + return false; + } + + persistance_printheader(&hdr); + + // check that the length of this frozen object is what we are expecting + if (hdr.length != len) { + errno = PERSIST_ERR_HDRLENMISMATCH; + return false; + } + + // check that it's the same version.. the version isn't used at the moment + // but if you want to change the header at some point it'll be useful + if (hdr.version != version) { + errno = PERSIST_ERR_VERSIONMISMATCH; + return false; + } + + // read in the data for the object.. if we couldn't read the amount of data + // that the header said there was the header is either wrong or the file is truncated. + if (read(fd, result, hdr.length) != hdr.length) { + errno = PERSIST_ERR_COULDNTREADDATA; + return false; + } + + // check it's crc32 to make sure it's not corrupt + uint32_t calculatedcrc32 = crc32(result, hdr.length); + if (calculatedcrc32 != hdr.crc32) { + printf("Calculated CRC is 0x%08"PRIx32"\n", calculatedcrc32); + errno = PERSIST_ERR_BADCRC32; + return false; + } + + return true; + +} + +int readUserBlock(FlashStruct *mem) { + + // try to unfreeze the main file + if (persistance_unfreeze(MAINFILE, mem, sizeof(*mem), 0)) + return sizeof(*mem); + // something went wrong + else { + printf("Error unfreezing %d.. trying backup\n", errno); + // hopefully we can use the backup.. + if (persistance_unfreeze(BACKUPFILE, mem, sizeof(*mem), 0)) { + // if the backup was good overwrite the main file + persistance_copyfile(BACKUPFILE, MAINFILE); + return sizeof(*mem); + } + // deadend :( + else { + printf("Error unfreezing backup %d.\n", errno); + } + } return 0; } -void writeUserBlock(FlashStruct *mem, int addr, int numbytes) -{ - // check if /root/flash.copy file exists - // - if it does, update the requested address and - // number of bytes, use fseek / fwrite / fflush +void writeUserBlock(FlashStruct *mem, int addr, int numbytes) { - // - if does not, create the file and - // set addr = 0, numbytes = sizeof (mem) - // so that entire struct will be written, - // instead of just the requested range + // *** There is a potential issue here.. if the mainfile is corrupt *** + // *** and this gets called before readUserBlock then the *** + // *** potentially workable backup will be lost .. we could check *** + // *** that the main file is valid before backing it up I guess... *** + // *** but I don't think this situation should arise. *** - // All writing should be done in a super-safe - // way. Non-corruption, even during a power-off transient, - // is the priority here. We do not want instruments - // losing configuration data ever, because that - // means expensive repairs. + // backup the main copy of the file + if (persistance_copyfile(MAINFILE, BACKUPFILE)) { + if (!persistance_freeze(MAINFILE, mem, addr, numbytes, sizeof(*mem), 0)) { + if (errno != PERSIST_ERR_COULDNTWRITE) + printf("Error while trying to write, %d. **Write did not happen!!!**\n", errno); + else + printf("Error while writing data to disk. **File is potentially corrupt!**\n"); + } + } + else { + printf("Could not backup current file. **Write did not happen!!!**\n"); + } } -void initFlash(FlashStruct *mem) -{ - if (readUserBlock(mem) > 0) { +void initFlash(FlashStruct *mem) { + if (readUserBlock(mem) > 0) return; - } - // uninitialized device! +// uninitialized device! mem->flash_start = (char) 99; - strcpy(mem->aux_error_message,"FIXME"); + strcpy(mem->aux_error_message, "FIXME"); mem->channels = (short) 1; mem->enable_avrq_extra_ampls = (char) 12; mem->ChanKey_frequency = (char) 0; - // much more needs to be added here - later +// much more needs to be added here - later - // save the default Flash config, for nonvolatile persistence +// save the default Flash config, for nonvolatile persistence writeUserBlock(mem, 0, sizeof(*mem)); } - -- cgit