add POSIX-style byte-level record locking
[unix-history] / usr / src / sys / miscfs / specfs / spec_vnops.c
CommitLineData
a1d35437
KM
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
dbf0c423 5 * %sccs.include.redist.c%
a1d35437 6 *
a4128336 7 * @(#)spec_vnops.c 7.30 (Berkeley) %G%
a1d35437
KM
8 */
9
10#include "param.h"
11#include "systm.h"
3d5d83ff
KM
12#include "user.h"
13#include "kernel.h"
a1d35437
KM
14#include "conf.h"
15#include "buf.h"
786053cd 16#include "mount.h"
a1d35437 17#include "vnode.h"
0f93ba7b 18#include "specdev.h"
1c00bf64 19#include "stat.h"
a1d35437 20#include "errno.h"
7d4e5ac1
KM
21#include "ioctl.h"
22#include "file.h"
23#include "disklabel.h"
a1d35437 24
ccee3c59
MK
25/* symbolic sleep message strings for devices */
26char devopn[] = "devopn";
27char devio[] = "devio";
28char devwait[] = "devwait";
29char devin[] = "devin";
30char devout[] = "devout";
31char devioc[] = "devioc";
32char devcls[] = "devcls";
33
ad27f720
KM
34int spec_lookup(),
35 spec_open(),
36 spec_read(),
37 spec_write(),
38 spec_strategy(),
b9a4d0ff 39 spec_bmap(),
ad27f720
KM
40 spec_ioctl(),
41 spec_select(),
42 spec_lock(),
43 spec_unlock(),
44 spec_close(),
b9a4d0ff 45 spec_print(),
a4128336 46 spec_advlock(),
7bbe72a5 47 spec_ebadf(),
ad27f720
KM
48 spec_badop(),
49 spec_nullop();
50
51struct vnodeops spec_vnodeops = {
7bbe72a5
KM
52 spec_lookup, /* lookup */
53 spec_badop, /* create */
54 spec_badop, /* mknod */
55 spec_open, /* open */
56 spec_close, /* close */
57 spec_ebadf, /* access */
58 spec_ebadf, /* getattr */
59 spec_ebadf, /* setattr */
60 spec_read, /* read */
61 spec_write, /* write */
62 spec_ioctl, /* ioctl */
63 spec_select, /* select */
64 spec_badop, /* mmap */
65 spec_nullop, /* fsync */
66 spec_badop, /* seek */
67 spec_badop, /* remove */
68 spec_badop, /* link */
69 spec_badop, /* rename */
70 spec_badop, /* mkdir */
71 spec_badop, /* rmdir */
72 spec_badop, /* symlink */
73 spec_badop, /* readdir */
74 spec_badop, /* readlink */
75 spec_badop, /* abortop */
76 spec_nullop, /* inactive */
77 spec_nullop, /* reclaim */
78 spec_lock, /* lock */
79 spec_unlock, /* unlock */
b9a4d0ff 80 spec_bmap, /* bmap */
7bbe72a5 81 spec_strategy, /* strategy */
b9a4d0ff 82 spec_print, /* print */
d4383369 83 spec_nullop, /* islocked */
a4128336 84 spec_advlock, /* advlock */
a1d35437
KM
85};
86
59b0713e
KM
87/*
88 * Trivial lookup routine that always fails.
89 */
ad27f720 90spec_lookup(vp, ndp)
59b0713e
KM
91 struct vnode *vp;
92 struct nameidata *ndp;
93{
94
95 ndp->ni_dvp = vp;
96 ndp->ni_vp = NULL;
97 return (ENOTDIR);
98}
99
a1d35437
KM
100/*
101 * Open called to allow handler
102 * of special files to initialize and
103 * validate before actual IO.
104 */
3d5d83ff 105/* ARGSUSED */
ad27f720 106spec_open(vp, mode, cred)
a1d35437
KM
107 register struct vnode *vp;
108 int mode;
109 struct ucred *cred;
110{
111 dev_t dev = (dev_t)vp->v_rdev;
112 register int maj = major(dev);
5b2e9327 113 int error;
a1d35437 114
54fb9dc2 115 if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV))
786053cd
KM
116 return (ENXIO);
117
a1d35437
KM
118 switch (vp->v_type) {
119
120 case VCHR:
121 if ((u_int)maj >= nchrdev)
122 return (ENXIO);
1c00bf64 123 return ((*cdevsw[maj].d_open)(dev, mode, S_IFCHR));
a1d35437
KM
124
125 case VBLK:
126 if ((u_int)maj >= nblkdev)
127 return (ENXIO);
5b2e9327
KM
128 if (error = mountedon(vp))
129 return (error);
1c00bf64 130 return ((*bdevsw[maj].d_open)(dev, mode, S_IFBLK));
a1d35437
KM
131 }
132 return (0);
133}
134
a1d35437
KM
135/*
136 * Vnode op for read
137 */
fe6cdffe 138/* ARGSUSED */
43444338 139spec_read(vp, uio, ioflag, cred)
a1d35437 140 register struct vnode *vp;
7d4e5ac1 141 register struct uio *uio;
a1d35437
KM
142 int ioflag;
143 struct ucred *cred;
144{
7d4e5ac1
KM
145 struct buf *bp;
146 daddr_t bn;
147 long bsize, bscale;
148 struct partinfo dpart;
149 register int n, on;
150 int error = 0;
43444338 151 extern int mem_no;
a1d35437 152
43444338
KM
153 if (uio->uio_rw != UIO_READ)
154 panic("spec_read mode");
155 if (uio->uio_resid == 0)
156 return (0);
43444338
KM
157
158 switch (vp->v_type) {
159
160 case VCHR:
161 /*
162 * Negative offsets allowed only for /dev/kmem
163 */
164 if (uio->uio_offset < 0 && major(vp->v_rdev) != mem_no)
165 return (EINVAL);
3d5d83ff 166 VOP_UNLOCK(vp);
43444338
KM
167 error = (*cdevsw[major(vp->v_rdev)].d_read)
168 (vp->v_rdev, uio, ioflag);
169 VOP_LOCK(vp);
170 return (error);
171
172 case VBLK:
173 if (uio->uio_offset < 0)
174 return (EINVAL);
7d4e5ac1
KM
175 bsize = BLKDEV_IOSIZE;
176 if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART,
177 (caddr_t)&dpart, FREAD) == 0) {
178 if (dpart.part->p_fstype == FS_BSDFFS &&
179 dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
180 bsize = dpart.part->p_frag *
181 dpart.part->p_fsize;
182 }
183 bscale = bsize / DEV_BSIZE;
184 do {
185 bn = (uio->uio_offset / DEV_BSIZE) &~ (bscale - 1);
186 on = uio->uio_offset % bsize;
187 n = MIN((unsigned)(bsize - on), uio->uio_resid);
188 if (vp->v_lastr + bscale == bn)
189 error = breada(vp, bn, (int)bsize, bn + bscale,
190 (int)bsize, NOCRED, &bp);
191 else
192 error = bread(vp, bn, (int)bsize, NOCRED, &bp);
193 vp->v_lastr = bn;
194 n = MIN(n, bsize - bp->b_resid);
195 if (error) {
196 brelse(bp);
197 return (error);
198 }
199 error = uiomove(bp->b_un.b_addr + on, n, uio);
200 if (n + on == bsize)
201 bp->b_flags |= B_AGE;
202 brelse(bp);
203 } while (error == 0 && uio->uio_resid > 0 && n != 0);
204 return (error);
43444338
KM
205
206 default:
207 panic("spec_read type");
208 }
209 /* NOTREACHED */
a1d35437
KM
210}
211
212/*
213 * Vnode op for write
214 */
fe6cdffe 215/* ARGSUSED */
43444338 216spec_write(vp, uio, ioflag, cred)
a1d35437 217 register struct vnode *vp;
7d4e5ac1 218 register struct uio *uio;
a1d35437
KM
219 int ioflag;
220 struct ucred *cred;
221{
7d4e5ac1
KM
222 struct buf *bp;
223 daddr_t bn;
224 int bsize, blkmask;
225 struct partinfo dpart;
9db58063
KM
226 register int n, on;
227 int error = 0;
43444338 228 extern int mem_no;
a1d35437 229
43444338
KM
230 if (uio->uio_rw != UIO_WRITE)
231 panic("spec_write mode");
43444338
KM
232
233 switch (vp->v_type) {
234
235 case VCHR:
236 /*
237 * Negative offsets allowed only for /dev/kmem
238 */
239 if (uio->uio_offset < 0 && major(vp->v_rdev) != mem_no)
240 return (EINVAL);
3d5d83ff 241 VOP_UNLOCK(vp);
43444338
KM
242 error = (*cdevsw[major(vp->v_rdev)].d_write)
243 (vp->v_rdev, uio, ioflag);
244 VOP_LOCK(vp);
245 return (error);
246
247 case VBLK:
248 if (uio->uio_resid == 0)
249 return (0);
250 if (uio->uio_offset < 0)
251 return (EINVAL);
7d4e5ac1
KM
252 bsize = BLKDEV_IOSIZE;
253 if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART,
254 (caddr_t)&dpart, FREAD) == 0) {
255 if (dpart.part->p_fstype == FS_BSDFFS &&
256 dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
257 bsize = dpart.part->p_frag *
258 dpart.part->p_fsize;
259 }
260 blkmask = (bsize / DEV_BSIZE) - 1;
261 do {
262 bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask;
263 on = uio->uio_offset % bsize;
264 n = MIN((unsigned)(bsize - on), uio->uio_resid);
7d4e5ac1
KM
265 if (n == bsize)
266 bp = getblk(vp, bn, bsize);
267 else
268 error = bread(vp, bn, bsize, NOCRED, &bp);
269 n = MIN(n, bsize - bp->b_resid);
270 if (error) {
271 brelse(bp);
272 return (error);
273 }
274 error = uiomove(bp->b_un.b_addr + on, n, uio);
275 if (n + on == bsize) {
276 bp->b_flags |= B_AGE;
277 bawrite(bp);
278 } else
279 bdwrite(bp);
280 } while (error == 0 && uio->uio_resid > 0 && n != 0);
281 return (error);
43444338
KM
282
283 default:
284 panic("spec_write type");
285 }
286 /* NOTREACHED */
a1d35437
KM
287}
288
289/*
290 * Device ioctl operation.
291 */
3d5d83ff 292/* ARGSUSED */
ad27f720 293spec_ioctl(vp, com, data, fflag, cred)
a1d35437 294 struct vnode *vp;
b9a4d0ff 295 int com;
a1d35437
KM
296 caddr_t data;
297 int fflag;
298 struct ucred *cred;
299{
3d5d83ff 300 dev_t dev = vp->v_rdev;
a1d35437
KM
301
302 switch (vp->v_type) {
303
304 case VCHR:
305 return ((*cdevsw[major(dev)].d_ioctl)(dev, com, data, fflag));
306
307 case VBLK:
b9a4d0ff
KM
308 if (com == 0 && (int)data == B_TAPE)
309 if (bdevsw[major(dev)].d_flags & B_TAPE)
310 return (0);
311 else
312 return (1);
a1d35437
KM
313 return ((*bdevsw[major(dev)].d_ioctl)(dev, com, data, fflag));
314
315 default:
ad27f720 316 panic("spec_ioctl");
a1d35437
KM
317 /* NOTREACHED */
318 }
319}
320
3d5d83ff 321/* ARGSUSED */
fbb80d56 322spec_select(vp, which, fflags, cred)
a1d35437 323 struct vnode *vp;
fbb80d56 324 int which, fflags;
a1d35437
KM
325 struct ucred *cred;
326{
a1d35437
KM
327 register dev_t dev;
328
329 switch (vp->v_type) {
330
331 default:
332 return (1); /* XXX */
333
334 case VCHR:
3d5d83ff 335 dev = vp->v_rdev;
a1d35437
KM
336 return (*cdevsw[major(dev)].d_select)(dev, which);
337 }
338}
339
340/*
341 * Just call the device strategy routine
342 */
ad27f720 343spec_strategy(bp)
a1d35437
KM
344 register struct buf *bp;
345{
b9a4d0ff 346
a1d35437
KM
347 (*bdevsw[major(bp->b_dev)].d_strategy)(bp);
348 return (0);
349}
350
b9a4d0ff
KM
351/*
352 * This is a noop, simply returning what one has been given.
353 */
354spec_bmap(vp, bn, vpp, bnp)
355 struct vnode *vp;
356 daddr_t bn;
357 struct vnode **vpp;
358 daddr_t *bnp;
359{
360
361 if (vpp != NULL)
362 *vpp = vp;
363 if (bnp != NULL)
364 *bnp = bn;
365 return (0);
366}
367
1c00bf64
KM
368/*
369 * At the moment we do not do any locking.
370 */
ff4fb102 371/* ARGSUSED */
ad27f720 372spec_lock(vp)
a1d35437
KM
373 struct vnode *vp;
374{
a1d35437 375
a1d35437
KM
376 return (0);
377}
378
ff4fb102 379/* ARGSUSED */
ad27f720 380spec_unlock(vp)
a1d35437
KM
381 struct vnode *vp;
382{
a1d35437 383
a1d35437
KM
384 return (0);
385}
386
a1d35437
KM
387/*
388 * Device close routine
389 */
3d5d83ff 390/* ARGSUSED */
ad27f720 391spec_close(vp, flag, cred)
3d5d83ff 392 register struct vnode *vp;
a1d35437
KM
393 int flag;
394 struct ucred *cred;
395{
396 dev_t dev = vp->v_rdev;
3d5d83ff 397 int (*cfunc)();
ccee3c59 398 int mode;
a1d35437 399
3d5d83ff
KM
400 switch (vp->v_type) {
401
402 case VCHR:
f4b3ea62
KM
403 /*
404 * If the vnode is locked, then we are in the midst
405 * of forcably closing the device, otherwise we only
406 * close on last reference.
407 */
3ab57521 408 if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
3d5d83ff
KM
409 return (0);
410 cfunc = cdevsw[major(dev)].d_close;
1c00bf64 411 mode = S_IFCHR;
3d5d83ff
KM
412 break;
413
414 case VBLK:
415 /*
416 * On last close of a block device (that isn't mounted)
417 * we must invalidate any in core blocks, so that
418 * we can, for instance, change floppy disks.
419 */
b9a4d0ff
KM
420 vflushbuf(vp, 0);
421 if (vinvalbuf(vp, 1))
4a29fa35 422 return (0);
3d5d83ff 423 /*
f4b3ea62
KM
424 * We do not want to really close the device if it
425 * is still in use unless we are trying to close it
426 * forcibly. Since every use (buffer, vnode, swap, cmap)
3ab57521
KM
427 * holds a reference to the vnode, and because we mark
428 * any other vnodes that alias this device, when the
429 * sum of the reference counts on all the aliased
430 * vnodes descends to one, we are on last close.
3d5d83ff 431 */
3ab57521 432 if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
3d5d83ff
KM
433 return (0);
434 cfunc = bdevsw[major(dev)].d_close;
1c00bf64 435 mode = S_IFBLK;
3d5d83ff
KM
436 break;
437
438 default:
ad27f720 439 panic("spec_close: not special");
3d5d83ff
KM
440 }
441
ccee3c59 442 return ((*cfunc)(dev, flag, mode));
a1d35437
KM
443}
444
b9a4d0ff
KM
445/*
446 * Print out the contents of a special device vnode.
447 */
448spec_print(vp)
449 struct vnode *vp;
450{
451
452 printf("tag VT_NON, dev %d, %d\n", major(vp->v_rdev),
453 minor(vp->v_rdev));
454}
455
a4128336
KM
456/*
457 * Special device advisory byte-level locks.
458 */
459spec_advlock(vp, id, op, fl, flags)
460 struct vnode *vp;
461 caddr_t id;
462 int op;
463 struct flock *fl;
464 int flags;
465{
466
467 return (EOPNOTSUPP);
468}
469
a1d35437 470/*
7bbe72a5
KM
471 * Special device failed operation
472 */
473spec_ebadf()
474{
475
476 return (EBADF);
477}
478
479/*
480 * Special device bad operation
a1d35437 481 */
ad27f720 482spec_badop()
a1d35437
KM
483{
484
ad27f720 485 panic("spec_badop called");
59b0713e 486 /* NOTREACHED */
a1d35437
KM
487}
488
489/*
7bbe72a5 490 * Special device null operation
a1d35437 491 */
ad27f720 492spec_nullop()
a1d35437
KM
493{
494
495 return (0);
496}