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