Reads and writes to devices must be both sector aligned, and with byte counts that are integer multiples of the sector size.
Don't make assumptions about the sector size, you should query the sector size for any device, and work dynamically with that. Typical sizes are 512 for hard drives, and 2048 for optical drives.
If you want functions that allow you to read byte by byte on devices, without incurring wasteful overhead, try this trick:
FILE *file_pointer = fopen("/path/to/device", "rb");
size_t sector_size;
ioctl(fd, BLKSSZGET, §or_size);
setvbuf(file_pointer, NULL, _IOFBF, sector_size);
If you need to get the sector size on Windows you can call DeviceIoControl()
with IOCTL_DISK_GET_DRIVE_GEOMETRY
.
Stdio will align seeks to s
and read chunks of size s
. Additionally you can provide a buffer of your own using posix_memalign()
, or _aligned_malloc()
, if your underlying stdio implementation doesn't do this.
Edit: To clear up some confusion in a comment
You're working with a device with sector size 512, with FILE *f;
. You fseek()
to offset 37. f
's position is updated, but no seek is made on the device. You fread()
500 bytes. lseek()
is called with an offset of 0. 512 bytes are read into f
's buffer. Bytes 37 to 512 are copied to the buffer you provided. lseek()
is called with an offset of 512. 512 bytes are read, and the remaining 463 bytes you're expecting are copied out to the buffer you passed to fread()
. If you were to now fread()
a single byte, that would simply be copied out of the existing buffer in f
, without hitting the device.