]> git.saurik.com Git - apple/xnu.git/blame - bsd/miscfs/volfs/volfs_vnops.c
xnu-201.tar.gz
[apple/xnu.git] / bsd / miscfs / volfs / volfs_vnops.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright (c) 1998-1999 Apple Computer, Inc. All Rights Reserved.
24 *
25 * Modification History:
26 *
27 * 2/10/2000 Clark Warner Added copyfile
28 * 5/24/1999 Don Brady Fixed security hole in get_fsvnode.
29 * 11/18/1998 Don Brady Special case 2 to mean the root of a file system.
30 * 9/28/1998 Umesh Vaishampayan Use the default vnode ops. Cleanup
31 * header includes.
32 * 11/12/1998 Scott Roberts validfsnode only checks to see if the volfs mount flag is set
33 * 8/5/1998 Don Brady fix validfsnode logic to handle a "bad" VFS_GET
34 * 7/5/1998 Don Brady In volfs_reclaim set vp->v_data to NULL after private data is free (VFS expects a NULL).
35 * 4/5/1998 Don Brady Changed lockstatus calls to VOP_ISLOCKED (radar #2231108);
36 * 3/25/1998 Pat Dirks Added include for sys/attr.h, which is no longer included indirectly.
37 */
38
39#include <mach/mach_types.h>
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/resourcevar.h>
44#include <sys/kernel.h>
45#include <sys/file.h>
46#include <sys/stat.h>
47#include <sys/buf.h>
48#include <sys/proc.h>
49#include <sys/conf.h>
50#include <sys/mount.h>
51#include <sys/vnode.h>
52#include <sys/malloc.h>
53#include <sys/dirent.h>
54#include <sys/namei.h>
55#include <sys/attr.h>
56
57#include <sys/vm.h>
58#include <sys/errno.h>
59#include <vfs/vfs_support.h>
60
61#include "volfs.h"
62
63/*
64 * volfs acts as a bridge between the requirements of the MacOS API and the Unix API.
65 * MacOS applications describe files by a <Volume ID><Directory ID><File Name> triple.
66 * The Unix API describes files by pathname. Volfs is a virtual file system that sits over
67 * the HFS VFS interface and allows files to be described by a <Volume ID>/<Directory ID>/<File Name>
68 * pathname.
69 *
70 * The root of the volfs filesystem consists of directories named the volume ID's of all the
71 * currently mounted filesystems which support the VFS vget() routine. Each of those directories
72 * supports the lookup by file ID of all files and directories within the filesystem. When a
73 * file or directory is resolved its vnode from that filesystem rather than a volfs vnode is returned
74 * allowing immediate access to the target file or directory.
75 *
76 * Readdir on the root of the volfs filesystem returns the list of available file systems. Readdir
77 * on a filesystem node, however, returns only . and .. since it is not practical to list all
78 * of the file ID's in a timely fashion and furthermore VFS does not provide a mechanism for
79 * enumerating all of the file id's.
80 *
81 * Volume ID's are taken from the low 32 bits of the f_fsid field, formatted as a base 10 ASCII
82 * string with no leading zeros (volume ID 1 is represented as "1").
83 *
84 * File ID's are created in same manner, with their 32 bits formatted as a base 10 ASCII
85 * string with no leading zeros.
86 *
87 * Volfs does create a security hole since it is possible to bypass directory permissions higher
88 * in the namespace tree. This security hole is about the same as the one created by NFS which uses
89 * a similar mechanism.
90 */
91
92#define VOPFUNC int (*)(void *)
93
94/* Global vfs data structures for volfs. */
95int (**volfs_vnodeop_p) (void *);
96struct vnodeopv_entry_desc volfs_vnodeop_entries[] = {
97 {&vop_default_desc, (VOPFUNC)vn_default_error},
98 {&vop_strategy_desc, (VOPFUNC)err_strategy}, /* strategy */
99 {&vop_bwrite_desc, (VOPFUNC)err_bwrite}, /* bwrite */
100 {&vop_lookup_desc, (VOPFUNC)volfs_lookup}, /* lookup */
101 {&vop_create_desc, (VOPFUNC)err_create}, /* create */
102 {&vop_whiteout_desc, (VOPFUNC)err_whiteout}, /* whiteout */
103 {&vop_mknod_desc, (VOPFUNC)err_mknod}, /* mknod */
104 {&vop_mkcomplex_desc, (VOPFUNC)err_mkcomplex}, /* mkcomplex */
105 {&vop_open_desc, (VOPFUNC)nop_open}, /* open */
106 {&vop_close_desc, (VOPFUNC)nop_close}, /* close */
107 {&vop_access_desc, (VOPFUNC)volfs_access}, /* access */
108 {&vop_getattr_desc, (VOPFUNC)volfs_getattr}, /* getattr */
109 {&vop_setattr_desc, (VOPFUNC)err_setattr}, /* setattr */
110 {&vop_getattrlist_desc, (VOPFUNC)err_getattrlist}, /* getattrlist */
111 {&vop_setattrlist_desc, (VOPFUNC)err_setattrlist}, /* setattrlist */
112 {&vop_read_desc, (VOPFUNC)err_read}, /* read */
113 {&vop_write_desc, (VOPFUNC)err_write}, /* write */
114 {&vop_lease_desc, (VOPFUNC)err_lease}, /* lease */
115 {&vop_ioctl_desc, (VOPFUNC)err_ioctl}, /* ioctl */
116 {&vop_select_desc, (VOPFUNC)volfs_select}, /* select */
117 {&vop_exchange_desc, (VOPFUNC)err_exchange}, /* exchange */
118 {&vop_revoke_desc, (VOPFUNC)nop_revoke}, /* revoke */
119 {&vop_mmap_desc, (VOPFUNC)err_mmap}, /* mmap */
120 {&vop_fsync_desc, (VOPFUNC)err_fsync}, /* fsync */
121 {&vop_seek_desc, (VOPFUNC)nop_seek}, /* seek */
122 {&vop_remove_desc, (VOPFUNC)err_remove}, /* remove */
123 {&vop_link_desc, (VOPFUNC)err_link}, /* link */
124 {&vop_rename_desc, (VOPFUNC)err_rename}, /* rename */
125 {&vop_mkdir_desc, (VOPFUNC)err_mkdir}, /* mkdir */
126 {&vop_rmdir_desc, (VOPFUNC)volfs_rmdir}, /* rmdir */
127 {&vop_symlink_desc, (VOPFUNC)err_symlink}, /* symlink */
128 {&vop_readdir_desc, (VOPFUNC)volfs_readdir}, /* readdir */
129 {&vop_readdirattr_desc, (VOPFUNC)err_readdirattr}, /* readdirattr */
130 {&vop_readlink_desc, (VOPFUNC)err_readlink}, /* readlink */
131 {&vop_abortop_desc, (VOPFUNC)err_abortop}, /* abortop */
132 {&vop_inactive_desc, (VOPFUNC)err_inactive}, /* inactive */
133 {&vop_reclaim_desc, (VOPFUNC)volfs_reclaim}, /* reclaim */
134 {&vop_lock_desc, (VOPFUNC)volfs_lock}, /* lock */
135 {&vop_unlock_desc, (VOPFUNC)volfs_unlock}, /* unlock */
136 {&vop_bmap_desc, (VOPFUNC)err_bmap}, /* bmap */
137 {&vop_print_desc, (VOPFUNC)err_print}, /* print */
138 {&vop_islocked_desc, (VOPFUNC)volfs_islocked}, /* islocked */
139 {&vop_pathconf_desc, (VOPFUNC)volfs_pathconf}, /* pathconf */
140 {&vop_advlock_desc, (VOPFUNC)err_advlock}, /* advlock */
141 {&vop_blkatoff_desc, (VOPFUNC)err_blkatoff}, /* blkatoff */
142 {&vop_valloc_desc, (VOPFUNC)err_valloc}, /* valloc */
143 {&vop_reallocblks_desc, (VOPFUNC)err_reallocblks}, /* reallocblks */
144 {&vop_vfree_desc, (VOPFUNC)err_vfree}, /* vfree */
145 {&vop_truncate_desc, (VOPFUNC)err_truncate}, /* truncate */
146 {&vop_allocate_desc, (VOPFUNC)err_allocate}, /* allocate */
147 {&vop_update_desc, (VOPFUNC)err_update}, /* update */
148 {&vop_pgrd_desc, (VOPFUNC)err_pgrd}, /* pgrd */
149 {&vop_pgwr_desc, (VOPFUNC)err_pgwr}, /* pgwr */
150 {&vop_pagein_desc, (VOPFUNC)err_pagein}, /* pagein */
151 {&vop_pageout_desc, (VOPFUNC)err_pageout}, /* pageout */
152 {&vop_devblocksize_desc, (VOPFUNC)err_devblocksize}, /* devblocksize */
153 {&vop_searchfs_desc, (VOPFUNC)err_searchfs}, /* searchfs */
154 {&vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
155 {&vop_blktooff_desc, (VOPFUNC)err_blktooff}, /* blktooff */
156 {&vop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */
157 {&vop_cmap_desc, (VOPFUNC)err_cmap }, /* cmap */
158 {(struct vnodeop_desc *) NULL, (int (*) ()) NULL}
159};
160
161/*
162 * Oh what a tangled web we weave. This structure will be used by
163 * bsd/vfs/vfs_conf.c to actually do the initialization of volfs_vnodeop_p
164 */
165struct vnodeopv_desc volfs_vnodeop_opv_desc =
166{&volfs_vnodeop_p, volfs_vnodeop_entries};
167
168
169static int validfsnode(struct mount *fsnode);
170
171#if DBG_VOP_TEST_LOCKS
172static void DbgVopTest (int max, int error, VopDbgStoreRec *VopDbgStore, char *funcname);
173#endif /* DBG_VOP_TEST_LOCKS */
174
175
176/*
177 * volfs_reclaim - Reclaim a vnode so that it can be used for other purposes.
178 *
179 * Locking policy: ignored
180 */
181int
182volfs_reclaim(ap)
183 struct vop_reclaim_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap;
184{
185 struct vnode *vp = ap->a_vp;
186 void *data = vp->v_data;
187
188 DBG_FUNC_NAME("volfs_reclaim");
189 DBG_VOP_LOCKS_DECL(1);
190 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
191
192 DBG_VOP_LOCKS_INIT(0, vp, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
193
194 vp->v_data = NULL;
195 FREE(data, M_VOLFSNODE);
196
197 DBG_VOP_LOCKS_TEST(0);
198 return (0);
199}
200
201/*
202 * volfs_access - same access policy for all vnodes and all users (file/directory vnodes
203 * for the actual file systems are handled by actual file system)
204 *
205 * Locking policy: a_vp locked on input and output
206 */
207int
208volfs_access(ap)
209 struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct
210 ucred *a_cred; struct proc *a_p; } */ *ap;
211{
212 int ret_err;
213 DBG_FUNC_NAME("volfs_access");
214 DBG_VOP_LOCKS_DECL(1);
215 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
216
217 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
218
219 /*
220 * We don't need to check credentials! FS is read-only for everyone
221 */
222 if (ap->a_mode == VREAD || ap->a_mode == VEXEC)
223 ret_err = 0;
224 else
225 ret_err = EACCES;
226
227 DBG_VOP_LOCKS_TEST(ret_err);
228 return (ret_err);
229}
230
231/*
232 * volfs_getattr - fill in the attributes for this vnode
233 *
234 * Locking policy: don't change anything
235 */
236int
237volfs_getattr(ap)
238 struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap;
239 struct ucred *a_cred; struct proc *a_p; } */ *ap;
240{
241 struct volfs_vndata *priv_data;
242 struct vnode *a_vp;
243 struct vattr *a_vap;
244 int numMounts = 0;
245 DBG_FUNC_NAME("volfs_getattr");
246 DBG_VOP_LOCKS_DECL(1);
247 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
248
249 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
250
251 a_vp = ap->a_vp;
252 a_vap = ap->a_vap;
253
254 priv_data = a_vp->v_data;
255
256 a_vap->va_type = VDIR;
257 a_vap->va_mode = 0444; /* Yup, hard - coded to read - only */
258 a_vap->va_nlink = 2;
259 a_vap->va_uid = 0; /* Always owned by root */
260 a_vap->va_gid = 0; /* Always part of group 0 */
261 a_vap->va_fsid = (int) a_vp->v_mount->mnt_stat.f_fsid.val[0];
262 a_vap->va_fileid = priv_data->nodeID;
263
264 /*
265 * If it's the root vnode calculate its size based on the number of eligible
266 * file systems
267 */
268 if (priv_data->vnode_type == VOLFS_ROOT)
269 {
270 register struct mount *mp, *nmp;
271
272 simple_lock(&mountlist_slock);
273 for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) {
274 if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, ap->a_p)) {
275 nmp = mp->mnt_list.cqe_next;
276 continue;
277 }
278
279 if (mp != a_vp->v_mount && validfsnode(mp))
280 numMounts++;
281
282 simple_lock(&mountlist_slock);
283 nmp = mp->mnt_list.cqe_next;
284 vfs_unbusy(mp, ap->a_p);
285 }
286 simple_unlock(&mountlist_slock);
287
288 DBG_VOP(("found %d file systems that volfs can support\n", numMounts));
289 a_vap->va_size = (numMounts + 2) * VLFSDIRENTLEN;
290 }
291 else
292 {
293 a_vap->va_size = 2 * VLFSDIRENTLEN;
294 }
295 DBG_VOP(("va_size = %d, VLFSDIRENTLEN = %ld\n", (int) a_vap->va_size, VLFSDIRENTLEN));
296 a_vap->va_blocksize = 512;
297
298 a_vap->va_atime.tv_sec = boottime.tv_sec;
299 a_vap->va_atime.tv_nsec = 0;
300
301 a_vap->va_mtime.tv_sec = boottime.tv_sec;
302 a_vap->va_mtime.tv_nsec = 0;
303
304 a_vap->va_ctime.tv_sec = boottime.tv_sec;
305 a_vap->va_ctime.tv_nsec = 0;
306
307 a_vap->va_gen = 0;
308 a_vap->va_flags = 0;
309 a_vap->va_rdev = 0;
310 a_vap->va_bytes = a_vap->va_size;
311 a_vap->va_filerev = 0;
312 a_vap->va_vaflags = 0;
313
314 DBG_VOP_LOCKS_TEST(0);
315 return (0);
316}
317
318/*
319 * volfs_select - just say OK. Only possible op is readdir
320 *
321 * Locking policy: ignore
322 */
323int
324volfs_select(ap)
325 struct vop_select_args /* { struct vnode *a_vp; int a_which; int
0b4e3aa0 326 * a_fflags; struct ucred *a_cred; void * a_wql; struct
1c79356b
A
327 proc *a_p; } */ *ap;
328{
329 DBG_VOP(("volfs_select called\n"));
330
331 return (1);
332}
333
334/*
335 * vofls_rmdir - not possible to remove directories in volfs
336 *
337 * Locking policy: a_dvp & a_vp - locked on entry, unlocked on exit
338 */
339int
340volfs_rmdir(ap)
341 struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp;
342 struct componentname *a_cnp; } */ *ap;
343{
344 DBG_VOP(("volfs_rmdir called\n"));
345 if (ap->a_dvp == ap->a_vp) {
346 (void) nop_rmdir(ap);
347 return (EINVAL);
348 } else
349 return (err_rmdir(ap));
350}
351
352/*
353 * volfs_readdir - Get directory entries
354 *
355 * Directory listings are only produced for the root volfs node. Filesystems
356 * just return . & ..
357 * Filesystems contained within the volfs root are named by the decimal
358 * equivalent of the f_fsid.val[0] from their mount structure (typically
359 * the device id of the volume). The maximum length for a name, then is
360 * 10 characters.
361 *
362 * Locking policy: a_vp locked on entry and exit
363 */
364int
365volfs_readdir(ap)
366 struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio;
367 * struct ucred *a_cred; int *a_eofflag; int
368 *ncookies; u_long **a_cookies; } */ *ap;
369{
370 struct volfs_vndata *priv_data;
371 register struct uio *uio = ap->a_uio;
372 int error = 0;
373 size_t count, lost;
374 int rec_offset;
375 struct dirent local_dir;
376 int i;
377 int starting_resid;
378 off_t off;
379 DBG_FUNC_NAME("volfs_readdir");
380 DBG_VOP_LOCKS_DECL(1);
381
382 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
383 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
384
385 DBG_VOP(("\tuio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid));
386 /* We assume it's all one big buffer... */
387 if (uio->uio_iovcnt > 1)
388 DBG_VOP(("\tuio->uio_iovcnt = %d?\n", uio->uio_iovcnt));
389
390 off = uio->uio_offset;
391 priv_data = ap->a_vp->v_data;
392 starting_resid = uio->uio_resid;
393 count = uio->uio_resid;
394
395 /* Make sure we don't return partial entries. */
396 count -= (uio->uio_offset + count) & (VLFSDIRENTLEN - 1);
397 if (count <= 0)
398 {
399 DBG_VOP(("volfs_readdir: Not enough buffer to read in entries\n"));
400 DBG_VOP_LOCKS_TEST(EINVAL);
401 return (EINVAL);
402 }
403 /*
404 * Make sure we're starting on a directory boundary
405 */
406 if (off & (VLFSDIRENTLEN - 1))
407 {
408 DBG_VOP_LOCKS_TEST(EINVAL);
409 return (EINVAL);
410 }
411 rec_offset = off / VLFSDIRENTLEN;
412 lost = uio->uio_resid - count;
413 uio->uio_resid = count;
414 uio->uio_iov->iov_len = count;
415
416 local_dir.d_reclen = VLFSDIRENTLEN;
417 /*
418 * We must synthesize . and ..
419 */
420 DBG_VOP(("\tstarting ... uio_offset = %d, uio_resid = %d\n",
421 (int) uio->uio_offset, uio->uio_resid));
422 if (rec_offset == 0)
423 {
424 DBG_VOP(("\tAdding .\n"));
425 /*
426 * Synthesize .
427 */
428 local_dir.d_fileno = priv_data->nodeID;
429 local_dir.d_type = DT_DIR;
430 local_dir.d_namlen = 1;
431 local_dir.d_name[0] = '.';
432 for (i = 1; i < MAXVLFSNAMLEN; i++)
433 local_dir.d_name[i] = 0;
434 error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
435 DBG_VOP(("\t after adding ., uio_offset = %d, uio_resid = %d\n",
436 (int) uio->uio_offset, uio->uio_resid));
437 rec_offset++;
438 }
439 if (rec_offset == 1)
440 {
441 DBG_VOP(("\tAdding ..\n"));
442 /*
443 * Synthesize ..
444 * We only have two levels in the volfs hierarchy. Root's
445 * .. points to itself and the second level points to root,
446 * hence we've hardcoded d_fileno for .. here
447 */
448 local_dir.d_fileno = ROOT_DIRID;
449 local_dir.d_type = DT_DIR;
450 local_dir.d_namlen = 2;
451 local_dir.d_name[0] = '.';
452 local_dir.d_name[1] = '.';
453 for (i = 2; i < MAXVLFSNAMLEN; i++)
454 local_dir.d_name[i] = 0;
455 error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
456 rec_offset++;
457 DBG_VOP(("\t after adding .., uio_offset = %d, uio_resid = %d\n",
458 (int) uio->uio_offset, uio->uio_resid));
459 }
460
461 /*
462 * OK, we've given them the . & .. entries. If this is a
463 * filesystem node then we've gone as far as we're going
464 * to go
465 */
466 if (priv_data->vnode_type == VOLFS_FSNODE)
467 {
468 *ap->a_eofflag = 1; /* we got all the way to the end */
469 DBG_VOP_LOCKS_TEST(error);
470 return (error);
471 }
472
473 if (rec_offset > 1) {
474 register struct mount *mp, *nmp;
475 int validnodeindex;
476 struct proc *p = uio->uio_procp;
477
478 validnodeindex = 1; /* we always have "." and ".." */
479
480 simple_lock(&mountlist_slock);
481 for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) {
482 if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) {
483 nmp = mp->mnt_list.cqe_next;
484 continue;
485 }
486
487 if (mp != ap->a_vp->v_mount && validfsnode(mp))
488 validnodeindex++;
489
490 if (rec_offset == validnodeindex)
491 {
492 local_dir.d_fileno = mp->mnt_stat.f_fsid.val[0];
493 local_dir.d_type = DT_DIR;
494 local_dir.d_reclen = VLFSDIRENTLEN;
495 DBG_VOP(("\tAdding dir entry %d for offset %d\n", mp->mnt_stat.f_fsid.val[0], rec_offset));
496 local_dir.d_namlen = sprintf(&local_dir.d_name[0], "%d", mp->mnt_stat.f_fsid.val[0]);
497 error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
498 DBG_VOP(("\t after adding entry '%s', uio_offset = %d, uio_resid = %d\n",
499 &local_dir.d_name[0], (int) uio->uio_offset, uio->uio_resid));
500 rec_offset++;
501 }
502
503 simple_lock(&mountlist_slock);
504 nmp = mp->mnt_list.cqe_next;
505 vfs_unbusy(mp, p);
506 }
507 simple_unlock(&mountlist_slock);
508
509 if (mp == (void *) &mountlist)
510 *ap->a_eofflag = 1; /* we got all the way to the end */
511 }
512
513 uio->uio_resid += lost;
514 if (starting_resid == uio->uio_resid)
515 uio->uio_offset = 0;
516
517 DBG_VOP(("\tExiting, uio_offset = %d, uio_resid = %d, ap->a_eofflag = %d\n",
518 (int) uio->uio_offset, uio->uio_resid, *ap->a_eofflag));
519
520 DBG_VOP_LOCKS_TEST(error);
521 return (error);
522}
523
524
525/*
526 * validfsnode - test to see if a file system supports VGET
527 *
528 * This can cause context switching, so caller should be lock safe
529 */
530static int
531validfsnode(struct mount *fsnode)
532{
533
534 /*
535 * Just check to see if the the mount flag is set, if it is we assume the
536 * file system supports all of volfs symantecs
537 */
538
539 if ((! (fsnode->mnt_kern_flag & MNTK_UNMOUNT)) && (fsnode->mnt_flag & MNT_DOVOLFS))
540 return 1;
541 else
542 return 0;
543}
544
545/*
546 * volfs_lock - Lock an inode.
547 * If its already locked, set the WANT bit and sleep.
548 *
549 * Locking policy: handled by lockmgr
550 */
551int
552volfs_lock(ap)
553 struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct
554 proc *a_p; } */ *ap;
555{
556 int retval;
557 struct volfs_vndata *priv_data;
558 DBG_FUNC_NAME("volfs_lock");
559 DBG_VOP_LOCKS_DECL(1);
560 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
561
562 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
563
564 priv_data = (struct volfs_vndata *) ap->a_vp->v_data;
565 retval = lockmgr(&priv_data->lock, ap->a_flags, &ap->a_vp->v_interlock, ap->a_p);
566 DBG_VOP_LOCKS_TEST(retval);
567 return (retval);
568}
569
570/*
571 * volfs_unlock - Unlock an inode.
572 *
573 * Locking policy: handled by lockmgr
574 */
575int
576volfs_unlock(ap)
577 struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct
578 proc *a_p; } */ *ap;
579{
580 int retval;
581 struct volfs_vndata *priv_data;
582 DBG_FUNC_NAME("volfs_unlock");
583 DBG_VOP_LOCKS_DECL(1);
584 DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
585
586 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
587
588 priv_data = (struct volfs_vndata *) ap->a_vp->v_data;
589 retval = lockmgr(&priv_data->lock, ap->a_flags | LK_RELEASE,
590 &ap->a_vp->v_interlock, ap->a_p);
591
592 DBG_VOP_LOCKS_TEST(retval);
593 return (retval);
594}
595
596/*
597 * volfs_islocked - Check for a locked inode.
598 *
599 * Locking policy: ignore
600 */
601int
602volfs_islocked(ap)
603 struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap;
604{
605 int retval;
606 struct volfs_vndata *priv_data;
607
608 DBG_FUNC_NAME("volfs_islocked");
609 DBG_VOP_LOCKS_DECL(1);
610 //DBG_VOP_PRINT_FUNCNAME();DBG_VOP(("\n"));
611
612 DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
613 priv_data = (struct volfs_vndata *) ap->a_vp->v_data;
614 retval = lockstatus(&priv_data->lock);
615
616 DBG_VOP_LOCKS_TEST(retval);
617 return (retval);
618}
619
620/*
621 * volfs_pathconf - Return POSIX pathconf information applicable to ufs filesystems.
622 *
623 * Locking policy: a_vp locked on input and output
624 */
625int
626volfs_pathconf(ap)
627 struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int
628 *a_retval; } */ *ap;
629{
630 DBG_VOP(("volfs_pathconf called\n"));
631
632 switch (ap->a_name)
633 {
634 case _PC_LINK_MAX:
635 *ap->a_retval = LINK_MAX;
636 return (0);
637 case _PC_NAME_MAX:
638 *ap->a_retval = NAME_MAX;
639 return (0);
640 case _PC_PATH_MAX:
641 *ap->a_retval = PATH_MAX;
642 return (0);
643 case _PC_PIPE_BUF:
644 *ap->a_retval = PIPE_BUF;
645 return (0);
646 case _PC_CHOWN_RESTRICTED:
647 *ap->a_retval = 1;
648 return (0);
649 case _PC_NO_TRUNC:
650 *ap->a_retval = 1;
651 return (0);
652 default:
653 return (EINVAL);
654 }
655 /* NOTREACHED */
656}
657
658/*
659 * get_fsvnode - internal routine to create a vnode for a file system. Called with mount pointer,
660 * id of filesystem to lookup and pointer to vnode pointer to fill in
661 */
662static int
663get_fsvnode(our_mount, id, ret_vnode)
664 struct mount *our_mount;
665 int id;
666 struct vnode **ret_vnode;
667{
668 register struct mount *mp;
669 struct mount *cur_mount;
670 struct vnode *cur_vnode;
671 struct volfs_vndata *cur_privdata;
672 int retval;
673
674 //DBG_VOP(("volfs: get_fsvnode called\n"));
675
676 /*
677 * OK, first look up the matching mount on the list of mounted file systems
678 */
679 cur_mount = NULL;
680 simple_lock(&mountlist_slock);
681 for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = mp->mnt_list.cqe_next)
682 {
683 if (validfsnode(mp) && mp->mnt_stat.f_fsid.val[0] == id)
684 {
685 cur_mount = mp;
686 break;
687 }
688 }
689 simple_unlock(&mountlist_slock);
690
691 if (cur_mount == NULL) {
692 /*
693 * No mounted file system by the specified ID currently exists in the system.
694 *
695 * XXX We could deal with a vnode that is still hanging about for an FS that
696 * does not exists or has been unmounted now, or count on the update below
697 * to happen later...
698 */
699 *ret_vnode = NULL;
700 return ENOENT;
701 };
702
703 /*
704 * Now search the list attached to the mount structure to
705 * see if this vnode is already floating around
706 */
707search_vnodelist:
708 cur_vnode = our_mount->mnt_vnodelist.lh_first;
709 while (cur_vnode != NULL)
710 {
711 cur_privdata = (struct volfs_vndata *) cur_vnode->v_data;
712 if (cur_privdata->nodeID == id)
713 {
714 if (cur_privdata->fs_mount != cur_mount) {
715 DBG_VOP(("volfs get_fsvnode: Updating fs_mount for vnode 0x%08lX (id = %d) from 0x%08lX to 0x%08lX...\n",
716 (unsigned long)cur_vnode,
717 cur_privdata->nodeID,
718 (unsigned long)cur_privdata->fs_mount,
719 (unsigned long)cur_mount));
720 cur_privdata->fs_mount = cur_mount;
721 };
722 break;
723 }
724 cur_vnode = cur_vnode->v_mntvnodes.le_next;
725 }
726
727 //DBG_VOP(("\tfinal cur_mount: 0x%x\n",cur_mount));
728 if (cur_vnode) {
729 /* If vget returns an error, cur_vnode will not be what we think it is, try again */
730 if (vget(cur_vnode, LK_EXCLUSIVE, current_proc()) != 0) {
731 goto search_vnodelist;
732 };
733 }
734 else
735 {
736 MALLOC(cur_privdata, struct volfs_vndata *,
737 sizeof(struct volfs_vndata), M_VOLFSNODE, M_WAITOK);
738 retval = getnewvnode(VT_VOLFS, our_mount, volfs_vnodeop_p, &cur_vnode);
739 if (retval != 0) {
740 FREE(cur_privdata, M_VOLFSNODE);
741 return retval;
742 };
743
744 cur_privdata->vnode_type = VOLFS_FSNODE;
745 cur_privdata->nodeID = id;
746
747 cur_privdata->fs_mount = cur_mount;
748 lockinit(&cur_privdata->lock, PINOD, "volfsnode", 0, 0);
749 lockmgr(&cur_privdata->lock, LK_EXCLUSIVE, (struct slock *)0, current_proc());
750 cur_vnode->v_data = cur_privdata;
751 cur_vnode->v_type = VDIR;
752 DBG_VOP(("get_fsvnode returned with new node of "));
753 DBG_VOP_PRINT_VNODE_INFO(cur_vnode);DBG_VOP(("\n"));
754 }
755
756 *ret_vnode = cur_vnode;
757
758 return (0);
759}
760
761
762
763/*
764 * get_filevnode - returns the vnode for the given id within a filesystem. The parent vnode
765 * is a filesystem, id is the 32-bit id of the file/directory and ret_vnode is a pointer
766 * to a vnode pointer
767 */
768static int
769get_filevnode(parent_fs, id, ret_vnode)
770 struct mount *parent_fs;
771 u_int id;
772 struct vnode **ret_vnode;
773{
774 int retval;
775
776 DBG_VOP(("get_filevnode called for ID %d\n", id));
777
778 /*
779 * Special case 2 to mean the root of a file system
780 */
781 if (id == 2)
782 retval = VFS_ROOT(parent_fs, ret_vnode);
783 else
784 retval = VFS_VGET(parent_fs, &id, ret_vnode);
785
786 return (retval);
787}
788
789
790int
791volfs_lookup(ap)
792 struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode
793 **a_vpp; struct componentname *a_cnp; } */ *ap;
794{
795 struct volfs_vndata *priv_data;
796 char *cnp;
797 long namelen;
798 struct mount *parent_fs;
799 int unlocked_parent = 0;
800 int ret_err = ENOENT;
801 DBG_FUNC_NAME("volfs_lookup");
802 DBG_VOP_LOCKS_DECL(2);
803
804 DBG_VOP(("volfs_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
805
806 DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
807 DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
808 DBG_VOP_PRINT_FUNCNAME();DBG_VOP(("\n"));
809 DBG_VOP(("\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP(("\n"));
810 if (ap->a_cnp->cn_flags & LOCKPARENT)
811 DBG_VOP(("\tLOCKPARENT is set\n"));
812 if (ap->a_cnp->cn_flags & ISLASTCN)
813 {
814 DBG_VOP(("\tISLASTCN is set\n"));
815 if (ap->a_cnp->cn_nameiop == DELETE || ap->a_cnp->cn_nameiop == RENAME) /* XXX PPD Shouldn't we check for CREATE, too? */
816 {
817 ret_err = EROFS;
818 goto Err_Exit;
819 }
820 }
821 priv_data = ap->a_dvp->v_data;
822 cnp = ap->a_cnp->cn_nameptr;
823 namelen = ap->a_cnp->cn_namelen;
824
825#if VOLFS_DEBUG
826 switch (priv_data->vnode_type) {
827 case VOLFS_ROOT:
828 DBG_VOP(("\tparent directory (vnode 0x%08lX) vnode_type is VOLFS_ROOT.\n", (unsigned long)ap->a_dvp));
829 break;
830
831 case VOLFS_FSNODE:
832 DBG_VOP(("\tparent directory (vnode 0x%08lX) vnode_type is VOLFS_FSNODE, nodeID = %d, fs_mount = 0x%08lX.\n",
833 (unsigned long)ap->a_dvp,
834 priv_data->nodeID,
835 (unsigned long)priv_data->fs_mount));
836
837 default:
838 DBG_VOP(("\tparent directory (vnode 0x%08lX) has unknown vnode_type (%d), nodeID = %d.\n",
839 (unsigned long)ap->a_dvp,
840 priv_data->vnode_type,
841 priv_data->nodeID));
842 };
843#endif /* VOLFS_DEBUG */
844
845 /* first check for "." and ".." */
846 if (cnp[0] == '.')
847 {
848 if (namelen == 1)
849 {
850 /* "." requested */
851 *ap->a_vpp = ap->a_dvp;
852 VREF(*ap->a_vpp);
853 DBG_VOP_LOCKS_TEST(0);
854 return (0);
855 }
856 else if (cnp[1] == '.' && namelen == 2)
857 {
858 /* ".." requested */
859 ret_err = volfs_root(ap->a_dvp->v_mount, ap->a_vpp);
860 }
861 }
862
863 /* then look for special file system root symbol ('@') */
864 else if (cnp[0] == '@')
865 {
866 if ((namelen == 1) && (priv_data->vnode_type != VOLFS_ROOT)) {
867 parent_fs = priv_data->fs_mount;
868 if (!(ap->a_cnp->cn_flags & LOCKPARENT) || !(ap->a_cnp->cn_flags & ISLASTCN)) {
869 VOP_UNLOCK(ap->a_dvp, 0, ap->a_cnp->cn_proc);
870 unlocked_parent = 1;
871 };
872 ret_err = VFS_ROOT(parent_fs, ap->a_vpp);
873 } else {
874 DBG_VOP(("volfs_lookup: pathname = '@' but namelen = %ld and parent vnode_type = %d.\n", namelen, priv_data->vnode_type));
875 *ap->a_vpp = NULL;
876 ret_err = ENOENT;
877 };
878 }
879
880 /* finally, just look for numeric ids... */
881 else if (namelen <= 10 && cnp[0] > '0' && cnp[0] <= '9') /* 10 digits max lead digit must be 1 - 9 */
882 {
883 char *check_ptr;
884 u_long id;
885
0b4e3aa0 886 id = strtoul(cnp, &check_ptr, 10);
1c79356b
A
887
888 /*
889 * strtol will leave us at the first non-numeric character.
890 * we've checked to make sure the component name does
891 * begin with a numeric so check_ptr must wind up on
892 * the terminating null or there was other junk following the
893 * number
894 */
895 if ((check_ptr - cnp) == namelen)
896 {
897 if (priv_data->vnode_type == VOLFS_ROOT)
898 ret_err = get_fsvnode(ap->a_dvp->v_mount, id, ap->a_vpp);
899 else {
900 parent_fs = priv_data->fs_mount;
901 if (!(ap->a_cnp->cn_flags & LOCKPARENT) || !(ap->a_cnp->cn_flags & ISLASTCN)) {
902 VOP_UNLOCK(ap->a_dvp, 0, ap->a_cnp->cn_proc);
903 unlocked_parent = 1;
904 };
905 ret_err = get_filevnode(parent_fs, id, ap->a_vpp);
906 }
907 }
908
909 }
910
911 if (!unlocked_parent && (!(ap->a_cnp->cn_flags & LOCKPARENT) || !(ap->a_cnp->cn_flags & ISLASTCN))) {
912 VOP_UNLOCK(ap->a_dvp, 0, ap->a_cnp->cn_proc);
913 };
914
915 /* XXX PPD Should we do something special in case LOCKLEAF isn't set? */
916
917Err_Exit:
918
919 DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
920 DBG_VOP_LOCKS_TEST(ret_err);
921
922 return (ret_err);
923}
924
925#if DBG_VOP_TEST_LOCKS
926
927#if 0
928static void DbgLookupTest( char *funcname, struct componentname *cnp, struct vnode *dvp, struct vnode *vp)
929{
930 int flags = cnp->cn_flags;
931 int nameiop = cnp->cn_nameiop;
932
933 DBG_VOP (("%s: Action:", funcname));
934 switch (nameiop)
935 {
936 case LOOKUP:
937 PRINTIT ("LOOKUP");
938 break;
939 case CREATE:
940 PRINTIT ("CREATE");
941 break;
942 case DELETE:
943 PRINTIT ("DELETE");
944 break;
945 case RENAME:
946 PRINTIT ("RENAME");
947 break;
948 default:
949 PRINTIT ("!!!UNKNOWN!!!!");
950 break;
951 }
952 PRINTIT(" flags: 0x%x ",flags );
953 if (flags & LOCKPARENT)
954 PRINTIT (" Lock Parent");
955 if (flags & ISLASTCN)
956 PRINTIT (" Last Action");
957 PRINTIT("\n");
958
959 if (dvp)
960 {
961 PRINTIT ("%s: Parent vnode exited ", funcname);
962 if (VOP_ISLOCKED(dvp))
963 PRINTIT("LOCKED\n");
964 else
965 PRINTIT("UNLOCKED\n");
966 }
967 if (vp && vp==dvp)
968 {
969 PRINTIT ("%s: Found and Parent are the same\n", funcname);
970 }
971 else if (vp)
972 {
973 PRINTIT ("%s: Found vnode exited ", funcname);
974 if (VOP_ISLOCKED(vp))
975 PRINTIT("LOCKED\n");
976 else
977 PRINTIT("UNLOCKED\n");
978 }
979 else
980 PRINTIT ("%s: Found vnode exited NULL\n", funcname);
981
982
983}
984#endif
985
986static void DbgVopTest( int maxSlots,
987 int retval,
988 VopDbgStoreRec *VopDbgStore,
989 char *funcname)
990{
991 int index;
992
993 for (index = 0; index < maxSlots; index++)
994 {
995 if (VopDbgStore[index].id != index) {
996 PRINTIT("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname, VopDbgStore[index].id, index);
997 return;
998 };
999
1000 if ((VopDbgStore[index].vp != NULL) &&
1001 ((VopDbgStore[index].vp->v_data==NULL)))
1002 continue;
1003
1004 switch (VopDbgStore[index].inState)
1005 {
1006 case VOPDBG_IGNORE:
1007 case VOPDBG_SAME:
1008 /* Do Nothing !!! */
1009 break;
1010 case VOPDBG_LOCKED:
1011 case VOPDBG_UNLOCKED:
1012 case VOPDBG_LOCKNOTNIL:
1013 {
1014 if (VopDbgStore[index].vp == NULL && (VopDbgStore[index].inState != VOPDBG_LOCKNOTNIL)) {
1015 PRINTIT ("%s: InState check: Null vnode ptr in entry #%d\n", funcname, index);
1016 } else if (VopDbgStore[index].vp != NULL) {
1017 switch (VopDbgStore[index].inState)
1018 {
1019 case VOPDBG_LOCKED:
1020 case VOPDBG_LOCKNOTNIL:
1021 if (VopDbgStore[index].inValue == 0)
1022 {
1023 PRINTIT ("%s: %d Entry: not LOCKED:", funcname, index); DBG_VOP(("\n"));
1024 }
1025 break;
1026 case VOPDBG_UNLOCKED:
1027 if (VopDbgStore[index].inValue != 0)
1028 {
1029 PRINTIT ("%s: %d Entry: not UNLOCKED:", funcname, index); DBG_VOP(("\n"));
1030 }
1031 break;
1032 }
1033 }
1034 break;
1035 }
1036 default:
1037 PRINTIT ("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname, VopDbgStore[index].errState);
1038 }
1039
1040
1041 if (retval != 0)
1042 {
1043 switch (VopDbgStore[index].errState)
1044 {
1045 case VOPDBG_IGNORE:
1046 /* Do Nothing !!! */
1047 break;
1048 case VOPDBG_LOCKED:
1049 case VOPDBG_UNLOCKED:
1050 case VOPDBG_SAME:
1051 {
1052 if (VopDbgStore[index].vp == NULL) {
1053 PRINTIT ("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname, index);
1054 } else {
1055 VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
1056 switch (VopDbgStore[index].errState)
1057 {
1058 case VOPDBG_LOCKED:
1059 if (VopDbgStore[index].outValue == 0)
1060 {
1061 PRINTIT ("%s: %d Error: not LOCKED:", funcname, index); DBG_VOP(("\n"));
1062 }
1063 break;
1064 case VOPDBG_UNLOCKED:
1065 if (VopDbgStore[index].outValue != 0)
1066 {
1067 PRINTIT ("%s: %d Error: not UNLOCKED:", funcname, index); DBG_VOP(("\n"));
1068 }
1069 break;
1070 case VOPDBG_SAME:
1071 if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
1072 PRINTIT ("%s: Error: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue);
1073 break;
1074 }
1075 }
1076 break;
1077 }
1078 case VOPDBG_LOCKNOTNIL:
1079 if (VopDbgStore[index].vp != NULL) {
1080 VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
1081 if (VopDbgStore[index].outValue == 0)
1082 PRINTIT ("%s: Error: %d Not LOCKED: 0x%x\n", funcname, index, (u_int)VopDbgStore[index].vp);
1083 }
1084 break;
1085 default:
1086 PRINTIT ("%s: Error: bad lock test value: %d\n", funcname, VopDbgStore[index].errState);
1087 }
1088 }
1089 else
1090 {
1091 switch (VopDbgStore[index].outState)
1092 {
1093 case VOPDBG_IGNORE:
1094 /* Do Nothing !!! */
1095 break;
1096 case VOPDBG_LOCKED:
1097 case VOPDBG_UNLOCKED:
1098 case VOPDBG_SAME:
1099 if (VopDbgStore[index].vp == NULL) {
1100 PRINTIT ("%s: OutState: Null vnode ptr in entry #%d\n", funcname, index);
1101 };
1102 if (VopDbgStore[index].vp != NULL)
1103 {
1104 VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
1105 switch (VopDbgStore[index].outState)
1106 {
1107 case VOPDBG_LOCKED:
1108 if (VopDbgStore[index].outValue == 0)
1109 {
1110 PRINTIT ("%s: %d Out: not LOCKED:", funcname, index); DBG_VOP(("\n"));
1111 }
1112 break;
1113 case VOPDBG_UNLOCKED:
1114 if (VopDbgStore[index].outValue != 0)
1115 {
1116 PRINTIT ("%s: %d Out: not UNLOCKED:", funcname, index); DBG_VOP(("\n"));
1117 }
1118 break;
1119 case VOPDBG_SAME:
1120 if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
1121 PRINTIT ("%s: Out: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue);
1122 break;
1123 }
1124 }
1125 break;
1126 case VOPDBG_LOCKNOTNIL:
1127 if (VopDbgStore[index].vp != NULL) {
1128 if (&((struct volfs_vndata *)(VopDbgStore[index].vp->v_data))->lock == NULL)
1129 PRINTIT ("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname, (u_int)VopDbgStore[index].vp);
1130 else {
1131 VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
1132 if (VopDbgStore[index].outValue == 0)
1133 {
1134 PRINTIT ("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname); DBG_VOP(("\n"));
1135 }
1136 }
1137 }
1138 break;
1139 default:
1140 PRINTIT ("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname, VopDbgStore[index].outState);
1141 }
1142 }
1143
1144 VopDbgStore[index].id = -1; /* Invalidate the entry to allow panic-free re-use */
1145 }
1146}
1147
1148#endif /* DBG_VOP_TEST_LOCKS */
1149