getsize.c revision 343fa65e9fc67e55717d622ee1db6553dc2285b4
1/*
2 * getsize.c --- get the size of a partition.
3 *
4 * Copyright (C) 1995, 1995 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#define _LARGEFILE_SOURCE
13#define _LARGEFILE64_SOURCE
14
15#include <stdio.h>
16#if HAVE_UNISTD_H
17#include <unistd.h>
18#endif
19#if HAVE_ERRNO_H
20#include <errno.h>
21#endif
22#include <fcntl.h>
23#ifdef HAVE_LINUX_FD_H
24#include <sys/ioctl.h>
25#include <linux/fd.h>
26#endif
27#ifdef HAVE_SYS_DISKLABEL_H
28#include <sys/ioctl.h>
29#include <sys/disklabel.h>
30#endif /* HAVE_SYS_DISKLABEL_H */
31
32#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
33#define BLKGETSIZE _IO(0x12,96)	/* return device size */
34#endif
35#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE64)
36#define BLKGETSIZE64 _IO(0x12,109)	/* return device size */
37#endif
38
39#include "ext2_fs.h"
40#include "ext2fs.h"
41
42static int valid_offset (int fd, ext2_loff_t offset)
43{
44	char ch;
45
46	if (ext2fs_llseek (fd, offset, 0) < 0)
47		return 0;
48	if (read (fd, &ch, 1) < 1)
49		return 0;
50	return 1;
51}
52
53/*
54 * Returns the number of blocks in a partition
55 */
56errcode_t ext2fs_get_device_size(const char *file, int blocksize,
57				 blk_t *retblocks)
58{
59	int	fd;
60#ifdef BLKGETSIZE64
61	unsigned long long	size64;
62#endif
63#ifdef BLKGETSIZE
64	unsigned long	size;
65#endif
66	ext2_loff_t high, low;
67#ifdef FDGETPRM
68	struct floppy_struct this_floppy;
69#endif
70#ifdef HAVE_SYS_DISKLABEL_H
71	int part;
72	struct disklabel lab;
73	struct partition *pp;
74	char ch;
75#endif /* HAVE_SYS_DISKLABEL_H */
76
77#ifdef HAVE_OPEN64
78	fd = open64(file, O_RDONLY);
79#else
80	fd = open(file, O_RDONLY);
81#endif
82	if (fd < 0)
83		return errno;
84
85#ifdef BLKGETSIZE64
86	if (ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
87		close(fd);
88		size64 = size64 / (blocksize / 512);
89		*retblocks = size64;
90		if (*retblocks != size64) {
91			return EFBIG;
92		}
93		return 0;
94	}
95#endif
96#ifdef BLKGETSIZE
97	if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
98		close(fd);
99		*retblocks = size / (blocksize / 512);
100		return 0;
101	}
102#endif
103#ifdef FDGETPRM
104	if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
105		close(fd);
106		*retblocks = this_floppy.size / (blocksize / 512);
107		return 0;
108	}
109#endif
110#ifdef HAVE_SYS_DISKLABEL_H
111	part = strlen(file) - 1;
112	if (part >= 0) {
113		ch = file[part];
114		if (isdigit(ch))
115			part = 0;
116		else if (ch >= 'a' && ch <= 'h')
117			part = ch - 'a';
118		else
119			part = -1;
120	}
121	if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
122		pp = &lab.d_partitions[part];
123		if (pp->p_size) {
124			close(fd);
125			*retblocks = pp->p_size / (blocksize / 512);
126			return 0;
127		}
128	}
129#endif /* HAVE_SYS_DISKLABEL_H */
130
131	/*
132	 * OK, we couldn't figure it out by using a specialized ioctl,
133	 * which is generally the best way.  So do binary search to
134	 * find the size of the partition.
135	 */
136	low = 0;
137	for (high = 1024; valid_offset (fd, high); high *= 2)
138		low = high;
139	while (low < high - 1)
140	{
141		const ext2_loff_t mid = (low + high) / 2;
142
143		if (valid_offset (fd, mid))
144			low = mid;
145		else
146			high = mid;
147	}
148	valid_offset (fd, 0);
149	close(fd);
150	*retblocks = (low + 1) / blocksize;
151	return 0;
152}
153
154#ifdef DEBUG
155int main(int argc, char **argv)
156{
157	blk_t	blocks;
158	int	retval;
159
160	if (argc < 2) {
161		fprintf(stderr, "Usage: %s device\n", argv[0]);
162		exit(1);
163	}
164
165	retval = ext2fs_get_device_size(argv[1], 1024, &blocks);
166	if (retval) {
167		com_err(argv[0], retval,
168			"while calling ext2fs_get_device_size");
169		exit(1);
170	}
171	printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
172	exit(0);
173}
174#endif
175