1 /**	$MirOS: src/sbin/fdisk/disk.c,v 1.3 2006/09/20 20:03:30 tg Exp $ */
2 /*	$OpenBSD: disk.c,v 1.24 2005/12/18 03:42:23 krw Exp $	*/
3 
4 /*
5  * Copyright (c) 1997, 2001 Tobias Weingartner
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <err.h>
30 #include <util.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <sys/param.h>
35 #include <sys/fcntl.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <sys/reboot.h>
39 #include <sys/disklabel.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
DISK_open(char * disk,int mode)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
DISK_close(int fd)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 *
DISK_getlabelmetrics(char * name)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, NULL);
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 			unit_types[SECTORS].conversion = dl.d_secsize;
102 		}
103 		DISK_close(fd);
104 	}
105 
106 	return (lm);
107 }
108 
109 #ifdef CPU_BIOS
110 /*
111  * Routine to go after sysctl info for BIOS
112  * geometry.  This should only really work on PC
113  * type machines.  There is still a problem with
114  * correlating the BIOS drive to the BSD drive.
115  */
116 DISK_metrics *
DISK_getbiosmetrics(char * name)117 DISK_getbiosmetrics(char *name)
118 {
119 	bios_diskinfo_t di;
120 	DISK_metrics *bm;
121 	struct stat st;
122 	int mib[4], fd;
123 	size_t size;
124 	dev_t devno;
125 
126 	if ((fd = DISK_open(name, O_RDONLY)) == -1)
127 		return (NULL);
128 	fstat(fd, &st);
129 	DISK_close(fd);
130 
131 	/* Get BIOS metrics */
132 	mib[0] = CTL_MACHDEP;
133 	mib[1] = CPU_CHR2BLK;
134 	mib[2] = st.st_rdev;
135 	size = sizeof(devno);
136 	if (sysctl(mib, 3, &devno, &size, NULL, 0) == -1) {
137 		warn("sysctl(machdep.chr2blk)");
138 		return (NULL);
139 	}
140 	devno = MAKEBOOTDEV(major(devno), 0, 0, DISKUNIT(devno), RAW_PART);
141 
142 	mib[0] = CTL_MACHDEP;
143 	mib[1] = CPU_BIOS;
144 	mib[2] = BIOS_DISKINFO;
145 	mib[3] = devno;
146 	size = sizeof(di);
147 	if (sysctl(mib, 4, &di, &size, NULL, 0) == -1) {
148 		warn("sysctl(machdep.bios.diskinfo)");
149 		return (NULL);
150 	}
151 
152 	bm = malloc(sizeof(di));
153 	if (bm == NULL)
154 		err(1, NULL);
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 *
DISK_getbiosmetrics(char * name)167 DISK_getbiosmetrics(char *name)
168 {
169 	return (NULL);
170 }
171 #endif
172 
173 /* This is ugly, and convoluted.  All the magic
174  * for disk geo/size happens here.  Basically,
175  * the real size is the one we will use in the
176  * rest of the program, the label size is what we
177  * got from the disklabel.  If the disklabel fails,
178  * we assume we are working with a normal file,
179  * and should request the user to specify the
180  * geometry he/she wishes to use.
181  */
182 int
DISK_getmetrics(disk_t * disk,DISK_metrics * user)183 DISK_getmetrics(disk_t *disk, DISK_metrics *user)
184 {
185 
186 	disk->label = DISK_getlabelmetrics(disk->name);
187 	disk->bios = DISK_getbiosmetrics(disk->name);
188 
189 	/* If user supplied, use that */
190 	if (user) {
191 		disk->real = user;
192 		return (0);
193 	}
194 
195 	/* Fixup bios metrics to include cylinders past 1023 boundary */
196 	if(disk->label && disk->bios){
197 		int cyls, secs;
198 
199 		cyls = disk->label->size / (disk->bios->heads * disk->bios->sectors);
200 		secs = cyls * (disk->bios->heads * disk->bios->sectors);
201 		if (secs > disk->label->size)
202 			errx(1, "BIOS fixup botch (secs (%d) > size (%d))",
203 			    secs, disk->label->size);
204 		disk->bios->cylinders = cyls;
205 		disk->bios->size = secs;
206 	}
207 
208 	/* If we have a (fixed) BIOS geometry, use that */
209 	if (disk->bios) {
210 		disk->real = disk->bios;
211 		return (0);
212 	}
213 
214 	/* If we have a label, use that */
215 	if (disk->label) {
216 		disk->real = disk->label;
217 		return (0);
218 	}
219 
220 	/* Can not get geometry, punt */
221 	disk->real = NULL;
222 	return (1);
223 }
224 
225 /*
226  * Print the disk geometry information. Take an optional modifier
227  * to indicate the units that should be used for display.
228  */
229 int
DISK_printmetrics(disk_t * disk,char * units)230 DISK_printmetrics(disk_t *disk, char *units)
231 {
232 	int i;
233 	double size;
234 	i = unit_lookup(units);
235 	size = ((double)disk->real->size * unit_types[SECTORS].conversion) /
236 	    unit_types[i].conversion;
237 	printf("Disk: %s\t", disk->name);
238 	if (disk->real)
239 		printf("geometry: %d/%d/%d [%.0f %s]\n", disk->real->cylinders,
240 		    disk->real->heads, disk->real->sectors, size,
241 		    unit_types[i].lname);
242 	else
243 		printf("geometry: <none>\n");
244 
245 	return (0);
246 }
247