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