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