CS50 PSet4: Recover - Conditional Statement Problem - c

I have written the code for PSet-4: Recover from CS50 and I have a question regarding the usage of if-else statements in my code inside the while loop.
When I run the first code, check50 shows the following errors -
:) recover.c exists.
:) recover.c compiles.
:) handles lack of forensic image
:( recovers 000.jpg correctly
expected exit code 0, not None
:( recovers middle images correctly
expected exit code 0, not None
:( recovers 049.jpg correctly
expected exit code 0, not None
:| program is free of memory errors
can't check until a frown turns upside down
I tried writing this code and it fails.
char jpeg[8]; //to store each jpeg file
int i = 0; // to count the number of jpeg files
BYTE buffer[512]; // store each stream of 512 bytes to read
FILE *imout; // output file
bool jpegStart = false; // to check for already opened jpeg file
// read the file 512 bytes at a time until the end
while (fread(buffer, 1, 512, card) == 512)
{
// if buffer detects signature first 4 bytes of JPEG file
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && ((buffer[3] & 0xff) == 0xe0))
{
//if already jpeg opened and new detected, close the already opened jpeg
if (jpegStart)
{
fclose(imout);
}
//set true when signature detected and write to output file with current index i
else if (jpegStart == false)
{
jpegStart = true;
sprintf(jpeg, "%03i.jpg", i);
imout = fopen(jpeg, "w");
fwrite(buffer, 1, 512, imout);
i++;
}
}
}
Whereas when I run the code without the else if clause, it runs successfully as per the following code:
char jpeg[8]; //to store each jpeg file
int i = 0; // to count the number of jpeg files
BYTE buffer[512]; // store each stream of 512 bytes to read
FILE *imout; // output file
bool jpegStart = false; // to check for already opened jpeg file
// read the file 512 bytes at a time until the end
while (fread(buffer, 1, 512, card) == 512)
{
// if buffer detects signature first 4 bytes of JPEG file
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && ((buffer[3] & 0xff) == 0xe0))
{
//if already jpeg opened and new detected, close the already opened jpeg
if (jpegStart)
{
fclose(imout);
}
//set true when signature detected and write to output file with current index i
jpegStart = true;
sprintf(jpeg, "%03i.jpg", i);
imout = fopen(jpeg, "w");
fwrite(buffer, 1, 512, imout);
i++;
}
}
I think it might be a very silly doubt but I would still like to get some clarification on why the first code failed when the else-if conditional statement was included? Thanks.

In the first example, jpegStart is never reset to false. As a consequence, the condition:
if (jpegStart)
remains true and the else if is discarded.
The first example goes like:
if (jpeg is true) {
do this..
} else if (jpeg is false) {
do this.. /* This line is only executed if jpeg is false */
}
The second example on the other hand translates to:
if (jpeg is true) {
do this..
}
do this.. /* This line always executes */
}
Do you see the difference?
Here's an example that might help:
#include <stdio.h>
#include <stdlib.h>
static void print_greatest(int x, int y)
{
if (X > y) {
printf("%d\n", x);
}
printf("%d\n", y);
}
int main(void)
{
print_greatest(10, 5);
return EXIT_SUCCESS;
}
Output:
10
5
y always gets printed. But if we were to add an else, things change:
#include <stdio.h>
#include <stdlib.h>
static void print_greatest(int x, int y)
{
if (x > y) {
printf("%d", x);
} else {
printf("%d", y);
}
}
int main(void)
{
print_greatest(10, 5);
return EXIT_SUCCESS;
}
Output:
10
y is only printed if it was greater than x.
Side note:
You should always check the return value of fopen to ensure you're not — trying to — reading from or writing to a NULL pointer. Functions like fwrite and fclose should also be checked.

Related

CS50 PSET4 RECOVER: fread() not populating buffer array

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.

CS50 pset4 recover - Recovered image does not match

I have been trying to do this problem for at least a week now, and can't seem to understand where is the problem, I already checked everything in google, and dont know any programmer in real life to ask them personaly, so if anyone can help me it would be great.
None of the images generated load, and it doesnt recover 50 as it is suposed to, it recovers 986.
I get this results in check50:
:) 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 049.jpg correctly
recovered image does not match
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <cs50.h>
typedef uint8_t BYTE;
#define BLOCK_SIZE 512
int main(int argc, char *argv[])
{
//it only accepts one comand argument in the name of an image
if (argc != 2)
{
printf("Usage: ./recover IMAGE");
return 1;
}
//check if it can open the image
FILE *file = fopen(argv[1], "r");
if (file == NULL)
{
printf("The image cannot be opened");
return 1;
}
bool jpg_before = false;
int counter = 0;
FILE *image = NULL;
char name[8];
unsigned char buffer[BLOCK_SIZE];
//while there is still jpegs in the file
while (fread(buffer, BLOCK_SIZE, 1, file) == 1)
{
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xe0) == 0xe0)
{
jpg_before = true;
}
if(jpg_before == true)
{
sprintf(name, "%03i.jpg", counter);
counter++;
image = fopen(name, "a");
fwrite(buffer, BLOCK_SIZE, 1, image);
fclose(image);
}
}
fclose(file);
}
(also please keep in mind I'm new to programming, 16 years old and english is not my first lenguage)
When you detect a header in the input, you set jpg_before. But, you never clear it.
Once the flag is set, each block will be put into a different file.
Every output file should consist of a header, followed by the associated data blocks.
name[8] is a bit too small. The compiler will complain because the int could [in theory] be 10 or so digits, so the sprintf could overflow. Don't be stingy--use (e.g.): char name[20];
Output file should be opened with "w" instead of "a". If the program is run twice, the second time, the output file(s) will be incorrect.
Here is the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//#include <cs50.h>
typedef uint8_t BYTE;
#define BLOCK_SIZE 512
int
main(int argc, char *argv[])
{
// it only accepts one comand argument in the name of an image
if (argc != 2) {
printf("Usage: ./recover IMAGE");
return 1;
}
// check if it can open the image
FILE *file = fopen(argv[1], "r");
if (file == NULL) {
printf("The image cannot be opened");
return 1;
}
int counter = 0;
FILE *image = NULL;
char name[20];
unsigned char buffer[BLOCK_SIZE];
// while there is still jpegs in the file
while (fread(buffer, BLOCK_SIZE, 1, file) == 1) {
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xe0) == 0xe0) {
if (image != NULL)
fclose(image);
sprintf(name, "%03i.jpg", counter);
counter++;
image = fopen(name, "w");
}
fwrite(buffer, BLOCK_SIZE, 1, image);
}
if (image != NULL)
fclose(image);
fclose(file);
}
UPDATE:
From the comments below:
Points 2 and 3 look to be handled by the file being opened for appending. Leaving the file open is probably a better idea, though. Faster and handles point 6. –
user4581301
If H is header and D is data, for an input of (e.g): H1,D1,D2,D3,D4,H2,D5,D6,D7:
Instead of two output files: F0:H1,D1,D2,D3,D4 and F1:H2,D5,D6,D7
We'd have: F0:H1, F1:D1, F2:D2, F3:D3, F4:D4, F5:H2, F6:D5, F7:D6, F8:D7
Although my refactored code was correct, the top section of my answer had an incorrect analysis of what OP's code was actually doing.
I've fixed that. But, to make user4581301's make sense, here is the original analysis:
When you detect a header in the input, you set jpg_before. But, you never clear it.
You only write to the output stream for the header block, so any data is not copied. So, each output file will only be 512 bytes
You immediately close the output stream after writing the header. It should be left open.
Every block must go to a given output file, not just the header.
name[8] is a bit too small. The compiler will complain because the int could [in theory] be 10 or so digits, so the sprintf could overflow. Don't be stingy--use (e.g.): char name[20];
Output file should be opened with "w" instead of "a". If the program is run twice, the second time, the output file(s) will be incorrect.
UPDATE #2:
First of all thanks! But it is giving me a segmentation fault, do you have any idea why? because everything seems correct –
Isa M
From code inspection, the only place that could segfault is the fwrite call (i.e. image is NULL).
I confirmed this by running the program under gdb [I have the cs50 recover input file]. When the program faults, just do tb to get a stack traceback.
image could be NULL for the following reasons:
The fopen for output file could fail (due to permissions, space, etc.) and return NULL. There was no check after the call as there was for opening the input file.
image starts out being NULL. If there is some sort of extra file data/file header before the first jpg header (e.g. before FF/D8/FF/E0) the if will not match on the first block read. The fwrite will be called even with a NULL in image.
Option (2) is what actually occurred because cs50's file has an extra header at the top of the file. You can see this by examining the file with a hex editor/dumper (e.g.) od or xxd:
00000000: 00000000 00000000 00000000 00000000 ................
*
00000200: 63733530 2E6C792F 73757270 72697365 cs50.ly/surprise
00000210: 00000000 00000000 00000000 00000000 ................
*
00000400: FFD8FFE0 00104A46 49460001 01000001 ......JFIF......
The code will not see a valid header (i.e. the if matches) until offset 400. So, there are two extraneous fread calls at the start until things sync up.
The fix is to change:
fwrite(buffer, BLOCK_SIZE, 1, image);
Into:
if (image != NULL)
fwrite(buffer, BLOCK_SIZE, 1, image);
I've written a few answers on this problem before. However, I forgot to include this. I just wrote the code but did not test it ;-)
To round things out, I've added more return code checking and added "rb" and "wb" to the fopen calls, just in case you're running on Windoze.
Here is the updated/fixed code (I've tested it this time ;-):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
//#include <cs50.h>
typedef uint8_t BYTE;
#define BLOCK_SIZE 512
void
onerr(const char *action,const char *file)
{
printf("%s -- %s -- %s\n",action,file,strerror(errno));
exit(1);
}
int
main(int argc, char *argv[])
{
// it only accepts one comand argument in the name of an image
if (argc != 2) {
printf("Usage: ./recover IMAGE");
return 1;
}
// check if it can open the image
FILE *file = fopen(argv[1], "rb");
if (file == NULL)
onerr("The image cannot be opened",argv[1]);
int counter = 0;
FILE *image = NULL;
char name[20];
unsigned char buffer[BLOCK_SIZE];
// while there is still jpegs in the file
while (fread(buffer, BLOCK_SIZE, 1, file) == 1) {
if (buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xe0) == 0xe0) {
if (image != NULL)
fclose(image);
sprintf(name, "%03i.jpg", counter);
counter++;
image = fopen(name, "wb");
if (image == NULL)
onerr("unable to open output file",name);
}
#if 0
fwrite(buffer, BLOCK_SIZE, 1, image);
#else
if (image != NULL)
fwrite(buffer, BLOCK_SIZE, 1, image);
#endif
}
if (image != NULL)
fclose(image);
fclose(file);
return 0;
}

Need advice pset4 recover. Tried for a week and still getting segmentation fault

Output:
:( recovers 000.jpg correctly
failed to execute program due to segmentation fault
:( recovers middle images correctly
failed to execute program due to segmentation fault
:( recovers 049.jpg correctly
failed to execute program due to segmentation fault
Code:
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
if (argc != 2) //to make sure that accept exactly one command-line argument
{
printf("Usage: ./recover key\n");
return 1;
}
FILE *infile = fopen(argv[1], "r"); //open the file card.raw and creating a new file called f in read format
if (infile == NULL)//if file cannot open then print below if can open just continue
{
printf("Cannot open file\n");
return 2;
}
FILE *img; //img is the output
int jpeg_counter = 0; // to count the no. of jpeg files
uint8_t buffer[512]; //cos 512 bytes and the buffer is the temporary storage
char filename[8];
while (fread(buffer, sizeof(buffer), 1, infile) == 512)
//continue doing this loop if the while conditions are true. to repeat until end of card.like while the file you reading is true,
{
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
//removing last 4 bits of the 8 bits, only looking at they first 4 which is e. setting all to 0
//if start of new jpeg with above conditions
{
if (jpeg_counter != 0) // telling them that you previously found jpeg
{
fclose(img);//else if never find before, tell them now that you have found it by making it true
}
sprintf(filename, "%03i.jpg", jpeg_counter); //%03i means print an integer with 3 digits
jpeg_counter ++;
img = fopen(filename , "w"); //open the new file w for writting
if (img == NULL) //see if can remove this
return 3;
fwrite(buffer, sizeof(buffer), 1, img);// writing new output file
}
if (jpeg_counter != 0)
fwrite(buffer, sizeof(buffer), 1, img);
}
fclose(infile);
fclose(img);
return 0;
}
the segfault is cased by fclose(img) and img is an invalid FILE pointer. The problem is your while loop condition is never true and the loop is never taken. Your fread will never return 512, it returns 1 on a success read. I have fixed the loop condition for you and added some printfs to print out more information so you have a better understanding of what happens. Here is a link of the fixed code in our cloud IDE, you can use it to debug segfault in the future.

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

End of File detection

I’m a very novice programmer and I’ve encountered an issue whose nature I don’t understand, whilst working on a problem set of the excellent cs50 course. I have implemented a program to recover JPEG pictures from an image of a memory card and am implementing a break at End of File as follows:
if(file > 1)
{
if (fread(&buffer, 1, 512, in_pointer) != 512)
{
free(filename);
return 0;
}
else
fseek(in_pointer, -512, SEEK_CUR);
}
(the pictures are filling up the card in 512 byte blocks). When I first implemented this it broke my first picture (it was recognizable but distorted) so I excluded it by means of the first if statement. Now however the middle files of the set are slightly off– they still open as Jpegs but I can’t get their thumbnails to work. My hypothesis is that I am corrupting the JPEG file format header. The beginning (including first and last images of the set work perfectly).
My questions are:
What is an elegant way to implement an EOF break since my getto solution is causing trouble?
What is the likely nature of the problem I’ve created (in layman’s terms)?
Thank you very much,
Tikhon
ps here is the whole thing
#include <cs50.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 2)
{
fprintf(stderr, "enter exactly two command line arguments: ./recover and destination of disc to scan\n");
return 1;
}
//name the file
char *infile = argv[1];
//open card file and ensure proper format
FILE *in_pointer = fopen(infile, "r");
if (in_pointer == NULL)
{
fprintf(stderr, "could not open %s\n", infile);
return 2;
}
typedef uint8_t BYTE;
BYTE buffer[512];
bool new_jpeg = false;
int block = 0;
int file = 0;
char *filename = malloc(3);
//sprintf(filename, "%03i.jpg",1);
do
{
//read a 512 block of a jpeg
fread(&buffer, 512, 1, in_pointer);
//check for new jpeg
if (buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xf0) == 0xe0) // took me a while to figure this out
{
new_jpeg = true;
//printf("jpeg found, block %i\n", block);
}
block++;
} while(new_jpeg == false);
do
{
//set name of file to write to
sprintf(filename, "%03i.jpg",file);
file++;
new_jpeg = false;
// open output file
FILE *img = fopen(filename, "w");
if (img == NULL)
{
fprintf(stderr, "Could not create %s.\n", filename);
return 3;
}
//add blocks to file while before we reach the nea JPEG.
do
{
fwrite(&buffer, 1, 512, img);
//read the next block
fread(&buffer, 1, 512, in_pointer);
//There MUST be a better way... Anyhow this checks for end of file but backtracks becouse the act of checking moved the file coursor forward...
if(file > 1)
{
if (fread(&buffer, 1, 512, in_pointer) != 512)
{
free(filename);
return 0;
}
else
fseek(in_pointer, -512, SEEK_CUR);
}
block++; //we are reading off teh next block
if (buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xf0) == 0xe0) // took me a while to figure this out
{
new_jpeg = true;
//printf("jpeg %i found, block %i\n", file, block);
}
}while(new_jpeg == false);
}while(!feof(in_pointer));
free(filename);
//ran valgrind no probs detected.
}
OK, I fixed it.
Instead of having a whole separate section of code to check eof I killed two birds in one stone and fread the file WHILE checking for eof:
//read the next block
int k = fread(&buffer, 1, 512, in_pointer);
if(k != 512)
{
free(filename);
return 0;
}
I still have no idea why my previous method didn't work, I would be extremly grateful for suggestions...

Resources