must have directory vnode as first op
[unix-history] / usr / src / sys / dev / vn.c
CommitLineData
60f56dfc
KM
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 *
0c17d899 12 * from: Utah $Hdr: vn.c 1.1 91/04/30$
60f56dfc 13 *
55ff681d 14 * @(#)vn.c 7.8 (Berkeley) %G%
60f56dfc
KM
15 */
16
0893c353 17/*
0c17d899 18 * Vnode disk driver.
60f56dfc 19 *
0c17d899
MH
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.).
60f56dfc 22 *
0c17d899
MH
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.
60f56dfc
KM
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 */
0c17d899
MH
33#include "vn.h"
34#if NVN > 0
60f56dfc 35
b28b3a13
KB
36#include "sys/param.h"
37#include "sys/systm.h"
0c17d899
MH
38#include "sys/namei.h"
39#include "sys/proc.h"
b28b3a13
KB
40#include "sys/errno.h"
41#include "sys/dkstat.h"
0c17d899
MH
42#include "sys/buf.h"
43#include "sys/malloc.h"
b28b3a13 44#include "sys/ioctl.h"
0c17d899 45#include "sys/mount.h"
b28b3a13 46#include "sys/vnode.h"
0c17d899 47#include "sys/specdev.h"
b28b3a13
KB
48#include "sys/file.h"
49#include "sys/uio.h"
60f56dfc 50
0c17d899 51#include "vnioctl.h"
60f56dfc
KM
52
53#ifdef DEBUG
0c17d899
MH
54int vndebug = 0x00;
55#define VDB_FOLLOW 0x01
56#define VDB_INIT 0x02
57#define VDB_IO 0x04
60f56dfc
KM
58#endif
59
0c17d899
MH
60struct buf vnbuf[NVN];
61struct buf vntab[NVN];
60f56dfc
KM
62
63#define b_cylin b_resid
64
0c17d899 65#define vnunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */
60f56dfc 66
0c17d899 67#define getvnbuf() \
60f56dfc 68 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
0c17d899 69#define putvnbuf(bp) \
60f56dfc
KM
70 free((caddr_t)(bp), M_DEVBUF)
71
0c17d899 72struct vn_softc {
60f56dfc 73 int sc_flags; /* flags */
0c17d899 74 size_t sc_size; /* size of vn */
60f56dfc
KM
75 struct vnode *sc_vp; /* vnode */
76 struct ucred *sc_cred; /* credentials */
77 int sc_maxactive; /* max # of active requests */
0c17d899 78} vn_softc[NVN];
60f56dfc
KM
79
80/* sc_flags */
0c17d899
MH
81#define VNF_ALIVE 0x01
82#define VNF_INITED 0x02
60f56dfc 83
0c17d899
MH
84int
85vnopen(dev, flags, mode, p)
60f56dfc 86 dev_t dev;
0c17d899
MH
87 int flags, mode;
88 struct proc *p;
60f56dfc 89{
0c17d899 90 int unit = vnunit(dev);
60f56dfc
KM
91
92#ifdef DEBUG
0c17d899
MH
93 if (vndebug & VDB_FOLLOW)
94 printf("vnopen(%x, %x, %x, %x)\n", dev, flags, mode, p);
60f56dfc 95#endif
0c17d899 96 if (unit >= NVN)
60f56dfc
KM
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 */
0c17d899 106vnstrategy(bp)
60f56dfc
KM
107 register struct buf *bp;
108{
0c17d899
MH
109 int unit = vnunit(bp->b_dev);
110 register struct vn_softc *vn = &vn_softc[unit];
60f56dfc
KM
111 register struct buf *nbp;
112 register int bn, bsize, resid;
113 register caddr_t addr;
114 int sz, flags;
0c17d899 115 extern int vniodone();
60f56dfc
KM
116
117#ifdef DEBUG
0c17d899
MH
118 if (vndebug & VDB_FOLLOW)
119 printf("vnstrategy(%x): unit %d\n", bp, unit);
60f56dfc 120#endif
0c17d899 121 if ((vn->sc_flags & VNF_INITED) == 0) {
60f56dfc
KM
122 bp->b_error = ENXIO;
123 bp->b_flags |= B_ERROR;
0c17d899 124 biodone(bp);
60f56dfc
KM
125 return;
126 }
127 bn = bp->b_blkno;
128 sz = howmany(bp->b_bcount, DEV_BSIZE);
129 bp->b_resid = bp->b_bcount;
0c17d899
MH
130 if (bn < 0 || bn + sz > vn->sc_size) {
131 if (bn != vn->sc_size) {
60f56dfc
KM
132 bp->b_error = EINVAL;
133 bp->b_flags |= B_ERROR;
134 }
0c17d899 135 biodone(bp);
60f56dfc
KM
136 return;
137 }
138 bn = dbtob(bn);
cc173077 139 bsize = vn->sc_vp->v_mount->mnt_stat.f_iosize;
60f56dfc
KM
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
0c17d899 147 nbp = getvnbuf();
60f56dfc
KM
148 off = bn % bsize;
149 sz = MIN(bsize - off, resid);
0c17d899 150 (void) VOP_BMAP(vn->sc_vp, bn / bsize, &vp, &nbn);
60f56dfc 151#ifdef DEBUG
0c17d899
MH
152 if (vndebug & VDB_IO)
153 printf("vnstrategy: vp %x/%x bn %x/%x\n",
154 vn->sc_vp, vp, bn, nbn);
60f56dfc
KM
155#endif
156 nbp->b_flags = flags;
157 nbp->b_bcount = sz;
158 nbp->b_bufsize = bp->b_bufsize;
159 nbp->b_error = 0;
0c17d899
MH
160 if (vp->v_type == VBLK || vp->v_type == VCHR)
161 nbp->b_dev = vp->v_rdev;
162 else
163 nbp->b_dev = NODEV;
60f56dfc
KM
164 nbp->b_un.b_addr = addr;
165 nbp->b_blkno = nbn + btodb(off);
166 nbp->b_proc = bp->b_proc;
0c17d899 167 nbp->b_iodone = vniodone;
60f56dfc
KM
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();
0c17d899
MH
175 disksort(&vntab[unit], nbp);
176 if (vntab[unit].b_active < vn->sc_maxactive) {
177 vntab[unit].b_active++;
178 vnstart(unit);
60f56dfc
KM
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 */
0c17d899 192vnstart(unit)
60f56dfc 193{
0c17d899 194 register struct vn_softc *vn = &vn_softc[unit];
60f56dfc
KM
195 register struct buf *bp;
196
197 /*
198 * Dequeue now since lower level strategy routine might
199 * queue using same links
200 */
0c17d899
MH
201 bp = vntab[unit].b_actf;
202 vntab[unit].b_actf = bp->b_actf;
60f56dfc 203#ifdef DEBUG
0c17d899
MH
204 if (vndebug & VDB_IO)
205 printf("vnstart(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
60f56dfc
KM
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
0c17d899 212vniodone(bp)
60f56dfc
KM
213 register struct buf *bp;
214{
215 register struct buf *pbp = (struct buf *)bp->b_pfcent; /* XXX */
0c17d899 216 register int unit = vnunit(pbp->b_dev);
60f56dfc
KM
217 int s;
218
219 s = splbio();
220#ifdef DEBUG
0c17d899
MH
221 if (vndebug & VDB_IO)
222 printf("vniodone(%d): bp %x vp %x blkno %x addr %x cnt %x\n",
60f56dfc
KM
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
0c17d899
MH
228 if (vndebug & VDB_IO)
229 printf("vniodone: bp %x error %d\n", bp, bp->b_error);
60f56dfc
KM
230#endif
231 pbp->b_flags |= B_ERROR;
0c17d899 232 pbp->b_error = biowait(bp);
60f56dfc
KM
233 }
234 pbp->b_resid -= bp->b_bcount;
0c17d899 235 putvnbuf(bp);
60f56dfc
KM
236 if (pbp->b_resid == 0) {
237#ifdef DEBUG
0c17d899
MH
238 if (vndebug & VDB_IO)
239 printf("vniodone: pbp %x iodone\n", pbp);
60f56dfc 240#endif
0c17d899 241 biodone(pbp);
60f56dfc 242 }
0c17d899
MH
243 if (vntab[unit].b_actf)
244 vnstart(unit);
60f56dfc 245 else
0c17d899 246 vntab[unit].b_active--;
60f56dfc
KM
247 splx(s);
248}
249
0c17d899 250vnread(dev, uio, flags, p)
60f56dfc
KM
251 dev_t dev;
252 struct uio *uio;
0c17d899
MH
253 int flags;
254 struct proc *p;
60f56dfc 255{
0c17d899 256 register int unit = vnunit(dev);
60f56dfc
KM
257
258#ifdef DEBUG
0c17d899
MH
259 if (vndebug & VDB_FOLLOW)
260 printf("vnread(%x, %x, %x, %x)\n", dev, uio, flags, p);
60f56dfc 261#endif
0c17d899 262 return(physio(vnstrategy, &vnbuf[unit], dev, B_READ, minphys, uio));
60f56dfc
KM
263}
264
0c17d899 265vnwrite(dev, uio, flags, p)
60f56dfc
KM
266 dev_t dev;
267 struct uio *uio;
0c17d899
MH
268 int flags;
269 struct proc *p;
60f56dfc 270{
0c17d899 271 register int unit = vnunit(dev);
60f56dfc
KM
272
273#ifdef DEBUG
0c17d899
MH
274 if (vndebug & VDB_FOLLOW)
275 printf("vnwrite(%x, %x, %x, %x)\n", dev, uio, flags, p);
60f56dfc 276#endif
0c17d899 277 return(physio(vnstrategy, &vnbuf[unit], dev, B_WRITE, minphys, uio));
60f56dfc
KM
278}
279
280/* ARGSUSED */
0c17d899 281vnioctl(dev, cmd, data, flag, p)
60f56dfc
KM
282 dev_t dev;
283 u_long cmd;
284 caddr_t data;
285 int flag;
0c17d899 286 struct proc *p;
60f56dfc 287{
0c17d899
MH
288 int unit = vnunit(dev);
289 register struct vn_softc *vn;
290 struct vn_ioctl *vio;
60f56dfc 291 struct vattr vattr;
0c17d899 292 struct nameidata nd;
60f56dfc
KM
293 int error;
294
295#ifdef DEBUG
0c17d899
MH
296 if (vndebug & VDB_FOLLOW)
297 printf("vnioctl(%x, %x, %x, %x, %x): unit %d\n",
298 dev, cmd, data, flag, p, unit);
60f56dfc 299#endif
0c17d899 300 error = suser(p->p_ucred, &p->p_acflag);
60f56dfc
KM
301 if (error)
302 return (error);
0c17d899 303 if (unit >= NVN)
60f56dfc
KM
304 return (ENXIO);
305
0c17d899
MH
306 vn = &vn_softc[unit];
307 vio = (struct vn_ioctl *)data;
60f56dfc
KM
308 switch (cmd) {
309
0c17d899
MH
310 case VNIOCSET:
311 if (vn->sc_flags & VNF_INITED)
60f56dfc
KM
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 */
55ff681d
MH
319 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, vio->vn_file, p);
320 if (error = vn_open(&nd, FREAD|FWRITE, 0))
60f56dfc 321 return(error);
f2eb348c
KM
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);
60f56dfc
KM
325 return(error);
326 }
f2eb348c 327 VOP_UNLOCK(nd.ni_vp);
0c17d899
MH
328 vn->sc_vp = nd.ni_vp;
329 vn->sc_size = btodb(vattr.va_size); /* note truncation */
f2eb348c
KM
330 if (error = vnsetcred(vn, p->p_ucred)) {
331 (void) vn_close(vn->sc_vp, FREAD|FWRITE, p->p_ucred, p);
60f56dfc
KM
332 return(error);
333 }
0c17d899
MH
334 vnthrottle(vn, vn->sc_vp);
335 vio->vn_size = dbtob(vn->sc_size);
336 vn->sc_flags |= VNF_INITED;
60f56dfc 337#ifdef DEBUG
0c17d899
MH
338 if (vndebug & VDB_INIT)
339 printf("vnioctl: SET vp %x size %x\n",
340 vn->sc_vp, vn->sc_size);
60f56dfc
KM
341#endif
342 break;
343
0c17d899
MH
344 case VNIOCCLR:
345 if ((vn->sc_flags & VNF_INITED) == 0)
60f56dfc 346 return(ENXIO);
0c17d899 347 vnclear(vn);
60f56dfc 348#ifdef DEBUG
0c17d899
MH
349 if (vndebug & VDB_INIT)
350 printf("vnioctl: CLRed\n");
60f56dfc
KM
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 */
0c17d899
MH
366vnsetcred(vn, cred)
367 register struct vn_softc *vn;
368 struct ucred cred;
60f56dfc
KM
369{
370 struct uio auio;
371 struct iovec aiov;
372 char tmpbuf[DEV_BSIZE];
373
0c17d899 374 vn->sc_cred = crdup(cred);
60f56dfc
KM
375 /* XXX: Horrible kludge to establish credentials for NFS */
376 aiov.iov_base = tmpbuf;
0c17d899 377 aiov.iov_len = MIN(DEV_BSIZE, dbtob(vn->sc_size));
60f56dfc
KM
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;
0c17d899 384 return(VOP_READ(vn->sc_vp, &auio, 0, vn->sc_cred));
60f56dfc
KM
385}
386
387/*
388 * Set maxactive based on FS type
389 */
0c17d899
MH
390vnthrottle(vn, vp)
391 register struct vn_softc *vn;
60f56dfc
KM
392 struct vnode *vp;
393{
0c17d899 394 extern struct vnodeops ufs_vnodeops, nfsv2_vnodeops;
60f56dfc 395
0c17d899
MH
396 if (vp->v_op == &nfsv2_vnodeops)
397 vn->sc_maxactive = 2;
60f56dfc 398 else
0c17d899 399 vn->sc_maxactive = 8;
60f56dfc 400
0c17d899
MH
401 if (vn->sc_maxactive < 1)
402 vn->sc_maxactive = 1;
60f56dfc
KM
403}
404
0c17d899 405vnshutdown()
60f56dfc 406{
0c17d899 407 register struct vn_softc *vn;
60f56dfc 408
0c17d899
MH
409 for (vn = &vn_softc[0]; vn < &vn_softc[NVN]; vn++)
410 if (vn->sc_flags & VNF_INITED)
411 vnclear(vn);
60f56dfc
KM
412}
413
0c17d899
MH
414vnclear(vn)
415 register struct vn_softc *vn;
60f56dfc 416{
0c17d899 417 register struct vnode *vp = vn->sc_vp;
f2eb348c 418 struct proc *p = curproc; /* XXX */
60f56dfc
KM
419
420#ifdef DEBUG
0c17d899
MH
421 if (vndebug & VDB_FOLLOW)
422 printf("vnclear(%x): vp %x\n", vp);
60f56dfc 423#endif
0c17d899 424 vn->sc_flags &= ~VNF_INITED;
60f56dfc 425 if (vp == (struct vnode *)0)
0c17d899 426 panic("vnioctl: null vp");
60f56dfc
KM
427#if 0
428 /* XXX - this doesn't work right now */
0c17d899 429 (void) VOP_FSYNC(vp, 0, vn->sc_cred, MNT_WAIT, p);
60f56dfc 430#endif
f2eb348c 431 (void) vn_close(vp, FREAD|FWRITE, vn->sc_cred, p);
0c17d899
MH
432 crfree(vn->sc_cred);
433 vn->sc_vp = (struct vnode *)0;
434 vn->sc_cred = (struct ucred *)0;
435 vn->sc_size = 0;
60f56dfc
KM
436}
437
0c17d899 438vnsize(dev)
60f56dfc
KM
439 dev_t dev;
440{
0c17d899
MH
441 int unit = vnunit(dev);
442 register struct vn_softc *vn = &vn_softc[unit];
60f56dfc 443
0c17d899 444 if (unit >= NVN || (vn->sc_flags & VNF_INITED) == 0)
60f56dfc 445 return(-1);
0c17d899 446 return(vn->sc_size);
60f56dfc
KM
447}
448
0c17d899 449vndump(dev)
60f56dfc
KM
450{
451 return(ENXIO);
452}
453#endif