Disklabel separation work - Generally shift all disklabel-specific
[dragonfly/vkernel-mp.git] / sys / kern / subr_disklabel32.c
blob6ece8d99117efa462a053834abafe357ba03060b
1 /*
2 * Copyright (c) 2003-2007 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
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:
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
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * ----------------------------------------------------------------------------
35 * "THE BEER-WARE LICENSE" (Revision 42):
36 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
37 * can do whatever you want with this stuff. If we meet some day, and you think
38 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
39 * ----------------------------------------------------------------------------
41 * Copyright (c) 1982, 1986, 1988, 1993
42 * The Regents of the University of California. All rights reserved.
43 * (c) UNIX System Laboratories, Inc.
44 * All or some portions of this file are derived from material licensed
45 * to the University of California by American Telephone and Telegraph
46 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
47 * the permission of UNIX System Laboratories, Inc.
49 * Copyright (c) 1994 Bruce D. Evans.
50 * All rights reserved.
52 * Copyright (c) 1990 The Regents of the University of California.
53 * All rights reserved.
55 * This code is derived from software contributed to Berkeley by
56 * William Jolitz.
58 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
59 * All rights reserved.
61 * Redistribution and use in source and binary forms, with or without
62 * modification, are permitted provided that the following conditions
63 * are met:
64 * 1. Redistributions of source code must retain the above copyright
65 * notice, this list of conditions and the following disclaimer.
66 * 2. Redistributions in binary form must reproduce the above copyright
67 * notice, this list of conditions and the following disclaimer in the
68 * documentation and/or other materials provided with the distribution.
69 * 3. All advertising materials mentioning features or use of this software
70 * must display the following acknowledgement:
71 * This product includes software developed by the University of
72 * California, Berkeley and its contributors.
73 * 4. Neither the name of the University nor the names of its contributors
74 * may be used to endorse or promote products derived from this software
75 * without specific prior written permission.
77 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
87 * SUCH DAMAGE.
89 * @(#)ufs_disksubr.c 8.5 (Berkeley) 1/21/94
90 * $FreeBSD: src/sys/kern/subr_disk.c,v 1.20.2.6 2001/10/05 07:14:57 peter Exp $
91 * $FreeBSD: src/sys/ufs/ufs/ufs_disksubr.c,v 1.44.2.3 2001/03/05 05:42:19 obrien Exp $
92 * $DragonFly: src/sys/kern/subr_disklabel32.c,v 1.1 2007/06/17 23:50:16 dillon Exp $
95 #include <sys/param.h>
96 #include <sys/systm.h>
97 #include <sys/kernel.h>
98 #include <sys/proc.h>
99 #include <sys/sysctl.h>
100 #include <sys/buf.h>
101 #include <sys/conf.h>
102 #include <sys/disklabel.h>
103 #include <sys/diskslice.h>
104 #include <sys/disk.h>
105 #include <sys/dtype.h> /* DTYPE_* constants */
106 #include <sys/sysctl.h>
107 #include <machine/md_var.h>
108 #include <sys/ctype.h>
109 #include <sys/syslog.h>
110 #include <sys/device.h>
111 #include <sys/msgport.h>
112 #include <sys/msgport2.h>
113 #include <sys/buf2.h>
115 #include <vfs/ufs/dinode.h> /* XXX used only for fs.h */
116 #include <vfs/ufs/fs.h> /* XXX used only to get BBSIZE/SBSIZE */
118 static void partition_info(const char *sname, int part, struct partition *pp);
119 static void slice_info(const char *sname, struct diskslice *sp);
122 * Retrieve the partition start and extent, in blocks. Return 0 on success,
123 * EINVAL on error.
126 getpartbounds(struct disklabel *lp, u_int32_t part,
127 u_int64_t *start, u_int64_t *blocks)
129 struct partition *pp;
131 if (part >= lp->d_npartitions)
132 return (EINVAL);
133 pp = &lp->d_partitions[part];
134 *blocks = pp->p_size;
135 *start = pp->p_offset;
136 return(0);
140 getpartfstype(struct disklabel *lp, u_int32_t part)
142 struct partition *pp;
144 if (part >= lp->d_npartitions)
145 return (0);
146 pp = &lp->d_partitions[part];
147 return(pp->p_fstype);
150 u_int32_t
151 getnumparts(struct disklabel *lp)
153 return(lp->d_npartitions);
157 * Attempt to read a disk label from a device using the indicated strategy
158 * routine. The label must be partly set up before this: secpercyl, secsize
159 * and anything required in the strategy routine (e.g., dummy bounds for the
160 * partition containing the label) must be filled in before calling us.
161 * Returns NULL on success and an error string on failure.
163 char *
164 readdisklabel(cdev_t dev, struct disklabel *lp)
166 struct buf *bp;
167 struct disklabel *dlp;
168 char *msg = NULL;
170 bp = geteblk((int)lp->d_secsize);
171 bp->b_bio1.bio_offset = (off_t)LABELSECTOR * lp->d_secsize;
172 bp->b_bcount = lp->d_secsize;
173 bp->b_flags &= ~B_INVAL;
174 bp->b_cmd = BUF_CMD_READ;
175 dev_dstrategy(dev, &bp->b_bio1);
176 if (biowait(bp))
177 msg = "I/O error";
178 else for (dlp = (struct disklabel *)bp->b_data;
179 dlp <= (struct disklabel *)((char *)bp->b_data +
180 lp->d_secsize - sizeof(*dlp));
181 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
182 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
183 if (msg == NULL)
184 msg = "no disk label";
185 } else if (dlp->d_npartitions > MAXPARTITIONS ||
186 dkcksum(dlp) != 0)
187 msg = "disk label corrupted";
188 else {
189 *lp = *dlp;
190 msg = NULL;
191 break;
194 bp->b_flags |= B_INVAL | B_AGE;
195 brelse(bp);
196 return (msg);
200 * Check new disk label for sensibility before setting it.
203 setdisklabel(struct disklabel *olp, struct disklabel *nlp,
204 struct diskslice *sp, u_int32_t *openmask)
206 struct partition *opp, *npp;
207 int part;
208 int i;
211 * Check it is actually a disklabel we are looking at.
213 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
214 dkcksum(nlp) != 0)
215 return (EINVAL);
218 * For each partition that we think is open, check the new disklabel
219 * for compatibility. Ignore special partitions (>= 128).
221 i = 0;
222 while (i < 128) {
223 if (openmask[i >> 5] == 0) {
224 i += 32;
225 continue;
227 if ((openmask[i >> 5] & (1 << (i & 31))) == 0) {
228 ++i;
229 continue;
231 if (nlp->d_npartitions <= i)
232 return (EBUSY);
233 opp = &olp->d_partitions[i];
234 npp = &nlp->d_partitions[i];
235 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
236 return (EBUSY);
238 * Copy internally-set partition information
239 * if new label doesn't include it. XXX
240 * (If we are using it then we had better stay the same type)
241 * This is possibly dubious, as someone else noted (XXX)
243 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
244 npp->p_fstype = opp->p_fstype;
245 npp->p_fsize = opp->p_fsize;
246 npp->p_frag = opp->p_frag;
247 npp->p_cpg = opp->p_cpg;
249 ++i;
251 nlp->d_checksum = 0;
252 nlp->d_checksum = dkcksum(nlp);
253 *olp = *nlp;
255 if (olp->d_partitions[RAW_PART].p_offset)
256 return (EXDEV);
257 if (olp->d_secperunit > sp->ds_size)
258 return (ENOSPC);
259 for (part = 0; part < olp->d_npartitions; ++part) {
260 if (olp->d_partitions[part].p_size > sp->ds_size)
261 return(ENOSPC);
263 return (0);
267 * Write disk label back to device after modification.
270 writedisklabel(cdev_t dev, struct disklabel *lp)
272 struct buf *bp;
273 struct disklabel *dlp;
274 int error = 0;
276 if (lp->d_partitions[RAW_PART].p_offset != 0)
277 return (EXDEV); /* not quite right */
278 bp = geteblk((int)lp->d_secsize);
279 bp->b_bio1.bio_offset = (off_t)LABELSECTOR * lp->d_secsize;
280 bp->b_bcount = lp->d_secsize;
281 #if 1
283 * We read the label first to see if it's there,
284 * in which case we will put ours at the same offset into the block..
285 * (I think this is stupid [Julian])
286 * Note that you can't write a label out over a corrupted label!
287 * (also stupid.. how do you write the first one? by raw writes?)
289 bp->b_flags &= ~B_INVAL;
290 bp->b_cmd = BUF_CMD_READ;
291 dev_dstrategy(dkmodpart(dev, WHOLE_SLICE_PART), &bp->b_bio1);
292 error = biowait(bp);
293 if (error)
294 goto done;
295 for (dlp = (struct disklabel *)bp->b_data;
296 dlp <= (struct disklabel *)
297 ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp));
298 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
299 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
300 dkcksum(dlp) == 0) {
301 *dlp = *lp;
302 bp->b_cmd = BUF_CMD_WRITE;
303 dev_dstrategy(dkmodpart(dev, WHOLE_SLICE_PART), &bp->b_bio1);
304 error = biowait(bp);
305 goto done;
308 error = ESRCH;
309 done:
310 #else
311 bzero(bp->b_data, lp->d_secsize);
312 dlp = (struct disklabel *)bp->b_data;
313 *dlp = *lp;
314 bp->b_flags &= ~B_INVAL;
315 bp->b_cmd = BUF_CMD_WRITE;
316 BUF_STRATEGY(bp, 1);
317 error = biowait(bp);
318 #endif
319 bp->b_flags |= B_INVAL | B_AGE;
320 brelse(bp);
321 return (error);
325 * Create a disklabel based on a disk_info structure, initializing
326 * the appropriate fields and creating a raw partition that covers the
327 * whole disk.
329 * If a diskslice is passed, the label is truncated to the slice
331 struct disklabel *
332 clone_label(struct disk_info *info, struct diskslice *sp)
334 struct disklabel *lp1;
336 lp1 = kmalloc(sizeof *lp1, M_DEVBUF, M_WAITOK | M_ZERO);
337 lp1->d_nsectors = info->d_secpertrack;
338 lp1->d_ntracks = info->d_nheads;
339 lp1->d_secpercyl = info->d_secpercyl;
340 lp1->d_secsize = info->d_media_blksize;
342 if (sp)
343 lp1->d_secperunit = (u_int)sp->ds_size;
344 else
345 lp1->d_secperunit = (u_int)info->d_media_blocks;
347 if (lp1->d_typename[0] == '\0')
348 strncpy(lp1->d_typename, "amnesiac", sizeof(lp1->d_typename));
349 if (lp1->d_packname[0] == '\0')
350 strncpy(lp1->d_packname, "fictitious", sizeof(lp1->d_packname));
351 if (lp1->d_nsectors == 0)
352 lp1->d_nsectors = 32;
353 if (lp1->d_ntracks == 0)
354 lp1->d_ntracks = 64;
355 lp1->d_secpercyl = lp1->d_nsectors * lp1->d_ntracks;
356 lp1->d_ncylinders = lp1->d_secperunit / lp1->d_secpercyl;
357 if (lp1->d_rpm == 0)
358 lp1->d_rpm = 3600;
359 if (lp1->d_interleave == 0)
360 lp1->d_interleave = 1;
361 if (lp1->d_npartitions < RAW_PART + 1)
362 lp1->d_npartitions = MAXPARTITIONS;
363 if (lp1->d_bbsize == 0)
364 lp1->d_bbsize = BBSIZE;
365 if (lp1->d_sbsize == 0)
366 lp1->d_sbsize = SBSIZE;
369 * Used by various devices to create a compatibility slice which
370 * allows us to mount root from devices which do not have a
371 * disklabel. Particularly: CDs.
373 lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
374 if (info->d_dsflags & DSO_COMPATPARTA) {
375 lp1->d_partitions[0].p_size = lp1->d_secperunit;
376 lp1->d_partitions[0].p_fstype = FS_OTHER;
378 lp1->d_magic = DISKMAGIC;
379 lp1->d_magic2 = DISKMAGIC;
380 lp1->d_checksum = dkcksum(lp1);
381 return (lp1);
384 void
385 makevirginlabel(struct disklabel *lp, struct diskslices *ssp,
386 struct diskslice *sp, struct disk_info *info)
388 struct partition *pp;
390 if (ssp->dss_slices[WHOLE_DISK_SLICE].ds_label) {
391 bcopy(ssp->dss_slices[WHOLE_DISK_SLICE].ds_label, lp,
392 sizeof(*lp));
393 } else {
394 bzero(lp, sizeof(*lp));
396 lp->d_magic = DISKMAGIC;
397 lp->d_magic2 = DISKMAGIC;
399 lp->d_npartitions = MAXPARTITIONS;
400 if (lp->d_interleave == 0)
401 lp->d_interleave = 1;
402 if (lp->d_rpm == 0)
403 lp->d_rpm = 3600;
404 if (lp->d_nsectors == 0) /* sectors per track */
405 lp->d_nsectors = 32;
406 if (lp->d_ntracks == 0) /* heads */
407 lp->d_ntracks = 64;
408 lp->d_ncylinders = 0;
409 lp->d_bbsize = BBSIZE;
410 lp->d_sbsize = SBSIZE;
413 * If the slice or GPT partition is really small we could
414 * wind up with an absurd calculation for ncylinders.
416 while (lp->d_ncylinders < 4) {
417 if (lp->d_ntracks > 1)
418 lp->d_ntracks >>= 1;
419 else if (lp->d_nsectors > 1)
420 lp->d_nsectors >>= 1;
421 else
422 break;
423 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
424 lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
428 * Set or Modify the partition sizes to accomodate the slice,
429 * since we started with a copy of the virgin label stored
430 * in the whole-disk-slice and we are probably not a
431 * whole-disk slice.
433 lp->d_secperunit = sp->ds_size;
434 pp = &lp->d_partitions[RAW_PART];
435 pp->p_offset = 0;
436 pp->p_size = lp->d_secperunit;
437 if (info->d_dsflags & DSO_COMPATPARTA) {
438 pp = &lp->d_partitions[0];
439 pp->p_offset = 0;
440 pp->p_size = lp->d_secperunit;
441 pp->p_fstype = FS_OTHER;
443 lp->d_checksum = 0;
444 lp->d_checksum = dkcksum(lp);
447 char *
448 fixlabel(const char *sname, struct diskslice *sp, struct disklabel *lp, int writeflag)
450 u_int64_t start;
451 u_int64_t end;
452 u_int64_t offset;
453 int part;
454 struct partition *pp;
455 int warned;
457 /* These errors "can't happen" so don't bother reporting details. */
458 if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC)
459 return ("fixlabel: invalid magic");
460 if (dkcksum(lp) != 0)
461 return ("fixlabel: invalid checksum");
463 pp = &lp->d_partitions[RAW_PART];
466 * What a mess. For ages old backwards compatibility the disklabel
467 * on-disk stores absolute offsets instead of slice-relative offsets.
468 * So fix it up when reading, writing, or snooping.
470 * The in-core label is always slice-relative.
472 if (writeflag) {
473 start = 0;
474 offset = sp->ds_offset;
475 } else {
476 start = sp->ds_offset;
477 offset = -sp->ds_offset;
479 if (pp->p_offset != start) {
480 if (sname != NULL) {
481 kprintf(
482 "%s: rejecting BSD label: raw partition offset != slice offset\n",
483 sname);
484 slice_info(sname, sp);
485 partition_info(sname, RAW_PART, pp);
487 return ("fixlabel: raw partition offset != slice offset");
489 if (pp->p_size != sp->ds_size) {
490 if (sname != NULL) {
491 kprintf("%s: raw partition size != slice size\n", sname);
492 slice_info(sname, sp);
493 partition_info(sname, RAW_PART, pp);
495 if (pp->p_size > sp->ds_size) {
496 if (sname == NULL)
497 return ("fixlabel: raw partition size > slice size");
498 kprintf("%s: truncating raw partition\n", sname);
499 pp->p_size = sp->ds_size;
502 end = start + sp->ds_size;
503 if (start > end)
504 return ("fixlabel: slice wraps");
505 if (lp->d_secpercyl <= 0)
506 return ("fixlabel: d_secpercyl <= 0");
507 pp -= RAW_PART;
508 warned = FALSE;
509 for (part = 0; part < lp->d_npartitions; part++, pp++) {
510 if (pp->p_offset != 0 || pp->p_size != 0) {
511 if (pp->p_offset < start
512 || pp->p_offset + pp->p_size > end
513 || pp->p_offset + pp->p_size < pp->p_offset) {
514 if (sname != NULL) {
515 kprintf(
516 "%s: rejecting partition in BSD label: it isn't entirely within the slice\n",
517 sname);
518 if (!warned) {
519 slice_info(sname, sp);
520 warned = TRUE;
522 partition_info(sname, part, pp);
524 /* XXX else silently discard junk. */
525 bzero(pp, sizeof *pp);
526 } else {
527 pp->p_offset += offset;
531 lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
532 lp->d_secperunit = sp->ds_size;
533 lp->d_checksum = 0;
534 lp->d_checksum = dkcksum(lp);
535 return (NULL);
538 void
539 adjust_label_reserved(struct diskslices *ssp, int slice, struct diskslice *sp)
541 struct disklabel *lp = sp->ds_label;
544 * If the slice is not the whole-disk slice, setup the reserved
545 * area(s).
547 * The reserved area for the original bsd disklabel, inclusive of
548 * the label and space for boot2, is 15 sectors. If you've
549 * noticed people traditionally skipping 16 sectors its because
550 * the sector numbers start at the beginning of the slice rather
551 * then the beginning of the disklabel and traditional dos slices
552 * reserve a sector at the beginning for the boot code.
554 * NOTE! With the traditional bsdlabel, the first N bytes of boot2
555 * overlap with the disklabel. The disklabel program checks that
556 * they are 0.
558 * When clearing a label, the bsdlabel reserved area is reset.
560 if (slice != WHOLE_DISK_SLICE) {
561 if (lp) {
563 * BSD uses in-band labels, meaning the label itself
564 * is accessible from partitions within the label.
565 * We must reserved the area taken up by the label
566 * itself to prevent mistakes from wiping it.
568 sp->ds_reserved = SBSIZE / ssp->dss_secsize;
569 } else {
570 sp->ds_reserved = 0;
575 static void
576 partition_info(const char *sname, int part, struct partition *pp)
578 kprintf("%s%c: start %lu, end %lu, size %lu\n", sname, 'a' + part,
579 (u_long)pp->p_offset, (u_long)(pp->p_offset + pp->p_size - 1),
580 (u_long)pp->p_size);
583 static void
584 slice_info(const char *sname, struct diskslice *sp)
586 kprintf("%s: start %llu, end %llu, size %llu\n", sname,
587 sp->ds_offset, sp->ds_offset + sp->ds_size - 1, sp->ds_size);