/* * nwfs_getattr call from vfs. * * nwfs_getattr(struct vnode *a_vp, struct vattr *a_vap) */ static int nwfs_getattr(struct vop_getattr_args *ap) { thread_t td = curthread; /* XXX */ struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); struct vattr *va=ap->a_vap; struct nwmount *nmp = VTONWFS(vp); struct nw_entry_info fattr; int error; u_int32_t oldsize; NCPVNDEBUG("%lx:%d: '%s' %d\n", (long)vp, nmp->n_volume, np->n_name, (vp->v_flag & VROOT) != 0); error = nwfs_attr_cachelookup(vp,va); if (!error) return 0; NCPVNDEBUG("not in cache\n"); oldsize = np->n_size; if (np->n_flag & NVOLUME) { error = ncp_obtain_info(nmp, np->n_fid.f_id, 0, NULL, &fattr, td,proc0.p_ucred); } else { error = ncp_obtain_info(nmp, np->n_fid.f_parent, np->n_nmlen, np->n_name, &fattr, td, proc0.p_ucred); } if (error) { NCPVNDEBUG("error %d\n", error); return error; } nwfs_attr_cacheenter(vp, &fattr); *va = np->n_vattr; if (np->opened) np->n_size = oldsize; return (0); }
int nwfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag) { struct nwmount *nmp = VTONWFS(vp); struct nwnode *np = VTONW(vp); struct thread *td; /* struct vattr vattr;*/ int error = 0; if (vp->v_type != VREG) { kprintf("%s: vn types other than VREG unsupported !\n",__func__); return EIO; } NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); if (uiop->uio_offset < 0) return EINVAL; td = uiop->uio_td; if (ioflag & (IO_APPEND | IO_SYNC)) { if (np->n_flag & NMODIFIED) { nwfs_attr_cacheremove(vp); error = nwfs_vinvalbuf(vp, V_SAVE, 1); if (error) return (error); } if (ioflag & IO_APPEND) { /* We can relay only on local information about file size, * because until file is closed NetWare will not return * the correct size. */ #if 0 /* notyet */ nwfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr); if (error) return (error); #endif uiop->uio_offset = np->n_size; } } if (uiop->uio_resid == 0) return 0; if (td->td_proc && uiop->uio_offset + uiop->uio_resid > td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) { lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ); return (EFBIG); } error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, uiop, cred); NCPVNDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); if (!error) { if (uiop->uio_offset > np->n_size) { np->n_vattr.va_size = np->n_size = uiop->uio_offset; vnode_pager_setsize(vp, np->n_size); } } return (error); }
/* * nwfs_close(struct vnode *a_vp, int a_fflag) */ static int nwfs_close(struct vop_close_args *ap) { thread_t td = curthread; /* XXX */ struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); int error; NCPVNDEBUG("name=%s,td=%p,c=%d\n",np->n_name,ap->a_td,np->opened); vn_lock(vp, LK_UPGRADE | LK_RETRY); error = 0; if (vp->v_type == VDIR) goto done; if (np->opened == 0) goto done; error = nwfs_vinvalbuf(vp, V_SAVE, 1); if (np->opened == 0) { error = 0; /* huh? */ goto done; } if (--np->opened == 0) { error = ncp_close_file(NWFSTOCONN(VTONWFS(vp)), &np->n_fh, td, proc0.p_ucred); } np->n_atime = 0; done: vop_stdclose(ap); return (error); }
/* * nwfs_read call. * * nwfs_read(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, * struct ucred *a_cred) */ static int nwfs_read(struct vop_read_args *ap) { struct vnode *vp = ap->a_vp; struct uio *uio=ap->a_uio; int error; NCPVNDEBUG("nwfs_read:\n"); if (vp->v_type != VREG && vp->v_type != VDIR) return (EPERM); error = nwfs_readvnode(vp, uio, ap->a_cred); return error; }
/* * nwfs_access vnode op * * nwfs_access(struct vnode *a_vp, int a_mode, struct ucred *a_cred) */ static int nwfs_access(struct vop_access_args *ap) { struct vnode *vp = ap->a_vp; struct nwmount *nmp = VTONWFS(vp); int error; int mode; NCPVNDEBUG("\n"); mode = ((vp->v_type == VREG) ? nmp->m.file_mode : nmp->m.dir_mode); error = vop_helper_access(ap, nmp->m.uid, nmp->m.gid, mode, 0); return (error); }
/* * nwfs_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, * struct ucred *a_cred) */ static int nwfs_write(struct vop_write_args *ap) { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; int error; NCPVNDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid); if (vp->v_type != VREG) return (EPERM); error = nwfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag); return(error); }
/* * nwfs_setattr call from vfs. * * nwfs_setattr(struct vnode *a_vp, struct vattr *a_vap, struct ucred *a_cred) */ static int nwfs_setattr(struct vop_setattr_args *ap) { thread_t td = curthread; /* XXX */ struct vnode *vp = ap->a_vp; struct nwnode *np = VTONW(vp); struct vattr *vap = ap->a_vap; u_quad_t tsize=0; int error = 0; NCPVNDEBUG("\n"); if (vap->va_flags != VNOVAL) return (EOPNOTSUPP); /* * Disallow write attempts if the filesystem is mounted read-only. */ if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) &&(vp->v_mount->mnt_flag & MNT_RDONLY)) return (EROFS); if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return (EISDIR); case VREG: /* * Disallow write attempts if the filesystem is * mounted read-only. */ if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); vnode_pager_setsize(vp, (u_long)vap->va_size); tsize = np->n_size; np->n_size = vap->va_size; break; default: return EINVAL; } } error = ncp_setattr(vp, vap, ap->a_cred, td); if (error && vap->va_size != VNOVAL) { np->n_size = tsize; vnode_pager_setsize(vp, (u_long)tsize); } np->n_atime = 0; /* invalidate cache */ VOP_GETATTR(vp, vap); np->n_mtime = vap->va_mtime.tv_sec; return (0); }
/* * nwfs_strategy(struct vnode *a_vp, struct bio *a_bio) */ static int nwfs_strategy(struct vop_strategy_args *ap) { struct bio *bio = ap->a_bio; int error = 0; struct thread *td = NULL; NCPVNDEBUG("\n"); if ((bio->bio_flags & BIO_SYNC)) td = curthread; /* YYY dunno if this is legal */ /* * If the op is asynchronous and an i/o daemon is waiting * queue the request, wake it up and wait for completion * otherwise just do it ourselves. */ if (bio->bio_flags & BIO_SYNC) error = nwfs_doio(ap->a_vp, bio, proc0.p_ucred, td); return (error); }
/* * nwfs_create call * Create a regular file. On entry the directory to contain the file being * created is locked. We must release before we return. * * nwfs_create(struct vnode *a_dvp, struct vnode **a_vpp, * struct componentname *a_cnpl, struct vattr *a_vap) */ static int nwfs_create(struct vop_old_create_args *ap) { struct vnode *dvp = ap->a_dvp; struct vattr *vap = ap->a_vap; struct vnode **vpp=ap->a_vpp; struct componentname *cnp = ap->a_cnp; struct vnode *vp = NULL; int error = 0, fmode; struct vattr vattr; struct nwnode *np; struct ncp_open_info no; struct nwmount *nmp=VTONWFS(dvp); ncpfid fid; NCPVNDEBUG("\n"); *vpp = NULL; if (vap->va_type == VSOCK) return (EOPNOTSUPP); if ((error = VOP_GETATTR(dvp, &vattr))) { return (error); } fmode = AR_READ | AR_WRITE; /* if (vap->va_vaflags & VA_EXCLUSIVE) fmode |= AR_DENY_READ | AR_DENY_WRITE;*/ error = ncp_open_create_file_or_subdir(nmp,dvp,cnp->cn_namelen,cnp->cn_nameptr, OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, 0, fmode, &no, cnp->cn_td, cnp->cn_cred); if (!error) { error = ncp_close_file(NWFSTOCONN(nmp), &no.fh, cnp->cn_td,cnp->cn_cred); fid.f_parent = VTONW(dvp)->n_fid.f_id; fid.f_id = no.fattr.dirEntNum; error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &vp); if (!error) { np = VTONW(vp); np->opened = 0; *vpp = vp; } } return (error); }
static int nwfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred) { struct nwmount *nmp = VTONWFS(vp); int error, count, i; struct dirent dp; struct nwnode *np = VTONW(vp); struct nw_entry_info fattr; struct vnode *newvp; struct componentname cn; ncpfid fid; np = VTONW(vp); NCPVNDEBUG("dirname='%s'\n",np->n_name); if (uio->uio_resid < DE_SIZE || (uio->uio_offset < 0)) return (EINVAL); error = 0; count = 0; i = uio->uio_offset / DE_SIZE; /* offset in directory */ if (i == 0) { error = ncp_initsearch(vp, uio->uio_td, cred); if (error) { NCPVNDEBUG("cannot initialize search, error=%d",error); return( error ); } } for (; uio->uio_resid >= DE_SIZE; i++) { bzero((char *) &dp, DE_SIZE); dp.d_reclen = DE_SIZE; switch (i) { case 0: /* `.' */ case 1: /* `..' */ dp.d_fileno = (i == 0) ? np->n_fid.f_id : np->n_parent.f_id; if (!dp.d_fileno) dp.d_fileno = NWFS_ROOT_INO; dp.d_namlen = i + 1; dp.d_name[0] = '.'; dp.d_name[1] = '.'; dp.d_name[i + 1] = '\0'; dp.d_type = DT_DIR; break; default: error = ncp_search_for_file_or_subdir(nmp, &np->n_seq, &fattr, uio->uio_td, cred); if (error && error < 0x80) break; dp.d_fileno = fattr.dirEntNum; dp.d_type = (fattr.attributes & aDIR) ? DT_DIR : DT_REG; dp.d_namlen = fattr.nameLen; bcopy(fattr.entryName, dp.d_name, dp.d_namlen); dp.d_name[dp.d_namlen] = '\0'; #if 0 if (error && eofflag) { /* *eofflag = 1;*/ break; } #endif break; } if (nwfs_fastlookup && !error && i > 1) { fid.f_id = fattr.dirEntNum; fid.f_parent = np->n_fid.f_id; error = nwfs_nget(vp->v_mount, fid, &fattr, vp, &newvp); if (!error) { VTONW(newvp)->n_ctime = VTONW(newvp)->n_vattr.va_ctime.tv_sec; cn.cn_nameptr = dp.d_name; cn.cn_namelen = dp.d_namlen; cache_enter(vp, newvp, &cn); vput(newvp); } else error = 0; } if (error >= 0x80) { error = 0; break; } if ((error = uiomove(&dp, DE_SIZE, uio))) break; } uio->uio_offset = i * DE_SIZE; return (error); }
/* * How to keep the brain busy ... * Currently lookup routine can make two lookup for vnode. This can be * avoided by reorg the code. * * nwfs_lookup(struct vnode *a_dvp, struct vnode **a_vpp, * struct componentname *a_cnp) */ int nwfs_lookup(struct vop_old_lookup_args *ap) { struct componentname *cnp = ap->a_cnp; struct vnode *dvp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; int flags = cnp->cn_flags; struct vnode *vp; struct nwmount *nmp; struct mount *mp = dvp->v_mount; struct nwnode *dnp, *npp; struct nw_entry_info fattr, *fap; ncpfid fid; int nameiop=cnp->cn_nameiop; int lockparent, wantparent, error = 0, notfound; struct thread *td = cnp->cn_td; char _name[cnp->cn_namelen+1]; bcopy(cnp->cn_nameptr,_name,cnp->cn_namelen); _name[cnp->cn_namelen]=0; if (dvp->v_type != VDIR) return (ENOTDIR); if ((flags & CNP_ISDOTDOT) && (dvp->v_flag & VROOT)) { kprintf("nwfs_lookup: invalid '..'\n"); return EIO; } NCPVNDEBUG("%d '%s' in '%s' id=d\n", nameiop, _name, VTONW(dvp)->n_name/*, VTONW(dvp)->n_name*/); if ((mp->mnt_flag & MNT_RDONLY) && nameiop != NAMEI_LOOKUP) return (EROFS); if ((error = VOP_EACCESS(dvp, VEXEC, cnp->cn_cred))) return (error); lockparent = flags & CNP_LOCKPARENT; wantparent = flags & (CNP_LOCKPARENT | CNP_WANTPARENT); nmp = VFSTONWFS(mp); dnp = VTONW(dvp); /* kprintf("dvp %d:%d:%d\n", (int)mp, (int)dvp->v_flag & VROOT, (int)flags & CNP_ISDOTDOT); */ error = ncp_pathcheck(cnp->cn_nameptr, cnp->cn_namelen, &nmp->m.nls, (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && (nmp->m.nls.opt & NWHP_NOSTRICT) == 0); if (error) return ENOENT; error = 0; *vpp = NULLVP; fap = NULL; if (flags & CNP_ISDOTDOT) { if (NWCMPF(&dnp->n_parent, &nmp->n_rootent)) { fid = nmp->n_rootent; fap = NULL; notfound = 0; } else { error = nwfs_lookupnp(nmp, dnp->n_parent, td, &npp); if (error) { return error; } fid = dnp->n_parent; fap = &fattr; /*np = *npp;*/ notfound = ncp_obtain_info(nmp, npp->n_dosfid, 0, NULL, fap, td, cnp->cn_cred); } } else { fap = &fattr; notfound = ncp_lookup(dvp, cnp->cn_namelen, cnp->cn_nameptr, fap, td, cnp->cn_cred); fid.f_id = fap->dirEntNum; if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { fid.f_parent = dnp->n_fid.f_parent; } else fid.f_parent = dnp->n_fid.f_id; NCPVNDEBUG("call to ncp_lookup returned=%d\n",notfound); } if (notfound && notfound < 0x80 ) return (notfound); /* hard error */ if (notfound) { /* entry not found */ /* Handle RENAME or CREATE case... */ if ((nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && wantparent) { if (!lockparent) vn_unlock(dvp); return (EJUSTRETURN); } return ENOENT; }/* else { NCPVNDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); }*/ /* handle DELETE case ... */ if (nameiop == NAMEI_DELETE) { /* delete last component */ error = VOP_EACCESS(dvp, VWRITE, cnp->cn_cred); if (error) return (error); if (NWCMPF(&dnp->n_fid, &fid)) { /* we found ourselfs */ vref(dvp); *vpp = dvp; return 0; } error = nwfs_nget(mp, fid, fap, dvp, &vp); if (error) return (error); *vpp = vp; if (!lockparent) vn_unlock(dvp); return (0); } if (nameiop == NAMEI_RENAME && wantparent) { error = VOP_EACCESS(dvp, VWRITE, cnp->cn_cred); if (error) return (error); if (NWCMPF(&dnp->n_fid, &fid)) return EISDIR; error = nwfs_nget(mp, fid, fap, dvp, &vp); if (error) return (error); *vpp = vp; if (!lockparent) vn_unlock(dvp); return (0); } if (flags & CNP_ISDOTDOT) { vn_unlock(dvp); /* race to get the inode */ error = nwfs_nget(mp, fid, NULL, NULL, &vp); if (error) { vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); return (error); } if (lockparent) { error = vn_lock(dvp, LK_EXCLUSIVE | LK_FAILRECLAIM); if (error) { vput(vp); return (error); } } *vpp = vp; } else if (NWCMPF(&dnp->n_fid, &fid)) { vref(dvp); *vpp = dvp; } else { error = nwfs_nget(mp, fid, fap, dvp, &vp); if (error) return (error); *vpp = vp; NCPVNDEBUG("lookup: getnewvp!\n"); if (!lockparent) vn_unlock(dvp); } #if 0 /* XXX MOVE TO NREMOVE */ if ((cnp->cn_flags & CNP_MAKEENTRY)) { VTONW(*vpp)->n_ctime = VTONW(*vpp)->n_vattr.va_ctime.tv_sec; /* XXX */ } #endif return (0); }
/* ARGSUSED */ static int nwfs_open(struct vop_open_args *ap) { thread_t td = curthread; /* XXX */ struct vnode *vp = ap->a_vp; int mode = ap->a_mode; struct nwnode *np = VTONW(vp); struct ncp_open_info no; struct nwmount *nmp = VTONWFS(vp); struct vattr vattr; int error, nwm; NCPVNDEBUG("%s,%d\n",np->n_name, np->opened); if (vp->v_type != VREG && vp->v_type != VDIR) { NCPFATAL("open vtype = %d\n", vp->v_type); return (EACCES); } if (vp->v_type == VDIR) return 0; /* nothing to do now */ if (np->n_flag & NMODIFIED) { if ((error = nwfs_vinvalbuf(vp, V_SAVE, 1)) == EINTR) return (error); np->n_atime = 0; error = VOP_GETATTR(vp, &vattr); if (error) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } else { error = VOP_GETATTR(vp, &vattr); if (error) return (error); if (np->n_mtime != vattr.va_mtime.tv_sec) { if ((error = nwfs_vinvalbuf(vp, V_SAVE, 1)) == EINTR) return (error); np->n_mtime = vattr.va_mtime.tv_sec; } } if (np->opened) { np->opened++; return (vop_stdopen(ap)); } nwm = AR_READ; if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) nwm |= AR_WRITE; error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0, nwm, &no, td, ap->a_cred); if (error) { if (mode & FWRITE) return EACCES; nwm = AR_READ; error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0, nwm, &no, td, ap->a_cred); } np->n_atime = 0; if (error == 0) { np->opened++; np->n_fh = no.fh; np->n_origfh = no.origfh; error = vop_stdopen(ap); } return (error); }
static int nwfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred) { struct nwmount *nmp = VTONWFS(vp); int error, i; struct nwnode *np; struct nw_entry_info fattr; struct vnode *newvp; ncpfid fid; ino_t d_ino; size_t d_namlen; const char *d_name; uint8_t d_type; np = VTONW(vp); NCPVNDEBUG("dirname='%s'\n",np->n_name); if (uio->uio_offset < 0 || uio->uio_offset > INT_MAX) return (EINVAL); error = 0; i = (int)uio->uio_offset; /* offset in directory */ if (i == 0) { error = ncp_initsearch(vp, uio->uio_td, cred); if (error) { NCPVNDEBUG("cannot initialize search, error=%d",error); return( error ); } } for (; !error && uio->uio_resid > 0; i++) { switch (i) { case 0: /* `.' */ d_ino = np->n_fid.f_id; if (d_ino == 0) d_ino = NWFS_ROOT_INO; d_namlen = 1; d_name = "."; d_type = DT_DIR; break; case 1: /* `..' */ d_ino = np->n_parent.f_id; if (d_ino == 0) d_ino = NWFS_ROOT_INO; d_namlen = 2; d_name = ".."; d_type = DT_DIR; break; default: error = ncp_search_for_file_or_subdir(nmp, &np->n_seq, &fattr, uio->uio_td, cred); if (error && error < 0x80) goto done; d_ino = fattr.dirEntNum; d_type = (fattr.attributes & aDIR) ? DT_DIR : DT_REG; d_namlen = fattr.nameLen; d_name = fattr.entryName; #if 0 if (error && eofflag) { /* *eofflag = 1;*/ break; } #endif break; } if (nwfs_fastlookup && !error && i > 1) { fid.f_id = fattr.dirEntNum; fid.f_parent = np->n_fid.f_id; error = nwfs_nget(vp->v_mount, fid, &fattr, vp, &newvp); if (!error) { VTONW(newvp)->n_ctime = VTONW(newvp)->n_vattr.va_ctime.tv_sec; vput(newvp); } else error = 0; } if (error >= 0x80) { error = 0; break; } if (vop_write_dirent(&error, uio, d_ino, d_type, d_namlen, d_name)) break; } done: uio->uio_offset = i; return (error); }
/* * Vnode op for VM putpages. * possible bug: all IO done in sync mode * Note that vop_close always invalidate pages before close, so it's * not necessary to open vnode. * * nwfs_putpages(struct vnode *a_vp, vm_page_t *a_m, int a_count, * int a_sync, int *a_rtvals, vm_ooffset_t a_offset) */ int nwfs_putpages(struct vop_putpages_args *ap) { int error; struct thread *td = curthread; /* XXX */ struct vnode *vp = ap->a_vp; struct ucred *cred; #ifndef NWFS_RWCACHE KKASSERT(td->td_proc); cred = td->td_proc->p_ucred; /* XXX */ VOP_OPEN(vp, FWRITE, cred, NULL); error = vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count, ap->a_sync, ap->a_rtvals); VOP_CLOSE(vp, FWRITE, cred); return error; #else struct uio uio; struct iovec iov; vm_offset_t kva; struct buf *bp; int i, npages, count; int *rtvals; struct nwmount *nmp; struct nwnode *np; vm_page_t *pages; KKASSERT(td->td_proc); cred = td->td_proc->p_ucred; /* XXX */ /* VOP_OPEN(vp, FWRITE, cred, NULL);*/ np = VTONW(vp); nmp = VFSTONWFS(vp->v_mount); pages = ap->a_m; count = ap->a_count; rtvals = ap->a_rtvals; npages = btoc(count); for (i = 0; i < npages; i++) { rtvals[i] = VM_PAGER_AGAIN; } bp = getpbuf_kva(&nwfs_pbuf_freecnt); kva = (vm_offset_t) bp->b_data; pmap_qenter(kva, pages, npages); iov.iov_base = (caddr_t) kva; iov.iov_len = count; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = IDX_TO_OFF(pages[0]->pindex); uio.uio_resid = count; uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = UIO_WRITE; uio.uio_td = td; NCPVNDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid); error = ncp_write(NWFSTOCONN(nmp), &np->n_fh, &uio, cred); /* VOP_CLOSE(vp, FWRITE, cred);*/ NCPVNDEBUG("paged write done: %d\n", error); pmap_qremove(kva, npages); relpbuf(bp, &nwfs_pbuf_freecnt); if (!error) { int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE; for (i = 0; i < nwritten; i++) { rtvals[i] = VM_PAGER_OK; vm_page_undirty(pages[i]); } } return rtvals[0]; #endif /* NWFS_RWCACHE */ }