MFC numerous features from HEAD.
[dragonfly.git] / sys / kern / subr_diskslice.c
blob227069bbe138492395216e7834e10713709d7b9b
1 /*-
2 * Copyright (c) 1994 Bruce D. Evans.
3 * All rights reserved.
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * William Jolitz.
11 * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
12 * All rights reserved.
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.
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.
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.50.2.1 2008/09/25 01:44:52 dillon Exp $
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>
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 */
72 static int dsreadandsetlabel(cdev_t dev, u_int flags,
73 struct diskslices *ssp, struct diskslice *sp,
74 struct disk_info *info);
75 static void free_ds_label (struct diskslices *ssp, int slice);
76 static void set_ds_label (struct diskslices *ssp, int slice, disklabel_t lp,
77 disklabel_ops_t ops);
78 static void set_ds_wlabel (struct diskslices *ssp, int slice, int wlabel);
81 * Determine the size of the transfer, and make sure it is
82 * within the boundaries of the partition. Adjust transfer
83 * if needed, and signal errors or early completion.
85 * XXX TODO:
86 * o Split buffers that are too big for the device.
87 * o Check for overflow.
88 * o Finish cleaning this up.
90 * This function returns 1 on success, 0 if transfer equates
91 * to EOF (end of disk) or -1 on failure. The appropriate
92 * 'errno' value is also set in bp->b_error and bp->b_flags
93 * is marked with B_ERROR.
95 struct bio *
96 dscheck(cdev_t dev, struct bio *bio, struct diskslices *ssp)
98 struct buf *bp = bio->bio_buf;
99 struct bio *nbio;
100 disklabel_t lp;
101 disklabel_ops_t ops;
102 long nsec;
103 u_int64_t secno;
104 u_int64_t endsecno;
105 u_int64_t slicerel_secno;
106 struct diskslice *sp;
107 u_int32_t part;
108 u_int32_t slice;
109 int shift;
110 int mask;
112 slice = dkslice(dev);
113 part = dkpart(dev);
115 if (bio->bio_offset < 0) {
116 kprintf("dscheck(%s): negative bio_offset %lld\n",
117 devtoname(dev), bio->bio_offset);
118 goto bad;
120 if (slice >= ssp->dss_nslices) {
121 kprintf("dscheck(%s): slice too large %d/%d\n",
122 devtoname(dev), slice, ssp->dss_nslices);
123 goto bad;
125 sp = &ssp->dss_slices[slice];
128 * Calculate secno and nsec
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;
153 * Calculate slice-relative sector number end slice-relative
154 * limit.
156 if (slice == WHOLE_DISK_SLICE) {
158 * Labels have not been allowed on whole-disks for a while.
159 * This really puts the nail in the coffin.
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.
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);
185 * sp->ds_size is for the whole disk in the WHOLE_DISK_SLICE,
186 * there are no reserved areas.
188 endsecno = sp->ds_size;
189 slicerel_secno = secno;
190 } else if (part == WHOLE_SLICE_PART) {
192 * NOTE! opens on a whole-slice partition will not attempt
193 * to read a disklabel in, so there may not be an in-core
194 * disklabel even if there is one on the disk.
196 endsecno = sp->ds_size;
197 slicerel_secno = secno;
198 } else if ((lp = sp->ds_label).opaque != NULL) {
200 * A label is present, extract the partition. Snooping of
201 * the disklabel is not supported even if accessible. Of
202 * course, the reserved area is still write protected.
204 ops = sp->ds_ops;
205 if (ops->op_getpartbounds(ssp, lp, part,
206 &slicerel_secno, &endsecno)) {
207 kprintf("dscheck(%s): partition %d out of bounds\n",
208 devtoname(dev), part);
209 goto bad;
211 slicerel_secno += secno;
212 } else {
214 * Attempt to access partition when no disklabel present
216 kprintf("dscheck(%s): attempt to access non-existent partition\n",
217 devtoname(dev));
218 goto bad;
222 * Disallow writes to reserved areas unless ds_wlabel allows it.
224 if (slicerel_secno < sp->ds_reserved && nsec &&
225 bp->b_cmd == BUF_CMD_WRITE && sp->ds_wlabel == 0) {
226 bp->b_error = EROFS;
227 goto error;
231 * If we get here, bio_offset must be on a block boundary and
232 * the sector size must be a power of 2.
234 if ((bio->bio_offset & (ssp->dss_secsize - 1)) ||
235 (ssp->dss_secsize ^ (ssp->dss_secsize - 1)) !=
236 ((ssp->dss_secsize << 1) - 1)) {
237 kprintf("%s: invalid BIO offset, not sector aligned or"
238 " invalid sector size (not power of 2) %08llx %d\n",
239 devtoname(dev), bio->bio_offset, ssp->dss_secsize);
240 goto bad;
244 * EOF handling
246 if (secno + nsec > endsecno) {
248 * Return an error if beyond the end of the disk, or
249 * if B_BNOCLIP is set. Tell the system that we do not
250 * need to keep the buffer around.
252 if (secno > endsecno || (bp->b_flags & B_BNOCLIP))
253 goto bad;
256 * If exactly at end of disk, return an EOF. Throw away
257 * the buffer contents, if any, by setting B_INVAL.
259 if (secno == endsecno) {
260 bp->b_resid = bp->b_bcount;
261 bp->b_flags |= B_INVAL;
262 goto done;
266 * Else truncate
268 nsec = endsecno - secno;
269 bp->b_bcount = nsec * ssp->dss_secsize;
272 nbio = push_bio(bio);
273 nbio->bio_offset = (off_t)(sp->ds_offset + slicerel_secno) *
274 ssp->dss_secsize;
275 return (nbio);
277 bad_bcount:
278 kprintf(
279 "dscheck(%s): b_bcount %d is not on a sector boundary (ssize %d)\n",
280 devtoname(dev), bp->b_bcount, ssp->dss_secsize);
281 goto bad;
283 bad_blkno:
284 kprintf(
285 "dscheck(%s): bio_offset %lld is not on a sector boundary (ssize %d)\n",
286 devtoname(dev), bio->bio_offset, ssp->dss_secsize);
287 bad:
288 bp->b_error = EINVAL;
289 /* fall through */
290 error:
292 * Terminate the I/O with a ranging error. Since the buffer is
293 * either illegal or beyond the file EOF, mark it B_INVAL as well.
295 bp->b_resid = bp->b_bcount;
296 bp->b_flags |= B_ERROR | B_INVAL;
297 done:
299 * Caller must biodone() the originally passed bio if NULL is
300 * returned.
302 return (NULL);
305 void
306 dsclose(cdev_t dev, int mode, struct diskslices *ssp)
308 u_int32_t part;
309 u_int32_t slice;
310 struct diskslice *sp;
312 slice = dkslice(dev);
313 part = dkpart(dev);
314 if (slice < ssp->dss_nslices) {
315 sp = &ssp->dss_slices[slice];
316 dsclrmask(sp, part);
320 void
321 dsgone(struct diskslices **sspp)
323 int slice;
324 struct diskslice *sp;
325 struct diskslices *ssp;
327 for (slice = 0, ssp = *sspp; slice < ssp->dss_nslices; slice++) {
328 sp = &ssp->dss_slices[slice];
329 free_ds_label(ssp, slice);
331 kfree(ssp, M_DEVBUF);
332 *sspp = NULL;
336 * For the "write" commands (DIOCSDINFO and DIOCWDINFO), this
337 * is subject to the same restriction as dsopen().
340 dsioctl(cdev_t dev, u_long cmd, caddr_t data, int flags,
341 struct diskslices **sspp, struct disk_info *info)
343 int error;
344 disklabel_t lp;
345 disklabel_t lptmp;
346 disklabel_ops_t ops;
347 int old_wlabel;
348 u_int32_t openmask[DKMAXPARTITIONS/(sizeof(u_int32_t)*8)];
349 int part;
350 int slice;
351 struct diskslice *sp;
352 struct diskslices *ssp;
354 slice = dkslice(dev);
355 part = dkpart(dev);
356 ssp = *sspp;
357 if (slice >= ssp->dss_nslices)
358 return (EINVAL);
359 sp = &ssp->dss_slices[slice];
360 lp = sp->ds_label;
361 ops = sp->ds_ops; /* may be NULL if no label */
363 switch (cmd) {
364 case DIOCGDVIRGIN32:
365 ops = &disklabel32_ops;
366 /* fall through */
367 case DIOCGDVIRGIN64:
368 if (cmd != DIOCGDVIRGIN32)
369 ops = &disklabel64_ops;
371 * You can only retrieve a virgin disklabel on the whole
372 * disk slice or whole-slice partition.
374 if (slice != WHOLE_DISK_SLICE &&
375 part != WHOLE_SLICE_PART) {
376 return(EINVAL);
379 lp.opaque = data;
380 ops->op_makevirginlabel(lp, ssp, sp, info);
381 return (0);
383 case DIOCGDINFO32:
384 case DIOCGDINFO64:
386 * You can only retrieve a disklabel on the whole
387 * slice partition.
389 * We do not support labels directly on whole-disks
390 * any more (that is, disks without slices), unless the
391 * device driver has asked for a compatible label (e.g.
392 * for a CD) to allow booting off of storage that is
393 * otherwise unlabeled.
395 error = 0;
396 if (part != WHOLE_SLICE_PART)
397 return(EINVAL);
398 if (slice == WHOLE_DISK_SLICE &&
399 (info->d_dsflags & DSO_COMPATLABEL) == 0) {
400 return (ENODEV);
402 if (sp->ds_label.opaque == NULL) {
403 error = dsreadandsetlabel(dev, info->d_dsflags,
404 ssp, sp, info);
405 ops = sp->ds_ops; /* may be NULL */
409 * The type of label we found must match the type of
410 * label requested.
412 if (error == 0 && IOCPARM_LEN(cmd) != ops->labelsize)
413 error = ENOATTR;
414 if (error == 0)
415 bcopy(sp->ds_label.opaque, data, ops->labelsize);
416 return (error);
418 case DIOCGPART:
420 struct partinfo *dpart = (void *)data;
423 * The disk management layer may not have read the
424 * disklabel yet because simply opening a slice no
425 * longer 'probes' the disk that way. Be sure we
426 * have tried.
428 * We ignore any error.
430 if (sp->ds_label.opaque == NULL &&
431 part == WHOLE_SLICE_PART &&
432 slice != WHOLE_DISK_SLICE) {
433 dsreadandsetlabel(dev, info->d_dsflags,
434 ssp, sp, info);
435 ops = sp->ds_ops; /* may be NULL */
438 bzero(dpart, sizeof(*dpart));
439 dpart->media_offset = (u_int64_t)sp->ds_offset *
440 info->d_media_blksize;
441 dpart->media_size = (u_int64_t)sp->ds_size *
442 info->d_media_blksize;
443 dpart->media_blocks = sp->ds_size;
444 dpart->media_blksize = info->d_media_blksize;
445 dpart->reserved_blocks= sp->ds_reserved;
446 dpart->fstype_uuid = sp->ds_type_uuid;
447 dpart->storage_uuid = sp->ds_stor_uuid;
449 if (slice != WHOLE_DISK_SLICE &&
450 part != WHOLE_SLICE_PART) {
451 u_int64_t start;
452 u_int64_t blocks;
453 if (lp.opaque == NULL)
454 return(EINVAL);
455 if (ops->op_getpartbounds(ssp, lp, part,
456 &start, &blocks)) {
457 return(EINVAL);
459 ops->op_loadpartinfo(lp, part, dpart);
460 dpart->media_offset += start *
461 info->d_media_blksize;
462 dpart->media_size = blocks *
463 info->d_media_blksize;
464 dpart->media_blocks = blocks;
467 * partition starting sector (p_offset)
468 * requires slice's reserved areas to be
469 * adjusted.
471 if (dpart->reserved_blocks > start)
472 dpart->reserved_blocks -= start;
473 else
474 dpart->reserved_blocks = 0;
478 * Load remaining fields from the info structure
480 dpart->d_nheads = info->d_nheads;
481 dpart->d_ncylinders = info->d_ncylinders;
482 dpart->d_secpertrack = info->d_secpertrack;
483 dpart->d_secpercyl = info->d_secpercyl;
485 return (0);
487 case DIOCGSLICEINFO:
488 bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] -
489 (char *)ssp);
490 return (0);
492 case DIOCSDINFO32:
493 ops = &disklabel32_ops;
494 /* fall through */
495 case DIOCSDINFO64:
496 if (cmd != DIOCSDINFO32)
497 ops = &disklabel64_ops;
499 * You can write a disklabel on the whole disk slice or
500 * whole-slice partition.
502 if (slice != WHOLE_DISK_SLICE &&
503 part != WHOLE_SLICE_PART) {
504 return(EINVAL);
508 * We no longer support writing disklabels directly to media
509 * without there being a slice. Keep this as a separate
510 * conditional.
512 if (slice == WHOLE_DISK_SLICE)
513 return (ENODEV);
514 if (!(flags & FWRITE))
515 return (EBADF);
518 * If an existing label is present it must be the same
519 * type as the label being passed by the ioctl.
521 if (sp->ds_label.opaque && sp->ds_ops != ops)
522 return (ENOATTR);
525 * Create a temporary copy of the existing label
526 * (if present) so setdisklabel can compare it against
527 * the new label.
529 lp.opaque = kmalloc(ops->labelsize, M_DEVBUF, M_WAITOK);
530 if (sp->ds_label.opaque == NULL)
531 bzero(lp.opaque, ops->labelsize);
532 else
533 bcopy(sp->ds_label.opaque, lp.opaque, ops->labelsize);
534 if (sp->ds_label.opaque == NULL) {
535 bzero(openmask, sizeof(openmask));
536 } else {
537 bcopy(sp->ds_openmask, openmask, sizeof(openmask));
539 lptmp.opaque = data;
540 error = ops->op_setdisklabel(lp, lptmp, ssp, sp, openmask);
541 if (error != 0) {
542 kfree(lp.opaque, M_DEVBUF);
543 return (error);
545 free_ds_label(ssp, slice);
546 set_ds_label(ssp, slice, lp, ops);
547 return (0);
549 case DIOCSYNCSLICEINFO:
551 * This ioctl can only be done on the whole disk
553 if (slice != WHOLE_DISK_SLICE || part != WHOLE_SLICE_PART)
554 return (EINVAL);
556 if (*(int *)data == 0) {
557 for (slice = 0; slice < ssp->dss_nslices; slice++) {
558 struct diskslice *ds = &ssp->dss_slices[slice];
560 switch(dscountmask(ds)) {
561 case 0:
562 break;
563 case 1:
564 if (slice != WHOLE_DISK_SLICE)
565 return (EBUSY);
566 if (!dschkmask(ds, RAW_PART))
567 return (EBUSY);
568 break;
569 default:
570 return (EBUSY);
576 * Temporarily forget the current slices struct and read
577 * the current one.
579 * NOTE:
581 * XXX should wait for current accesses on this disk to
582 * complete, then lock out future accesses and opens.
584 *sspp = NULL;
585 error = dsopen(dev, S_IFCHR, ssp->dss_oflags, sspp, info);
586 if (error != 0) {
587 *sspp = ssp;
588 return (error);
592 * Reopen everything. This is a no-op except in the "force"
593 * case and when the raw bdev and cdev are both open. Abort
594 * if anything fails.
596 for (slice = 0; slice < ssp->dss_nslices; slice++) {
597 for (part = 0; part < DKMAXPARTITIONS; ++part) {
598 if (!dschkmask(&ssp->dss_slices[slice], part))
599 continue;
600 error = dsopen(dkmodslice(dkmodpart(dev, part),
601 slice),
602 S_IFCHR, ssp->dss_oflags, sspp,
603 info);
604 if (error != 0) {
605 *sspp = ssp;
606 return (EBUSY);
611 dsgone(&ssp);
612 return (0);
614 case DIOCWDINFO32:
615 case DIOCWDINFO64:
616 error = dsioctl(dev, ((cmd == DIOCWDINFO32) ?
617 DIOCSDINFO32 : DIOCSDINFO64),
618 data, flags, &ssp, info);
619 if (error == 0 && sp->ds_label.opaque == NULL)
620 error = EINVAL;
621 if (error != 0)
622 return (error);
625 * Allow the reserved area to be written, reload ops
626 * because the DIOCSDINFO op above may have installed
627 * a new label type.
629 ops = sp->ds_ops;
630 old_wlabel = sp->ds_wlabel;
631 set_ds_wlabel(ssp, slice, TRUE);
632 error = ops->op_writedisklabel(dev, ssp, sp, sp->ds_label);
633 set_ds_wlabel(ssp, slice, old_wlabel);
634 /* XXX should invalidate in-core label if write failed. */
635 return (error);
637 case DIOCWLABEL:
638 if (slice == WHOLE_DISK_SLICE)
639 return (ENODEV);
640 if (!(flags & FWRITE))
641 return (EBADF);
642 set_ds_wlabel(ssp, slice, *(int *)data != 0);
643 return (0);
645 default:
646 return (ENOIOCTL);
651 dsisopen(struct diskslices *ssp)
653 int slice;
655 if (ssp == NULL)
656 return (0);
657 for (slice = 0; slice < ssp->dss_nslices; slice++) {
658 if (dscountmask(&ssp->dss_slices[slice]))
659 return (1);
661 return (0);
665 * Allocate a slices "struct" and initialize it to contain only an empty
666 * compatibility slice (pointing to itself), a whole disk slice (covering
667 * the disk as described by the label), and (nslices - BASE_SLICES) empty
668 * slices beginning at BASE_SLICE.
670 * Note that the compatibility slice is no longer really a compatibility
671 * slice. It is slice 0 if a GPT label is present, and the dangerously
672 * dedicated slice if no slice table otherwise exists. Else it is 0-sized.
674 struct diskslices *
675 dsmakeslicestruct(int nslices, struct disk_info *info)
677 struct diskslice *sp;
678 struct diskslices *ssp;
680 ssp = kmalloc(offsetof(struct diskslices, dss_slices) +
681 nslices * sizeof *sp, M_DEVBUF, M_WAITOK);
682 ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
683 ssp->dss_nslices = nslices;
684 ssp->dss_oflags = 0;
687 * Figure out if we can use shifts or whether we have to
688 * use mod/multply to translate byte offsets into sector numbers.
690 if ((info->d_media_blksize ^ (info->d_media_blksize - 1)) ==
691 (info->d_media_blksize << 1) - 1) {
692 ssp->dss_secmult = info->d_media_blksize / DEV_BSIZE;
693 if (ssp->dss_secmult & (ssp->dss_secmult - 1))
694 ssp->dss_secshift = -1;
695 else
696 ssp->dss_secshift = ffs(ssp->dss_secmult) - 1;
697 } else {
698 ssp->dss_secmult = 0;
699 ssp->dss_secshift = -1;
701 ssp->dss_secsize = info->d_media_blksize;
702 sp = &ssp->dss_slices[0];
703 bzero(sp, nslices * sizeof *sp);
704 sp[WHOLE_DISK_SLICE].ds_size = info->d_media_blocks;
705 return (ssp);
708 char *
709 dsname(cdev_t dev, int unit, int slice, int part, char *partname)
711 static char name[32];
712 const char *dname;
713 int used;
715 dname = dev_dname(dev);
716 if (strlen(dname) > 16)
717 dname = "nametoolong";
718 ksnprintf(name, sizeof(name), "%s%d", dname, unit);
719 partname[0] = '\0';
720 used = strlen(name);
722 if (slice != WHOLE_DISK_SLICE) {
724 * slice or slice + partition. BASE_SLICE is s1, but
725 * the compatibility slice (0) needs to be s0.
727 used += ksnprintf(name + used, sizeof(name) - used,
728 "s%d", (slice ? slice - BASE_SLICE + 1 : 0));
729 if (part != WHOLE_SLICE_PART) {
730 used += ksnprintf(name + used, sizeof(name) - used,
731 "%c", 'a' + part);
732 partname[0] = 'a' + part;
733 partname[1] = 0;
735 } else if (part == WHOLE_SLICE_PART) {
737 * whole-disk-device, raw access to disk
739 /* no string extension */
740 } else if (part > 128) {
742 * whole-disk-device, extended raw access partitions.
743 * (typically used to access CD audio tracks)
745 used += ksnprintf(name + used, sizeof(name) - used,
746 "t%d", part - 128);
747 } else {
749 * whole-disk-device, illegal partition number
751 used += ksnprintf(name + used, sizeof(name) - used,
752 "?%d", part);
754 return (name);
758 * This should only be called when the unit is inactive and the strategy
759 * routine should not allow it to become active unless we call it. Our
760 * strategy routine must be special to allow activity.
763 dsopen(cdev_t dev, int mode, u_int flags,
764 struct diskslices **sspp, struct disk_info *info)
766 cdev_t dev1;
767 int error;
768 int need_init;
769 struct diskslice *sp;
770 struct diskslices *ssp;
771 int slice;
772 int part;
774 dev->si_bsize_phys = info->d_media_blksize;
777 * Do not attempt to read the slice table or disk label when
778 * accessing the whole-disk slice or a while-slice partition.
780 if (dkslice(dev) == WHOLE_DISK_SLICE)
781 flags |= DSO_ONESLICE | DSO_NOLABELS;
782 if (dkpart(dev) == WHOLE_SLICE_PART)
783 flags |= DSO_NOLABELS;
786 * Reinitialize the slice table unless there is an open device
787 * on the unit.
789 * It would be nice if we didn't have to do this but when a
790 * user is slicing and partitioning up a disk it is a lot safer
791 * to not take any chances.
793 ssp = *sspp;
794 need_init = !dsisopen(ssp);
795 if (ssp != NULL && need_init)
796 dsgone(sspp);
797 if (need_init) {
799 * Allocate a minimal slices "struct". This will become
800 * the final slices "struct" if we don't want real slices
801 * or if we can't find any real slices.
803 * Then scan the disk
805 *sspp = dsmakeslicestruct(BASE_SLICE, info);
807 if ((flags & DSO_ONESLICE) == 0) {
808 error = mbrinit(dev, info, sspp);
809 if (error != 0) {
810 dsgone(sspp);
811 return (error);
814 ssp = *sspp;
815 ssp->dss_oflags = flags;
818 * If there are no real slices, then make the compatiblity
819 * slice cover the whole disk.
821 if (ssp->dss_nslices == BASE_SLICE) {
822 sp = &ssp->dss_slices[COMPATIBILITY_SLICE];
824 sp->ds_size = info->d_media_blocks;
825 sp->ds_reserved = 0;
829 * Set dss_first_bsd_slice to point at the first BSD
830 * slice, if any.
832 for (slice = BASE_SLICE; slice < ssp->dss_nslices; slice++) {
833 sp = &ssp->dss_slices[slice];
834 if (sp->ds_type == DOSPTYP_386BSD /* XXX */) {
835 #if 0
836 struct diskslice *csp;
837 #endif
839 ssp->dss_first_bsd_slice = slice;
840 #if 0
842 * no longer supported, s0 is a real slice
843 * for GPT
845 csp = &ssp->dss_slices[COMPATIBILITY_SLICE];
846 csp->ds_offset = sp->ds_offset;
847 csp->ds_size = sp->ds_size;
848 csp->ds_type = sp->ds_type;
849 csp->ds_reserved = sp->ds_reserved;
850 #endif
851 break;
856 * By definition accesses via the whole-disk device do not
857 * specify any reserved areas. The whole disk may be read
858 * or written by the whole-disk device.
860 * The whole-disk slice does not ever have a label.
862 sp = &ssp->dss_slices[WHOLE_DISK_SLICE];
863 sp->ds_wlabel = TRUE;
864 sp->ds_reserved = 0;
868 * Load the disklabel for the slice being accessed unless it is
869 * a whole-disk-slice or a whole-slice-partition (as determined
870 * by DSO_NOLABELS).
872 * We could scan all slices here and try to load up their
873 * disklabels, but that would cause us to access slices that
874 * the user may otherwise not intend us to access, or corrupted
875 * slices, etc.
877 * XXX if there are no opens on the slice we may want to re-read
878 * the disklabel anyway, even if we have one cached.
880 slice = dkslice(dev);
881 if (slice >= ssp->dss_nslices)
882 return (ENXIO);
883 sp = &ssp->dss_slices[slice];
884 part = dkpart(dev);
886 if ((flags & DSO_NOLABELS) == 0 && sp->ds_label.opaque == NULL) {
887 dev1 = dkmodslice(dkmodpart(dev, WHOLE_SLICE_PART), slice);
890 * If opening a raw disk we do not try to
891 * read the disklabel now. No interpretation of raw disks
892 * (e.g. like 'da0') ever occurs. We will try to read the
893 * disklabel for a raw slice if asked to via DIOC* ioctls.
895 * Access to the label area is disallowed by default. Note
896 * however that accesses via WHOLE_DISK_SLICE, and accesses
897 * via WHOLE_SLICE_PART for slices without valid disklabels,
898 * will allow writes and ignore the flag.
900 set_ds_wlabel(ssp, slice, FALSE);
901 dsreadandsetlabel(dev1, flags, ssp, sp, info);
905 * If opening a particular partition the disklabel must exist and
906 * the partition must be present in the label.
908 * If the partition is the special whole-disk-slice no partition
909 * table need exist.
911 if (part != WHOLE_SLICE_PART && slice != WHOLE_DISK_SLICE) {
912 if (sp->ds_label.opaque == NULL ||
913 part >= sp->ds_ops->op_getnumparts(sp->ds_label)) {
914 return (EINVAL);
919 * Do not allow special raw-extension partitions to be opened
920 * if the device doesn't support them. Raw-extension partitions
921 * are typically used to handle CD tracks.
923 if (slice == WHOLE_DISK_SLICE && part >= 128 &&
924 part != WHOLE_SLICE_PART) {
925 if ((info->d_dsflags & DSO_RAWEXTENSIONS) == 0)
926 return (EINVAL);
930 * Ok, we are open
932 dssetmask(sp, part);
933 return (0);
937 * Attempt to read the disklabel. If successful, store it in sp->ds_label.
939 * If we cannot read the disklabel and DSO_COMPATLABEL is set, we construct
940 * a fake label covering the whole disk.
942 static
944 dsreadandsetlabel(cdev_t dev, u_int flags,
945 struct diskslices *ssp, struct diskslice *sp,
946 struct disk_info *info)
948 disklabel_t lp;
949 disklabel_ops_t ops;
950 const char *msg;
951 const char *sname;
952 char partname[2];
953 int slice = dkslice(dev);
956 * Probe the disklabel
958 lp.opaque = NULL;
959 sname = dsname(dev, dkunit(dev), slice, WHOLE_SLICE_PART, partname);
960 ops = &disklabel32_ops;
961 msg = ops->op_readdisklabel(dev, sp, &lp, info);
962 if (msg && strcmp(msg, "no disk label") == 0) {
963 ops = &disklabel64_ops;
964 msg = disklabel64_ops.op_readdisklabel(dev, sp, &lp, info);
968 * If we failed and COMPATLABEL is set, create a dummy disklabel.
970 if (msg != NULL && (flags & DSO_COMPATLABEL)) {
971 msg = NULL;
972 if (sp->ds_size >= 0x100000000ULL)
973 ops = &disklabel64_ops;
974 else
975 ops = &disklabel32_ops;
976 lp = ops->op_clone_label(info, sp);
978 if (msg != NULL) {
979 if (sp->ds_type == DOSPTYP_386BSD /* XXX */)
980 log(LOG_WARNING, "%s: cannot find label (%s)\n",
981 sname, msg);
982 if (lp.opaque)
983 kfree(lp.opaque, M_DEVBUF);
984 } else {
985 set_ds_label(ssp, slice, lp, ops);
986 set_ds_wlabel(ssp, slice, FALSE);
988 return (msg ? EINVAL : 0);
991 int64_t
992 dssize(cdev_t dev, struct diskslices **sspp)
994 disklabel_t lp;
995 disklabel_ops_t ops;
996 int part;
997 int slice;
998 struct diskslices *ssp;
999 u_int64_t start;
1000 u_int64_t blocks;
1002 slice = dkslice(dev);
1003 part = dkpart(dev);
1004 ssp = *sspp;
1005 if (ssp == NULL || slice >= ssp->dss_nslices
1006 || !dschkmask(&ssp->dss_slices[slice], part)) {
1007 if (dev_dopen(dev, FREAD, S_IFCHR, proc0.p_ucred) != 0)
1008 return (-1);
1009 dev_dclose(dev, FREAD, S_IFCHR);
1010 ssp = *sspp;
1012 lp = ssp->dss_slices[slice].ds_label;
1013 if (lp.opaque == NULL)
1014 return (-1);
1015 ops = ssp->dss_slices[slice].ds_ops;
1016 if (ops->op_getpartbounds(ssp, lp, part, &start, &blocks))
1017 return (-1);
1018 return ((int64_t)blocks);
1021 static void
1022 free_ds_label(struct diskslices *ssp, int slice)
1024 struct diskslice *sp;
1025 disklabel_t lp;
1027 sp = &ssp->dss_slices[slice];
1028 lp = sp->ds_label;
1029 if (lp.opaque != NULL) {
1030 kfree(lp.opaque, M_DEVBUF);
1031 lp.opaque = NULL;
1032 set_ds_label(ssp, slice, lp, NULL);
1036 static void
1037 set_ds_label(struct diskslices *ssp, int slice,
1038 disklabel_t lp, disklabel_ops_t ops)
1040 struct diskslice *sp = &ssp->dss_slices[slice];
1042 sp->ds_label = lp;
1043 sp->ds_ops = ops;
1044 if (lp.opaque && slice != WHOLE_DISK_SLICE)
1045 ops->op_adjust_label_reserved(ssp, slice, sp);
1046 else
1047 sp->ds_reserved = 0;
1050 static void
1051 set_ds_wlabel(struct diskslices *ssp, int slice, int wlabel)
1053 ssp->dss_slices[slice].ds_wlabel = wlabel;