2007-11-07 12:37:30 +01:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
2010-03-16 13:46:58 +01:00
|
|
|
#include <sys/stat.h>
|
2007-11-07 12:37:30 +01:00
|
|
|
#include <sys/ioctl.h>
|
2008-07-24 00:30:21 +02:00
|
|
|
#include <unistd.h>
|
2010-03-16 17:31:39 +01:00
|
|
|
#include <stdint.h>
|
2007-11-07 12:37:30 +01:00
|
|
|
|
2010-03-10 15:41:40 +01:00
|
|
|
#ifdef HAVE_LINUX_FD_H
|
|
|
|
#include <linux/fd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_DISKLABEL_H
|
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_DISK_H
|
|
|
|
#ifdef HAVE_SYS_QUEUE_H
|
|
|
|
#include <sys/queue.h> /* for LIST_HEAD */
|
|
|
|
#endif
|
|
|
|
#include <sys/disk.h>
|
|
|
|
#endif
|
|
|
|
|
2007-11-07 12:37:30 +01:00
|
|
|
#include "blkdev.h"
|
|
|
|
#include "linux_version.h"
|
|
|
|
|
2008-07-24 00:30:21 +02:00
|
|
|
static long
|
|
|
|
blkdev_valid_offset (int fd, off_t offset) {
|
|
|
|
char ch;
|
|
|
|
|
|
|
|
if (lseek (fd, offset, 0) < 0)
|
|
|
|
return 0;
|
|
|
|
if (read (fd, &ch, 1) < 1)
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
off_t
|
|
|
|
blkdev_find_size (int fd) {
|
2010-03-16 17:31:39 +01:00
|
|
|
uintmax_t high, low = 0;
|
|
|
|
|
|
|
|
for (high = 1024; blkdev_valid_offset (fd, high); ) {
|
|
|
|
if (high == UINTMAX_MAX)
|
|
|
|
return -1;
|
2008-07-24 00:30:21 +02:00
|
|
|
|
|
|
|
low = high;
|
2010-03-16 17:31:39 +01:00
|
|
|
|
|
|
|
if (high >= UINTMAX_MAX/2)
|
|
|
|
high = UINTMAX_MAX;
|
|
|
|
else
|
|
|
|
high *= 2;
|
|
|
|
}
|
|
|
|
|
2008-07-24 00:30:21 +02:00
|
|
|
while (low < high - 1)
|
|
|
|
{
|
2010-03-16 17:31:39 +01:00
|
|
|
uintmax_t mid = (low + high) / 2;
|
2008-07-24 00:30:21 +02:00
|
|
|
|
|
|
|
if (blkdev_valid_offset (fd, mid))
|
|
|
|
low = mid;
|
|
|
|
else
|
|
|
|
high = mid;
|
|
|
|
}
|
|
|
|
blkdev_valid_offset (fd, 0);
|
|
|
|
return (low + 1);
|
|
|
|
}
|
|
|
|
|
2007-11-07 12:37:30 +01:00
|
|
|
/* get size in bytes */
|
|
|
|
int
|
|
|
|
blkdev_get_size(int fd, unsigned long long *bytes)
|
|
|
|
{
|
2010-03-10 15:41:40 +01:00
|
|
|
#ifdef DKIOCGETBLOCKCOUNT
|
|
|
|
/* Apple Darwin */
|
|
|
|
if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
|
|
|
|
*bytes <<= 9;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2007-11-07 12:37:30 +01:00
|
|
|
|
2008-07-24 00:30:21 +02:00
|
|
|
#ifdef BLKGETSIZE64
|
2010-03-10 15:41:40 +01:00
|
|
|
{
|
2008-07-24 00:30:21 +02:00
|
|
|
#ifdef __linux__
|
2010-03-10 15:41:40 +01:00
|
|
|
int ver = get_linux_version();
|
|
|
|
|
|
|
|
/* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */
|
|
|
|
if (ver >= KERNEL_VERSION (2,6,0) ||
|
|
|
|
(ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0)))
|
2008-07-24 00:30:21 +02:00
|
|
|
#endif
|
2010-03-10 15:41:40 +01:00
|
|
|
if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-24 00:30:21 +02:00
|
|
|
#endif /* BLKGETSIZE64 */
|
|
|
|
|
|
|
|
#ifdef BLKGETSIZE
|
|
|
|
{
|
|
|
|
unsigned long size;
|
|
|
|
|
|
|
|
if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
|
|
|
|
*bytes = ((unsigned long long)size << 9);
|
|
|
|
return 0;
|
|
|
|
}
|
2007-11-07 12:37:30 +01:00
|
|
|
}
|
|
|
|
|
2008-07-24 00:30:21 +02:00
|
|
|
#endif /* BLKGETSIZE */
|
|
|
|
|
2010-03-10 15:41:40 +01:00
|
|
|
#ifdef DIOCGMEDIASIZE
|
|
|
|
/* FreeBSD */
|
|
|
|
if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
|
|
|
|
return 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FDGETPRM
|
|
|
|
{
|
|
|
|
struct floppy_struct this_floppy;
|
|
|
|
|
|
|
|
if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
|
|
|
|
*bytes = this_floppy.size << 9;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* FDGETPRM */
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_DISKLABEL_H
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This code works for FreeBSD 4.11 i386, except for the full device
|
|
|
|
* (such as /dev/ad0). It doesn't work properly for newer FreeBSD
|
|
|
|
* though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
|
|
|
|
* above however.
|
|
|
|
*
|
|
|
|
* Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
|
|
|
|
* character) devices, so we need to check for S_ISCHR, too.
|
|
|
|
*/
|
|
|
|
int part = -1;
|
|
|
|
struct disklabel lab;
|
|
|
|
struct partition *pp;
|
|
|
|
char ch;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if ((fstat(fd, &st) >= 0) &&
|
|
|
|
(S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
|
|
|
|
part = st.st_rdev & 7;
|
|
|
|
|
|
|
|
if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
|
|
|
|
pp = &lab.d_partitions[part];
|
|
|
|
if (pp->p_size) {
|
|
|
|
*bytes = pp->p_size << 9;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* HAVE_SYS_DISKLABEL_H */
|
|
|
|
|
2010-03-11 00:59:04 +01:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
|
|
*bytes = st.st_size;
|
|
|
|
return 0;
|
|
|
|
}
|
2010-03-16 17:31:39 +01:00
|
|
|
if (!S_ISBLK(st.st_mode))
|
|
|
|
return -1;
|
2010-03-11 00:59:04 +01:00
|
|
|
}
|
|
|
|
|
2008-07-24 00:30:21 +02:00
|
|
|
*bytes = blkdev_find_size(fd);
|
|
|
|
return 0;
|
2007-11-07 12:37:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* get 512-byte sector count */
|
|
|
|
int
|
|
|
|
blkdev_get_sectors(int fd, unsigned long long *sectors)
|
|
|
|
{
|
|
|
|
unsigned long long bytes;
|
|
|
|
|
|
|
|
if (blkdev_get_size(fd, &bytes) == 0) {
|
|
|
|
*sectors = (bytes >> 9);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-10-22 14:52:39 +02:00
|
|
|
/* get logical sector size (default is 512)
|
|
|
|
*
|
|
|
|
* This is the smallest unit the storage device can
|
|
|
|
* address. It is typically 512 bytes.
|
|
|
|
*/
|
2007-11-07 12:37:30 +01:00
|
|
|
int
|
|
|
|
blkdev_get_sector_size(int fd, int *sector_size)
|
|
|
|
{
|
2008-07-24 00:30:21 +02:00
|
|
|
#ifdef BLKSSZGET
|
|
|
|
#ifdef __linux__
|
2007-11-07 12:37:30 +01:00
|
|
|
if (get_linux_version() < KERNEL_VERSION(2,3,3)) {
|
|
|
|
*sector_size = DEFAULT_SECTOR_SIZE;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-24 00:30:21 +02:00
|
|
|
#endif
|
2007-11-07 12:37:30 +01:00
|
|
|
if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return -1;
|
2008-07-24 00:30:21 +02:00
|
|
|
#else
|
|
|
|
*sector_size = DEFAULT_SECTOR_SIZE;
|
|
|
|
return 0;
|
|
|
|
#endif
|
2007-11-07 12:37:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-18 15:43:34 +01:00
|
|
|
#ifdef TEST_PROGRAM
|
2007-11-07 12:37:30 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <err.h>
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
unsigned long long bytes;
|
|
|
|
unsigned long long sectors;
|
|
|
|
int sector_size;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (argc != 2) {
|
|
|
|
fprintf(stderr, "usage: %s device\n", argv[0]);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((fd = open(argv[1], O_RDONLY)) < 0)
|
|
|
|
err(EXIT_FAILURE, "open %s failed", argv[1]);
|
|
|
|
|
|
|
|
if (blkdev_get_size(fd, &bytes) < 0)
|
|
|
|
err(EXIT_FAILURE, "blkdev_get_size() failed");
|
|
|
|
if (blkdev_get_sectors(fd, §ors) < 0)
|
|
|
|
err(EXIT_FAILURE, "blkdev_get_sectors() failed");
|
|
|
|
if (blkdev_get_sector_size(fd, §or_size) < 0)
|
|
|
|
err(EXIT_FAILURE, "blkdev_get_sector_size() failed");
|
|
|
|
|
|
|
|
printf("bytes %llu\n", bytes);
|
|
|
|
printf("sectors %llu\n", sectors);
|
|
|
|
printf("sectorsize %d\n", sector_size);
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
#endif /* TEST_PROGRAM */
|