clinuxlseek

lseek()'s offset and large block devices


For fun and as practice I tried to create a program that writes a bit pattern to a specific sector of a hard drive. My idea was to be able to supply a specific sector number (in LBA format) and have the program try to write to it and then read from it and determine if the written and read is the same.

So far, the program works. However, I run into problems when dealing with very large drives (and therefor really large offsets).

For example, seeking from the beginning to LBA 1306638144 (i.e. seeking to 1306638144*512) overflows seek's off_t.

  //approximately what I am trying to achieve

  #define SECTOR_SIZE 512

  char buf[SECTOR_SIZE] = {0}; //would be initialised with a specific pattern
  long long offset = 1306638144*SECTOR_SIZE
  int fd = open("/dev/sdb", O_RDWR);
  
  lseek(fd, offset, SEEK_SET); //offset overflows
  write(fd, buf, sizeof(buf)/sizeof(char));

I tried working around the issue by iteratively increasing from SEEK_CUR and avoiding passing the huge number.

Is there a proper way to deal with this issue? Is lseek() the wrong function to do this? Is there a better way to seek for a specific sector?

Best regards!


Solution

  • lseek only accepts off_t offset values and on most architectures, they are 32 bits signed long values. Even if you prepare a long long offset it will be converted to a 32 bits value before being used.

    But Posix now comes with lseek64. It is almost the same function with same interface, except that it uses an off64_t type offset which is guaranteed to have (at least) 64 bits.

    So just use the new 64 bits enabled lseek64 instead of the legacy lseek if you have to process files (or devices) where the offset could be greater that what can be represented with a (signed) 32 bits value.