3

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;
}
  • Where is the segmentation fault happening? Compile your program with the `-g` flag, then use `gdb` to run the binary. This should tell you where the segmentation fault happens – smac89 Sep 11 '18 at 04:56

3 Answers3

2
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.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • I'm not quite understanding what you are saying about the fclose() function call. Could you clarify? – zach dalton Sep 11 '18 at 03:18
  • If `img == NULL` then you shouldn't be calling `fclose(img)`, cause you're just passing it a `NULL` pointer. The file didn't open successfully, so don't be trying to close it. And conversely, you *should* be calling `fclose(img)` at the end of the `while` loop when you're done with the file, but that call's missing. You have a call to `fclose()` *after* the loop, but it really should be *inside* the loop at the end. – John Kugelman Sep 11 '18 at 12:20
  • I've implemented the changes that have been mentioned in this thread to my code however I am still getting a segmentation fault. The fault only goes away if I move the fclose() call outside the while loop. I am totally confused. What am I doing wrong? – zach dalton Sep 12 '18 at 03:44
0

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.

smac89
  • 39,374
  • 15
  • 132
  • 179
  • Okay, that makes a lot of sense. However I found that after changing the number of characters in the string for the name and changing the last if statement as I have described I am able to swap the numbers in fwrite and fread and the program still runs successfully. Is there something else going on here that I am missing? – zach dalton Sep 11 '18 at 04:44
  • @zachdalton, Running successfully does not mean it is correct. If you look at the documentation for `fwrite` for example, you will see that it does not check the size of the array, and all it is concerned with is writing `count` objects of size `size` to the buffer. In your case it works because it writes `1` object of size `512` bytes to your array which happens to work because your array can take `512` bytes. – smac89 Sep 11 '18 at 04:51
  • I see. I clearly have a lot to learn. I appreciate the help. I feel like as I'm learning all of this I'm missing a lot of the nuances and finer points that I should be picking up. – zach dalton Sep 11 '18 at 05:05
  • @zachdalton, yw. Don't worry in no time it will become second nature – smac89 Sep 11 '18 at 05:08
  • Swapping the size and count arguments won't break anything. They're interchangeable. – John Kugelman Sep 11 '18 at 12:16
  • @zachdalton for future reference, when you update your question after it has been answered, don't change the original code, just add the new one below that. It helps future readers know what the problem was. In your new code, the problem is either that you close the file before you write to it or you are trying to write to the original file which was opened in read mode. Check your logic and you will see where this happens – smac89 Sep 12 '18 at 04:09
  • I feel really dumb. I'm just not seeing it. – zach dalton Sep 14 '18 at 01:06
-1

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?

  • `!counter` and `counter != 0` are equivalent expressions. They do the exact same thing. It's only coincidence that the behavior changed when you changed that line. – John Kugelman Sep 11 '18 at 12:18
  • That's what I had originally thought after watching the lecture in which the operator was used. Another tutorial confused me about that though and since it worked after I switched it I thought I was wrong. – zach dalton Sep 11 '18 at 23:27