xref: /openbsd-src/sbin/fdisk/disk.c (revision 47911bd667ac77dc523b8a13ef40b012dbffa741)
1 /*	$OpenBSD: disk.c,v 1.16 2002/07/11 21:23:28 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1997, 2001 Tobias Weingartner
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *    This product includes software developed by Tobias Weingartner.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <err.h>
34 #include <util.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <sys/fcntl.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/reboot.h>
43 #include <sys/disklabel.h>
44 #include <sys/param.h>
45 #include <sys/sysctl.h>
46 #include <machine/cpu.h>
47 #ifdef CPU_BIOS
48 #include <machine/biosvar.h>
49 #endif
50 #include "disk.h"
51 #include "misc.h"
52 
53 int
54 DISK_open(disk, mode)
55 	char *disk;
56 	int mode;
57 {
58 	int fd;
59 	struct stat st;
60 
61 	fd = opendev(disk, mode, OPENDEV_PART, NULL);
62 	if (fd == -1)
63 		err(1, "%s", disk);
64 	if (fstat(fd, &st) == -1)
65 		err(1, "%s", disk);
66 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode))
67 		err(1, "%s is not a character device or a regular file", disk);
68 	return (fd);
69 }
70 
71 int
72 DISK_close(fd)
73 	int fd;
74 {
75 
76 	return (close(fd));
77 }
78 
79 /* Routine to go after the disklabel for geometry
80  * information.  This should work everywhere, but
81  * in the land of PC, things are not always what
82  * they seem.
83  */
84 DISK_metrics *
85 DISK_getlabelmetrics(name)
86 	char *name;
87 {
88 	DISK_metrics *lm = NULL;
89 	struct disklabel dl;
90 	int fd;
91 
92 	/* Get label metrics */
93 	if ((fd = DISK_open(name, O_RDONLY)) != -1) {
94 		lm = malloc(sizeof(DISK_metrics));
95 		if (lm == NULL)
96 			err(1, "malloc");
97 
98 		if (ioctl(fd, DIOCGDINFO, &dl) == -1) {
99 			warn("DIOCGDINFO");
100 			free(lm);
101 			lm = NULL;
102 		} else {
103 			lm->cylinders = dl.d_ncylinders;
104 			lm->heads = dl.d_ntracks;
105 			lm->sectors = dl.d_nsectors;
106 			lm->size = dl.d_secperunit;
107 		}
108 		DISK_close(fd);
109 	}
110 
111 	return (lm);
112 }
113 
114 #ifdef CPU_BIOS
115 /*
116  * Routine to go after sysctl info for BIOS
117  * geometry.  This should only really work on PC
118  * type machines.  There is still a problem with
119  * correlating the BIOS drive to the BSD drive.
120  */
121 DISK_metrics *
122 DISK_getbiosmetrics(name)
123 	char *name;
124 {
125 	bios_diskinfo_t di;
126 	DISK_metrics *bm;
127 	struct stat st;
128 	int mib[4], fd;
129 	size_t size;
130 	dev_t devno;
131 
132 	if ((fd = DISK_open(name, O_RDONLY)) == -1)
133 		return (NULL);
134 	fstat(fd, &st);
135 	DISK_close(fd);
136 
137 	/* Get BIOS metrics */
138 	mib[0] = CTL_MACHDEP;
139 	mib[1] = CPU_CHR2BLK;
140 	mib[2] = st.st_rdev;
141 	size = sizeof(devno);
142 	if (sysctl(mib, 3, &devno, &size, NULL, 0) == -1) {
143 		warn("sysctl(machdep.chr2blk)");
144 		return (NULL);
145 	}
146 	devno = MAKEBOOTDEV(major(devno), 0, 0, DISKUNIT(devno), RAW_PART);
147 
148 	mib[0] = CTL_MACHDEP;
149 	mib[1] = CPU_BIOS;
150 	mib[2] = BIOS_DISKINFO;
151 	mib[3] = devno;
152 	size = sizeof(di);
153 	if (sysctl(mib, 4, &di, &size, NULL, 0) == -1) {
154 		warn("sysctl(machdep.bios.diskinfo)");
155 		return (NULL);
156 	}
157 
158 	bm = malloc(sizeof(di));
159 	if (bm == NULL)
160 		err(1, "malloc");
161 	bm->cylinders = di.bios_cylinders;
162 	bm->heads = di.bios_heads;
163 	bm->sectors = di.bios_sectors;
164 	bm->size = di.bios_cylinders * di.bios_heads * di.bios_sectors;
165 	return (bm);
166 }
167 #else
168 /*
169  * We are not a PC, so we do not have BIOS metrics to contend
170  * with.  Return NULL to indicate so.
171  */
172 DISK_metrics *
173 DISK_getbiosmetrics(name)
174 	char *name;
175 {
176 	return (NULL);
177 }
178 #endif
179 
180 /* This is ugly, and convoluted.  All the magic
181  * for disk geo/size happens here.  Basically,
182  * the real size is the one we will use in the
183  * rest of the program, the label size is what we
184  * got from the disklabel.  If the disklabel fails,
185  * we assume we are working with a normal file,
186  * and should request the user to specify the
187  * geometry he/she wishes to use.
188  */
189 int
190 DISK_getmetrics(disk, user)
191 	disk_t *disk;
192 	DISK_metrics *user;
193 {
194 
195 	disk->label = DISK_getlabelmetrics(disk->name);
196 	disk->bios = DISK_getbiosmetrics(disk->name);
197 
198 	/* If user supplied, use that */
199 	if (user) {
200 		disk->real = user;
201 		return (0);
202 	}
203 
204 	/* Fixup bios metrics to include cylinders past 1023 boundary */
205 	if(disk->label && disk->bios){
206 		int cyls, secs;
207 
208 		cyls = disk->label->size / (disk->bios->heads * disk->bios->sectors);
209 		secs = cyls * (disk->bios->heads * disk->bios->sectors);
210 		if ((disk->label->size - secs) < 0)
211 			errx(1, "BIOS fixup botch (%d sectors)", disk->label->size - secs);
212 		disk->bios->cylinders = cyls;
213 		disk->bios->size = secs;
214 	}
215 
216 	/* If we have a (fixed) BIOS geometry, use that */
217 	if (disk->bios) {
218 		disk->real = disk->bios;
219 		return (0);
220 	}
221 
222 	/* If we have a label, use that */
223 	if (disk->label) {
224 		disk->real = disk->label;
225 		return (0);
226 	}
227 
228 	/* Can not get geometry, punt */
229 	disk->real = NULL;
230 	return (1);
231 }
232 
233 /*
234  * Print the disk geometry information. Take an optional modifier
235  * to indicate the units that should be used for display.
236  */
237 int
238 DISK_printmetrics(disk, units)
239 	disk_t *disk;
240 	char *units;
241 {
242 	int i;
243 	double size;
244 	i = unit_lookup(units);
245 	size = (double)disk->real->size * DEV_BSIZE / unit_types[i].conversion;
246 	printf("Disk: %s\t", disk->name);
247 	if (disk->real)
248 		printf("geometry: %d/%d/%d [%.0f %s]\n", disk->real->cylinders,
249 		    disk->real->heads, disk->real->sectors, size,
250 		       unit_types[i].lname);
251 	else
252 		printf("geometry: <none>\n");
253 
254 	return (0);
255 }
256 
257