gkr2d2
gkr2d2

Reputation: 773

How to read a block device till the last byte

I want to read a block device file, block by block until last byte even if the block is full of zeroes. My code is this. size is the no. of bytes I want it to read in one go - could be anything 4K, 8K etc.

for (int i = 1; i <= num; i++){
    read_bytes = read(fd, buf, size);
    if (read_bytes < 0) {
       perror("read failed");
       return 1;
    }
    lseek_offset = lseek(fd, size, SEEK_CUR);
    if (lseek_offset < 0){
       perror("lseek failed.\n");   
       return 1;
    }
}

When a block is filled with zero bytes (not the length of block but the data in block), lseek fails with EINV. And I can see from df -h that that disk is half full and rest is zero bytes since it was formatted with ext4 before using it.

Upvotes: 0

Views: 517

Answers (1)

gstukelj
gstukelj

Reputation: 2551

As already mentioned by @Mat in the comments read already updates the file offset so you should remove the lseek call. From read(2) man pages:

On files that support seeking, the read operation commences at the current file offset, and the file offset is incremented by the number of bytes read. If the current file offset is at or past the end of file, no bytes are read, and read() returns zero.

Also note that the read call might fail due to an interrupt, so you should check the value of errno for that (I'm guessing you'd still like to continue reading).

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

for (int i = 1; i <= num; i++) {
    read_bytes = read(fd, buf, size);
    if (read_bytes < 0 && errno != EINTR) {
        perror("read failed");
        return 1;
    }
}

Finally, note that you are not guaranteed that the size number of bytes will be read in one go (see read(2)), most likely because of a signal interrupt. So here's an idea of the top of my head. You could check file size in a while loop within a single iteration of the for loop to determine how much you still need to read. For example:

for (int i = 1; i <= num; i++) {
    size_t remain = size;
    while(remain) {    // keep reading until you've read size bytes
        read_bytes = read(fd, buf, remain);
        if (read_bytes < 0 && errno != EINTR) {
            perror("read failed");
            return 1;
        }
        ....           // determine the amount of bytes read in the last iteration
        remain = ...   // update the size to the bytes still needed to be read
    }                  
}

Upvotes: 2

Related Questions