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