0

So I am creating a password vault and need to be able to save a HashTable to a binary file and read those same contents from the binary file back into the programs HashTable upon login

I have been playing around with fwrite and fread for a solid 4 hours and cannot seem to find where i am going wrong. Currently i am segfaulting, i have tried GDB to find it but just get the message:

Thread 2 received signal SIGSEGV, Segmentation fault.
0x00007fff9a9a16a0 in ?? ()

My code for reading in a file is:

void readFile(char * fileName, HTable * hashTable){
//Create file pointer and point it at fild to open
FILE * fp;
fp = fopen(fileName, "rb+");

//check if file exists
if(!fp){
    printf("File could not be opened\n");
    return;
}

//Set a temp Node to hold data, read data into the temp Node
//and insert temp into hashtable
Node * temp;    
while(1){
    fread(&temp, sizeof(Node), 1, fp);
    insertData(hashTable, temp->key, temp->data);
    if(feof(fp) != 0)
        break;
    printf("is this working?\n");
}

//Close file pointer
fclose(fp);
}

and my code for writing to the file is:

void saveFile(char * fileName, HTable * hashTable){
//Create a FILE pointer and point it to the file to open or create
FILE * fp;
fp = fopen(fileName, "wb+");

//If the file does not exist or cannot be created give error message
if(!fp){
    printf("ERROR: File could not be created\n");
    return;
}


//for each index of the hashTable write its contents to the file
for(int i = 0; i < hashTable->size; i++){
    Node * temp = hashTable->table[i];
    while(temp != NULL){
        fwrite(&temp, sizeof(Node), 1, fp);
        temp = temp->next;
        printf("saved....\n");
    }
    printf("Index %d saved\n", i);
}

//Close file pointer
fclose(fp);

}

I know the segfault isnt coming from the insertData function as i have tested that and know it works properly. My best guess for what I am doing wrong is that something about my fwrite conditions are not right or that when I am reading the data I am mis managing memory somewhere.

also the HashTable struct is:

typedef struct HTable
{
size_t size; 
Node **table;
void (*destroyData)(void *data);
int (*hashFunction)(size_t tableSize, char * key);
void (*printData)(void *toBePrinted); 
}HTable;

and my Node struck is:

typedef struct Node
{
char * key;
void *data;
struct Node *next;
} Node;

Thank you for any feedback!!

  • 2
    You can't fread/fwrite pointer data for key/data. You will need to read/write their values. – ffhighwind Jun 21 '18 at 03:54
  • Writing raw pointers to disk and then reading and using those pointers in a different process is a recipe for crashing and burning. The pointers in the hash table written to disk might be OK for the same process if you've not done anything inconvenient like freed the memory in the interim, but it isn't recommended. You have to ignore the pointers written to disk, or arrange not to write pointers to disk (maybe offsets, maybe some other information). – Jonathan Leffler Jun 21 '18 at 04:07
  • 1
    `temp` is a pointer and you never allocate memory for it, yet you read data into wherever it points. Undefined behavior and an easy segfault. Please find a tutorial on how to use a debugger since it will help you a lot in every problem you have and tell you what went wrong where. – Sami Kuhmonen Jun 21 '18 at 04:34
  • `fread(&temp...` does not read data wherever temp points. It reads data over top of temp, and then proceeds to smash the stack. – rici Jun 21 '18 at 05:30

2 Answers2

0

First, the argument 1 of fwrite()/fread() is a void *, not **void ****.

Second, you can not use fwrite()/fread() to save/restore your data because members {key, data} of Node are pointers which indicate extra memory data, however using fwrite()/fread() just manipulate on pointers {char *, void *, Node *}, not the extra data.

For key, there is a example:

Node n;
n.key = malloc(sizeof(char) * DEF_SIZE); // Suppose n.key = 0xb8b270
strcpy(n.key, "Hello world!");
fwrite(&n, sizeof(Node), 1, fp); // Only 0xb8b270 is written.

Only address of string (n.key), not the string "Hello world" is written to file. Can you understand what I said?

In short, fwrite()/fread() just writes/reads raw data, please be careful to use them when your structure contains pointers which indicate extra memory data.

For structure

typedef struct Item
{
    char key[128];
    char value[128];
    struct Item *next;
} Item;

Using fwrite()/fread() may be OK here, because {char[], char[], Item *} will be written/read, only {Item *} is invalid data, {char[], char[]} can be saved/restored correctly.

I suppose using fprintf()/fscanf() to replace fwrite()/fread() in your case.

Third, there is a hash map implementation of mine, which may can help you: https://github.com/foreverpersist/hashmap

Joy Allen
  • 402
  • 3
  • 8
  • I would use fprintf() and fscanf() but i need the information to not be human readable. And it is okay if struct Node * next isnt stored as when i use my insertData() it will place the information in the correct place as long as i can save the Key and Data in a binary file. So i think what youre saying is i need to pass in the raw data aka the strings of key and data and not their address? – PotatoBrain Jun 21 '18 at 19:55
  • Yes. For members you care, their raw data should be saved. For other members, you can do anything you like. Maybe you need rebuild your structure like `struct Item` above. – Joy Allen Jun 22 '18 at 01:13
  • If you don't want the data to be humanly readable then look into [encryption](https://stackoverflow.com/questions/7622617/simply-encrypt-a-string-in-c) and try using a [database](https://dev.mysql.com/doc/refman/5.7/en/c-api.html). – ffhighwind Jun 22 '18 at 02:19
-1

Here's some of the code. It compiles but I haven't tested it. I would finish but I have to sleep.

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <malloc.h>

typedef struct Node
{
    char * key; //assuming this is null terminated
    void *data;
    int datasize; //number of bytes in data
    Node *next; //is this actually necessary?
} Node;

typedef struct HTable
{
    size_t size;
    Node *table;
    void(*destroyData)(void *data);
    int(*hashFunction)(size_t tableSize, char *key);
    char *(*printData)(void *data);
} HTable;


void writenode(FILE* fp, Node *node) {
    fprintf(fp, "%s\n", node->key);
    fprintf(fp, "%d\n", node->datasize);
    fwrite(node->data, sizeof(char), node->datasize, fp);
}

void readnode(FILE* fp, Node *node) {
    char buffer[5001];
    int len;
    //key
    fgets(buffer, 5000, fp);
    len = strlen(buffer) + 1;
    node->key = (char*) malloc(sizeof(char)*len);
    strcpy(node->key, buffer);
    //datasize
    fgets(buffer, 5000, fp);
    fscanf(fp, "%d", &(node->datasize));
    fread(buffer, node->datasize, sizeof(char), fp);
    //data
    node->data = malloc(sizeof(char)*node->datasize);
    memcpy(node->data, buffer, sizeof(char)*node->datasize);
}
ffhighwind
  • 165
  • 4
  • 13
  • So I'm guessing I got a downvote for suggesting using a language other than C, even when I post a decent solution. Fact is SEGFAULTs wouldn't happen in a more modern language, and all this error prone memory management code would dissapear. – ffhighwind Jun 22 '18 at 02:07