Commit | Line | Data |
---|---|---|
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 */ |
26 | char devopn[] = "devopn"; | |
27 | char devio[] = "devio"; | |
28 | char devwait[] = "devwait"; | |
29 | char devin[] = "devin"; | |
30 | char devout[] = "devout"; | |
31 | char devioc[] = "devioc"; | |
32 | char devcls[] = "devcls"; | |
33 | ||
ad27f720 KM |
34 | int 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 | ||
51 | struct 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 | 90 | spec_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 | 106 | spec_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 | 139 | spec_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 | 216 | spec_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 | 293 | spec_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 | 322 | spec_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 | 343 | spec_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 | */ | |
354 | spec_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 | 372 | spec_lock(vp) |
a1d35437 KM |
373 | struct vnode *vp; |
374 | { | |
a1d35437 | 375 | |
a1d35437 KM |
376 | return (0); |
377 | } | |
378 | ||
ff4fb102 | 379 | /* ARGSUSED */ |
ad27f720 | 380 | spec_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 | 391 | spec_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 | */ | |
448 | spec_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 | */ | |
459 | spec_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 | */ | |
473 | spec_ebadf() | |
474 | { | |
475 | ||
476 | return (EBADF); | |
477 | } | |
478 | ||
479 | /* | |
480 | * Special device bad operation | |
a1d35437 | 481 | */ |
ad27f720 | 482 | spec_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 | 492 | spec_nullop() |
a1d35437 KM |
493 | { |
494 | ||
495 | return (0); | |
496 | } |