CS50 PSET4 RECOVER: fread() not populating buffer array - c

Hello and thank you for taking a look.
I'm working through CS50x and am struggling with Recover. The aim is to open a .raw file, read its contents in 512-byte blocks, check the initial four bytes for .jpg headers, and then write each JPEG data to a new file.
I have a body of code written, and the file compiles. The debugger tells me that my buffer[512] variable remains empty/zeroed. This then means the program skips if/else conditions and the program exits.
While my logic within the While loop may be flawed, I haven't been able to step far enough into the program to consider this.
I looked up my issue before posting. Some sources like to use fread(buffer, 512, 1, input), but CS50 itself uses fread(buffer, 1, 512, input). Also, when initialising the filename, I have tried both char *filename = malloc(8 * sizeof(char)); and char filename[8];. For both lines I have tried each method and am still missing something.
My code is below. Thank you in advance for your time.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char *argv[])
{
// First check the number of arguments is correct.
if (argc != 2)
{
printf("Correct Usage: ./recover.c [filename]\n");
return 1;
}
// Open the file.
FILE *inputFile = fopen(argv[1], "r");
if (inputFile == NULL)
{
printf("File not found.\n");
return 1;
}
// Create counter of number of files.
int counter = 0;
// Create filename variable
char *filename = malloc(8 * sizeof(char)); // 7 + 1 for \0
// Create a 512-size array buffer.
BYTE buffer[512];
// Initialise img file for scope access.
FILE *img = NULL;
while (fread(buffer, sizeof(BYTE), 512, inputFile))
{
// If start of new JPEG:
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
if (counter == 0) // If the FIRST JPEG
{
// Make new file:
sprintf(filename, "%03i.jpg", counter);
img = fopen(filename, "w");
fwrite(buffer, sizeof(BYTE), 512, img);
}
else // If not the first JPEG
{
fclose(img); // Close previous file.
counter++;
// Make new file:
sprintf(filename, "%03i.jpg", counter); // Update filename.
img = fopen(filename, "w");
fwrite(buffer, sizeof(BYTE), 512, img);
}
}
else if (counter > 0) // buffer is continuation of previous.
{
fwrite(buffer, sizeof(BYTE), 512, img);
}
else
{
printf("I exited with no images.\n");
return 2;
}
}
free(filename);
fclose(img);
fclose(inputFile);
return 0;
}

The program exits (returns) after the first line in the raw file is read (assuming it's not a jpeg header, which is the case with the distro raw file). else if (counter > 0) evaluates to false, so the else branch executes.

Thank you everyone for your response. The issue is now fixed!
#DinoCoderSaurus (sorry, can't upvote yet) prompted me to realise that I had assumed (wrongly) that the data in the raw file would immediately begin with a .jpeg header (in fact it looks like the data begins with a hidden message, "surprise").
The Else condition was initially put there to avoid errors but of course it was prematurely exiting the While loop. The buffer was populated correctly after a couple of loops.
I then encountered the second problem (pointed out by #Some_programmer_dude) that counter++; was in the wrong place, which meant after the first new JPEG, no others could be written.
I'll also take your comments about best practice into consideration.

Related

I am getting a segmentation fault in my code

I am writing a code that reads information from a memory card (card.raw is the one we are provided but the code uses user input) and extracts the jpegs from it using the signatures that jpegs have of (0xff,0xd8,0xff,0x00 - 0xff). I am getting a segmentation fault because i am using malloc, but i dont see where i went wrong. I am pasting my code here any help would be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char *argv[])
{
//check terminal usage
if (argc != 2)
{
printf("Usage: ./recover image");
return 1;
}
//open inputted file and check for valid file
FILE *file = fopen(argv[1], "r");
if (!file)
{
printf("Invalid or missing file.");
return 1;
}
BYTE *buff = malloc(512 * sizeof(BYTE));
int counter = 0;
FILE *image = NULL;
char *name = malloc(8 * sizeof(char));
//loop till end of file reached and read a block of input
while(fread(buff, sizeof(BYTE), 512, file) == 1 && !feof(file))
{
bool foundJPEG = buff[0] == 0xff && buff[1] == 0xd8 && buff[2] == 0xff && ((buff[3] & 0xf0) == 0xe0);
//check if found jpeg, and open file for writing
if (foundJPEG)
{
sprintf(name, "%03i.jpg", counter);
image = fopen(name, "w");
}
//if image file open, write to it
if (image != NULL)
{
fwrite(buff, sizeof(BYTE), 512, image);
}
//if found a jpeg already, close it so new one can be written
if (foundJPEG && image != NULL)
{
fclose(image);
counter++;
}
}
free(name);
free(buff);
fclose(image);
fclose(file);
return 0;
}
There are three issues with the code above which are not mentioned in the comments:
The return value of fread is not 1 but 512, upon successful read. You exchanged the parameters for the blocksize and the blockcount -> fread definition. Therefore the while loop is not entered.
Don't try to save space with packing to much code into one statement. If would be more clever to separate the checks for the fread return value and the EOF and use a do ... while() loop, instead. Then you had the chance of seeing this issue in the debugger. This was exactly what i have done and how i found this out.
The second issue is that you close the image after rescuing the first 512 bytes, but you do not reset the file pointer image back to NULL along with the fclose statement.
As a consequence, the code would repeatedly write to an a file which is closed until a new block with a jpg header is found.
The third issue is that you only rescue the first 512 bytes of the jpg but not the whole jpg. You need to scan the input stream for the jpg end indicator FF D9 and copy bytes until it is found. ->jpg format

CS50: I can't open the JPG's my image recovery program creates

As I said in the title I can't open the JPG's my image recovery program creates. The purpose of this program is to scan the argument infile for JPEGs a 512 byte block at a time. If the block signals the start of a new file the program should close the last outfile, open a new outfile and start writing into it. If the data in the block is not the start of a new file, the program should continue writing into the current outfile. My program creates 50 files which is the amount of photos in the infile. However, when I try to open them I am told "Invalid or Unsupported Image Format".
Could anyone offer any advice as I am a little stumped?
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
BYTE block[512];
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: ./recover filename\n");
return 1;
}
char *card = argv[1];
FILE *raw_data = fopen(card, "r");
if (raw_data == NULL)
{
printf("Couldn't open file.\n");
return 1;
}
char file_name[8];
BYTE buffer[512];
int counter = 0;
FILE *image = NULL;
while (fread(buffer, sizeof(block), 1, raw_data) != 0)
{
if (counter == 0)
{
sprintf(file_name, "%03i.jpg", counter);
image = fopen(file_name, "w");
fwrite(&buffer, sizeof(block), 1, image);
counter++;
}
else if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
fclose(image);
sprintf(file_name, "%03i.jpg", counter);
image = fopen(file_name, "w");
counter++;
}
else
{
fwrite(&buffer, sizeof(block), 1, image);
}
}
fclose(raw_data);
fclose(image);
return 0;
}
The answer of your question is in this else if statement.
else if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
fclose(image);
sprintf(file_name, "%03i.jpg", counter);
image = fopen(file_name, "w");
counter++;
}
When there is a jpeg signature in the beginning of each 512 byte block, this statement is executed. You close the image file if it is open before. You create a new image file, and you open it for writing. But then?? You did not write the buffer on image file where it has jpeg signature.. Bam! Your jpeg file is not supported! How can a computer know if it is jpeg without a jpeg signature?
There are other problems in while loop. Even if you fixed the main issue, you first jpeg file will be just garbage.. Why? Look at this if statement below. It is writing the very first 512 byte block of raw file without any check if it carries jpeg signature or not.
if (counter == 0)
{
sprintf(file_name, "%03i.jpg", counter);
image = fopen(file_name, "w");
fwrite(&buffer, sizeof(block), 1, image);
counter++;
}
The very first if statement in while loop should rather be to check if the 512 byte memory block has the jpeg signature or not. A pseudo code for a new design would help you. Because you probably want to fix other issues by yourself.
//If the buffer starts with the magic sequence found in the original code.
//If there is already an image found, then close. (Check if counter is not equal to zero.
//Create a new jpeg file.
//Open that new jpeg file.
//Write buffer on new jpeg file. This one should be outside of the main if statement.
Good luck with solving other issues. You are pretty close.

C - Why does my code break when removing unused variable declaration

I am writing a program in C to recover images from a raw file for CS50 and I am having a strange problem. I have a variable int cnt that I was using for debug purposes and I got the program to work so I was removing leftover debug code. But when I remove the cnt declaration I start outputting corrupt files.
Before removing line 25 below I was outputing .jpg files that I could open and view, then I removed the line, recompiled, deleted the photos from the last run, and reran the program on the same .raw data and the new files I got were unrecognized. So I put the declaration back in, recompiled, deleted the old photos, and ran the program again and got good files. Does anyone know why removing an unused declaration is messing with my results? The offending declaration is on line 25.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: ./recover image\n");
return 1;
}
int filesFound = 0;
FILE *inFile = fopen(argv[1], "r");
FILE *outFile = NULL;
if (inFile == NULL)
{
printf("Image file could not be opened\n");
return 1;
}
uint8_t buffer[512];
int cnt = 0;
while (!feof(inFile))
{
fread(buffer, 512, 1, inFile);
// check for start of jpg file
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
// start of jpg was found
if (outFile != NULL)
{
// close the current file and then open a new file to write to
fclose(outFile);
outFile = NULL;
}
// open a file to write to
char fName[4];
sprintf(fName, "%03i.jpg", filesFound);
outFile = fopen(fName, "w");
filesFound++;
}
if (outFile != NULL){
// we have found data to write and opened a file
fwrite(buffer, 512, 1, outFile);
}
}
//Be sure to close my files
fclose(inFile);
if (outFile != NULL)
{
fclose(outFile);
}
return 0;
}
char fName[4] does not have sufficient room for the name generated by "%03i.jpg", so you are overrunning the buffer. Make it larger and use snprintf, not sprintf, and test the return value to detect errors:
int result = snprintf(fName, sizeof fName, "%03i.jpg", filesFound);
if (sizeof fName <= result)
{
fprintf(stderr, "Internal error, buffer is too small for file name.\n");
exit(EXIT_FAILURE);
}
Instead of printing an error, you could instead use the return value of snprintf, which indicates the length needed, to allocate memory for a larger buffer and then redo the snprintf with that buffer.
(Note that snprintf may return a negative result if an error occurs. Normally, this will become a large number upon conversion to size_t for the comparison, so it will trigger this error message. However, in a robust program, you might want to insert a separate test for result < 0.)

CS50 Recover Segmentation Fault issue

The goal of this program is recover JPGs from a file.
I've been working on this problem for about four or five days in the CS50 online class and I just cannot figure it out. I continue to get a segmentation fault and I have no idea why.
I've tried debug50 and find that I get the fault when the program tries to write to a new file. Why it does this I cannot figure out.
I've been bashing my head up against a wall on this one and I've completely erased and rewritten it multiple times. Any help would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage ./recover file.type\n");
return 1;
}
char *infile = argv[1];
FILE *inptr = fopen(infile, "rb");
if (inptr == NULL)
{
fprintf(stderr, "Could not open file designated to be recovered\n");
fclose(inptr);
return 2;
}
int counter = 0;
FILE *img;
uint8_t buffer[512];
while (fread(buffer, sizeof(*buffer), 512, inptr))
{
if (buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xf0) == 0xe0)
{
if (counter > 0)
{
fclose(img);
}
char filename[8];
sprintf(filename, "%03i.jpg", counter);
img = fopen(filename, "w");
counter++;
}
if (counter !=0)
{
fwrite(buffer, sizeof(*buffer), 512, img);
}
}
fclose(img);
fclose(inptr);
return 0;
}
char filename[7];
sprintf(filename, "%03i.jpg", counter);
A seven character string takes up 8 chars due to the NUL-terminator \0. Make the array larger so you don't write past the end of it.
if(img == NULL)
{
fprintf(stderr, "Could not create image file\n");
fclose(img);
return 3;
}
If the img didn't open you don't need to close it. On the other hand, if it did open then you do need to close it. Move the fclose() call to the end of the loop.
In addition to the answer above,
Look at the syntax of the fwrite function:
https://en.cppreference.com/w/c/io/fwrite
size_t fwrite( const void *buffer, size_t size, size_t count,
FILE *stream );
According to the documentation, the size parameter is the size of each value in the buffer.
In your code, you have:
fwrite(buffer, 512, 1, img);
The problem is obvious.
It looks like you do the same for fread. The syntax of the function is:
https://en.cppreference.com/w/c/io/fread
size_t fread( void *buffer, size_t size, size_t count,
FILE *stream );
In your code you do:
fread(buffer, 512, 1, inptr)
But it should be
fread(buffer, sizeof *buffer, 512, inptr)
As an aside, when dealing with such files, I recommend opening them in binary mode so as to not tamper with the data being read.
FILE *inptr = fopen(infile, "rb");
Finally you should make use of the return value of fread which tells you the number of bytes that was actually read. You can then make use of this value in fwrite to make sure you write the correct number of bytes.
Okay, so I figured out that it was the NOT operator that I had put in the last if statement when what I should have put was:
if (counter != 0)
{
fwrite(buffer, sizeof(buffer), 1, img);
}
I am however still confused as to why the program returned a segmentation fault.
My understanding is that when the program reached this particular if statement it would not execute because the counter would be evaluated as false.
Wouldn't the program then just end after reading the whole input file returning 0?
Where does the segmentation fault come from?

CS5Ox Pset4 Recover: code only recovers partial images

I have some semi-working code for CS50 Pset4. If you run it you will see that it recovers 27 jpg files but only the first couple of lines are visible.
Can someone point me in the right direction?
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
typedef uint8_t BYTE;
int main (int argc, char *argv[])
{
// ensure proper usage
if (argc != 2)
{
fprintf(stderr, "Usage: ./recover infile\n");
return 1;
}
// open file to be recovered
FILE *infile = fopen(argv[1], "r");
if (infile == NULL)
{
fprintf(stderr, "Could not open infile.\n");
return 2;
}
// temp storage for blocks
BYTE buffer[512];
// variable to store filename
char filename[8];
//store number of recovered files
int n = 0;
// temp storage for outfiles
FILE* outfile = NULL;
// iterate over all blocks of memory until end of SD card is reached
while (fread(buffer, 512, 1, infile) != 0)
{
// read one block
fread(buffer, 512, 1, infile);
// check if block is start of jpeg
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
//close previous file if already open
if(outfile != NULL)
{
fclose(outfile);
}
// creeate new outfile
sprintf(filename, "%03i.jpg", n);
outfile = fopen(filename, "w");
// write block to outfile
fwrite(buffer, 512, 1, outfile);
n++;
}
else
{
// write block to current outfile
if(outfile != NULL)
{
fwrite(buffer, 512, 1, outfile);
}
}
}
//close last outfile
fclose(outfile);
//close infile
fclose(infile);
}
I see several things that could be causing that problem.
First, check the order of your n counter. The counter should be added before you actually start writing on the new file, but that's just a matter of preference, and how clean you want your code to be.
Secondly, try to substitute your else condition with the following code:
if(outfile != NULL)
{
fwrite(buffer, 512, 1, outfile);
}
Note: Please bear in mind that I've substituted your else condition for an if condition. This is due to the fact that when the first if condition is met, then it executes this condition and 'jumps out of the block'. So the else will be executed only the first if condition won't execute.
If you wanted to keep your else condition, then you should have to nest another if , as you've done in your code.
By substituting that else with an if that will always be checked regardless of the value of the first 3 bytes of your jpg (that is, regardless if they're 0x00, 0xff, 0x00), you will obtain a much clear and understandable code.
Lastly, and more important: why are you writing twice to the same file in the same operation? Pay attention to the fwrite() function below your n++ counter. Is it really necessary?
In other words: delete this line:
// read one block
fread(buffer, 512, 1, infile);
Other mistake, is that you're reading your file twice, and advance twice in every step, so you will get half information.
This is the reason you're getting half of the images (27 or so).
Erase these two lines:
// read one block
fread(buffer, 512, 1, infile);
As I've said, by reading and writing twice within your files, you get half of the information. This leads to that fuzzy way in which you're getting your images all colored in senseless colors (I guess) and half of the image files.
I've run check50 2016.recover recover.c with your code and with the fixed solutions I've just provided you and it passes all the checks from CS50's check50. Take your time and think about everything in your program, including the control-flow, which is an important part of it, as well as the pointers usage.
Starting CS50 without any prior experience can be daunting. Keep it up. You've already made it almost into Week 5.

Resources