1 /* $NetBSD: disksubr.c,v 1.58 2019/04/03 22:10:50 christos Exp $ */
2
3 /*
4 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91
32 */
33 /*-
34 * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo,
35 * Michael L. Finch, Bradley A. Grantham, and
36 * Lawrence A. Kesteloot
37 * All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the Alice Group.
50 * 4. The names of the Alice Group or any of its members may not be used
51 * to endorse or promote products derived from this software without
52 * specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
55 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
56 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
57 * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
58 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
59 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
60 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
61 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
63 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 *
65 */
66
67 #include <sys/cdefs.h>
68 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.58 2019/04/03 22:10:50 christos Exp $");
69
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/buf.h>
73 #include <sys/disk.h>
74 #include <sys/disklabel.h>
75 #include <sys/bootblock.h>
76 #include <sys/syslog.h>
77
78 #include <sys/bswap.h>
79
80 #define NUM_PARTS 32
81
82 #define ROOT_PART 1
83 #define UFS_PART 2
84 #define SWAP_PART 3
85 #define HFS_PART 4
86 #define SCRATCH_PART 5
87
88 int fat_types[] = {
89 MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S,
90 MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32,
91 MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L,
92 -1
93 };
94
95 static int getFreeLabelEntry(struct disklabel *);
96 static int whichType(struct part_map_entry *);
97 static void setpartition(struct part_map_entry *, struct partition *, int);
98 static int getNamedType(struct part_map_entry *, int, struct disklabel *, int,
99 int, int *);
100 static char *read_mac_label(char *, struct disklabel *, int *);
101 static char *read_mbr_label(char *, struct disklabel *, int *);
102 static const char *read_bsd_label(char *, struct disklabel *, int *);
103
104
105 /*
106 * Find an entry in the disk label that is unused and return it
107 * or -1 if no entry
108 */
109 static int
getFreeLabelEntry(struct disklabel * lp)110 getFreeLabelEntry(struct disklabel *lp)
111 {
112 int i;
113
114 for (i = 0; i < MAXPARTITIONS; i++) {
115 if ((i != RAW_PART)
116 && (lp->d_partitions[i].p_fstype == FS_UNUSED))
117 return i;
118 }
119 return -1;
120 }
121
122 /*
123 * figure out what the type of the given part is and return it
124 */
125 static int
whichType(struct part_map_entry * part)126 whichType(struct part_map_entry *part)
127 {
128 struct blockzeroblock *bzb;
129 char typestr[32], *s;
130 int type;
131
132 if (part->pmSig != PART_ENTRY_MAGIC || part->pmPartType[0] == '\0')
133 return 0;
134
135 strncpy(typestr, (char *)part->pmPartType, sizeof(typestr));
136 typestr[sizeof(typestr) - 1] = '\0';
137 for (s = typestr; *s; s++)
138 if ((*s >= 'a') && (*s <= 'z'))
139 *s = (*s - 'a' + 'A');
140
141 if (strcmp(PART_TYPE_DRIVER, typestr) == 0 ||
142 strcmp(PART_TYPE_DRIVER43, typestr) == 0 ||
143 strcmp(PART_TYPE_DRIVERATA, typestr) == 0 ||
144 strcmp(PART_TYPE_FWB_COMPONENT, typestr) == 0 ||
145 strcmp(PART_TYPE_PARTMAP, typestr) == 0)
146 type = 0;
147 else if (strcmp(PART_TYPE_UNIX, typestr) == 0) {
148 /* unix part, swap, root, usr */
149 bzb = (struct blockzeroblock *)(&part->pmBootArgs);
150 if (bzb->bzbMagic != BZB_MAGIC)
151 type = 0;
152 else if (bzb->bzbFlags & BZB_ROOTFS)
153 type = ROOT_PART;
154 else if (bzb->bzbFlags & BZB_USRFS)
155 type = UFS_PART;
156 else if (bzb->bzbType == BZB_TYPESWAP)
157 type = SWAP_PART;
158 else
159 type = SCRATCH_PART;
160 } else if (strcmp(PART_TYPE_MAC, typestr) == 0)
161 type = HFS_PART;
162 else
163 type = SCRATCH_PART; /* no known type */
164
165 return type;
166 }
167
168 static void
setpartition(struct part_map_entry * part,struct partition * pp,int fstype)169 setpartition(struct part_map_entry *part, struct partition *pp, int fstype)
170 {
171 pp->p_size = part->pmPartBlkCnt;
172 pp->p_offset = part->pmPyPartStart;
173 pp->p_fstype = fstype;
174
175 part->pmPartType[0] = '\0';
176 }
177
178 static int
getNamedType(struct part_map_entry * part,int num_parts,struct disklabel * lp,int type,int alt,int * maxslot)179 getNamedType(struct part_map_entry *part, int num_parts, struct disklabel *lp,
180 int type, int alt, int *maxslot)
181 {
182 struct blockzeroblock *bzb;
183 int i;
184
185 for (i = 0; i < num_parts; i++) {
186 if (whichType(part + i) != type)
187 continue;
188
189 if (type == ROOT_PART) {
190 bzb = (struct blockzeroblock *)
191 (&(part + i)->pmBootArgs);
192 if (alt >= 0 && alt != bzb->bzbCluster)
193 continue;
194 setpartition(part + i, &lp->d_partitions[0], FS_BSDFFS);
195 } else if (type == UFS_PART) {
196 bzb = (struct blockzeroblock *)
197 (&(part + i)->pmBootArgs);
198 if (alt >= 0 && alt != bzb->bzbCluster)
199 continue;
200 setpartition(part + i, &lp->d_partitions[6], FS_BSDFFS);
201 if (*maxslot < 6)
202 *maxslot = 6;
203 } else if (type == SWAP_PART) {
204 setpartition(part + i, &lp->d_partitions[1], FS_SWAP);
205 if (*maxslot < 1)
206 *maxslot = 1;
207 } else
208 printf("disksubr.c: can't do type %d\n", type);
209
210 return 0;
211 }
212
213 return -1;
214 }
215
216 /*
217 * MF --
218 * here's what i'm gonna do:
219 * read in the entire diskpartition table, it may be bigger or smaller
220 * than NUM_PARTS but read that many entries. Each entry has a magic
221 * number so we'll know if an entry is crap.
222 * next fill in the disklabel with info like this
223 * next fill in the root, usr, and swap parts.
224 * then look for anything else and fit it in.
225 * A: root
226 * B: Swap
227 * C: Whole disk
228 * G: Usr
229 *
230 *
231 * I'm not entirely sure what netbsd386 wants in c & d
232 * 386bsd wants other stuff, so i'll leave them alone
233 *
234 * AKB -- I added to Mike's original algorithm by searching for a bzbCluster
235 * of zero for root, first. This allows A/UX to live on cluster 1 and
236 * NetBSD to live on cluster 0--regardless of the actual order on the
237 * disk. This whole algorithm should probably be changed in the future.
238 */
239
240 /*
241 * This uses sector zero. If this contains what looks like a valid
242 * Macintosh boot sector, we attempt to fill in the disklabel structure
243 * with the partition data from block #1 on.
244 */
245 static char *
read_mac_label(char * dlbuf,struct disklabel * lp,int * match)246 read_mac_label(char *dlbuf, struct disklabel *lp, int *match)
247 {
248 u_int16_t *sbSigp;
249 struct part_map_entry *part;
250 struct partition *pp;
251 char *msg;
252 int i, slot, maxslot;
253
254 maxslot = 0;
255 *match = 0;
256 msg = NULL;
257
258 sbSigp = (u_int16_t *)dlbuf;
259 if (*sbSigp != DRIVER_MAP_MAGIC)
260 return msg;
261
262 /* Found Macintosh partition magic number; set up disklabel */
263 *match = (-1);
264
265 /* the Macintosh partition table starts at sector #1 */
266 part = (struct part_map_entry *)(dlbuf + DEV_BSIZE);
267
268 /* Fill in standard partitions */
269 lp->d_npartitions = RAW_PART + 1;
270 if (getNamedType(part, NUM_PARTS, lp, ROOT_PART, 0, &maxslot))
271 getNamedType(part, NUM_PARTS, lp, ROOT_PART, -1, &maxslot);
272 if (getNamedType(part, NUM_PARTS, lp, UFS_PART, 0, &maxslot))
273 getNamedType(part, NUM_PARTS, lp, UFS_PART, -1, &maxslot);
274 getNamedType(part, NUM_PARTS, lp, SWAP_PART, -1, &maxslot);
275
276 /* Now get as many of the rest of the partitions as we can */
277 for (i = 0; i < NUM_PARTS; i++) {
278 slot = getFreeLabelEntry(lp);
279 if (slot < 0)
280 break;
281
282 pp = &lp->d_partitions[slot];
283
284 switch (whichType(part + i)) {
285 case ROOT_PART:
286 /*
287 * another root part will turn into a plain old
288 * UFS_PART partition, live with it.
289 */
290 case UFS_PART:
291 setpartition(part + i, pp, FS_BSDFFS);
292 break;
293 case SWAP_PART:
294 setpartition(part + i, pp, FS_SWAP);
295 break;
296 case HFS_PART:
297 setpartition(part + i, pp, FS_HFS);
298 break;
299 case SCRATCH_PART:
300 setpartition(part + i, pp, FS_OTHER);
301 break;
302 default:
303 slot = 0;
304 break;
305 }
306 if (slot > maxslot)
307 maxslot = slot;
308 }
309 lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
310 return msg;
311 }
312
313 /*
314 * Scan the disk buffer for a DOS style master boot record.
315 * Return if no match; otherwise, set up an in-core disklabel .
316 *
317 * XXX stuff like this really should be MI
318 *
319 * Since FFS is endian sensitive, we pay no effort in attempting to
320 * dig up *BSD/i386 disk labels that may be present on the disk.
321 * Hence anything but DOS partitions is treated as unknown FS type, but
322 * this should suffice to mount_msdos Zip and other removable media.
323 */
324 static char *
read_mbr_label(char * dlbuf,struct disklabel * lp,int * match)325 read_mbr_label(char *dlbuf, struct disklabel *lp, int *match)
326 {
327 struct mbr_partition *dp;
328 struct partition *pp;
329 char *msg;
330 size_t mbr_lbl_off;
331 int i, *ip, slot, maxslot;
332
333 maxslot = 0;
334 *match = 0;
335 msg = NULL;
336
337 if (MBR_MAGIC != bswap16(*(u_int16_t *)(dlbuf + MBR_MAGIC_OFFSET)))
338 return msg;
339
340 /* Found MBR magic number; set up disklabel */
341 *match = (-1);
342 mbr_lbl_off = MBR_BBSECTOR * lp->d_secsize + MBR_PART_OFFSET;
343
344 dp = (struct mbr_partition *)(dlbuf + mbr_lbl_off);
345 for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
346 if (dp->mbrp_type == 0)
347 continue;
348
349 slot = getFreeLabelEntry(lp);
350 maxslot = (slot > maxslot) ? maxslot : slot;
351
352 pp = &lp->d_partitions[slot];
353 pp->p_fstype = FS_OTHER;
354 pp->p_offset = bswap32(dp->mbrp_start);
355 pp->p_size = bswap32(dp->mbrp_size);
356
357 for (ip = fat_types; *ip != -1; ip++) {
358 if (dp->mbrp_type == *ip) {
359 pp->p_fstype = FS_MSDOS;
360 break;
361 }
362 }
363 }
364 lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
365 return msg;
366 }
367
368 /*
369 * Scan the disk buffer in four byte steps for a native BSD disklabel
370 * (different ports have variable-sized bootcode before the label)
371 */
372 static const char *
read_bsd_label(char * dlbuf,struct disklabel * lp,int * match)373 read_bsd_label(char *dlbuf, struct disklabel *lp, int *match)
374 {
375 struct disklabel *dlp;
376 const char *msg;
377 struct disklabel *blk_start, *blk_end;
378
379 *match = 0;
380 msg = NULL;
381
382 blk_start = (struct disklabel *)dlbuf;
383 blk_end = (struct disklabel *)(dlbuf + (NUM_PARTS << DEV_BSHIFT) -
384 sizeof(struct disklabel));
385
386 for (dlp = blk_start; dlp <= blk_end;
387 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
388 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) {
389 /* Sanity check */
390 if (dlp->d_npartitions <= MAXPARTITIONS &&
391 dkcksum(dlp) == 0) {
392 *lp = *dlp;
393 *match = (-1);
394 } else
395 msg = "Disk label corrupted";
396 break;
397 }
398 }
399 return msg;
400 }
401
402 /*
403 * Attempt to read a disk label from a device using the indicated strategy
404 * routine. The label must be partly set up before this: secpercyl and
405 * anything required in the strategy routine (e.g., sector size) must be
406 * filled in before calling us. Returns null on success and an error
407 * string on failure.
408 */
409 const char *
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)410 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
411 struct cpu_disklabel *osdep)
412 {
413 struct buf *bp;
414 const char *msg;
415 int size;
416
417 if (lp->d_secperunit == 0)
418 lp->d_secperunit = 0x1fffffff;
419
420 if (lp->d_secpercyl == 0)
421 return msg = "Zero secpercyl";
422
423 msg = NULL;
424
425 /*
426 * Read in the first #(NUM_PARTS + 1) blocks of the disk.
427 * The native Macintosh partition table starts at
428 * sector #1, but we want #0 too for the BSD label.
429 */
430
431 size = roundup((NUM_PARTS + 1) << DEV_BSHIFT, lp->d_secsize);
432 bp = geteblk(size);
433
434 bp->b_dev = dev;
435 bp->b_blkno = 0;
436 bp->b_resid = 0;
437 bp->b_bcount = size;
438 bp->b_flags |= B_READ;
439 bp->b_cylinder = 1 / lp->d_secpercyl;
440 (*strat)(bp);
441
442 if (biowait(bp)) {
443 msg = "I/O error reading block zero";
444 } else {
445 int match;
446
447 /* Add any offsets in the table handlers */
448 msg = read_mac_label(bp->b_data, lp, &match);
449 if (!match && msg == NULL)
450 msg = read_mbr_label(bp->b_data, lp, &match);
451 if (!match && msg == NULL)
452 msg = read_bsd_label(bp->b_data, lp, &match);
453 if (!match && msg == NULL)
454 msg = "no disk label";
455 }
456
457 brelse(bp, 0);
458 return (msg);
459 }
460
461 /*
462 * Check new disk label for sensibility before setting it.
463 */
464 int
setdisklabel(struct disklabel * olp,struct disklabel * nlp,u_long openmask,struct cpu_disklabel * osdep)465 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask,
466 struct cpu_disklabel *osdep)
467 {
468 return 0;
469 }
470
471 /*
472 * Write disk label back to device after modification.
473 *
474 * MF - 8-14-93 This function is never called. It is here just in case
475 * we want to write dos disklabels some day. Really!
476 */
477 int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)478 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
479 struct cpu_disklabel *osdep)
480 {
481 #if 0
482 struct buf *bp;
483 struct disklabel *dlp;
484 int labelpart;
485 int error = 0;
486
487 labelpart = DISKPART(dev);
488 if (lp->d_partitions[labelpart].p_offset != 0) {
489 if (lp->d_partitions[0].p_offset != 0)
490 return (EXDEV); /* not quite right */
491 labelpart = 0;
492 }
493 bp = geteblk((int)lp->d_secsize);
494 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), labelpart);
495 bp->b_blkno = LABELSECTOR;
496 bp->b_bcount = lp->d_secsize;
497 bp->b_flags |= B_READ;
498 (*strat)(bp);
499 if (error = biowait(bp))
500 goto done;
501 for (dlp = (struct disklabel *)bp->b_data;
502 dlp <= (struct disklabel *)
503 ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp));
504 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
505 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
506 dkcksum(dlp) == 0) {
507 *dlp = *lp;
508 bp->b_oflags &= ~(BO_DONE);
509 bp->b_flags &= ~(B_READ);
510 bp->b_flags |= B_WRITE;
511 (*strat)(bp);
512 error = biowait(bp);
513 goto done;
514 }
515 }
516 error = ESRCH;
517 done:
518 brelse(bp, 0);
519 return (error);
520 #else
521 int i;
522
523 /*
524 * Clear and re-analyze the ondisk Apple Disk Partition Map,
525 * then recompute the faked incore disk label. This is necessary
526 * for sysinst, which may have modified the disk layout. We don't
527 * (yet?) support writing real BSD disk labels, so this hack
528 * instead causes the DIOCWDINFO ioctl invoked by sysinst to
529 * update the in-core disk label when it is "written" to disk.
530 * This code was originally developed by Bob Nestor on 9/13/99.
531 */
532 lp->d_npartitions = 0;
533 for (i = 0; i < MAXPARTITIONS; i++) {
534 lp->d_partitions[i].p_fstype = FS_UNUSED;
535 lp->d_partitions[i].p_offset = 0;
536 if (i != RAW_PART)
537 lp->d_partitions[i].p_size = 0;
538 }
539 return (readdisklabel(dev, strat, lp, osdep) ? EINVAL : 0);
540 #endif
541 }
542