andrej@2174: #ifndef HAVE_RETAIN andrej@2174: #include andrej@2174: #include andrej@2174: #include andrej@2174: #include "iec_types.h" andrej@2174: andrej@2174: int GetRetainSize(); andrej@2174: andrej@2174: /* Retain buffer. */ andrej@2174: FILE *retain_buffer; andrej@2174: const char rb_file[] = "retain_buffer_file"; andrej@2174: const char rb_file_bckp[] = "retain_buffer_file.bak"; andrej@2174: andrej@2174: andrej@2174: /* Retain header struct. */ andrej@2174: struct retain_info_t { andrej@2174: uint32_t retain_size; andrej@2174: uint32_t hash_size; andrej@2174: uint8_t* hash; andrej@2174: uint32_t header_offset; andrej@2174: uint32_t header_crc; andrej@2174: }; andrej@2174: andrej@2174: /* Init retain info structure. */ andrej@2174: struct retain_info_t retain_info; andrej@2174: andrej@2174: /* CRC lookup table and initial state. */ andrej@2174: uint32_t crc32_table[256]; andrej@2174: uint32_t retain_crc; andrej@2174: andrej@2174: andrej@2174: /* Generate CRC32 lookup table. */ andrej@2174: void GenerateCRC32Table(void) andrej@2174: { andrej@2174: unsigned int i, j; andrej@2174: /* Use CRC-32-IEEE 802.3 polynomial 0x04C11DB7 (bit reflected). */ andrej@2174: uint32_t poly = 0xEDB88320; andrej@2174: andrej@2174: for (i = 0; i <= 0xFF; i++) andrej@2174: { andrej@2174: uint32_t c = i; andrej@2174: for (j = 0 ; j < 8 ; j++) andrej@2174: c = (c & 1) ? (c >> 1 ) ^ poly : (c >> 1); andrej@2174: crc32_table[i] = c; andrej@2174: } andrej@2174: } andrej@2174: andrej@2174: andrej@2174: /* Calculate CRC32 for len bytes from pointer buf with init starting value. */ andrej@2174: uint32_t GenerateCRC32Sum(const void* buf, unsigned int len, uint32_t init) andrej@2174: { andrej@2174: uint32_t crc = ~init; andrej@2174: unsigned char* current = (unsigned char*) buf; andrej@2174: while (len--) andrej@2174: crc = crc32_table[(crc ^ *current++) & 0xFF] ^ (crc >> 8); andrej@2174: return ~crc; andrej@2174: } andrej@2174: andrej@2174: /* Calc CRC32 for retain file byte by byte. */ andrej@2174: int CheckFileCRC(FILE* file_buffer) andrej@2174: { andrej@2174: /* Set the magic constant for one-pass CRC calc according to ZIP CRC32. */ andrej@2174: const uint32_t magic_number = 0x2144df1c; andrej@2174: andrej@2174: /* CRC initial state. */ andrej@2174: uint32_t calc_crc32 = 0; andrej@2174: char data_block = 0; andrej@2174: andrej@2174: while(!feof(file_buffer)){ andrej@2174: if (fread(&data_block, sizeof(data_block), 1, file_buffer)) andrej@2174: calc_crc32 = GenerateCRC32Sum(&data_block, sizeof(char), calc_crc32); andrej@2174: } andrej@2174: andrej@2174: /* Compare crc result with a magic number. */ andrej@2174: return (calc_crc32 == magic_number) ? 1 : 0; andrej@2174: } andrej@2174: andrej@2174: /* Compare current hash with hash from file byte by byte. */ andrej@2174: int CheckFilehash(void) andrej@2174: { andrej@2174: int k; andrej@2174: int offset = sizeof(retain_info.retain_size); andrej@2174: andrej@2174: rewind(retain_buffer); andrej@2174: fseek(retain_buffer, offset , SEEK_SET); andrej@2174: andrej@2174: uint32_t size; andrej@2174: fread(&size, sizeof(size), 1, retain_buffer); andrej@2174: if (size != retain_info.hash_size) andrej@2174: return 0; andrej@2174: andrej@2174: for(k = 0; k < retain_info.hash_size; k++){ andrej@2174: uint8_t file_digit; andrej@2174: fread(&file_digit, sizeof(char), 1, retain_buffer); andrej@2174: if (file_digit != *(retain_info.hash+k)) andrej@2174: return 0; andrej@2174: } andrej@2174: andrej@2174: return 1; andrej@2174: } andrej@2174: andrej@2174: void InitRetain(void) andrej@2174: { andrej@2174: int i; andrej@2174: andrej@2174: /* Generate CRC32 lookup table. */ andrej@2174: GenerateCRC32Table(); andrej@2174: andrej@2174: /* Get retain size in bytes */ andrej@2174: retain_info.retain_size = GetRetainSize(); andrej@2174: andrej@2174: /* Hash stored in retain file as array of char in hex digits andrej@2174: (that's why we divide strlen in two). */ andrej@2174: retain_info.hash_size = PLC_ID ? strlen(PLC_ID)/2 : 0; andrej@2174: //retain_info.hash_size = 0; andrej@2174: retain_info.hash = malloc(retain_info.hash_size); andrej@2174: andrej@2174: /* Transform hash string into byte sequence. */ andrej@2174: for (i = 0; i < retain_info.hash_size; i++) { andrej@2174: int byte = 0; andrej@2174: sscanf((PLC_ID + i*2), "%02X", &byte); andrej@2174: retain_info.hash[i] = byte; andrej@2174: } andrej@2174: andrej@2174: /* Calc header offset. */ andrej@2174: retain_info.header_offset = sizeof(retain_info.retain_size) + \ andrej@2174: sizeof(retain_info.hash_size) + \ andrej@2174: retain_info.hash_size; andrej@2174: andrej@2174: /* Set header CRC initial state. */ andrej@2174: retain_info.header_crc = 0; andrej@2174: andrej@2174: /* Calc crc for header. */ andrej@2174: retain_info.header_crc = GenerateCRC32Sum( andrej@2174: &retain_info.retain_size, andrej@2174: sizeof(retain_info.retain_size), andrej@2174: retain_info.header_crc); andrej@2174: andrej@2174: retain_info.header_crc = GenerateCRC32Sum( andrej@2174: &retain_info.hash_size, andrej@2174: sizeof(retain_info.hash_size), andrej@2174: retain_info.header_crc); andrej@2174: andrej@2174: retain_info.header_crc = GenerateCRC32Sum( andrej@2174: retain_info.hash, andrej@2174: retain_info.hash_size, andrej@2174: retain_info.header_crc); andrej@2174: } andrej@2174: andrej@2174: void CleanupRetain(void) andrej@2174: { andrej@2174: /* Free hash memory. */ andrej@2174: free(retain_info.hash); andrej@2174: } andrej@2174: andrej@2174: int CheckRetainFile(const char * file) andrej@2174: { andrej@2174: retain_buffer = fopen(file, "rb"); andrej@2174: if (retain_buffer) { andrej@2174: /* Check CRC32 and hash. */ andrej@2174: if (CheckFileCRC(retain_buffer)) andrej@2174: if (CheckFilehash()) andrej@2174: return 1; andrej@2174: fclose(retain_buffer); andrej@2174: retain_buffer = NULL; andrej@2174: } andrej@2174: return 0; andrej@2174: } andrej@2174: andrej@2174: int CheckRetainBuffer(void) andrej@2174: { andrej@2174: retain_buffer = NULL; andrej@2174: if (!retain_info.retain_size) andrej@2174: return 1; andrej@2174: andrej@2174: /* Check latest retain file. */ andrej@2174: if (CheckRetainFile(rb_file)) andrej@2174: return 1; andrej@2174: andrej@2174: /* Check if we have backup. */ andrej@2174: if (CheckRetainFile(rb_file_bckp)) andrej@2174: return 1; andrej@2174: andrej@2174: /* We don't have any valid retain buffer - nothing to remind. */ andrej@2174: return 0; andrej@2174: } andrej@2174: andrej@2174: #ifndef FILE_RETAIN_SAVE_PERIOD_S andrej@2174: #define FILE_RETAIN_SAVE_PERIOD_S 1.0 andrej@2174: #endif andrej@2174: andrej@2174: static double CalcDiffSeconds(IEC_TIME* t1, IEC_TIME *t2) andrej@2174: { andrej@2174: IEC_TIME dt ={ andrej@2174: t1->tv_sec - t2->tv_sec, andrej@2174: t1->tv_nsec - t2->tv_nsec andrej@2174: }; andrej@2174: andrej@2174: if ((dt.tv_nsec < -1000000000) || ((dt.tv_sec > 0) && (dt.tv_nsec < 0))){ andrej@2174: dt.tv_sec--; andrej@2174: dt.tv_nsec += 1000000000; andrej@2174: } andrej@2174: if ((dt.tv_nsec > +1000000000) || ((dt.tv_sec < 0) && (dt.tv_nsec > 0))){ andrej@2174: dt.tv_sec++; andrej@2174: dt.tv_nsec -= 1000000000; andrej@2174: } andrej@2174: return dt.tv_sec + 1e-9*dt.tv_nsec; andrej@2174: } andrej@2174: andrej@2174: andrej@2174: int RetainSaveNeeded(void) andrej@2174: { andrej@2174: int ret = 0; andrej@2174: static IEC_TIME last_save; andrej@2174: IEC_TIME now; andrej@2174: double diff_s; andrej@2174: andrej@2174: /* no retain */ andrej@2174: if (!retain_info.retain_size) andrej@2174: return 0; andrej@2174: andrej@2174: /* periodic retain flush to avoid high I/O load */ andrej@2174: PLC_GetTime(&now); andrej@2174: andrej@2174: diff_s = CalcDiffSeconds(&now, &last_save); andrej@2174: andrej@2174: if ((diff_s > FILE_RETAIN_SAVE_PERIOD_S) || ForceSaveRetainReq()) { andrej@2174: ret = 1; andrej@2174: last_save = now; andrej@2174: } andrej@2174: return ret; andrej@2174: } andrej@2174: andrej@2174: void ValidateRetainBuffer(void) andrej@2174: { andrej@2174: if (!retain_buffer) andrej@2174: return; andrej@2174: andrej@2174: /* Add retain data CRC to the end of buffer file. */ andrej@2174: fseek(retain_buffer, 0, SEEK_END); andrej@2174: fwrite(&retain_crc, sizeof(uint32_t), 1, retain_buffer); andrej@2174: andrej@2174: /* Sync file buffer and close file. */ andrej@2174: #ifdef __WIN32 andrej@2174: fflush(retain_buffer); andrej@2174: #else andrej@2174: fsync(fileno(retain_buffer)); andrej@2174: #endif andrej@2174: andrej@2174: fclose(retain_buffer); andrej@2174: retain_buffer = NULL; andrej@2174: } andrej@2174: andrej@2174: void InValidateRetainBuffer(void) andrej@2174: { andrej@2174: if (!RetainSaveNeeded()) andrej@2174: return; andrej@2174: andrej@2174: /* Rename old retain file into *.bak if it exists. */ andrej@2174: rename(rb_file, rb_file_bckp); andrej@2174: andrej@2174: /* Set file CRC initial value. */ andrej@2174: retain_crc = retain_info.header_crc; andrej@2174: andrej@2174: /* Create new retain file. */ andrej@2174: retain_buffer = fopen(rb_file, "wb+"); andrej@2174: if (!retain_buffer) { andrej@2174: fprintf(stderr, "Failed to create retain file : %s\n", rb_file); andrej@2174: return; andrej@2174: } andrej@2174: andrej@2174: /* Write header to the new file. */ andrej@2174: fwrite(&retain_info.retain_size, andrej@2174: sizeof(retain_info.retain_size), 1, retain_buffer); andrej@2174: fwrite(&retain_info.hash_size, andrej@2174: sizeof(retain_info.hash_size), 1, retain_buffer); andrej@2174: fwrite(retain_info.hash , andrej@2174: sizeof(char), retain_info.hash_size, retain_buffer); andrej@2174: } andrej@2174: andrej@2174: void Retain(unsigned int offset, unsigned int count, void *p) andrej@2174: { andrej@2174: if (!retain_buffer) andrej@2174: return; andrej@2174: andrej@2174: /* Generate CRC 32 for each data block. */ andrej@2174: retain_crc = GenerateCRC32Sum(p, count, retain_crc); andrej@2174: andrej@2174: /* Save current var in file. */ andrej@2174: fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); andrej@2174: fwrite(p, count, 1, retain_buffer); andrej@2174: } andrej@2174: andrej@2174: void Remind(unsigned int offset, unsigned int count, void *p) andrej@2174: { andrej@2174: /* Remind variable from file. */ andrej@2174: fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET); andrej@2174: fread((void *)p, count, 1, retain_buffer); andrej@2174: } andrej@2174: #endif // !HAVE_RETAIN