xref: /dragonfly/sys/kern/subr_diskslice.c (revision 32efd857)
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 *
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
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
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
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
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 *
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 *
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
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
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
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
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
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
902 set_ds_wlabel(struct diskslices *ssp, int slice, int wlabel)
903 {
904 	ssp->dss_slices[slice].ds_wlabel = wlabel;
905 }
906 
907