Remove the roll-your-own disklabel from CCD. Use the kernel disk manager
[dragonfly.git] / sys / kern / subr_diskslice.c
1 /*-
2  * Copyright (c) 1994 Bruce D. Evans.
3  * All rights reserved.
4  *
5  * Copyright (c) 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * William Jolitz.
10  *
11  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
12  * All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. All advertising materials mentioning features or use of this software
23  *    must display the following acknowledgement:
24  *      This product includes software developed by the University of
25  *      California, Berkeley and its contributors.
26  * 4. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  *      from: @(#)wd.c  7.2 (Berkeley) 5/9/91
43  *      from: wd.c,v 1.55 1994/10/22 01:57:12 phk Exp $
44  *      from: @(#)ufs_disksubr.c        7.16 (Berkeley) 5/4/91
45  *      from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $
46  * $FreeBSD: src/sys/kern/subr_diskslice.c,v 1.82.2.6 2001/07/24 09:49:41 dd Exp $
47  * $DragonFly: src/sys/kern/subr_diskslice.c,v 1.33 2007/05/17 03:20:09 dillon Exp $
48  */
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/buf.h>
53 #include <sys/conf.h>
54 #include <sys/disklabel.h>
55 #include <sys/diskslice.h>
56 #include <sys/disk.h>
57 #include <sys/diskmbr.h>
58 #include <sys/fcntl.h>
59 #include <sys/malloc.h>
60 #include <sys/stat.h>
61 #include <sys/syslog.h>
62 #include <sys/proc.h>
63 #include <sys/vnode.h>
64 #include <sys/device.h>
65 #include <sys/thread2.h>
66
67 #include <vfs/ufs/dinode.h>     /* XXX used only for fs.h */
68 #include <vfs/ufs/fs.h>         /* XXX used only to get BBSIZE/SBSIZE */
69
70 #define TRACE(str)      do { if (ds_debug) kprintf str; } while (0)
71
72 typedef u_char  bool_t;
73
74 static volatile bool_t ds_debug;
75
76 static struct disklabel *clone_label (struct disk_info *info,
77                                         struct diskslice *sp);
78 static void dsiodone (struct bio *bio);
79 static char *fixlabel (char *sname, struct diskslice *sp,
80                            struct disklabel *lp, int writeflag);
81 static void free_ds_label (struct diskslices *ssp, int slice);
82 static void partition_info (char *sname, int part, struct partition *pp);
83 static void slice_info (char *sname, struct diskslice *sp);
84 static void set_ds_label (struct diskslices *ssp, int slice,
85                               struct disklabel *lp);
86 static void set_ds_wlabel (struct diskslices *ssp, int slice, int wlabel);
87
88 /*
89  * Create a disklabel based on a disk_info structure, initializing
90  * the appropriate fields and creating a raw partition that covers the
91  * whole disk.
92  *
93  * If a diskslice is passed, the label is truncated to the slice
94  */
95 static struct disklabel *
96 clone_label(struct disk_info *info, struct diskslice *sp)
97 {
98         struct disklabel *lp1;
99
100         lp1 = kmalloc(sizeof *lp1, M_DEVBUF, M_WAITOK | M_ZERO);
101         lp1->d_nsectors = info->d_secpertrack;
102         lp1->d_ntracks = info->d_nheads;
103         lp1->d_secpercyl = info->d_secpercyl;
104         lp1->d_secsize = info->d_media_blksize;
105
106         if (sp) {
107                 lp1->d_secperunit = (u_int)sp->ds_size;
108                 lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
109         } else {
110                 lp1->d_secperunit = (u_int)info->d_media_blocks;
111                 lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
112         }
113
114         /*
115          * Used by the CD driver to create a compatibility slice which
116          * allows us to mount root from the CD.
117          */
118         if (info->d_dsflags & DSO_COMPATPARTA) {
119                 lp1->d_partitions[0].p_size = lp1->d_secperunit;
120                 lp1->d_partitions[0].p_fstype = FS_OTHER;
121         }
122
123         if (lp1->d_typename[0] == '\0')
124                 strncpy(lp1->d_typename, "amnesiac", sizeof(lp1->d_typename));
125         if (lp1->d_packname[0] == '\0')
126                 strncpy(lp1->d_packname, "fictitious", sizeof(lp1->d_packname));
127         if (lp1->d_nsectors == 0)
128                 lp1->d_nsectors = 32;
129         if (lp1->d_ntracks == 0)
130                 lp1->d_ntracks = 64;
131         lp1->d_secpercyl = lp1->d_nsectors * lp1->d_ntracks;
132         lp1->d_ncylinders = lp1->d_secperunit / lp1->d_secpercyl;
133         if (lp1->d_rpm == 0)
134                 lp1->d_rpm = 3600;
135         if (lp1->d_interleave == 0)
136                 lp1->d_interleave = 1;
137         if (lp1->d_npartitions < RAW_PART + 1)
138                 lp1->d_npartitions = MAXPARTITIONS;
139         if (lp1->d_bbsize == 0)
140                 lp1->d_bbsize = BBSIZE;
141         if (lp1->d_sbsize == 0)
142                 lp1->d_sbsize = SBSIZE;
143         lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
144         lp1->d_magic = DISKMAGIC;
145         lp1->d_magic2 = DISKMAGIC;
146         lp1->d_checksum = dkcksum(lp1);
147         return (lp1);
148 }
149
150 /*
151  * Determine the size of the transfer, and make sure it is
152  * within the boundaries of the partition. Adjust transfer
153  * if needed, and signal errors or early completion.
154  *
155  * XXX TODO:
156  *      o Split buffers that are too big for the device.
157  *      o Check for overflow.
158  *      o Finish cleaning this up.
159  *
160  * This function returns 1 on success, 0 if transfer equates
161  * to EOF (end of disk) or -1 on failure.  The appropriate 
162  * 'errno' value is also set in bp->b_error and bp->b_flags
163  * is marked with B_ERROR.
164  */
165 struct bio *
166 dscheck(cdev_t dev, struct bio *bio, struct diskslices *ssp)
167 {
168         struct buf *bp = bio->bio_buf;
169         struct bio *nbio;
170         struct disklabel *lp;
171         char *msg;
172         long nsec;
173         struct partition *pp;
174         u_int64_t secno;
175         u_int64_t endsecno;
176         u_int64_t labelsect;
177         u_int64_t slicerel_secno;
178         struct diskslice *sp;
179         int shift;
180         int mask;
181
182         if (bio->bio_offset < 0) {
183                 kprintf("dscheck(%s): negative bio_offset %lld\n", 
184                     devtoname(dev), bio->bio_offset);
185                 goto bad;
186         }
187         sp = &ssp->dss_slices[dkslice(dev)];
188         lp = sp->ds_label;
189
190         if (ssp->dss_secmult == 1) {
191                 shift = DEV_BSHIFT;
192                 goto doshift;
193         } else if (ssp->dss_secshift != -1) {
194                 shift = DEV_BSHIFT + ssp->dss_secshift;
195 doshift:
196                 mask = (1 << shift) - 1;
197                 if ((int)bp->b_bcount & mask)
198                         goto bad_bcount;
199                 if ((int)bio->bio_offset & mask)
200                         goto bad_blkno;
201                 secno = bio->bio_offset >> shift;
202                 nsec = bp->b_bcount >> shift;
203         } else {
204                 if (bp->b_bcount % ssp->dss_secsize)
205                         goto bad_bcount;
206                 if (bio->bio_offset % ssp->dss_secsize)
207                         goto bad_blkno;
208                 secno = bio->bio_offset / ssp->dss_secsize;
209                 nsec = bp->b_bcount / ssp->dss_secsize;
210         }
211         if (lp == NULL) {
212                 labelsect = -LABELSECTOR - 1;
213                 endsecno = sp->ds_size;
214                 slicerel_secno = secno;
215         } else {
216                 labelsect = lp->d_partitions[LABEL_PART].p_offset;
217                 if (labelsect != 0)
218                         Debugger("labelsect != 0 in dscheck()");
219                 pp = &lp->d_partitions[dkpart(dev)];
220                 endsecno = pp->p_size;
221                 slicerel_secno = pp->p_offset + secno;
222         }
223
224         /* overwriting disk label ? */
225         /* XXX should also protect bootstrap in first 8K */
226         if (slicerel_secno <= LABELSECTOR + labelsect &&
227 #if LABELSECTOR != 0
228             slicerel_secno + nsec > LABELSECTOR + labelsect &&
229 #endif
230             bp->b_cmd != BUF_CMD_READ && sp->ds_wlabel == 0) {
231                 bp->b_error = EROFS;
232                 goto error;
233         }
234
235 #if defined(DOSBBSECTOR) && defined(notyet)
236         /* overwriting master boot record? */
237         if (slicerel_secno <= DOSBBSECTOR && bp->b_cmd != BUF_CMD_READ &&
238             sp->ds_wlabel == 0) {
239                 bp->b_error = EROFS;
240                 goto error;
241         }
242 #endif
243
244         /*
245          * EOF handling
246          */
247         if (secno + nsec > endsecno) {
248                 /*
249                  * Return an error if beyond the end of the disk, or
250                  * if B_BNOCLIP is set.  Tell the system that we do not
251                  * need to keep the buffer around.
252                  */
253                 if (secno > endsecno || (bp->b_flags & B_BNOCLIP))
254                         goto bad;
255
256                 /*
257                  * If exactly at end of disk, return an EOF.  Throw away
258                  * the buffer contents, if any, by setting B_INVAL.
259                  */
260                 if (secno == endsecno) {
261                         bp->b_resid = bp->b_bcount;
262                         bp->b_flags |= B_INVAL;
263                         goto done;
264                 }
265
266                 /*
267                  * Else truncate
268                  */
269                 nsec = endsecno - secno;
270                 bp->b_bcount = nsec * ssp->dss_secsize;
271         }
272
273         nbio = push_bio(bio);
274         nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) * 
275                            ssp->dss_secsize;
276
277         /*
278          * Snoop on label accesses if the slice offset is nonzero.  Fudge
279          * offsets in the label to keep the in-core label coherent with
280          * the on-disk one.
281          */
282         if (slicerel_secno <= LABELSECTOR + labelsect
283 #if LABELSECTOR != 0
284             && slicerel_secno + nsec > LABELSECTOR + labelsect
285 #endif
286             && sp->ds_offset != 0) {
287                 nbio->bio_done = dsiodone;
288                 nbio->bio_caller_info1.ptr = sp;
289                 nbio->bio_caller_info2.offset = 
290                         (off_t)(LABELSECTOR + labelsect - slicerel_secno) *
291                         ssp->dss_secsize;
292                 if (bp->b_cmd != BUF_CMD_READ) {
293                         /*
294                          * XXX even disklabel(8) writes directly so we need
295                          * to adjust writes.  Perhaps we should drop support
296                          * for DIOCWLABEL (always write protect labels) and
297                          * require the use of DIOCWDINFO.
298                          *
299                          * XXX probably need to copy the data to avoid even
300                          * temporarily corrupting the in-core copy.
301                          */
302                         /* XXX need name here. */
303                         msg = fixlabel(
304                                 NULL, sp,
305                                (struct disklabel *)
306                                (bp->b_data + (int)nbio->bio_caller_info2.offset),
307                                TRUE);
308                         if (msg != NULL) {
309                                 kprintf("dscheck(%s): %s\n", 
310                                     devtoname(dev), msg);
311                                 bp->b_error = EROFS;
312                                 pop_bio(nbio);
313                                 goto error;
314                         }
315                 }
316         }
317         return (nbio);
318
319 bad_bcount:
320         kprintf(
321         "dscheck(%s): b_bcount %d is not on a sector boundary (ssize %d)\n",
322             devtoname(dev), bp->b_bcount, ssp->dss_secsize);
323         goto bad;
324
325 bad_blkno:
326         kprintf(
327         "dscheck(%s): bio_offset %lld is not on a sector boundary (ssize %d)\n",
328             devtoname(dev), bio->bio_offset, ssp->dss_secsize);
329 bad:
330         bp->b_error = EINVAL;
331         /* fall through */
332 error:
333         /*
334          * Terminate the I/O with a ranging error.  Since the buffer is
335          * either illegal or beyond the file EOF, mark it B_INVAL as well.
336          */
337         bp->b_resid = bp->b_bcount;
338         bp->b_flags |= B_ERROR | B_INVAL;
339 done:
340         /*
341          * Caller must biodone() the originally passed bio if NULL is
342          * returned.
343          */
344         return (NULL);
345 }
346
347 void
348 dsclose(cdev_t dev, int mode, struct diskslices *ssp)
349 {
350         u_char mask;
351         struct diskslice *sp;
352
353         sp = &ssp->dss_slices[dkslice(dev)];
354         mask = 1 << dkpart(dev);
355         sp->ds_openmask &= ~mask;
356 }
357
358 void
359 dsgone(struct diskslices **sspp)
360 {
361         int slice;
362         struct diskslice *sp;
363         struct diskslices *ssp;
364
365         for (slice = 0, ssp = *sspp; slice < ssp->dss_nslices; slice++) {
366                 sp = &ssp->dss_slices[slice];
367                 free_ds_label(ssp, slice);
368         }
369         kfree(ssp, M_DEVBUF);
370         *sspp = NULL;
371 }
372
373 /*
374  * For the "write" commands (DIOCSDINFO and DIOCWDINFO), this
375  * is subject to the same restriction as dsopen().
376  */
377 int
378 dsioctl(cdev_t dev, u_long cmd, caddr_t data, int flags,
379         struct diskslices **sspp, struct disk_info *info)
380 {
381         int error;
382         struct disklabel *lp;
383         int old_wlabel;
384         u_char openmask;
385         int part;
386         int slice;
387         struct diskslice *sp;
388         struct diskslices *ssp;
389         struct partition *pp;
390
391         slice = dkslice(dev);
392         ssp = *sspp;
393         sp = &ssp->dss_slices[slice];
394         lp = sp->ds_label;
395         switch (cmd) {
396
397         case DIOCGDVIRGIN:
398                 lp = (struct disklabel *)data;
399                 if (ssp->dss_slices[WHOLE_DISK_SLICE].ds_label) {
400                         *lp = *ssp->dss_slices[WHOLE_DISK_SLICE].ds_label;
401                 } else {
402                         bzero(lp, sizeof(struct disklabel));
403                 }
404
405                 lp->d_magic = DISKMAGIC;
406                 lp->d_magic2 = DISKMAGIC;
407                 pp = &lp->d_partitions[RAW_PART];
408                 pp->p_offset = 0;
409                 pp->p_size = sp->ds_size;
410
411                 lp->d_npartitions = MAXPARTITIONS;
412                 if (lp->d_interleave == 0)
413                         lp->d_interleave = 1;
414                 if (lp->d_rpm == 0)
415                         lp->d_rpm = 3600;
416                 if (lp->d_nsectors == 0)
417                         lp->d_nsectors = 32;
418                 if (lp->d_ntracks == 0)
419                         lp->d_ntracks = 64;
420
421                 lp->d_bbsize = BBSIZE;
422                 lp->d_sbsize = SBSIZE;
423                 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
424                 lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
425                 lp->d_secperunit = sp->ds_size;
426                 lp->d_checksum = 0;
427                 lp->d_checksum = dkcksum(lp);
428                 return (0);
429
430         case DIOCGDINFO:
431                 if (lp == NULL)
432                         return (EINVAL);
433                 *(struct disklabel *)data = *lp;
434                 return (0);
435
436 #ifdef notyet
437         case DIOCGDINFOP:
438                 if (lp == NULL)
439                         return (EINVAL);
440                 *(struct disklabel **)data = lp;
441                 return (0);
442 #endif
443
444         case DIOCGPART:
445                 {
446                         struct partinfo *dpart = (void *)data;
447
448                         bzero(dpart, sizeof(*dpart));
449                         dpart->media_offset   = (u_int64_t)sp->ds_offset *
450                                                 info->d_media_blksize;
451                         dpart->media_size     = (u_int64_t)sp->ds_size *
452                                                 info->d_media_blksize;
453                         dpart->media_blocks   = sp->ds_size;
454                         dpart->media_blksize  = info->d_media_blksize;
455                         dpart->skip_platform = sp->ds_skip_platform;
456                         dpart->skip_bsdlabel = sp->ds_skip_bsdlabel;
457                         if (lp && slice != WHOLE_DISK_SLICE) {
458                                 struct partition *p;
459
460                                 p = &lp->d_partitions[dkpart(dev)];
461                                 dpart->fstype = p->p_fstype;
462                                 dpart->media_offset += (u_int64_t)p->p_offset *
463                                                        info->d_media_blksize;
464                                 dpart->media_size = (u_int64_t)p->p_size *
465                                                     info->d_media_blksize;
466                                 dpart->media_blocks = (u_int64_t)p->p_size;
467
468                                 /*
469                                  * partition starting sector (p_offset)
470                                  * requires slice's reserved areas to be
471                                  * adjusted.
472                                  */
473                                 if (dpart->skip_platform > p->p_offset)
474                                         dpart->skip_platform -= p->p_offset;
475                                 else
476                                         dpart->skip_platform = 0;
477                                 if (dpart->skip_bsdlabel > p->p_offset)
478                                         dpart->skip_bsdlabel -= p->p_offset;
479                                 else
480                                         dpart->skip_bsdlabel = 0;
481                         }
482                 }
483                 return (0);
484
485         case DIOCGSLICEINFO:
486                 bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] -
487                                  (char *)ssp);
488                 return (0);
489
490         case DIOCSDINFO:
491                 if (slice == WHOLE_DISK_SLICE)
492                         return (ENODEV);
493                 if (!(flags & FWRITE))
494                         return (EBADF);
495                 lp = kmalloc(sizeof *lp, M_DEVBUF, M_WAITOK);
496                 if (sp->ds_label == NULL)
497                         bzero(lp, sizeof *lp);
498                 else
499                         bcopy(sp->ds_label, lp, sizeof *lp);
500                 if (sp->ds_label == NULL) {
501                         openmask = 0;
502                 } else {
503                         openmask = sp->ds_openmask;
504                         if (slice == COMPATIBILITY_SLICE) {
505                                 openmask |= ssp->dss_slices[
506                                     ssp->dss_first_bsd_slice].ds_openmask;
507                         } else if (slice == ssp->dss_first_bsd_slice) {
508                                 openmask |= ssp->dss_slices[
509                                     COMPATIBILITY_SLICE].ds_openmask;
510                         }
511                 }
512                 error = setdisklabel(lp, (struct disklabel *)data,
513                                      (u_long)openmask);
514                 /* XXX why doesn't setdisklabel() check this? */
515                 if (error == 0 && lp->d_partitions[RAW_PART].p_offset != 0)
516                         error = EXDEV;
517                 if (error == 0) {
518                         if (lp->d_secperunit > sp->ds_size)
519                                 error = ENOSPC;
520                         for (part = 0; part < lp->d_npartitions; part++)
521                                 if (lp->d_partitions[part].p_size > sp->ds_size)
522                                         error = ENOSPC;
523                 }
524                 if (error != 0) {
525                         kfree(lp, M_DEVBUF);
526                         return (error);
527                 }
528                 free_ds_label(ssp, slice);
529                 set_ds_label(ssp, slice, lp);
530                 return (0);
531
532         case DIOCSYNCSLICEINFO:
533                 if (slice != WHOLE_DISK_SLICE || dkpart(dev) != RAW_PART)
534                         return (EINVAL);
535                 if (*(int *)data == 0) {
536                         for (slice = 0; slice < ssp->dss_nslices; slice++) {
537                                 openmask = ssp->dss_slices[slice].ds_openmask;
538                                 if (openmask &&
539                                     (slice != WHOLE_DISK_SLICE || 
540                                      openmask & ~(1 << RAW_PART))) {
541                                         return (EBUSY);
542                                 }
543                         }
544                 }
545
546                 /*
547                  * Temporarily forget the current slices struct and read
548                  * the current one.
549                  *
550                  * NOTE:
551                  *
552                  * XXX should wait for current accesses on this disk to
553                  * complete, then lock out future accesses and opens.
554                  */
555                 *sspp = NULL;
556                 lp = kmalloc(sizeof *lp, M_DEVBUF, M_WAITOK);
557                 *lp = *ssp->dss_slices[WHOLE_DISK_SLICE].ds_label;
558                 error = dsopen(dev, S_IFCHR, ssp->dss_oflags, sspp, info);
559                 if (error != 0) {
560                         kfree(lp, M_DEVBUF);
561                         *sspp = ssp;
562                         return (error);
563                 }
564
565                 /*
566                  * Reopen everything.  This is a no-op except in the "force"
567                  * case and when the raw bdev and cdev are both open.  Abort
568                  * if anything fails.
569                  */
570                 for (slice = 0; slice < ssp->dss_nslices; slice++) {
571                         for (openmask = ssp->dss_slices[slice].ds_openmask,
572                              part = 0; openmask; openmask >>= 1, part++) {
573                                 if (!(openmask & 1))
574                                         continue;
575                                 error = dsopen(dkmodslice(dkmodpart(dev, part),
576                                                           slice),
577                                                S_IFCHR, ssp->dss_oflags, sspp,
578                                                info);
579                                 if (error != 0) {
580                                         kfree(lp, M_DEVBUF);
581                                         *sspp = ssp;
582                                         return (EBUSY);
583                                 }
584                         }
585                 }
586
587                 kfree(lp, M_DEVBUF);
588                 dsgone(&ssp);
589                 return (0);
590
591         case DIOCWDINFO:
592                 error = dsioctl(dev, DIOCSDINFO, data, flags, &ssp, info);
593                 if (error != 0)
594                         return (error);
595                 /*
596                  * XXX this used to hack on dk_openpart to fake opening
597                  * partition 0 in case that is used instead of dkpart(dev).
598                  */
599                 old_wlabel = sp->ds_wlabel;
600                 set_ds_wlabel(ssp, slice, TRUE);
601                 error = writedisklabel(dev, sp->ds_label);
602                 /* XXX should invalidate in-core label if write failed. */
603                 set_ds_wlabel(ssp, slice, old_wlabel);
604                 return (error);
605
606         case DIOCWLABEL:
607                 if (slice == WHOLE_DISK_SLICE)
608                         return (ENODEV);
609                 if (!(flags & FWRITE))
610                         return (EBADF);
611                 set_ds_wlabel(ssp, slice, *(int *)data != 0);
612                 return (0);
613
614         default:
615                 return (ENOIOCTL);
616         }
617 }
618
619 /*
620  * Chain the bio_done.  b_cmd remains valid through such chaining.
621  */
622 static void
623 dsiodone(struct bio *bio)
624 {
625         struct buf *bp = bio->bio_buf;
626         char *msg;
627
628         if (bp->b_cmd != BUF_CMD_READ
629             || (!(bp->b_flags & B_ERROR) && bp->b_error == 0)) {
630                 msg = fixlabel(NULL, bio->bio_caller_info1.ptr,
631                                (struct disklabel *)
632                                (bp->b_data + (int)bio->bio_caller_info2.offset),
633                                FALSE);
634                 if (msg != NULL)
635                         kprintf("%s\n", msg);
636         }
637         biodone(bio->bio_prev);
638 }
639
640 int
641 dsisopen(struct diskslices *ssp)
642 {
643         int slice;
644
645         if (ssp == NULL)
646                 return (0);
647         for (slice = 0; slice < ssp->dss_nslices; slice++) {
648                 if (ssp->dss_slices[slice].ds_openmask)
649                         return (1);
650         }
651         return (0);
652 }
653
654 /*
655  * Allocate a slices "struct" and initialize it to contain only an empty
656  * compatibility slice (pointing to itself), a whole disk slice (covering
657  * the disk as described by the label), and (nslices - BASE_SLICES) empty
658  * slices beginning at BASE_SLICE.
659  */
660 struct diskslices *
661 dsmakeslicestruct(int nslices, struct disk_info *info)
662 {
663         struct diskslice *sp;
664         struct diskslices *ssp;
665
666         ssp = kmalloc(offsetof(struct diskslices, dss_slices) +
667                      nslices * sizeof *sp, M_DEVBUF, M_WAITOK);
668         ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
669         ssp->dss_nslices = nslices;
670         ssp->dss_oflags = 0;
671         ssp->dss_secmult = info->d_media_blksize / DEV_BSIZE;
672         if (ssp->dss_secmult & (ssp->dss_secmult - 1))
673                 ssp->dss_secshift = -1;
674         else
675                 ssp->dss_secshift = ffs(ssp->dss_secmult) - 1;
676         ssp->dss_secsize = info->d_media_blksize;
677         sp = &ssp->dss_slices[0];
678         bzero(sp, nslices * sizeof *sp);
679         sp[WHOLE_DISK_SLICE].ds_size = info->d_media_blocks;
680         return (ssp);
681 }
682
683 char *
684 dsname(cdev_t dev, int unit, int slice, int part, char *partname)
685 {
686         static char name[32];
687         const char *dname;
688
689         dname = dev_dname(dev);
690         if (strlen(dname) > 16)
691                 dname = "nametoolong";
692         ksnprintf(name, sizeof(name), "%s%d", dname, unit);
693         partname[0] = '\0';
694         if (slice != WHOLE_DISK_SLICE || part != RAW_PART) {
695                 partname[0] = 'a' + part;
696                 partname[1] = '\0';
697                 if (slice != COMPATIBILITY_SLICE) {
698                         ksnprintf(name + strlen(name),
699                             sizeof(name) - strlen(name), "s%d", slice - 1);
700                 }
701         }
702         return (name);
703 }
704
705 /*
706  * This should only be called when the unit is inactive and the strategy
707  * routine should not allow it to become active unless we call it.  Our
708  * strategy routine must be special to allow activity.
709  */
710 int
711 dsopen(cdev_t dev, int mode, u_int flags, 
712         struct diskslices **sspp, struct disk_info *info)
713 {
714         cdev_t dev1;
715         int error;
716         struct disklabel *lp1;
717         char *msg;
718         u_char mask;
719         bool_t need_init;
720         int part;
721         char partname[2];
722         int slice;
723         char *sname;
724         struct diskslice *sp;
725         struct diskslices *ssp;
726         int unit;
727
728         dev->si_bsize_phys = info->d_media_blksize;
729
730         unit = dkunit(dev);
731         if (info->d_media_blksize % DEV_BSIZE) {
732                 kprintf("%s: invalid sector size %lu\n", devtoname(dev),
733                     (u_long)info->d_media_blksize);
734                 return (EINVAL);
735         }
736
737         /*
738          * Do not attempt to read the slice table or disk label when
739          * accessing the raw disk.
740          */
741         if (dkslice(dev) == WHOLE_DISK_SLICE && dkpart(dev) == RAW_PART) {
742                 flags |= DSO_ONESLICE | DSO_NOLABELS;
743         }
744
745         /*
746          * XXX reinitialize the slice table unless there is an open device
747          * on the unit.  This should only be done if the media has changed.
748          */
749         ssp = *sspp;
750         need_init = !dsisopen(ssp);
751         if (ssp != NULL && need_init)
752                 dsgone(sspp);
753         if (need_init) {
754                 /*
755                  * Allocate a minimal slices "struct".  This will become
756                  * the final slices "struct" if we don't want real slices
757                  * or if we can't find any real slices.
758                  *
759                  * Then scan the disk
760                  */
761                 *sspp = dsmakeslicestruct(BASE_SLICE, info);
762
763                 if (!(flags & DSO_ONESLICE)) {
764                         TRACE(("mbrinit\n"));
765                         error = mbrinit(dev, info, sspp);
766                         if (error != 0) {
767                                 dsgone(sspp);
768                                 return (error);
769                         }
770                 }
771                 ssp = *sspp;
772                 ssp->dss_oflags = flags;
773
774                 /*
775                  * If there are no real slices, then make the compatiblity
776                  * slice cover the whole disk.
777                  *
778                  * no sectors are reserved for the platform (ds_skip_platform
779                  * will be 0) in this case.  This means that if a disklabel
780                  * is installed it will be directly installed in sector 0
781                  * unless DSO_COMPATMBR is requested.
782                  */
783                 if (ssp->dss_nslices == BASE_SLICE) {
784                         sp = &ssp->dss_slices[COMPATIBILITY_SLICE];
785
786                         sp->ds_size = info->d_media_blocks;
787                         if (info->d_dsflags & DSO_COMPATMBR)
788                                 sp->ds_skip_platform = 1;
789                 }
790
791                 /*
792                  * Point the compatibility slice at the BSD slice, if any. 
793                  */
794                 for (slice = BASE_SLICE; slice < ssp->dss_nslices; slice++) {
795                         sp = &ssp->dss_slices[slice];
796                         if (sp->ds_type == DOSPTYP_386BSD /* XXX */) {
797                                 struct diskslice *csp;
798
799                                 csp = &ssp->dss_slices[COMPATIBILITY_SLICE];
800                                 ssp->dss_first_bsd_slice = slice;
801                                 csp->ds_offset = sp->ds_offset;
802                                 csp->ds_size = sp->ds_size;
803                                 csp->ds_type = sp->ds_type;
804                                 csp->ds_skip_platform = sp->ds_skip_platform;
805                                 csp->ds_skip_bsdlabel = sp->ds_skip_bsdlabel;
806                                 break;
807                         }
808                 }
809
810                 /*
811                  * By definition accesses via the whole-disk device do not
812                  * specify any reserved areas.  The whole disk may be read
813                  * or written by the whole-disk device.
814                  *
815                  * XXX do not set a label for the whole disk slice, the
816                  * code should be able to operate without one once we
817                  * fix the virgin label code.
818                  */
819                 sp = &ssp->dss_slices[WHOLE_DISK_SLICE];
820                 sp->ds_label = clone_label(info, NULL);
821                 sp->ds_wlabel = TRUE;
822                 sp->ds_skip_platform = 0;
823                 sp->ds_skip_bsdlabel = 0;
824         }
825
826         /*
827          * Initialize secondary info for all slices.  It is needed for more
828          * than the current slice in the DEVFS case.  XXX DEVFS is no more.
829          *
830          * Attempt to read the disklabel for each slice, creating a virgin
831          * label if a slice does not have one.
832          */
833         for (slice = 0; slice < ssp->dss_nslices; slice++) {
834                 sp = &ssp->dss_slices[slice];
835                 if (sp->ds_label != NULL)
836                         continue;
837                 dev1 = dkmodslice(dkmodpart(dev, RAW_PART), slice);
838                 sname = dsname(dev, unit, slice, RAW_PART, partname);
839                 /*
840                  * XXX this should probably only be done for the need_init
841                  * case, but there may be a problem with DIOCSYNCSLICEINFO.
842                  */
843                 set_ds_wlabel(ssp, slice, TRUE);        /* XXX invert */
844                 lp1 = clone_label(info, sp);
845                 TRACE(("readdisklabel\n"));
846                 if (flags & DSO_NOLABELS) {
847                         msg = NULL;
848                 } else {
849                         msg = readdisklabel(dev1, lp1);
850
851                         /*
852                          * readdisklabel() returns NULL for success, and an
853                          * error string for failure.
854                          *
855                          * If there isn't a label on the disk, and if the
856                          * DSO_COMPATLABEL is set, we want to use the
857                          * faked-up label provided by the caller.
858                          *
859                          * So we set msg to NULL to indicate that there is
860                          * no failure (since we have a faked-up label),
861                          * free lp1, and then clone it again from lp.
862                          * (In case readdisklabel() modified lp1.)
863                          */
864                         if (msg != NULL && (flags & DSO_COMPATLABEL)) {
865                                 msg = NULL;
866                                 kfree(lp1, M_DEVBUF);
867                                 lp1 = clone_label(info, sp);
868                         }
869                 }
870                 if (msg == NULL)
871                         msg = fixlabel(sname, sp, lp1, FALSE);
872                 if (msg == NULL && lp1->d_secsize != ssp->dss_secsize)
873                         msg = "inconsistent sector size";
874                 if (msg != NULL) {
875                         if (sp->ds_type == DOSPTYP_386BSD /* XXX */)
876                                 log(LOG_WARNING, "%s: cannot find label (%s)\n",
877                                     sname, msg);
878                         kfree(lp1, M_DEVBUF);
879                         continue;
880                 }
881                 set_ds_label(ssp, slice, lp1);
882                 set_ds_wlabel(ssp, slice, FALSE);
883         }
884
885         slice = dkslice(dev);
886         if (slice >= ssp->dss_nslices)
887                 return (ENXIO);
888         sp = &ssp->dss_slices[slice];
889         part = dkpart(dev);
890         if (part != RAW_PART
891             && (sp->ds_label == NULL || part >= sp->ds_label->d_npartitions))
892                 return (EINVAL);        /* XXX needs translation */
893         mask = 1 << part;
894         sp->ds_openmask |= mask;
895         return (0);
896 }
897
898 int64_t
899 dssize(cdev_t dev, struct diskslices **sspp)
900 {
901         struct disklabel *lp;
902         int part;
903         int slice;
904         struct diskslices *ssp;
905
906         slice = dkslice(dev);
907         part = dkpart(dev);
908         ssp = *sspp;
909         if (ssp == NULL || slice >= ssp->dss_nslices
910             || !(ssp->dss_slices[slice].ds_openmask & (1 << part))) {
911                 if (dev_dopen(dev, FREAD, S_IFCHR, proc0.p_ucred) != 0)
912                         return (-1);
913                 dev_dclose(dev, FREAD, S_IFCHR);
914                 ssp = *sspp;
915         }
916         lp = ssp->dss_slices[slice].ds_label;
917         if (lp == NULL)
918                 return (-1);
919         return ((int64_t)lp->d_partitions[part].p_size);
920 }
921
922 static void
923 free_ds_label(struct diskslices *ssp, int slice)
924 {
925         struct disklabel *lp;
926         struct diskslice *sp;
927
928         sp = &ssp->dss_slices[slice];
929         lp = sp->ds_label;
930         if (lp == NULL)
931                 return;
932         kfree(lp, M_DEVBUF);
933         set_ds_label(ssp, slice, (struct disklabel *)NULL);
934 }
935
936 static char *
937 fixlabel(char *sname, struct diskslice *sp, struct disklabel *lp, int writeflag)
938 {
939         u_int64_t start;
940         u_int64_t end;
941         u_int64_t offset;
942         int part;
943         struct partition *pp;
944         bool_t warned;
945
946         /* These errors "can't happen" so don't bother reporting details. */
947         if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC)
948                 return ("fixlabel: invalid magic");
949         if (dkcksum(lp) != 0)
950                 return ("fixlabel: invalid checksum");
951
952         pp = &lp->d_partitions[RAW_PART];
953         if (writeflag) {
954                 start = 0;
955                 offset = sp->ds_offset;
956         } else {
957                 start = sp->ds_offset;
958                 offset = -sp->ds_offset;
959         }
960         if (pp->p_offset != start) {
961                 if (sname != NULL) {
962                         kprintf(
963 "%s: rejecting BSD label: raw partition offset != slice offset\n",
964                                sname);
965                         slice_info(sname, sp);
966                         partition_info(sname, RAW_PART, pp);
967                 }
968                 return ("fixlabel: raw partition offset != slice offset");
969         }
970         if (pp->p_size != sp->ds_size) {
971                 if (sname != NULL) {
972                         kprintf("%s: raw partition size != slice size\n", sname);
973                         slice_info(sname, sp);
974                         partition_info(sname, RAW_PART, pp);
975                 }
976                 if (pp->p_size > sp->ds_size) {
977                         if (sname == NULL)
978                                 return ("fixlabel: raw partition size > slice size");
979                         kprintf("%s: truncating raw partition\n", sname);
980                         pp->p_size = sp->ds_size;
981                 }
982         }
983         end = start + sp->ds_size;
984         if (start > end)
985                 return ("fixlabel: slice wraps");
986         if (lp->d_secpercyl <= 0)
987                 return ("fixlabel: d_secpercyl <= 0");
988         pp -= RAW_PART;
989         warned = FALSE;
990         for (part = 0; part < lp->d_npartitions; part++, pp++) {
991                 if (pp->p_offset != 0 || pp->p_size != 0) {
992                         if (pp->p_offset < start
993                             || pp->p_offset + pp->p_size > end
994                             || pp->p_offset + pp->p_size < pp->p_offset) {
995                                 if (sname != NULL) {
996                                         kprintf(
997 "%s: rejecting partition in BSD label: it isn't entirely within the slice\n",
998                                                sname);
999                                         if (!warned) {
1000                                                 slice_info(sname, sp);
1001                                                 warned = TRUE;
1002                                         }
1003                                         partition_info(sname, part, pp);
1004                                 }
1005                                 /* XXX else silently discard junk. */
1006                                 bzero(pp, sizeof *pp);
1007                         } else
1008                                 pp->p_offset += offset;
1009                 }
1010         }
1011         lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
1012         lp->d_secperunit = sp->ds_size;
1013         lp->d_checksum = 0;
1014         lp->d_checksum = dkcksum(lp);
1015         return (NULL);
1016 }
1017
1018 static void
1019 partition_info(char *sname, int part, struct partition *pp)
1020 {
1021         kprintf("%s%c: start %lu, end %lu, size %lu\n", sname, 'a' + part,
1022                (u_long)pp->p_offset, (u_long)(pp->p_offset + pp->p_size - 1),
1023                (u_long)pp->p_size);
1024 }
1025
1026 static void
1027 slice_info(char *sname, struct diskslice *sp)
1028 {
1029         kprintf("%s: start %llu, end %llu, size %llu\n", sname,
1030                sp->ds_offset, sp->ds_offset + sp->ds_size - 1, sp->ds_size);
1031 }
1032
1033 static void
1034 set_ds_label(struct diskslices *ssp, int slice, struct disklabel *lp)
1035 {
1036         struct diskslice *sp1 = &ssp->dss_slices[slice];
1037         struct diskslice *sp2;
1038
1039         if (slice == COMPATIBILITY_SLICE)
1040                 sp2 = &ssp->dss_slices[ssp->dss_first_bsd_slice];
1041         else if (slice == ssp->dss_first_bsd_slice)
1042                 sp2 = &ssp->dss_slices[COMPATIBILITY_SLICE];
1043         else
1044                 sp2 = NULL;
1045         sp1->ds_label = lp;
1046         if (sp2)
1047                 sp2->ds_label = lp;
1048
1049         /*
1050          * If the slice is not the whole-disk slice, setup the reserved
1051          * area(s).
1052          *
1053          * The reserved area for the original bsd disklabel, inclusive of
1054          * the label and space for boot2, is 15 sectors.  If you've
1055          * noticed people traditionally skipping 16 sectors its because
1056          * the sector numbers start at the beginning of the slice rather
1057          * then the beginning of the disklabel and traditional dos slices
1058          * reserve a sector at the beginning for the boot code.
1059          *
1060          * NOTE! With the traditional bsdlabel, the first N bytes of boot2
1061          * overlap with the disklabel.  The disklabel program checks that
1062          * they are 0.
1063          */
1064         if (slice != WHOLE_DISK_SLICE) {
1065                 sp1->ds_skip_bsdlabel = sp1->ds_skip_platform + 15;
1066                 if (sp2)
1067                         sp2->ds_skip_bsdlabel = sp1->ds_skip_bsdlabel;
1068         }
1069 }
1070
1071 static void
1072 set_ds_wlabel(struct diskslices *ssp, int slice, int wlabel)
1073 {
1074         ssp->dss_slices[slice].ds_wlabel = wlabel;
1075         if (slice == COMPATIBILITY_SLICE)
1076                 ssp->dss_slices[ssp->dss_first_bsd_slice].ds_wlabel = wlabel;
1077         else if (slice == ssp->dss_first_bsd_slice)
1078                 ssp->dss_slices[COMPATIBILITY_SLICE].ds_wlabel = wlabel;
1079 }