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