getsize.c revision b22ea17742b3e354c3ee1372d43630dda21a59f5
1/* 2 * getsize.c --- get the size of a partition. 3 * 4 * Copyright (C) 1995, 1995 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the 8 * GNU Lesser General Public License. 9 * %End-Header% 10 */ 11 12#define _LARGEFILE_SOURCE 13#define _LARGEFILE64_SOURCE 14 15/* include this before sys/queues.h! */ 16#include "blkidP.h" 17 18#include <stdio.h> 19#if HAVE_UNISTD_H 20#include <unistd.h> 21#endif 22#if HAVE_ERRNO_H 23#include <errno.h> 24#endif 25#include <fcntl.h> 26#ifdef HAVE_SYS_IOCTL_H 27#include <sys/ioctl.h> 28#endif 29#ifdef HAVE_LINUX_FD_H 30#include <linux/fd.h> 31#endif 32#ifdef HAVE_SYS_DISKLABEL_H 33#include <sys/disklabel.h> 34#include <sys/stat.h> 35#endif 36#ifdef HAVE_SYS_DISK_H 37#ifdef HAVE_SYS_QUEUE_H 38#include <sys/queue.h> /* for LIST_HEAD */ 39#endif 40#include <sys/disk.h> 41#endif 42#ifdef __linux__ 43#include <sys/utsname.h> 44#endif 45 46#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) 47#define BLKGETSIZE _IO(0x12,96) /* return device size */ 48#endif 49 50#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) 51#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ 52#endif 53 54#ifdef APPLE_DARWIN 55#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 56#endif /* APPLE_DARWIN */ 57 58static int valid_offset(int fd, blkid_loff_t offset) 59{ 60 char ch; 61 62 if (blkid_llseek(fd, offset, 0) < 0) 63 return 0; 64 if (read(fd, &ch, 1) < 1) 65 return 0; 66 return 1; 67} 68 69/* 70 * Returns the number of blocks in a partition 71 */ 72blkid_loff_t blkid_get_dev_size(int fd) 73{ 74 int valid_blkgetsize64 = 1; 75#ifdef __linux__ 76 struct utsname ut; 77#endif 78 unsigned long long size64; 79 unsigned long size; 80 blkid_loff_t high, low; 81#ifdef FDGETPRM 82 struct floppy_struct this_floppy; 83#endif 84#ifdef HAVE_SYS_DISKLABEL_H 85 int part = -1; 86 struct disklabel lab; 87 struct partition *pp; 88 char ch; 89 struct stat st; 90#endif /* HAVE_SYS_DISKLABEL_H */ 91 92#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ 93 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { 94 if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) 95 && (size64 << 9 > 0xFFFFFFFF)) 96 return 0; /* EFBIG */ 97 return (blkid_loff_t) size64 << 9; 98 } 99#endif 100 101#ifdef BLKGETSIZE64 102#ifdef __linux__ 103 if ((uname(&ut) == 0) && 104 ((ut.release[0] == '2') && (ut.release[1] == '.') && 105 (ut.release[2] < '6') && (ut.release[3] == '.'))) 106 valid_blkgetsize64 = 0; 107#endif 108 if (valid_blkgetsize64 && 109 ioctl(fd, BLKGETSIZE64, &size64) >= 0) { 110 if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) 111 && ((size64) > 0xFFFFFFFF)) 112 return 0; /* EFBIG */ 113 return size64; 114 } 115#endif 116 117#ifdef BLKGETSIZE 118 if (ioctl(fd, BLKGETSIZE, &size) >= 0) 119 return (blkid_loff_t)size << 9; 120#endif 121 122#ifdef FDGETPRM 123 if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) 124 return (blkid_loff_t)this_floppy.size << 9; 125#endif 126#ifdef HAVE_SYS_DISKLABEL_H 127#if 0 128 /* 129 * This should work in theory but I haven't tested it. Anyone 130 * on a BSD system want to test this for me? In the meantime, 131 * binary search mechanism should work just fine. 132 */ 133 if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode)) 134 part = st.st_rdev & 7; 135 if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { 136 pp = &lab.d_partitions[part]; 137 if (pp->p_size) 138 return pp->p_size << 9; 139 } 140#endif 141#endif /* HAVE_SYS_DISKLABEL_H */ 142 143 /* 144 * OK, we couldn't figure it out by using a specialized ioctl, 145 * which is generally the best way. So do binary search to 146 * find the size of the partition. 147 */ 148 low = 0; 149 for (high = 1024; valid_offset(fd, high); high *= 2) 150 low = high; 151 while (low < high - 1) 152 { 153 const blkid_loff_t mid = (low + high) / 2; 154 155 if (valid_offset(fd, mid)) 156 low = mid; 157 else 158 high = mid; 159 } 160 return low + 1; 161} 162 163#ifdef TEST_PROGRAM 164int main(int argc, char **argv) 165{ 166 blkid_loff_t bytes; 167 int fd; 168 169 if (argc < 2) { 170 fprintf(stderr, "Usage: %s device\n" 171 "Determine the size of a device\n", argv[0]); 172 return 1; 173 } 174 175 if ((fd = open(argv[1], O_RDONLY)) < 0) 176 perror(argv[0]); 177 178 bytes = blkid_get_dev_size(fd); 179 printf("Device %s has %Ld 1k blocks.\n", argv[1], bytes >> 10); 180 181 return 0; 182} 183#endif 184