Question

so I have a functions which takes a an offset and a width to read from a device(usually harddisks). Now I currently use fseeko() and fread() to read from the disk. However I like to replace this with pread as its just more concise. It seems however that pread ALWAYS reads from offset 0 no matter what.

Here is the code of the function:

Just some Heads up. I do have

#define _FILE_OFFSET_BITS 64

at the top of my code. I also tried pread64() with the same result!

fseeko() with fread(), this does what I want!:

uint8_t retrievedata(FILE *fp,uint64_t seekpoint, uint64_t seekwidth) {
    unsigned char     buf[seekwidth];

    if(fseeko(fp,seekpoint,SEEK_SET)==0) {
        if(fread(buf,sizeof buf,1,fp)==1) {
             /* do work with retrieved data */
        }
        else {
            printf("ERROR READING AT: %"PRIu64"| WITH VAL WIDTH: %"PRIu64"\n",seekpoint,seekwidth);
            return 4;
        }
    }
    else {
        printf("ERROR SEEKING AT: %"PRIu64"\n",seekpoint);
        return 3;
    }
}

pread(), This always reads from offset 0 no matter what the 'seekpoint' is:

uint8_t retrievedata(FILE *fp,uint64_t seekpoint, uint64_t seekwidth) {
    unsigned char     buf[seekwidth]

    if (pread(fileno(fp),buf,seekwidth,seekpoint)!=-1) {
        /* do something */
    } else {
        printf("ERROR SEEKING AND/OR READING AT: %"PRIu64"\n",seekpoint);
        return 3;
    }
}
Was it helpful?

Solution

Enable compiler warnings (-W -Wall for GCC), and read the "Feature Test Macro Requirements for GLIBC" sections in the man pages.

The warnings would have indicated you missed another macro definition needed, and the man 2 pread man page told you that the macro definition you also need is

#define _POSIX_C_SOURCE 200809L

to get the GNU C library version 2.12 or newer to declare pread() etc. correctly.

The behaviour you are seeing, is due to not having that declaration.


I want to tell you how I perceive the full story, because I hope it will show you exactly how and how much you can save sweat and effort by reading and understanding the man pages, and more importantly, by enabling and addressing the compiler warnings.

I know you think you just don't have time to do all that right now (maybe later, right?), but you're wrong: it is one of the best ways to save time, when writing C for Linux (or POSIX-like systems in general).

I myself always use -W -Wall with GCC. It just makes it much easier to find where the cause of the problem lies. Right now, you were concentrating on the primary symptom instead, and since programming is not politics (yet), it won't get you anywhere.

So, full picture:

fread() returns the number of elements read. In your case, you read one element of seekwidth bytes, and you verify that one element was correctly read.

pread() returns the number of bytes read. Your second code snippet tries to read seekwidth bytes starting at offset seekpoint, but you don't check how much data was actually read, you only check whether an error occurred.

Here is what I think is happening to you:

  1. You have omitted the #define _POSIX_C_SOURCE 200809L declaration (prior to #include <unistd.h>) required since glibc 2.12 to get the pread() function prototype declared

  2. You are compiling without warnings, or you are ignoring the "implicit declaration of function `pread`" warning

  3. You are compiling on a 32-bit architecture, or to 32-bit architecture using -m32 GCC option

Without a function prototype, the compiler assumes all parameters to the pread() function are ints -- and therefore 32-bit --, and only supplies a 32-bit seekpoint value to pread(). The three first parameters (an int, a pointer, and a size_t) all happen to be 32-bit, but the fourth parameter, file offset, is 64-bit. (Remember, you told your C library so, using #define _FILE_OFFSET_BITS 64.)

This means that the 64-bit file offset pread() receives, is garbage. (It depends on the byte order -- little-endian or Intel-like, or big-endian or Motorola/PowerPC/ARM-like -- and how the particular architecture binary interface (ABI) passes the fourth parameter to a function, exactly how the value is garbled.)

In this case, I believe you're targeting 32-bit Intel architecture, where often the value pread() actually receives (as garbled) is positive and larger than the file size, and therefore pread() returns 0: "past end-of-file, no more data to read".

Your code ignores that (since it is not -1), and instead assumes it read the data successfully. Most likely the data you're seeing is from a previous read -- pread() does not modify the buffer if it returns 0.

(There are other possible variants of the situation, too; perhaps even some where pread() always receives a zero as the (garbled) offset. It does not actually matter, as in all cases having the proper function prototype fixes the problem. You do need to check the pread() return value, too, as there are no guarantees it'll actually read the number of bytes you requested. It does so often, yes; but there are no guarantees, so make no unfounded assumptions, please.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top