xref: /openbsd-src/sbin/fdisk/disk.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: disk.c,v 1.13 2001/06/23 01:54:37 kjell 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 
52 int
53 DISK_open(disk, mode)
54 	char *disk;
55 	int mode;
56 {
57 	int fd;
58 	struct stat st;
59 
60 	fd = opendev(disk, mode, OPENDEV_PART, NULL);
61 	if (fd == -1)
62 		err(1, "%s", disk);
63 	if (fstat(fd, &st) == -1)
64 		err(1, "%s", disk);
65 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode))
66 		err(1, "%s is not a character device or a regular file", disk);
67 	return (fd);
68 }
69 
70 int
71 DISK_close(fd)
72 	int fd;
73 {
74 
75 	return (close(fd));
76 }
77 
78 /* Routine to go after the disklabel for geometry
79  * information.  This should work everywhere, but
80  * in the land of PC, things are not always what
81  * they seem.
82  */
83 DISK_metrics *
84 DISK_getlabelmetrics(name)
85 	char *name;
86 {
87 	DISK_metrics *lm = NULL;
88 	struct disklabel dl;
89 	int fd;
90 
91 	/* Get label metrics */
92 	if ((fd = DISK_open(name, O_RDONLY)) != -1) {
93 		lm = malloc(sizeof(DISK_metrics));
94 
95 		if (ioctl(fd, DIOCGDINFO, &dl) == -1) {
96 			warn("DIOCGDINFO");
97 			free(lm);
98 			lm = NULL;
99 		} else {
100 			lm->cylinders = dl.d_ncylinders;
101 			lm->heads = dl.d_ntracks;
102 			lm->sectors = dl.d_nsectors;
103 			lm->size = dl.d_secperunit;
104 		}
105 		DISK_close(fd);
106 	}
107 
108 	return (lm);
109 }
110 
111 #ifdef CPU_BIOS
112 /*
113  * Routine to go after sysctl info for BIOS
114  * geometry.  This should only really work on PC
115  * type machines.  There is still a problem with
116  * correlating the BIOS drive to the BSD drive.
117  */
118 DISK_metrics *
119 DISK_getbiosmetrics(name)
120 	char *name;
121 {
122 	bios_diskinfo_t di;
123 	DISK_metrics *bm;
124 	struct stat st;
125 	int mib[4], size, fd;
126 	dev_t devno;
127 
128 	if ((fd = DISK_open(name, O_RDONLY)) == -1)
129 		return (NULL);
130 	fstat(fd, &st);
131 	DISK_close(fd);
132 
133 	/* Get BIOS metrics */
134 	mib[0] = CTL_MACHDEP;
135 	mib[1] = CPU_CHR2BLK;
136 	mib[2] = st.st_rdev;
137 	size = sizeof(devno);
138 	if (sysctl(mib, 3, &devno, &size, NULL, 0) == -1) {
139 		warn("sysctl(machdep.chr2blk)");
140 		return (NULL);
141 	}
142 	devno = MAKEBOOTDEV(major(devno), 0, 0, DISKUNIT(devno), RAW_PART);
143 
144 	mib[0] = CTL_MACHDEP;
145 	mib[1] = CPU_BIOS;
146 	mib[2] = BIOS_DISKINFO;
147 	mib[3] = devno;
148 	size = sizeof(di);
149 	if (sysctl(mib, 4, &di, &size, NULL, 0) == -1) {
150 		warn("sysctl(machdep.bios.diskinfo)");
151 		return (NULL);
152 	}
153 
154 	bm = malloc(sizeof(di));
155 	bm->cylinders = di.bios_cylinders;
156 	bm->heads = di.bios_heads;
157 	bm->sectors = di.bios_sectors;
158 	bm->size = di.bios_cylinders * di.bios_heads * di.bios_sectors;
159 	return (bm);
160 }
161 #else
162 /*
163  * We are not a PC, so we do not have BIOS metrics to contend
164  * with.  Return NULL to indicate so.
165  */
166 DISK_metrics *
167 DISK_getbiosmetrics(name)
168 	char *name;
169 {
170 	return (NULL);
171 }
172 #endif
173 
174 /* This is ugly, and convoluted.  All the magic
175  * for disk geo/size happens here.  Basically,
176  * the real size is the one we will use in the
177  * rest of the program, the label size is what we
178  * got from the disklabel.  If the disklabel fails,
179  * we assume we are working with a normal file,
180  * and should request the user to specify the
181  * geometry he/she wishes to use.
182  */
183 int
184 DISK_getmetrics(disk, user)
185 	disk_t *disk;
186 	DISK_metrics *user;
187 {
188 
189 	disk->label = DISK_getlabelmetrics(disk->name);
190 	disk->bios = DISK_getbiosmetrics(disk->name);
191 
192 	/* If user supplied, use that */
193 	if (user) {
194 		disk->real = user;
195 		return (0);
196 	}
197 
198 	/* Fixup bios metrics to include cylinders past 1023 boundary */
199 	if(disk->label && disk->bios){
200 		int cyls, secs;
201 
202 		cyls = disk->label->size / (disk->bios->heads * disk->bios->sectors);
203 		secs = cyls * (disk->bios->heads * disk->bios->sectors);
204 		if ((disk->label->size - secs) < 0)
205 			errx(1, "BIOS fixup botch (%d sectors)", disk->label->size - secs);
206 		disk->bios->cylinders = cyls;
207 		disk->bios->size = secs;
208 	}
209 
210 	/* If we have a (fixed) BIOS geometry, use that */
211 	if (disk->bios) {
212 		disk->real = disk->bios;
213 		return (0);
214 	}
215 
216 	/* If we have a label, use that */
217 	if (disk->label) {
218 		disk->real = disk->label;
219 		return (0);
220 	}
221 
222 	/* Can not get geometry, punt */
223 	disk->real = NULL;
224 	return (1);
225 }
226 
227 int
228 DISK_printmetrics(disk)
229 	disk_t *disk;
230 {
231 
232 	printf("Disk: %s\t", disk->name);
233 	if (disk->real)
234 		printf("geometry: %d/%d/%d [%d sectors]\n", disk->real->cylinders,
235 		    disk->real->heads, disk->real->sectors, disk->real->size);
236 	else
237 		printf("geometry: <none>\n");
238 
239 	return (0);
240 }
241 
242