3

I am trying to seek and read from a file and my objective is that all reads come directly from disk. In order to do this, I open() the file with O_DIRECT, lseek() to the required offset, and try to read() a block from disk. I encounter an error while reading from the disk:

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

int main(int argc, char *argv[])
{
        int disk_fd_read;
        off_t disk_off;
        int ret;

        int next_block;

        disk_fd_read = open(argv[1], O_RDONLY | O_DIRECT);
        if (disk_fd_read < 0) {
                printf("disk open error: %s\n", strerror(errno));
                exit(1);
        }

        disk_off = 100;

        disk_off = lseek(disk_fd_read, disk_off, SEEK_SET);
        if (disk_off != 100 || disk_off < 0) {
                printf("Error: could not seek %s\n", strerror(errno));
                exit (1);
        }
        printf("disk offset = %ld\n", disk_off);
        ret = read(disk_fd_read, &next_block, sizeof(uint64_t));
        /* 
        pread does not work either...
        ret = pread(disk_fd_read, &next_block, sizeof(uint64_t), disk_off);
        */
        if( ret == -1) {
                printf("error reading from device %s\n",strerror(errno));
                exit(1);
        }
        close(disk_fd_read);
}

/* RUN:
dd if=/dev/zero of=1Mfile bs=1M count=1
./a.out 1Mfile
disk offset = 100
error reading from device Invalid argument
*/

The error goes away when I remove O_DIRECT while opening the file. From read manpage:

    EINVAL fd  is attached to an object which is unsuitable for reading; or the file was 
opened with the O_DIRECT flag, and either the address specified in buf, the value specified 
in count, or the current file offset is not suitably aligned.

Does this mean lseek does not support O_DIRECT? How can we seek to different disk offsets and directly read from disk?

Shehbaz Jaffer
  • 1,944
  • 8
  • 23
  • 30
  • Never access beyond `argv[0]` with out first checking `argc` to assure the command line parameter was actually entered by the user – user3629249 Jul 05 '20 at 04:41

1 Answers1

3

"suitably aligned" is the key here. You need to ensure your offset is 4k (pagesize) aligned. Also the size needs to be a multiple of 4k.

janneb
  • 36,249
  • 2
  • 81
  • 97
  • thank you, to followup: I changed disk offset to 4k, I read 4k bytes in a char buffer (this makes value specified in count and current file offset pagesize aligned). I still get the error, I believe it is because I do not know how to align the address in buf to 4k? I currently use char buf[4096] and read data into the buffer. – Shehbaz Jaffer Jul 04 '20 at 17:33
  • 1
    [this](https://stackoverflow.com/questions/21550420/how-allocate-memory-which-is-page-size-aligned/21550475) helped in using memaligned addresses. the code works now. thank you! – Shehbaz Jaffer Jul 04 '20 at 17:39