CS5Ox Pset4 Recover: code only recovers partial images - c

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.

Related

Can't figure out solution to cs50's "Recover" in C. New JPEGs are being created but I cannot view them-

I am currently on the "Recover" pset in cs50 and I have tried multiple variations of my solution. The assignment is to recover 50 JPEG files that were deleted from a memory card. We are instructed to check the first 4 bytes of each 512 byte pass of fread to indicate the start of a JPEG file and to write it to a new file. Once it reaches another 4 bytes that match, we are suppose to close the previous file and write to a new one until reaching EOF (the data from the memory card is provided to us in a file called card.raw).
Below is my current solution. My confusion is that I am able to produce exactly 50 JPEGS upon running my program, as described. However, when I go to open any of the new files, I am met with an error in VS Code, which I will include below my code. Any help would be appreciated here-
`
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: ./recover IMAGE\n");
return 1;
}
FILE *forensic = fopen(argv[1], "r");
if (forensic == NULL)
{
printf("File was not found\n");
return 1;
}
FILE *currentFile;
currentFile = NULL;
int jpegCount = 1;
char *filename = NULL;
filename = malloc(8 * sizeof(char));
BYTE buffer[512];
while (fread(buffer, sizeof(char), 512, forensic) != 0)
{
// Check for matching 4 bytes, indicating start of a new JPEG
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
// If file exists, close it and start a new one
if (currentFile != NULL)
{
fclose(currentFile);
sprintf(filename, "%03i.jpg", jpegCount);
currentFile = fopen(filename, "w");
jpegCount++;
}
// If no file exists yet, create one
else
{
sprintf(filename, "%03i.jpg", jpegCount);
currentFile = fopen(filename, "w");
jpegCount++;
}
}
// If file exists, write to it
if (currentFile != NULL)
{
BYTE wBuffer[512];
fwrite(wBuffer, sizeof(BYTE), 512, currentFile);
}
}
fclose(currentFile);
fclose(forensic);
free(filename);
return 0;
}
`
Again, my program successfully produces 50 new JPEGs, but when I try to open them in VS Code I get the following error:
Error loading webview: Error: Could not register service workers: NotSupportedError: Failed to register a ServiceWorker for scope ('https://0foennjt10in3q0224oduooshmepdo2jsp1eg131ejbmkj7a2i7k.vscode-cdn.net/stable/8fa188b2b301d36553cbc9ce1b0a146ccb93351f/out/vs/workbench/contrib/webview/browser/pre/') with script ('https://0foennjt10in3q0224oduooshmepdo2jsp1eg131ejbmkj7a2i7k.vscode-cdn.net/stable/8fa188b2b301d36553cbc9ce1b0a146ccb93351f/out/vs/workbench/contrib/webview/browser/pre/service-worker.js?v=4&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&remoteAuthority=codespaces+sebdoubleu-code50-111027905-wpxr5g54pv9h9667'): The user denied permission to use Service Worker..
I would really appreciate any help here. I am a beginner and it is very possible that I have made a minor error that I'm not seeing. It is also possible that I am going about this completely wrong and need to start over. I just don't know where to go from here because I feel like I've tried everything that I can think of at this point.

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.

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.

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?

Resources