must have directory vnode as first op
[unix-history] / usr / src / sys / dev / vn.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1990 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
10 * %sccs.include.redist.c%
11 *
12 * from: Utah $Hdr: vn.c 1.1 91/04/30$
13 *
14 * @(#)vn.c 7.8 (Berkeley) %G%
15 */
16
17/*
18 * Vnode disk driver.
19 *
20 * Block/character interface to a vnode. Allows one to treat a file
21 * as a disk (e.g. build a filesystem in it, mount it, etc.).
22 *
23 * NOTE 1: This uses the VOP_BMAP/VOP_STRATEGY interface to the vnode
24 * instead of a simple VOP_RDWR. We do this to avoid distorting the
25 * local buffer cache.
26 *
27 * NOTE 2: There is a security issue involved with this driver.
28 * Once mounted all access to the contents of the "mapped" file via
29 * the special file is controlled by the permissions on the special
30 * file, the protection of the mapped file is ignored (effectively,
31 * by using root credentials in all transactions).
32 */
33#include "vn.h"
34#if NVN > 0
35
36#include "sys/param.h"
37#include "sys/systm.h"
38#include "sys/namei.h"
39#include "sys/proc.h"
40#include "sys/errno.h"
41#include "sys/dkstat.h"
42#include "sys/buf.h"
43#include "sys/malloc.h"
44#include "sys/ioctl.h"
45#include "sys/mount.h"
46#include "sys/vnode.h"
47#include "sys/specdev.h"
48#include "sys/file.h"
49#include "sys/uio.h"
50
51#include "vnioctl.h"
52
53#ifdef DEBUG
54int vndebug = 0x00;
55#define VDB_FOLLOW 0x01
56#define VDB_INIT 0x02
57#define VDB_IO 0x04
58#endif
59
60struct buf vnbuf[NVN];
61struct buf vntab[NVN];
62
63#define b_cylin b_resid
64
65#define vnunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */
66
67#define getvnbuf() \
68 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
69#define putvnbuf(bp) \
70 free((caddr_t)(bp), M_DEVBUF)
71
72struct vn_softc {
73 int sc_flags; /* flags */
74 size_t sc_size; /* size of vn */
75 struct vnode *sc_vp; /* vnode */
76 struct ucred *sc_cred; /* credentials */
77 int sc_maxactive; /* max # of active requests */
78} vn_softc[NVN];
79
80/* sc_flags */
81#define VNF_ALIVE 0x01
82#define VNF_INITED 0x02
83
84int
85vnopen(dev, flags, mode, p)
86 dev_t dev;
87 int flags, mode;
88 struct proc *p;
89{
90 int unit = vnunit(dev);
91
92#ifdef DEBUG
93 if (vndebug & VDB_FOLLOW)
94 printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p);
95#endif
96 if (unit >= NVN)
97 return(ENXIO);
98 return(0);
99}
100
101/*
102 * Break the request into bsize pieces and submit using VOP_BMAP/VOP_STRATEGY.
103 * Note that this driver can only be used for swapping over NFS on the hp
104 * since nfs_strategy on the vax cannot handle u-areas and page tables.
105 */
106vnstrategy(bp)
107 register struct buf *bp;
108{
109 int unit = vnunit(bp->b_dev);
110 register struct vn_softc *vn = &vn_softc[unit];
111 register struct buf *nbp;
112 register int bn, bsize, resid;
113 register caddr_t addr;
114 int sz, flags;
115 extern int vniodone();
116
117#ifdef DEBUG
118 if (vndebug & VDB_FOLLOW)
119 printf("vnstrategy(%x): unit %d\n", bp, unit);
120#endif
121 if ((vn->sc_flags & VNF_INITED) == 0) {
122 bp->b_error = ENXIO;
123 bp->b_flags |= B_ERROR;
124 biodone(bp);
125 return;
126 }
127 bn = bp->b_blkno;
128 sz = howmany(bp->b_bcount, DEV_BSIZE);
129 bp->b_resid = bp->b_bcount;
130 if (bn < 0 || bn + sz > vn->sc_size) {
131 if (bn != vn->sc_size) {
132 bp->b_error = EINVAL;
133 bp->b_flags |= B_ERROR;
134 }
135 biodone(bp);
136 return;
137 }
138 bn = dbtob(bn);
139 bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize;
140 addr = bp->b_un.b_addr;
141 flags = bp->b_flags | B_CALL;
142 for (resid = bp->b_resid; resid; resid -= sz) {
143 struct vnode *vp;
144 daddr_t nbn;
145 int off, s;
146
147 nbp = getvnbuf();
148 off = bn % bsize;
149 sz = MIN(bsize - off, resid);
150 (void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn);
151#ifdef DEBUG
152 if (vndebug & VDB_IO)
153 printf("vnstrategy: vp %x/%x bn %x/%x\n",
154 vn->sc_vp, vp, bn, nbn);
155#endif
156 nbp->b_flags = flags;
157 nbp->b_bcount = sz;
158 nbp->b_bufsize = bp->b_bufsize;
159 nbp->b_error = 0;
160 if (vp->v_type == VBLK || vp->v_type == VCHR)
161 nbp->b_dev = vp->v_rdev;
162 else
163 nbp->b_dev = NODEV;
164 nbp->b_un.b_addr = addr;
165 nbp->b_blkno = nbn + btodb(off);
166 nbp->b_proc = bp->b_proc;
167 nbp->b_iodone = vniodone;
168 nbp->b_vp = vp;
169 nbp->b_pfcent = (int) bp; /* XXX */
170 /*
171 * Just sort by block number
172 */
173 nbp->b_cylin = nbp->b_blkno;
174 s = splbio();
175 disksort(&vntab[unit], nbp);
176 if (vntab[unit].b_active < vn->sc_maxactive) {
177 vntab[unit].b_active++;
178 vnstart(unit);
179 }
180 splx(s);
181 bn += sz;
182 addr += sz;
183 }
184}
185
186/*
187 * Feed requests sequentially.
188 * We do it this way to keep from flooding NFS servers if we are connected
189 * to an NFS file. This places the burden on the client rather than the
190 * server.
191 */
192vnstart(unit)
193{
194 register struct vn_softc *vn = &vn_softc[unit];
195 register struct buf *bp;
196
197 /*
198 * Dequeue now since lower level strategy routine might
199 * queue using same links
200 */
201 bp = vntab[unit].b_actf;
202 vntab[unit].b_actf = bp->b_actf;
203#ifdef DEBUG
204 if (vndebug & VDB_IO)
205 printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
206 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
207 bp->b_bcount);
208#endif
209 VOP_STRATEGY(bp);
210}
211
212vniodone(bp)
213 register struct buf *bp;
214{
215 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */
216 register int unit = vnunit(pbp->b_dev);
217 int s;
218
219 s = splbio();
220#ifdef DEBUG
221 if (vndebug & VDB_IO)
222 printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
223 unit, bp, bp->b_vp, bp->b_blkno, bp->b_un.b_addr,
224 bp->b_bcount);
225#endif
226 if (bp->b_error) {
227#ifdef DEBUG
228 if (vndebug & VDB_IO)
229 printf("vniodone: bp %x error %d\n", bp, bp->b_error);
230#endif
231 pbp->b_flags |= B_ERROR;
232 pbp->b_error = biowait(bp);
233 }
234 pbp->b_resid -= bp->b_bcount;
235 putvnbuf(bp);
236 if (pbp->b_resid == 0) {
237#ifdef DEBUG
238 if (vndebug & VDB_IO)
239 printf("vniodone: pbp %x iodone\n", pbp);
240#endif
241 biodone(pbp);
242 }
243 if (vntab[unit].b_actf)
244 vnstart(unit);
245 else
246 vntab[unit].b_active--;
247 splx(s);
248}
249
250vnread(dev, uio, flags, p)
251 dev_t dev;
252 struct uio *uio;
253 int flags;
254 struct proc *p;
255{
256 register int unit = vnunit(dev);
257
258#ifdef DEBUG
259 if (vndebug & VDB_FOLLOW)
260 printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p);
261#endif
262 return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio));
263}
264
265vnwrite(dev, uio, flags, p)
266 dev_t dev;
267 struct uio *uio;
268 int flags;
269 struct proc *p;
270{
271 register int unit = vnunit(dev);
272
273#ifdef DEBUG
274 if (vndebug & VDB_FOLLOW)
275 printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p);
276#endif
277 return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio));
278}
279
280/* ARGSUSED */
281vnioctl(dev, cmd, data, flag, p)
282 dev_t dev;
283 u_long cmd;
284 caddr_t data;
285 int flag;
286 struct proc *p;
287{
288 int unit = vnunit(dev);
289 register struct vn_softc *vn;
290 struct vn_ioctl *vio;
291 struct vattr vattr;
292 struct nameidata nd;
293 int error;
294
295#ifdef DEBUG
296 if (vndebug & VDB_FOLLOW)
297 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
298 dev, cmd, data, flag, p, unit);
299#endif
300 error = suser(p->p_ucred, &p->p_acflag);
301 if (error)
302 return (error);
303 if (unit >= NVN)
304 return (ENXIO);
305
306 vn = &vn_softc[unit];
307 vio = (struct vn_ioctl *)data;
308 switch (cmd) {
309
310 case VNIOCSET:
311 if (vn->sc_flags & VNF_INITED)
312 return(EBUSY);
313 /*
314 * Always open for read and write.
315 * This is probably bogus, but it lets vn_open()
316 * weed out directories, sockets, etc. so we don't
317 * have to worry about them.
318 */
319 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
320 if (error = vn_open(&nd, FREAD|FWRITE, 0))
321 return(error);
322 if (error = VOP_GETATTR(nd.ni_vp, &vattr, p->p_ucred, p)) {
323 VOP_UNLOCK(nd.ni_vp);
324 (void) vn_close(nd.ni_vp, FREAD|FWRITE, p->p_ucred, p);
325 return(error);
326 }
327 VOP_UNLOCK(nd.ni_vp);
328 vn->sc_vp = nd.ni_vp;
329 vn->sc_size = btodb(vattr.va_size); /* note truncation */
330 if (error = vnsetcred(vn, p->p_ucred)) {
331 (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p);
332 return(error);
333 }
334 vnthrottle(vn, vn->sc_vp);
335 vio->vn_size = dbtob(vn->sc_size);
336 vn->sc_flags |= VNF_INITED;
337#ifdef DEBUG
338 if (vndebug & VDB_INIT)
339 printf("vnioctl: SET vp %x size %x\n",
340 vn->sc_vp, vn->sc_size);
341#endif
342 break;
343
344 case VNIOCCLR:
345 if ((vn->sc_flags & VNF_INITED) == 0)
346 return(ENXIO);
347 vnclear(vn);
348#ifdef DEBUG
349 if (vndebug & VDB_INIT)
350 printf("vnioctl: CLRed\n");
351#endif
352 break;
353
354 default:
355 return(ENXIO);
356 }
357 return(0);
358}
359
360/*
361 * Duplicate the current processes' credentials. Since we are called only
362 * as the result of a SET ioctl and only root can do that, any future access
363 * to this "disk" is essentially as root. Note that credentials may change
364 * if some other uid can write directly to the mapped file (NFS).
365 */
366vnsetcred(vn, cred)
367 register struct vn_softc *vn;
368 struct ucred cred;
369{
370 struct uio auio;
371 struct iovec aiov;
372 char tmpbuf[DEV_BSIZE];
373
374 vn->sc_cred = crdup(cred);
375 /* XXX: Horrible kludge to establish credentials for NFS */
376 aiov.iov_base = tmpbuf;
377 aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size));
378 auio.uio_iov = &aiov;
379 auio.uio_iovcnt = 1;
380 auio.uio_offset = 0;
381 auio.uio_rw = UIO_READ;
382 auio.uio_segflg = UIO_SYSSPACE;
383 auio.uio_resid = aiov.iov_len;
384 return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred));
385}
386
387/*
388 * Set maxactive based on FS type
389 */
390vnthrottle(vn, vp)
391 register struct vn_softc *vn;
392 struct vnode *vp;
393{
394 extern struct vnodeops ufs_vnodeops, nfsv2_vnodeops;
395
396 if (vp->v_op == &nfsv2_vnodeops)
397 vn->sc_maxactive = 2;
398 else
399 vn->sc_maxactive = 8;
400
401 if (vn->sc_maxactive < 1)
402 vn->sc_maxactive = 1;
403}
404
405vnshutdown()
406{
407 register struct vn_softc *vn;
408
409 for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++)
410 if (vn->sc_flags & VNF_INITED)
411 vnclear(vn);
412}
413
414vnclear(vn)
415 register struct vn_softc *vn;
416{
417 register struct vnode *vp = vn->sc_vp;
418 struct proc *p = curproc; /* XXX */
419
420#ifdef DEBUG
421 if (vndebug & VDB_FOLLOW)
422 printf("vnclear(%x): vp %x\n", vp);
423#endif
424 vn->sc_flags &= ~VNF_INITED;
425 if (vp == (struct vnode *)0)
426 panic("vnioctl: null vp");
427#if 0
428 /* XXX - this doesn't work right now */
429 (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
430#endif
431 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
432 crfree(vn->sc_cred);
433 vn->sc_vp = (struct vnode *)0;
434 vn->sc_cred = (struct ucred *)0;
435 vn->sc_size = 0;
436}
437
438vnsize(dev)
439 dev_t dev;
440{
441 int unit = vnunit(dev);
442 register struct vn_softc *vn = &vn_softc[unit];
443
444 if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0)
445 return(-1);
446 return(vn->sc_size);
447}
448
449vndump(dev)
450{
451 return(ENXIO);
452}
453#endif