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 | * |
406c9a0d | 7 | * @(#)spec_vnops.c 7.45 (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> | |
19 | #include <sys/specdev.h> | |
20 | #include <sys/stat.h> | |
21 | #include <sys/errno.h> | |
22 | #include <sys/ioctl.h> | |
23 | #include <sys/file.h> | |
24 | #include <sys/disklabel.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 */ | |
41 | { &vop_open_desc, spec_open }, /* open */ | |
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 */ | |
46 | { &vop_read_desc, spec_read }, /* read */ | |
47 | { &vop_write_desc, spec_write }, /* write */ | |
48 | { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ | |
49 | { &vop_select_desc, spec_select }, /* select */ | |
50 | { &vop_mmap_desc, spec_mmap }, /* mmap */ | |
51 | { &vop_fsync_desc, spec_fsync }, /* fsync */ | |
52 | { &vop_seek_desc, spec_seek }, /* seek */ | |
53 | { &vop_remove_desc, spec_remove }, /* remove */ | |
54 | { &vop_link_desc, spec_link }, /* link */ | |
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 */ | |
64 | { &vop_lock_desc, spec_lock }, /* lock */ | |
65 | { &vop_unlock_desc, spec_unlock }, /* unlock */ | |
66 | { &vop_bmap_desc, spec_bmap }, /* bmap */ | |
67 | { &vop_strategy_desc, spec_strategy }, /* strategy */ | |
68 | { &vop_print_desc, spec_print }, /* print */ | |
69 | { &vop_islocked_desc, spec_islocked }, /* islocked */ | |
70 | { &vop_advlock_desc, spec_advlock }, /* advlock */ | |
71 | { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */ | |
72 | { &vop_vget_desc, spec_vget }, /* vget */ | |
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 |
9342689a JH |
87 | spec_lookup (ap) |
88 | struct vop_lookup_args *ap; | |
59b0713e KM |
89 | { |
90 | ||
e1b76915 | 91 | *ap->a_vpp = NULL; |
59b0713e KM |
92 | return (ENOTDIR); |
93 | } | |
94 | ||
a1d35437 | 95 | /* |
e15ce6a3 MK |
96 | * Open a special file: Don't allow open if fs is mounted -nodev, |
97 | * and don't allow opens of block devices that are currently mounted. | |
98 | * Otherwise, call device driver open function. | |
a1d35437 | 99 | */ |
3d5d83ff | 100 | /* ARGSUSED */ |
9342689a JH |
101 | spec_open (ap) |
102 | struct vop_open_args *ap; | |
a1d35437 | 103 | { |
9342689a JH |
104 | USES_VOP_LOCK; |
105 | USES_VOP_UNLOCK; | |
406c9a0d JH |
106 | register struct vnode *vp = ap->a_vp; |
107 | dev_t dev = (dev_t)vp->v_rdev; | |
a1d35437 | 108 | register int maj = major(dev); |
5b2e9327 | 109 | int error; |
a1d35437 | 110 | |
406c9a0d | 111 | if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) |
786053cd KM |
112 | return (ENXIO); |
113 | ||
406c9a0d | 114 | switch (vp->v_type) { |
a1d35437 KM |
115 | |
116 | case VCHR: | |
117 | if ((u_int)maj >= nchrdev) | |
118 | return (ENXIO); | |
406c9a0d | 119 | VOP_UNLOCK(vp); |
e1b76915 | 120 | error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p); |
406c9a0d | 121 | VOP_LOCK(vp); |
b3bf09c7 | 122 | return (error); |
a1d35437 KM |
123 | |
124 | case VBLK: | |
125 | if ((u_int)maj >= nblkdev) | |
126 | return (ENXIO); | |
406c9a0d | 127 | if (error = ufs_mountedon(vp)) |
5b2e9327 | 128 | return (error); |
e1b76915 | 129 | return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_p)); |
a1d35437 KM |
130 | } |
131 | return (0); | |
132 | } | |
133 | ||
a1d35437 KM |
134 | /* |
135 | * Vnode op for read | |
136 | */ | |
fe6cdffe | 137 | /* ARGSUSED */ |
9342689a JH |
138 | spec_read (ap) |
139 | struct vop_read_args *ap; | |
a1d35437 | 140 | { |
9342689a JH |
141 | USES_VOP_LOCK; |
142 | USES_VOP_UNLOCK; | |
406c9a0d JH |
143 | register struct vnode *vp = ap->a_vp; |
144 | register struct uio *uio = ap->a_uio; | |
145 | struct proc *p = uio->uio_procp; | |
7d4e5ac1 | 146 | struct buf *bp; |
2e4bbf7a | 147 | daddr_t bn, nextbn; |
7d4e5ac1 KM |
148 | long bsize, bscale; |
149 | struct partinfo dpart; | |
150 | register int n, on; | |
151 | int error = 0; | |
a1d35437 | 152 | |
68a834b0 | 153 | #ifdef DIAGNOSTIC |
406c9a0d | 154 | if (uio->uio_rw != UIO_READ) |
43444338 | 155 | panic("spec_read mode"); |
406c9a0d | 156 | if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) |
68a834b0 KM |
157 | panic("spec_read proc"); |
158 | #endif | |
406c9a0d | 159 | if (uio->uio_resid == 0) |
43444338 | 160 | return (0); |
43444338 | 161 | |
406c9a0d | 162 | switch (vp->v_type) { |
43444338 KM |
163 | |
164 | case VCHR: | |
406c9a0d JH |
165 | VOP_UNLOCK(vp); |
166 | error = (*cdevsw[major(vp->v_rdev)].d_read) | |
167 | (vp->v_rdev, uio, ap->a_ioflag); | |
168 | VOP_LOCK(vp); | |
43444338 KM |
169 | return (error); |
170 | ||
171 | case VBLK: | |
406c9a0d | 172 | if (uio->uio_offset < 0) |
43444338 | 173 | return (EINVAL); |
7d4e5ac1 | 174 | bsize = BLKDEV_IOSIZE; |
406c9a0d | 175 | if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART, |
8429d022 | 176 | (caddr_t)&dpart, FREAD, p) == 0) { |
7d4e5ac1 KM |
177 | if (dpart.part->p_fstype == FS_BSDFFS && |
178 | dpart.part->p_frag != 0 && dpart.part->p_fsize != 0) | |
179 | bsize = dpart.part->p_frag * | |
180 | dpart.part->p_fsize; | |
181 | } | |
182 | bscale = bsize / DEV_BSIZE; | |
183 | do { | |
406c9a0d JH |
184 | bn = (uio->uio_offset / DEV_BSIZE) &~ (bscale - 1); |
185 | on = uio->uio_offset % bsize; | |
186 | n = MIN((unsigned)(bsize - on), uio->uio_resid); | |
187 | if (vp->v_lastr + bscale == bn) { | |
2e4bbf7a | 188 | nextbn = bn + bscale; |
406c9a0d | 189 | error = breadn(vp, bn, (int)bsize, &nextbn, |
2e4bbf7a KM |
190 | (int *)&bsize, 1, NOCRED, &bp); |
191 | } else | |
406c9a0d JH |
192 | error = bread(vp, bn, (int)bsize, NOCRED, &bp); |
193 | vp->v_lastr = bn; | |
7d4e5ac1 KM |
194 | n = MIN(n, bsize - bp->b_resid); |
195 | if (error) { | |
196 | brelse(bp); | |
197 | return (error); | |
198 | } | |
406c9a0d | 199 | error = uiomove(bp->b_un.b_addr + on, n, uio); |
7d4e5ac1 KM |
200 | if (n + on == bsize) |
201 | bp->b_flags |= B_AGE; | |
202 | brelse(bp); | |
406c9a0d | 203 | } while (error == 0 && uio->uio_resid > 0 && n != 0); |
7d4e5ac1 | 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 */ |
9342689a JH |
216 | spec_write (ap) |
217 | struct vop_write_args *ap; | |
a1d35437 | 218 | { |
9342689a JH |
219 | USES_VOP_LOCK; |
220 | USES_VOP_UNLOCK; | |
406c9a0d JH |
221 | register struct vnode *vp = ap->a_vp; |
222 | register struct uio *uio = ap->a_uio; | |
223 | struct proc *p = uio->uio_procp; | |
7d4e5ac1 KM |
224 | struct buf *bp; |
225 | daddr_t bn; | |
226 | int bsize, blkmask; | |
227 | struct partinfo dpart; | |
9db58063 KM |
228 | register int n, on; |
229 | int error = 0; | |
a1d35437 | 230 | |
68a834b0 | 231 | #ifdef DIAGNOSTIC |
406c9a0d | 232 | if (uio->uio_rw != UIO_WRITE) |
43444338 | 233 | panic("spec_write mode"); |
406c9a0d | 234 | if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc) |
68a834b0 KM |
235 | panic("spec_write proc"); |
236 | #endif | |
43444338 | 237 | |
406c9a0d | 238 | switch (vp->v_type) { |
43444338 KM |
239 | |
240 | case VCHR: | |
406c9a0d JH |
241 | VOP_UNLOCK(vp); |
242 | error = (*cdevsw[major(vp->v_rdev)].d_write) | |
243 | (vp->v_rdev, uio, ap->a_ioflag); | |
244 | VOP_LOCK(vp); | |
43444338 KM |
245 | return (error); |
246 | ||
247 | case VBLK: | |
406c9a0d | 248 | if (uio->uio_resid == 0) |
43444338 | 249 | return (0); |
406c9a0d | 250 | if (uio->uio_offset < 0) |
43444338 | 251 | return (EINVAL); |
7d4e5ac1 | 252 | bsize = BLKDEV_IOSIZE; |
406c9a0d | 253 | if ((*bdevsw[major(vp->v_rdev)].d_ioctl)(vp->v_rdev, DIOCGPART, |
8429d022 | 254 | (caddr_t)&dpart, FREAD, p) == 0) { |
7d4e5ac1 KM |
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 { | |
406c9a0d JH |
262 | bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask; |
263 | on = uio->uio_offset % bsize; | |
264 | n = MIN((unsigned)(bsize - on), uio->uio_resid); | |
7d4e5ac1 | 265 | if (n == bsize) |
406c9a0d | 266 | bp = getblk(vp, bn, bsize); |
7d4e5ac1 | 267 | else |
406c9a0d | 268 | error = bread(vp, bn, bsize, NOCRED, &bp); |
7d4e5ac1 KM |
269 | n = MIN(n, bsize - bp->b_resid); |
270 | if (error) { | |
271 | brelse(bp); | |
272 | return (error); | |
273 | } | |
406c9a0d | 274 | error = uiomove(bp->b_un.b_addr + on, n, uio); |
7d4e5ac1 KM |
275 | if (n + on == bsize) { |
276 | bp->b_flags |= B_AGE; | |
277 | bawrite(bp); | |
278 | } else | |
279 | bdwrite(bp); | |
406c9a0d | 280 | } while (error == 0 && uio->uio_resid > 0 && n != 0); |
7d4e5ac1 | 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 */ |
9342689a JH |
293 | spec_ioctl (ap) |
294 | struct vop_ioctl_args *ap; | |
a1d35437 | 295 | { |
e1b76915 | 296 | dev_t dev = ap->a_vp->v_rdev; |
a1d35437 | 297 | |
e1b76915 | 298 | switch (ap->a_vp->v_type) { |
a1d35437 KM |
299 | |
300 | case VCHR: | |
e1b76915 JH |
301 | return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, |
302 | ap->a_fflag, ap->a_p)); | |
a1d35437 KM |
303 | |
304 | case VBLK: | |
e1b76915 | 305 | if (ap->a_command == 0 && (int)ap->a_data == B_TAPE) |
b9a4d0ff KM |
306 | if (bdevsw[major(dev)].d_flags & B_TAPE) |
307 | return (0); | |
308 | else | |
309 | return (1); | |
e1b76915 JH |
310 | return ((*bdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data, |
311 | ap->a_fflag, ap->a_p)); | |
a1d35437 KM |
312 | |
313 | default: | |
ad27f720 | 314 | panic("spec_ioctl"); |
a1d35437 KM |
315 | /* NOTREACHED */ |
316 | } | |
317 | } | |
318 | ||
3d5d83ff | 319 | /* ARGSUSED */ |
9342689a JH |
320 | spec_select (ap) |
321 | struct vop_select_args *ap; | |
a1d35437 | 322 | { |
a1d35437 KM |
323 | register dev_t dev; |
324 | ||
e1b76915 | 325 | switch (ap->a_vp->v_type) { |
a1d35437 KM |
326 | |
327 | default: | |
328 | return (1); /* XXX */ | |
329 | ||
330 | case VCHR: | |
e1b76915 JH |
331 | dev = ap->a_vp->v_rdev; |
332 | return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p); | |
a1d35437 KM |
333 | } |
334 | } | |
335 | ||
336 | /* | |
337 | * Just call the device strategy routine | |
338 | */ | |
9342689a JH |
339 | spec_strategy (ap) |
340 | struct vop_strategy_args *ap; | |
a1d35437 | 341 | { |
b9a4d0ff | 342 | |
e1b76915 | 343 | (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp); |
a1d35437 KM |
344 | return (0); |
345 | } | |
346 | ||
b9a4d0ff KM |
347 | /* |
348 | * This is a noop, simply returning what one has been given. | |
349 | */ | |
9342689a JH |
350 | spec_bmap (ap) |
351 | struct vop_bmap_args *ap; | |
b9a4d0ff KM |
352 | { |
353 | ||
e1b76915 JH |
354 | if (ap->a_vpp != NULL) |
355 | *ap->a_vpp = ap->a_vp; | |
356 | if (ap->a_bnp != NULL) | |
357 | *ap->a_bnp = ap->a_bn; | |
b9a4d0ff KM |
358 | return (0); |
359 | } | |
360 | ||
1c00bf64 KM |
361 | /* |
362 | * At the moment we do not do any locking. | |
363 | */ | |
ff4fb102 | 364 | /* ARGSUSED */ |
9342689a JH |
365 | spec_lock (ap) |
366 | struct vop_lock_args *ap; | |
a1d35437 | 367 | { |
a1d35437 | 368 | |
a1d35437 KM |
369 | return (0); |
370 | } | |
371 | ||
ff4fb102 | 372 | /* ARGSUSED */ |
9342689a JH |
373 | spec_unlock (ap) |
374 | struct vop_unlock_args *ap; | |
a1d35437 | 375 | { |
a1d35437 | 376 | |
a1d35437 KM |
377 | return (0); |
378 | } | |
379 | ||
a1d35437 KM |
380 | /* |
381 | * Device close routine | |
382 | */ | |
3d5d83ff | 383 | /* ARGSUSED */ |
9342689a JH |
384 | spec_close (ap) |
385 | struct vop_close_args *ap; | |
a1d35437 | 386 | { |
406c9a0d JH |
387 | register struct vnode *vp = ap->a_vp; |
388 | dev_t dev = vp->v_rdev; | |
e15ce6a3 | 389 | int (*devclose) __P((dev_t, int, int, struct proc *)); |
ccee3c59 | 390 | int mode; |
a1d35437 | 391 | |
406c9a0d | 392 | switch (vp->v_type) { |
3d5d83ff KM |
393 | |
394 | case VCHR: | |
f4b3ea62 KM |
395 | /* |
396 | * If the vnode is locked, then we are in the midst | |
397 | * of forcably closing the device, otherwise we only | |
398 | * close on last reference. | |
399 | */ | |
406c9a0d | 400 | if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) |
3d5d83ff | 401 | return (0); |
e15ce6a3 | 402 | devclose = cdevsw[major(dev)].d_close; |
1c00bf64 | 403 | mode = S_IFCHR; |
3d5d83ff KM |
404 | break; |
405 | ||
406 | case VBLK: | |
407 | /* | |
408 | * On last close of a block device (that isn't mounted) | |
409 | * we must invalidate any in core blocks, so that | |
410 | * we can, for instance, change floppy disks. | |
411 | */ | |
406c9a0d JH |
412 | vflushbuf(vp, 0); |
413 | if (vinvalbuf(vp, 1)) | |
4a29fa35 | 414 | return (0); |
3d5d83ff | 415 | /* |
f4b3ea62 KM |
416 | * We do not want to really close the device if it |
417 | * is still in use unless we are trying to close it | |
418 | * forcibly. Since every use (buffer, vnode, swap, cmap) | |
3ab57521 KM |
419 | * holds a reference to the vnode, and because we mark |
420 | * any other vnodes that alias this device, when the | |
421 | * sum of the reference counts on all the aliased | |
422 | * vnodes descends to one, we are on last close. | |
3d5d83ff | 423 | */ |
406c9a0d | 424 | if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0) |
3d5d83ff | 425 | return (0); |
e15ce6a3 | 426 | devclose = bdevsw[major(dev)].d_close; |
1c00bf64 | 427 | mode = S_IFBLK; |
3d5d83ff KM |
428 | break; |
429 | ||
430 | default: | |
ad27f720 | 431 | panic("spec_close: not special"); |
3d5d83ff KM |
432 | } |
433 | ||
e1b76915 | 434 | return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p)); |
a1d35437 KM |
435 | } |
436 | ||
b9a4d0ff KM |
437 | /* |
438 | * Print out the contents of a special device vnode. | |
439 | */ | |
9342689a JH |
440 | spec_print (ap) |
441 | struct vop_print_args *ap; | |
b9a4d0ff KM |
442 | { |
443 | ||
e1b76915 JH |
444 | printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev), |
445 | minor(ap->a_vp->v_rdev)); | |
b9a4d0ff KM |
446 | } |
447 | ||
a4128336 KM |
448 | /* |
449 | * Special device advisory byte-level locks. | |
450 | */ | |
68a834b0 | 451 | /* ARGSUSED */ |
9342689a JH |
452 | spec_advlock (ap) |
453 | struct vop_advlock_args *ap; | |
a4128336 KM |
454 | { |
455 | ||
456 | return (EOPNOTSUPP); | |
457 | } | |
458 | ||
a1d35437 | 459 | /* |
7bbe72a5 KM |
460 | * Special device failed operation |
461 | */ | |
462 | spec_ebadf() | |
463 | { | |
464 | ||
465 | return (EBADF); | |
466 | } | |
467 | ||
468 | /* | |
469 | * Special device bad operation | |
a1d35437 | 470 | */ |
ad27f720 | 471 | spec_badop() |
a1d35437 KM |
472 | { |
473 | ||
ad27f720 | 474 | panic("spec_badop called"); |
59b0713e | 475 | /* NOTREACHED */ |
a1d35437 | 476 | } |