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