Continue untangling the disklabel.
[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.35 2007/05/19 00:52:01 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 (const char *sname, struct diskslice *sp,
80                            struct disklabel *lp, int writeflag);
81 static int  dsreadandsetlabel(cdev_t dev, u_int flags,
82                            struct diskslices *ssp, struct diskslice *sp,
83                            struct disk_info *info);
84 static void free_ds_label (struct diskslices *ssp, int slice);
85 static void partition_info (const char *sname, int part, struct partition *pp);
86 static void slice_info (const char *sname, struct diskslice *sp);
87 static void set_ds_label (struct diskslices *ssp, int slice,
88                               struct disklabel *lp);
89 static void set_ds_wlabel (struct diskslices *ssp, int slice, int wlabel);
90
91 /*
92  * Create a disklabel based on a disk_info structure, initializing
93  * the appropriate fields and creating a raw partition that covers the
94  * whole disk.
95  *
96  * If a diskslice is passed, the label is truncated to the slice
97  */
98 static struct disklabel *
99 clone_label(struct disk_info *info, struct diskslice *sp)
100 {
101         struct disklabel *lp1;
102
103         lp1 = kmalloc(sizeof *lp1, M_DEVBUF, M_WAITOK | M_ZERO);
104         lp1->d_nsectors = info->d_secpertrack;
105         lp1->d_ntracks = info->d_nheads;
106         lp1->d_secpercyl = info->d_secpercyl;
107         lp1->d_secsize = info->d_media_blksize;
108
109         if (sp) {
110                 lp1->d_secperunit = (u_int)sp->ds_size;
111                 lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
112         } else {
113                 lp1->d_secperunit = (u_int)info->d_media_blocks;
114                 lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
115         }
116
117         /*
118          * Used by the CD driver to create a compatibility slice which
119          * allows us to mount root from the CD.
120          */
121         if (info->d_dsflags & DSO_COMPATPARTA) {
122                 lp1->d_partitions[0].p_size = lp1->d_secperunit;
123                 lp1->d_partitions[0].p_fstype = FS_OTHER;
124         }
125
126         if (lp1->d_typename[0] == '\0')
127                 strncpy(lp1->d_typename, "amnesiac", sizeof(lp1->d_typename));
128         if (lp1->d_packname[0] == '\0')
129                 strncpy(lp1->d_packname, "fictitious", sizeof(lp1->d_packname));
130         if (lp1->d_nsectors == 0)
131                 lp1->d_nsectors = 32;
132         if (lp1->d_ntracks == 0)
133                 lp1->d_ntracks = 64;
134         lp1->d_secpercyl = lp1->d_nsectors * lp1->d_ntracks;
135         lp1->d_ncylinders = lp1->d_secperunit / lp1->d_secpercyl;
136         if (lp1->d_rpm == 0)
137                 lp1->d_rpm = 3600;
138         if (lp1->d_interleave == 0)
139                 lp1->d_interleave = 1;
140         if (lp1->d_npartitions < RAW_PART + 1)
141                 lp1->d_npartitions = MAXPARTITIONS;
142         if (lp1->d_bbsize == 0)
143                 lp1->d_bbsize = BBSIZE;
144         if (lp1->d_sbsize == 0)
145                 lp1->d_sbsize = SBSIZE;
146         lp1->d_partitions[RAW_PART].p_size = lp1->d_secperunit;
147         lp1->d_magic = DISKMAGIC;
148         lp1->d_magic2 = DISKMAGIC;
149         lp1->d_checksum = dkcksum(lp1);
150         return (lp1);
151 }
152
153 /*
154  * Determine the size of the transfer, and make sure it is
155  * within the boundaries of the partition. Adjust transfer
156  * if needed, and signal errors or early completion.
157  *
158  * XXX TODO:
159  *      o Split buffers that are too big for the device.
160  *      o Check for overflow.
161  *      o Finish cleaning this up.
162  *
163  * This function returns 1 on success, 0 if transfer equates
164  * to EOF (end of disk) or -1 on failure.  The appropriate 
165  * 'errno' value is also set in bp->b_error and bp->b_flags
166  * is marked with B_ERROR.
167  */
168 struct bio *
169 dscheck(cdev_t dev, struct bio *bio, struct diskslices *ssp)
170 {
171         struct buf *bp = bio->bio_buf;
172         struct bio *nbio;
173         struct disklabel *lp;
174         char *msg;
175         long nsec;
176         u_int64_t secno;
177         u_int64_t endsecno;
178         u_int64_t labelsect;
179         u_int64_t slicerel_secno;
180         struct diskslice *sp;
181         u_int32_t part;
182         u_int32_t slice;
183         int shift;
184         int mask;
185
186         slice = dkslice(dev);
187         part  = dkpart(dev);
188
189         if (bio->bio_offset < 0) {
190                 kprintf("dscheck(%s): negative bio_offset %lld\n", 
191                         devtoname(dev), bio->bio_offset);
192                 goto bad;
193         }
194         if (slice >= ssp->dss_nslices) {
195                 kprintf("dscheck(%s): slice too large %d/%d\n",
196                         devtoname(dev), slice, ssp->dss_nslices);
197                 goto bad;
198         }
199         sp = &ssp->dss_slices[slice];
200
201         /*
202          * Calculate secno and nsec
203          */
204         if (ssp->dss_secmult == 1) {
205                 shift = DEV_BSHIFT;
206                 goto doshift;
207         } else if (ssp->dss_secshift != -1) {
208                 shift = DEV_BSHIFT + ssp->dss_secshift;
209 doshift:
210                 mask = (1 << shift) - 1;
211                 if ((int)bp->b_bcount & mask)
212                         goto bad_bcount;
213                 if ((int)bio->bio_offset & mask)
214                         goto bad_blkno;
215                 secno = bio->bio_offset >> shift;
216                 nsec = bp->b_bcount >> shift;
217         } else {
218                 if (bp->b_bcount % ssp->dss_secsize)
219                         goto bad_bcount;
220                 if (bio->bio_offset % ssp->dss_secsize)
221                         goto bad_blkno;
222                 secno = bio->bio_offset / ssp->dss_secsize;
223                 nsec = bp->b_bcount / ssp->dss_secsize;
224         }
225
226         /*
227          * Calculate slice-relative sector number end slice-relative
228          * limit.
229          */
230         lp = sp->ds_label;
231         if (slice == WHOLE_DISK_SLICE || lp == NULL) {
232                 /*
233                  * Labels have not been allowed on whole-disks for a while.
234                  * This really puts the nail in the coffin... no disk
235                  * snooping will occur even if you tried to write a label
236                  * without a slice structure.
237                  */
238                 lp = NULL;
239                 labelsect = -LABELSECTOR - 1;
240                 endsecno = sp->ds_size;
241                 slicerel_secno = secno;
242         } else if (part == WHOLE_SLICE_PART) {
243                 /*
244                  * XXX messy snoop case.  Both disklabel -r -w and
245                  * DIOCWDINFO depend on write snooping to properly
246                  * correct the partition offsets for the label being
247                  * written to disk.  In both cases ds_label will be
248                  * non-NULL.  This is a mess because it violates the
249                  * idea of being able to write to the raw slice.
250                  */
251                 lp = NULL;
252                 labelsect = 0;
253                 endsecno = sp->ds_size;
254                 slicerel_secno = secno;
255         } else if (part >= lp->d_npartitions) {
256                 kprintf("dscheck(%s): partition out of bounds %d/%d\n",
257                         devtoname(dev),
258                         part, lp->d_npartitions);
259                 goto bad;
260         } else {
261                 struct partition *pp;
262
263                 labelsect = 0;
264                 pp = &lp->d_partitions[dkpart(dev)];
265                 endsecno = pp->p_size;
266                 slicerel_secno = pp->p_offset + secno;
267         }
268
269         /* overwriting disk label ? */
270         /* XXX should also protect bootstrap in first 8K */
271         if (slicerel_secno <= LABELSECTOR + labelsect &&
272 #if LABELSECTOR != 0
273             slicerel_secno + nsec > LABELSECTOR + labelsect &&
274 #endif
275             bp->b_cmd != BUF_CMD_READ && sp->ds_wlabel == 0) {
276                 bp->b_error = EROFS;
277                 goto error;
278         }
279
280 #if defined(DOSBBSECTOR) && defined(notyet)
281         /* overwriting master boot record? */
282         if (slicerel_secno <= DOSBBSECTOR && bp->b_cmd != BUF_CMD_READ &&
283             sp->ds_wlabel == 0) {
284                 bp->b_error = EROFS;
285                 goto error;
286         }
287 #endif
288
289         /*
290          * EOF handling
291          */
292         if (secno + nsec > endsecno) {
293                 /*
294                  * Return an error if beyond the end of the disk, or
295                  * if B_BNOCLIP is set.  Tell the system that we do not
296                  * need to keep the buffer around.
297                  */
298                 if (secno > endsecno || (bp->b_flags & B_BNOCLIP))
299                         goto bad;
300
301                 /*
302                  * If exactly at end of disk, return an EOF.  Throw away
303                  * the buffer contents, if any, by setting B_INVAL.
304                  */
305                 if (secno == endsecno) {
306                         bp->b_resid = bp->b_bcount;
307                         bp->b_flags |= B_INVAL;
308                         goto done;
309                 }
310
311                 /*
312                  * Else truncate
313                  */
314                 nsec = endsecno - secno;
315                 bp->b_bcount = nsec * ssp->dss_secsize;
316         }
317
318         nbio = push_bio(bio);
319         nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) * 
320                            ssp->dss_secsize;
321
322         /*
323          * Snoop on label accesses if the slice offset is nonzero.  Fudge
324          * offsets in the label to keep the in-core label coherent with
325          * the on-disk one.
326          */
327         if (slicerel_secno <= LABELSECTOR + labelsect
328 #if LABELSECTOR != 0
329             && slicerel_secno + nsec > LABELSECTOR + labelsect
330 #endif
331             && sp->ds_offset != 0) {
332                 nbio->bio_done = dsiodone;
333                 nbio->bio_caller_info1.ptr = sp;
334                 nbio->bio_caller_info2.offset = 
335                         (off_t)(LABELSECTOR + labelsect - slicerel_secno) *
336                         ssp->dss_secsize;
337                 if (bp->b_cmd != BUF_CMD_READ) {
338                         /*
339                          * XXX even disklabel(8) writes directly so we need
340                          * to adjust writes.  Perhaps we should drop support
341                          * for DIOCWLABEL (always write protect labels) and
342                          * require the use of DIOCWDINFO.
343                          *
344                          * XXX probably need to copy the data to avoid even
345                          * temporarily corrupting the in-core copy.
346                          */
347                         /* XXX need name here. */
348                         kprintf("SNOOP LABEL WRITE\n");
349                         msg = fixlabel(
350                                 NULL, sp,
351                                (struct disklabel *)
352                                (bp->b_data + (int)nbio->bio_caller_info2.offset),
353                                TRUE);
354                         if (msg != NULL) {
355                                 kprintf("dscheck(%s): %s\n", 
356                                     devtoname(dev), msg);
357                                 bp->b_error = EROFS;
358                                 pop_bio(nbio);
359                                 goto error;
360                         }
361                 }
362         }
363         return (nbio);
364
365 bad_bcount:
366         kprintf(
367         "dscheck(%s): b_bcount %d is not on a sector boundary (ssize %d)\n",
368             devtoname(dev), bp->b_bcount, ssp->dss_secsize);
369         goto bad;
370
371 bad_blkno:
372         kprintf(
373         "dscheck(%s): bio_offset %lld is not on a sector boundary (ssize %d)\n",
374             devtoname(dev), bio->bio_offset, ssp->dss_secsize);
375 bad:
376         bp->b_error = EINVAL;
377         /* fall through */
378 error:
379         /*
380          * Terminate the I/O with a ranging error.  Since the buffer is
381          * either illegal or beyond the file EOF, mark it B_INVAL as well.
382          */
383         bp->b_resid = bp->b_bcount;
384         bp->b_flags |= B_ERROR | B_INVAL;
385 done:
386         /*
387          * Caller must biodone() the originally passed bio if NULL is
388          * returned.
389          */
390         return (NULL);
391 }
392
393 void
394 dsclose(cdev_t dev, int mode, struct diskslices *ssp)
395 {
396         u_int32_t part;
397         u_int32_t slice;
398         struct diskslice *sp;
399
400         slice = dkslice(dev);
401         part  = dkpart(dev);
402         if (slice < ssp->dss_nslices) {
403                 sp = &ssp->dss_slices[slice];
404                 if (part < sizeof(sp->ds_openmask) * 8)
405                         sp->ds_openmask &= ~(1 << part);
406         }
407 }
408
409 void
410 dsgone(struct diskslices **sspp)
411 {
412         int slice;
413         struct diskslice *sp;
414         struct diskslices *ssp;
415
416         for (slice = 0, ssp = *sspp; slice < ssp->dss_nslices; slice++) {
417                 sp = &ssp->dss_slices[slice];
418                 free_ds_label(ssp, slice);
419         }
420         kfree(ssp, M_DEVBUF);
421         *sspp = NULL;
422 }
423
424 /*
425  * For the "write" commands (DIOCSDINFO and DIOCWDINFO), this
426  * is subject to the same restriction as dsopen().
427  */
428 int
429 dsioctl(cdev_t dev, u_long cmd, caddr_t data, int flags,
430         struct diskslices **sspp, struct disk_info *info)
431 {
432         int error;
433         struct disklabel *lp;
434         int old_wlabel;
435         u_char openmask;
436         int part;
437         int slice;
438         struct diskslice *sp;
439         struct diskslices *ssp;
440         struct partition *pp;
441
442         slice = dkslice(dev);
443         part = dkpart(dev);
444         ssp = *sspp;
445         if (slice >= ssp->dss_nslices)
446                 return (EINVAL);
447         sp = &ssp->dss_slices[slice];
448         lp = sp->ds_label;
449         switch (cmd) {
450
451         case DIOCGDVIRGIN:
452                 /*
453                  * You can only retrieve a virgin disklabel on the whole
454                  * disk slice or whole-slice partition.
455                  */
456                 if (slice != WHOLE_DISK_SLICE &&
457                     part != WHOLE_SLICE_PART) {
458                         return(EINVAL);
459                 }
460
461                 lp = (struct disklabel *)data;
462                 if (ssp->dss_slices[WHOLE_DISK_SLICE].ds_label) {
463                         *lp = *ssp->dss_slices[WHOLE_DISK_SLICE].ds_label;
464                 } else {
465                         bzero(lp, sizeof(struct disklabel));
466                 }
467
468                 lp->d_magic = DISKMAGIC;
469                 lp->d_magic2 = DISKMAGIC;
470                 pp = &lp->d_partitions[RAW_PART];
471                 pp->p_offset = 0;
472                 pp->p_size = sp->ds_size;
473
474                 lp->d_npartitions = MAXPARTITIONS;
475                 if (lp->d_interleave == 0)
476                         lp->d_interleave = 1;
477                 if (lp->d_rpm == 0)
478                         lp->d_rpm = 3600;
479                 if (lp->d_nsectors == 0)
480                         lp->d_nsectors = 32;
481                 if (lp->d_ntracks == 0)
482                         lp->d_ntracks = 64;
483
484                 lp->d_bbsize = BBSIZE;
485                 lp->d_sbsize = SBSIZE;
486                 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
487                 lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
488                 lp->d_secperunit = sp->ds_size;
489                 lp->d_checksum = 0;
490                 lp->d_checksum = dkcksum(lp);
491                 return (0);
492
493         case DIOCGDINFO:
494                 /*
495                  * You can only retrieve a disklabel on the whole
496                  * slice partition.
497                  *
498                  * We do not support labels directly on whole-disks
499                  * any more (that is, disks without slices), unless the
500                  * device driver has asked for a compatible label (e.g.
501                  * for a CD) to allow booting off of storage that is
502                  * otherwise unlabeled.
503                  */
504                 error = 0;
505                 if (part != WHOLE_SLICE_PART)
506                         return(EINVAL);
507                 if (slice == WHOLE_DISK_SLICE &&
508                     (info->d_dsflags & DSO_COMPATLABEL) == 0) {
509                         return (ENODEV);
510                 }
511                 if (sp->ds_label == NULL) {
512                         error = dsreadandsetlabel(dev, info->d_dsflags,
513                                                   ssp, sp, info);
514                 }
515                 if (error == 0)
516                         *(struct disklabel *)data = *sp->ds_label;
517                 return (error);
518
519         case DIOCGPART:
520                 {
521                         struct partinfo *dpart = (void *)data;
522
523                         bzero(dpart, sizeof(*dpart));
524                         dpart->media_offset   = (u_int64_t)sp->ds_offset *
525                                                 info->d_media_blksize;
526                         dpart->media_size     = (u_int64_t)sp->ds_size *
527                                                 info->d_media_blksize;
528                         dpart->media_blocks   = sp->ds_size;
529                         dpart->media_blksize  = info->d_media_blksize;
530                         dpart->skip_platform = sp->ds_skip_platform;
531                         dpart->skip_bsdlabel = sp->ds_skip_bsdlabel;
532
533                         if (slice != WHOLE_DISK_SLICE &&
534                             part != WHOLE_SLICE_PART) {
535                                 struct partition *p;
536
537                                 if (lp == NULL || part >= lp->d_npartitions)
538                                         return(EINVAL);
539
540                                 p = &lp->d_partitions[part];
541                                 dpart->fstype = p->p_fstype;
542                                 dpart->media_offset += (u_int64_t)p->p_offset *
543                                                        info->d_media_blksize;
544                                 dpart->media_size = (u_int64_t)p->p_size *
545                                                     info->d_media_blksize;
546                                 dpart->media_blocks = (u_int64_t)p->p_size;
547
548                                 /*
549                                  * partition starting sector (p_offset)
550                                  * requires slice's reserved areas to be
551                                  * adjusted.
552                                  */
553                                 if (dpart->skip_platform > p->p_offset)
554                                         dpart->skip_platform -= p->p_offset;
555                                 else
556                                         dpart->skip_platform = 0;
557                                 if (dpart->skip_bsdlabel > p->p_offset)
558                                         dpart->skip_bsdlabel -= p->p_offset;
559                                 else
560                                         dpart->skip_bsdlabel = 0;
561                         }
562                 }
563                 return (0);
564
565         case DIOCGSLICEINFO:
566                 bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] -
567                                  (char *)ssp);
568                 return (0);
569
570         case DIOCSDINFO:
571                 /*
572                  * You can write a disklabel on the whole disk slice or
573                  * whole-slice partition.
574                  */
575                 if (slice != WHOLE_DISK_SLICE &&
576                     part != WHOLE_SLICE_PART) {
577                         return(EINVAL);
578                 }
579
580                 /*
581                  * We no longer support writing disklabels directly to media
582                  * without there being a slice.  Keep this as a separate
583                  * conditional.
584                  */
585                 if (slice == WHOLE_DISK_SLICE)
586                         return (ENODEV);
587
588                 if (!(flags & FWRITE))
589                         return (EBADF);
590                 lp = kmalloc(sizeof *lp, M_DEVBUF, M_WAITOK);
591                 if (sp->ds_label == NULL)
592                         bzero(lp, sizeof *lp);
593                 else
594                         bcopy(sp->ds_label, lp, sizeof *lp);
595                 if (sp->ds_label == NULL) {
596                         openmask = 0;
597                 } else {
598                         openmask = sp->ds_openmask;
599                         if (slice == COMPATIBILITY_SLICE) {
600                                 openmask |= ssp->dss_slices[
601                                     ssp->dss_first_bsd_slice].ds_openmask;
602                         } else if (slice == ssp->dss_first_bsd_slice) {
603                                 openmask |= ssp->dss_slices[
604                                     COMPATIBILITY_SLICE].ds_openmask;
605                         }
606                 }
607                 error = setdisklabel(lp, (struct disklabel *)data,
608                                      (u_long)openmask);
609                 /* XXX why doesn't setdisklabel() check this? */
610                 if (error == 0 && lp->d_partitions[RAW_PART].p_offset != 0)
611                         error = EXDEV;
612                 if (error == 0) {
613                         if (lp->d_secperunit > sp->ds_size)
614                                 error = ENOSPC;
615                         for (part = 0; part < lp->d_npartitions; part++)
616                                 if (lp->d_partitions[part].p_size > sp->ds_size)
617                                         error = ENOSPC;
618                 }
619                 if (error != 0) {
620                         kfree(lp, M_DEVBUF);
621                         return (error);
622                 }
623                 free_ds_label(ssp, slice);
624                 set_ds_label(ssp, slice, lp);
625                 return (0);
626
627         case DIOCSYNCSLICEINFO:
628                 /*
629                  * This ioctl can only be done on the whole disk
630                  */
631                 if (slice != WHOLE_DISK_SLICE || part != WHOLE_SLICE_PART)
632                         return (EINVAL);
633
634                 if (*(int *)data == 0) {
635                         for (slice = 0; slice < ssp->dss_nslices; slice++) {
636                                 openmask = ssp->dss_slices[slice].ds_openmask;
637                                 if (openmask &&
638                                     (slice != WHOLE_DISK_SLICE || 
639                                      openmask & ~(1 << RAW_PART))) {
640                                         return (EBUSY);
641                                 }
642                         }
643                 }
644
645                 /*
646                  * Temporarily forget the current slices struct and read
647                  * the current one.
648                  *
649                  * NOTE:
650                  *
651                  * XXX should wait for current accesses on this disk to
652                  * complete, then lock out future accesses and opens.
653                  */
654                 *sspp = NULL;
655                 lp = kmalloc(sizeof *lp, M_DEVBUF, M_WAITOK);
656                 *lp = *ssp->dss_slices[WHOLE_DISK_SLICE].ds_label;
657                 error = dsopen(dev, S_IFCHR, ssp->dss_oflags, sspp, info);
658                 if (error != 0) {
659                         kfree(lp, M_DEVBUF);
660                         *sspp = ssp;
661                         return (error);
662                 }
663
664                 /*
665                  * Reopen everything.  This is a no-op except in the "force"
666                  * case and when the raw bdev and cdev are both open.  Abort
667                  * if anything fails.
668                  */
669                 for (slice = 0; slice < ssp->dss_nslices; slice++) {
670                         for (openmask = ssp->dss_slices[slice].ds_openmask,
671                              part = 0; openmask; openmask >>= 1, part++) {
672                                 if (!(openmask & 1))
673                                         continue;
674                                 error = dsopen(dkmodslice(dkmodpart(dev, part),
675                                                           slice),
676                                                S_IFCHR, ssp->dss_oflags, sspp,
677                                                info);
678                                 if (error != 0) {
679                                         kfree(lp, M_DEVBUF);
680                                         *sspp = ssp;
681                                         return (EBUSY);
682                                 }
683                         }
684                 }
685
686                 kfree(lp, M_DEVBUF);
687                 dsgone(&ssp);
688                 return (0);
689
690         case DIOCWDINFO:
691                 error = dsioctl(dev, DIOCSDINFO, data, flags, &ssp, info);
692                 if (error != 0)
693                         return (error);
694                 /*
695                  * XXX this used to hack on dk_openpart to fake opening
696                  * partition 0 in case that is used instead of dkpart(dev).
697                  */
698                 old_wlabel = sp->ds_wlabel;
699                 set_ds_wlabel(ssp, slice, TRUE);
700                 error = writedisklabel(dev, sp->ds_label);
701                 /* XXX should invalidate in-core label if write failed. */
702                 set_ds_wlabel(ssp, slice, old_wlabel);
703                 return (error);
704
705         case DIOCWLABEL:
706                 if (slice == WHOLE_DISK_SLICE)
707                         return (ENODEV);
708                 if (!(flags & FWRITE))
709                         return (EBADF);
710                 set_ds_wlabel(ssp, slice, *(int *)data != 0);
711                 return (0);
712
713         default:
714                 return (ENOIOCTL);
715         }
716 }
717
718 /*
719  * Chain the bio_done.  b_cmd remains valid through such chaining.
720  */
721 static void
722 dsiodone(struct bio *bio)
723 {
724         struct buf *bp = bio->bio_buf;
725         char *msg;
726
727         if (bp->b_cmd != BUF_CMD_READ
728             || (!(bp->b_flags & B_ERROR) && bp->b_error == 0)) {
729                 msg = fixlabel(NULL, bio->bio_caller_info1.ptr,
730                                (struct disklabel *)
731                                (bp->b_data + (int)bio->bio_caller_info2.offset),
732                                FALSE);
733                 if (msg != NULL)
734                         kprintf("%s\n", msg);
735         }
736         biodone(bio->bio_prev);
737 }
738
739 int
740 dsisopen(struct diskslices *ssp)
741 {
742         int slice;
743
744         if (ssp == NULL)
745                 return (0);
746         for (slice = 0; slice < ssp->dss_nslices; slice++) {
747                 if (ssp->dss_slices[slice].ds_openmask)
748                         return (1);
749         }
750         return (0);
751 }
752
753 /*
754  * Allocate a slices "struct" and initialize it to contain only an empty
755  * compatibility slice (pointing to itself), a whole disk slice (covering
756  * the disk as described by the label), and (nslices - BASE_SLICES) empty
757  * slices beginning at BASE_SLICE.
758  */
759 struct diskslices *
760 dsmakeslicestruct(int nslices, struct disk_info *info)
761 {
762         struct diskslice *sp;
763         struct diskslices *ssp;
764
765         ssp = kmalloc(offsetof(struct diskslices, dss_slices) +
766                      nslices * sizeof *sp, M_DEVBUF, M_WAITOK);
767         ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
768         ssp->dss_nslices = nslices;
769         ssp->dss_oflags = 0;
770         ssp->dss_secmult = info->d_media_blksize / DEV_BSIZE;
771         if (ssp->dss_secmult & (ssp->dss_secmult - 1))
772                 ssp->dss_secshift = -1;
773         else
774                 ssp->dss_secshift = ffs(ssp->dss_secmult) - 1;
775         ssp->dss_secsize = info->d_media_blksize;
776         sp = &ssp->dss_slices[0];
777         bzero(sp, nslices * sizeof *sp);
778         sp[WHOLE_DISK_SLICE].ds_size = info->d_media_blocks;
779         return (ssp);
780 }
781
782 char *
783 dsname(cdev_t dev, int unit, int slice, int part, char *partname)
784 {
785         static char name[32];
786         const char *dname;
787         int used;
788
789         dname = dev_dname(dev);
790         if (strlen(dname) > 16)
791                 dname = "nametoolong";
792         ksnprintf(name, sizeof(name), "%s%d", dname, unit);
793         partname[0] = '\0';
794         used = strlen(name);
795
796         if (slice != WHOLE_DISK_SLICE) {
797                 if (slice != COMPATIBILITY_SLICE) {
798                         used += ksnprintf(name + used, sizeof(name) - used,
799                                           "s%d", slice - BASE_SLICE + 1);
800                 }
801                 if (part != WHOLE_SLICE_PART) {
802                         used += ksnprintf(name + used, sizeof(name) - used,
803                                           "%c", 'a' + part);
804                         partname[0] = 'a' + part;
805                         partname[1] = 0;
806                 }
807         }
808         return (name);
809 }
810
811 /*
812  * This should only be called when the unit is inactive and the strategy
813  * routine should not allow it to become active unless we call it.  Our
814  * strategy routine must be special to allow activity.
815  */
816 int
817 dsopen(cdev_t dev, int mode, u_int flags, 
818         struct diskslices **sspp, struct disk_info *info)
819 {
820         cdev_t dev1;
821         int error;
822         u_char mask;
823         bool_t need_init;
824         struct diskslice *sp;
825         struct diskslices *ssp;
826         int slice;
827         int part;
828
829         dev->si_bsize_phys = info->d_media_blksize;
830
831         if (info->d_media_blksize % DEV_BSIZE) {
832                 kprintf("%s: invalid sector size %lu\n", devtoname(dev),
833                     (u_long)info->d_media_blksize);
834                 return (EINVAL);
835         }
836
837         /*
838          * Do not attempt to read the slice table or disk label when
839          * accessing the raw disk.
840          */
841         if (dkslice(dev) == WHOLE_DISK_SLICE)
842                 flags |= DSO_ONESLICE | DSO_NOLABELS;
843         if (dkpart(dev) == WHOLE_SLICE_PART)
844                 flags |= DSO_NOLABELS;
845
846         /*
847          * XXX reinitialize the slice table unless there is an open device
848          * on the unit.  This should only be done if the media has changed.
849          */
850         ssp = *sspp;
851         need_init = !dsisopen(ssp);
852         if (ssp != NULL && need_init)
853                 dsgone(sspp);
854         if (need_init) {
855                 /*
856                  * Allocate a minimal slices "struct".  This will become
857                  * the final slices "struct" if we don't want real slices
858                  * or if we can't find any real slices.
859                  *
860                  * Then scan the disk
861                  */
862                 *sspp = dsmakeslicestruct(BASE_SLICE, info);
863
864                 if ((flags & DSO_ONESLICE) == 0) {
865                         TRACE(("mbrinit\n"));
866                         error = mbrinit(dev, info, sspp);
867                         if (error != 0) {
868                                 dsgone(sspp);
869                                 return (error);
870                         }
871                 }
872                 ssp = *sspp;
873                 ssp->dss_oflags = flags;
874
875                 /*
876                  * If there are no real slices, then make the compatiblity
877                  * slice cover the whole disk.
878                  *
879                  * no sectors are reserved for the platform (ds_skip_platform
880                  * will be 0) in this case.  This means that if a disklabel
881                  * is installed it will be directly installed in sector 0
882                  * unless DSO_COMPATMBR is requested.
883                  */
884                 if (ssp->dss_nslices == BASE_SLICE) {
885                         sp = &ssp->dss_slices[COMPATIBILITY_SLICE];
886
887                         sp->ds_size = info->d_media_blocks;
888                         if (info->d_dsflags & DSO_COMPATMBR)
889                                 sp->ds_skip_platform = 1;
890                 }
891
892                 /*
893                  * Point the compatibility slice at the BSD slice, if any. 
894                  */
895                 for (slice = BASE_SLICE; slice < ssp->dss_nslices; slice++) {
896                         sp = &ssp->dss_slices[slice];
897                         if (sp->ds_type == DOSPTYP_386BSD /* XXX */) {
898                                 struct diskslice *csp;
899
900                                 csp = &ssp->dss_slices[COMPATIBILITY_SLICE];
901                                 ssp->dss_first_bsd_slice = slice;
902                                 csp->ds_offset = sp->ds_offset;
903                                 csp->ds_size = sp->ds_size;
904                                 csp->ds_type = sp->ds_type;
905                                 csp->ds_skip_platform = sp->ds_skip_platform;
906                                 csp->ds_skip_bsdlabel = sp->ds_skip_bsdlabel;
907                                 break;
908                         }
909                 }
910
911                 /*
912                  * By definition accesses via the whole-disk device do not
913                  * specify any reserved areas.  The whole disk may be read
914                  * or written by the whole-disk device.
915                  *
916                  * ds_label for a whole-disk device is only used as a
917                  * template.
918                  */
919                 sp = &ssp->dss_slices[WHOLE_DISK_SLICE];
920                 sp->ds_label = clone_label(info, NULL);
921                 sp->ds_wlabel = TRUE;
922                 sp->ds_skip_platform = 0;
923                 sp->ds_skip_bsdlabel = 0;
924         }
925
926         /*
927          * Initialize secondary info for all slices.  It is needed for more
928          * than the current slice in the DEVFS case.  XXX DEVFS is no more.
929          *
930          * Attempt to read the disklabel for each slice, creating a virgin
931          * label if a slice does not have one.
932          */
933         for (slice = 0; slice < ssp->dss_nslices; slice++) {
934                 sp = &ssp->dss_slices[slice];
935                 if (sp->ds_label != NULL)
936                         continue;
937
938                 dev1 = dkmodslice(dkmodpart(dev, WHOLE_SLICE_PART), slice);
939
940                 /*
941                  * If opening a raw disk or raw slice we do not try to
942                  * read the disklabel, and we allow access to the whole
943                  * gambino.
944                  */
945                 set_ds_wlabel(ssp, slice, TRUE);
946                 if ((flags & DSO_NOLABELS) == 0)
947                         dsreadandsetlabel(dev1, flags, ssp, sp, info);
948         }
949
950         slice = dkslice(dev);
951         if (slice >= ssp->dss_nslices)
952                 return (ENXIO);
953         sp = &ssp->dss_slices[slice];
954         part = dkpart(dev);
955
956         /*
957          * If opening a particular partition the disklabel must exist and
958          * the partition must be present in the label.
959          *
960          * If the partition is the special whole-disk-slice no partition
961          * table need exist.
962          */
963         if (part != WHOLE_SLICE_PART && slice != WHOLE_DISK_SLICE) {
964                 if (sp->ds_label == NULL || part >= sp->ds_label->d_npartitions)
965                         return (EINVAL);
966                 if (part < sizeof(sp->ds_openmask) * 8) {
967                         mask = 1 << part;
968                         sp->ds_openmask |= mask;
969                 }
970         }
971
972         /* 
973          * If the slice is the whole-disk slice, the partition must be set
974          * to the whole slice partition.
975          */
976         if (slice == WHOLE_DISK_SLICE && part != WHOLE_SLICE_PART)
977                 return (EINVAL);
978         return (0);
979 }
980
981 /*
982  * Attempt to read the disklabel.  If successful, store it in sp->ds_label.
983  *
984  * If we cannot read the disklabel and DSO_COMPATLABEL is set, we construct
985  * a fake label covering the whole disk.
986  */
987 static
988 int
989 dsreadandsetlabel(cdev_t dev, u_int flags,
990                   struct diskslices *ssp, struct diskslice *sp,
991                   struct disk_info *info)
992 {
993         struct disklabel *lp1;
994         const char *msg;
995         const char *sname;
996         char partname[2];
997         int slice = dkslice(dev);
998
999         sname = dsname(dev, dkunit(dev), slice, WHOLE_SLICE_PART, partname);
1000         lp1 = clone_label(info, sp);
1001         msg = readdisklabel(dev, lp1);
1002
1003         if (msg != NULL && (flags & DSO_COMPATLABEL)) {
1004                 msg = NULL;
1005                 kfree(lp1, M_DEVBUF);
1006                 lp1 = clone_label(info, sp);
1007         }
1008         if (msg == NULL)
1009                 msg = fixlabel(sname, sp, lp1, FALSE);
1010         if (msg == NULL && lp1->d_secsize != info->d_media_blksize)
1011                 msg = "inconsistent sector size";
1012         if (msg != NULL) {
1013                 if (sp->ds_type == DOSPTYP_386BSD /* XXX */)
1014                         log(LOG_WARNING, "%s: cannot find label (%s)\n",
1015                             sname, msg);
1016                 kfree(lp1, M_DEVBUF);
1017         } else {
1018                 set_ds_label(ssp, slice, lp1);
1019                 set_ds_wlabel(ssp, slice, FALSE);
1020         }
1021         return (msg ? EINVAL : 0);
1022 }
1023
1024 int64_t
1025 dssize(cdev_t dev, struct diskslices **sspp)
1026 {
1027         struct disklabel *lp;
1028         int part;
1029         int slice;
1030         struct diskslices *ssp;
1031
1032         slice = dkslice(dev);
1033         part = dkpart(dev);
1034         ssp = *sspp;
1035         if (ssp == NULL || slice >= ssp->dss_nslices
1036             || !(ssp->dss_slices[slice].ds_openmask & (1 << part))) {
1037                 if (dev_dopen(dev, FREAD, S_IFCHR, proc0.p_ucred) != 0)
1038                         return (-1);
1039                 dev_dclose(dev, FREAD, S_IFCHR);
1040                 ssp = *sspp;
1041         }
1042         lp = ssp->dss_slices[slice].ds_label;
1043         if (lp == NULL)
1044                 return (-1);
1045         return ((int64_t)lp->d_partitions[part].p_size);
1046 }
1047
1048 static void
1049 free_ds_label(struct diskslices *ssp, int slice)
1050 {
1051         struct disklabel *lp;
1052         struct diskslice *sp;
1053
1054         sp = &ssp->dss_slices[slice];
1055         lp = sp->ds_label;
1056         if (lp == NULL)
1057                 return;
1058         kfree(lp, M_DEVBUF);
1059         set_ds_label(ssp, slice, (struct disklabel *)NULL);
1060 }
1061
1062 static char *
1063 fixlabel(const char *sname, struct diskslice *sp, struct disklabel *lp, int writeflag)
1064 {
1065         u_int64_t start;
1066         u_int64_t end;
1067         u_int64_t offset;
1068         int part;
1069         struct partition *pp;
1070         bool_t warned;
1071
1072         /* These errors "can't happen" so don't bother reporting details. */
1073         if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC)
1074                 return ("fixlabel: invalid magic");
1075         if (dkcksum(lp) != 0)
1076                 return ("fixlabel: invalid checksum");
1077
1078         pp = &lp->d_partitions[RAW_PART];
1079
1080         /*
1081          * What a mess.  For ages old backwards compatibility the disklabel
1082          * on-disk stores absolute offsets instead of slice-relative offsets.
1083          * So fix it up when reading, writing, or snooping.
1084          *
1085          * The in-core label is always slice-relative.
1086          */
1087         if (writeflag) {
1088                 start = 0;
1089                 offset = sp->ds_offset;
1090         } else {
1091                 start = sp->ds_offset;
1092                 offset = -sp->ds_offset;
1093         }
1094         if (pp->p_offset != start) {
1095                 if (sname != NULL) {
1096                         kprintf(
1097 "%s: rejecting BSD label: raw partition offset != slice offset\n",
1098                                sname);
1099                         slice_info(sname, sp);
1100                         partition_info(sname, RAW_PART, pp);
1101                 }
1102                 return ("fixlabel: raw partition offset != slice offset");
1103         }
1104         if (pp->p_size != sp->ds_size) {
1105                 if (sname != NULL) {
1106                         kprintf("%s: raw partition size != slice size\n", sname);
1107                         slice_info(sname, sp);
1108                         partition_info(sname, RAW_PART, pp);
1109                 }
1110                 if (pp->p_size > sp->ds_size) {
1111                         if (sname == NULL)
1112                                 return ("fixlabel: raw partition size > slice size");
1113                         kprintf("%s: truncating raw partition\n", sname);
1114                         pp->p_size = sp->ds_size;
1115                 }
1116         }
1117         end = start + sp->ds_size;
1118         if (start > end)
1119                 return ("fixlabel: slice wraps");
1120         if (lp->d_secpercyl <= 0)
1121                 return ("fixlabel: d_secpercyl <= 0");
1122         pp -= RAW_PART;
1123         warned = FALSE;
1124         for (part = 0; part < lp->d_npartitions; part++, pp++) {
1125                 if (pp->p_offset != 0 || pp->p_size != 0) {
1126                         if (pp->p_offset < start
1127                             || pp->p_offset + pp->p_size > end
1128                             || pp->p_offset + pp->p_size < pp->p_offset) {
1129                                 if (sname != NULL) {
1130                                         kprintf(
1131 "%s: rejecting partition in BSD label: it isn't entirely within the slice\n",
1132                                                sname);
1133                                         if (!warned) {
1134                                                 slice_info(sname, sp);
1135                                                 warned = TRUE;
1136                                         }
1137                                         partition_info(sname, part, pp);
1138                                 }
1139                                 /* XXX else silently discard junk. */
1140                                 bzero(pp, sizeof *pp);
1141                         } else {
1142                                 pp->p_offset += offset;
1143                         }
1144                 }
1145         }
1146         lp->d_ncylinders = sp->ds_size / lp->d_secpercyl;
1147         lp->d_secperunit = sp->ds_size;
1148         lp->d_checksum = 0;
1149         lp->d_checksum = dkcksum(lp);
1150         return (NULL);
1151 }
1152
1153 static void
1154 partition_info(const char *sname, int part, struct partition *pp)
1155 {
1156         kprintf("%s%c: start %lu, end %lu, size %lu\n", sname, 'a' + part,
1157                (u_long)pp->p_offset, (u_long)(pp->p_offset + pp->p_size - 1),
1158                (u_long)pp->p_size);
1159 }
1160
1161 static void
1162 slice_info(const char *sname, struct diskslice *sp)
1163 {
1164         kprintf("%s: start %llu, end %llu, size %llu\n", sname,
1165                sp->ds_offset, sp->ds_offset + sp->ds_size - 1, sp->ds_size);
1166 }
1167
1168 static void
1169 set_ds_label(struct diskslices *ssp, int slice, struct disklabel *lp)
1170 {
1171         struct diskslice *sp1 = &ssp->dss_slices[slice];
1172         struct diskslice *sp2;
1173
1174         if (slice == COMPATIBILITY_SLICE)
1175                 sp2 = &ssp->dss_slices[ssp->dss_first_bsd_slice];
1176         else if (slice == ssp->dss_first_bsd_slice)
1177                 sp2 = &ssp->dss_slices[COMPATIBILITY_SLICE];
1178         else
1179                 sp2 = NULL;
1180         sp1->ds_label = lp;
1181         if (sp2)
1182                 sp2->ds_label = lp;
1183
1184         /*
1185          * If the slice is not the whole-disk slice, setup the reserved
1186          * area(s).
1187          *
1188          * The reserved area for the original bsd disklabel, inclusive of
1189          * the label and space for boot2, is 15 sectors.  If you've
1190          * noticed people traditionally skipping 16 sectors its because
1191          * the sector numbers start at the beginning of the slice rather
1192          * then the beginning of the disklabel and traditional dos slices
1193          * reserve a sector at the beginning for the boot code.
1194          *
1195          * NOTE! With the traditional bsdlabel, the first N bytes of boot2
1196          * overlap with the disklabel.  The disklabel program checks that
1197          * they are 0.
1198          */
1199         if (slice != WHOLE_DISK_SLICE) {
1200                 sp1->ds_skip_bsdlabel = sp1->ds_skip_platform + 15;
1201                 if (sp2)
1202                         sp2->ds_skip_bsdlabel = sp1->ds_skip_bsdlabel;
1203         }
1204 }
1205
1206 static void
1207 set_ds_wlabel(struct diskslices *ssp, int slice, int wlabel)
1208 {
1209         ssp->dss_slices[slice].ds_wlabel = wlabel;
1210         if (slice == COMPATIBILITY_SLICE)
1211                 ssp->dss_slices[ssp->dss_first_bsd_slice].ds_wlabel = wlabel;
1212         else if (slice == ssp->dss_first_bsd_slice)
1213                 ssp->dss_slices[COMPATIBILITY_SLICE].ds_wlabel = wlabel;
1214 }