vnd.c revision 1.216
1/*	$NetBSD: vnd.c,v 1.216 2011/05/23 21:30:56 joerg Exp $	*/
2
3/*-
4 * Copyright (c) 1996, 1997, 1998, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 1988 University of Utah.
34 * Copyright (c) 1990, 1993
35 *	The Regents of the University of California.  All rights reserved.
36 *
37 * This code is derived from software contributed to Berkeley by
38 * the Systems Programming Group of the University of Utah Computer
39 * Science Department.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 * 3. Neither the name of the University nor the names of its contributors
50 *    may be used to endorse or promote products derived from this software
51 *    without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 * from: Utah $Hdr: vn.c 1.13 94/04/02$
66 *
67 *	@(#)vn.c	8.9 (Berkeley) 5/14/95
68 */
69
70/*
71 * Vnode disk driver.
72 *
73 * Block/character interface to a vnode.  Allows one to treat a file
74 * as a disk (e.g. build a filesystem in it, mount it, etc.).
75 *
76 * NOTE 1: If the vnode supports the VOP_BMAP and VOP_STRATEGY operations,
77 * this uses them to avoid distorting the local buffer cache.  If those
78 * block-level operations are not available, this falls back to the regular
79 * read and write calls.  Using these may distort the cache in some cases
80 * but better have the driver working than preventing it to work on file
81 * systems where the block-level operations are not implemented for
82 * whatever reason.
83 *
84 * NOTE 2: There is a security issue involved with this driver.
85 * Once mounted all access to the contents of the "mapped" file via
86 * the special file is controlled by the permissions on the special
87 * file, the protection of the mapped file is ignored (effectively,
88 * by using root credentials in all transactions).
89 *
90 * NOTE 3: Doesn't interact with leases, should it?
91 */
92
93#include <sys/cdefs.h>
94__KERNEL_RCSID(0, "$NetBSD: vnd.c,v 1.216 2011/05/23 21:30:56 joerg Exp $");
95
96#if defined(_KERNEL_OPT)
97#include "opt_vnd.h"
98#include "opt_compat_netbsd.h"
99#endif
100
101#include <sys/param.h>
102#include <sys/systm.h>
103#include <sys/namei.h>
104#include <sys/proc.h>
105#include <sys/kthread.h>
106#include <sys/errno.h>
107#include <sys/buf.h>
108#include <sys/bufq.h>
109#include <sys/malloc.h>
110#include <sys/ioctl.h>
111#include <sys/disklabel.h>
112#include <sys/device.h>
113#include <sys/disk.h>
114#include <sys/stat.h>
115#include <sys/mount.h>
116#include <sys/vnode.h>
117#include <sys/file.h>
118#include <sys/uio.h>
119#include <sys/conf.h>
120#include <sys/kauth.h>
121
122#include <net/zlib.h>
123
124#include <miscfs/genfs/genfs.h>
125#include <miscfs/specfs/specdev.h>
126
127#include <dev/dkvar.h>
128#include <dev/vndvar.h>
129
130#include <prop/proplib.h>
131
132#if defined(VNDDEBUG) && !defined(DEBUG)
133#define DEBUG
134#endif
135
136#ifdef DEBUG
137int dovndcluster = 1;
138#define VDB_FOLLOW	0x01
139#define VDB_INIT	0x02
140#define VDB_IO		0x04
141#define VDB_LABEL	0x08
142int vnddebug = 0x00;
143#endif
144
145#define vndunit(x)	DISKUNIT(x)
146
147struct vndxfer {
148	struct buf vx_buf;
149	struct vnd_softc *vx_vnd;
150};
151#define	VND_BUFTOXFER(bp)	((struct vndxfer *)(void *)bp)
152
153#define VND_GETXFER(vnd)	pool_get(&(vnd)->sc_vxpool, PR_WAITOK)
154#define VND_PUTXFER(vnd, vx)	pool_put(&(vnd)->sc_vxpool, (vx))
155
156#define VNDLABELDEV(dev) \
157    (MAKEDISKDEV(major((dev)), vndunit((dev)), RAW_PART))
158
159/* called by main() at boot time */
160void	vndattach(int);
161
162static void	vndclear(struct vnd_softc *, int);
163static int	vnddoclear(struct vnd_softc *, int, int, bool);
164static int	vndsetcred(struct vnd_softc *, kauth_cred_t);
165static void	vndthrottle(struct vnd_softc *, struct vnode *);
166static void	vndiodone(struct buf *);
167#if 0
168static void	vndshutdown(void);
169#endif
170
171static void	vndgetdefaultlabel(struct vnd_softc *, struct disklabel *);
172static void	vndgetdisklabel(dev_t, struct vnd_softc *);
173
174static int	vndlock(struct vnd_softc *);
175static void	vndunlock(struct vnd_softc *);
176#ifdef VND_COMPRESSION
177static void	compstrategy(struct buf *, off_t);
178static void	*vnd_alloc(void *, u_int, u_int);
179static void	vnd_free(void *, void *);
180#endif /* VND_COMPRESSION */
181
182static void	vndthread(void *);
183static bool	vnode_has_op(const struct vnode *, int);
184static void	handle_with_rdwr(struct vnd_softc *, const struct buf *,
185		    struct buf *);
186static void	handle_with_strategy(struct vnd_softc *, const struct buf *,
187		    struct buf *);
188static void	vnd_set_properties(struct vnd_softc *);
189
190static dev_type_open(vndopen);
191static dev_type_close(vndclose);
192static dev_type_read(vndread);
193static dev_type_write(vndwrite);
194static dev_type_ioctl(vndioctl);
195static dev_type_strategy(vndstrategy);
196static dev_type_dump(vnddump);
197static dev_type_size(vndsize);
198
199const struct bdevsw vnd_bdevsw = {
200	vndopen, vndclose, vndstrategy, vndioctl, vnddump, vndsize, D_DISK
201};
202
203const struct cdevsw vnd_cdevsw = {
204	vndopen, vndclose, vndread, vndwrite, vndioctl,
205	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
206};
207
208static int	vnd_match(device_t, cfdata_t, void *);
209static void	vnd_attach(device_t, device_t, void *);
210static int	vnd_detach(device_t, int);
211
212CFATTACH_DECL3_NEW(vnd, sizeof(struct vnd_softc),
213    vnd_match, vnd_attach, vnd_detach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN);
214extern struct cfdriver vnd_cd;
215
216static struct vnd_softc	*vnd_spawn(int);
217int	vnd_destroy(device_t);
218
219static struct	dkdriver vnddkdriver = { vndstrategy, minphys };
220
221void
222vndattach(int num)
223{
224	int error;
225
226	error = config_cfattach_attach(vnd_cd.cd_name, &vnd_ca);
227	if (error)
228		aprint_error("%s: unable to register cfattach\n",
229		    vnd_cd.cd_name);
230}
231
232static int
233vnd_match(device_t self, cfdata_t cfdata, void *aux)
234{
235
236	return 1;
237}
238
239static void
240vnd_attach(device_t parent, device_t self, void *aux)
241{
242	struct vnd_softc *sc = device_private(self);
243
244	sc->sc_dev = self;
245	sc->sc_comp_offsets = NULL;
246	sc->sc_comp_buff = NULL;
247	sc->sc_comp_decombuf = NULL;
248	bufq_alloc(&sc->sc_tab, "disksort", BUFQ_SORT_RAWBLOCK);
249	disk_init(&sc->sc_dkdev, device_xname(self), &vnddkdriver);
250	if (!pmf_device_register(self, NULL, NULL))
251		aprint_error_dev(self, "couldn't establish power handler\n");
252}
253
254static int
255vnd_detach(device_t self, int flags)
256{
257	int error;
258	struct vnd_softc *sc = device_private(self);
259
260	if (sc->sc_flags & VNF_INITED) {
261		error = vnddoclear(sc, 0, -1, (flags & DETACH_FORCE) != 0);
262		if (error != 0)
263			return error;
264	}
265
266	pmf_device_deregister(self);
267	bufq_free(sc->sc_tab);
268	disk_destroy(&sc->sc_dkdev);
269
270	return 0;
271}
272
273static struct vnd_softc *
274vnd_spawn(int unit)
275{
276	cfdata_t cf;
277
278	cf = malloc(sizeof(*cf), M_DEVBUF, M_WAITOK);
279	cf->cf_name = vnd_cd.cd_name;
280	cf->cf_atname = vnd_cd.cd_name;
281	cf->cf_unit = unit;
282	cf->cf_fstate = FSTATE_STAR;
283
284	return device_private(config_attach_pseudo(cf));
285}
286
287int
288vnd_destroy(device_t dev)
289{
290	int error;
291	cfdata_t cf;
292
293	cf = device_cfdata(dev);
294	error = config_detach(dev, DETACH_QUIET);
295	if (error)
296		return error;
297	free(cf, M_DEVBUF);
298	return 0;
299}
300
301static int
302vndopen(dev_t dev, int flags, int mode, struct lwp *l)
303{
304	int unit = vndunit(dev);
305	struct vnd_softc *sc;
306	int error = 0, part, pmask;
307	struct disklabel *lp;
308
309#ifdef DEBUG
310	if (vnddebug & VDB_FOLLOW)
311		printf("vndopen(0x%"PRIx64", 0x%x, 0x%x, %p)\n", dev, flags, mode, l);
312#endif
313	sc = device_lookup_private(&vnd_cd, unit);
314	if (sc == NULL) {
315		sc = vnd_spawn(unit);
316		if (sc == NULL)
317			return ENOMEM;
318	}
319
320	if ((error = vndlock(sc)) != 0)
321		return error;
322
323	if ((sc->sc_flags & VNF_CLEARING) != 0) {
324		error = ENXIO;
325		goto done;
326	}
327
328	lp = sc->sc_dkdev.dk_label;
329
330	part = DISKPART(dev);
331	pmask = (1 << part);
332
333	/*
334	 * If we're initialized, check to see if there are any other
335	 * open partitions.  If not, then it's safe to update the
336	 * in-core disklabel.  Only read the disklabel if it is
337	 * not already valid.
338	 */
339	if ((sc->sc_flags & (VNF_INITED|VNF_VLABEL)) == VNF_INITED &&
340	    sc->sc_dkdev.dk_openmask == 0)
341		vndgetdisklabel(dev, sc);
342
343	/* Check that the partitions exists. */
344	if (part != RAW_PART) {
345		if (((sc->sc_flags & VNF_INITED) == 0) ||
346		    ((part >= lp->d_npartitions) ||
347		     (lp->d_partitions[part].p_fstype == FS_UNUSED))) {
348			error = ENXIO;
349			goto done;
350		}
351	}
352
353	/* Prevent our unit from being unconfigured while open. */
354	switch (mode) {
355	case S_IFCHR:
356		sc->sc_dkdev.dk_copenmask |= pmask;
357		break;
358
359	case S_IFBLK:
360		sc->sc_dkdev.dk_bopenmask |= pmask;
361		break;
362	}
363	sc->sc_dkdev.dk_openmask =
364	    sc->sc_dkdev.dk_copenmask | sc->sc_dkdev.dk_bopenmask;
365
366 done:
367	vndunlock(sc);
368	return error;
369}
370
371static int
372vndclose(dev_t dev, int flags, int mode, struct lwp *l)
373{
374	int unit = vndunit(dev);
375	struct vnd_softc *sc;
376	int error = 0, part;
377
378#ifdef DEBUG
379	if (vnddebug & VDB_FOLLOW)
380		printf("vndclose(0x%"PRIx64", 0x%x, 0x%x, %p)\n", dev, flags, mode, l);
381#endif
382	sc = device_lookup_private(&vnd_cd, unit);
383	if (sc == NULL)
384		return ENXIO;
385
386	if ((error = vndlock(sc)) != 0)
387		return error;
388
389	part = DISKPART(dev);
390
391	/* ...that much closer to allowing unconfiguration... */
392	switch (mode) {
393	case S_IFCHR:
394		sc->sc_dkdev.dk_copenmask &= ~(1 << part);
395		break;
396
397	case S_IFBLK:
398		sc->sc_dkdev.dk_bopenmask &= ~(1 << part);
399		break;
400	}
401	sc->sc_dkdev.dk_openmask =
402	    sc->sc_dkdev.dk_copenmask | sc->sc_dkdev.dk_bopenmask;
403
404	vndunlock(sc);
405
406	if ((sc->sc_flags & VNF_INITED) == 0) {
407		if ((error = vnd_destroy(sc->sc_dev)) != 0) {
408			aprint_error_dev(sc->sc_dev,
409			    "unable to detach instance\n");
410			return error;
411		}
412	}
413
414	return 0;
415}
416
417/*
418 * Queue the request, and wakeup the kernel thread to handle it.
419 */
420static void
421vndstrategy(struct buf *bp)
422{
423	int unit = vndunit(bp->b_dev);
424	struct vnd_softc *vnd =
425	    device_lookup_private(&vnd_cd, unit);
426	struct disklabel *lp;
427	daddr_t blkno;
428	int s = splbio();
429
430	if (vnd == NULL) {
431		bp->b_error = ENXIO;
432		goto done;
433	}
434	lp = vnd->sc_dkdev.dk_label;
435
436	if ((vnd->sc_flags & VNF_INITED) == 0) {
437		bp->b_error = ENXIO;
438		goto done;
439	}
440
441	/*
442	 * The transfer must be a whole number of blocks.
443	 */
444	if ((bp->b_bcount % lp->d_secsize) != 0) {
445		bp->b_error = EINVAL;
446		goto done;
447	}
448
449	/*
450	 * check if we're read-only.
451	 */
452	if ((vnd->sc_flags & VNF_READONLY) && !(bp->b_flags & B_READ)) {
453		bp->b_error = EACCES;
454		goto done;
455	}
456
457	/* If it's a nil transfer, wake up the top half now. */
458	if (bp->b_bcount == 0) {
459		goto done;
460	}
461
462	/*
463	 * Do bounds checking and adjust transfer.  If there's an error,
464	 * the bounds check will flag that for us.
465	 */
466	if (DISKPART(bp->b_dev) == RAW_PART) {
467		if (bounds_check_with_mediasize(bp, DEV_BSIZE,
468		    vnd->sc_size) <= 0)
469			goto done;
470	} else {
471		if (bounds_check_with_label(&vnd->sc_dkdev,
472		    bp, vnd->sc_flags & (VNF_WLABEL|VNF_LABELLING)) <= 0)
473			goto done;
474	}
475
476	/*
477	 * Put the block number in terms of the logical blocksize
478	 * of the "device".
479	 */
480
481	blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
482
483	/*
484	 * Translate the partition-relative block number to an absolute.
485	 */
486	if (DISKPART(bp->b_dev) != RAW_PART) {
487		struct partition *pp;
488
489		pp = &vnd->sc_dkdev.dk_label->d_partitions[
490		    DISKPART(bp->b_dev)];
491		blkno += pp->p_offset;
492	}
493	bp->b_rawblkno = blkno;
494
495#ifdef DEBUG
496	if (vnddebug & VDB_FOLLOW)
497		printf("vndstrategy(%p): unit %d\n", bp, unit);
498#endif
499	bufq_put(vnd->sc_tab, bp);
500	wakeup(&vnd->sc_tab);
501	splx(s);
502	return;
503
504done:
505	bp->b_resid = bp->b_bcount;
506	biodone(bp);
507	splx(s);
508}
509
510static bool
511vnode_has_strategy(struct vnd_softc *vnd)
512{
513	return vnode_has_op(vnd->sc_vp, VOFFSET(vop_bmap)) &&
514	    vnode_has_op(vnd->sc_vp, VOFFSET(vop_strategy));
515}
516
517/* XXX this function needs a reliable check to detect
518 * sparse files. Otherwise, bmap/strategy may be used
519 * and fail on non-allocated blocks. VOP_READ/VOP_WRITE
520 * works on sparse files.
521 */
522#if notyet
523static bool
524vnode_strategy_probe(struct vnd_softc *vnd)
525{
526	int error;
527	daddr_t nbn;
528
529	if (!vnode_has_strategy(vnd))
530		return false;
531
532	/* Convert the first logical block number to its
533	 * physical block number.
534	 */
535	error = 0;
536	vn_lock(vnd->sc_vp, LK_EXCLUSIVE | LK_RETRY);
537	error = VOP_BMAP(vnd->sc_vp, 0, NULL, &nbn, NULL);
538	VOP_UNLOCK(vnd->sc_vp);
539
540	/* Test if that worked. */
541	if (error == 0 && (long)nbn == -1)
542		return false;
543
544	return true;
545}
546#endif
547
548static void
549vndthread(void *arg)
550{
551	struct vnd_softc *vnd = arg;
552	bool usestrategy;
553	int s;
554
555	/* Determine whether we can *use* VOP_BMAP and VOP_STRATEGY to
556	 * directly access the backing vnode.  If we can, use these two
557	 * operations to avoid messing with the local buffer cache.
558	 * Otherwise fall back to regular VOP_READ/VOP_WRITE operations
559	 * which are guaranteed to work with any file system. */
560	usestrategy = vnode_has_strategy(vnd);
561
562#ifdef DEBUG
563	if (vnddebug & VDB_INIT)
564		printf("vndthread: vp %p, %s\n", vnd->sc_vp,
565		    usestrategy ?
566		    "using bmap/strategy operations" :
567		    "using read/write operations");
568#endif
569
570	s = splbio();
571	vnd->sc_flags |= VNF_KTHREAD;
572	wakeup(&vnd->sc_kthread);
573
574	/*
575	 * Dequeue requests and serve them depending on the available
576	 * vnode operations.
577	 */
578	while ((vnd->sc_flags & VNF_VUNCONF) == 0) {
579		struct vndxfer *vnx;
580		int flags;
581		struct buf *obp;
582		struct buf *bp;
583
584		obp = bufq_get(vnd->sc_tab);
585		if (obp == NULL) {
586			tsleep(&vnd->sc_tab, PRIBIO, "vndbp", 0);
587			continue;
588		};
589		splx(s);
590		flags = obp->b_flags;
591#ifdef DEBUG
592		if (vnddebug & VDB_FOLLOW)
593			printf("vndthread(%p)\n", obp);
594#endif
595
596		if (vnd->sc_vp->v_mount == NULL) {
597			obp->b_error = ENXIO;
598			goto done;
599		}
600#ifdef VND_COMPRESSION
601		/* handle a compressed read */
602		if ((flags & B_READ) != 0 && (vnd->sc_flags & VNF_COMP)) {
603			off_t bn;
604
605			/* Convert to a byte offset within the file. */
606			bn = obp->b_rawblkno *
607			    vnd->sc_dkdev.dk_label->d_secsize;
608
609			compstrategy(obp, bn);
610			goto done;
611		}
612#endif /* VND_COMPRESSION */
613
614		/*
615		 * Allocate a header for this transfer and link it to the
616		 * buffer
617		 */
618		s = splbio();
619		vnx = VND_GETXFER(vnd);
620		splx(s);
621		vnx->vx_vnd = vnd;
622
623		s = splbio();
624		while (vnd->sc_active >= vnd->sc_maxactive) {
625			tsleep(&vnd->sc_tab, PRIBIO, "vndac", 0);
626		}
627		vnd->sc_active++;
628		splx(s);
629
630		/* Instrumentation. */
631		disk_busy(&vnd->sc_dkdev);
632
633		bp = &vnx->vx_buf;
634		buf_init(bp);
635		bp->b_flags = (obp->b_flags & B_READ);
636		bp->b_oflags = obp->b_oflags;
637		bp->b_cflags = obp->b_cflags;
638		bp->b_iodone = vndiodone;
639		bp->b_private = obp;
640		bp->b_vp = vnd->sc_vp;
641		bp->b_objlock = &bp->b_vp->v_interlock;
642		bp->b_data = obp->b_data;
643		bp->b_bcount = obp->b_bcount;
644		BIO_COPYPRIO(bp, obp);
645
646		/* Handle the request using the appropriate operations. */
647		if (usestrategy)
648			handle_with_strategy(vnd, obp, bp);
649		else
650			handle_with_rdwr(vnd, obp, bp);
651
652		s = splbio();
653		continue;
654
655done:
656		biodone(obp);
657		s = splbio();
658	}
659
660	vnd->sc_flags &= (~VNF_KTHREAD | VNF_VUNCONF);
661	wakeup(&vnd->sc_kthread);
662	splx(s);
663	kthread_exit(0);
664}
665
666/*
667 * Checks if the given vnode supports the requested operation.
668 * The operation is specified the offset returned by VOFFSET.
669 *
670 * XXX The test below used to determine this is quite fragile
671 * because it relies on the file system to use genfs to specify
672 * unimplemented operations.  There might be another way to do
673 * it more cleanly.
674 */
675static bool
676vnode_has_op(const struct vnode *vp, int opoffset)
677{
678	int (*defaultp)(void *);
679	int (*opp)(void *);
680
681	defaultp = vp->v_op[VOFFSET(vop_default)];
682	opp = vp->v_op[opoffset];
683
684	return opp != defaultp && opp != genfs_eopnotsupp &&
685	    opp != genfs_badop && opp != genfs_nullop;
686}
687
688/*
689 * Handes the read/write request given in 'bp' using the vnode's VOP_READ
690 * and VOP_WRITE operations.
691 *
692 * 'obp' is a pointer to the original request fed to the vnd device.
693 */
694static void
695handle_with_rdwr(struct vnd_softc *vnd, const struct buf *obp, struct buf *bp)
696{
697	bool doread;
698	off_t offset;
699	size_t resid;
700	struct vnode *vp;
701
702	doread = bp->b_flags & B_READ;
703	offset = obp->b_rawblkno * vnd->sc_dkdev.dk_label->d_secsize;
704	vp = vnd->sc_vp;
705
706#if defined(DEBUG)
707	if (vnddebug & VDB_IO)
708		printf("vnd (rdwr): vp %p, %s, rawblkno 0x%" PRIx64
709		    ", secsize %d, offset %" PRIu64
710		    ", bcount %d\n",
711		    vp, doread ? "read" : "write", obp->b_rawblkno,
712		    vnd->sc_dkdev.dk_label->d_secsize, offset,
713		    bp->b_bcount);
714#endif
715
716	/* Issue the read or write operation. */
717	bp->b_error =
718	    vn_rdwr(doread ? UIO_READ : UIO_WRITE,
719	    vp, bp->b_data, bp->b_bcount, offset,
720	    UIO_SYSSPACE, 0, vnd->sc_cred, &resid, NULL);
721	bp->b_resid = resid;
722
723	/* We need to increase the number of outputs on the vnode if
724	 * there was any write to it. */
725	if (!doread) {
726		mutex_enter(&vp->v_interlock);
727		vp->v_numoutput++;
728		mutex_exit(&vp->v_interlock);
729	}
730
731	biodone(bp);
732}
733
734/*
735 * Handes the read/write request given in 'bp' using the vnode's VOP_BMAP
736 * and VOP_STRATEGY operations.
737 *
738 * 'obp' is a pointer to the original request fed to the vnd device.
739 */
740static void
741handle_with_strategy(struct vnd_softc *vnd, const struct buf *obp,
742    struct buf *bp)
743{
744	int bsize, error, flags, skipped;
745	size_t resid, sz;
746	off_t bn, offset;
747	struct vnode *vp;
748
749	flags = obp->b_flags;
750
751	if (!(flags & B_READ)) {
752		vp = bp->b_vp;
753		mutex_enter(&vp->v_interlock);
754		vp->v_numoutput++;
755		mutex_exit(&vp->v_interlock);
756	}
757
758	/* convert to a byte offset within the file. */
759	bn = obp->b_rawblkno * vnd->sc_dkdev.dk_label->d_secsize;
760
761	bsize = vnd->sc_vp->v_mount->mnt_stat.f_iosize;
762	skipped = 0;
763
764	/*
765	 * Break the request into bsize pieces and feed them
766	 * sequentially using VOP_BMAP/VOP_STRATEGY.
767	 * We do it this way to keep from flooding NFS servers if we
768	 * are connected to an NFS file.  This places the burden on
769	 * the client rather than the server.
770	 */
771	error = 0;
772	bp->b_resid = bp->b_bcount;
773	for (offset = 0, resid = bp->b_resid; resid;
774	    resid -= sz, offset += sz) {
775		struct buf *nbp;
776		daddr_t nbn;
777		int off, nra;
778
779		nra = 0;
780		vn_lock(vnd->sc_vp, LK_EXCLUSIVE | LK_RETRY);
781		error = VOP_BMAP(vnd->sc_vp, bn / bsize, &vp, &nbn, &nra);
782		VOP_UNLOCK(vnd->sc_vp);
783
784		if (error == 0 && (long)nbn == -1)
785			error = EIO;
786
787		/*
788		 * If there was an error or a hole in the file...punt.
789		 * Note that we may have to wait for any operations
790		 * that we have already fired off before releasing
791		 * the buffer.
792		 *
793		 * XXX we could deal with holes here but it would be
794		 * a hassle (in the write case).
795		 */
796		if (error) {
797			skipped += resid;
798			break;
799		}
800
801#ifdef DEBUG
802		if (!dovndcluster)
803			nra = 0;
804#endif
805
806		off = bn % bsize;
807		sz = MIN(((off_t)1 + nra) * bsize - off, resid);
808#ifdef	DEBUG
809		if (vnddebug & VDB_IO)
810			printf("vndstrategy: vp %p/%p bn 0x%qx/0x%" PRIx64
811			    " sz 0x%zx\n", vnd->sc_vp, vp, (long long)bn,
812			    nbn, sz);
813#endif
814
815		nbp = getiobuf(vp, true);
816		nestiobuf_setup(bp, nbp, offset, sz);
817		nbp->b_blkno = nbn + btodb(off);
818
819#if 0 /* XXX #ifdef DEBUG */
820		if (vnddebug & VDB_IO)
821			printf("vndstart(%ld): bp %p vp %p blkno "
822			    "0x%" PRIx64 " flags %x addr %p cnt 0x%x\n",
823			    (long) (vnd-vnd_softc), &nbp->vb_buf,
824			    nbp->vb_buf.b_vp, nbp->vb_buf.b_blkno,
825			    nbp->vb_buf.b_flags, nbp->vb_buf.b_data,
826			    nbp->vb_buf.b_bcount);
827#endif
828		VOP_STRATEGY(vp, nbp);
829		bn += sz;
830	}
831	nestiobuf_done(bp, skipped, error);
832}
833
834static void
835vndiodone(struct buf *bp)
836{
837	struct vndxfer *vnx = VND_BUFTOXFER(bp);
838	struct vnd_softc *vnd = vnx->vx_vnd;
839	struct buf *obp = bp->b_private;
840	int s = splbio();
841
842	KASSERT(&vnx->vx_buf == bp);
843	KASSERT(vnd->sc_active > 0);
844#ifdef DEBUG
845	if (vnddebug & VDB_IO) {
846		printf("vndiodone1: bp %p iodone: error %d\n",
847		    bp, bp->b_error);
848	}
849#endif
850	disk_unbusy(&vnd->sc_dkdev, bp->b_bcount - bp->b_resid,
851	    (bp->b_flags & B_READ));
852	vnd->sc_active--;
853	if (vnd->sc_active == 0) {
854		wakeup(&vnd->sc_tab);
855	}
856	splx(s);
857	obp->b_error = bp->b_error;
858	obp->b_resid = bp->b_resid;
859	buf_destroy(bp);
860	VND_PUTXFER(vnd, vnx);
861	biodone(obp);
862}
863
864/* ARGSUSED */
865static int
866vndread(dev_t dev, struct uio *uio, int flags)
867{
868	int unit = vndunit(dev);
869	struct vnd_softc *sc;
870
871#ifdef DEBUG
872	if (vnddebug & VDB_FOLLOW)
873		printf("vndread(0x%"PRIx64", %p)\n", dev, uio);
874#endif
875
876	sc = device_lookup_private(&vnd_cd, unit);
877	if (sc == NULL)
878		return ENXIO;
879
880	if ((sc->sc_flags & VNF_INITED) == 0)
881		return ENXIO;
882
883	return physio(vndstrategy, NULL, dev, B_READ, minphys, uio);
884}
885
886/* ARGSUSED */
887static int
888vndwrite(dev_t dev, struct uio *uio, int flags)
889{
890	int unit = vndunit(dev);
891	struct vnd_softc *sc;
892
893#ifdef DEBUG
894	if (vnddebug & VDB_FOLLOW)
895		printf("vndwrite(0x%"PRIx64", %p)\n", dev, uio);
896#endif
897
898	sc = device_lookup_private(&vnd_cd, unit);
899	if (sc == NULL)
900		return ENXIO;
901
902	if ((sc->sc_flags & VNF_INITED) == 0)
903		return ENXIO;
904
905	return physio(vndstrategy, NULL, dev, B_WRITE, minphys, uio);
906}
907
908static int
909vnd_cget(struct lwp *l, int unit, int *un, struct vattr *va)
910{
911	struct vnd_softc *vnd;
912
913	if (*un == -1)
914		*un = unit;
915	if (*un < 0)
916		return EINVAL;
917
918	vnd = device_lookup_private(&vnd_cd, *un);
919	if (vnd == NULL)
920		return (*un >= vnd_cd.cd_ndevs) ? ENXIO : -1;
921
922	if ((vnd->sc_flags & VNF_INITED) == 0)
923		return -1;
924
925	return VOP_GETATTR(vnd->sc_vp, va, l->l_cred);
926}
927
928static int
929vnddoclear(struct vnd_softc *vnd, int pmask, int minor, bool force)
930{
931	int error;
932
933	if ((error = vndlock(vnd)) != 0)
934		return error;
935
936	/*
937	 * Don't unconfigure if any other partitions are open
938	 * or if both the character and block flavors of this
939	 * partition are open.
940	 */
941	if (DK_BUSY(vnd, pmask) && !force) {
942		vndunlock(vnd);
943		return EBUSY;
944	}
945
946	/*
947	 * XXX vndclear() might call vndclose() implicitly;
948	 * release lock to avoid recursion
949	 *
950	 * Set VNF_CLEARING to prevent vndopen() from
951	 * sneaking in after we vndunlock().
952	 */
953	vnd->sc_flags |= VNF_CLEARING;
954	vndunlock(vnd);
955	vndclear(vnd, minor);
956#ifdef DEBUG
957	if (vnddebug & VDB_INIT)
958		printf("vndioctl: CLRed\n");
959#endif
960
961	/* Destroy the xfer and buffer pools. */
962	pool_destroy(&vnd->sc_vxpool);
963
964	/* Detach the disk. */
965	disk_detach(&vnd->sc_dkdev);
966
967	return 0;
968}
969
970/* ARGSUSED */
971static int
972vndioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
973{
974	bool force;
975	int unit = vndunit(dev);
976	struct vnd_softc *vnd;
977	struct vnd_ioctl *vio;
978	struct vattr vattr;
979	struct pathbuf *pb;
980	struct nameidata nd;
981	int error, part, pmask;
982	size_t geomsize;
983	int fflags;
984#ifdef __HAVE_OLD_DISKLABEL
985	struct disklabel newlabel;
986#endif
987	struct dkwedge_info *dkw;
988	struct dkwedge_list *dkwl;
989
990#ifdef DEBUG
991	if (vnddebug & VDB_FOLLOW)
992		printf("vndioctl(0x%"PRIx64", 0x%lx, %p, 0x%x, %p): unit %d\n",
993		    dev, cmd, data, flag, l->l_proc, unit);
994#endif
995	vnd = device_lookup_private(&vnd_cd, unit);
996	if (vnd == NULL &&
997#ifdef COMPAT_30
998	    cmd != VNDIOCGET30 &&
999#endif
1000#ifdef COMPAT_50
1001	    cmd != VNDIOCGET50 &&
1002#endif
1003	    cmd != VNDIOCGET)
1004		return ENXIO;
1005	vio = (struct vnd_ioctl *)data;
1006
1007	/* Must be open for writes for these commands... */
1008	switch (cmd) {
1009	case VNDIOCSET:
1010	case VNDIOCCLR:
1011#ifdef COMPAT_50
1012	case VNDIOCSET50:
1013	case VNDIOCCLR50:
1014#endif
1015	case DIOCSDINFO:
1016	case DIOCWDINFO:
1017#ifdef __HAVE_OLD_DISKLABEL
1018	case ODIOCSDINFO:
1019	case ODIOCWDINFO:
1020#endif
1021	case DIOCKLABEL:
1022	case DIOCWLABEL:
1023		if ((flag & FWRITE) == 0)
1024			return EBADF;
1025	}
1026
1027	/* Must be initialized for these... */
1028	switch (cmd) {
1029	case VNDIOCCLR:
1030#ifdef VNDIOCCLR50
1031	case VNDIOCCLR50:
1032#endif
1033	case DIOCGDINFO:
1034	case DIOCSDINFO:
1035	case DIOCWDINFO:
1036	case DIOCGPART:
1037	case DIOCKLABEL:
1038	case DIOCWLABEL:
1039	case DIOCGDEFLABEL:
1040	case DIOCCACHESYNC:
1041#ifdef __HAVE_OLD_DISKLABEL
1042	case ODIOCGDINFO:
1043	case ODIOCSDINFO:
1044	case ODIOCWDINFO:
1045	case ODIOCGDEFLABEL:
1046#endif
1047		if ((vnd->sc_flags & VNF_INITED) == 0)
1048			return ENXIO;
1049	}
1050
1051	switch (cmd) {
1052#ifdef VNDIOCSET50
1053	case VNDIOCSET50:
1054#endif
1055	case VNDIOCSET:
1056		if (vnd->sc_flags & VNF_INITED)
1057			return EBUSY;
1058
1059		if ((error = vndlock(vnd)) != 0)
1060			return error;
1061
1062		fflags = FREAD;
1063		if ((vio->vnd_flags & VNDIOF_READONLY) == 0)
1064			fflags |= FWRITE;
1065		error = pathbuf_copyin(vio->vnd_file, &pb);
1066		if (error) {
1067			goto unlock_and_exit;
1068		}
1069		NDINIT(&nd, LOOKUP, FOLLOW, pb);
1070		if ((error = vn_open(&nd, fflags, 0)) != 0) {
1071			pathbuf_destroy(pb);
1072			goto unlock_and_exit;
1073		}
1074		KASSERT(l);
1075		error = VOP_GETATTR(nd.ni_vp, &vattr, l->l_cred);
1076		if (!error && nd.ni_vp->v_type != VREG)
1077			error = EOPNOTSUPP;
1078		if (!error && vattr.va_bytes < vattr.va_size)
1079			/* File is definitely sparse, reject here */
1080			error = EINVAL;
1081		if (error) {
1082			VOP_UNLOCK(nd.ni_vp);
1083			goto close_and_exit;
1084		}
1085
1086		/* If using a compressed file, initialize its info */
1087		/* (or abort with an error if kernel has no compression) */
1088		if (vio->vnd_flags & VNF_COMP) {
1089#ifdef VND_COMPRESSION
1090			struct vnd_comp_header *ch;
1091			int i;
1092			u_int32_t comp_size;
1093			u_int32_t comp_maxsize;
1094
1095			/* allocate space for compresed file header */
1096			ch = malloc(sizeof(struct vnd_comp_header),
1097			M_TEMP, M_WAITOK);
1098
1099			/* read compressed file header */
1100			error = vn_rdwr(UIO_READ, nd.ni_vp, (void *)ch,
1101			  sizeof(struct vnd_comp_header), 0, UIO_SYSSPACE,
1102			  IO_UNIT|IO_NODELOCKED, l->l_cred, NULL, NULL);
1103			if (error) {
1104				free(ch, M_TEMP);
1105				VOP_UNLOCK(nd.ni_vp);
1106				goto close_and_exit;
1107			}
1108
1109			/* save some header info */
1110			vnd->sc_comp_blksz = ntohl(ch->block_size);
1111			/* note last offset is the file byte size */
1112			vnd->sc_comp_numoffs = ntohl(ch->num_blocks)+1;
1113			free(ch, M_TEMP);
1114			if (vnd->sc_comp_blksz == 0 ||
1115			    vnd->sc_comp_blksz % DEV_BSIZE !=0) {
1116				VOP_UNLOCK(nd.ni_vp);
1117				error = EINVAL;
1118				goto close_and_exit;
1119			}
1120			if (sizeof(struct vnd_comp_header) +
1121			  sizeof(u_int64_t) * vnd->sc_comp_numoffs >
1122			  vattr.va_size) {
1123				VOP_UNLOCK(nd.ni_vp);
1124				error = EINVAL;
1125				goto close_and_exit;
1126			}
1127
1128			/* set decompressed file size */
1129			vattr.va_size =
1130			    ((u_quad_t)vnd->sc_comp_numoffs - 1) *
1131			     (u_quad_t)vnd->sc_comp_blksz;
1132
1133			/* allocate space for all the compressed offsets */
1134			vnd->sc_comp_offsets =
1135			malloc(sizeof(u_int64_t) * vnd->sc_comp_numoffs,
1136			M_DEVBUF, M_WAITOK);
1137
1138			/* read in the offsets */
1139			error = vn_rdwr(UIO_READ, nd.ni_vp,
1140			  (void *)vnd->sc_comp_offsets,
1141			  sizeof(u_int64_t) * vnd->sc_comp_numoffs,
1142			  sizeof(struct vnd_comp_header), UIO_SYSSPACE,
1143			  IO_UNIT|IO_NODELOCKED, l->l_cred, NULL, NULL);
1144			if (error) {
1145				VOP_UNLOCK(nd.ni_vp);
1146				goto close_and_exit;
1147			}
1148			/*
1149			 * find largest block size (used for allocation limit).
1150			 * Also convert offset to native byte order.
1151			 */
1152			comp_maxsize = 0;
1153			for (i = 0; i < vnd->sc_comp_numoffs - 1; i++) {
1154				vnd->sc_comp_offsets[i] =
1155				  be64toh(vnd->sc_comp_offsets[i]);
1156				comp_size = be64toh(vnd->sc_comp_offsets[i + 1])
1157				  - vnd->sc_comp_offsets[i];
1158				if (comp_size > comp_maxsize)
1159					comp_maxsize = comp_size;
1160			}
1161			vnd->sc_comp_offsets[vnd->sc_comp_numoffs - 1] =
1162			  be64toh(vnd->sc_comp_offsets[vnd->sc_comp_numoffs - 1]);
1163
1164			/* create compressed data buffer */
1165			vnd->sc_comp_buff = malloc(comp_maxsize,
1166			  M_DEVBUF, M_WAITOK);
1167
1168			/* create decompressed buffer */
1169			vnd->sc_comp_decombuf = malloc(vnd->sc_comp_blksz,
1170			  M_DEVBUF, M_WAITOK);
1171			vnd->sc_comp_buffblk = -1;
1172
1173			/* Initialize decompress stream */
1174			memset(&vnd->sc_comp_stream, 0, sizeof(z_stream));
1175			vnd->sc_comp_stream.zalloc = vnd_alloc;
1176			vnd->sc_comp_stream.zfree = vnd_free;
1177			error = inflateInit2(&vnd->sc_comp_stream, MAX_WBITS);
1178			if (error) {
1179				if (vnd->sc_comp_stream.msg)
1180					printf("vnd%d: compressed file, %s\n",
1181					  unit, vnd->sc_comp_stream.msg);
1182				VOP_UNLOCK(nd.ni_vp);
1183				error = EINVAL;
1184				goto close_and_exit;
1185			}
1186
1187			vnd->sc_flags |= VNF_COMP | VNF_READONLY;
1188#else /* !VND_COMPRESSION */
1189			VOP_UNLOCK(nd.ni_vp);
1190			error = EOPNOTSUPP;
1191			goto close_and_exit;
1192#endif /* VND_COMPRESSION */
1193		}
1194
1195		VOP_UNLOCK(nd.ni_vp);
1196		vnd->sc_vp = nd.ni_vp;
1197		vnd->sc_size = btodb(vattr.va_size);	/* note truncation */
1198
1199		/*
1200		 * Use pseudo-geometry specified.  If none was provided,
1201		 * use "standard" Adaptec fictitious geometry.
1202		 */
1203		if (vio->vnd_flags & VNDIOF_HASGEOM) {
1204
1205			memcpy(&vnd->sc_geom, &vio->vnd_geom,
1206			    sizeof(vio->vnd_geom));
1207
1208			/*
1209			 * Sanity-check the sector size.
1210			 * XXX Don't allow secsize < DEV_BSIZE.	 Should
1211			 * XXX we?
1212			 */
1213			if (vnd->sc_geom.vng_secsize < DEV_BSIZE ||
1214			    (vnd->sc_geom.vng_secsize % DEV_BSIZE) != 0 ||
1215			    vnd->sc_geom.vng_ncylinders == 0 ||
1216			    (vnd->sc_geom.vng_ntracks *
1217			     vnd->sc_geom.vng_nsectors) == 0) {
1218				error = EINVAL;
1219				goto close_and_exit;
1220			}
1221
1222			/*
1223			 * Compute the size (in DEV_BSIZE blocks) specified
1224			 * by the geometry.
1225			 */
1226			geomsize = (vnd->sc_geom.vng_nsectors *
1227			    vnd->sc_geom.vng_ntracks *
1228			    vnd->sc_geom.vng_ncylinders) *
1229			    (vnd->sc_geom.vng_secsize / DEV_BSIZE);
1230
1231			/*
1232			 * Sanity-check the size against the specified
1233			 * geometry.
1234			 */
1235			if (vnd->sc_size < geomsize) {
1236				error = EINVAL;
1237				goto close_and_exit;
1238			}
1239		} else if (vnd->sc_size >= (32 * 64)) {
1240			/*
1241			 * Size must be at least 2048 DEV_BSIZE blocks
1242			 * (1M) in order to use this geometry.
1243			 */
1244			vnd->sc_geom.vng_secsize = DEV_BSIZE;
1245			vnd->sc_geom.vng_nsectors = 32;
1246			vnd->sc_geom.vng_ntracks = 64;
1247			vnd->sc_geom.vng_ncylinders = vnd->sc_size / (64 * 32);
1248		} else {
1249			vnd->sc_geom.vng_secsize = DEV_BSIZE;
1250			vnd->sc_geom.vng_nsectors = 1;
1251			vnd->sc_geom.vng_ntracks = 1;
1252			vnd->sc_geom.vng_ncylinders = vnd->sc_size;
1253		}
1254
1255		vnd_set_properties(vnd);
1256
1257		if (vio->vnd_flags & VNDIOF_READONLY) {
1258			vnd->sc_flags |= VNF_READONLY;
1259		}
1260
1261		if ((error = vndsetcred(vnd, l->l_cred)) != 0)
1262			goto close_and_exit;
1263
1264		vndthrottle(vnd, vnd->sc_vp);
1265		vio->vnd_osize = dbtob(vnd->sc_size);
1266#ifdef VNDIOCSET50
1267		if (cmd != VNDIOCSET50)
1268#endif
1269			vio->vnd_size = dbtob(vnd->sc_size);
1270		vnd->sc_flags |= VNF_INITED;
1271
1272		/* create the kernel thread, wait for it to be up */
1273		error = kthread_create(PRI_NONE, 0, NULL, vndthread, vnd,
1274		    &vnd->sc_kthread, "%s", device_xname(vnd->sc_dev));
1275		if (error)
1276			goto close_and_exit;
1277		while ((vnd->sc_flags & VNF_KTHREAD) == 0) {
1278			tsleep(&vnd->sc_kthread, PRIBIO, "vndthr", 0);
1279		}
1280#ifdef DEBUG
1281		if (vnddebug & VDB_INIT)
1282			printf("vndioctl: SET vp %p size 0x%lx %d/%d/%d/%d\n",
1283			    vnd->sc_vp, (unsigned long) vnd->sc_size,
1284			    vnd->sc_geom.vng_secsize,
1285			    vnd->sc_geom.vng_nsectors,
1286			    vnd->sc_geom.vng_ntracks,
1287			    vnd->sc_geom.vng_ncylinders);
1288#endif
1289
1290		/* Attach the disk. */
1291		disk_attach(&vnd->sc_dkdev);
1292		disk_blocksize(&vnd->sc_dkdev, vnd->sc_geom.vng_secsize);
1293
1294		/* Initialize the xfer and buffer pools. */
1295		pool_init(&vnd->sc_vxpool, sizeof(struct vndxfer), 0,
1296		    0, 0, "vndxpl", NULL, IPL_BIO);
1297
1298		/* Try and read the disklabel. */
1299		vndgetdisklabel(dev, vnd);
1300
1301		vndunlock(vnd);
1302
1303		pathbuf_destroy(pb);
1304		break;
1305
1306close_and_exit:
1307		(void) vn_close(nd.ni_vp, fflags, l->l_cred);
1308		pathbuf_destroy(pb);
1309unlock_and_exit:
1310#ifdef VND_COMPRESSION
1311		/* free any allocated memory (for compressed file) */
1312		if (vnd->sc_comp_offsets) {
1313			free(vnd->sc_comp_offsets, M_DEVBUF);
1314			vnd->sc_comp_offsets = NULL;
1315		}
1316		if (vnd->sc_comp_buff) {
1317			free(vnd->sc_comp_buff, M_DEVBUF);
1318			vnd->sc_comp_buff = NULL;
1319		}
1320		if (vnd->sc_comp_decombuf) {
1321			free(vnd->sc_comp_decombuf, M_DEVBUF);
1322			vnd->sc_comp_decombuf = NULL;
1323		}
1324#endif /* VND_COMPRESSION */
1325		vndunlock(vnd);
1326		return error;
1327
1328#ifdef VNDIOCCLR50
1329	case VNDIOCCLR50:
1330#endif
1331	case VNDIOCCLR:
1332		part = DISKPART(dev);
1333		pmask = (1 << part);
1334		force = (vio->vnd_flags & VNDIOF_FORCE) != 0;
1335
1336		if ((error = vnddoclear(vnd, pmask, minor(dev), force)) != 0)
1337			return error;
1338
1339		break;
1340
1341#ifdef COMPAT_30
1342	case VNDIOCGET30: {
1343		struct vnd_user30 *vnu;
1344		struct vattr va;
1345		vnu = (struct vnd_user30 *)data;
1346		KASSERT(l);
1347		switch (error = vnd_cget(l, unit, &vnu->vnu_unit, &va)) {
1348		case 0:
1349			vnu->vnu_dev = va.va_fsid;
1350			vnu->vnu_ino = va.va_fileid;
1351			break;
1352		case -1:
1353			/* unused is not an error */
1354			vnu->vnu_dev = 0;
1355			vnu->vnu_ino = 0;
1356			break;
1357		default:
1358			return error;
1359		}
1360		break;
1361	}
1362#endif
1363
1364#ifdef COMPAT_50
1365	case VNDIOCGET50: {
1366		struct vnd_user50 *vnu;
1367		struct vattr va;
1368		vnu = (struct vnd_user50 *)data;
1369		KASSERT(l);
1370		switch (error = vnd_cget(l, unit, &vnu->vnu_unit, &va)) {
1371		case 0:
1372			vnu->vnu_dev = va.va_fsid;
1373			vnu->vnu_ino = va.va_fileid;
1374			break;
1375		case -1:
1376			/* unused is not an error */
1377			vnu->vnu_dev = 0;
1378			vnu->vnu_ino = 0;
1379			break;
1380		default:
1381			return error;
1382		}
1383		break;
1384	}
1385#endif
1386
1387	case VNDIOCGET: {
1388		struct vnd_user *vnu;
1389		struct vattr va;
1390		vnu = (struct vnd_user *)data;
1391		KASSERT(l);
1392		switch (error = vnd_cget(l, unit, &vnu->vnu_unit, &va)) {
1393		case 0:
1394			vnu->vnu_dev = va.va_fsid;
1395			vnu->vnu_ino = va.va_fileid;
1396			break;
1397		case -1:
1398			/* unused is not an error */
1399			vnu->vnu_dev = 0;
1400			vnu->vnu_ino = 0;
1401			break;
1402		default:
1403			return error;
1404		}
1405		break;
1406	}
1407
1408	case DIOCGDINFO:
1409		*(struct disklabel *)data = *(vnd->sc_dkdev.dk_label);
1410		break;
1411
1412#ifdef __HAVE_OLD_DISKLABEL
1413	case ODIOCGDINFO:
1414		newlabel = *(vnd->sc_dkdev.dk_label);
1415		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
1416			return ENOTTY;
1417		memcpy(data, &newlabel, sizeof (struct olddisklabel));
1418		break;
1419#endif
1420
1421	case DIOCGPART:
1422		((struct partinfo *)data)->disklab = vnd->sc_dkdev.dk_label;
1423		((struct partinfo *)data)->part =
1424		    &vnd->sc_dkdev.dk_label->d_partitions[DISKPART(dev)];
1425		break;
1426
1427	case DIOCWDINFO:
1428	case DIOCSDINFO:
1429#ifdef __HAVE_OLD_DISKLABEL
1430	case ODIOCWDINFO:
1431	case ODIOCSDINFO:
1432#endif
1433	{
1434		struct disklabel *lp;
1435
1436		if ((error = vndlock(vnd)) != 0)
1437			return error;
1438
1439		vnd->sc_flags |= VNF_LABELLING;
1440
1441#ifdef __HAVE_OLD_DISKLABEL
1442		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
1443			memset(&newlabel, 0, sizeof newlabel);
1444			memcpy(&newlabel, data, sizeof (struct olddisklabel));
1445			lp = &newlabel;
1446		} else
1447#endif
1448		lp = (struct disklabel *)data;
1449
1450		error = setdisklabel(vnd->sc_dkdev.dk_label,
1451		    lp, 0, vnd->sc_dkdev.dk_cpulabel);
1452		if (error == 0) {
1453			if (cmd == DIOCWDINFO
1454#ifdef __HAVE_OLD_DISKLABEL
1455			    || cmd == ODIOCWDINFO
1456#endif
1457			   )
1458				error = writedisklabel(VNDLABELDEV(dev),
1459				    vndstrategy, vnd->sc_dkdev.dk_label,
1460				    vnd->sc_dkdev.dk_cpulabel);
1461		}
1462
1463		vnd->sc_flags &= ~VNF_LABELLING;
1464
1465		vndunlock(vnd);
1466
1467		if (error)
1468			return error;
1469		break;
1470	}
1471
1472	case DIOCKLABEL:
1473		if (*(int *)data != 0)
1474			vnd->sc_flags |= VNF_KLABEL;
1475		else
1476			vnd->sc_flags &= ~VNF_KLABEL;
1477		break;
1478
1479	case DIOCWLABEL:
1480		if (*(int *)data != 0)
1481			vnd->sc_flags |= VNF_WLABEL;
1482		else
1483			vnd->sc_flags &= ~VNF_WLABEL;
1484		break;
1485
1486	case DIOCGDEFLABEL:
1487		vndgetdefaultlabel(vnd, (struct disklabel *)data);
1488		break;
1489
1490#ifdef __HAVE_OLD_DISKLABEL
1491	case ODIOCGDEFLABEL:
1492		vndgetdefaultlabel(vnd, &newlabel);
1493		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
1494			return ENOTTY;
1495		memcpy(data, &newlabel, sizeof (struct olddisklabel));
1496		break;
1497#endif
1498
1499	case DIOCCACHESYNC:
1500		vn_lock(vnd->sc_vp, LK_EXCLUSIVE | LK_RETRY);
1501		error = VOP_FSYNC(vnd->sc_vp, vnd->sc_cred,
1502		    FSYNC_WAIT | FSYNC_DATAONLY | FSYNC_CACHE, 0, 0);
1503		VOP_UNLOCK(vnd->sc_vp);
1504		return error;
1505
1506	case DIOCAWEDGE:
1507		dkw = (void *) data;
1508
1509		if ((flag & FWRITE) == 0)
1510			return EBADF;
1511
1512		/* If the ioctl happens here, the parent is us. */
1513		strlcpy(dkw->dkw_parent, device_xname(vnd->sc_dev),
1514		    sizeof(dkw->dkw_parent));
1515		return dkwedge_add(dkw);
1516
1517	case DIOCDWEDGE:
1518		dkw = (void *) data;
1519
1520		if ((flag & FWRITE) == 0)
1521			return EBADF;
1522
1523		/* If the ioctl happens here, the parent is us. */
1524		strlcpy(dkw->dkw_parent, device_xname(vnd->sc_dev),
1525		    sizeof(dkw->dkw_parent));
1526		return dkwedge_del(dkw);
1527
1528	case DIOCLWEDGES:
1529		dkwl = (void *) data;
1530
1531		return dkwedge_list(&vnd->sc_dkdev, dkwl, l);
1532
1533	default:
1534		return ENOTTY;
1535	}
1536
1537	return 0;
1538}
1539
1540/*
1541 * Duplicate the current processes' credentials.  Since we are called only
1542 * as the result of a SET ioctl and only root can do that, any future access
1543 * to this "disk" is essentially as root.  Note that credentials may change
1544 * if some other uid can write directly to the mapped file (NFS).
1545 */
1546static int
1547vndsetcred(struct vnd_softc *vnd, kauth_cred_t cred)
1548{
1549	struct uio auio;
1550	struct iovec aiov;
1551	char *tmpbuf;
1552	int error;
1553
1554	vnd->sc_cred = kauth_cred_dup(cred);
1555	tmpbuf = malloc(DEV_BSIZE, M_TEMP, M_WAITOK);
1556
1557	/* XXX: Horrible kludge to establish credentials for NFS */
1558	aiov.iov_base = tmpbuf;
1559	aiov.iov_len = min(DEV_BSIZE, dbtob(vnd->sc_size));
1560	auio.uio_iov = &aiov;
1561	auio.uio_iovcnt = 1;
1562	auio.uio_offset = 0;
1563	auio.uio_rw = UIO_READ;
1564	auio.uio_resid = aiov.iov_len;
1565	UIO_SETUP_SYSSPACE(&auio);
1566	vn_lock(vnd->sc_vp, LK_EXCLUSIVE | LK_RETRY);
1567	error = VOP_READ(vnd->sc_vp, &auio, 0, vnd->sc_cred);
1568	if (error == 0) {
1569		/*
1570		 * Because vnd does all IO directly through the vnode
1571		 * we need to flush (at least) the buffer from the above
1572		 * VOP_READ from the buffer cache to prevent cache
1573		 * incoherencies.  Also, be careful to write dirty
1574		 * buffers back to stable storage.
1575		 */
1576		error = vinvalbuf(vnd->sc_vp, V_SAVE, vnd->sc_cred,
1577			    curlwp, 0, 0);
1578	}
1579	VOP_UNLOCK(vnd->sc_vp);
1580
1581	free(tmpbuf, M_TEMP);
1582	return error;
1583}
1584
1585/*
1586 * Set maxactive based on FS type
1587 */
1588static void
1589vndthrottle(struct vnd_softc *vnd, struct vnode *vp)
1590{
1591
1592	if (vp->v_tag == VT_NFS)
1593		vnd->sc_maxactive = 2;
1594	else
1595		vnd->sc_maxactive = 8;
1596
1597	if (vnd->sc_maxactive < 1)
1598		vnd->sc_maxactive = 1;
1599}
1600
1601#if 0
1602static void
1603vndshutdown(void)
1604{
1605	struct vnd_softc *vnd;
1606
1607	for (vnd = &vnd_softc[0]; vnd < &vnd_softc[numvnd]; vnd++)
1608		if (vnd->sc_flags & VNF_INITED)
1609			vndclear(vnd);
1610}
1611#endif
1612
1613static void
1614vndclear(struct vnd_softc *vnd, int myminor)
1615{
1616	struct vnode *vp = vnd->sc_vp;
1617	int fflags = FREAD;
1618	int bmaj, cmaj, i, mn;
1619	int s;
1620
1621#ifdef DEBUG
1622	if (vnddebug & VDB_FOLLOW)
1623		printf("vndclear(%p): vp %p\n", vnd, vp);
1624#endif
1625	/* locate the major number */
1626	bmaj = bdevsw_lookup_major(&vnd_bdevsw);
1627	cmaj = cdevsw_lookup_major(&vnd_cdevsw);
1628
1629	/* Nuke the vnodes for any open instances */
1630	for (i = 0; i < MAXPARTITIONS; i++) {
1631		mn = DISKMINOR(device_unit(vnd->sc_dev), i);
1632		vdevgone(bmaj, mn, mn, VBLK);
1633		if (mn != myminor) /* XXX avoid to kill own vnode */
1634			vdevgone(cmaj, mn, mn, VCHR);
1635	}
1636
1637	if ((vnd->sc_flags & VNF_READONLY) == 0)
1638		fflags |= FWRITE;
1639
1640	s = splbio();
1641	bufq_drain(vnd->sc_tab);
1642	splx(s);
1643
1644	vnd->sc_flags |= VNF_VUNCONF;
1645	wakeup(&vnd->sc_tab);
1646	while (vnd->sc_flags & VNF_KTHREAD)
1647		tsleep(&vnd->sc_kthread, PRIBIO, "vnthr", 0);
1648
1649#ifdef VND_COMPRESSION
1650	/* free the compressed file buffers */
1651	if (vnd->sc_flags & VNF_COMP) {
1652		if (vnd->sc_comp_offsets) {
1653			free(vnd->sc_comp_offsets, M_DEVBUF);
1654			vnd->sc_comp_offsets = NULL;
1655		}
1656		if (vnd->sc_comp_buff) {
1657			free(vnd->sc_comp_buff, M_DEVBUF);
1658			vnd->sc_comp_buff = NULL;
1659		}
1660		if (vnd->sc_comp_decombuf) {
1661			free(vnd->sc_comp_decombuf, M_DEVBUF);
1662			vnd->sc_comp_decombuf = NULL;
1663		}
1664	}
1665#endif /* VND_COMPRESSION */
1666	vnd->sc_flags &=
1667	    ~(VNF_INITED | VNF_READONLY | VNF_VLABEL
1668	      | VNF_VUNCONF | VNF_COMP | VNF_CLEARING);
1669	if (vp == NULL)
1670		panic("vndclear: null vp");
1671	(void) vn_close(vp, fflags, vnd->sc_cred);
1672	kauth_cred_free(vnd->sc_cred);
1673	vnd->sc_vp = NULL;
1674	vnd->sc_cred = NULL;
1675	vnd->sc_size = 0;
1676}
1677
1678static int
1679vndsize(dev_t dev)
1680{
1681	struct vnd_softc *sc;
1682	struct disklabel *lp;
1683	int part, unit, omask;
1684	int size;
1685
1686	unit = vndunit(dev);
1687	sc = device_lookup_private(&vnd_cd, unit);
1688	if (sc == NULL)
1689		return -1;
1690
1691	if ((sc->sc_flags & VNF_INITED) == 0)
1692		return -1;
1693
1694	part = DISKPART(dev);
1695	omask = sc->sc_dkdev.dk_openmask & (1 << part);
1696	lp = sc->sc_dkdev.dk_label;
1697
1698	if (omask == 0 && vndopen(dev, 0, S_IFBLK, curlwp))	/* XXX */
1699		return -1;
1700
1701	if (lp->d_partitions[part].p_fstype != FS_SWAP)
1702		size = -1;
1703	else
1704		size = lp->d_partitions[part].p_size *
1705		    (lp->d_secsize / DEV_BSIZE);
1706
1707	if (omask == 0 && vndclose(dev, 0, S_IFBLK, curlwp))	/* XXX */
1708		return -1;
1709
1710	return size;
1711}
1712
1713static int
1714vnddump(dev_t dev, daddr_t blkno, void *va,
1715    size_t size)
1716{
1717
1718	/* Not implemented. */
1719	return ENXIO;
1720}
1721
1722static void
1723vndgetdefaultlabel(struct vnd_softc *sc, struct disklabel *lp)
1724{
1725	struct vndgeom *vng = &sc->sc_geom;
1726	struct partition *pp;
1727
1728	memset(lp, 0, sizeof(*lp));
1729
1730	lp->d_secperunit = sc->sc_size / (vng->vng_secsize / DEV_BSIZE);
1731	lp->d_secsize = vng->vng_secsize;
1732	lp->d_nsectors = vng->vng_nsectors;
1733	lp->d_ntracks = vng->vng_ntracks;
1734	lp->d_ncylinders = vng->vng_ncylinders;
1735	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
1736
1737	strncpy(lp->d_typename, "vnd", sizeof(lp->d_typename));
1738	lp->d_type = DTYPE_VND;
1739	strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
1740	lp->d_rpm = 3600;
1741	lp->d_interleave = 1;
1742	lp->d_flags = 0;
1743
1744	pp = &lp->d_partitions[RAW_PART];
1745	pp->p_offset = 0;
1746	pp->p_size = lp->d_secperunit;
1747	pp->p_fstype = FS_UNUSED;
1748	lp->d_npartitions = RAW_PART + 1;
1749
1750	lp->d_magic = DISKMAGIC;
1751	lp->d_magic2 = DISKMAGIC;
1752	lp->d_checksum = dkcksum(lp);
1753}
1754
1755/*
1756 * Read the disklabel from a vnd.  If one is not present, create a fake one.
1757 */
1758static void
1759vndgetdisklabel(dev_t dev, struct vnd_softc *sc)
1760{
1761	const char *errstring;
1762	struct disklabel *lp = sc->sc_dkdev.dk_label;
1763	struct cpu_disklabel *clp = sc->sc_dkdev.dk_cpulabel;
1764	int i;
1765
1766	memset(clp, 0, sizeof(*clp));
1767
1768	vndgetdefaultlabel(sc, lp);
1769
1770	/*
1771	 * Call the generic disklabel extraction routine.
1772	 */
1773	errstring = readdisklabel(VNDLABELDEV(dev), vndstrategy, lp, clp);
1774	if (errstring) {
1775		/*
1776		 * Lack of disklabel is common, but we print the warning
1777		 * anyway, since it might contain other useful information.
1778		 */
1779		aprint_normal_dev(sc->sc_dev, "%s\n", errstring);
1780
1781		/*
1782		 * For historical reasons, if there's no disklabel
1783		 * present, all partitions must be FS_BSDFFS and
1784		 * occupy the entire disk.
1785		 */
1786		for (i = 0; i < MAXPARTITIONS; i++) {
1787			/*
1788			 * Don't wipe out port specific hack (such as
1789			 * dos partition hack of i386 port).
1790			 */
1791			if (lp->d_partitions[i].p_size != 0)
1792				continue;
1793
1794			lp->d_partitions[i].p_size = lp->d_secperunit;
1795			lp->d_partitions[i].p_offset = 0;
1796			lp->d_partitions[i].p_fstype = FS_BSDFFS;
1797		}
1798
1799		strncpy(lp->d_packname, "default label",
1800		    sizeof(lp->d_packname));
1801
1802		lp->d_npartitions = MAXPARTITIONS;
1803		lp->d_checksum = dkcksum(lp);
1804	}
1805
1806	/* In-core label now valid. */
1807	sc->sc_flags |= VNF_VLABEL;
1808}
1809
1810/*
1811 * Wait interruptibly for an exclusive lock.
1812 *
1813 * XXX
1814 * Several drivers do this; it should be abstracted and made MP-safe.
1815 */
1816static int
1817vndlock(struct vnd_softc *sc)
1818{
1819	int error;
1820
1821	while ((sc->sc_flags & VNF_LOCKED) != 0) {
1822		sc->sc_flags |= VNF_WANTED;
1823		if ((error = tsleep(sc, PRIBIO | PCATCH, "vndlck", 0)) != 0)
1824			return error;
1825	}
1826	sc->sc_flags |= VNF_LOCKED;
1827	return 0;
1828}
1829
1830/*
1831 * Unlock and wake up any waiters.
1832 */
1833static void
1834vndunlock(struct vnd_softc *sc)
1835{
1836
1837	sc->sc_flags &= ~VNF_LOCKED;
1838	if ((sc->sc_flags & VNF_WANTED) != 0) {
1839		sc->sc_flags &= ~VNF_WANTED;
1840		wakeup(sc);
1841	}
1842}
1843
1844#ifdef VND_COMPRESSION
1845/* compressed file read */
1846static void
1847compstrategy(struct buf *bp, off_t bn)
1848{
1849	int error;
1850	int unit = vndunit(bp->b_dev);
1851	struct vnd_softc *vnd =
1852	    device_lookup_private(&vnd_cd, unit);
1853	u_int32_t comp_block;
1854	struct uio auio;
1855	char *addr;
1856	int s;
1857
1858	/* set up constants for data move */
1859	auio.uio_rw = UIO_READ;
1860	UIO_SETUP_SYSSPACE(&auio);
1861
1862	/* read, and transfer the data */
1863	addr = bp->b_data;
1864	bp->b_resid = bp->b_bcount;
1865	s = splbio();
1866	while (bp->b_resid > 0) {
1867		unsigned length;
1868		size_t length_in_buffer;
1869		u_int32_t offset_in_buffer;
1870		struct iovec aiov;
1871
1872		/* calculate the compressed block number */
1873		comp_block = bn / (off_t)vnd->sc_comp_blksz;
1874
1875		/* check for good block number */
1876		if (comp_block >= vnd->sc_comp_numoffs) {
1877			bp->b_error = EINVAL;
1878			splx(s);
1879			return;
1880		}
1881
1882		/* read in the compressed block, if not in buffer */
1883		if (comp_block != vnd->sc_comp_buffblk) {
1884			length = vnd->sc_comp_offsets[comp_block + 1] -
1885			    vnd->sc_comp_offsets[comp_block];
1886			vn_lock(vnd->sc_vp, LK_EXCLUSIVE | LK_RETRY);
1887			error = vn_rdwr(UIO_READ, vnd->sc_vp, vnd->sc_comp_buff,
1888			    length, vnd->sc_comp_offsets[comp_block],
1889			    UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, vnd->sc_cred,
1890			    NULL, NULL);
1891			if (error) {
1892				bp->b_error = error;
1893				VOP_UNLOCK(vnd->sc_vp);
1894				splx(s);
1895				return;
1896			}
1897			/* uncompress the buffer */
1898			vnd->sc_comp_stream.next_in = vnd->sc_comp_buff;
1899			vnd->sc_comp_stream.avail_in = length;
1900			vnd->sc_comp_stream.next_out = vnd->sc_comp_decombuf;
1901			vnd->sc_comp_stream.avail_out = vnd->sc_comp_blksz;
1902			inflateReset(&vnd->sc_comp_stream);
1903			error = inflate(&vnd->sc_comp_stream, Z_FINISH);
1904			if (error != Z_STREAM_END) {
1905				if (vnd->sc_comp_stream.msg)
1906					aprint_normal_dev(vnd->sc_dev,
1907					    "compressed file, %s\n",
1908					    vnd->sc_comp_stream.msg);
1909				bp->b_error = EBADMSG;
1910				VOP_UNLOCK(vnd->sc_vp);
1911				splx(s);
1912				return;
1913			}
1914			vnd->sc_comp_buffblk = comp_block;
1915			VOP_UNLOCK(vnd->sc_vp);
1916		}
1917
1918		/* transfer the usable uncompressed data */
1919		offset_in_buffer = bn % (off_t)vnd->sc_comp_blksz;
1920		length_in_buffer = vnd->sc_comp_blksz - offset_in_buffer;
1921		if (length_in_buffer > bp->b_resid)
1922			length_in_buffer = bp->b_resid;
1923		auio.uio_iov = &aiov;
1924		auio.uio_iovcnt = 1;
1925		aiov.iov_base = addr;
1926		aiov.iov_len = length_in_buffer;
1927		auio.uio_resid = aiov.iov_len;
1928		auio.uio_offset = 0;
1929		error = uiomove(vnd->sc_comp_decombuf + offset_in_buffer,
1930		    length_in_buffer, &auio);
1931		if (error) {
1932			bp->b_error = error;
1933			splx(s);
1934			return;
1935		}
1936
1937		bn += length_in_buffer;
1938		addr += length_in_buffer;
1939		bp->b_resid -= length_in_buffer;
1940	}
1941	splx(s);
1942}
1943
1944/* compression memory allocation routines */
1945static void *
1946vnd_alloc(void *aux, u_int items, u_int siz)
1947{
1948	return malloc(items * siz, M_TEMP, M_NOWAIT);
1949}
1950
1951static void
1952vnd_free(void *aux, void *ptr)
1953{
1954	free(ptr, M_TEMP);
1955}
1956#endif /* VND_COMPRESSION */
1957
1958static void
1959vnd_set_properties(struct vnd_softc *vnd)
1960{
1961	prop_dictionary_t disk_info, odisk_info, geom;
1962
1963	disk_info = prop_dictionary_create();
1964
1965	geom = prop_dictionary_create();
1966
1967	prop_dictionary_set_uint64(geom, "sectors-per-unit",
1968	    vnd->sc_geom.vng_nsectors * vnd->sc_geom.vng_ntracks *
1969	    vnd->sc_geom.vng_ncylinders);
1970
1971	prop_dictionary_set_uint32(geom, "sector-size",
1972	    vnd->sc_geom.vng_secsize);
1973
1974	prop_dictionary_set_uint16(geom, "sectors-per-track",
1975	    vnd->sc_geom.vng_nsectors);
1976
1977	prop_dictionary_set_uint16(geom, "tracks-per-cylinder",
1978	    vnd->sc_geom.vng_ntracks);
1979
1980	prop_dictionary_set_uint64(geom, "cylinders-per-unit",
1981	    vnd->sc_geom.vng_ncylinders);
1982
1983	prop_dictionary_set(disk_info, "geometry", geom);
1984	prop_object_release(geom);
1985
1986	prop_dictionary_set(device_properties(vnd->sc_dev),
1987	    "disk-info", disk_info);
1988
1989	/*
1990	 * Don't release disk_info here; we keep a reference to it.
1991	 * disk_detach() will release it when we go away.
1992	 */
1993
1994	odisk_info = vnd->sc_dkdev.dk_info;
1995	vnd->sc_dkdev.dk_info = disk_info;
1996	if (odisk_info)
1997		prop_object_release(odisk_info);
1998}
1999
2000#ifdef _MODULE
2001
2002#include <sys/module.h>
2003
2004MODULE(MODULE_CLASS_DRIVER, vnd, NULL);
2005CFDRIVER_DECL(vnd, DV_DISK, NULL);
2006
2007static int
2008vnd_modcmd(modcmd_t cmd, void *arg)
2009{
2010	int bmajor = -1, cmajor = -1,  error = 0;
2011
2012	switch (cmd) {
2013	case MODULE_CMD_INIT:
2014		error = config_cfdriver_attach(&vnd_cd);
2015		if (error)
2016			break;
2017
2018		error = config_cfattach_attach(vnd_cd.cd_name, &vnd_ca);
2019	        if (error) {
2020			config_cfdriver_detach(&vnd_cd);
2021			aprint_error("%s: unable to register cfattach\n",
2022			    vnd_cd.cd_name);
2023			break;
2024		}
2025
2026		error = devsw_attach("vnd", &vnd_bdevsw, &bmajor,
2027		    &vnd_cdevsw, &cmajor);
2028		if (error) {
2029			config_cfattach_detach(vnd_cd.cd_name, &vnd_ca);
2030			config_cfdriver_detach(&vnd_cd);
2031			break;
2032		}
2033
2034		break;
2035
2036	case MODULE_CMD_FINI:
2037		error = config_cfattach_detach(vnd_cd.cd_name, &vnd_ca);
2038		if (error)
2039			break;
2040		config_cfdriver_detach(&vnd_cd);
2041		devsw_detach(&vnd_bdevsw, &vnd_cdevsw);
2042		break;
2043
2044	case MODULE_CMD_STAT:
2045		return ENOTTY;
2046
2047	default:
2048		return ENOTTY;
2049	}
2050
2051	return error;
2052}
2053
2054#endif
2055