0

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.

gkr2d2
  • 693
  • 2
  • 9
  • 20
  • 2
    Why are you doing a seek after the read? The read already updates the file offset. – Mat Nov 14 '19 at 09:05
  • When you say 0 bytes do you mean the block is 0 bytes long, or some of the bytes in the block are 0? – user253751 Nov 14 '19 at 10:15
  • If `read()` returns zero it means end of stream, not 'a block has zero bytes'. When you get zero you need to exit the loop. And the seek is redundant, as @Mat states. The read will advance the pointer, and seeking to the current position is always redundant. – user207421 Nov 14 '19 at 10:15
  • I mean that even when block contains only zero bytes as data. Size of block is still valid but block is filled with zeroes. – gkr2d2 Nov 14 '19 at 10:26
  • 1
    So remove the redundant `lseek()`, as you've been told three times, and see what happens. But it's hard to believe that that really causes an `lseek()` error. I suspect you are confusing both possibilities. – user207421 Nov 14 '19 at 10:33

1 Answers1

2

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
    }                  
}
gstukelj
  • 2,291
  • 1
  • 7
  • 20