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