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?
Related
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.
I could do with some advice for this, to me this makes sense logically however when I run check50 only one of the images are recovered. I've looked through the code multiple times so I don't think its a syntax error so it must be some error with the logic. Any tips would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <cs50.h>
typedef uint8_t BYTE;
bool is_jpeg_header(BYTE buffer[]);
int main(int argc, char *argv[])
{
// Check if command line argument is valid
if (argc != 2)
{
printf("Usage: ./recover image\n");
return 1;
}
// Open memory card files
char* mem_card = argv[1];
FILE* inptr = fopen(mem_card, "r");
if (inptr == NULL)
{
printf("File not found/n");
return 1;
}
BYTE buffer[512];
bool found_first_jpeg = false;
int image_count = 0;
char filename[8];
FILE* outptr = NULL;
while (!feof(inptr) && fread(buffer, sizeof(buffer), 1, inptr) == true)
{
// Check if we have found a JPEG
if (is_jpeg_header(buffer) == true)
{
// Check if this is the first JPEG
if (found_first_jpeg == false)
{
found_first_jpeg = true;
sprintf(filename, "%03i.jpg", image_count);
outptr = fopen(filename, "w");
fwrite(buffer, sizeof(buffer), 1, outptr);
image_count++;
}
// If this isn't the first JPEG, close file current JPEG and open new one for new JPEG
else
{
fclose(outptr);
image_count++;
sprintf(filename, "%03i.jpg", image_count);
outptr = fopen(filename, "w");
}
}
// If we haven't found a new JPEG:
else if (is_jpeg_header(buffer) == false)
{
// Continue reading file if we have not found first JPEG
if (found_first_jpeg == false)
{
continue;
}
// Continue writing current JPEG into current file
else
{
fwrite(buffer, sizeof(buffer), 1, outptr);
}
}
}
fclose(inptr);
fclose(outptr);
return 0;
}
bool is_jpeg_header(BYTE buffer[])
{
if (((buffer[0] == 0xff) && (buffer [1] == 0xd8) && (buffer[2] == 0xff) && ((buffer[3] & 0xf0) == 0xe0)))
{
return true;
}
return false;
}
This is the error code I receive from check50
:) recover.c exists.
:) recover.c compiles.
:) handles lack of forensic image
:) recovers 000.jpg correctly
:( recovers middle images correctly
001.jpg not found
:( recovers 049.jpg correctly
recovered image does not match
One bug I see is that filename is too short: you haven't left any room for the terminating zero. That's undefined behavior, and likely your source of trouble.
But the logic overall is very convoluted for what amounts to a simple problem. Here's how I'd write it. Since you're not in general checking for errors, I've left it that way - it's likely OK for this test assignment, although I've not read it. It does help to return different error codes for different errors though - it'd have really helped with the original typo!
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef uint8_t bool;
static const bool true = 1;
static const bool false = 0;
bool is_jpeg_header(const uint8_t buffer[]);
int main(int argc, char *argv[])
{
// Check if command line argument is valid
if (argc != 2)
{
printf("Usage: ./recover image\n");
return 1;
}
// Open the memory card image
char* mem_card = argv[1];
FILE* infile = fopen(mem_card, "r");
if (!infile)
{
printf("File not found/n");
return 2;
}
uint8_t buffer[512];
int image_count = 0;
char filename[9];
FILE* outfile = NULL;
while (!feof(infile) && fread(buffer, sizeof(buffer), 1, infile) == 1)
{
// Check if we have found a JPEG
if (is_jpeg_header(buffer))
{
// If we're already writing output - close it
if (outfile)
fclose(outfile);
sprintf(filename, "%03i.jpg", image_count);
outfile = fopen(filename, "w");
image_count ++;
}
// Write the output if we're ready to write
if (outfile)
fwrite(buffer, sizeof(buffer), 1, outfile);
}
fclose(infile);
fclose(outfile);
return 0;
}
bool is_jpeg_header(const uint8_t buffer[])
{
return
buffer[0] == 0xff
&& buffer[1] == 0xd8
&& buffer[2] == 0xff
&& (buffer[3] & 0xf0) == 0xe0;
}
why while(!foef() is always wrong
regarding:
printf("File not found/n");
Error messages should be output to stderr, not stdout.
When the error indication comes from a C library function should also output to stderr, the text reason the system thinks the error occurred. The function:
perror( "your error msg" );
is made for this purpose.
regarding:
printf("Usage: ./recover image\n");
1) this should be to stderr, not stdout. 2) don't hardcode the executable name. Suggest:
fprintf( stderr, "Usage: %s imagefile\n". argv[0] );
regarding:
while (!feof(inptr) && fread(buffer, sizeof(buffer), 1, inptr) == true)
1) true and false are defined in stdbool.h so that header file needs to be included.
2) fread() returns the number of items read. (which is also the third parameter, (and remembering the prior statement about while( !foef() ) so the statement would be much better written as:
while ( fread(buffer, sizeof(buffer), 1, inptr) == 1)
which catches EOF and partial reads and I/O errors.
regarding;
outptr = fopen(filename, "w");
The success/failure of the call to fopen() is not under the control of your program, Therefore, always check (!=NULL) the returned value to assure the operation was successful.
regarding:
if (is_jpeg_header(buffer) == false)
{ // Continue reading file if we have not found first JPEG
if (found_first_jpeg == false)
{
continue;
} // Continue writing current JPEG into current file
else
This code can be completely removed
the posted code fails to close the current output file after the second file is started.
the posted code always reads sizeof(buffer) bytes (assuming no errors) but there is no guarantee that each image data is exactly a multiple of sizeof(buffer) in length so it can miss the encounter with the next image AND can result in part of the header, etc data from the next image being written into the current output file.
Please post the function:
is_jpeg_header(buffer)
as it is unlikely to correct the problems listed above.
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 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.)
I'm driving myself crazy trying to figure out what is happening with me code.
I'm currently in CS50's pset4. Recover Challenge.
For those who don't know what is it about:
We're given a file called card.raw in which there are some deleted photos. Our task is to implement a program that can do a bit of forensics (idyllically) and recover the lost photos.
Hereby I attach my code:
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: ./recover file\n");
return 1;
}
//declaring pointer infile and giving the address of argv[1];
char *infile = argv[1];
//Opening file
FILE *raw_data;
raw_data = fopen(infile, "r");
//Checking for NULL error
if(raw_data == NULL)
{
fprintf(stderr, "Could not open file.\n");
return 2;
}
uint8_t buffer[512]; //Delcaring unsigned int variable type. Array of 512 bytes.
int counter = 0; //Declaring counter for counting jpegs files
FILE *outfile; //Setting pointer named outfile for printing here
char filename[8]; //declaring 'filename' variable for storing the file's name
//While we can reads blocks of memory of 512 bytes from raw_data (aka the address from the infile) into buffer:
while (fread(buffer, 512, 1, raw_data))
{
//Condition for tracking the first bytes that form a JPEG file
if(buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xf0) == 0xe0)
{
if(counter == 0) //If this is the 1st file, then name the file with
//counter value with 3 digits (%03d)
{
sprintf(filename, "%03d.jpg", counter); // And 3 digits (%i3)
outfile = fopen(filename, "w"); //Open file named outfile in write mode
counter++;
}
else //If this is not the first JPG opened, firstly close the
{ // current open file, and then open a new one with the
fclose(outfile); // current counter value and 3 digits for its name
sprintf(filename, "%03d.jpg", counter);
outfile = fopen(filename, "w"); //Open file named 'outfile' in write mode
counter++;
}
}
fwrite(buffer, 1, sizeof(buffer), outfile); /* Write function that takes buffer data (aka the
pointer to the array of elements to be written,
writes 1 byte of elements of the syze buffer (512)
and it writes it to the output, aka 'outfile' */
}
fclose(outfile); //Remember to close the last file once we get out of the while-loop
}
Here's the tricky part:
I've successfully recovered all the problem images.
But, if I run the code several times, let's say for example, 5 times, I end up having a Segmentation Fault.
When I run check50, I get the following message (I will attach an image with both the segmentation fault after some successful runs and the check50 veredict). Click here to see the image
I just can't get it. I supose there might be some trouble with memory, but I just don't know what is it.
Thank you very much for your time and your help guys. StackOVerFlow is always such a nice place to seek for guidance.
EDIT
If I run echo $? once the Segmentation Fault has prompted, I get a value of 139.
Here's the terminal prompt screenshot
EDIT
Just as #Thomas Dickey has pointed out, the program was writing on a file regardless of having an open file yet or not.
I've updated and fixed a bit my code in order to keep it cleaner, and added an if condition in order to fix it.
Here's the solution:
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: ./recover file\n");
return 1;
}
//declaring pointer infile and giving the address of argv[1];
char *infile = argv[1];
//Opening file
FILE *raw_data;
raw_data = fopen(infile, "r");
//Checking for NULL error
if(raw_data == NULL)
{
fprintf(stderr, "Could not open file.\n");
return 2;
}
uint8_t buffer[512]; //Delcaring unsigned int variable type. Array of 512 bytes.
int counter = 0; //Declaring counter for counting jpegs files
FILE *outfile; //Setting pointer named outfile for printing here
char filename[8]; //declaring 'filename' variable for storing the file's name
//While we can reads blocks of memory of 512 bytes from raw_data (aka the address from the infile) into buffer:
while (fread(buffer, 512, 1, raw_data))
{
//Condition for tracking the first bytes that form a JPEG file
if(buffer[0] == 0xff &&
buffer[1] == 0xd8 &&
buffer[2] == 0xff &&
(buffer[3] & 0xf0) == 0xe0)
{
if(counter != 0)
{
fclose(outfile); //If this is not the first JPG opened, close previous file
}
sprintf(filename, "%03d.jpg", counter); //print stream to 'filename' the value of 'counter' in 3 digits
outfile = fopen(filename, "w"); //Open file named outfile in write mode
counter++; //Add 1 to counter
}
if(counter != 0) //Don't start writing on a file until the first jpeg is found
{
fwrite(buffer, sizeof(buffer), 1, outfile); /* - Write function that takes buffer data
(aka the array of elements to be written) ,
- Write a block of 512 bytes of elements
(aka the size of buffer),
- 1 block of 512 bytes at a time,
- And it writes it to the output, aka 'outfile' */
}
}
fclose(outfile); //Remember to close the last file once we get out of the while-loop
return 0;
}
The program only opens the output file if the header looks okay, but writes to the output irregardless. If you read a file that doesn't have a jpeg header, it'll break.