lseek() and read() proper usage - c

I am reading in a "non human readable file" full of structs. I have declared my struct that is a known member of the file. I want to read the file from the begging to the sizeof the stuct populating the fields. I don't understand why my out put is zero for all the struct fields. I have looked at the man pages of these functions and I believe I am using them correctly, so am I just not accessing the fields correctly from the buffer, or? please see below code snippet.
int main (int argc, char * argv[])
{
// make sure you were given an image
if (argc != 2)
{
printf("no image\n");
return -1;
}
int fs_fd = open(argv[1], O_RDWR);
// make sure you can open the image
if(fs_fd == -1)
{
printf("image is busted\n");
return -1;
}
struct superblock *mySB;
mySB = malloc(sizeof(struct superblock));
mySB->size = 0;
mySB->nblocks = 0;
mySB->ninodes = 0;
char buffer[4096];
if(lseek(fs_fd, 0, SEEK_SET));
read(fs_fd, &buffer, sizeof(struct superblock));
mySB->size = buffer[0];
mySB->nblocks = buffer[4];
mySB->ninodes = buffer[8];
printf("Size: %u, nblocks: %u, ninodes: %u\n", mySB->size, mySB->nblocks, mySB->ninodes);
}
ok, suggestions taken into consideration, though in truth I'm not concerned withe good error checking at this point. the following results in a seg fault as i'm sure this is not reading into the struct correctly, but I am trying to use the suggestions.
int main (int argc, char * argv[])
{
// make sure you were given an image
if (argc != 2)
{
printf("usage: file.img");
return -1;
}
int fd = open(argv[1], O_RDWR);
// make sure you can open the image
if(fd == -1)
{
perror("Error> ");
return -1;
}
struct superblock *mySB;
mySB = malloc(sizeof(struct superblock));
read(fd, &mySB, sizeof(struct superblock));
printf("Size: %u, nblocks: %u, ninodes: %u\n", mySB->size, mySB->nblocks, mySB->ninodes);
}

Why no compilable testcase along with an example file?
int fs_fd = open(argv[1], O_RDWR);
// make sure you can open the image
if(fs_fd == -1)
{
printf("image is busted\n");
return -1;
}
Wrong. At the very least use perror to tell what the user what the error is. What's up fs_fd name anyway? Standard name is mere fd.
struct superblock *mySB;
mySB = malloc(sizeof(struct superblock));
Consider sizeof(*mySB);. Null-checks can arguably be ignored.
mySB->size = 0;
mySB->nblocks = 0;
mySB->ninodes = 0;
What's the point of this if you initialize same fields below?
char buffer[4096];
if(lseek(fs_fd, 0, SEEK_SET));
What? Not only teh offset is already at the beginning, you are just ignoring the error (if any) in the worst possible way. If you really need to call this and ignore the error, do (void)lseek(fd_fd, 0, SEEK_SET).
read(fs_fd, &buffer, sizeof(struct superblock));
How about some error checking. If you read only teh size of the struct, why having the buffer be 4096? What if the struct is larger?
mySB->size = buffer[0];
mySB->nblocks = buffer[4];
mySB->ninodes = buffer[8];
Nobody knows how this struct looks like. It seems you have 4-byte sized fields. But you only read ONE byte for each field here.
You likely can just read into the structure instead. If you really want to have an intermediate buffer you have to properly calculate offsets as padding could have screwed you over. Google for offsetof and padding.

Related

Save struct with pointer members in file

I'm trying to save a struct into a .dat file and read it back in later.
struct myStruct{
char **one;
mytype **two;
mytype2 *three;
}
With an assigning function:
struct MyStruct get_struct() = {
char **pi = ...;
mytype **pa = ...;
mytype2 **po = ...;
MyStruct n = {pi, pa, po};
return n;
}
I originally tried to save this struct into a .dat file by doing this:
struct MyStruct s = get_struct();
myoutfile = fopen("file.dat", "w");
if (myoutfile == NULL) {
fprintf(stderr, "\nError opend file\n");
exit(1);
}
fwrite(&s, sizeof(struct MyStruct), 1, myoutfile);
fclose(myoutfile);
and read it back in with:
fread(&t, sizeof(struct MyStruct), 1, myinfile)
Now I learned, that this does not work (segmentation error), because I only save the location where the pointer points to, not the actual thing.
Now my question is, how can I do it properly? I have found some solutions for C++ but I need to stay in C.
EDIT:
Later on, I want to call a function which looks like this:
void work_with_struct(MyStruct s){
char ** xone = s.one;
mytype **xtwo = s.two;
mytype2 *xthree = s.three;
}
This post is related to this post, but as I could specify my mistake now, asking in a new post makes more sense to me.
As always in programming, you break up the task to smaller chunks, and break up smaller chunks to yet smaller chunks, until every chunk is easy.
int saveMyStruct (struct myStruct* myStruct, FILE* file) {
// what do I do here?!?!
// well it has three members
// so treat each one in sequence
int result;
result = saveStringArray(myStruct->one, file);
if (result >= 0)
result = saveMyTypeArray (myStruct->two, file);
if (result >= 0)
result = saveMyType (myStruct->three, file);
return result;
}
Note how the status is checked all the time. If you work with files, you need to check the status all the time.
What next? You need to write three functions mentioned above.
saveStringArray(char** stringArray, FILE* file)
{
// first save the length of the array, then save each individual string
int length = getStringArrayLength(stringArray);
int result = fwrite(&length, sizeof(length), 1, file);
if (result != 1)
return -1;
for (i = 0; i < length; ++i)
{
result = saveString(stringArray[i], file);
if (result < 0)
return -1;
}
return i;
}
And so on and so forth. I presume your array of pointers is NULL-terminated; if not, you need to have some other way to know its length.
Note how array length is always saved before array elements. This is because you will need to read your array later, and you will need to know where to stop. It will also be easy to allocate your array when you read it.

C Segmentation fault - saving struct in .dat file

Can someone please tell me, what causes the segementation fault in my C - code? I am trying to save a struct into a file, and calling it afterwards, following this guide. I don't see anything assigned wrongly, so I'm happy to learn what's my mistake by someone more experienced.
Here is my code, simplified:
int main(int argc, char **argv)
{
char *key = (argc > 4) ? argv[4]: 0;
if(0==strcmp(key, "write")){
struct MyStruct s;
FILE *myoutfile;
myoutfile = fopen("file.dat","w")
if (myoutfile == NULL)
{
fprintf(stderr, "\nError opend file\n");
exit (1);
}
s = get_struct(argv[2]);
fwrite(&s, sizeof(struct MyStruct), 1, myoutfile);
fclose(myoutfile);
}else{
struct MyStruct t;
FILE *myinfile;
myinfile = fopen("file.dat", "r")
if (myinfile == NULL)
{
fprintf(stderr, "\nError opend file\n");
exit (1);
}
while (fread(&t, sizeof(struct MyStruct), 1, myinfile))
printf("Done reading");
}
work_with_struct(t);
fclose(myinfile);
}
Also, as I read in another stack overflow post, doing this:
fread(&t.one, sizeof(t.one), 1, myinfile);
fread(&t.two, sizeof(t.two), 1, myinfile);
fread(&t.three, sizeof(t.three), 1, myinfile);
did not work either.
EDIT: I now think i have located the problem a bit more. The first part of the function (if) works fine. What I thought was not necessary to mention first, was that in the end of the "else" I have a function that works with t. This is the one throwing the error, i believe.
It works fine, when I leave out the .dat-file-part, so by just saying
t = get_struct(argv[2]);
work_with_struct(t);
which I actually want to avoid, because "get_struct" is huge. Doing it once, and working with the data in the .dat file was my solution, that I calculate it only once.
My assumption now is, that putting the struct into the fstream and getting it back will somehow destroy it, or makes it somehow not-readable for work_with_struct. What I think is also worth mentioning is, that inside the struct there are three members: two char **, and one custom data type.
I didn't find any other solution suggesting other ways of reading in the file.
Maybe with this explanation someone gets alerted, where the segmentation fault might come from. Thanks a lot!
The issue is that you are passing a NULL pointer to strcmp which expects a const char*. When no arguments are passed to the program key = 0 and the program segfaults because strcmp attempts to dereference this NULL pointer.
Edit:
I attempted to correct the example program to make it compile and run, here it is:
Edit 2: print content of saved struct
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
struct MyStruct {
int a;
};
int main(int argc, char **argv) {
const char *key = (argc > 4) ? argv[4] : "write";
if (0 == strcmp(key, "write")) {
struct MyStruct s;
FILE *myoutfile;
myoutfile = fopen("file.dat", "w");
if (myoutfile == NULL) {
fprintf(stderr, "\nError opend file\n");
exit(1);
}
s = (struct MyStruct){ 'a' };
fwrite(&s, sizeof(struct MyStruct), 1, myoutfile);
fclose(myoutfile);
} else {
struct MyStruct t;
FILE *myinfile;
myinfile = fopen("file.dat", "r");
if (myinfile == NULL) {
fprintf(stderr, "\nError opend file\n");
exit(1);
}
while (fread(&t, sizeof(struct MyStruct), 1, myinfile))
printf("Done reading\n");
printf("%c\n", t.a);
fclose(myinfile);
}
}
Edit 3:
After updating the description of the struct MyStruct the problem is obvious. You need to find a different way to save the struct to the file then by using fwrite since you have pointer types in it. The way you would go about that is a topic for a different question.

C write struct to file

I am trying to save struct data to file. I saved the data this way.
node_trx * trx_list;
trx_list = calloc(1, sizeof(node_trx *));
trx_list->amount = "123123123";
trx_list->currency = 123;
trx_list->next_node = NULL;
if (1 != fwrite(trx_list, length, 1, f)) {
//error
}
free(trx_list);
Here is my struct:
typedef struct {
char amount;
int currency;
struct node_trx * next_node; } node_trx;
Main problem is after i saved struct to file and then after read, when print values, it is printing wrong values. For example: i stored currency as 123, then printed 6788576 this kind of numbers.
here is my reading code:
int read_last_trx_from_file (const char * file_name, node_trx * * trx, unsigned * trx_len)
{
FILE * f;
*trx = NULL;
if (NULL == (f = fopen(tools_get_full_filename_const(file_name), "rb")))
{
return 2; //error
}
size_t fsize;
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fprintf(stdout, "file size: %zd\n", fsize);
if (!fsize)
{
fclose(f);
return 3; //no data
} else {
if (fsize == 1) {
return 3; // no data
}
}
rewind(f);
if (NULL != (*trx = (node_trx *) calloc(1, fsize)))
{
if (1 != fread(*trx, fsize, 1, f))
{
fclose(f);
free(*trx);
return 2; //error
}
}
fclose(f);
*trx_len = fsize;
return 0; //OK }
Main function that calls read function:
int display_trx() {
node_trx * card_data;
if (3 != read_last_trx_from_file(LAST_TRX_OBJECT, &card_data, &data_set_len)) {
if (card_data != NULL) {
printf("%s AMOUNT \n", card_data->amount);
printf("%d CURRENCY \n", &card_data->currency);
}
}
}
After i read this way , when i print amount data, segmentation fault error occurs. so why segment error occured?
And when i print currency, it printing 734364636 this kinda numbers. So why it prints wrong numbers.
Or i only wrote pointer of struct to file?
Please help me guys.
There are two obvious errors in your code.
In struct declaration, the type of amount is char, but when you initialize it in trx_list->amount = "123123123";, you assigned a string, or char[10] array (there is an extra one for NULL terminator).
In function display_trx, second printf, the result of &card_data->currency is int *, not int. If you want to print out currency, why don't follow the first print, use card_data->currency (without &)? You get that large number because you are printing pointer value implicitly converted into int, or the address of currency in card_data.
And there is one error which compiler will not warn you (because it is not syntactically wrong.). As BLUEPIXY said in the comments, when allocating and initializing trx_list, you should really use calloc(1, sizeof(node_trx)). You are allocating space for what pointer trx_list points to, not the pointer itself, so there should not be an asterisk in sizeof.
My suggestion is using a "smart" compiler, such as gcc, and enable warnings. This is a good practice (at least for me). Thank you!

memset throwing segmentation fault

I am having some problem with writing a function to extract strings from a file as part of a bigger program. Everything seems to be working fine, except when I use memset or bzero to erase the character arrays I have been using. I have been sitting with this problem for more than an hour and I keep getting seg faults whatever I do. I am getting this error for both bzero and memset. Please help me out.
I am attaching my code below. The statement "Come out of addfront" is printed but none of the "Done with all bzero" statements are printing. I get a segmentation fault at that point. Thank you
void extractFileData(FILE *fp , char clientName[])
{
char tempFileName[50], tempFilePath[100], tempFileSize[50];
struct stat fileDetails;
while(fgets(tempFileName, sizeof(tempFileName), fp)!= NULL)
{
if((newLinePos = strchr(tempFileName, '\n')) != NULL)
{
*newLinePos = '\0';
}
strcat(tempFilePath, "SharedFiles/");
strcat(tempFilePath, tempFileName);
if(stat(tempFilePath, &fileDetails) < 0)
{
perror("Stat error");
exit(1);
}
//Copy it into a string
sprintf(tempFileSize, "%zu", fileDetails.st_size);
printf("temp file size: %s\n", tempFileSize);
//Add all these details to the file list by creating a new node
addFront(tempFileName, tempFileSize, clientName);
printf("Come out of addfront\n");
memset(&tempFileName, 0, 45);
printf("Done with all bzero\n");
memset(&tempFileSize, 0, sizeof(tempFileSize));
memset(&tempFilePath, 0, sizeof(tempFilePath));
printf("Done with all bzero\n");
}
}
EDIT:
void addFront(char fileName[], char fileSize[], char clientName[])
{
FILENODE* n;
printf("Inside add front function\n");
strcpy(n->fileName, fileName);
printf("n->filename: %s\n", n->fileName);
strcpy(n->fileSize, fileSize);
printf("n->filesize: %s\n", n->fileSize);
strcpy(n->ownerName, clientName);
printf("n->ownername: %s\n", n->ownerName);
myFileList.head = n;
printf("Did it go past myfilelist head = n\n");
myFileList.numOfNodes++;
printf("num of nodes: %d\n", myFileList.numOfNodes);
}
I have added my code for the addFront function. It basically adds
the details to a struct myFileList which is basically an implementation
of a linked list. The FILENODE represents each entry in the list.
EDIT:
Adding the structs I am using
struct fileNode
{
char fileName[50];
char fileSize[50];
char ownerName[25];
struct fileNode* next;
};
struct fileList
{
struct fileNode* head;
struct fileNode* tail;
int numOfNodes;
};
typedef struct fileList FILELIST;
typedef struct fileNode FILENODE;
I don't know why your program would crash there. But I can another error in the program. Fix the other error first, see if you still have problems.
This is wrong:
strcat(tempFilePath, "SharedFiles/");
strcat(tempFilePath, tempFileName);
The tempFilePath variable is uninitialized. This may coincidentally not crash, but you cannot rely on it not to crash. It may scribble on your stack.
Do this instead:
snprintf(tempFilePath, sizeof(tempFilePath), "SharedFiles/%s", tempFileName);
Finally, there is no need to zero the arrays. The contents of the arrays are not used in the next loop iteration, so you might as well ignore them.
void extractFileData(FILE *fp , char clientName[])
{
char tempFileName[50], tempFilePath[100], *newLinePos;
struct stat fileDetails;
while (fgets(tempFileName, sizeof(tempFileName), fp)) {
if ((newLinePos = strchr(tempFileName, '\n')))
*newLinePos = '\0';
snprintf(tempFilePath, sizeof(tempFilePath),
"SharedFiles/%s", tempFileName);
if (stat(tempFilePath, &fileDetails) < 0) {
perror("Stat error");
exit(1);
}
printf("temp file size: %zu\n", tempFileSize);
addFront(tempFileName, tempFileSize, clientName);
}
}
The snprintf() function is really the number one choice for doing work like this in C. It's easy to write code with snprintf() that "obviously won't crash", as opposed to code that "won't obviously crash".
If your code still crashes, there is an error somewhere else.
addFront() needs a n = malloc( sizeof *n) before you do anything with it.

what is wrong with the following code

The following function gets file offsets from the rabin_polynomial structure, opens the input_file for md5 fingerprint generation and writes the result to fpfile
My problem is it seems to use the same chunk_buffer content some times that it generates similar fingerprints for chunks with different legths.
What could be the reason?
I have tested the md5 function with other inputs separately and it generates correct digests.
int write_rabin_fingerprints_to_binary_file(FILE *fpfile,FILE *input_file,
struct rabin_polynomial *head)
{
struct rabin_polynomial *poly=head;
unsigned char fing_print[33]={'\0'};
size_t bytes_read;
while(poly != NULL)
{
char *chunk_buffer;
chunk_buffer = (char*) malloc ((poly->length));
bytes_read=fread (chunk_buffer,1, poly->length,input_file);
if(bytes_read!=poly->length)
{
printf("Error reading from%s ",input_file);
return -1;
}
strncpy((char*)fing_print,md5(chunk_buffer).c_str(),32);
size_t ret_val=fprintf(fpfile, "%llu\t%lu\t%s\n",poly->start,
poly->length,fing_print);
if(ret_val == 0)
{
fprintf(stderr, "Could not write rabin polynomials to file.");
return -1;
}
poly=poly->next_polynomial;
free(chunk_buffer);
}
return 0;
}
EDIT:
I am running this program using visual studio 2010. Could typecasting to char * in the malloc() line create the problem?
The number of bytes read is just as specified in the argument.
There was nothing wrong in the code to cause such faults. I just found out that it happened because of zero-length strings which are also called as file holes.
int write_rabin_fingerprints_to_binary_file(FILE *fpfile,FILE *input_file
, struct rabin_polynomial *head)
{
struct rabin_polynomial *poly;
unsigned char fing_print[33];
for (poly=head; poly != NULL;poly=poly->next_polynomial ) {
char *chunk_buffer;
int retval;
chunk_buffer = malloc (1+poly->length);
retval = fread (chunk_buf,1, poly->length,input_file);
/* check retval here */
chunk_buff[poly->length] = 0;
strncpy(fing_print,md5(chunk_buffer).c_str(), sizeof fing_print);
fing_print[sizeof fing_print -1] = 0;
retval = fprintf(fpfile, "%llu\t%lu\t%s\n"
,poly->start, poly->length, fing_print);
if(retval <= 0)
{
fprintf(stderr, "Could not write rabin polynomials to file.");
return -1;
}
free(chunk_buffer);
}
return 0;
}

Resources