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