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