Hello, thanks to everyone who responded with their suggestions. Using various non-portable ioctls I can device size on most platforms, for both block and raw devices.
This is more convoluted than a single lseek() call, but it is what it is. If anyone wants to do something similar, then the following example can give you a good start. /* * devsize.c * gcc -DNETBSD -Wall -Wextra -pedantic -o devsize devsize.c */ #include <assert.h> #include <fcntl.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <unistd.h> #if defined(NETBSD) #include <sys/dkio.h> #endif #if defined(FREEBSD) #include <sys/disk.h> #endif #if defined(MACOS) #include <sys/disk.h> #endif #if defined(LINUX) #include <linux/fs.h> #endif /* * Use lseek() to get device size in bytes. * This should be portable, but on some platforms returns 0 bytes, * in which case nonportable ioctl should be used. */ static void print_devsize_lseek(int fd) { off_t size; size = lseek(fd, 0, SEEK_END); assert(size >= 0); printf("lseek SEEK_END = %ju bytes\n", (uintmax_t)size); } /* NetBSD, FreeBSD */ #if defined(DIOCGMEDIASIZE) static void print_devsize_DIOCGMEDIASIZE(int fd) { int int_val; off_t size; /* This sets size to total number of bytes */ int_val = ioctl(fd, DIOCGMEDIASIZE, &size); assert(int_val >= 0); printf("ioctl DIOCGMEDIASIZE = %ju bytes\n", (uintmax_t)size); } #endif /* MacOS */ #if defined(DKIOCGETBLOCKCOUNT) && defined(DKIOCGETBLOCKSIZE) static void print_devsize_DKIOCGETBLOCKCOUNT(int fd) { int int_val uint64_t block_count; uint32_t block_size; /* Total block count */ int_val = ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count); assert(int_val >= 0); /* Block size */ int_val = ioctl(fd, DKIOCGETBLOCKSIZE, &block_size); assert(int_val >= 0); printf("ioctl DKIOCGETBLOCKCOUNT = %ju bytes\n", (uintmax_t)block_count * (uintmax_t)block_size); } #endif /* Linux */ #if defined(BLKGETSIZE) static void print_devsize_BLKGETSIZE(int fd) { int int_val; unsigned long size; /* This sets size to number of 512-byte blocks */ int_val = ioctl(fd, BLKGETSIZE, &size); assert(int_val >= 0); printf("ioctl BLKGETSIZE = %ju bytes\n", (uintmax_t)size * 512); } #endif /* Linux */ #if defined(BLKGETSIZE64) static void print_devsize_BLKGETSIZE64(int fd) { int int_val; uint64_t size; /* This sets size to total number of bytes */ int_val = ioctl(fd, BLKGETSIZE64, &size); assert(int_val >= 0); printf("ioctl BLKGETSIZE = %ju bytes\n", (uintmax_t)size); } #endif int main(int argc, char *argv[]) { int fd; if (argc != 2) { fprintf(stderr, "Usage: devsize <device>\n"); exit(EXIT_FAILURE); } /* Open device */ fd = open(argv[1], O_RDONLY); assert(fd >= 0); print_devsize_lseek(fd); #if defined(DIOCGMEDIASIZE) print_devsize_DIOCGMEDIASIZE(fd); #endif #if defined(DKIOCGETBLOCKCOUNT) && defined(DKIOCGETBLOCKSIZE) print_devsize_DKIOCGETBLOCKCOUNT(fd); #endif #if defined(BLKGETSIZE) print_devsize_BLKGETSIZE(fd); #endif #if defined(BLKGETSIZE64) print_devsize_BLKGETSIZE64(fd); #endif }