getsize.c revision abf7d3880b84994783437e291bd4e7950c4b0fe7
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
36#if EXT2_FLAT_INCLUDES
37#include "ext2_fs.h"
38#else
39#include <linux/ext2_fs.h>
40#endif
41
42#include "ext2fs.h"
43
44static int valid_offset (int fd, ext2_loff_t offset)
45{
46	char ch;
47
48	if (ext2fs_llseek (fd, offset, 0) < 0)
49		return 0;
50	if (read (fd, &ch, 1) < 1)
51		return 0;
52	return 1;
53}
54
55/*
56 * Returns the number of blocks in a partition
57 */
58errcode_t ext2fs_get_device_size(const char *file, int blocksize,
59				 blk_t *retblocks)
60{
61	int	fd;
62#ifdef BLKGETSIZE
63	long	size;
64#endif
65	ext2_loff_t high, low;
66#ifdef FDGETPRM
67	struct floppy_struct this_floppy;
68#endif
69#ifdef HAVE_SYS_DISKLABEL_H
70	int part;
71	struct disklabel lab;
72	struct partition *pp;
73	char ch;
74#endif /* HAVE_SYS_DISKLABEL_H */
75
76#ifdef HAVE_OPEN64
77	fd = open64(file, O_RDONLY);
78#else
79	fd = open(file, O_RDONLY);
80#endif
81	if (fd < 0)
82		return errno;
83
84#ifdef BLKGETSIZE
85	if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
86		close(fd);
87		*retblocks = size / (blocksize / 512);
88		return 0;
89	}
90#endif
91#ifdef FDGETPRM
92	if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
93		close(fd);
94		*retblocks = this_floppy.size / (blocksize / 512);
95		return 0;
96	}
97#endif
98#ifdef HAVE_SYS_DISKLABEL_H
99	part = strlen(file) - 1;
100	if (part >= 0) {
101		ch = file[part];
102		if (isdigit(ch))
103			part = 0;
104		else if (ch >= 'a' && ch <= 'h')
105			part = ch - 'a';
106		else
107			part = -1;
108	}
109	if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
110		pp = &lab.d_partitions[part];
111		if (pp->p_size) {
112			close(fd);
113			*retblocks = pp->p_size / (blocksize / 512);
114			return 0;
115		}
116	}
117#endif /* HAVE_SYS_DISKLABEL_H */
118
119	/*
120	 * OK, we couldn't figure it out by using a specialized ioctl,
121	 * which is generally the best way.  So do binary search to
122	 * find the size of the partition.
123	 */
124	low = 0;
125	for (high = 1024; valid_offset (fd, high); high *= 2)
126		low = high;
127	while (low < high - 1)
128	{
129		const ext2_loff_t mid = (low + high) / 2;
130
131		if (valid_offset (fd, mid))
132			low = mid;
133		else
134			high = mid;
135	}
136	valid_offset (fd, 0);
137	close(fd);
138	*retblocks = (low + 1) / blocksize;
139	return 0;
140}
141
142#ifdef DEBUG
143int main(int argc, char **argv)
144{
145	blk_t	blocks;
146	int	retval;
147
148	if (argc < 2) {
149		fprintf(stderr, "Usage: %s device\n", argv[0]);
150		exit(1);
151	}
152
153	retval = ext2fs_get_device_size(argv[1], 1024, &blocks);
154	if (retval) {
155		com_err(argv[0], retval,
156			"while calling ext2fs_get_device_size");
157		exit(1);
158	}
159	printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
160	exit(0);
161}
162#endif
163