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 | * |
93a50028 | 7 | * @(#)spec_vnops.c 7.56 (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 && | |
144 | (error = ufs_mountedon(bvp))) | |
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 | */ | |
406c9a0d | 169 | if (error = ufs_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; | |
195 | register int n, on; | |
196 | int error = 0; | |
a1d35437 | 197 | |
68a834b0 | 198 | #ifdef DIAGNOSTIC |
406c9a0d | 199 | if (uio->uio_rw != UIO_READ) |
43444338 | 200 | panic("spec_read mode"); |
406c9a0d | 201 | if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) |
68a834b0 KM |
202 | panic("spec_read proc"); |
203 | #endif | |
406c9a0d | 204 | if (uio->uio_resid == 0) |
43444338 | 205 | return (0); |
43444338 | 206 | |
406c9a0d | 207 | switch (vp->v_type) { |
43444338 KM |
208 | |
209 | case VCHR: | |
406c9a0d JH |
210 | VOP_UNLOCK(vp); |
211 | error = (*cdevsw[major(vp->v_rdev)].d_read) | |
212 | (vp->v_rdev, uio, ap->a_ioflag); | |
213 | VOP_LOCK(vp); | |
43444338 KM |
214 | return (error); |
215 | ||
216 | case VBLK: | |
406c9a0d | 217 | if (uio->uio_offset < 0) |
43444338 | 218 | return (EINVAL); |
7d4e5ac1 | 219 | bsize = BLKDEV_IOSIZE; |
406c9a0d | 220 | if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART, |
8429d022 | 221 | (caddr_t)&dpart, FREAD, p) == 0) { |
7d4e5ac1 KM |
222 | if (dpart.part->p_fstype == FS_BSDFFS && |
223 | dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) | |
224 | bsize = dpart.part->p_frag * | |
225 | dpart.part->p_fsize; | |
226 | } | |
227 | bscale = bsize / DEV_BSIZE; | |
228 | do { | |
406c9a0d JH |
229 | bn = (uio->uio_offset / DEV_BSIZE) &~ (bscale - 1); |
230 | on = uio->uio_offset % bsize; | |
48c77711 | 231 | n = min((unsigned)(bsize - on), uio->uio_resid); |
406c9a0d | 232 | if (vp->v_lastr + bscale == bn) { |
2e4bbf7a | 233 | nextbn = bn + bscale; |
406c9a0d | 234 | error = breadn(vp, bn, (int)bsize, &nextbn, |
2e4bbf7a KM |
235 | (int *)&bsize, 1, NOCRED, &bp); |
236 | } else | |
406c9a0d JH |
237 | error = bread(vp, bn, (int)bsize, NOCRED, &bp); |
238 | vp->v_lastr = bn; | |
48c77711 | 239 | n = min(n, bsize - bp->b_resid); |
7d4e5ac1 KM |
240 | if (error) { |
241 | brelse(bp); | |
242 | return (error); | |
243 | } | |
406c9a0d | 244 | error = uiomove(bp->b_un.b_addr + on, n, uio); |
7d4e5ac1 KM |
245 | if (n + on == bsize) |
246 | bp->b_flags |= B_AGE; | |
247 | brelse(bp); | |
406c9a0d | 248 | } while (error == 0 && uio->uio_resid > 0 && n != 0); |
7d4e5ac1 | 249 | return (error); |
43444338 KM |
250 | |
251 | default: | |
252 | panic("spec_read type"); | |
253 | } | |
254 | /* NOTREACHED */ | |
a1d35437 KM |
255 | } |
256 | ||
257 | /* | |
258 | * Vnode op for write | |
259 | */ | |
fe6cdffe | 260 | /* ARGSUSED */ |
e2afbd7b KM |
261 | spec_write(ap) |
262 | struct vop_write_args /* { | |
263 | struct vnode *a_vp; | |
264 | struct uio *a_uio; | |
265 | int a_ioflag; | |
266 | struct ucred *a_cred; | |
267 | } */ *ap; | |
a1d35437 | 268 | { |
406c9a0d JH |
269 | register struct vnode *vp = ap->a_vp; |
270 | register struct uio *uio = ap->a_uio; | |
271 | struct proc *p = uio->uio_procp; | |
7d4e5ac1 KM |
272 | struct buf *bp; |
273 | daddr_t bn; | |
274 | int bsize, blkmask; | |
275 | struct partinfo dpart; | |
9db58063 KM |
276 | register int n, on; |
277 | int error = 0; | |
a1d35437 | 278 | |
68a834b0 | 279 | #ifdef DIAGNOSTIC |
406c9a0d | 280 | if (uio->uio_rw != UIO_WRITE) |
43444338 | 281 | panic("spec_write mode"); |
406c9a0d | 282 | if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) |
68a834b0 KM |
283 | panic("spec_write proc"); |
284 | #endif | |
43444338 | 285 | |
406c9a0d | 286 | switch (vp->v_type) { |
43444338 KM |
287 | |
288 | case VCHR: | |
406c9a0d JH |
289 | VOP_UNLOCK(vp); |
290 | error = (*cdevsw[major(vp->v_rdev)].d_write) | |
291 | (vp->v_rdev, uio, ap->a_ioflag); | |
292 | VOP_LOCK(vp); | |
43444338 KM |
293 | return (error); |
294 | ||
295 | case VBLK: | |
406c9a0d | 296 | if (uio->uio_resid == 0) |
43444338 | 297 | return (0); |
406c9a0d | 298 | if (uio->uio_offset < 0) |
43444338 | 299 | return (EINVAL); |
7d4e5ac1 | 300 | bsize = BLKDEV_IOSIZE; |
406c9a0d | 301 | if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART, |
8429d022 | 302 | (caddr_t)&dpart, FREAD, p) == 0) { |
7d4e5ac1 KM |
303 | if (dpart.part->p_fstype == FS_BSDFFS && |
304 | dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) | |
305 | bsize = dpart.part->p_frag * | |
306 | dpart.part->p_fsize; | |
307 | } | |
308 | blkmask = (bsize / DEV_BSIZE) - 1; | |
309 | do { | |
406c9a0d JH |
310 | bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask; |
311 | on = uio->uio_offset % bsize; | |
48c77711 | 312 | n = min((unsigned)(bsize - on), uio->uio_resid); |
7d4e5ac1 | 313 | if (n == bsize) |
c74a8e8b | 314 | bp = getblk(vp, bn, bsize, 0, 0); |
7d4e5ac1 | 315 | else |
406c9a0d | 316 | error = bread(vp, bn, bsize, NOCRED, &bp); |
48c77711 | 317 | n = min(n, bsize - bp->b_resid); |
7d4e5ac1 KM |
318 | if (error) { |
319 | brelse(bp); | |
320 | return (error); | |
321 | } | |
406c9a0d | 322 | error = uiomove(bp->b_un.b_addr + on, n, uio); |
7d4e5ac1 KM |
323 | if (n + on == bsize) { |
324 | bp->b_flags |= B_AGE; | |
325 | bawrite(bp); | |
326 | } else | |
327 | bdwrite(bp); | |
406c9a0d | 328 | } while (error == 0 && uio->uio_resid > 0 && n != 0); |
7d4e5ac1 | 329 | return (error); |
43444338 KM |
330 | |
331 | default: | |
332 | panic("spec_write type"); | |
333 | } | |
334 | /* NOTREACHED */ | |
a1d35437 KM |
335 | } |
336 | ||
337 | /* | |
338 | * Device ioctl operation. | |
339 | */ | |
3d5d83ff | 340 | /* ARGSUSED */ |
e2afbd7b KM |
341 | spec_ioctl(ap) |
342 | struct vop_ioctl_args /* { | |
343 | struct vnode *a_vp; | |
344 | int a_command; | |
345 | caddr_t a_data; | |
346 | int a_fflag; | |
347 | struct ucred *a_cred; | |
348 | struct proc *a_p; | |
349 | } */ *ap; | |
a1d35437 | 350 | { |
e1b76915 | 351 | dev_t dev = ap->a_vp->v_rdev; |
a1d35437 | 352 | |
e1b76915 | 353 | switch (ap->a_vp->v_type) { |
a1d35437 KM |
354 | |
355 | case VCHR: | |
e1b76915 JH |
356 | return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, |
357 | ap->a_fflag, ap->a_p)); | |
a1d35437 KM |
358 | |
359 | case VBLK: | |
e1b76915 | 360 | if (ap->a_command == 0 && (int)ap->a_data == B_TAPE) |
b9a4d0ff KM |
361 | if (bdevsw[major(dev)].d_flags & B_TAPE) |
362 | return (0); | |
363 | else | |
364 | return (1); | |
e1b76915 JH |
365 | return ((*bdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, |
366 | ap->a_fflag, ap->a_p)); | |
a1d35437 KM |
367 | |
368 | default: | |
ad27f720 | 369 | panic("spec_ioctl"); |
a1d35437 KM |
370 | /* NOTREACHED */ |
371 | } | |
372 | } | |
373 | ||
3d5d83ff | 374 | /* ARGSUSED */ |
e2afbd7b KM |
375 | spec_select(ap) |
376 | struct vop_select_args /* { | |
377 | struct vnode *a_vp; | |
378 | int a_which; | |
379 | int a_fflags; | |
380 | struct ucred *a_cred; | |
381 | struct proc *a_p; | |
382 | } */ *ap; | |
a1d35437 | 383 | { |
a1d35437 KM |
384 | register dev_t dev; |
385 | ||
e1b76915 | 386 | switch (ap->a_vp->v_type) { |
a1d35437 KM |
387 | |
388 | default: | |
389 | return (1); /* XXX */ | |
390 | ||
391 | case VCHR: | |
e1b76915 JH |
392 | dev = ap->a_vp->v_rdev; |
393 | return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p); | |
a1d35437 KM |
394 | } |
395 | } | |
e2afbd7b KM |
396 | /* |
397 | * Synch buffers associated with a block device | |
398 | */ | |
399 | /* ARGSUSED */ | |
400 | int | |
401 | spec_fsync(ap) | |
402 | struct vop_fsync_args /* { | |
403 | struct vnode *a_vp; | |
404 | struct ucred *a_cred; | |
405 | int a_waitfor; | |
406 | struct proc *a_p; | |
407 | } */ *ap; | |
408 | { | |
409 | register struct vnode *vp = ap->a_vp; | |
410 | register struct buf *bp; | |
411 | struct buf *nbp; | |
a376c42f | 412 | int s; |
e2afbd7b KM |
413 | |
414 | if (vp->v_type == VCHR) | |
415 | return (0); | |
416 | /* | |
417 | * Flush all dirty buffers associated with a block device. | |
418 | */ | |
419 | loop: | |
420 | s = splbio(); | |
243083d2 KM |
421 | for (bp = vp->v_dirtyblkhd.le_next; bp; bp = nbp) { |
422 | nbp = bp->b_vnbufs.qe_next; | |
e2afbd7b KM |
423 | if ((bp->b_flags & B_BUSY)) |
424 | continue; | |
425 | if ((bp->b_flags & B_DELWRI) == 0) | |
426 | panic("spec_fsync: not dirty"); | |
427 | bremfree(bp); | |
428 | bp->b_flags |= B_BUSY; | |
429 | splx(s); | |
a376c42f | 430 | bawrite(bp); |
e2afbd7b KM |
431 | goto loop; |
432 | } | |
433 | if (ap->a_waitfor == MNT_WAIT) { | |
434 | while (vp->v_numoutput) { | |
435 | vp->v_flag |= VBWAIT; | |
436 | sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1); | |
437 | } | |
438 | #ifdef DIAGNOSTIC | |
243083d2 | 439 | if (vp->v_dirtyblkhd.le_next) { |
e2afbd7b KM |
440 | vprint("spec_fsync: dirty", vp); |
441 | goto loop; | |
442 | } | |
443 | #endif | |
444 | } | |
445 | splx(s); | |
a376c42f | 446 | return (0); |
e2afbd7b | 447 | } |
a1d35437 KM |
448 | |
449 | /* | |
450 | * Just call the device strategy routine | |
451 | */ | |
e2afbd7b KM |
452 | spec_strategy(ap) |
453 | struct vop_strategy_args /* { | |
454 | struct buf *a_bp; | |
455 | } */ *ap; | |
a1d35437 | 456 | { |
b9a4d0ff | 457 | |
e1b76915 | 458 | (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp); |
a1d35437 KM |
459 | return (0); |
460 | } | |
461 | ||
b9a4d0ff KM |
462 | /* |
463 | * This is a noop, simply returning what one has been given. | |
464 | */ | |
e2afbd7b KM |
465 | spec_bmap(ap) |
466 | struct vop_bmap_args /* { | |
467 | struct vnode *a_vp; | |
468 | daddr_t a_bn; | |
469 | struct vnode **a_vpp; | |
470 | daddr_t *a_bnp; | |
471 | } */ *ap; | |
b9a4d0ff KM |
472 | { |
473 | ||
e1b76915 JH |
474 | if (ap->a_vpp != NULL) |
475 | *ap->a_vpp = ap->a_vp; | |
476 | if (ap->a_bnp != NULL) | |
477 | *ap->a_bnp = ap->a_bn; | |
b9a4d0ff KM |
478 | return (0); |
479 | } | |
480 | ||
1c00bf64 KM |
481 | /* |
482 | * At the moment we do not do any locking. | |
483 | */ | |
ff4fb102 | 484 | /* ARGSUSED */ |
e2afbd7b KM |
485 | spec_lock(ap) |
486 | struct vop_lock_args /* { | |
487 | struct vnode *a_vp; | |
488 | } */ *ap; | |
a1d35437 | 489 | { |
a1d35437 | 490 | |
a1d35437 KM |
491 | return (0); |
492 | } | |
493 | ||
ff4fb102 | 494 | /* ARGSUSED */ |
e2afbd7b KM |
495 | spec_unlock(ap) |
496 | struct vop_unlock_args /* { | |
497 | struct vnode *a_vp; | |
498 | } */ *ap; | |
a1d35437 | 499 | { |
a1d35437 | 500 | |
a1d35437 KM |
501 | return (0); |
502 | } | |
503 | ||
a1d35437 KM |
504 | /* |
505 | * Device close routine | |
506 | */ | |
3d5d83ff | 507 | /* ARGSUSED */ |
e2afbd7b KM |
508 | spec_close(ap) |
509 | struct vop_close_args /* { | |
510 | struct vnode *a_vp; | |
511 | int a_fflag; | |
512 | struct ucred *a_cred; | |
513 | struct proc *a_p; | |
514 | } */ *ap; | |
a1d35437 | 515 | { |
406c9a0d JH |
516 | register struct vnode *vp = ap->a_vp; |
517 | dev_t dev = vp->v_rdev; | |
e15ce6a3 | 518 | int (*devclose) __P((dev_t, int, int, struct proc *)); |
e2afbd7b | 519 | int mode, error; |
a1d35437 | 520 | |
406c9a0d | 521 | switch (vp->v_type) { |
3d5d83ff KM |
522 | |
523 | case VCHR: | |
ed74b0e9 KM |
524 | /* |
525 | * Hack: a tty device that is a controlling terminal | |
526 | * has a reference from the session structure. | |
527 | * We cannot easily tell that a character device is | |
528 | * a controlling terminal, unless it is the closing | |
529 | * process' controlling terminal. In that case, | |
530 | * if the reference count is 2 (this last descriptor | |
531 | * plus the session), release the reference from the session. | |
532 | */ | |
93a50028 KM |
533 | if (vcount(vp) == 2 && ap->a_p && |
534 | vp == ap->a_p->p_session->s_ttyvp) { | |
ed74b0e9 KM |
535 | vrele(vp); |
536 | ap->a_p->p_session->s_ttyvp = NULL; | |
537 | } | |
f4b3ea62 KM |
538 | /* |
539 | * If the vnode is locked, then we are in the midst | |
540 | * of forcably closing the device, otherwise we only | |
541 | * close on last reference. | |
542 | */ | |
406c9a0d | 543 | if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) |
3d5d83ff | 544 | return (0); |
e15ce6a3 | 545 | devclose = cdevsw[major(dev)].d_close; |
1c00bf64 | 546 | mode = S_IFCHR; |
3d5d83ff KM |
547 | break; |
548 | ||
549 | case VBLK: | |
550 | /* | |
551 | * On last close of a block device (that isn't mounted) | |
552 | * we must invalidate any in core blocks, so that | |
553 | * we can, for instance, change floppy disks. | |
554 | */ | |
c74a8e8b | 555 | if (error = vinvalbuf(vp, 1, ap->a_cred, ap->a_p, 0, 0)) |
e2afbd7b | 556 | return (error); |
3d5d83ff | 557 | /* |
f4b3ea62 KM |
558 | * We do not want to really close the device if it |
559 | * is still in use unless we are trying to close it | |
560 | * forcibly. Since every use (buffer, vnode, swap, cmap) | |
3ab57521 KM |
561 | * holds a reference to the vnode, and because we mark |
562 | * any other vnodes that alias this device, when the | |
563 | * sum of the reference counts on all the aliased | |
564 | * vnodes descends to one, we are on last close. | |
3d5d83ff | 565 | */ |
406c9a0d | 566 | if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) |
3d5d83ff | 567 | return (0); |
e15ce6a3 | 568 | devclose = bdevsw[major(dev)].d_close; |
1c00bf64 | 569 | mode = S_IFBLK; |
3d5d83ff KM |
570 | break; |
571 | ||
572 | default: | |
ad27f720 | 573 | panic("spec_close: not special"); |
3d5d83ff KM |
574 | } |
575 | ||
e1b76915 | 576 | return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p)); |
a1d35437 KM |
577 | } |
578 | ||
b9a4d0ff KM |
579 | /* |
580 | * Print out the contents of a special device vnode. | |
581 | */ | |
e2afbd7b KM |
582 | spec_print(ap) |
583 | struct vop_print_args /* { | |
584 | struct vnode *a_vp; | |
585 | } */ *ap; | |
b9a4d0ff KM |
586 | { |
587 | ||
e1b76915 JH |
588 | printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev), |
589 | minor(ap->a_vp->v_rdev)); | |
b9a4d0ff KM |
590 | } |
591 | ||
b7c4b3cc KM |
592 | /* |
593 | * Return POSIX pathconf information applicable to special devices. | |
594 | */ | |
595 | spec_pathconf(ap) | |
596 | struct vop_pathconf_args /* { | |
597 | struct vnode *a_vp; | |
598 | int a_name; | |
599 | int *a_retval; | |
600 | } */ *ap; | |
601 | { | |
602 | ||
603 | switch (ap->a_name) { | |
604 | case _PC_LINK_MAX: | |
605 | *ap->a_retval = LINK_MAX; | |
606 | return (0); | |
607 | case _PC_MAX_CANON: | |
608 | *ap->a_retval = MAX_CANON; | |
609 | return (0); | |
610 | case _PC_MAX_INPUT: | |
611 | *ap->a_retval = MAX_INPUT; | |
612 | return (0); | |
613 | case _PC_PIPE_BUF: | |
614 | *ap->a_retval = PIPE_BUF; | |
615 | return (0); | |
616 | case _PC_CHOWN_RESTRICTED: | |
617 | *ap->a_retval = 1; | |
618 | return (0); | |
619 | case _PC_VDISABLE: | |
620 | *ap->a_retval = _POSIX_VDISABLE; | |
621 | return (0); | |
622 | default: | |
623 | return (EINVAL); | |
624 | } | |
625 | /* NOTREACHED */ | |
626 | } | |
627 | ||
a4128336 KM |
628 | /* |
629 | * Special device advisory byte-level locks. | |
630 | */ | |
68a834b0 | 631 | /* ARGSUSED */ |
e2afbd7b KM |
632 | spec_advlock(ap) |
633 | struct vop_advlock_args /* { | |
634 | struct vnode *a_vp; | |
635 | caddr_t a_id; | |
636 | int a_op; | |
637 | struct flock *a_fl; | |
638 | int a_flags; | |
639 | } */ *ap; | |
a4128336 KM |
640 | { |
641 | ||
642 | return (EOPNOTSUPP); | |
643 | } | |
644 | ||
a1d35437 | 645 | /* |
7bbe72a5 KM |
646 | * Special device failed operation |
647 | */ | |
648 | spec_ebadf() | |
649 | { | |
650 | ||
651 | return (EBADF); | |
652 | } | |
653 | ||
654 | /* | |
655 | * Special device bad operation | |
a1d35437 | 656 | */ |
ad27f720 | 657 | spec_badop() |
a1d35437 KM |
658 | { |
659 | ||
ad27f720 | 660 | panic("spec_badop called"); |
59b0713e | 661 | /* NOTREACHED */ |
a1d35437 | 662 | } |