Commit | Line | Data |
---|---|---|
a1d35437 | 1 | /* |
1446b03c KB |
2 | * Copyright (c) 1989, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
a1d35437 | 4 | * |
dbf0c423 | 5 | * %sccs.include.redist.c% |
a1d35437 | 6 | * |
ede91379 | 7 | * @(#)spec_vnops.c 8.6 (Berkeley) %G% |
a1d35437 KM |
8 | */ |
9 | ||
17c64659 KB |
10 | #include <sys/param.h> |
11 | #include <sys/proc.h> | |
12 | #include <sys/systm.h> | |
13 | #include <sys/kernel.h> | |
14 | #include <sys/conf.h> | |
15 | #include <sys/buf.h> | |
16 | #include <sys/mount.h> | |
17 | #include <sys/namei.h> | |
18 | #include <sys/vnode.h> | |
17c64659 KB |
19 | #include <sys/stat.h> |
20 | #include <sys/errno.h> | |
21 | #include <sys/ioctl.h> | |
22 | #include <sys/file.h> | |
23 | #include <sys/disklabel.h> | |
a798c07b | 24 | #include <miscfs/specfs/specdev.h> |
a1d35437 | 25 | |
ccee3c59 MK |
26 | /* symbolic sleep message strings for devices */ |
27 | char devopn[] = "devopn"; | |
28 | char devio[] = "devio"; | |
29 | char devwait[] = "devwait"; | |
30 | char devin[] = "devin"; | |
31 | char devout[] = "devout"; | |
32 | char devioc[] = "devioc"; | |
33 | char devcls[] = "devcls"; | |
34 | ||
9342689a JH |
35 | int (**spec_vnodeop_p)(); |
36 | struct vnodeopv_entry_desc spec_vnodeop_entries[] = { | |
37 | { &vop_default_desc, vn_default_error }, | |
38 | { &vop_lookup_desc, spec_lookup }, /* lookup */ | |
39 | { &vop_create_desc, spec_create }, /* create */ | |
40 | { &vop_mknod_desc, spec_mknod }, /* mknod */ | |
b7c4b3cc | 41 | { &vop_open_desc, spec_open }, /* open */ |
9342689a JH |
42 | { &vop_close_desc, spec_close }, /* close */ |
43 | { &vop_access_desc, spec_access }, /* access */ | |
44 | { &vop_getattr_desc, spec_getattr }, /* getattr */ | |
45 | { &vop_setattr_desc, spec_setattr }, /* setattr */ | |
b7c4b3cc | 46 | { &vop_read_desc, spec_read }, /* read */ |
9342689a JH |
47 | { &vop_write_desc, spec_write }, /* write */ |
48 | { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ | |
49 | { &vop_select_desc, spec_select }, /* select */ | |
b7c4b3cc | 50 | { &vop_mmap_desc, spec_mmap }, /* mmap */ |
9342689a | 51 | { &vop_fsync_desc, spec_fsync }, /* fsync */ |
b7c4b3cc | 52 | { &vop_seek_desc, spec_seek }, /* seek */ |
9342689a | 53 | { &vop_remove_desc, spec_remove }, /* remove */ |
b7c4b3cc | 54 | { &vop_link_desc, spec_link }, /* link */ |
9342689a JH |
55 | { &vop_rename_desc, spec_rename }, /* rename */ |
56 | { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ | |
57 | { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ | |
58 | { &vop_symlink_desc, spec_symlink }, /* symlink */ | |
59 | { &vop_readdir_desc, spec_readdir }, /* readdir */ | |
60 | { &vop_readlink_desc, spec_readlink }, /* readlink */ | |
61 | { &vop_abortop_desc, spec_abortop }, /* abortop */ | |
62 | { &vop_inactive_desc, spec_inactive }, /* inactive */ | |
63 | { &vop_reclaim_desc, spec_reclaim }, /* reclaim */ | |
b7c4b3cc | 64 | { &vop_lock_desc, spec_lock }, /* lock */ |
9342689a | 65 | { &vop_unlock_desc, spec_unlock }, /* unlock */ |
b7c4b3cc | 66 | { &vop_bmap_desc, spec_bmap }, /* bmap */ |
9342689a JH |
67 | { &vop_strategy_desc, spec_strategy }, /* strategy */ |
68 | { &vop_print_desc, spec_print }, /* print */ | |
69 | { &vop_islocked_desc, spec_islocked }, /* islocked */ | |
b7c4b3cc | 70 | { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ |
9342689a JH |
71 | { &vop_advlock_desc, spec_advlock }, /* advlock */ |
72 | { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ | |
9342689a JH |
73 | { &vop_valloc_desc, spec_valloc }, /* valloc */ |
74 | { &vop_vfree_desc, spec_vfree }, /* vfree */ | |
75 | { &vop_truncate_desc, spec_truncate }, /* truncate */ | |
76 | { &vop_update_desc, spec_update }, /* update */ | |
77 | { &vop_bwrite_desc, spec_bwrite }, /* bwrite */ | |
78 | { (struct vnodeop_desc*)NULL, (int(*)())NULL } | |
a1d35437 | 79 | }; |
9342689a JH |
80 | struct vnodeopv_desc spec_vnodeop_opv_desc = |
81 | { &spec_vnodeop_p, spec_vnodeop_entries }; | |
a1d35437 | 82 | |
59b0713e KM |
83 | /* |
84 | * Trivial lookup routine that always fails. | |
85 | */ | |
6ee99c46 | 86 | int |
e2afbd7b KM |
87 | spec_lookup(ap) |
88 | struct vop_lookup_args /* { | |
89 | struct vnode *a_dvp; | |
90 | struct vnode **a_vpp; | |
91 | struct componentname *a_cnp; | |
92 | } */ *ap; | |
59b0713e KM |
93 | { |
94 | ||
e1b76915 | 95 | *ap->a_vpp = NULL; |
59b0713e KM |
96 | return (ENOTDIR); |
97 | } | |
98 | ||
a1d35437 | 99 | /* |
196b7053 | 100 | * Open a special file. |
a1d35437 | 101 | */ |
3d5d83ff | 102 | /* ARGSUSED */ |
e2afbd7b KM |
103 | spec_open(ap) |
104 | struct vop_open_args /* { | |
105 | struct vnode *a_vp; | |
106 | int a_mode; | |
107 | struct ucred *a_cred; | |
108 | struct proc *a_p; | |
109 | } */ *ap; | |
a1d35437 | 110 | { |
196b7053 KM |
111 | struct vnode *bvp, *vp = ap->a_vp; |
112 | dev_t bdev, dev = (dev_t)vp->v_rdev; | |
a1d35437 | 113 | register int maj = major(dev); |
5b2e9327 | 114 | int error; |
a1d35437 | 115 | |
196b7053 KM |
116 | /* |
117 | * Don't allow open if fs is mounted -nodev. | |
118 | */ | |
406c9a0d | 119 | if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) |
786053cd KM |
120 | return (ENXIO); |
121 | ||
406c9a0d | 122 | switch (vp->v_type) { |
a1d35437 KM |
123 | |
124 | case VCHR: | |
125 | if ((u_int)maj >= nchrdev) | |
126 | return (ENXIO); | |
196b7053 KM |
127 | if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) { |
128 | /* | |
129 | * When running in very secure mode, do not allow | |
130 | * opens for writing of any disk character devices. | |
131 | */ | |
132 | if (securelevel >= 2 && isdisk(dev, VCHR)) | |
133 | return (EPERM); | |
134 | /* | |
135 | * When running in secure mode, do not allow opens | |
136 | * for writing of /dev/mem, /dev/kmem, or character | |
137 | * devices whose corresponding block devices are | |
138 | * currently mounted. | |
139 | */ | |
140 | if (securelevel >= 1) { | |
141 | if ((bdev = chrtoblk(dev)) != NODEV && | |
142 | vfinddev(bdev, VBLK, &bvp) && | |
143 | bvp->v_usecount > 0 && | |
21c31ae2 | 144 | (error = vfs_mountedon(bvp))) |
196b7053 KM |
145 | return (error); |
146 | if (iskmemdev(dev)) | |
147 | return (EPERM); | |
148 | } | |
149 | } | |
406c9a0d | 150 | VOP_UNLOCK(vp); |
e1b76915 | 151 | error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p); |
406c9a0d | 152 | VOP_LOCK(vp); |
b3bf09c7 | 153 | return (error); |
a1d35437 KM |
154 | |
155 | case VBLK: | |
156 | if ((u_int)maj >= nblkdev) | |
157 | return (ENXIO); | |
196b7053 KM |
158 | /* |
159 | * When running in very secure mode, do not allow | |
160 | * opens for writing of any disk block devices. | |
161 | */ | |
162 | if (securelevel >= 2 && ap->a_cred != FSCRED && | |
163 | (ap->a_mode & FWRITE) && isdisk(dev, VBLK)) | |
164 | return (EPERM); | |
165 | /* | |
166 | * Do not allow opens of block devices that are | |
167 | * currently mounted. | |
168 | */ | |
21c31ae2 | 169 | if (error = vfs_mountedon(vp)) |
5b2e9327 | 170 | return (error); |
e1b76915 | 171 | return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_p)); |
a1d35437 KM |
172 | } |
173 | return (0); | |
174 | } | |
175 | ||
a1d35437 KM |
176 | /* |
177 | * Vnode op for read | |
178 | */ | |
fe6cdffe | 179 | /* ARGSUSED */ |
e2afbd7b KM |
180 | spec_read(ap) |
181 | struct vop_read_args /* { | |
182 | struct vnode *a_vp; | |
183 | struct uio *a_uio; | |
184 | int a_ioflag; | |
185 | struct ucred *a_cred; | |
186 | } */ *ap; | |
a1d35437 | 187 | { |
406c9a0d JH |
188 | register struct vnode *vp = ap->a_vp; |
189 | register struct uio *uio = ap->a_uio; | |
190 | struct proc *p = uio->uio_procp; | |
7d4e5ac1 | 191 | struct buf *bp; |
2e4bbf7a | 192 | daddr_t bn, nextbn; |
7d4e5ac1 KM |
193 | long bsize, bscale; |
194 | struct partinfo dpart; | |
714d32cb | 195 | int n, on, majordev, (*ioctl)(); |
7d4e5ac1 | 196 | int error = 0; |
714d32cb | 197 | dev_t dev; |
a1d35437 | 198 | |
68a834b0 | 199 | #ifdef DIAGNOSTIC |
406c9a0d | 200 | if (uio->uio_rw != UIO_READ) |
43444338 | 201 | panic("spec_read mode"); |
406c9a0d | 202 | if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) |
68a834b0 KM |
203 | panic("spec_read proc"); |
204 | #endif | |
406c9a0d | 205 | if (uio->uio_resid == 0) |
43444338 | 206 | return (0); |
43444338 | 207 | |
406c9a0d | 208 | switch (vp->v_type) { |
43444338 KM |
209 | |
210 | case VCHR: | |
406c9a0d JH |
211 | VOP_UNLOCK(vp); |
212 | error = (*cdevsw[major(vp->v_rdev)].d_read) | |
213 | (vp->v_rdev, uio, ap->a_ioflag); | |
214 | VOP_LOCK(vp); | |
43444338 KM |
215 | return (error); |
216 | ||
217 | case VBLK: | |
406c9a0d | 218 | if (uio->uio_offset < 0) |
43444338 | 219 | return (EINVAL); |
7d4e5ac1 | 220 | bsize = BLKDEV_IOSIZE; |
714d32cb KM |
221 | dev = vp->v_rdev; |
222 | if ((majordev = major(dev)) < nblkdev && | |
223 | (ioctl = bdevsw[majordev].d_ioctl) != NULL && | |
224 | (*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0 && | |
225 | dpart.part->p_fstype == FS_BSDFFS && | |
226 | dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) | |
227 | bsize = dpart.part->p_frag * dpart.part->p_fsize; | |
7d4e5ac1 KM |
228 | bscale = bsize / DEV_BSIZE; |
229 | do { | |
406c9a0d JH |
230 | bn = (uio->uio_offset / DEV_BSIZE) &~ (bscale - 1); |
231 | on = uio->uio_offset % bsize; | |
48c77711 | 232 | n = min((unsigned)(bsize - on), uio->uio_resid); |
406c9a0d | 233 | if (vp->v_lastr + bscale == bn) { |
2e4bbf7a | 234 | nextbn = bn + bscale; |
406c9a0d | 235 | error = breadn(vp, bn, (int)bsize, &nextbn, |
2e4bbf7a KM |
236 | (int *)&bsize, 1, NOCRED, &bp); |
237 | } else | |
406c9a0d JH |
238 | error = bread(vp, bn, (int)bsize, NOCRED, &bp); |
239 | vp->v_lastr = bn; | |
48c77711 | 240 | n = min(n, bsize - bp->b_resid); |
7d4e5ac1 KM |
241 | if (error) { |
242 | brelse(bp); | |
243 | return (error); | |
244 | } | |
6e36b147 | 245 | error = uiomove((char *)bp->b_data + on, n, uio); |
7d4e5ac1 KM |
246 | if (n + on == bsize) |
247 | bp->b_flags |= B_AGE; | |
248 | brelse(bp); | |
406c9a0d | 249 | } while (error == 0 && uio->uio_resid > 0 && n != 0); |
7d4e5ac1 | 250 | return (error); |
43444338 KM |
251 | |
252 | default: | |
253 | panic("spec_read type"); | |
254 | } | |
255 | /* NOTREACHED */ | |
a1d35437 KM |
256 | } |
257 | ||
258 | /* | |
259 | * Vnode op for write | |
260 | */ | |
fe6cdffe | 261 | /* ARGSUSED */ |
e2afbd7b KM |
262 | spec_write(ap) |
263 | struct vop_write_args /* { | |
264 | struct vnode *a_vp; | |
265 | struct uio *a_uio; | |
266 | int a_ioflag; | |
267 | struct ucred *a_cred; | |
268 | } */ *ap; | |
a1d35437 | 269 | { |
406c9a0d JH |
270 | register struct vnode *vp = ap->a_vp; |
271 | register struct uio *uio = ap->a_uio; | |
272 | struct proc *p = uio->uio_procp; | |
7d4e5ac1 KM |
273 | struct buf *bp; |
274 | daddr_t bn; | |
275 | int bsize, blkmask; | |
276 | struct partinfo dpart; | |
9db58063 KM |
277 | register int n, on; |
278 | int error = 0; | |
a1d35437 | 279 | |
68a834b0 | 280 | #ifdef DIAGNOSTIC |
406c9a0d | 281 | if (uio->uio_rw != UIO_WRITE) |
43444338 | 282 | panic("spec_write mode"); |
406c9a0d | 283 | if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) |
68a834b0 KM |
284 | panic("spec_write proc"); |
285 | #endif | |
43444338 | 286 | |
406c9a0d | 287 | switch (vp->v_type) { |
43444338 KM |
288 | |
289 | case VCHR: | |
406c9a0d JH |
290 | VOP_UNLOCK(vp); |
291 | error = (*cdevsw[major(vp->v_rdev)].d_write) | |
292 | (vp->v_rdev, uio, ap->a_ioflag); | |
293 | VOP_LOCK(vp); | |
43444338 KM |
294 | return (error); |
295 | ||
296 | case VBLK: | |
406c9a0d | 297 | if (uio->uio_resid == 0) |
43444338 | 298 | return (0); |
406c9a0d | 299 | if (uio->uio_offset < 0) |
43444338 | 300 | return (EINVAL); |
7d4e5ac1 | 301 | bsize = BLKDEV_IOSIZE; |
406c9a0d | 302 | if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART, |
8429d022 | 303 | (caddr_t)&dpart, FREAD, p) == 0) { |
7d4e5ac1 KM |
304 | if (dpart.part->p_fstype == FS_BSDFFS && |
305 | dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) | |
306 | bsize = dpart.part->p_frag * | |
307 | dpart.part->p_fsize; | |
308 | } | |
309 | blkmask = (bsize / DEV_BSIZE) - 1; | |
310 | do { | |
406c9a0d JH |
311 | bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask; |
312 | on = uio->uio_offset % bsize; | |
48c77711 | 313 | n = min((unsigned)(bsize - on), uio->uio_resid); |
7d4e5ac1 | 314 | if (n == bsize) |
c74a8e8b | 315 | bp = getblk(vp, bn, bsize, 0, 0); |
7d4e5ac1 | 316 | else |
406c9a0d | 317 | error = bread(vp, bn, bsize, NOCRED, &bp); |
48c77711 | 318 | n = min(n, bsize - bp->b_resid); |
7d4e5ac1 KM |
319 | if (error) { |
320 | brelse(bp); | |
321 | return (error); | |
322 | } | |
6e36b147 | 323 | error = uiomove((char *)bp->b_data + on, n, uio); |
7d4e5ac1 KM |
324 | if (n + on == bsize) { |
325 | bp->b_flags |= B_AGE; | |
326 | bawrite(bp); | |
327 | } else | |
328 | bdwrite(bp); | |
406c9a0d | 329 | } while (error == 0 && uio->uio_resid > 0 && n != 0); |
7d4e5ac1 | 330 | return (error); |
43444338 KM |
331 | |
332 | default: | |
333 | panic("spec_write type"); | |
334 | } | |
335 | /* NOTREACHED */ | |
a1d35437 KM |
336 | } |
337 | ||
338 | /* | |
339 | * Device ioctl operation. | |
340 | */ | |
3d5d83ff | 341 | /* ARGSUSED */ |
e2afbd7b KM |
342 | spec_ioctl(ap) |
343 | struct vop_ioctl_args /* { | |
344 | struct vnode *a_vp; | |
345 | int a_command; | |
346 | caddr_t a_data; | |
347 | int a_fflag; | |
348 | struct ucred *a_cred; | |
349 | struct proc *a_p; | |
350 | } */ *ap; | |
a1d35437 | 351 | { |
e1b76915 | 352 | dev_t dev = ap->a_vp->v_rdev; |
a1d35437 | 353 | |
e1b76915 | 354 | switch (ap->a_vp->v_type) { |
a1d35437 KM |
355 | |
356 | case VCHR: | |
e1b76915 JH |
357 | return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, |
358 | ap->a_fflag, ap->a_p)); | |
a1d35437 KM |
359 | |
360 | case VBLK: | |
e1b76915 | 361 | if (ap->a_command == 0 && (int)ap->a_data == B_TAPE) |
b9a4d0ff KM |
362 | if (bdevsw[major(dev)].d_flags & B_TAPE) |
363 | return (0); | |
364 | else | |
365 | return (1); | |
e1b76915 JH |
366 | return ((*bdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, |
367 | ap->a_fflag, ap->a_p)); | |
a1d35437 KM |
368 | |
369 | default: | |
ad27f720 | 370 | panic("spec_ioctl"); |
a1d35437 KM |
371 | /* NOTREACHED */ |
372 | } | |
373 | } | |
374 | ||
3d5d83ff | 375 | /* ARGSUSED */ |
e2afbd7b KM |
376 | spec_select(ap) |
377 | struct vop_select_args /* { | |
378 | struct vnode *a_vp; | |
379 | int a_which; | |
380 | int a_fflags; | |
381 | struct ucred *a_cred; | |
382 | struct proc *a_p; | |
383 | } */ *ap; | |
a1d35437 | 384 | { |
a1d35437 KM |
385 | register dev_t dev; |
386 | ||
e1b76915 | 387 | switch (ap->a_vp->v_type) { |
a1d35437 KM |
388 | |
389 | default: | |
390 | return (1); /* XXX */ | |
391 | ||
392 | case VCHR: | |
e1b76915 JH |
393 | dev = ap->a_vp->v_rdev; |
394 | return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p); | |
a1d35437 KM |
395 | } |
396 | } | |
e2afbd7b KM |
397 | /* |
398 | * Synch buffers associated with a block device | |
399 | */ | |
400 | /* ARGSUSED */ | |
401 | int | |
402 | spec_fsync(ap) | |
403 | struct vop_fsync_args /* { | |
404 | struct vnode *a_vp; | |
405 | struct ucred *a_cred; | |
406 | int a_waitfor; | |
407 | struct proc *a_p; | |
408 | } */ *ap; | |
409 | { | |
410 | register struct vnode *vp = ap->a_vp; | |
411 | register struct buf *bp; | |
412 | struct buf *nbp; | |
a376c42f | 413 | int s; |
e2afbd7b KM |
414 | |
415 | if (vp->v_type == VCHR) | |
416 | return (0); | |
417 | /* | |
418 | * Flush all dirty buffers associated with a block device. | |
419 | */ | |
420 | loop: | |
421 | s = splbio(); | |
736c7794 KM |
422 | for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { |
423 | nbp = bp->b_vnbufs.le_next; | |
e2afbd7b KM |
424 | if ((bp->b_flags & B_BUSY)) |
425 | continue; | |
426 | if ((bp->b_flags & B_DELWRI) == 0) | |
427 | panic("spec_fsync: not dirty"); | |
428 | bremfree(bp); | |
429 | bp->b_flags |= B_BUSY; | |
430 | splx(s); | |
a376c42f | 431 | bawrite(bp); |
e2afbd7b KM |
432 | goto loop; |
433 | } | |
434 | if (ap->a_waitfor == MNT_WAIT) { | |
435 | while (vp->v_numoutput) { | |
436 | vp->v_flag |= VBWAIT; | |
437 | sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); | |
438 | } | |
439 | #ifdef DIAGNOSTIC | |
736c7794 | 440 | if (vp->v_dirtyblkhd.lh_first) { |
e2afbd7b KM |
441 | vprint("spec_fsync: dirty", vp); |
442 | goto loop; | |
443 | } | |
444 | #endif | |
445 | } | |
446 | splx(s); | |
a376c42f | 447 | return (0); |
e2afbd7b | 448 | } |
a1d35437 KM |
449 | |
450 | /* | |
451 | * Just call the device strategy routine | |
452 | */ | |
e2afbd7b KM |
453 | spec_strategy(ap) |
454 | struct vop_strategy_args /* { | |
455 | struct buf *a_bp; | |
456 | } */ *ap; | |
a1d35437 | 457 | { |
b9a4d0ff | 458 | |
e1b76915 | 459 | (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp); |
a1d35437 KM |
460 | return (0); |
461 | } | |
462 | ||
b9a4d0ff KM |
463 | /* |
464 | * This is a noop, simply returning what one has been given. | |
465 | */ | |
e2afbd7b KM |
466 | spec_bmap(ap) |
467 | struct vop_bmap_args /* { | |
468 | struct vnode *a_vp; | |
469 | daddr_t a_bn; | |
470 | struct vnode **a_vpp; | |
471 | daddr_t *a_bnp; | |
472 | } */ *ap; | |
b9a4d0ff KM |
473 | { |
474 | ||
e1b76915 JH |
475 | if (ap->a_vpp != NULL) |
476 | *ap->a_vpp = ap->a_vp; | |
477 | if (ap->a_bnp != NULL) | |
478 | *ap->a_bnp = ap->a_bn; | |
b9a4d0ff KM |
479 | return (0); |
480 | } | |
481 | ||
1c00bf64 KM |
482 | /* |
483 | * At the moment we do not do any locking. | |
484 | */ | |
ff4fb102 | 485 | /* ARGSUSED */ |
e2afbd7b KM |
486 | spec_lock(ap) |
487 | struct vop_lock_args /* { | |
488 | struct vnode *a_vp; | |
489 | } */ *ap; | |
a1d35437 | 490 | { |
a1d35437 | 491 | |
a1d35437 KM |
492 | return (0); |
493 | } | |
494 | ||
ff4fb102 | 495 | /* ARGSUSED */ |
e2afbd7b KM |
496 | spec_unlock(ap) |
497 | struct vop_unlock_args /* { | |
498 | struct vnode *a_vp; | |
499 | } */ *ap; | |
a1d35437 | 500 | { |
a1d35437 | 501 | |
a1d35437 KM |
502 | return (0); |
503 | } | |
504 | ||
a1d35437 KM |
505 | /* |
506 | * Device close routine | |
507 | */ | |
3d5d83ff | 508 | /* ARGSUSED */ |
e2afbd7b KM |
509 | spec_close(ap) |
510 | struct vop_close_args /* { | |
511 | struct vnode *a_vp; | |
512 | int a_fflag; | |
513 | struct ucred *a_cred; | |
514 | struct proc *a_p; | |
515 | } */ *ap; | |
a1d35437 | 516 | { |
406c9a0d JH |
517 | register struct vnode *vp = ap->a_vp; |
518 | dev_t dev = vp->v_rdev; | |
e15ce6a3 | 519 | int (*devclose) __P((dev_t, int, int, struct proc *)); |
e2afbd7b | 520 | int mode, error; |
a1d35437 | 521 | |
406c9a0d | 522 | switch (vp->v_type) { |
3d5d83ff KM |
523 | |
524 | case VCHR: | |
ed74b0e9 KM |
525 | /* |
526 | * Hack: a tty device that is a controlling terminal | |
527 | * has a reference from the session structure. | |
528 | * We cannot easily tell that a character device is | |
529 | * a controlling terminal, unless it is the closing | |
530 | * process' controlling terminal. In that case, | |
531 | * if the reference count is 2 (this last descriptor | |
532 | * plus the session), release the reference from the session. | |
533 | */ | |
93a50028 KM |
534 | if (vcount(vp) == 2 && ap->a_p && |
535 | vp == ap->a_p->p_session->s_ttyvp) { | |
ed74b0e9 KM |
536 | vrele(vp); |
537 | ap->a_p->p_session->s_ttyvp = NULL; | |
538 | } | |
f4b3ea62 KM |
539 | /* |
540 | * If the vnode is locked, then we are in the midst | |
541 | * of forcably closing the device, otherwise we only | |
542 | * close on last reference. | |
543 | */ | |
406c9a0d | 544 | if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) |
3d5d83ff | 545 | return (0); |
e15ce6a3 | 546 | devclose = cdevsw[major(dev)].d_close; |
1c00bf64 | 547 | mode = S_IFCHR; |
3d5d83ff KM |
548 | break; |
549 | ||
550 | case VBLK: | |
551 | /* | |
552 | * On last close of a block device (that isn't mounted) | |
553 | * we must invalidate any in core blocks, so that | |
554 | * we can, for instance, change floppy disks. | |
555 | */ | |
ede91379 | 556 | if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0)) |
e2afbd7b | 557 | return (error); |
3d5d83ff | 558 | /* |
f4b3ea62 KM |
559 | * We do not want to really close the device if it |
560 | * is still in use unless we are trying to close it | |
561 | * forcibly. Since every use (buffer, vnode, swap, cmap) | |
3ab57521 KM |
562 | * holds a reference to the vnode, and because we mark |
563 | * any other vnodes that alias this device, when the | |
564 | * sum of the reference counts on all the aliased | |
565 | * vnodes descends to one, we are on last close. | |
3d5d83ff | 566 | */ |
406c9a0d | 567 | if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) |
3d5d83ff | 568 | return (0); |
e15ce6a3 | 569 | devclose = bdevsw[major(dev)].d_close; |
1c00bf64 | 570 | mode = S_IFBLK; |
3d5d83ff KM |
571 | break; |
572 | ||
573 | default: | |
ad27f720 | 574 | panic("spec_close: not special"); |
3d5d83ff KM |
575 | } |
576 | ||
e1b76915 | 577 | return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p)); |
a1d35437 KM |
578 | } |
579 | ||
b9a4d0ff KM |
580 | /* |
581 | * Print out the contents of a special device vnode. | |
582 | */ | |
e2afbd7b KM |
583 | spec_print(ap) |
584 | struct vop_print_args /* { | |
585 | struct vnode *a_vp; | |
586 | } */ *ap; | |
b9a4d0ff KM |
587 | { |
588 | ||
e1b76915 JH |
589 | printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev), |
590 | minor(ap->a_vp->v_rdev)); | |
b9a4d0ff KM |
591 | } |
592 | ||
b7c4b3cc KM |
593 | /* |
594 | * Return POSIX pathconf information applicable to special devices. | |
595 | */ | |
596 | spec_pathconf(ap) | |
597 | struct vop_pathconf_args /* { | |
598 | struct vnode *a_vp; | |
599 | int a_name; | |
600 | int *a_retval; | |
601 | } */ *ap; | |
602 | { | |
603 | ||
604 | switch (ap->a_name) { | |
605 | case _PC_LINK_MAX: | |
606 | *ap->a_retval = LINK_MAX; | |
607 | return (0); | |
608 | case _PC_MAX_CANON: | |
609 | *ap->a_retval = MAX_CANON; | |
610 | return (0); | |
611 | case _PC_MAX_INPUT: | |
612 | *ap->a_retval = MAX_INPUT; | |
613 | return (0); | |
614 | case _PC_PIPE_BUF: | |
615 | *ap->a_retval = PIPE_BUF; | |
616 | return (0); | |
617 | case _PC_CHOWN_RESTRICTED: | |
618 | *ap->a_retval = 1; | |
619 | return (0); | |
620 | case _PC_VDISABLE: | |
621 | *ap->a_retval = _POSIX_VDISABLE; | |
622 | return (0); | |
623 | default: | |
624 | return (EINVAL); | |
625 | } | |
626 | /* NOTREACHED */ | |
627 | } | |
628 | ||
a4128336 KM |
629 | /* |
630 | * Special device advisory byte-level locks. | |
631 | */ | |
68a834b0 | 632 | /* ARGSUSED */ |
e2afbd7b KM |
633 | spec_advlock(ap) |
634 | struct vop_advlock_args /* { | |
635 | struct vnode *a_vp; | |
636 | caddr_t a_id; | |
637 | int a_op; | |
638 | struct flock *a_fl; | |
639 | int a_flags; | |
640 | } */ *ap; | |
a4128336 KM |
641 | { |
642 | ||
643 | return (EOPNOTSUPP); | |
644 | } | |
645 | ||
a1d35437 | 646 | /* |
7bbe72a5 KM |
647 | * Special device failed operation |
648 | */ | |
649 | spec_ebadf() | |
650 | { | |
651 | ||
652 | return (EBADF); | |
653 | } | |
654 | ||
655 | /* | |
656 | * Special device bad operation | |
a1d35437 | 657 | */ |
ad27f720 | 658 | spec_badop() |
a1d35437 KM |
659 | { |
660 | ||
ad27f720 | 661 | panic("spec_badop called"); |
59b0713e | 662 | /* NOTREACHED */ |
a1d35437 | 663 | } |