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