summaryrefslogtreecommitdiff
path: root/flash.c
diff options
context:
space:
mode:
authorMichael J. Chudobiak <mjc@avtechpulse.com>2012-08-22 07:55:11 -0400
committerMichael J. Chudobiak <mjc@avtechpulse.com>2012-08-22 07:55:11 -0400
commita6fb0a6560a89b2fdf16595254335481eb7e0d3a (patch)
tree6541b4815084f4e2468aa3b602eeea5f288acfdd /flash.c
parentd51639c91c2aca7db6df8150ab438b6cbf62227a (diff)
first attempt at flash persistence
Diffstat (limited to 'flash.c')
-rw-r--r--flash.c295
1 files changed, 267 insertions, 28 deletions
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 <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <mhash.h>
-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));
}
-