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