xref: /minix3/minix/drivers/storage/vnd/vnd.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /* VNode Disk driver, by D.C. van Moolenbroek <david@minix3.org> */
2*433d6423SLionel Sambuc 
3*433d6423SLionel Sambuc #include <minix/drivers.h>
4*433d6423SLionel Sambuc #include <minix/blockdriver.h>
5*433d6423SLionel Sambuc #include <minix/drvlib.h>
6*433d6423SLionel Sambuc #include <sys/ioctl.h>
7*433d6423SLionel Sambuc #include <sys/mman.h>
8*433d6423SLionel Sambuc #include <sys/stat.h>
9*433d6423SLionel Sambuc #include <fcntl.h>
10*433d6423SLionel Sambuc #include <assert.h>
11*433d6423SLionel Sambuc 
12*433d6423SLionel Sambuc #define VND_BUF_SIZE	65536
13*433d6423SLionel Sambuc 
14*433d6423SLionel Sambuc static struct {
15*433d6423SLionel Sambuc 	int fd;			/* file descriptor for the underlying file */
16*433d6423SLionel Sambuc 	int openct;		/* number of times the device is open */
17*433d6423SLionel Sambuc 	int exiting;		/* exit after the last close? */
18*433d6423SLionel Sambuc 	int rdonly;		/* is the device set up read-only? */
19*433d6423SLionel Sambuc 	dev_t dev;		/* device on which the file resides */
20*433d6423SLionel Sambuc 	ino_t ino;		/* inode number of the file */
21*433d6423SLionel Sambuc 	struct device part[DEV_PER_DRIVE];	/* partition bases and sizes */
22*433d6423SLionel Sambuc 	struct device subpart[SUB_PER_DRIVE];	/* same for subpartitions */
23*433d6423SLionel Sambuc 	struct part_geom geom;	/* geometry information */
24*433d6423SLionel Sambuc 	char *buf;		/* intermediate I/O transfer buffer */
25*433d6423SLionel Sambuc } state;
26*433d6423SLionel Sambuc 
27*433d6423SLionel Sambuc static unsigned int instance;
28*433d6423SLionel Sambuc 
29*433d6423SLionel Sambuc static int vnd_open(devminor_t, int);
30*433d6423SLionel Sambuc static int vnd_close(devminor_t);
31*433d6423SLionel Sambuc static int vnd_transfer(devminor_t, int, u64_t, endpoint_t, iovec_t *,
32*433d6423SLionel Sambuc 	unsigned int, int);
33*433d6423SLionel Sambuc static int vnd_ioctl(devminor_t, unsigned long, endpoint_t, cp_grant_id_t,
34*433d6423SLionel Sambuc 	endpoint_t);
35*433d6423SLionel Sambuc static struct device *vnd_part(devminor_t);
36*433d6423SLionel Sambuc static void vnd_geometry(devminor_t, struct part_geom *);
37*433d6423SLionel Sambuc 
38*433d6423SLionel Sambuc static struct blockdriver vnd_dtab = {
39*433d6423SLionel Sambuc 	.bdr_type	= BLOCKDRIVER_TYPE_DISK,
40*433d6423SLionel Sambuc 	.bdr_open	= vnd_open,
41*433d6423SLionel Sambuc 	.bdr_close	= vnd_close,
42*433d6423SLionel Sambuc 	.bdr_transfer	= vnd_transfer,
43*433d6423SLionel Sambuc 	.bdr_ioctl	= vnd_ioctl,
44*433d6423SLionel Sambuc 	.bdr_part	= vnd_part,
45*433d6423SLionel Sambuc 	.bdr_geometry	= vnd_geometry
46*433d6423SLionel Sambuc };
47*433d6423SLionel Sambuc 
48*433d6423SLionel Sambuc /*
49*433d6423SLionel Sambuc  * Parse partition tables.
50*433d6423SLionel Sambuc  */
51*433d6423SLionel Sambuc static void
vnd_partition(void)52*433d6423SLionel Sambuc vnd_partition(void)
53*433d6423SLionel Sambuc {
54*433d6423SLionel Sambuc 	memset(state.part, 0, sizeof(state.part));
55*433d6423SLionel Sambuc 	memset(state.subpart, 0, sizeof(state.subpart));
56*433d6423SLionel Sambuc 
57*433d6423SLionel Sambuc 	state.part[0].dv_size = state.geom.size;
58*433d6423SLionel Sambuc 
59*433d6423SLionel Sambuc 	partition(&vnd_dtab, 0, P_PRIMARY, FALSE /*atapi*/);
60*433d6423SLionel Sambuc }
61*433d6423SLionel Sambuc 
62*433d6423SLionel Sambuc /*
63*433d6423SLionel Sambuc  * Open a device.
64*433d6423SLionel Sambuc  */
65*433d6423SLionel Sambuc static int
vnd_open(devminor_t minor,int access)66*433d6423SLionel Sambuc vnd_open(devminor_t minor, int access)
67*433d6423SLionel Sambuc {
68*433d6423SLionel Sambuc 	/* No sub/partition devices are available before initialization. */
69*433d6423SLionel Sambuc 	if (state.fd == -1 && minor != 0)
70*433d6423SLionel Sambuc 		return ENXIO;
71*433d6423SLionel Sambuc 	else if (state.fd != -1 && vnd_part(minor) == NULL)
72*433d6423SLionel Sambuc 		return ENXIO;
73*433d6423SLionel Sambuc 
74*433d6423SLionel Sambuc 	/*
75*433d6423SLionel Sambuc 	 * If the device either is not configured or configured as read-only,
76*433d6423SLionel Sambuc 	 * block open calls that request write permission.  This is what user-
77*433d6423SLionel Sambuc 	 * land expects, although it does mean that vnconfig(8) has to open the
78*433d6423SLionel Sambuc 	 * device as read-only in order to (un)configure it.
79*433d6423SLionel Sambuc 	 */
80*433d6423SLionel Sambuc 	if (access & BDEV_W_BIT) {
81*433d6423SLionel Sambuc 		if (state.fd == -1)
82*433d6423SLionel Sambuc 			return ENXIO;
83*433d6423SLionel Sambuc 		if (state.rdonly)
84*433d6423SLionel Sambuc 			return EACCES;
85*433d6423SLionel Sambuc 	}
86*433d6423SLionel Sambuc 
87*433d6423SLionel Sambuc 	/*
88*433d6423SLionel Sambuc 	 * Userland expects that if the device is opened after having been
89*433d6423SLionel Sambuc 	 * fully closed, partition tables are (re)parsed.  Since we already
90*433d6423SLionel Sambuc 	 * parse partition tables upon initialization, we could skip this for
91*433d6423SLionel Sambuc 	 * the first open, but that would introduce more state.
92*433d6423SLionel Sambuc 	 */
93*433d6423SLionel Sambuc 	if (state.fd != -1 && state.openct == 0) {
94*433d6423SLionel Sambuc 		vnd_partition();
95*433d6423SLionel Sambuc 
96*433d6423SLionel Sambuc 		/* Make sure our target device didn't just disappear. */
97*433d6423SLionel Sambuc 		if (vnd_part(minor) == NULL)
98*433d6423SLionel Sambuc 			return ENXIO;
99*433d6423SLionel Sambuc 	}
100*433d6423SLionel Sambuc 
101*433d6423SLionel Sambuc 	state.openct++;
102*433d6423SLionel Sambuc 
103*433d6423SLionel Sambuc 	return OK;
104*433d6423SLionel Sambuc }
105*433d6423SLionel Sambuc 
106*433d6423SLionel Sambuc /*
107*433d6423SLionel Sambuc  * Close a device.
108*433d6423SLionel Sambuc  */
109*433d6423SLionel Sambuc static int
vnd_close(devminor_t UNUSED (minor))110*433d6423SLionel Sambuc vnd_close(devminor_t UNUSED(minor))
111*433d6423SLionel Sambuc {
112*433d6423SLionel Sambuc 	if (state.openct == 0) {
113*433d6423SLionel Sambuc 		printf("VND%u: closing already-closed device\n", instance);
114*433d6423SLionel Sambuc 		return EINVAL;
115*433d6423SLionel Sambuc 	}
116*433d6423SLionel Sambuc 
117*433d6423SLionel Sambuc 	state.openct--;
118*433d6423SLionel Sambuc 
119*433d6423SLionel Sambuc 	if (state.exiting)
120*433d6423SLionel Sambuc 		blockdriver_terminate();
121*433d6423SLionel Sambuc 
122*433d6423SLionel Sambuc 	return OK;
123*433d6423SLionel Sambuc }
124*433d6423SLionel Sambuc 
125*433d6423SLionel Sambuc /*
126*433d6423SLionel Sambuc  * Copy a number of bytes from or to the caller, to or from the intermediate
127*433d6423SLionel Sambuc  * buffer.  If the given endpoint is SELF, a local memory copy must be made.
128*433d6423SLionel Sambuc  */
129*433d6423SLionel Sambuc static int
vnd_copy(iovec_s_t * iov,size_t iov_off,size_t bytes,endpoint_t endpt,int do_write)130*433d6423SLionel Sambuc vnd_copy(iovec_s_t *iov, size_t iov_off, size_t bytes, endpoint_t endpt,
131*433d6423SLionel Sambuc 	int do_write)
132*433d6423SLionel Sambuc {
133*433d6423SLionel Sambuc 	struct vscp_vec vvec[SCPVEC_NR], *vvp;
134*433d6423SLionel Sambuc 	size_t off, chunk;
135*433d6423SLionel Sambuc 	int count;
136*433d6423SLionel Sambuc 	char *ptr;
137*433d6423SLionel Sambuc 
138*433d6423SLionel Sambuc 	assert(bytes > 0 && bytes <= VND_BUF_SIZE);
139*433d6423SLionel Sambuc 
140*433d6423SLionel Sambuc 	vvp = vvec;
141*433d6423SLionel Sambuc 	count = 0;
142*433d6423SLionel Sambuc 
143*433d6423SLionel Sambuc 	for (off = 0; off < bytes; off += chunk) {
144*433d6423SLionel Sambuc 		chunk = MIN(bytes - off, iov->iov_size - iov_off);
145*433d6423SLionel Sambuc 
146*433d6423SLionel Sambuc 		if (endpt == SELF) {
147*433d6423SLionel Sambuc 			ptr = (char *) iov->iov_grant + iov_off;
148*433d6423SLionel Sambuc 
149*433d6423SLionel Sambuc 			if (do_write)
150*433d6423SLionel Sambuc 				memcpy(&state.buf[off], ptr, chunk);
151*433d6423SLionel Sambuc 			else
152*433d6423SLionel Sambuc 				memcpy(ptr, &state.buf[off], chunk);
153*433d6423SLionel Sambuc 		} else {
154*433d6423SLionel Sambuc 			assert(count < SCPVEC_NR); /* SCPVEC_NR >= NR_IOREQS */
155*433d6423SLionel Sambuc 
156*433d6423SLionel Sambuc 			vvp->v_from = do_write ? endpt : SELF;
157*433d6423SLionel Sambuc 			vvp->v_to = do_write ? SELF : endpt;
158*433d6423SLionel Sambuc 			vvp->v_bytes = chunk;
159*433d6423SLionel Sambuc 			vvp->v_gid = iov->iov_grant;
160*433d6423SLionel Sambuc 			vvp->v_offset = iov_off;
161*433d6423SLionel Sambuc 			vvp->v_addr = (vir_bytes) &state.buf[off];
162*433d6423SLionel Sambuc 
163*433d6423SLionel Sambuc 			vvp++;
164*433d6423SLionel Sambuc 			count++;
165*433d6423SLionel Sambuc 		}
166*433d6423SLionel Sambuc 
167*433d6423SLionel Sambuc 		iov_off += chunk;
168*433d6423SLionel Sambuc 		if (iov_off == iov->iov_size) {
169*433d6423SLionel Sambuc 			iov++;
170*433d6423SLionel Sambuc 			iov_off = 0;
171*433d6423SLionel Sambuc 		}
172*433d6423SLionel Sambuc 	}
173*433d6423SLionel Sambuc 
174*433d6423SLionel Sambuc 	if (endpt != SELF)
175*433d6423SLionel Sambuc 		return sys_vsafecopy(vvec, count);
176*433d6423SLionel Sambuc 	else
177*433d6423SLionel Sambuc 		return OK;
178*433d6423SLionel Sambuc }
179*433d6423SLionel Sambuc 
180*433d6423SLionel Sambuc /*
181*433d6423SLionel Sambuc  * Advance the given I/O vector, and the offset into its first element, by the
182*433d6423SLionel Sambuc  * given number of bytes.
183*433d6423SLionel Sambuc  */
184*433d6423SLionel Sambuc static iovec_s_t *
vnd_advance(iovec_s_t * iov,size_t * iov_offp,size_t bytes)185*433d6423SLionel Sambuc vnd_advance(iovec_s_t *iov, size_t *iov_offp, size_t bytes)
186*433d6423SLionel Sambuc {
187*433d6423SLionel Sambuc 	size_t iov_off;
188*433d6423SLionel Sambuc 
189*433d6423SLionel Sambuc 	assert(bytes > 0 && bytes <= VND_BUF_SIZE);
190*433d6423SLionel Sambuc 
191*433d6423SLionel Sambuc 	iov_off = *iov_offp;
192*433d6423SLionel Sambuc 
193*433d6423SLionel Sambuc 	while (bytes > 0) {
194*433d6423SLionel Sambuc 		if (bytes >= iov->iov_size - iov_off) {
195*433d6423SLionel Sambuc 			bytes -= iov->iov_size - iov_off;
196*433d6423SLionel Sambuc 			iov++;
197*433d6423SLionel Sambuc 			iov_off = 0;
198*433d6423SLionel Sambuc 		} else {
199*433d6423SLionel Sambuc 			iov_off += bytes;
200*433d6423SLionel Sambuc 			bytes = 0;
201*433d6423SLionel Sambuc 		}
202*433d6423SLionel Sambuc 	}
203*433d6423SLionel Sambuc 
204*433d6423SLionel Sambuc 	*iov_offp = iov_off;
205*433d6423SLionel Sambuc 	return iov;
206*433d6423SLionel Sambuc }
207*433d6423SLionel Sambuc 
208*433d6423SLionel Sambuc /*
209*433d6423SLionel Sambuc  * Perform data transfer on the selected device.
210*433d6423SLionel Sambuc  */
211*433d6423SLionel Sambuc static int
vnd_transfer(devminor_t minor,int do_write,u64_t position,endpoint_t endpt,iovec_t * iovt,unsigned int nr_req,int flags)212*433d6423SLionel Sambuc vnd_transfer(devminor_t minor, int do_write, u64_t position,
213*433d6423SLionel Sambuc 	endpoint_t endpt, iovec_t *iovt, unsigned int nr_req, int flags)
214*433d6423SLionel Sambuc {
215*433d6423SLionel Sambuc 	struct device *dv;
216*433d6423SLionel Sambuc 	iovec_s_t *iov;
217*433d6423SLionel Sambuc 	size_t off, chunk, bytes, iov_off;
218*433d6423SLionel Sambuc 	ssize_t r;
219*433d6423SLionel Sambuc 	unsigned int i;
220*433d6423SLionel Sambuc 
221*433d6423SLionel Sambuc 	iov = (iovec_s_t *) iovt;
222*433d6423SLionel Sambuc 
223*433d6423SLionel Sambuc 	if (state.fd == -1 || (dv = vnd_part(minor)) == NULL)
224*433d6423SLionel Sambuc 		return ENXIO;
225*433d6423SLionel Sambuc 
226*433d6423SLionel Sambuc 	/* Prevent write operations on devices opened as write-only. */
227*433d6423SLionel Sambuc 	if (do_write && state.rdonly)
228*433d6423SLionel Sambuc 		return EACCES;
229*433d6423SLionel Sambuc 
230*433d6423SLionel Sambuc 	/* Determine the total number of bytes to transfer. */
231*433d6423SLionel Sambuc 	if (position >= dv->dv_size)
232*433d6423SLionel Sambuc 		return 0;
233*433d6423SLionel Sambuc 
234*433d6423SLionel Sambuc 	bytes = 0;
235*433d6423SLionel Sambuc 
236*433d6423SLionel Sambuc 	for (i = 0; i < nr_req; i++) {
237*433d6423SLionel Sambuc 		if (iov[i].iov_size == 0 || iov[i].iov_size > LONG_MAX)
238*433d6423SLionel Sambuc 			return EINVAL;
239*433d6423SLionel Sambuc 		bytes += iov[i].iov_size;
240*433d6423SLionel Sambuc 		if (bytes > LONG_MAX)
241*433d6423SLionel Sambuc 			return EINVAL;
242*433d6423SLionel Sambuc 	}
243*433d6423SLionel Sambuc 
244*433d6423SLionel Sambuc 	if (bytes > dv->dv_size - position)
245*433d6423SLionel Sambuc 		bytes = dv->dv_size - position;
246*433d6423SLionel Sambuc 
247*433d6423SLionel Sambuc 	position += dv->dv_base;
248*433d6423SLionel Sambuc 
249*433d6423SLionel Sambuc 	/* Perform the actual transfer, in chunks if necessary. */
250*433d6423SLionel Sambuc 	iov_off = 0;
251*433d6423SLionel Sambuc 
252*433d6423SLionel Sambuc 	for (off = 0; off < bytes; off += chunk) {
253*433d6423SLionel Sambuc 		chunk = MIN(bytes - off, VND_BUF_SIZE);
254*433d6423SLionel Sambuc 
255*433d6423SLionel Sambuc 		assert((unsigned int) (iov - (iovec_s_t *) iovt) < nr_req);
256*433d6423SLionel Sambuc 
257*433d6423SLionel Sambuc 		/* For reads, read in the data for the chunk; possibly less. */
258*433d6423SLionel Sambuc 		if (!do_write) {
259*433d6423SLionel Sambuc 			chunk = r = pread(state.fd, state.buf, chunk,
260*433d6423SLionel Sambuc 			    position);
261*433d6423SLionel Sambuc 
262*433d6423SLionel Sambuc 			if (r < 0) {
263*433d6423SLionel Sambuc 				printf("VND%u: pread failed (%d)\n", instance,
264*433d6423SLionel Sambuc 				    -errno);
265*433d6423SLionel Sambuc 				return -errno;
266*433d6423SLionel Sambuc 			}
267*433d6423SLionel Sambuc 			if (r == 0)
268*433d6423SLionel Sambuc 				break;
269*433d6423SLionel Sambuc 		}
270*433d6423SLionel Sambuc 
271*433d6423SLionel Sambuc 		/* Copy the data for this chunk from or to the caller. */
272*433d6423SLionel Sambuc 		if ((r = vnd_copy(iov, iov_off, chunk, endpt, do_write)) < 0) {
273*433d6423SLionel Sambuc 			printf("VND%u: data copy failed (%d)\n", instance, r);
274*433d6423SLionel Sambuc 			return r;
275*433d6423SLionel Sambuc 		}
276*433d6423SLionel Sambuc 
277*433d6423SLionel Sambuc 		/* For writes, write the data to the file; possibly less. */
278*433d6423SLionel Sambuc 		if (do_write) {
279*433d6423SLionel Sambuc 			chunk = r = pwrite(state.fd, state.buf, chunk,
280*433d6423SLionel Sambuc 			    position);
281*433d6423SLionel Sambuc 
282*433d6423SLionel Sambuc 			if (r <= 0) {
283*433d6423SLionel Sambuc 				if (r < 0)
284*433d6423SLionel Sambuc 					r = -errno;
285*433d6423SLionel Sambuc 				printf("VND%u: pwrite failed (%d)\n", instance,
286*433d6423SLionel Sambuc 				    r);
287*433d6423SLionel Sambuc 				return (r < 0) ? r : EIO;
288*433d6423SLionel Sambuc 			}
289*433d6423SLionel Sambuc 		}
290*433d6423SLionel Sambuc 
291*433d6423SLionel Sambuc 		/* Move ahead on the I/O vector and the file position. */
292*433d6423SLionel Sambuc 		iov = vnd_advance(iov, &iov_off, chunk);
293*433d6423SLionel Sambuc 
294*433d6423SLionel Sambuc 		position += chunk;
295*433d6423SLionel Sambuc 	}
296*433d6423SLionel Sambuc 
297*433d6423SLionel Sambuc 	/* If force-write is requested, flush the underlying file to disk. */
298*433d6423SLionel Sambuc 	if (do_write && (flags & BDEV_FORCEWRITE))
299*433d6423SLionel Sambuc 		fsync(state.fd);
300*433d6423SLionel Sambuc 
301*433d6423SLionel Sambuc 	/* Return the number of bytes transferred. */
302*433d6423SLionel Sambuc 	return off;
303*433d6423SLionel Sambuc }
304*433d6423SLionel Sambuc 
305*433d6423SLionel Sambuc /*
306*433d6423SLionel Sambuc  * Initialize the size and geometry for the device and any partitions.  If the
307*433d6423SLionel Sambuc  * user provided a geometry, this will be used; otherwise, a geometry will be
308*433d6423SLionel Sambuc  * computed.
309*433d6423SLionel Sambuc  */
310*433d6423SLionel Sambuc static int
vnd_layout(u64_t size,struct vnd_ioctl * vnd)311*433d6423SLionel Sambuc vnd_layout(u64_t size, struct vnd_ioctl *vnd)
312*433d6423SLionel Sambuc {
313*433d6423SLionel Sambuc 	u64_t sectors;
314*433d6423SLionel Sambuc 
315*433d6423SLionel Sambuc 	state.geom.base = 0ULL;
316*433d6423SLionel Sambuc 
317*433d6423SLionel Sambuc 	if (vnd->vnd_flags & VNDIOF_HASGEOM) {
318*433d6423SLionel Sambuc 		/*
319*433d6423SLionel Sambuc 		 * The geometry determines the accessible part of the file.
320*433d6423SLionel Sambuc 		 * The resulting size must not exceed the file size.
321*433d6423SLionel Sambuc 		 */
322*433d6423SLionel Sambuc 		state.geom.cylinders = vnd->vnd_geom.vng_ncylinders;
323*433d6423SLionel Sambuc 		state.geom.heads = vnd->vnd_geom.vng_ntracks;
324*433d6423SLionel Sambuc 		state.geom.sectors = vnd->vnd_geom.vng_nsectors;
325*433d6423SLionel Sambuc 
326*433d6423SLionel Sambuc 		state.geom.size = (u64_t) state.geom.cylinders *
327*433d6423SLionel Sambuc 		    state.geom.heads * state.geom.sectors *
328*433d6423SLionel Sambuc 		    vnd->vnd_geom.vng_secsize;
329*433d6423SLionel Sambuc 		if (state.geom.size == 0 || state.geom.size > size)
330*433d6423SLionel Sambuc 			return EINVAL;
331*433d6423SLionel Sambuc 	} else {
332*433d6423SLionel Sambuc 		sectors = size / SECTOR_SIZE;
333*433d6423SLionel Sambuc 		state.geom.size = sectors * SECTOR_SIZE;
334*433d6423SLionel Sambuc 
335*433d6423SLionel Sambuc 		if (sectors >= 32 * 64) {
336*433d6423SLionel Sambuc 			state.geom.cylinders = sectors / (32 * 64);
337*433d6423SLionel Sambuc 			state.geom.heads = 64;
338*433d6423SLionel Sambuc 			state.geom.sectors = 32;
339*433d6423SLionel Sambuc 		} else {
340*433d6423SLionel Sambuc 			state.geom.cylinders = sectors;
341*433d6423SLionel Sambuc 			state.geom.heads = 1;
342*433d6423SLionel Sambuc 			state.geom.sectors = 1;
343*433d6423SLionel Sambuc 		}
344*433d6423SLionel Sambuc 	}
345*433d6423SLionel Sambuc 
346*433d6423SLionel Sambuc 	/*
347*433d6423SLionel Sambuc 	 * Parse partition tables immediately, so that (sub)partitions can be
348*433d6423SLionel Sambuc 	 * opened right away.  The first open will perform the same procedure,
349*433d6423SLionel Sambuc 	 * but that is only necessary to match userland expectations.
350*433d6423SLionel Sambuc 	 */
351*433d6423SLionel Sambuc 	vnd_partition();
352*433d6423SLionel Sambuc 
353*433d6423SLionel Sambuc 	return OK;
354*433d6423SLionel Sambuc }
355*433d6423SLionel Sambuc 
356*433d6423SLionel Sambuc /*
357*433d6423SLionel Sambuc  * Process I/O control requests.
358*433d6423SLionel Sambuc  */
359*433d6423SLionel Sambuc static int
vnd_ioctl(devminor_t UNUSED (minor),unsigned long request,endpoint_t endpt,cp_grant_id_t grant,endpoint_t user_endpt)360*433d6423SLionel Sambuc vnd_ioctl(devminor_t UNUSED(minor), unsigned long request, endpoint_t endpt,
361*433d6423SLionel Sambuc 	cp_grant_id_t grant, endpoint_t user_endpt)
362*433d6423SLionel Sambuc {
363*433d6423SLionel Sambuc 	struct vnd_ioctl vnd;
364*433d6423SLionel Sambuc 	struct vnd_user vnu;
365*433d6423SLionel Sambuc 	struct stat st;
366*433d6423SLionel Sambuc 	int r;
367*433d6423SLionel Sambuc 
368*433d6423SLionel Sambuc 	switch (request) {
369*433d6423SLionel Sambuc 	case VNDIOCSET:
370*433d6423SLionel Sambuc 		/*
371*433d6423SLionel Sambuc 		 * The VND must not be busy.  Note that the caller has the
372*433d6423SLionel Sambuc 		 * device open to perform the IOCTL request.
373*433d6423SLionel Sambuc 		 */
374*433d6423SLionel Sambuc 		if (state.fd != -1 || state.openct != 1)
375*433d6423SLionel Sambuc 			return EBUSY;
376*433d6423SLionel Sambuc 
377*433d6423SLionel Sambuc 		if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &vnd,
378*433d6423SLionel Sambuc 		    sizeof(vnd))) != OK)
379*433d6423SLionel Sambuc 			return r;
380*433d6423SLionel Sambuc 
381*433d6423SLionel Sambuc 		/*
382*433d6423SLionel Sambuc 		 * Issue a special VFS backcall that copies a file descriptor
383*433d6423SLionel Sambuc 		 * to the current process, from the user process ultimately
384*433d6423SLionel Sambuc 		 * making the IOCTL call.  The result is either a newly
385*433d6423SLionel Sambuc 		 * allocated file descriptor or an error.
386*433d6423SLionel Sambuc 		 */
387*433d6423SLionel Sambuc 		if ((r = copyfd(user_endpt, vnd.vnd_fildes, COPYFD_FROM)) < 0)
388*433d6423SLionel Sambuc 			return r;
389*433d6423SLionel Sambuc 
390*433d6423SLionel Sambuc 		state.fd = r;
391*433d6423SLionel Sambuc 
392*433d6423SLionel Sambuc 		/* The target file must be regular. */
393*433d6423SLionel Sambuc 		if (fstat(state.fd, &st) == -1) {
394*433d6423SLionel Sambuc 			printf("VND%u: fstat failed (%d)\n", instance, -errno);
395*433d6423SLionel Sambuc 			r = -errno;
396*433d6423SLionel Sambuc 		}
397*433d6423SLionel Sambuc 		if (r == OK && !S_ISREG(st.st_mode))
398*433d6423SLionel Sambuc 			r = EINVAL;
399*433d6423SLionel Sambuc 
400*433d6423SLionel Sambuc 		/*
401*433d6423SLionel Sambuc 		 * Allocate memory for an intermediate I/O transfer buffer. In
402*433d6423SLionel Sambuc 		 * order to save on memory in the common case, the buffer is
403*433d6423SLionel Sambuc 		 * only allocated when the vnd is in use.  We use mmap instead
404*433d6423SLionel Sambuc 		 * of malloc to allow the memory to be actually freed later.
405*433d6423SLionel Sambuc 		 */
406*433d6423SLionel Sambuc 		if (r == OK) {
407*433d6423SLionel Sambuc 			state.buf = mmap(NULL, VND_BUF_SIZE, PROT_READ |
408*433d6423SLionel Sambuc 			    PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
409*433d6423SLionel Sambuc 			if (state.buf == MAP_FAILED)
410*433d6423SLionel Sambuc 				r = ENOMEM;
411*433d6423SLionel Sambuc 		}
412*433d6423SLionel Sambuc 
413*433d6423SLionel Sambuc 		if (r != OK) {
414*433d6423SLionel Sambuc 			close(state.fd);
415*433d6423SLionel Sambuc 			state.fd = -1;
416*433d6423SLionel Sambuc 			return r;
417*433d6423SLionel Sambuc 		}
418*433d6423SLionel Sambuc 
419*433d6423SLionel Sambuc 		/* Set various device state fields. */
420*433d6423SLionel Sambuc 		state.dev = st.st_dev;
421*433d6423SLionel Sambuc 		state.ino = st.st_ino;
422*433d6423SLionel Sambuc 		state.rdonly = !!(vnd.vnd_flags & VNDIOF_READONLY);
423*433d6423SLionel Sambuc 
424*433d6423SLionel Sambuc 		r = vnd_layout(st.st_size, &vnd);
425*433d6423SLionel Sambuc 
426*433d6423SLionel Sambuc 		/* Upon success, return the device size to userland. */
427*433d6423SLionel Sambuc 		if (r == OK) {
428*433d6423SLionel Sambuc 			vnd.vnd_size = state.geom.size;
429*433d6423SLionel Sambuc 
430*433d6423SLionel Sambuc 			r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &vnd,
431*433d6423SLionel Sambuc 			    sizeof(vnd));
432*433d6423SLionel Sambuc 		}
433*433d6423SLionel Sambuc 
434*433d6423SLionel Sambuc 		if (r != OK) {
435*433d6423SLionel Sambuc 			munmap(state.buf, VND_BUF_SIZE);
436*433d6423SLionel Sambuc 			close(state.fd);
437*433d6423SLionel Sambuc 			state.fd = -1;
438*433d6423SLionel Sambuc 		}
439*433d6423SLionel Sambuc 
440*433d6423SLionel Sambuc 		return r;
441*433d6423SLionel Sambuc 
442*433d6423SLionel Sambuc 	case VNDIOCCLR:
443*433d6423SLionel Sambuc 		/* The VND can only be cleared if it has been configured. */
444*433d6423SLionel Sambuc 		if (state.fd == -1)
445*433d6423SLionel Sambuc 			return ENXIO;
446*433d6423SLionel Sambuc 
447*433d6423SLionel Sambuc 		if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &vnd,
448*433d6423SLionel Sambuc 		    sizeof(vnd))) != OK)
449*433d6423SLionel Sambuc 			return r;
450*433d6423SLionel Sambuc 
451*433d6423SLionel Sambuc 		/* The caller has the device open to do the IOCTL request. */
452*433d6423SLionel Sambuc 		if (!(vnd.vnd_flags & VNDIOF_FORCE) && state.openct != 1)
453*433d6423SLionel Sambuc 			return EBUSY;
454*433d6423SLionel Sambuc 
455*433d6423SLionel Sambuc 		/*
456*433d6423SLionel Sambuc 		 * Close the associated file descriptor immediately, but do not
457*433d6423SLionel Sambuc 		 * allow reuse until the device has been closed by the other
458*433d6423SLionel Sambuc 		 * users.
459*433d6423SLionel Sambuc 		 */
460*433d6423SLionel Sambuc 		munmap(state.buf, VND_BUF_SIZE);
461*433d6423SLionel Sambuc 		close(state.fd);
462*433d6423SLionel Sambuc 		state.fd = -1;
463*433d6423SLionel Sambuc 
464*433d6423SLionel Sambuc 		return OK;
465*433d6423SLionel Sambuc 
466*433d6423SLionel Sambuc 	case VNDIOCGET:
467*433d6423SLionel Sambuc 		/*
468*433d6423SLionel Sambuc 		 * We need not copy in the given structure.  It would contain
469*433d6423SLionel Sambuc 		 * the requested unit number, but each driver instance provides
470*433d6423SLionel Sambuc 		 * only one unit anyway.
471*433d6423SLionel Sambuc 		 */
472*433d6423SLionel Sambuc 
473*433d6423SLionel Sambuc 		memset(&vnu, 0, sizeof(vnu));
474*433d6423SLionel Sambuc 
475*433d6423SLionel Sambuc 		vnu.vnu_unit = instance;
476*433d6423SLionel Sambuc 
477*433d6423SLionel Sambuc 		/* Leave these fields zeroed if the device is not in use. */
478*433d6423SLionel Sambuc 		if (state.fd != -1) {
479*433d6423SLionel Sambuc 			vnu.vnu_dev = state.dev;
480*433d6423SLionel Sambuc 			vnu.vnu_ino = state.ino;
481*433d6423SLionel Sambuc 		}
482*433d6423SLionel Sambuc 
483*433d6423SLionel Sambuc 		return sys_safecopyto(endpt, grant, 0, (vir_bytes) &vnu,
484*433d6423SLionel Sambuc 		    sizeof(vnu));
485*433d6423SLionel Sambuc 
486*433d6423SLionel Sambuc 	case DIOCOPENCT:
487*433d6423SLionel Sambuc 		return sys_safecopyto(endpt, grant, 0,
488*433d6423SLionel Sambuc 		    (vir_bytes) &state.openct, sizeof(state.openct));
489*433d6423SLionel Sambuc 
490*433d6423SLionel Sambuc 	case DIOCFLUSH:
491*433d6423SLionel Sambuc 		if (state.fd == -1)
492*433d6423SLionel Sambuc 			return ENXIO;
493*433d6423SLionel Sambuc 
494*433d6423SLionel Sambuc 		fsync(state.fd);
495*433d6423SLionel Sambuc 
496*433d6423SLionel Sambuc 		return OK;
497*433d6423SLionel Sambuc 	}
498*433d6423SLionel Sambuc 
499*433d6423SLionel Sambuc 	return ENOTTY;
500*433d6423SLionel Sambuc }
501*433d6423SLionel Sambuc 
502*433d6423SLionel Sambuc /*
503*433d6423SLionel Sambuc  * Return a pointer to the partition structure for the given minor device.
504*433d6423SLionel Sambuc  */
505*433d6423SLionel Sambuc static struct device *
vnd_part(devminor_t minor)506*433d6423SLionel Sambuc vnd_part(devminor_t minor)
507*433d6423SLionel Sambuc {
508*433d6423SLionel Sambuc 	if (minor >= 0 && minor < DEV_PER_DRIVE)
509*433d6423SLionel Sambuc 		return &state.part[minor];
510*433d6423SLionel Sambuc 	else if ((unsigned int) (minor -= MINOR_d0p0s0) < SUB_PER_DRIVE)
511*433d6423SLionel Sambuc 		return &state.subpart[minor];
512*433d6423SLionel Sambuc 	else
513*433d6423SLionel Sambuc 		return NULL;
514*433d6423SLionel Sambuc }
515*433d6423SLionel Sambuc 
516*433d6423SLionel Sambuc /*
517*433d6423SLionel Sambuc  * Return geometry information.
518*433d6423SLionel Sambuc  */
519*433d6423SLionel Sambuc static void
vnd_geometry(devminor_t UNUSED (minor),struct part_geom * part)520*433d6423SLionel Sambuc vnd_geometry(devminor_t UNUSED(minor), struct part_geom *part)
521*433d6423SLionel Sambuc {
522*433d6423SLionel Sambuc 	part->cylinders = state.geom.cylinders;
523*433d6423SLionel Sambuc 	part->heads = state.geom.heads;
524*433d6423SLionel Sambuc 	part->sectors = state.geom.sectors;
525*433d6423SLionel Sambuc }
526*433d6423SLionel Sambuc 
527*433d6423SLionel Sambuc /*
528*433d6423SLionel Sambuc  * Initialize the device.
529*433d6423SLionel Sambuc  */
530*433d6423SLionel Sambuc static int
vnd_init(int UNUSED (type),sef_init_info_t * UNUSED (info))531*433d6423SLionel Sambuc vnd_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
532*433d6423SLionel Sambuc {
533*433d6423SLionel Sambuc 	long v;
534*433d6423SLionel Sambuc 
535*433d6423SLionel Sambuc 	/*
536*433d6423SLionel Sambuc 	 * No support for crash recovery.  The driver would have no way to
537*433d6423SLionel Sambuc 	 * reacquire the file descriptor for the target file.
538*433d6423SLionel Sambuc 	 */
539*433d6423SLionel Sambuc 
540*433d6423SLionel Sambuc 	/*
541*433d6423SLionel Sambuc 	 * The instance number is used for two purposes: reporting errors, and
542*433d6423SLionel Sambuc 	 * returning the proper unit number to userland in VNDIOCGET calls.
543*433d6423SLionel Sambuc 	 */
544*433d6423SLionel Sambuc 	v = 0;
545*433d6423SLionel Sambuc 	(void) env_parse("instance", "d", 0, &v, 0, 255);
546*433d6423SLionel Sambuc 	instance = (unsigned int) v;
547*433d6423SLionel Sambuc 
548*433d6423SLionel Sambuc 	state.openct = 0;
549*433d6423SLionel Sambuc 	state.exiting = FALSE;
550*433d6423SLionel Sambuc 	state.fd = -1;
551*433d6423SLionel Sambuc 
552*433d6423SLionel Sambuc 	return OK;
553*433d6423SLionel Sambuc }
554*433d6423SLionel Sambuc 
555*433d6423SLionel Sambuc /*
556*433d6423SLionel Sambuc  * Process an incoming signal.
557*433d6423SLionel Sambuc  */
558*433d6423SLionel Sambuc static void
vnd_signal(int signo)559*433d6423SLionel Sambuc vnd_signal(int signo)
560*433d6423SLionel Sambuc {
561*433d6423SLionel Sambuc 
562*433d6423SLionel Sambuc 	/* In case of a termination signal, initiate driver shutdown. */
563*433d6423SLionel Sambuc 	if (signo != SIGTERM)
564*433d6423SLionel Sambuc 		return;
565*433d6423SLionel Sambuc 
566*433d6423SLionel Sambuc 	state.exiting = TRUE;
567*433d6423SLionel Sambuc 
568*433d6423SLionel Sambuc 	/* Keep running until the device has been fully closed. */
569*433d6423SLionel Sambuc 	if (state.openct == 0)
570*433d6423SLionel Sambuc 		blockdriver_terminate();
571*433d6423SLionel Sambuc }
572*433d6423SLionel Sambuc 
573*433d6423SLionel Sambuc /*
574*433d6423SLionel Sambuc  * Set callbacks and initialize the System Event Framework (SEF).
575*433d6423SLionel Sambuc  */
576*433d6423SLionel Sambuc static void
vnd_startup(void)577*433d6423SLionel Sambuc vnd_startup(void)
578*433d6423SLionel Sambuc {
579*433d6423SLionel Sambuc 
580*433d6423SLionel Sambuc 	/* Register init and signal callbacks. */
581*433d6423SLionel Sambuc 	sef_setcb_init_fresh(vnd_init);
582*433d6423SLionel Sambuc 	sef_setcb_signal_handler(vnd_signal);
583*433d6423SLionel Sambuc 
584*433d6423SLionel Sambuc 	/* Let SEF perform startup. */
585*433d6423SLionel Sambuc 	sef_startup();
586*433d6423SLionel Sambuc }
587*433d6423SLionel Sambuc 
588*433d6423SLionel Sambuc /*
589*433d6423SLionel Sambuc  * Driver task.
590*433d6423SLionel Sambuc  */
591*433d6423SLionel Sambuc int
main(int argc,char ** argv)592*433d6423SLionel Sambuc main(int argc, char **argv)
593*433d6423SLionel Sambuc {
594*433d6423SLionel Sambuc 
595*433d6423SLionel Sambuc 	/* Initialize the driver. */
596*433d6423SLionel Sambuc 	env_setargs(argc, argv);
597*433d6423SLionel Sambuc 	vnd_startup();
598*433d6423SLionel Sambuc 
599*433d6423SLionel Sambuc 	/* Process requests until shutdown. */
600*433d6423SLionel Sambuc 	blockdriver_task(&vnd_dtab);
601*433d6423SLionel Sambuc 
602*433d6423SLionel Sambuc 	return 0;
603*433d6423SLionel Sambuc }
604