]> git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/fdisk.tproj/disk.c
Merge branch 'apple'
[apple_cmds.git] / diskdev_cmds / fdisk.tproj / disk.c
1 /*
2 * Copyright (c) 2002, 2012 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Copyright (c) 1997, 2001 Tobias Weingartner
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by Tobias Weingartner.
39 * 4. The name of the author may not be used to endorse or promote products
40 * derived from this software without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
43 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
44 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
45 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
46 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
47 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
48 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
49 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
50 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
51 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 */
53
54 #include <err.h>
55 #include <util.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 #include <string.h>
60 #include <sys/fcntl.h>
61 #include <sys/ioctl.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <sys/reboot.h>
65 #include <sys/disk.h>
66 #include <sys/param.h>
67 #include <sys/sysctl.h>
68 #ifdef __i386__
69 #include <pexpert/i386/boot.h>
70 #endif
71 #include "disk.h"
72
73 int
74 DISK_open(disk, mode)
75 char *disk;
76 int mode;
77 {
78 int fd;
79 struct stat st;
80
81 fd = open(disk, mode);
82 if (fd == -1)
83 err(1, "%s", disk);
84 if (fstat(fd, &st) == -1)
85 err(1, "%s", disk);
86 /* Don't be so picky about needing a character device */
87 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode))
88 errx(1, "%s is not a character device or a regular file", disk);
89 return (fd);
90 }
91
92 int
93 DISK_openshared(disk, mode, shared)
94 char *disk;
95 int mode;
96 int *shared;
97 {
98 int fd;
99 struct stat st;
100 *shared = 0;
101
102 fd = open(disk, mode|O_EXLOCK);
103 if (fd == -1) {
104 // if we can't have exclusive access, attempt
105 // to gracefully degrade to shared access
106 fd = open(disk, mode|O_SHLOCK);
107 if(fd == -1)
108 err(1, "%s", disk);
109
110 *shared = 1;
111 }
112
113 if (fstat(fd, &st) == -1)
114 err(1, "%s", disk);
115 /* Don't be so picky about needing a character device */
116 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode))
117 errx(1, "%s is not a character device or a regular file", disk);
118 return (fd);
119 }
120
121 int
122 DISK_close(fd)
123 int fd;
124 {
125
126 return (close(fd));
127 }
128
129 /* Given a size in the metrics,
130 * fake up a CHS geometry.
131 */
132 void
133 DISK_fake_CHS(DISK_metrics *lm)
134 {
135 int heads = 4;
136 int spt = 63;
137 int cylinders = (lm->size / heads / spt);
138
139 while (cylinders > 1024 && heads < 256) {
140 heads *= 2;
141 cylinders /= 2;
142 }
143 if (heads == 256) {
144 heads = 255;
145 cylinders = (lm->size / heads / spt);
146 }
147 lm->cylinders = cylinders;
148 lm->heads = heads;
149 lm->sectors = spt;
150 }
151
152 /* Routine to go after the disklabel for geometry
153 * information. This should work everywhere, but
154 * in the land of PC, things are not always what
155 * they seem.
156 */
157 DISK_metrics *
158 DISK_getlabelmetrics(name)
159 char *name;
160 {
161 DISK_metrics *lm = NULL;
162 long long size;
163 uint32_t sector_size;
164 int fd;
165 struct stat st;
166
167 /* Get label metrics */
168 if ((fd = DISK_open(name, O_RDONLY)) != -1) {
169 lm = malloc(sizeof(DISK_metrics));
170
171 if (fstat(fd, &st) == -1)
172 err(1, "%s", name);
173 if (!S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) {
174 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size) == -1) {
175 err(1, "Could not get disk block count");
176 free(lm);
177 return NULL;
178 }
179 if (ioctl(fd, DKIOCGETBLOCKSIZE, &sector_size) == -1) {
180 err(1, "Could not get disk block size");
181 free(lm);
182 return NULL;
183 }
184 } else {
185 sector_size = 512;
186 size = st.st_size / sector_size;
187 }
188
189 lm->sector_size = sector_size;
190 lm->size = size;
191 DISK_fake_CHS(lm);
192 DISK_close(fd);
193 }
194
195 return (lm);
196 }
197
198 /*
199 * Don't try to get BIOS disk metrics.
200 */
201 DISK_metrics *
202 DISK_getbiosmetrics(name)
203 char *name;
204 {
205 return (NULL);
206 }
207
208 /* This is ugly, and convoluted. All the magic
209 * for disk geo/size happens here. Basically,
210 * the real size is the one we will use in the
211 * rest of the program, the label size is what we
212 * got from the disklabel. If the disklabel fails,
213 * we assume we are working with a normal file,
214 * and should request the user to specify the
215 * geometry he/she wishes to use.
216 */
217 int
218 DISK_getmetrics(disk, user)
219 disk_t *disk;
220 DISK_metrics *user;
221 {
222
223 disk->label = DISK_getlabelmetrics(disk->name);
224 disk->bios = DISK_getbiosmetrics(disk->name);
225
226 /* If user supplied, use that */
227 if (user) {
228 disk->real = user;
229 return (0);
230 }
231
232 /* Fixup bios metrics to include cylinders past 1023 boundary */
233 if(disk->label && disk->bios){
234 int cyls, secs;
235
236 cyls = disk->label->size / (disk->bios->heads * disk->bios->sectors);
237 secs = cyls * (disk->bios->heads * disk->bios->sectors);
238 if (disk->label->size < secs) {
239 errx(1, "BIOS fixup botch (%u sectors)", disk->label->size - secs);
240 }
241 disk->bios->cylinders = cyls;
242 disk->bios->size = secs;
243 }
244
245 /* If we have a (fixed) BIOS geometry, use that */
246 if (disk->bios) {
247 disk->real = disk->bios;
248 return (0);
249 }
250
251 /* If we have a label, use that */
252 if (disk->label) {
253 disk->real = disk->label;
254 return (0);
255 }
256
257 /* Can not get geometry, punt */
258 disk->real = NULL;
259 return (1);
260 }
261
262 /* Get the disk's native sector size, updating the metrics' sector_size field.
263 */
264 int
265 DISK_get_sector_size(disk, user)
266 disk_t *disk;
267 DISK_metrics *user;
268 {
269 int ret;
270 int fd;
271 uint32_t sector_size;
272
273 /* Default to 512 bytes per sector, in case of failure. */
274 user->sector_size = 512;
275 ret = 1;
276
277 fd = DISK_open(disk->name, O_RDONLY);
278 if (fd == -1) {
279 err(1, "Could not open %s", disk->name);
280 } else {
281 if (ioctl(fd, DKIOCGETBLOCKSIZE, &sector_size) == -1) {
282 err(1, "Could not get disk block size");
283 } else {
284 user->sector_size = sector_size;
285 ret = 0;
286 }
287 }
288
289 return ret;
290 }
291
292 int
293 DISK_printmetrics(disk)
294 disk_t *disk;
295 {
296
297 printf("Disk: %s\t", disk->name);
298 if (disk->real) {
299 printf("geometry: %u/%u/%u [%u sectors]\n", disk->real->cylinders,
300 disk->real->heads, disk->real->sectors, disk->real->size);
301 if (disk->real->sector_size != 512)
302 printf("Sector size: %u bytes\n", disk->real->sector_size);
303 } else {
304 printf("geometry: <none>\n");
305 }
306
307 return (0);
308 }
309