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