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