getsize.c revision 289e0557c24c68290b6d9b73b09674447801fdac
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/ioctl.h> 32#include <sys/disklabel.h> 33#endif /* HAVE_SYS_DISKLABEL_H */ 34#ifdef HAVE_SYS_DISK_H 35#include <sys/queue.h> /* for LIST_HEAD */ 36#include <sys/disk.h> 37#endif /* HAVE_SYS_DISK_H */ 38#ifdef __linux__ 39#include <sys/utsname.h> 40#endif 41 42#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) 43#define BLKGETSIZE _IO(0x12,96) /* return device size */ 44#endif 45 46#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) 47#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ 48#endif 49 50#ifdef APPLE_DARWIN 51#include <sys/ioctl.h> 52#include <sys/disk.h> 53 54#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 55#endif /* APPLE_DARWIN */ 56 57#include "ext2_fs.h" 58#include "ext2fs.h" 59 60#if defined(__CYGWIN__) || defined (WIN32) 61#include "windows.h" 62#include "winioctl.h" 63 64errcode_t ext2fs_get_device_size(const char *file, int blocksize, 65 blk_t *retblocks) 66{ 67 HANDLE dev; 68 PARTITION_INFORMATION pi; 69 DISK_GEOMETRY gi; 70 DWORD retbytes; 71 LARGE_INTEGER filesize; 72 73 dev = CreateFile(file, GENERIC_READ, 74 FILE_SHARE_READ | FILE_SHARE_WRITE , 75 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 76 77 if (dev == INVALID_HANDLE_VALUE) 78 return EBADF; 79 if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, 80 &pi, sizeof(PARTITION_INFORMATION), 81 &pi, sizeof(PARTITION_INFORMATION), 82 &retbytes, NULL)) { 83 84 *retblocks = pi.PartitionLength.QuadPart / blocksize; 85 86 } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, 87 &gi, sizeof(DISK_GEOMETRY), 88 &gi, sizeof(DISK_GEOMETRY), 89 &retbytes, NULL)) { 90 91 *retblocks = gi.BytesPerSector * 92 gi.SectorsPerTrack * 93 gi.TracksPerCylinder * 94 gi.Cylinders.QuadPart / blocksize; 95 96 } else if (GetFileSizeEx(dev, &filesize)) { 97 *retblocks = filesize.QuadPart / blocksize; 98 } 99 100 CloseHandle(dev); 101 return 0; 102} 103 104#else 105 106static int valid_offset (int fd, ext2_loff_t offset) 107{ 108 char ch; 109 110 if (ext2fs_llseek (fd, offset, 0) < 0) 111 return 0; 112 if (read (fd, &ch, 1) < 1) 113 return 0; 114 return 1; 115} 116 117/* 118 * Returns the number of blocks in a partition 119 */ 120errcode_t ext2fs_get_device_size(const char *file, int blocksize, 121 blk_t *retblocks) 122{ 123 int fd; 124 int valid_blkgetsize64 = 1; 125#ifdef __linux__ 126 struct utsname ut; 127#endif 128 unsigned long long size64; 129 unsigned long size; 130 ext2_loff_t high, low; 131#ifdef FDGETPRM 132 struct floppy_struct this_floppy; 133#endif 134#ifdef HAVE_SYS_DISKLABEL_H 135 int part; 136 struct disklabel lab; 137 struct partition *pp; 138 char ch; 139#endif /* HAVE_SYS_DISKLABEL_H */ 140 141#ifdef HAVE_OPEN64 142 fd = open64(file, O_RDONLY); 143#else 144 fd = open(file, O_RDONLY); 145#endif 146 if (fd < 0) 147 return errno; 148 149#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ 150 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { 151 if ((sizeof(*retblocks) < sizeof(unsigned long long)) 152 && ((size64 / (blocksize / 512)) > 0xFFFFFFFF)) 153 return EFBIG; 154 close(fd); 155 *retblocks = size64 / (blocksize / 512); 156 return 0; 157 } 158#endif 159 160#ifdef BLKGETSIZE64 161#ifdef __linux__ 162 if ((uname(&ut) == 0) && 163 ((ut.release[0] == '2') && (ut.release[1] == '.') && 164 (ut.release[2] < '6') && (ut.release[3] == '.'))) 165 valid_blkgetsize64 = 0; 166#endif 167 if (valid_blkgetsize64 && 168 ioctl(fd, BLKGETSIZE64, &size64) >= 0) { 169 if ((sizeof(*retblocks) < sizeof(unsigned long long)) 170 && ((size64 / blocksize) > 0xFFFFFFFF)) 171 return EFBIG; 172 close(fd); 173 *retblocks = size64 / blocksize; 174 return 0; 175 } 176#endif 177 178#ifdef BLKGETSIZE 179 if (ioctl(fd, BLKGETSIZE, &size) >= 0) { 180 close(fd); 181 *retblocks = size / (blocksize / 512); 182 return 0; 183 } 184#endif 185 186#ifdef FDGETPRM 187 if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { 188 close(fd); 189 *retblocks = this_floppy.size / (blocksize / 512); 190 return 0; 191 } 192#endif 193 194#ifdef HAVE_SYS_DISKLABEL_H 195#if defined(DIOCGMEDIASIZE) 196 { 197 off_t ms; 198 u_int bs; 199 if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { 200 *retblocks = ms / blocksize; 201 return 0; 202 } 203 } 204#elif defined(DIOCGDINFO) 205 /* old disklabel interface */ 206 part = strlen(file) - 1; 207 if (part >= 0) { 208 ch = file[part]; 209 if (isdigit(ch)) 210 part = 0; 211 else if (ch >= 'a' && ch <= 'h') 212 part = ch - 'a'; 213 else 214 part = -1; 215 } 216 if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { 217 pp = &lab.d_partitions[part]; 218 if (pp->p_size) { 219 close(fd); 220 *retblocks = pp->p_size / (blocksize / 512); 221 return 0; 222 } 223 } 224#endif /* defined(DIOCG*) */ 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