#include "globals.h" #include #include #include #include #include #include #include #include #include #include #include #define MAINFILE "/root/flash.copy" #define BACKUPFILE "/root/flash.bup" #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; } persistencehdr; static void persistence_printheader(persistencehdr* 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 persistence_copyfile(char* source, char* dest) { mode_t filemode = S_IRUSR | S_IWUSR; int src = open(source, O_RDONLY | O_CREAT); // should create the file for us if this is the first run // You would think that O_RDONLY would stop the file creation, but it seems to work 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 persistence_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 persistencehdr hdr; if (read(fd, &hdr, sizeof(persistencehdr)) != sizeof(persistencehdr)) { errno = PERSIST_ERR_COULDNTREADHDR; return false; } // load the data persistence_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 persistencehdr hdr; hdr.version = version; hdr.length = total; hdr.crc32 = crc; persistence_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 persistence_unfreeze(char* dest, void* result, unsigned int len, uint32_t version) { int fd = open(dest, O_RDONLY); // get the header persistencehdr hdr; if (read(fd, &hdr, sizeof(persistencehdr)) != sizeof(persistencehdr)) { errno = PERSIST_ERR_COULDNTREADHDR; return false; } persistence_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 (persistence_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 (persistence_unfreeze(BACKUPFILE, mem, sizeof(*mem), 0)) { // if the backup was good overwrite the main file persistence_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) { // *** 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. *** // backup the main copy of the file if (persistence_copyfile(MAINFILE, BACKUPFILE)) { if (!persistence_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) { return; } // uninitialized device! mem->flash_start = (char) 99; 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 // save the default Flash config, for nonvolatile persistence writeUserBlock(mem, 0, sizeof(*mem)); }