Cs50 Problem set 4 Recover Solution 2021: my explanation

Here it is, the last pset for week 4, as the name of the problem implies, we are required to write a program that recovers a JPEG from a forensic image.

this program takes two arguments from user’s and returns 50 jpegs as output after the successful running of the program.

I have downloaded the problem distribution code using wget and ensured all required file is included.

Program Specification Requirement

  • Implement your program in a file called recover.c in a directory called recover.
  • Your program should accept exactly one command-line argument, the name of a forensic image from which to recover JPEGs.
  • If your program is not executed with exactly one command-line argument, it should remind the user of correct usage, and main should return 1.
  • If the forensic image cannot be opened for reading, your program should inform the user as much, and main should return 1.
  • Your program, if it uses malloc, must not leak any memory.

these are the requirement we are to follow to run a successful program.

Problem Background

  • We want to write a program that recovers JPEGs from a Forensic image
  • From the problem background, we learned that a Jpeg image starts with three bytes of which are 0xff, 0xd8, and 0xff
  • Digital Camera tends to store photographs contiguously on a memory card
  • Digital Camera initializes cards with a file system whose “block-size” is 512 bytes, so a 1Mb image thus takes up 1048576 / 512 = 2048 blocks on a memory card.

above are some background we need to understand before writing anything.

Most especially is the Walkthrough pseudocode that will guide us through the overall steps from collecting input from users to printing jpegs, so make sure to watch the Walkthrough if you are doing this pset.

Pseudocode

  • make sure argument count is equal to 2
  • Open memory card for reading
    • Read 512 bytes into a buffer
    • if start of new JPEG
      • write JPEG into filename (in form of ###.jpg)
      • open new file name for writing and keep track of image found
    • then start writing into the new file
  • Close any remaining files

Making sure argument count is equal to 2

    if (argc != 2)
    {
        printf("Usage: ./recover card.raw");
        return 1;
    }

From our previous knowledge, we all know that argc means argument count, which means the number of words the user input in the terminal, if it is more than two, print out “Usage: ./recover card.raw” and return 1 to denotes an error.

Open memory card for reading

FILE *input_file = fopen(argv[1], "r");
    //if check Input_file pointer fail to open then REturn error code "Could not open file"
    if (input_file == NULL)
    {
        printf("Could not open file");
        return 2;
    }

    //declare a variable to unsigned char to store 512 chunks array
    unsigned char buffer[512];

    //for the purpose of counting of image later in the loop
    int count_image = 0;

    //An uninitialize file pointer to use to output data gotten from input file
    FILE *output_file = NULL;

    char *filename = malloc(8 * sizeof(char));

code explanation

  • for us to open a file we need to use the fopen(); function, the function returns a FILE* (file pointer on successful), and return NULL on unsuccessful implementation (when the file can not be opened).
  • The function takes in two parameters (the file you want to open and mode of operation), the memory card we want to open which is at argv[1] in the terminal and the mode of operation is reading “r”, we want to read from the file.
  • we also check if the File* is not equal to NULL i.e to check if the program couldn’t open the file to print out the above words as an error message
  • I created a variable unsigned char buffer to store an array of 512, this is to read 512 bytes of memory into the file
  • I also create a variable count_image to keep the track of the number of images we have found.
  • FILE *output_file is a pointer to a file I will be writing to at the end of the program, since I’m not using it now, I initialized to NULL;
  • I also use char *filename = malloc(8 * sizeof(char)), using malloc i allocated 8 multiply by size of char in order to save ###.jpg’/0′ in the filename.

Read 512 bytes into a buffer

while (fread(buffer, sizeof(char), 512, input_file) != 0)
  • To read data from the memory card, I used the fread(); function, it takes in 4 parameters(“pointer to where we are going to store the data we are reading, then size, size of each element to read, number, the number of the element you want to read at once, then last is inptr, the File* you want to read the data from.
  • in the above case, I’m reading 512 chunks of data of size char from input_file into the buffer.
  • the reason why I am using the while loop (while (fread !=0), is that I am checking if the program has reached the end of file. The logic here is that when fread reach the end of file it returns an integer less than 1.

If start of new JPEG, write JPEG into filename (in form of ###.jpg), open new file name for writing and keep track of image found

 if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
        {
            //write jpeg into file name in form 001.jpg, 002.jpg and so on
            sprintf(filename, "%03i.jpg", count_image);

            //open filename for writing
            output_file = fopen(filename, "w");

            //count number of image found
            count_image++;
        }

code explanation

  • from the walkthrough video, we learned that for an image to be a JPEG, it has to start with the first three bytes (0xff, 0xde, and 0xff), and the fourth bytes can be from (0xe0 to 0xef).
  • we check if the first three bytes are similar to JPEG and also use a bitwise operator to check for the forth bytes (buffer[3] & 0xf0) == 0xe0)
  • then we write into the filename with sprintf. the sprintf takes three parameters which is the (the name of the string we want to write to, the second parameter is the format string like (03i.jpg), and the number we want to substitute.
  • Then we open the filename for writing “w”,
  • we also keep track of the number of images found with count_image by incrementing it.

Writing into the new file

        if (output_file != NULL)
        {
            fwrite(buffer, sizeof(char), 512, output_file);
        }

code explanation,

  • The program checks if output_file has been open earlier i.e it has been initialized to something (in the case of the previous use (output_file = fopen(filename, “w”), the return is true,
  • if so, then we start writing from the buffer into the output_file with the fwrite(); functions. The fwrite also takes in 4 parameters like fread.

Closing any remaining files

    free(filename);
    fclose(output_file);
    fclose(input_file);

    return 0;
  • I close every file pointer we have open with fclose(); functions, I also free the memory I have allocated with free(); to avoid segmentation fault.

Overall Recover code here

Conclusion

This Pset help me learn how to work with File in C, it also teaches me more on how to ask for help when stranded with code, a very big thanks to David J. Malan, Doug Llyod, and other cs50 lecturers.

if you are doing this pset, do ensure you watch the problem walkthrough and understand it, before proceeding to write your code, also when asking for help don’t always copy-paste another person code, make sure to understand all line of your code, at least for just cs50.

thanks for reading, I hope you had a wonderful experience.

Pls if you have any questions don’t hesitate to ask in the comment section, also don’t forget to comment on your experience with the Recover problem set, will be expecting your comment.

thanks, do enjoy your day.

4 comments

  1. very good
    but you must remove the slashes in line 45
    wish the best

    1. Oh thanks, I never noticed. I have edited it.

  2. wait is line 45 without the slashes important or is it just out of context?

    1. The Code works well with or without it. it’s not necessary

Leave a Reply