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