Can someone explain why fread() is not working? - c

After a couple hours working on the recover exercise of cs50 i've been stumbling in the segmentention error problem. After running the debbuger i've discovered that the cause of the segmentation error is the malfuction of fread(memory, 512, 1, file), even after calling the function the memory[] array keeps empty, thus, the segmentation error.
i've tried to work with malloc(512) instead of an unsigned char array but the error persists. Can someone explain why is this happening and how to solve it?
(PS. Sorry for my bad english)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
// making sure the only user input is the name of the file
if (argc != 2)
{
printf("Usage: ./recover image\n");
return 1;
}
// open the file and check if it works
FILE *file = fopen("card.raw", "r");
if (file == NULL)
{
printf("Could not open card.raw.\n");
return 2;
}
int ending = 1000;
int count = 0;
char img = '\0';
FILE *picture = NULL;
unsigned char memory[512];
do
{
//creating buffer and reading the file into the buffer
fread(memory, 512, 1, file);
//checking if the block is a new jpg file
if (memory[0] == 0xff && memory[1] == 0xd8 && memory[2] == 0xff && (memory[3] & 0xf0) == 0xe0)
{
//if it's the first jpg file
if (count == 0)
{
sprintf(&img, "000.jpg");
picture = fopen(&img, "w");
fwrite(&memory, 512, 1, picture);
}
//closing previous jpg file and writing into a new one
else
{
fclose(picture);
img = '\0';
sprintf(&img, "%03i.jpg", count + 1);
picture = fopen(&img, "w");
fwrite(&memory, 512, 1, picture);
}
}
//continue writing into the file
else
{
picture = fopen(&img, "a");
fwrite(&memory, 512, 1, picture);
}
count++;
}
while(ending >= 512);
fclose(file);
fclose(picture);
return 0;
}

if you're using C or C++ then you should be fully aware of the memory model being used. For example, declaring a character local variable, means allocating from 1 to 4 bytes of memory in the stack, depending on memory alignment and architecture being used (16-bit? 32-bit? 64-bit?). And guess what happens when you do sprintf of more than 4 characters on such character local variable. It will overrun to whatever variable occupying the space after the img variable. So you must prepare a buffer large enough to hold characters that are needed to create the filename.
In C, if you make a mistake, there are several possibilities :
sometime you get segmentation fault after you do a mistake
sometime you didn't get any error but the data silently corrupted
sometime error happens long after the mistake is done
There are other problems with your code, which has been pointed out by Weather Vane and Jabberwocky in the comments above. I would like to add that reopening 'img' file and discarding previous file handle is not a good thing either (you already said continue writing? why need to reopen? ). You might get a dangling file handle or needlessly create many file handles during the iteration. C will not help you check such things, it assumes you really know what are you doing. Even mixing types will not cause compile error nor identifiable runtime error. It will just do one of the three things I said above.
You might want to use other modern language with more memory safety features in order to learn programming, like C#, Java or Python.

Related

Segmentation fault in CS50 (2020) recovery program

I am trying to write a program that will recover deleted images from a file and write each of those images to their own seperate files. I've been stuck on this problem for a few days, and have tried my best to solve it on my own, but I now realize I need some guidance. My code always compiles well, but everytime I run my program I suffer a segmentation fault. Using valgrind shows me that I don't have any memory leaks.
I think I have pinpointed the issue, though I'm not sure how to resolve it. When I run my program through the debugger, it always stops at the code inside my last 'else' condition (where the comment says "If already found JPEG") , and gives me an error message about the segmentation fault.
I have tried opening and initializing my file pointer jpegn atop this line of code, to prevent jpegn from being NULL when this condition is run, but that did not work to fix the fault.
I am very new to programming (and this site) so any advice or suggestions would be helpful.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char *argv[])
{
if(argc!=2) // Checks if the user typed in exactly 1 command-line argument
{
printf("Usage: ./recover image\n");
return 1;
}
if(fopen(argv[1],"r") == NULL) // Checks if the image can be opened for reading
{
printf("This image cannot be opened for reading\n");
return 1;
}
FILE *forensic_image = fopen(argv[1],"r"); // Opens the image inputted and stores it in a new file
BYTE *buffer = malloc(512 * sizeof(BYTE)); // Dynamically creates an array capable of holding 512 bytes of data
if(malloc(512*sizeof(BYTE)) == NULL) // Checks if there is enough memory in the system
{
printf("System error\n");
return 1;
}
// Creates a counting variable, a string and two file pointers
int JPEG_num=0;
char *filename = NULL;
FILE *jpeg0 = NULL;
FILE *jpegn = NULL;
while(!feof(forensic_image)) // Repeat until end of image
{
fread(buffer, sizeof(BYTE), 512, forensic_image); // Read 512 bytes of data from the image into a buffer
// Check for the start of a new JPEG file
if(buffer[0] == 0xff & buffer[1] == 0xd8 & buffer[2] == 0xff & (buffer[3] & 0xf0) == 0xe0)
{
// If first JPEG
if(JPEG_num == 0)
{
sprintf(filename, "%03i.jpg", JPEG_num);
jpeg0 = fopen(filename, "w");
fwrite(buffer, sizeof(BYTE), 512, jpeg0);
}
else // If not first JPEG
{
fclose(jpeg0);
JPEG_num++;
sprintf(filename, "%03i.jpg", JPEG_num);
jpegn = fopen(filename, "w");
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
}
else // If already found JPEG
{
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
}
// Close remaining files and free dynamically allocated memory
fclose(jpegn);
free(buffer);
}
There are quite many issues on your code. I am surprised if valgrind didn't identify these out.
First is this:
if(fopen(argv[1],"r") == NULL) // Checks if the image can be opened for reading
{
printf("This image cannot be opened for reading\n");
return 1;
}
FILE *forensic_image = fopen(argv[1],"r"); // Opens the image inputted and stores it in a new file
This is not fatal, but you opened the same file twice and discard the first file pointer. But the one with similar pattern below is definitely a memory leak:
BYTE *buffer = malloc(512 * sizeof(BYTE)); // Dynamically creates an array capable of holding 512 bytes of data
if(malloc(512*sizeof(BYTE)) == NULL) // Checks if there is enough memory in the system
{
printf("System error\n");
return 1;
}
Here you allocated 512-bytes twice and keep only the first allocation in the pointer buffer, while the second allocation is lost.
And then this:
char *filename = NULL;
// ...
sprintf(filename, "%03i.jpg", JPEG_num);
you are writing a string to an unallocated memory!
and also the lines:
else // If already found JPEG
{
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
How can you guarantee jpegn is a valid file pointer? Probably never because I see in your code that JPEG_num will always be 0. The part of else marked by // If not first JPEG is dead code.
when compiling, always enable the warnings, then fix those warnings.
gcc -ggdb3 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled1.c" -o "untitled1.o"
results in several warnings like:
untitled1.c:46:91: warning: suggest parentheses around comparison in operand of ‘&’ [-Wparentheses]
if(buffer[0] == 0xff & buffer[1] == 0xd8 & buffer[2] == 0xff & (buffer[3] & 0xf0) == 0xe0)
Note: a single & is a bit wise AND. You really want a logical AND && for all but the last one in this statement
regarding;
FILE *forensic_image = fopen(argv[1],"r");
Always check (!=NULL) the returned value to assure the operation was successful. If not successful (==NULL) then call
perror( "fopen failed" );
to output to stderr both your error message and the text reason the system thinks the error occurred.
regarding:
while(!feof(forensic_image))
please read: why while( !feof() is always wrong
regarding:
FILE *forensic_image = fopen(argv[1],"r");
This is already done in the prior code block. There is absolutely no reason to do this again AND it will create problems in the code. Suggest: replacing:
if(fopen(argv[1],"r") == NULL)
{
printf("This image cannot be opened for reading\n");
return 1;
}
with:
if( (forensic_image = fopen(argv[1],"r") ) == NULL)
{
perror( "fopen for input file failed" );
exit( EXIT_FAILURE );
}
regarding:
BYTE *buffer = malloc( 512 * sizeof(BYTE) );
and later:
free( buffer );
This is a waste of code and resources. The project only needs one such instance. Suggest:
#define RECORD_LEN 512
and
unsigned char buffer[ RECORD_LEN ];
regarding;
fread(buffer, sizeof(BYTE), 512, forensic_image);
The function: fread() returns a size_t. You should be assigning the returned value to a size_t variable and checking that value to assure the operation was successful. Infact, that statement should be in the while() condition
regarding;
sprintf(filename, "%03i.jpg", JPEG_num);
This results in undefined behavior and can result in a seg fault event because the pointer filename is initialized to NULL. Suggest:
char filename[20];
to avoid that problem
regarding:
else // If not first JPEG
{
fclose(jpeg0);
if your (for instance) working with the 3rd file, then jpeg0 is already closed, resulting in a run time error. Suggest removing the statement:
FILE *jpeg0;
and always using jpegn
regarding;
else // If already found JPEG
{
fwrite(buffer, sizeof(BYTE), 512, jpegn);
}
on the first output file, jpegn is not set, so this results in a crash. Again, ONLY use jpegn for all output file operations.
regarding:
fwrite(buffer, sizeof(BYTE), 512, jpegn);
this returns the number of (second parameter) amounts actually written, so this should be:
if( fwrite(buffer, sizeof(BYTE), 512, jpegn) != 512 ) { // handle error }
the posted code contains some 'magic' numbers, like 512. 'magic' numbers are numbers with no basis. 'magic' numbers make the code much more difficult to understand, debug, etc. Suggest using an enum statement or #define statement to give those 'magic' numbers meaningful names, then use those meaningful names throughout the code.

fread is not starting at the beginning of the file

I am working on a project that involves reading binary data from a file into certain data structures. While testing, I saw that incorrect data was being loaded into these structures. Adding a little debug code (using ftell) revealed that fread was not starting at the beginning of the file, but at some offset hundreds of bytes deep. What could be causing this?
I have tried adding fseek(infile, 0, SEEK_SET); before the first fread call, but the first call still started at the same offset as before. I also tried using rewind(infile) to no avail. I did see that whether this problem occurred depended on the file being read. Some files would always start at position 0 while others would always start at some other offset.
Here is a minimal example of code that exhibits this problem on my machine. I am currently running Windows 10 and the code was compiled in Visual Studio.
#include <stdio.h>
int main(int argc, char* argv[]) {
FILE* infile;
char* inname;
char x;
inname = argv[1];
if ( (fopen_s(&infile, inname, "r")) != 0) {
printf("Error opening file: %s\n", inname);
exit(1);
}
if (infile == 0) {
printf("Error opening file.\n");
exit(1);
}
while (fread(&x, sizeof(char), 1, infile) == 1) {
printf("%ld\n", ftell(infile));
printf("%hhx\n\n", x);
}
fclose(infile);
return 0;
}
You should open the file in binary read mode.
if ( (fopen_s(&infile, inname, "r")) != 0) {
to
if ( (fopen_s(&infile, inname, "rb")) != 0) {
From fopen man
The mode string can also include the letter 'b' either as a last
character or as a character between the characters in any of the
two-character strings described above. This is strictly for
compatibility with C89 and has no effect; the 'b' is ignored on all
POSIX conforming systems, including Linux. (Other systems may treat
text files and binary files differently, and adding the 'b' may be a
good idea if you do I/O to a binary file and expect that your program
may be ported to non-UNIX environments.)

Writing byte arrays back to their original state on disk in C

This might seem useless to most, but I'm trying to figure out how to write a byte array back to the original file it once was, rather than executing in memory (found an overwhelming amount of information on executing in memory).
Particularly, how to do this in C on linux.
I have converted the linux program 'touch' to a byte array:
char touch[] = {
0x7F,0x45,0x4C,0x46,0x02,0x01,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x3E,0x00,
0x01,0x00,0x00,0x00,0xA0,0x38,0x00,0x00,0x00,0x00,
0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x68,0x64,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x40,0x00,0x38,0x00,0x09,0x00,0x40,0x00,
0x1E,0x00,0x1D,0x00,0x06,0x00,0x00,0x00,0x05,0x00,
etc..
so I am basically just trying to write touch to the current directory as newtouch.
With windows I found the CreateFile function. Is open() the equivalent on linux?
Any help would be great. thanks
just write your array using fwrite, opening the file as binary (no issue on Linux, but text/default mode creates corrupt binary files on Windows because of endline conversion)
#include <stdio.h>
const char touch[] = {
0x7F,0x45,0x4C,0x46,0x02,0x01,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x3E,0x00,
0x01,0x00,0x00,0x00,0xA0,0x38,0x00,0x00,0x00,0x00};
int main()
{
int rc=1;
FILE *f=fopen("xxx","wb");
if (f!=NULL)
{
size_t written = fwrite(touch,sizeof(touch),1,f);
if (written != 1)
{
fprintf(stderr,"disk write issue\n");
}
else
{
rc = 0;
}
fclose(f);
}
else
{
fprintf(stderr,"cannot create file\n");
}
return rc;
}
Here I'm able to use sizeof(touch) to get the proper size because touch is an array, not just a pointer on data.
// Open a file for writing.
// (This will replace any existing file. Use "w+" for appending)
FILE *file = fopen("filename", "wb");
int results = fputs(array, file);
if (results == EOF) {
// Failed to write do error code here.
}
fclose(file);

My recovered IMGs doesn't match the Original in recover CS50

The problem is to recover some JPGs from a .raw file.
when I run check50 I get "recovered img don't match".
:) recover.c exists.
:) recover.c compiles.
:) handles lack of forensic image
:( recovers 000.jpg correctly –
recovered image does not match
:( recovers middle images correctly –
recovered image does not match
:( recovers 015.jpg correctly –
015.jpg not found
I really tried hard to identify the problem and every time I fail to Identify where the problem is, I hope someone can and give me a peace of advice.
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[]){
if(argc != 2){
fprintf(stderr, "Usage: ./recover image");
return 1;
}
//open file
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL){
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
int foundjpg = 0;
char filename[10];
int x=1;
//repeat until end of the card
while(x == 1){
//buffer
unsigned char buf[512];
x = fread(buf, 512, 1, inptr);
//read into buffer
fread(buf, 512, 1, inptr);
FILE *jpg = fopen(filename, "w");
//start of a new jpg?
if(buf[0]== 0xff && buf[1] == 0xd8 && buf[2] == 0xff && (buf[3] & 0xf0) == 0xe0 ){
if(jpg != NULL){// yes i found before
fclose(jpg);
sprintf(filename, "%03i.jpg" ,foundjpg );
foundjpg++;
jpg = fopen(filename, "w");
}
else{
sprintf(filename, "%03i.jpg" ,foundjpg );
jpg = fopen(filename , "w");
foundjpg++;
}
}
//already found a jpg?
if(jpg != NULL && foundjpg > 0){
fwrite(buf, 1, 512, jpg);
}
}
fclose(inptr);
// success
return 0;
}
The order in which you do things is quite confused and leads to errors. For example:
filename isn't initialised when you use it for the first time.
You increase the counter foundjpg after you use it to create the filename, which in your program means that the second image is called 01.jpg. All image indices are off by one and the last one is missing.
When the id bytes do not identify a valid jpg, no new record is read and your loop never ends.
You should re-organise your code so that it does one thing after another in a natural way. The program might look like this:
Check command line arguments
Open the raw file
Main loop:
Read fixed-size block. If it can't be read, exit the loop
Check if first bytes identify a jpg and if so:
Create file name
Open jpg file for writing
Write block and close jpg file
Increment block counter
Close raw file
You must decide how you handle errors. Do you just skip erroneous blocks or do you abort the program?
It is also not clear whether all images are 512 bytes long, which seems improbable. Perhaps you must read the actual image size from the header and then copy the whole image.

Trouble testing copy file function in C

Okay so this is probably has an easy solution, but after a bit of searching and testing I remain confused.. :(
Here is a snippet of the code that I have written:
int main(int argc, char *argv[]){
int test;
test = copyTheFile("test.txt", "testdir");
if(test == 1)
printf("something went wrong");
if(test == 0)
printf("copydone");
return 0;
}
int copyTheFile(char *sourcePath, char *destinationPath){
FILE *fin = fopen(sourcePath, "r");
FILE *fout = fopen(destinationPath, "w");
if(fin != NULL && fout != NULL){
char buffer[10000];//change to real size using stat()
size_t read, write;
while((read = fread(buffer, 1, sizeof(buffer), fin)) > 0){
write = fwrite(buffer, 1, read, fout);
if(write != read)
return 1;
}//end of while
}// end of if
else{
printf("Something wrong getting the file\n");
return 0;}
if(fin != NULL)
fclose(fin);
if(fout != NULL)
fclose(fout);
return 0;
}
Some quick notes: I am very new to C, programming, and especially file I/O. I looked up the man pages of fopen, fread, and fwrite. After looking at some example code I came up with this. I was trying to just copy a simple text file, and then place it in the destination folder specified by destinationPath.
The folder I want to place the text file into is called testdir, and the file I want to copy is called test.txt.
The arguments I have attempted to use in the copyFile function are:
"test.txt" "testdir"
".../Desktop/project/test.txt" ".../Desktop/project/testdir"
"/Desktop/project/test.txt" "/Desktop/project/testdir"
I just get the print statement "Something wrong getting the file" with every attempt. I am thinking that it may be because 'testdir' is a folder not a file, but then how would I copy to a folder?
Sorry if this a really basic question, I am just having trouble so any advice would be awesome!
Also, if you wanted to be extra helpful, the "copyTheFile" function is supposed to copy the file regardless of format. So like if its a .jpg or something it should copy it. Let me know if any of you guys see a problem with it.
This is with ISO/POSIX/C89/C99 on Linux.
At the start, you'll want to include stdio.h to provide FILE and the I/O function declarations:
#include <stdio.h>
Aside from this, your program compiles and works properly for me. Unfortunately you can't copy to a directory without using stat() to detect if the destination is a directory, and if so, appending a file name before opening the file.
Some other minor suggestions:
A buffer with a power of two bytes such as 4096 is probably more efficient due to it lining up with filesystem and disk access patterns
Conventionally, C functions that return a status code use 0 for success and other values such as 1 for failure, so swapping your return values may be less confusing
When a standard library function such as fopen, fread or fwrite fails, it is a good idea to use perror(NULL); or perror("error prefix"); to report it, which may look something like:
$ ./a.out
...
error prefix: No such file or directory
if you are trying to write a new file in a directory, you should be giving the full path of the file to be written. in your case
"C:...\Desktop\project\testdir\testfile"

Resources