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?
"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.