getsize.c revision 2c5cfbcb99f70d90f8e679bd2994b263ca6d11d6
1/* 2 * getsize.c --- get the size of a partition. 3 * 4 * Copyright (C) 1995, 1995 Theodore Ts'o. 5 * Copyright (C) 2003 VMware, Inc. 6 * 7 * Windows version of ext2fs_get_device_size by Chris Li, VMware. 8 * 9 * %Begin-Header% 10 * This file may be redistributed under the terms of the GNU Public 11 * License. 12 * %End-Header% 13 */ 14 15#define _LARGEFILE_SOURCE 16#define _LARGEFILE64_SOURCE 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_LINUX_FD_H 27#include <sys/ioctl.h> 28#include <linux/fd.h> 29#endif 30#ifdef HAVE_SYS_DISKLABEL_H 31#include <sys/param.h> /* for __FreeBSD_version */ 32#include <sys/ioctl.h> 33#include <sys/disklabel.h> 34#endif /* HAVE_SYS_DISKLABEL_H */ 35#ifdef HAVE_SYS_DISK_H 36#include <sys/queue.h> /* for LIST_HEAD */ 37#include <sys/disk.h> 38#endif /* HAVE_SYS_DISK_H */ 39#ifdef __linux__ 40#include <sys/utsname.h> 41#endif 42 43#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) 44#define BLKGETSIZE _IO(0x12,96) /* return device size */ 45#endif 46 47#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) 48#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ 49#endif 50 51#ifdef APPLE_DARWIN 52#include <sys/ioctl.h> 53#include <sys/disk.h> 54 55#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 56#endif /* APPLE_DARWIN */ 57 58#include "ext2_fs.h" 59#include "ext2fs.h" 60 61#if defined(__CYGWIN__) || defined (WIN32) 62#include "windows.h" 63#include "winioctl.h" 64 65errcode_t ext2fs_get_device_size(const char *file, int blocksize, 66 blk_t *retblocks) 67{ 68 HANDLE dev; 69 PARTITION_INFORMATION pi; 70 DISK_GEOMETRY gi; 71 DWORD retbytes; 72 LARGE_INTEGER filesize; 73 74 dev = CreateFile(file, GENERIC_READ, 75 FILE_SHARE_READ | FILE_SHARE_WRITE , 76 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 77 78 if (dev == INVALID_HANDLE_VALUE) 79 return EBADF; 80 if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, 81 &pi, sizeof(PARTITION_INFORMATION), 82 &pi, sizeof(PARTITION_INFORMATION), 83 &retbytes, NULL)) { 84 85 *retblocks = pi.PartitionLength.QuadPart / blocksize; 86 87 } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, 88 &gi, sizeof(DISK_GEOMETRY), 89 &gi, sizeof(DISK_GEOMETRY), 90 &retbytes, NULL)) { 91 92 *retblocks = gi.BytesPerSector * 93 gi.SectorsPerTrack * 94 gi.TracksPerCylinder * 95 gi.Cylinders.QuadPart / blocksize; 96 97 } else if (GetFileSizeEx(dev, &filesize)) { 98 *retblocks = filesize.QuadPart / blocksize; 99 } 100 101 CloseHandle(dev); 102 return 0; 103} 104 105#else 106 107static int valid_offset (int fd, ext2_loff_t offset) 108{ 109 char ch; 110 111 if (ext2fs_llseek (fd, offset, 0) < 0) 112 return 0; 113 if (read (fd, &ch, 1) < 1) 114 return 0; 115 return 1; 116} 117 118/* 119 * Returns the number of blocks in a partition 120 */ 121errcode_t ext2fs_get_device_size(const char *file, int blocksize, 122 blk_t *retblocks) 123{ 124 int fd; 125 int valid_blkgetsize64 = 1; 126#ifdef __linux__ 127 struct utsname ut; 128#endif 129 unsigned long long size64; 130 unsigned long size; 131 ext2_loff_t high, low; 132#ifdef FDGETPRM 133 struct floppy_struct this_floppy; 134#endif 135#ifdef HAVE_SYS_DISKLABEL_H 136 int part; 137 struct disklabel lab; 138 struct partition *pp; 139 char ch; 140#endif /* HAVE_SYS_DISKLABEL_H */ 141 142#ifdef HAVE_OPEN64 143 fd = open64(file, O_RDONLY); 144#else 145 fd = open(file, O_RDONLY); 146#endif 147 if (fd < 0) 148 return errno; 149 150#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ 151 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { 152 if ((sizeof(*retblocks) < sizeof(unsigned long long)) 153 && ((size64 / (blocksize / 512)) > 0xFFFFFFFF)) 154 return EFBIG; 155 close(fd); 156 *retblocks = size64 / (blocksize / 512); 157 return 0; 158 } 159#endif 160 161#ifdef BLKGETSIZE64 162#ifdef __linux__ 163 if ((uname(&ut) == 0) && 164 ((ut.release[0] == '2') && (ut.release[1] == '.') && 165 (ut.release[2] < '6') && (ut.release[3] == '.'))) 166 valid_blkgetsize64 = 0; 167#endif 168 if (valid_blkgetsize64 && 169 ioctl(fd, BLKGETSIZE64, &size64) >= 0) { 170 if ((sizeof(*retblocks) < sizeof(unsigned long long)) 171 && ((size64 / blocksize) > 0xFFFFFFFF)) 172 return EFBIG; 173 close(fd); 174 *retblocks = size64 / blocksize; 175 return 0; 176 } 177#endif 178 179#ifdef BLKGETSIZE 180 if (ioctl(fd, BLKGETSIZE, &size) >= 0) { 181 close(fd); 182 *retblocks = size / (blocksize / 512); 183 return 0; 184 } 185#endif 186 187#ifdef FDGETPRM 188 if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { 189 close(fd); 190 *retblocks = this_floppy.size / (blocksize / 512); 191 return 0; 192 } 193#endif 194#ifdef HAVE_SYS_DISKLABEL_H 195#if (defined(__FreeBSD__) && __FreeBSD_version < 500040) || defined(APPLE_DARWIN) 196 /* old disklabel interface */ 197 part = strlen(file) - 1; 198 if (part >= 0) { 199 ch = file[part]; 200 if (isdigit(ch)) 201 part = 0; 202 else if (ch >= 'a' && ch <= 'h') 203 part = ch - 'a'; 204 else 205 part = -1; 206 } 207 if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { 208 pp = &lab.d_partitions[part]; 209 if (pp->p_size) { 210 close(fd); 211 *retblocks = pp->p_size / (blocksize / 512); 212 return 0; 213 } 214 } 215#else /* __FreeBSD_version < 500040 */ 216 { 217 off_t ms; 218 u_int bs; 219 if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { 220 *retblocks = ms / blocksize; 221 return 0; 222 } 223 } 224#endif /* __FreeBSD_version < 500040 */ 225#endif /* HAVE_SYS_DISKLABEL_H */ 226 227 /* 228 * OK, we couldn't figure it out by using a specialized ioctl, 229 * which is generally the best way. So do binary search to 230 * find the size of the partition. 231 */ 232 low = 0; 233 for (high = 1024; valid_offset (fd, high); high *= 2) 234 low = high; 235 while (low < high - 1) 236 { 237 const ext2_loff_t mid = (low + high) / 2; 238 239 if (valid_offset (fd, mid)) 240 low = mid; 241 else 242 high = mid; 243 } 244 valid_offset (fd, 0); 245 close(fd); 246 *retblocks = (low + 1) / blocksize; 247 return 0; 248} 249 250#endif /* WIN32 */ 251 252#ifdef DEBUG 253int main(int argc, char **argv) 254{ 255 blk_t blocks; 256 int retval; 257 258 if (argc < 2) { 259 fprintf(stderr, "Usage: %s device\n", argv[0]); 260 exit(1); 261 } 262 263 retval = ext2fs_get_device_size(argv[1], 1024, &blocks); 264 if (retval) { 265 com_err(argv[0], retval, 266 "while calling ext2fs_get_device_size"); 267 exit(1); 268 } 269 printf("Device %s has %d 1k blocks.\n", argv[1], blocks); 270 exit(0); 271} 272#endif 273