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