getsize.c revision b34cbddbd66baedc163d36dd281b63ef478f547a
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 40#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) 41#define BLKGETSIZE _IO(0x12,96) /* return device size */ 42#endif 43 44#ifdef APPLE_DARWIN 45#include <sys/ioctl.h> 46#include <sys/disk.h> 47 48#define BLKGETSIZE DKIOCGETBLOCKCOUNT32 49#endif /* APPLE_DARWIN */ 50 51#include "ext2_fs.h" 52#include "ext2fs.h" 53 54#if defined(__CYGWIN__) || defined (WIN32) 55#include "windows.h" 56#include "winioctl.h" 57 58errcode_t ext2fs_get_device_size(const char *file, int blocksize, 59 blk_t *retblocks) 60{ 61 HANDLE dev; 62 PARTITION_INFORMATION pi; 63 DISK_GEOMETRY gi; 64 DWORD retbytes; 65 LARGE_INTEGER filesize; 66 67 dev = CreateFile(file, GENERIC_READ, 68 FILE_SHARE_READ | FILE_SHARE_WRITE , 69 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 70 71 if (dev == INVALID_HANDLE_VALUE) 72 return EBADF; 73 if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, 74 &pi, sizeof(PARTITION_INFORMATION), 75 &pi, sizeof(PARTITION_INFORMATION), 76 &retbytes, NULL)) { 77 78 *retblocks = pi.PartitionLength.QuadPart / blocksize; 79 80 } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, 81 &gi, sizeof(DISK_GEOMETRY), 82 &gi, sizeof(DISK_GEOMETRY), 83 &retbytes, NULL)) { 84 85 *retblocks = gi.BytesPerSector * 86 gi.SectorsPerTrack * 87 gi.TracksPerCylinder * 88 gi.Cylinders.QuadPart / blocksize; 89 90 } else if (GetFileSizeEx(dev, &filesize)) { 91 *retblocks = filesize.QuadPart / blocksize; 92 } 93 94 CloseHandle(dev); 95 return 0; 96} 97 98#else 99 100static int valid_offset (int fd, ext2_loff_t offset) 101{ 102 char ch; 103 104 if (ext2fs_llseek (fd, offset, 0) < 0) 105 return 0; 106 if (read (fd, &ch, 1) < 1) 107 return 0; 108 return 1; 109} 110 111/* 112 * Returns the number of blocks in a partition 113 */ 114errcode_t ext2fs_get_device_size(const char *file, int blocksize, 115 blk_t *retblocks) 116{ 117 int fd; 118#ifdef BLKGETSIZE 119 unsigned long size; 120#endif 121 ext2_loff_t high, low; 122#ifdef FDGETPRM 123 struct floppy_struct this_floppy; 124#endif 125#ifdef HAVE_SYS_DISKLABEL_H 126 int part; 127 struct disklabel lab; 128 struct partition *pp; 129 char ch; 130#endif /* HAVE_SYS_DISKLABEL_H */ 131 132#ifdef HAVE_OPEN64 133 fd = open64(file, O_RDONLY); 134#else 135 fd = open(file, O_RDONLY); 136#endif 137 if (fd < 0) 138 return errno; 139 140#ifdef BLKGETSIZE 141 if (ioctl(fd, BLKGETSIZE, &size) >= 0) { 142 close(fd); 143 *retblocks = size / (blocksize / 512); 144 return 0; 145 } 146#endif 147#ifdef FDGETPRM 148 if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { 149 close(fd); 150 *retblocks = this_floppy.size / (blocksize / 512); 151 return 0; 152 } 153#endif 154#ifdef HAVE_SYS_DISKLABEL_H 155#if defined(__FreeBSD__) && __FreeBSD_version < 500040 156 /* old disklabel interface */ 157 part = strlen(file) - 1; 158 if (part >= 0) { 159 ch = file[part]; 160 if (isdigit(ch)) 161 part = 0; 162 else if (ch >= 'a' && ch <= 'h') 163 part = ch - 'a'; 164 else 165 part = -1; 166 } 167 if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { 168 pp = &lab.d_partitions[part]; 169 if (pp->p_size) { 170 close(fd); 171 *retblocks = pp->p_size / (blocksize / 512); 172 return 0; 173 } 174 } 175#else /* __FreeBSD_version < 500040 */ 176 { 177 off_t ms; 178 u_int bs; 179 if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { 180 *retblocks = ms / blocksize; 181 return 0; 182 } 183 } 184#endif /* __FreeBSD_version < 500040 */ 185#endif /* HAVE_SYS_DISKLABEL_H */ 186 187 /* 188 * OK, we couldn't figure it out by using a specialized ioctl, 189 * which is generally the best way. So do binary search to 190 * find the size of the partition. 191 */ 192 low = 0; 193 for (high = 1024; valid_offset (fd, high); high *= 2) 194 low = high; 195 while (low < high - 1) 196 { 197 const ext2_loff_t mid = (low + high) / 2; 198 199 if (valid_offset (fd, mid)) 200 low = mid; 201 else 202 high = mid; 203 } 204 valid_offset (fd, 0); 205 close(fd); 206 *retblocks = (low + 1) / blocksize; 207 return 0; 208} 209 210#endif /* WIN32 */ 211 212#ifdef DEBUG 213int main(int argc, char **argv) 214{ 215 blk_t blocks; 216 int retval; 217 218 if (argc < 2) { 219 fprintf(stderr, "Usage: %s device\n", argv[0]); 220 exit(1); 221 } 222 223 retval = ext2fs_get_device_size(argv[1], 1024, &blocks); 224 if (retval) { 225 com_err(argv[0], retval, 226 "while calling ext2fs_get_device_size"); 227 exit(1); 228 } 229 printf("Device %s has %d 1k blocks.\n", argv[1], blocks); 230 exit(0); 231} 232#endif 233