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