update reference counts on vnodes using VREF
[unix-history] / usr / src / sys / ufs / ffs / ffs_vfsops.c
CommitLineData
da7c5cc6 1/*
7188ac27
KM
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
da7c5cc6 4 *
7188ac27
KM
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 *
8fe1c702 17 * @(#)ffs_vfsops.c 7.16 (Berkeley) %G%
da7c5cc6 18 */
71e4e98b 19
7188ac27 20
94368568
JB
21#include "param.h"
22#include "systm.h"
7188ac27
KM
23#include "time.h"
24#include "kernel.h"
25#include "namei.h"
26#include "vnode.h"
94368568 27#include "mount.h"
7188ac27 28#include "buf.h"
94368568 29#include "file.h"
ec67a3ce 30#include "disklabel.h"
7188ac27
KM
31#include "ioctl.h"
32#include "errno.h"
4def0c5e 33#include "malloc.h"
7188ac27
KM
34#include "../ufs/fs.h"
35#include "../ufs/ufsmount.h"
36#include "../ufs/inode.h"
609e7cfa
MK
37#include "ioctl.h"
38#include "disklabel.h"
39#include "stat.h"
71e4e98b 40
7188ac27
KM
41/*
42 * ufs vfs operations.
43 */
44int ufs_mount();
45int ufs_unmount();
46int ufs_root();
47int ufs_statfs();
48int ufs_sync();
49int ufs_fhtovp();
50int ufs_vptofh();
51
52struct vfsops ufs_vfsops = {
53 ufs_mount,
54 ufs_unmount,
55 ufs_root,
56 ufs_statfs,
57 ufs_sync,
58 ufs_fhtovp,
59 ufs_vptofh
60};
61
62/*
63 * ufs mount table.
64 */
65struct ufsmount mounttab[NMOUNT];
66
67/*
68 * Called by vfs_mountroot when ufs is going to be mounted as root
69 *
70 * XXX - Need to have a way of figuring the name of the root device
71 */
72#define ROOTNAME "root device"
73
74ufs_mountroot()
71e4e98b 75{
7188ac27
KM
76 register struct mount *mp;
77 extern struct vnode *rootvp;
78 struct ufsmount *ump;
71e4e98b 79 register struct fs *fs;
7188ac27
KM
80 u_int size;
81 int error;
71e4e98b 82
7188ac27
KM
83 mp = (struct mount *)malloc((u_long)sizeof(struct mount),
84 M_MOUNT, M_WAITOK);
85 mp->m_op = &ufs_vfsops;
86 mp->m_flag = 0;
87 mp->m_exroot = 0;
88 error = mountfs(rootvp, mp);
89 if (error) {
90 free((caddr_t)mp, M_MOUNT);
91 return (error);
71e4e98b 92 }
7188ac27
KM
93 error = vfs_add((struct vnode *)0, mp, 0);
94 if (error) {
95 (void)ufs_unmount(mp, 0);
96 free((caddr_t)mp, M_MOUNT);
97 return (error);
6d07f4cd 98 }
7188ac27
KM
99 ump = VFSTOUFS(mp);
100 fs = ump->um_fs;
101 fs->fs_fsmnt[0] = '/';
102 bzero(fs->fs_fsmnt + 1, sizeof(fs->fs_fsmnt) - 1);
103 (void) copystr(ROOTNAME, ump->um_mntname, MNAMELEN - 1, &size);
104 bzero(ump->um_mntname + size, MNAMELEN - size);
105 vfs_unlock(mp);
106 inittodr(fs->fs_time);
107 return (0);
108}
109
110/*
111 * VFS Operations.
112 *
113 * mount system call
114 */
115ufs_mount(mp, path, data, ndp)
116 struct mount *mp;
117 char *path;
118 caddr_t data;
119 struct nameidata *ndp;
120{
121 struct vnode *devvp;
122 struct ufs_args args;
123 struct ufsmount *ump;
124 register struct fs *fs;
125 u_int size;
126 int error;
127
128 if (error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args)))
129 return (error);
130 if ((error = getmdev(&devvp, args.fspec, ndp)) != 0)
131 return (error);
132 error = mountfs(devvp, mp);
133 if (error) {
134 vrele(devvp);
135 return (error);
27d00e76 136 }
7188ac27
KM
137 ump = VFSTOUFS(mp);
138 fs = ump->um_fs;
139 (void) copyinstr(path, fs->fs_fsmnt, sizeof(fs->fs_fsmnt) - 1, &size);
140 bzero(fs->fs_fsmnt + size, sizeof(fs->fs_fsmnt) - size);
141 (void) copyinstr(args.fspec, ump->um_mntname, MNAMELEN - 1, &size);
142 bzero(ump->um_mntname + size, MNAMELEN - size);
143 return (0);
71e4e98b
SL
144}
145
7188ac27
KM
146/*
147 * Common code for mount and mountroot
148 */
149mountfs(devvp, mp)
150 struct vnode *devvp;
151 struct mount *mp;
71e4e98b 152{
7188ac27
KM
153 register struct ufsmount *ump;
154 struct ufsmount *fmp = NULL;
155 struct buf *bp = NULL;
71e4e98b 156 register struct fs *fs;
7188ac27 157 dev_t dev = devvp->v_rdev;
ec67a3ce
MK
158 struct partinfo dpart;
159 int havepart = 0, blks;
27d00e76 160 caddr_t base, space;
7188ac27
KM
161 int havepart = 0, blks;
162 int error, i, size;
6d07f4cd 163 int needclose = 0;
7188ac27 164 int ronly = (mp->m_flag & M_RDONLY) != 0;
71e4e98b 165
7188ac27
KM
166 for (ump = &mounttab[0]; ump < &mounttab[NMOUNT]; ump++) {
167 if (ump->um_fs == NULL) {
27d00e76 168 if (fmp == NULL)
7188ac27
KM
169 fmp = ump;
170 } else if (dev == ump->um_dev) {
171 return (EBUSY); /* needs translation */
27d00e76
KM
172 }
173 }
ec67a3ce
MK
174 (*bdevsw[major(dev)].d_open)(dev, ronly ? FREAD : FREAD|FWRITE,
175 S_IFBLK);
27d00e76 176 if (error) {
7188ac27
KM
177 ump->um_fs = NULL;
178 return (error);
27d00e76 179 }
6d07f4cd 180 needclose = 1;
7188ac27
KM
181 if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD,
182 (struct ucred *)0) != 0)
609e7cfa 183 size = DEV_BSIZE;
7188ac27 184 else {
ec67a3ce
MK
185 havepart = 1;
186 size = dpart.disklab->d_secsize;
7188ac27
KM
187 }
188 if (error = bread(devvp, SBLOCK, SBSIZE, &bp)) {
189 ump->um_fs = NULL;
71e4e98b 190 goto out;
27d00e76 191 }
e018935f 192 fs = bp->b_un.b_fs;
7188ac27
KM
193 ump->um_fs = NULL;
194 error = EINVAL; /* XXX also needs translation */
1c281610
MK
195 goto out;
196 }
7188ac27 197 ump->um_fs = (struct fs *)malloc((u_long)fs->fs_sbsize, M_SUPERBLK,
5adcb337 198 M_WAITOK);
7188ac27 199 bcopy((caddr_t)bp->b_un.b_addr, (caddr_t)ump->um_fs,
71e4e98b 200 (u_int)fs->fs_sbsize);
e018935f
MK
201 brelse(bp);
202 bp = NULL;
7188ac27
KM
203 fs = ump->um_fs;
204 fs->fs_ronly = ronly;
71e4e98b
SL
205 if (ronly == 0)
206 fs->fs_fmod = 1;
609e7cfa
MK
207 if (havepart) {
208 dpart.part->p_fstype = FS_BSDFFS;
209 dpart.part->p_fsize = fs->fs_fsize;
210 dpart.part->p_frag = fs->fs_frag;
42ff4c2e 211 dpart.part->p_cpg = fs->fs_cpg;
609e7cfa 212 }
ec67a3ce
MK
213#ifdef SECSIZE
214 /*
215 * If we have a disk label, force per-partition
216 * filesystem information to be correct
217 * and set correct current fsbtodb shift.
218 */
219#endif SECSIZE
220 if (havepart) {
221 dpart.part->p_fstype = FS_BSDFFS;
222 dpart.part->p_fsize = fs->fs_fsize;
223 dpart.part->p_frag = fs->fs_frag;
224#ifdef SECSIZE
225#ifdef tahoe
226 /*
227 * Save the original fsbtodb shift to restore on updates.
228 * (Console doesn't understand fsbtodb changes.)
229 */
230 fs->fs_sparecon[0] = fs->fs_fsbtodb;
231#endif
232 i = fs->fs_fsize / size;
233 for (fs->fs_fsbtodb = 0; i > 1; i >>= 1)
234 fs->fs_fsbtodb++;
235#endif SECSIZE
236 fs->fs_dbsize = size;
237 }
71e4e98b 238 blks = howmany(fs->fs_cssize, fs->fs_fsize);
5adcb337
KM
239 base = space = (caddr_t)malloc((u_long)fs->fs_cssize, M_SUPERBLK,
240 M_WAITOK);
71e4e98b
SL
241 for (i = 0; i < blks; i += fs->fs_frag) {
242 size = fs->fs_bsize;
243 if (i + fs->fs_frag > blks)
244 size = (blks - i) * fs->fs_fsize;
ec67a3ce
MK
245#ifdef SECSIZE
246 tp = bread(dev, fsbtodb(fs, fs->fs_csaddr + i), size,
247 fs->fs_dbsize);
248#else SECSIZE
7188ac27
KM
249 error = bread(devvp, fsbtodb(fs, fs->fs_csaddr + i), size, &bp);
250 if (error) {
5adcb337 251 free((caddr_t)base, M_SUPERBLK);
71e4e98b
SL
252 goto out;
253 }
e018935f 254 bcopy((caddr_t)bp->b_un.b_addr, space, (u_int)size);
60f9c9e3 255 fs->fs_csp[fragstoblks(fs, i)] = (struct csum *)space;
71e4e98b 256 space += size;
e018935f
MK
257 brelse(bp);
258 bp = NULL;
71e4e98b 259 }
7188ac27
KM
260 mp->m_data = (qaddr_t)ump;
261 mp->m_bsize = fs->fs_bsize;
262 mp->m_fsize = fs->fs_fsize;
263 mp->m_fsid.val[0] = (long)dev;
264 mp->m_fsid.val[1] = MOUNT_UFS;
265 ump->um_mountp = mp;
266 ump->um_dev = dev;
267 ump->um_devvp = devvp;
268 ump->um_qinod = NULL;
269
94354803
KM
270 /* Sanity checks for old file systems. XXX */
271 fs->fs_npsect = MAX(fs->fs_npsect, fs->fs_nsect); /* XXX */
272 fs->fs_interleave = MAX(fs->fs_interleave, 1); /* XXX */
2af59000
KM
273 if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
274 fs->fs_nrpos = 8; /* XXX */
609e7cfa 275
7188ac27 276 return (0);
71e4e98b 277out:
609e7cfa 278 if (needclose)
7188ac27
KM
279 (void) VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE,
280 (struct ucred *)0);
281 if (ump->um_fs) {
282 free((caddr_t)ump->um_fs, M_SUPERBLK);
283 ump->um_fs = NULL;
27d00e76 284 }
e018935f
MK
285 if (bp)
286 brelse(bp);
7188ac27 287 return (error);
71e4e98b
SL
288}
289
71e4e98b 290
7188ac27
KM
291/*
292 * unmount system call
293 */
294ufs_unmount(mp, flags)
295 struct mount *mp;
296 int flags;
71e4e98b 297{
7188ac27 298 register struct ufsmount *ump;
71e4e98b 299 register struct fs *fs;
7188ac27
KM
300 dev_t dev;
301 int error, ronly;
71e4e98b 302
7188ac27
KM
303 if (flags & MNT_FORCE)
304 return (EINVAL);
305 ump = VFSTOUFS(mp);
306 dev = ump->um_dev;
71e4e98b 307#ifdef QUOTA
ec67a3ce 308 if ((error = iflush(dev, mp->m_qinod)) && !forcibly)
71e4e98b 309#else
ec67a3ce 310 if ((error = iflush(dev)) && !forcibly)
71e4e98b 311#endif
ec67a3ce 312 return (error);
71e4e98b 313#ifdef QUOTA
7188ac27 314 (void)closedq(ump);
71e4e98b
SL
315 /*
316 * Here we have to iflush again to get rid of the quota inode.
ec67a3ce 317 * A drag, but it would be ugly to cheat, & this doesn't happen often.
71e4e98b
SL
318 */
319 (void)iflush(dev, (struct inode *)NULL);
320#endif
7188ac27
KM
321 fs = ump->um_fs;
322 ronly = !fs->fs_ronly;
4def0c5e 323 free((caddr_t)fs->fs_csp[0], M_SUPERBLK);
ec67a3ce
MK
324 error = closei(dev, IFBLK, fs->fs_ronly? FREAD : FREAD|FWRITE);
325 irele(ip);
326 return (error);
71e4e98b
SL
327}
328
7188ac27
KM
329/*
330 * Return root of a filesystem
331 */
332ufs_root(mp, vpp)
333 struct mount *mp;
334 struct vnode **vpp;
335{
336 struct inode tip, *ip;
337 int error;
338
339 tip.i_dev = VFSTOUFS(mp)->um_dev;
340 tip.i_vnode.v_mount = mp;
341 error = iget(&tip, (ino_t)ROOTINO, &ip);
342 if (error)
343 return (error);
344 *vpp = ITOV(ip);
345 return (0);
346}
347
348/*
349 * Get file system statistics.
350 */
351ufs_statfs(mp, sbp)
352 struct mount *mp;
353 register struct statfs *sbp;
354{
355 register struct ufsmount *ump;
356 register struct fs *fs;
357
358 ump = VFSTOUFS(mp);
359 fs = ump->um_fs;
360 if (fs->fs_magic != FS_MAGIC)
361 panic("ufs_statfs");
362 sbp->f_type = MOUNT_UFS;
363 sbp->f_flags = mp->m_flag &~ (M_MLOCK|M_MWAIT);
364 sbp->f_fsize = fs->fs_fsize;
365 sbp->f_bsize = fs->fs_bsize;
366 sbp->f_blocks = fs->fs_dsize;
367 sbp->f_bfree = fs->fs_cstotal.cs_nbfree * fs->fs_frag +
368 fs->fs_cstotal.cs_nffree;
369 sbp->f_bavail = (fs->fs_dsize * (100 - fs->fs_minfree) / 100) -
370 (fs->fs_dsize - sbp->f_bfree);
371 if (sbp->f_bavail < 0)
372 sbp->f_bavail = 0;
373 sbp->f_files = fs->fs_ncg * fs->fs_ipg;
374 sbp->f_ffree = fs->fs_cstotal.cs_nifree;
375 sbp->f_fsid = mp->m_fsid;
376 bcopy((caddr_t)fs->fs_fsmnt, (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
377 bcopy((caddr_t)ump->um_mntname, (caddr_t)&sbp->f_mntfromname[0],
378 MNAMELEN);
379 return (0);
380}
381
382int syncprt = 0;
383
384/*
385 * Go through the disk queues to initiate sandbagged IO;
386 * go through the inodes to write those that have been modified;
387 * initiate the writing of the super block if it has been modified.
388 */
389ufs_sync(mp, waitfor)
71e4e98b 390 struct mount *mp;
7188ac27 391 int waitfor;
71e4e98b 392{
7188ac27
KM
393 register struct inode *ip;
394 register struct ufsmount *ump = VFSTOUFS(mp);
395 register struct fs *fs;
396 int error = 0;
397 static int updlock = 0;
398
399 if (syncprt)
400 bufstats();
401 if (updlock)
402 return (EBUSY);
403 fs = ump->um_fs;
404 if (fs == (struct fs *)1)
405 return (0);
406 updlock++;
407 /*
408 * Write back modified superblock.
409 * Consistency check that the superblock
410 * is still in the buffer cache.
411 */
412 if (fs->fs_fmod != 0) {
413 if (fs->fs_ronly != 0) { /* XXX */
414 printf("fs = %s\n", fs->fs_fsmnt);
415 panic("update: rofs mod");
416 }
417 fs->fs_fmod = 0;
418 fs->fs_time = time.tv_sec;
419 error = sbupdate(ump, waitfor);
420 }
421 /*
422 * Write back each (modified) inode.
423 */
424 for (ip = inode; ip < inodeNINODE; ip++) {
425 if (ip->i_devvp != ump->um_devvp ||
426 (ip->i_flag & ILOCKED) != 0 || ITOV(ip)->v_count == 0 ||
427 (ip->i_flag & (IMOD|IACC|IUPD|ICHG)) == 0)
428 continue;
9676d794 429 ILOCK(ip);
8fe1c702 430 VREF(ITOV(ip));
7188ac27
KM
431 error = iupdat(ip, &time, &time, waitfor == MNT_WAIT);
432 iput(ip);
433 }
434 updlock = 0;
435 /*
436 * Force stale buffer cache information to be flushed.
437 */
438 bflush(ump->um_devvp->v_rdev);
439 return (error);
440}
441
442/*
443 * Write a superblock and associated information back to disk.
444 */
445sbupdate(mp, waitfor)
446 struct ufsmount *mp;
447 int waitfor;
448{
449 register struct fs *fs = mp->um_fs;
71e4e98b
SL
450 register struct buf *bp;
451 int blks;
452 caddr_t space;
7188ac27 453 int i, size, error = 0;
71e4e98b 454
ec67a3ce
MK
455#ifdef SECSIZE
456 bp = getblk(mp->m_dev, (daddr_t)fsbtodb(fs, SBOFF / fs->fs_fsize),
457 (int)fs->fs_sbsize, fs->fs_dbsize);
458#else SECSIZE
7188ac27 459 bp = getblk(mp->um_devvp, SBLOCK, (int)fs->fs_sbsize);
ec67a3ce 460#endif SECSIZE
71e4e98b 461 bcopy((caddr_t)fs, bp->b_un.b_addr, (u_int)fs->fs_sbsize);
2af59000
KM
462 /* Restore compatibility to old file systems. XXX */
463 if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
464 bp->b_un.b_fs->fs_nrpos = -1; /* XXX */
ec67a3ce
MK
465#ifdef SECSIZE
466#ifdef tahoe
467 /* restore standard fsbtodb shift */
468 bp->b_un.b_fs->fs_fsbtodb = fs->fs_sparecon[0];
469 bp->b_un.b_fs->fs_sparecon[0] = 0;
470#endif
471#endif SECSIZE
7188ac27
KM
472 if (waitfor == MNT_WAIT)
473 error = bwrite(bp);
474 else
475 bawrite(bp);
71e4e98b
SL
476 blks = howmany(fs->fs_cssize, fs->fs_fsize);
477 space = (caddr_t)fs->fs_csp[0];
478 for (i = 0; i < blks; i += fs->fs_frag) {
479 size = fs->fs_bsize;
480 if (i + fs->fs_frag > blks)
481 size = (blks - i) * fs->fs_fsize;
ec67a3ce
MK
482#ifdef SECSIZE
483 bp = getblk(mp->m_dev, fsbtodb(fs, fs->fs_csaddr + i), size,
484 fs->fs_dbsize);
485#else SECSIZE
7188ac27 486 bp = getblk(mp->um_devvp, fsbtodb(fs, fs->fs_csaddr + i), size);
ec67a3ce 487#endif SECSIZE
71e4e98b
SL
488 bcopy(space, bp->b_un.b_addr, (u_int)size);
489 space += size;
7188ac27
KM
490 if (waitfor == MNT_WAIT)
491 error = bwrite(bp);
492 else
493 bawrite(bp);
71e4e98b 494 }
7188ac27 495 return (error);
71e4e98b
SL
496}
497
498/*
7188ac27
KM
499 * Print out statistics on the current allocation of the buffer pool.
500 * Can be enabled to print out on every ``sync'' by setting "syncprt"
501 * above.
502 */
503bufstats()
504{
505 int s, i, j, count;
506 register struct buf *bp, *dp;
507 int counts[MAXBSIZE/CLBYTES+1];
508 static char *bname[BQUEUES] = { "LOCKED", "LRU", "AGE", "EMPTY" };
509
510 for (bp = bfreelist, i = 0; bp < &bfreelist[BQUEUES]; bp++, i++) {
511 count = 0;
512 for (j = 0; j <= MAXBSIZE/CLBYTES; j++)
513 counts[j] = 0;
514 s = splbio();
515 for (dp = bp->av_forw; dp != bp; dp = dp->av_forw) {
516 counts[dp->b_bufsize/CLBYTES]++;
517 count++;
518 }
519 splx(s);
520 printf("%s: total-%d", bname[i], count);
521 for (j = 0; j <= MAXBSIZE/CLBYTES; j++)
522 if (counts[j] != 0)
523 printf(", %d-%d", j * CLBYTES, counts[j]);
524 printf("\n");
525 }
526}
527
528/*
529 * File handle to vnode
530 */
531ufs_fhtovp(mp, fhp, vpp)
532 struct mount *mp;
533 struct fid *fhp;
534 struct vnode **vpp;
535{
536 register struct ufid *ufhp;
537 struct inode tip, *ip;
538 int error;
539
540 ufhp = (struct ufid *)fhp;
541 tip.i_dev = VFSTOUFS(mp)->um_dev;
542 tip.i_vnode.v_mount = mp;
543 if (error = iget(&tip, ufhp->ufid_ino, &ip)) {
544 *vpp = NULL;
545 return (error);
546 }
547 if (ip->i_gen != ufhp->ufid_gen) {
548 iput(ip);
549 *vpp = NULL;
550 return (EINVAL);
551 }
552 *vpp = ITOV(ip);
553 return (0);
554}
555
556/*
557 * Vnode pointer to File handle, should never happen.
558 */
559/* ARGSUSED */
758e3529
KM
560ufs_vptofh(vp, fhp)
561 struct vnode *vp;
7188ac27 562 struct fid *fhp;
7188ac27 563{
758e3529
KM
564 register struct inode *ip = VTOI(vp);
565 register struct ufid *ufhp;
7188ac27 566
758e3529
KM
567 ufhp = (struct ufid *)fhp;
568 ufhp->ufid_len = sizeof(struct ufid);
569 ufhp->ufid_ino = ip->i_number;
570 ufhp->ufid_gen = ip->i_gen;
571 return (0);
7188ac27
KM
572}
573
574/*
575 * Common code for mount and quota.
71e4e98b
SL
576 * Check that the user's argument is a reasonable
577 * thing on which to mount, and return the device number if so.
578 */
7188ac27
KM
579getmdev(devvpp, fname, ndp)
580 struct vnode **devvpp;
715baff1 581 caddr_t fname;
7188ac27 582 register struct nameidata *ndp;
71e4e98b 583{
7188ac27
KM
584 register struct vnode *vp;
585 int error;
71e4e98b 586
7188ac27 587 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
715baff1
KM
588 ndp->ni_segflg = UIO_USERSPACE;
589 ndp->ni_dirp = fname;
7188ac27
KM
590 if (error = namei(ndp)) {
591 if (error == ENOENT)
592 return (ENODEV); /* needs translation */
593 return (error);
6d07f4cd 594 }
7188ac27
KM
595 vp = ndp->ni_vp;
596 if (vp->v_type != VBLK) {
597 vput(vp);
71e4e98b 598 return (ENOTBLK);
e95cdf5c 599 }
7188ac27 600 if (major(vp->v_rdev) >= nblkdev)
71e4e98b 601 return (ENXIO);
7188ac27
KM
602 iunlock(VTOI(vp));
603 *devvpp = vp;
71e4e98b
SL
604 return (0);
605}