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