DEVFS - Bring in Alex's GSOC kernel adjustments.
[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.51 2008/08/29 20:08:36 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/disklabel32.h>
56 #include <sys/disklabel64.h>
57 #include <sys/diskslice.h>
58 #include <sys/disk.h>
59 #include <sys/diskmbr.h>
60 #include <sys/fcntl.h>
61 #include <sys/malloc.h>
62 #include <sys/stat.h>
63 #include <sys/syslog.h>
64 #include <sys/proc.h>
65 #include <sys/vnode.h>
66 #include <sys/device.h>
67 #include <sys/thread2.h>
68
69 #include <vfs/ufs/dinode.h>     /* XXX used only for fs.h */
70 #include <vfs/ufs/fs.h>         /* XXX used only to get BBSIZE/SBSIZE */
71
72 static int  dsreadandsetlabel(cdev_t dev, u_int flags,
73                            struct diskslices *ssp, struct diskslice *sp,
74                            struct disk_info *info);
75 static void free_ds_label (struct diskslices *ssp, int slice);
76 static void set_ds_label (struct diskslices *ssp, int slice, disklabel_t lp,
77                            disklabel_ops_t ops);
78 static void set_ds_wlabel (struct diskslices *ssp, int slice, int wlabel);
79
80 /*
81  * Determine the size of the transfer, and make sure it is
82  * within the boundaries of the partition. Adjust transfer
83  * if needed, and signal errors or early completion.
84  *
85  * XXX TODO:
86  *      o Split buffers that are too big for the device.
87  *      o Check for overflow.
88  *      o Finish cleaning this up.
89  *
90  * This function returns 1 on success, 0 if transfer equates
91  * to EOF (end of disk) or -1 on failure.  The appropriate 
92  * 'errno' value is also set in bp->b_error and bp->b_flags
93  * is marked with B_ERROR.
94  */
95 struct bio *
96 dscheck(cdev_t dev, struct bio *bio, struct diskslices *ssp)
97 {
98         struct buf *bp = bio->bio_buf;
99         struct bio *nbio;
100         disklabel_t lp;
101         disklabel_ops_t ops;
102         long nsec;
103         u_int64_t secno;
104         u_int64_t endsecno;
105         u_int64_t slicerel_secno;
106         struct diskslice *sp;
107         u_int32_t part;
108         u_int32_t slice;
109         int shift;
110         int mask;
111
112         slice = dkslice(dev);
113         part  = dkpart(dev);
114
115         if (bio->bio_offset < 0) {
116                 kprintf("dscheck(%s): negative bio_offset %lld\n", 
117                         devtoname(dev), (long long)bio->bio_offset);
118                 goto bad;
119         }
120         if (slice >= ssp->dss_nslices) {
121                 kprintf("dscheck(%s): slice too large %d/%d\n",
122                         devtoname(dev), slice, ssp->dss_nslices);
123                 goto bad;
124         }
125         sp = &ssp->dss_slices[slice];
126         /*
127          * Calculate secno and nsec
128          */
129         if (ssp->dss_secmult == 1) {
130                 shift = DEV_BSHIFT;
131                 goto doshift;
132         } else if (ssp->dss_secshift != -1) {
133                 shift = DEV_BSHIFT + ssp->dss_secshift;
134 doshift:
135                 mask = (1 << shift) - 1;
136                 if ((int)bp->b_bcount & mask)
137                         goto bad_bcount;
138                 if ((int)bio->bio_offset & mask)
139                         goto bad_blkno;
140                 secno = bio->bio_offset >> shift;
141                 nsec = bp->b_bcount >> shift;
142         } else {
143                 if (bp->b_bcount % ssp->dss_secsize)
144                         goto bad_bcount;
145                 if (bio->bio_offset % ssp->dss_secsize)
146                         goto bad_blkno;
147                 secno = bio->bio_offset / ssp->dss_secsize;
148                 nsec = bp->b_bcount / ssp->dss_secsize;
149         }
150
151         /*
152          * Calculate slice-relative sector number end slice-relative
153          * limit.
154          */
155         if (slice == WHOLE_DISK_SLICE) {
156                 /*
157                  * Labels have not been allowed on whole-disks for a while.
158                  * This really puts the nail in the coffin.
159                  *
160                  * Accesses to the WHOLE_DISK_SLICE do not use a disklabel
161                  * and partition numbers are special-cased.  Currently numbers
162                  * less then 128 are not allowed.  Partition numbers >= 128
163                  * are encoded in the high 8 bits of the 64 bit buffer offset
164                  * and are fed directly through to the device with no
165                  * further interpretation.  In particular, no sector
166                  * translation interpretation should occur because the
167                  * sector size for the special raw access may not be the
168                  * same as the nominal sector size for the device.
169                  */
170                 lp.opaque = NULL;
171                 if (part < 128) {
172                         kprintf("dscheck(%s): illegal partition number (%d) "
173                                 "for WHOLE_DISK_SLICE access\n",
174                                 devtoname(dev), part);
175                         goto bad;
176                 } else if (part != WHOLE_SLICE_PART) {
177                         nbio = push_bio(bio);
178                         nbio->bio_offset = bio->bio_offset |
179                                            (u_int64_t)part << 56;
180                         return(nbio);
181                 }
182
183                 /*
184                  * sp->ds_size is for the whole disk in the WHOLE_DISK_SLICE,
185                  * there are no reserved areas.
186                  */
187                 endsecno = sp->ds_size;
188                 slicerel_secno = secno;
189         } else if (part == WHOLE_SLICE_PART) {
190                 /* 
191                  * NOTE! opens on a whole-slice partition will not attempt
192                  * to read a disklabel in, so there may not be an in-core
193                  * disklabel even if there is one on the disk.
194                  */
195                 endsecno = sp->ds_size;
196                 slicerel_secno = secno;
197         } else if ((lp = sp->ds_label).opaque != NULL) {
198                 /*
199                  * A label is present, extract the partition.  Snooping of
200                  * the disklabel is not supported even if accessible.  Of
201                  * course, the reserved area is still write protected.
202                  */
203                 ops = sp->ds_ops;
204                 if (ops->op_getpartbounds(ssp, lp, part,
205                                           &slicerel_secno, &endsecno)) {
206                         kprintf("dscheck(%s): partition %d out of bounds\n",
207                                 devtoname(dev), part);
208                         goto bad;
209                 }
210                 slicerel_secno += secno;
211         } else {
212                 /*
213                  * Attempt to access partition when no disklabel present
214                  */
215                 kprintf("dscheck(%s): attempt to access non-existent partition\n",
216                         devtoname(dev));
217                 goto bad;
218         }
219
220         /*
221          * Disallow writes to reserved areas unless ds_wlabel allows it.
222          */
223         if (slicerel_secno < sp->ds_reserved && nsec &&
224             bp->b_cmd == BUF_CMD_WRITE && sp->ds_wlabel == 0) {
225                 bp->b_error = EROFS;
226                 goto error;
227         }
228
229         /*
230          * If we get here, bio_offset must be on a block boundary and
231          * the sector size must be a power of 2.
232          */
233         if ((bio->bio_offset & (ssp->dss_secsize - 1)) ||
234             (ssp->dss_secsize ^ (ssp->dss_secsize - 1)) !=
235             ((ssp->dss_secsize << 1) - 1)) {
236                 kprintf("%s: invalid BIO offset, not sector aligned or"
237                         " invalid sector size (not power of 2) %08llx %d\n",
238                         devtoname(dev), (long long)bio->bio_offset,
239                         ssp->dss_secsize);
240                 goto bad;
241         }
242
243         /*
244          * EOF handling
245          */
246         if (secno + nsec > endsecno) {
247                 /*
248                  * Return an error if beyond the end of the disk, or
249                  * if B_BNOCLIP is set.  Tell the system that we do not
250                  * need to keep the buffer around.
251                  */
252                 if (secno > endsecno || (bp->b_flags & B_BNOCLIP))
253                         goto bad;
254
255                 /*
256                  * If exactly at end of disk, return an EOF.  Throw away
257                  * the buffer contents, if any, by setting B_INVAL.
258                  */
259                 if (secno == endsecno) {
260                         bp->b_resid = bp->b_bcount;
261                         bp->b_flags |= B_INVAL;
262                         goto done;
263                 }
264
265                 /*
266                  * Else truncate
267                  */
268                 nsec = endsecno - secno;
269                 bp->b_bcount = nsec * ssp->dss_secsize;
270         }
271
272         nbio = push_bio(bio);
273         nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) * 
274                            ssp->dss_secsize;
275         return (nbio);
276
277 bad_bcount:
278         kprintf(
279         "dscheck(%s): b_bcount %d is not on a sector boundary (ssize %d)\n",
280             devtoname(dev), bp->b_bcount, ssp->dss_secsize);
281         goto bad;
282
283 bad_blkno:
284         kprintf(
285         "dscheck(%s): bio_offset %lld is not on a sector boundary (ssize %d)\n",
286             devtoname(dev), (long long)bio->bio_offset, ssp->dss_secsize);
287 bad:
288         bp->b_error = EINVAL;
289         /* fall through */
290 error:
291         /*
292          * Terminate the I/O with a ranging error.  Since the buffer is
293          * either illegal or beyond the file EOF, mark it B_INVAL as well.
294          */
295         bp->b_resid = bp->b_bcount;
296         bp->b_flags |= B_ERROR | B_INVAL;
297 done:
298         /*
299          * Caller must biodone() the originally passed bio if NULL is
300          * returned.
301          */
302         return (NULL);
303 }
304
305 void
306 dsclose(cdev_t dev, int mode, struct diskslices *ssp)
307 {
308         u_int32_t part;
309         u_int32_t slice;
310         struct diskslice *sp;
311
312         slice = dkslice(dev);
313         part  = dkpart(dev);
314         if (slice < ssp->dss_nslices) {
315                 sp = &ssp->dss_slices[slice];
316                 dsclrmask(sp, part);
317         }
318 }
319
320 void
321 dsgone(struct diskslices **sspp)
322 {
323         int slice;
324         struct diskslice *sp;
325         struct diskslices *ssp;
326
327         kprintf("dsgone is called... fear!\n");
328
329         for (slice = 0, ssp = *sspp; slice < ssp->dss_nslices; slice++) {
330                 sp = &ssp->dss_slices[slice];
331                 free_ds_label(ssp, slice);
332         }
333         kfree(ssp, M_DEVBUF);
334         *sspp = NULL;
335 }
336
337 /*
338  * For the "write" commands (DIOCSDINFO and DIOCWDINFO), this
339  * is subject to the same restriction as dsopen().
340  */
341 int
342 dsioctl(cdev_t dev, u_long cmd, caddr_t data, int flags,
343         struct diskslices **sspp, struct disk_info *info)
344 {
345         int error;
346         disklabel_t lp;
347         disklabel_t lptmp;
348         disklabel_ops_t ops;
349         int old_wlabel;
350         u_int32_t openmask[DKMAXPARTITIONS/(sizeof(u_int32_t)*8)];
351         int part;
352         int slice;
353         struct diskslice *sp;
354         struct diskslices *ssp;
355
356         slice = dkslice(dev);
357         part = dkpart(dev);
358         ssp = *sspp;
359         if (slice >= ssp->dss_nslices)
360                 return (EINVAL);
361         sp = &ssp->dss_slices[slice];
362         lp = sp->ds_label;
363         ops = sp->ds_ops;       /* may be NULL if no label */
364
365         switch (cmd) {
366         case DIOCGDVIRGIN32:
367                 ops = &disklabel32_ops;
368                 /* fall through */
369         case DIOCGDVIRGIN64:
370                 if (cmd != DIOCGDVIRGIN32)
371                         ops = &disklabel64_ops;
372                 /*
373                  * You can only retrieve a virgin disklabel on the whole
374                  * disk slice or whole-slice partition.
375                  */
376                 if (slice != WHOLE_DISK_SLICE &&
377                     part != WHOLE_SLICE_PART) {
378                         return(EINVAL);
379                 }
380
381                 lp.opaque = data;
382                 ops->op_makevirginlabel(lp, ssp, sp, info);
383                 return (0);
384
385         case DIOCGDINFO32:
386         case DIOCGDINFO64:
387                 /*
388                  * You can only retrieve a disklabel on the whole
389                  * slice partition.
390                  *
391                  * We do not support labels directly on whole-disks
392                  * any more (that is, disks without slices), unless the
393                  * device driver has asked for a compatible label (e.g.
394                  * for a CD) to allow booting off of storage that is
395                  * otherwise unlabeled.
396                  */
397                 error = 0;
398                 if (part != WHOLE_SLICE_PART)
399                         return(EINVAL);
400                 if (slice == WHOLE_DISK_SLICE &&
401                     (info->d_dsflags & DSO_COMPATLABEL) == 0) {
402                         return (ENODEV);
403                 }
404                 if (sp->ds_label.opaque == NULL) {
405                         error = dsreadandsetlabel(dev, info->d_dsflags,
406                                                   ssp, sp, info);
407                         ops = sp->ds_ops;       /* may be NULL */
408                 }
409
410                 /*
411                  * The type of label we found must match the type of
412                  * label requested.
413                  */
414                 if (error == 0 && IOCPARM_LEN(cmd) != ops->labelsize)
415                         error = ENOATTR;
416                 if (error == 0)
417                         bcopy(sp->ds_label.opaque, data, ops->labelsize);
418                 return (error);
419
420         case DIOCGPART:
421                 {
422                         struct partinfo *dpart = (void *)data;
423
424                         /*
425                          * The disk management layer may not have read the
426                          * disklabel yet because simply opening a slice no
427                          * longer 'probes' the disk that way.  Be sure we
428                          * have tried.
429                          *
430                          * We ignore any error.
431                          */
432                         if (sp->ds_label.opaque == NULL &&
433                             part == WHOLE_SLICE_PART &&
434                             slice != WHOLE_DISK_SLICE) {
435                                 kprintf("dsioctl: I shouldn't be here...\n");
436                                 dsreadandsetlabel(dev, info->d_dsflags,
437                                                   ssp, sp, info);
438                                 ops = sp->ds_ops;       /* may be NULL */
439                         }
440
441                         bzero(dpart, sizeof(*dpart));
442                         dpart->media_offset   = (u_int64_t)sp->ds_offset *
443                                                 info->d_media_blksize;
444                         dpart->media_size     = (u_int64_t)sp->ds_size *
445                                                 info->d_media_blksize;
446                         dpart->media_blocks   = sp->ds_size;
447                         dpart->media_blksize  = info->d_media_blksize;
448                         dpart->reserved_blocks= sp->ds_reserved;
449                         dpart->fstype_uuid = sp->ds_type_uuid;
450                         dpart->storage_uuid = sp->ds_stor_uuid;
451
452                         if (slice != WHOLE_DISK_SLICE &&
453                             part != WHOLE_SLICE_PART) {
454                                 u_int64_t start;
455                                 u_int64_t blocks;
456                                 if (lp.opaque == NULL)
457                                         return(EINVAL);
458                                 if (ops->op_getpartbounds(ssp, lp, part,
459                                                           &start, &blocks)) {
460                                         return(EINVAL);
461                                 }
462                                 ops->op_loadpartinfo(lp, part, dpart);
463                                 dpart->media_offset += start *
464                                                        info->d_media_blksize;
465                                 dpart->media_size = blocks *
466                                                     info->d_media_blksize;
467                                 dpart->media_blocks = blocks;
468
469                                 /*
470                                  * partition starting sector (p_offset)
471                                  * requires slice's reserved areas to be
472                                  * adjusted.
473                                  */
474                                 if (dpart->reserved_blocks > start)
475                                         dpart->reserved_blocks -= start;
476                                 else
477                                         dpart->reserved_blocks = 0;
478                         }
479
480                         /*
481                          * Load remaining fields from the info structure
482                          */
483                         dpart->d_nheads =       info->d_nheads;
484                         dpart->d_ncylinders =   info->d_ncylinders;
485                         dpart->d_secpertrack =  info->d_secpertrack;
486                         dpart->d_secpercyl =    info->d_secpercyl;
487                 }
488                 return (0);
489
490         case DIOCGSLICEINFO:
491                 bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] -
492                                  (char *)ssp);
493                 return (0);
494
495         case DIOCSDINFO32:
496                 ops = &disklabel32_ops;
497                 /* fall through */
498         case DIOCSDINFO64:
499                 if (cmd != DIOCSDINFO32)
500                         ops = &disklabel64_ops;
501                 /*
502                  * You can write a disklabel on the whole disk slice or
503                  * whole-slice partition.
504                  */
505                 if (slice != WHOLE_DISK_SLICE &&
506                     part != WHOLE_SLICE_PART) {
507                         return(EINVAL);
508                 }
509
510                 /*
511                  * We no longer support writing disklabels directly to media
512                  * without there being a slice.  Keep this as a separate
513                  * conditional.
514                  */
515                 if (slice == WHOLE_DISK_SLICE)
516                         return (ENODEV);
517                 if (!(flags & FWRITE))
518                         return (EBADF);
519
520                 /*
521                  * If an existing label is present it must be the same
522                  * type as the label being passed by the ioctl.
523                  */
524                 if (sp->ds_label.opaque && sp->ds_ops != ops)
525                         return (ENOATTR);
526
527                 /*
528                  * Create a temporary copy of the existing label
529                  * (if present) so setdisklabel can compare it against
530                  * the new label.
531                  */
532                 lp.opaque = kmalloc(ops->labelsize, M_DEVBUF, M_WAITOK);
533                 if (sp->ds_label.opaque == NULL)
534                         bzero(lp.opaque, ops->labelsize);
535                 else
536                         bcopy(sp->ds_label.opaque, lp.opaque, ops->labelsize);
537                 if (sp->ds_label.opaque == NULL) {
538                         bzero(openmask, sizeof(openmask));
539                 } else {
540                         bcopy(sp->ds_openmask, openmask, sizeof(openmask));
541                 }
542                 lptmp.opaque = data;
543                 error = ops->op_setdisklabel(lp, lptmp, ssp, sp, openmask);
544                 //XXX: send reprobe message here.
545                 disk_msg_send(DISK_SLICE_REPROBE, dev->si_disk, sp);
546                 if (error != 0) {
547                         kfree(lp.opaque, M_DEVBUF);
548                         return (error);
549                 }
550                 free_ds_label(ssp, slice);
551                 set_ds_label(ssp, slice, lp, ops);
552                 return (0);
553
554         case DIOCSYNCSLICEINFO:
555                 /*
556                  * This ioctl can only be done on the whole disk
557                  */
558                 if (slice != WHOLE_DISK_SLICE || part != WHOLE_SLICE_PART)
559                         return (EINVAL);
560
561                 if (*(int *)data == 0) {
562                         for (slice = 0; slice < ssp->dss_nslices; slice++) {
563                                 struct diskslice *ds = &ssp->dss_slices[slice];
564
565                                 switch(dscountmask(ds)) {
566                                 case 0:
567                                         break;
568                                 case 1:
569                                         if (slice != WHOLE_DISK_SLICE)
570                                                 return (EBUSY);
571                                         if (!dschkmask(ds, RAW_PART))
572                                                 return (EBUSY);
573                                         break;
574                                 default:
575                                         return (EBUSY);
576                                 }
577                         }
578                 }
579
580                 disk_msg_send(DISK_DISK_REPROBE, dev->si_disk, NULL);
581                 return 0;
582
583                 /*
584                  * Temporarily forget the current slices struct and read
585                  * the current one.
586                  *
587                  * NOTE:
588                  *
589                  * XXX should wait for current accesses on this disk to
590                  * complete, then lock out future accesses and opens.
591                  */
592                 kprintf("dsioctl messed with our stuff!\n");
593                 *sspp = NULL;
594                 error = dsopen(dev, S_IFCHR, ssp->dss_oflags, sspp, info);
595                 if (error != 0) {
596                         *sspp = ssp;
597                         return (error);
598                 }
599
600                 /*
601                  * Reopen everything.  This is a no-op except in the "force"
602                  * case and when the raw bdev and cdev are both open.  Abort
603                  * if anything fails.
604                  */
605                 for (slice = 0; slice < ssp->dss_nslices; slice++) {
606                         for (part = 0; part < DKMAXPARTITIONS; ++part) {
607                                 if (!dschkmask(&ssp->dss_slices[slice], part))
608                                         continue;
609                                 error = dsopen(dkmodslice(dkmodpart(dev, part),
610                                                           slice),
611                                                S_IFCHR, ssp->dss_oflags, sspp,
612                                                info);
613                                 if (error != 0) {
614                                         *sspp = ssp;
615                                         return (EBUSY);
616                                 }
617                         }
618                 }
619
620                 //XXX: recheck this...
621                 dsgone(&ssp);
622                 return (0);
623
624         case DIOCWDINFO32:
625         case DIOCWDINFO64:
626                 error = dsioctl(dev, ((cmd == DIOCWDINFO32) ?
627                                         DIOCSDINFO32 : DIOCSDINFO64),
628                                 data, flags, &ssp, info);
629                 if (error == 0 && sp->ds_label.opaque == NULL)
630                         error = EINVAL;
631                 if (error != 0)
632                         return (error);
633
634                 /*
635                  * Allow the reserved area to be written, reload ops
636                  * because the DIOCSDINFO op above may have installed
637                  * a new label type.
638                  */
639                 ops = sp->ds_ops;
640                 old_wlabel = sp->ds_wlabel;
641                 set_ds_wlabel(ssp, slice, TRUE);
642                 error = ops->op_writedisklabel(dev, ssp, sp, sp->ds_label);
643                 disk_msg_send(DISK_SLICE_REPROBE, dev->si_disk, sp);
644                 set_ds_wlabel(ssp, slice, old_wlabel);
645                 /* XXX should invalidate in-core label if write failed. */
646                 return (error);
647
648         case DIOCWLABEL:
649                 if (slice == WHOLE_DISK_SLICE)
650                         return (ENODEV);
651                 if (!(flags & FWRITE))
652                         return (EBADF);
653                 set_ds_wlabel(ssp, slice, *(int *)data != 0);
654                 return (0);
655
656         default:
657                 return (ENOIOCTL);
658         }
659 }
660
661 int
662 dsisopen(struct diskslices *ssp)
663 {
664         int slice;
665
666         if (ssp == NULL)
667                 return (0);
668         for (slice = 0; slice < ssp->dss_nslices; slice++) {
669                 if (dscountmask(&ssp->dss_slices[slice]))
670                         return (1);
671         }
672         return (0);
673 }
674
675 /*
676  * Allocate a slices "struct" and initialize it to contain only an empty
677  * compatibility slice (pointing to itself), a whole disk slice (covering
678  * the disk as described by the label), and (nslices - BASE_SLICES) empty
679  * slices beginning at BASE_SLICE.
680  *
681  * Note that the compatibility slice is no longer really a compatibility
682  * slice.  It is slice 0 if a GPT label is present, and the dangerously
683  * dedicated slice if no slice table otherwise exists.  Else it is 0-sized.
684  */
685 struct diskslices *
686 dsmakeslicestruct(int nslices, struct disk_info *info)
687 {
688         struct diskslice *sp;
689         struct diskslices *ssp;
690
691         ssp = kmalloc(offsetof(struct diskslices, dss_slices) +
692                      nslices * sizeof *sp, M_DEVBUF, M_WAITOK);
693         ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
694         ssp->dss_nslices = nslices;
695         ssp->dss_oflags = 0;
696
697         /*
698          * Figure out if we can use shifts or whether we have to
699          * use mod/multply to translate byte offsets into sector numbers.
700          */
701         if ((info->d_media_blksize ^ (info->d_media_blksize - 1)) ==
702              (info->d_media_blksize << 1) - 1) {
703                 ssp->dss_secmult = info->d_media_blksize / DEV_BSIZE;
704                 if (ssp->dss_secmult & (ssp->dss_secmult - 1))
705                         ssp->dss_secshift = -1;
706                 else
707                         ssp->dss_secshift = ffs(ssp->dss_secmult) - 1;
708         } else {
709                 ssp->dss_secmult = 0;
710                 ssp->dss_secshift = -1;
711         }
712         ssp->dss_secsize = info->d_media_blksize;
713         sp = &ssp->dss_slices[0];
714         bzero(sp, nslices * sizeof *sp);
715         sp[WHOLE_DISK_SLICE].ds_size = info->d_media_blocks;
716         return (ssp);
717 }
718
719 char *
720 dsname(cdev_t dev, int unit, int slice, int part, char *partname)
721 {
722         return dev->si_name;
723 }
724
725 /*
726  * This should only be called when the unit is inactive and the strategy
727  * routine should not allow it to become active unless we call it.  Our
728  * strategy routine must be special to allow activity.
729  */
730 int
731 dsopen(cdev_t dev, int mode, u_int flags, 
732         struct diskslices **sspp, struct disk_info *info)
733 {
734         cdev_t dev1;
735         int error;
736         int need_init;
737         struct diskslice *sp;
738         struct diskslices *ssp;
739         int slice;
740         int part;
741
742         ssp = *sspp;
743         dev->si_bsize_phys = info->d_media_blksize;
744         slice = dkslice(dev);
745         part = dkpart(dev);
746         sp = &ssp->dss_slices[slice];
747         dssetmask(sp, part);
748
749         return 0;
750
751         /*
752          * Do not attempt to read the slice table or disk label when
753          * accessing the whole-disk slice or a while-slice partition.
754          */
755         if (dkslice(dev) == WHOLE_DISK_SLICE)
756                 flags |= DSO_ONESLICE | DSO_NOLABELS;
757         if (dkpart(dev) == WHOLE_SLICE_PART)
758                 flags |= DSO_NOLABELS;
759
760         /*
761          * Reinitialize the slice table unless there is an open device
762          * on the unit.
763          *
764          * It would be nice if we didn't have to do this but when a
765          * user is slicing and partitioning up a disk it is a lot safer
766          * to not take any chances.
767          */
768         ssp = *sspp;
769         need_init = !dsisopen(ssp);
770         if (ssp != NULL && need_init)
771                 dsgone(sspp);
772         if (need_init) {
773                 /*
774                  * Allocate a minimal slices "struct".  This will become
775                  * the final slices "struct" if we don't want real slices
776                  * or if we can't find any real slices.
777                  *
778                  * Then scan the disk
779                  */
780                 *sspp = dsmakeslicestruct(BASE_SLICE, info);
781
782                 if ((flags & DSO_ONESLICE) == 0) {
783                         error = mbrinit(dev, info, sspp);
784                         if (error != 0) {
785                                 dsgone(sspp);
786                                 return (error);
787                         }
788                 }
789                 ssp = *sspp;
790                 ssp->dss_oflags = flags;
791
792                 /*
793                  * If there are no real slices, then make the compatiblity
794                  * slice cover the whole disk.
795                  */
796                 if (ssp->dss_nslices == BASE_SLICE) {
797                         sp = &ssp->dss_slices[COMPATIBILITY_SLICE];
798
799                         sp->ds_size = info->d_media_blocks;
800                         sp->ds_reserved = 0;
801                 }
802
803                 /*
804                  * Set dss_first_bsd_slice to point at the first BSD
805                  * slice, if any.
806                  */
807                 for (slice = BASE_SLICE; slice < ssp->dss_nslices; slice++) {
808                         sp = &ssp->dss_slices[slice];
809                         if (sp->ds_type == DOSPTYP_386BSD /* XXX */) {
810 #if 0
811                                 struct diskslice *csp;
812 #endif
813
814                                 ssp->dss_first_bsd_slice = slice;
815 #if 0
816                                 /*
817                                  * no longer supported, s0 is a real slice
818                                  * for GPT
819                                  */
820                                 csp = &ssp->dss_slices[COMPATIBILITY_SLICE];
821                                 csp->ds_offset = sp->ds_offset;
822                                 csp->ds_size = sp->ds_size;
823                                 csp->ds_type = sp->ds_type;
824                                 csp->ds_reserved = sp->ds_reserved;
825 #endif
826                                 break;
827                         }
828                 }
829
830                 /*
831                  * By definition accesses via the whole-disk device do not
832                  * specify any reserved areas.  The whole disk may be read
833                  * or written by the whole-disk device.
834                  *
835                  * The whole-disk slice does not ever have a label.
836                  */
837                 sp = &ssp->dss_slices[WHOLE_DISK_SLICE];
838                 sp->ds_wlabel = TRUE;
839                 sp->ds_reserved = 0;
840         }
841
842         /*
843          * Load the disklabel for the slice being accessed unless it is
844          * a whole-disk-slice or a whole-slice-partition (as determined
845          * by DSO_NOLABELS).
846          *
847          * We could scan all slices here and try to load up their
848          * disklabels, but that would cause us to access slices that
849          * the user may otherwise not intend us to access, or corrupted
850          * slices, etc.
851          *
852          * XXX if there are no opens on the slice we may want to re-read
853          * the disklabel anyway, even if we have one cached.
854          */
855         slice = dkslice(dev);
856         if (slice >= ssp->dss_nslices)
857                 return (ENXIO);
858         sp = &ssp->dss_slices[slice];
859         part = dkpart(dev);
860
861         if ((flags & DSO_NOLABELS) == 0 && sp->ds_label.opaque == NULL) {
862                 dev1 = dkmodslice(dkmodpart(dev, WHOLE_SLICE_PART), slice);
863
864                 /*
865                  * If opening a raw disk we do not try to
866                  * read the disklabel now.  No interpretation of raw disks
867                  * (e.g. like 'da0') ever occurs.  We will try to read the
868                  * disklabel for a raw slice if asked to via DIOC* ioctls.
869                  *
870                  * Access to the label area is disallowed by default.  Note
871                  * however that accesses via WHOLE_DISK_SLICE, and accesses
872                  * via WHOLE_SLICE_PART for slices without valid disklabels,
873                  * will allow writes and ignore the flag.
874                  */
875                 set_ds_wlabel(ssp, slice, FALSE);
876                 dsreadandsetlabel(dev1, flags, ssp, sp, info);
877         }
878
879         /*
880          * If opening a particular partition the disklabel must exist and
881          * the partition must be present in the label.
882          *
883          * If the partition is the special whole-disk-slice no partition
884          * table need exist.
885          */
886         if (part != WHOLE_SLICE_PART && slice != WHOLE_DISK_SLICE) {
887                 if (sp->ds_label.opaque == NULL ||
888                     part >= sp->ds_ops->op_getnumparts(sp->ds_label)) {
889                         return (EINVAL);
890                 }
891         }
892
893         /*
894          * Do not allow special raw-extension partitions to be opened
895          * if the device doesn't support them.  Raw-extension partitions
896          * are typically used to handle CD tracks.
897          */
898         if (slice == WHOLE_DISK_SLICE && part >= 128 &&
899             part != WHOLE_SLICE_PART) {
900                 if ((info->d_dsflags & DSO_RAWEXTENSIONS) == 0)
901                         return (EINVAL);
902         }
903
904         /*
905          * Ok, we are open
906          */
907         dssetmask(sp, part);
908         return (0);
909 }
910
911 /*
912  * Attempt to read the disklabel.  If successful, store it in sp->ds_label.
913  *
914  * If we cannot read the disklabel and DSO_COMPATLABEL is set, we construct
915  * a fake label covering the whole disk.
916  */
917 static
918 int
919 dsreadandsetlabel(cdev_t dev, u_int flags,
920                   struct diskslices *ssp, struct diskslice *sp,
921                   struct disk_info *info)
922 {
923         disklabel_t lp;
924         disklabel_ops_t ops;
925         const char *msg;
926         const char *sname;
927         char partname[2];
928         int slice = dkslice(dev);
929
930         /*
931          * Probe the disklabel
932          */
933         lp.opaque = NULL;
934         sname = dsname(dev, dkunit(dev), slice, WHOLE_SLICE_PART, partname);
935         ops = &disklabel32_ops;
936         msg = ops->op_readdisklabel(dev, sp, &lp, info);
937         if (msg && strcmp(msg, "no disk label") == 0) {
938                 ops = &disklabel64_ops;
939                 msg = disklabel64_ops.op_readdisklabel(dev, sp, &lp, info);
940         }
941
942         /*
943          * If we failed and COMPATLABEL is set, create a dummy disklabel.
944          */
945         if (msg != NULL && (flags & DSO_COMPATLABEL)) {
946                 msg = NULL;
947                 if (sp->ds_size >= 0x100000000ULL)
948                         ops = &disklabel64_ops;
949                 else
950                         ops = &disklabel32_ops;
951                 lp = ops->op_clone_label(info, sp);
952         }
953         if (msg != NULL) {
954                 if (sp->ds_type == DOSPTYP_386BSD /* XXX */)
955                         log(LOG_WARNING, "%s: cannot find label (%s)\n",
956                             sname, msg);
957                 if (lp.opaque)
958                         kfree(lp.opaque, M_DEVBUF);
959         } else {
960                 set_ds_label(ssp, slice, lp, ops);
961                 set_ds_wlabel(ssp, slice, FALSE);
962         }
963         return (msg ? EINVAL : 0);
964 }
965
966 int64_t
967 dssize(cdev_t dev, struct diskslices **sspp)
968 {
969         disklabel_t lp;
970         disklabel_ops_t ops;
971         int part;
972         int slice;
973         struct diskslices *ssp;
974         u_int64_t start;
975         u_int64_t blocks;
976
977         slice = dkslice(dev);
978         part = dkpart(dev);
979         ssp = *sspp;
980         if (ssp == NULL || slice >= ssp->dss_nslices
981             || !dschkmask(&ssp->dss_slices[slice], part)) {
982                 if (dev_dopen(dev, FREAD, S_IFCHR, proc0.p_ucred) != 0)
983                         return (-1);
984                 dev_dclose(dev, FREAD, S_IFCHR);
985                 ssp = *sspp;
986         }
987         lp = ssp->dss_slices[slice].ds_label;
988         if (lp.opaque == NULL)
989                 return (-1);
990         ops = ssp->dss_slices[slice].ds_ops;
991         if (ops->op_getpartbounds(ssp, lp, part, &start, &blocks))
992                 return (-1);
993         return ((int64_t)blocks);
994 }
995
996 static void
997 free_ds_label(struct diskslices *ssp, int slice)
998 {
999         struct diskslice *sp;
1000         disklabel_t lp;
1001
1002         sp = &ssp->dss_slices[slice];
1003         lp = sp->ds_label;
1004         if (lp.opaque != NULL) {
1005                 kfree(lp.opaque, M_DEVBUF);
1006                 lp.opaque = NULL;
1007                 set_ds_label(ssp, slice, lp, NULL);
1008         }
1009 }
1010
1011 static void
1012 set_ds_label(struct diskslices *ssp, int slice,
1013              disklabel_t lp, disklabel_ops_t ops)
1014 {
1015         struct diskslice *sp = &ssp->dss_slices[slice];
1016
1017         sp->ds_label = lp;
1018         sp->ds_ops = ops;
1019         if (lp.opaque && slice != WHOLE_DISK_SLICE)
1020                 ops->op_adjust_label_reserved(ssp, slice, sp);
1021         else
1022                 sp->ds_reserved = 0;
1023 }
1024
1025 static void
1026 set_ds_wlabel(struct diskslices *ssp, int slice, int wlabel)
1027 {
1028         ssp->dss_slices[slice].ds_wlabel = wlabel;
1029 }
1030