getsize.c revision 7270fbe7fa51cbce01a07d031f03872f314206d1
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 "config.h" 17#include "blkidP.h" 18 19#include <stdio.h> 20#if HAVE_UNISTD_H 21#include <unistd.h> 22#endif 23#if HAVE_ERRNO_H 24#include <errno.h> 25#endif 26#include <fcntl.h> 27#ifdef HAVE_SYS_IOCTL_H 28#include <sys/ioctl.h> 29#endif 30#ifdef HAVE_LINUX_FD_H 31#include <linux/fd.h> 32#endif 33#ifdef HAVE_SYS_DISKLABEL_H 34#include <sys/disklabel.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#if HAVE_SYS_STAT_H 46#include <sys/stat.h> 47#endif 48 49 50#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) 51#define BLKGETSIZE _IO(0x12,96) /* return device size */ 52#endif 53 54#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) 55#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ 56#endif 57 58#ifdef APPLE_DARWIN 59#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 60#endif /* APPLE_DARWIN */ 61 62static int valid_offset(int fd, blkid_loff_t offset) 63{ 64 char ch; 65 66 if (blkid_llseek(fd, offset, 0) < 0) 67 return 0; 68 if (read(fd, &ch, 1) < 1) 69 return 0; 70 return 1; 71} 72 73/* 74 * Returns the number of bytes in a partition 75 */ 76blkid_loff_t blkid_get_dev_size(int fd) 77{ 78 int valid_blkgetsize64 = 1; 79#ifdef __linux__ 80 struct utsname ut; 81#endif 82 unsigned long long size64; 83 unsigned long size; 84 blkid_loff_t high, low; 85#ifdef FDGETPRM 86 struct floppy_struct this_floppy; 87#endif 88#ifdef HAVE_SYS_DISKLABEL_H 89 int part = -1; 90 struct disklabel lab; 91 struct partition *pp; 92 char ch; 93 struct stat st; 94#endif /* HAVE_SYS_DISKLABEL_H */ 95 96#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ 97 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { 98 if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) 99 && (size64 << 9 > 0xFFFFFFFF)) 100 return 0; /* EFBIG */ 101 return (blkid_loff_t) size64 << 9; 102 } 103#endif 104 105#ifdef BLKGETSIZE64 106#ifdef __linux__ 107 if ((uname(&ut) == 0) && 108 ((ut.release[0] == '2') && (ut.release[1] == '.') && 109 (ut.release[2] < '6') && (ut.release[3] == '.'))) 110 valid_blkgetsize64 = 0; 111#endif 112 if (valid_blkgetsize64 && 113 ioctl(fd, BLKGETSIZE64, &size64) >= 0) { 114 if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) 115 && ((size64) > 0xFFFFFFFF)) 116 return 0; /* EFBIG */ 117 return size64; 118 } 119#endif /* BLKGETSIZE64 */ 120 121#ifdef BLKGETSIZE 122 if (ioctl(fd, BLKGETSIZE, &size) >= 0) 123 return (blkid_loff_t)size << 9; 124#endif 125 126/* tested on FreeBSD 6.1-RELEASE i386 */ 127#ifdef DIOCGMEDIASIZE 128 if (ioctl(fd, DIOCGMEDIASIZE, &size64) >= 0) 129 return (off_t)size64; 130#endif /* DIOCGMEDIASIZE */ 131 132#ifdef FDGETPRM 133 if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) 134 return (blkid_loff_t)this_floppy.size << 9; 135#endif 136#ifdef HAVE_SYS_DISKLABEL_H 137 /* 138 * This code works for FreeBSD 4.11 i386, except for the full device 139 * (such as /dev/ad0). It doesn't work properly for newer FreeBSD 140 * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE 141 * above however. 142 * 143 * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw, 144 * character) devices, so we need to check for S_ISCHR, too. 145 */ 146 if (fstat(fd, &st) >= 0 && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) 147 part = st.st_rdev & 7; 148 149 if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { 150 pp = &lab.d_partitions[part]; 151 if (pp->p_size) 152 return pp->p_size << 9; 153 } 154#endif /* HAVE_SYS_DISKLABEL_H */ 155 { 156#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) 157 struct stat64 st; 158 if (fstat64(fd, &st) == 0) 159#else 160 struct stat st; 161 if (fstat(fd, &st) == 0) 162#endif 163 if (S_ISREG(st.st_mode)) 164 return st.st_size; 165 } 166 167 /* 168 * OK, we couldn't figure it out by using a specialized ioctl, 169 * which is generally the best way. So do binary search to 170 * find the size of the partition. 171 */ 172 low = 0; 173 for (high = 1024; valid_offset(fd, high); high *= 2) 174 low = high; 175 while (low < high - 1) { 176 const blkid_loff_t mid = (low + high) / 2; 177 178 if (valid_offset(fd, mid)) 179 low = mid; 180 else 181 high = mid; 182 } 183 return low + 1; 184} 185 186#ifdef TEST_PROGRAM 187int main(int argc, char **argv) 188{ 189 long long bytes; 190 int fd; 191 192 if (argc < 2) { 193 fprintf(stderr, "Usage: %s device\n" 194 "Determine the size of a device\n", argv[0]); 195 return 1; 196 } 197 198 if ((fd = open(argv[1], O_RDONLY)) < 0) 199 perror(argv[0]); 200 201 bytes = blkid_get_dev_size(fd); 202 printf("Device %s has %Ld 1k blocks.\n", argv[1], 203 (unsigned long long) bytes >> 10); 204 205 return 0; 206} 207#endif 208