int smbfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag) { struct smbmount *smp = VTOSMBFS(vp); struct smbnode *np = VTOSMB(vp); struct smb_cred *scred; struct thread *td; int error = 0; if (vp->v_type != VREG) { SMBERROR("vn types other than VREG unsupported !\n"); return EIO; } SMBVDEBUG("ofs=%jd,resid=%zd\n", (intmax_t)uiop->uio_offset, uiop->uio_resid); if (uiop->uio_offset < 0) return EINVAL; /* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize) return (EFBIG);*/ td = uiop->uio_td; if (ioflag & (IO_APPEND | IO_SYNC)) { if (np->n_flag & NMODIFIED) { smbfs_attr_cacheremove(vp); error = smbfs_vinvalbuf(vp, td); if (error) return error; } if (ioflag & IO_APPEND) { #ifdef notyet /* * File size can be changed by another client */ smbfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr, cred); if (error) return (error); #endif uiop->uio_offset = np->n_size; } } if (uiop->uio_resid == 0) return 0; if (vn_rlimit_fsize(vp, uiop, td)) return (EFBIG); scred = smbfs_malloc_scred(); smb_makescred(scred, td, cred); error = smb_write(smp->sm_share, np->n_fid, uiop, scred); smbfs_free_scred(scred); SMBVDEBUG("after: ofs=%jd,resid=%zd\n", (intmax_t)uiop->uio_offset, uiop->uio_resid); if (!error) { if (uiop->uio_offset > np->n_size) { np->n_size = uiop->uio_offset; vnode_pager_setsize(vp, np->n_size); } } return error; }
int smbfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag) { struct thread *td; struct smbmount *smp = VTOSMBFS(vp); struct smbnode *np = VTOSMB(vp); struct smb_cred scred; int error = 0; if (vp->v_type != VREG) { SMBERROR("vn types other than VREG unsupported !\n"); return EIO; } SMBVDEBUG("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) { smbfs_attr_cacheremove(vp); error = smbfs_vinvalbuf(vp, V_SAVE, 1); if (error) return error; } if (ioflag & IO_APPEND) { #if 0 /* notyet */ /* * File size can be changed by another client */ smbfs_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; } smb_makescred(&scred, td, cred); error = smb_write(smp->sm_share, np->n_fid, uiop, &scred); SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); if (!error) { if (uiop->uio_offset > np->n_size) { np->n_size = uiop->uio_offset; vnode_pager_setsize(vp, np->n_size); } } return error; }
int smbfs_access(void *v) { struct vop_access_args /* { struct vnode *a_vp; int a_mode; kauth_cred_t a_cred; } */ *ap = v; struct vnode *vp = ap->a_vp; struct smbnode *np = VTOSMB(vp); u_int acc_mode = ap->a_mode; int error; #ifdef SMB_VNODE_DEBUG struct smbmount *smp = VTOSMBFS(vp); #endif SMBVDEBUG("file '%.*s', node mode=%o, acc mode=%x\n", (int) np->n_nmlen, np->n_name, (vp->v_type == VDIR) ? smp->sm_args.dir_mode : smp->sm_args.file_mode, acc_mode); error = smbfs_check_possible(vp, np, acc_mode); if (error) return error; error = smbfs_check_permitted(vp, np, acc_mode, ap->a_cred); return error; }
/*ARGSUSED*/ int smbfs_init(struct vfsconf *vfsp) { smbfs_pbuf_freecnt = nswbuf_kva / 2 + 1; SMBVDEBUG("done.\n"); return 0; }
/*ARGSUSED*/ int smbfs_init(struct vfsconf *vfsp) { #ifdef SMBFS_USEZONE smbfsmount_zone = zinit("SMBFSMOUNT", sizeof(struct smbmount), 0, 0, 1); #endif smbfs_pbuf_freecnt = nswbuf / 2 + 1; SMBVDEBUG("done.\n"); return 0; }
/* Unmount the filesystem described by mp. */ static int smbfs_unmount(struct mount *mp, int mntflags) { struct thread *td; struct smbmount *smp = VFSTOSMBFS(mp); struct smb_cred *scred; struct smb_dev *dev; int error, flags; SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); td = curthread; flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; /* * Keep trying to flush the vnode list for the mount while * some are still busy and we are making progress towards * making them not busy. This is needed because smbfs vnodes * reference their parent directory but may appear after their * parent in the list; one pass over the vnode list is not * sufficient in this case. */ do { smp->sm_didrele = 0; /* There is 1 extra root vnode reference from smbfs_mount(). */ error = vflush(mp, 1, flags, td); } while (error == EBUSY && smp->sm_didrele != 0); if (error) return error; scred = smbfs_malloc_scred(); smb_makescred(scred, td, td->td_ucred); error = smb_share_lock(smp->sm_share); if (error) goto out; smb_share_put(smp->sm_share, scred); SMB_LOCK(); dev = smp->sm_dev; if (!dev) panic("No private data for mount point"); sdp_trydestroy(dev); mp->mnt_data = NULL; SMB_UNLOCK(); free(smp, M_SMBFSDATA); MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_LOCAL; MNT_IUNLOCK(mp); out: smbfs_free_scred(scred); return error; }
/* * smbfs_getattr call from vfs. */ int smbfs_getattr(void *v) { struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; kauth_cred_t a_cred; } */ *ap = v; struct vnode *vp = ap->a_vp; struct smbnode *np = VTOSMB(vp); struct vattr *va=ap->a_vap; struct smbfattr fattr; struct smb_cred scred; u_quad_t oldsize; int error; SMBVDEBUG("%p: '%.*s' isroot %d\n", vp, (int) np->n_nmlen, np->n_name, (vp->v_vflag & VV_ROOT) != 0); if ((error = smbfs_attr_cachelookup(vp, va)) == 0) return (0); SMBVDEBUG0("not in the cache\n"); smb_makescred(&scred, curlwp, ap->a_cred); oldsize = np->n_size; error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred); if (error) { SMBVDEBUG("error %d\n", error); return error; } smbfs_attr_cacheenter(vp, &fattr); smbfs_attr_cachelookup(vp, va); if ((np->n_flag & NOPEN) != 0) np->n_size = oldsize; return 0; }
int smbfs_write(void *v) { struct vop_write_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; kauth_cred_t a_cred; } */ *ap = v; struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; SMBVDEBUG("%d,ofs=%lld,sz=%zu\n",vp->v_type, (long long int)uio->uio_offset, uio->uio_resid); if (vp->v_type != VREG) return (EPERM); return smbfs_writevnode(vp, uio, ap->a_cred, ap->a_ioflag); }
/* Unmount the filesystem described by mp. */ int smbfs_unmount(struct mount *mp, int mntflags) { struct lwp *l = curlwp; struct smbmount *smp = VFSTOSMBFS(mp); struct smb_cred scred; struct vnode *smbfs_rootvp = SMBTOV(smp->sm_root); int error, flags; SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; if (smbfs_rootvp->v_usecount > 1 && (mntflags & MNT_FORCE) == 0) return EBUSY; /* Flush all vnodes. * Keep trying to flush the vnode list for the mount while * some are still busy and we are making progress towards * making them not busy. This is needed because smbfs vnodes * reference their parent directory but may appear after their * parent in the list; one pass over the vnode list is not * sufficient in this case. */ do { smp->sm_didrele = 0; error = vflush(mp, smbfs_rootvp, flags); } while (error == EBUSY && smp->sm_didrele != 0); if (error) return error; vgone(smbfs_rootvp); smb_makescred(&scred, l, l->l_cred); smb_share_lock(smp->sm_share); smb_share_put(smp->sm_share, &scred); mp->mnt_data = NULL; hashdone(smp->sm_hash, HASH_LIST, smp->sm_hashlen); mutex_destroy(&smp->sm_hashlock); free(smp, M_SMBFSDATA); return 0; }
/* Unmount the filesystem described by mp. */ static int smbfs_unmount(struct mount *mp, int mntflags) { struct smbmount *smp = VFSTOSMBFS(mp); struct smb_cred scred; int error, flags; SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; /* * Keep trying to flush the vnode list for the mount while * some are still busy and we are making progress towards * making them not busy. This is needed because smbfs vnodes * reference their parent directory but may appear after their * parent in the list; one pass over the vnode list is not * sufficient in this case. */ do { smp->sm_didrele = 0; /* There is 1 extra root vnode reference from smbfs_mount(). */ error = vflush(mp, 1, flags); } while (error == EBUSY && smp->sm_didrele != 0); if (error) return error; smb_makescred(&scred, curthread, smp->sm_cred); smb_share_put(smp->sm_share, &scred); mp->mnt_data = (qaddr_t)0; if (smp->sm_cred) crfree(smp->sm_cred); if (smp->sm_hash) kfree(smp->sm_hash, M_SMBFSHASH); lockdestroy(&smp->sm_hashlock); #ifdef SMBFS_USEZONE zfree(smbfsmount_zone, smp); #else kfree(smp, M_SMBFSDATA); #endif mp->mnt_flag &= ~MNT_LOCAL; return error; }
/* * smbfs mount vfsop * Set up mount info record and attach it to vfs struct. */ static int smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) { char *data = uap->dataptr; int error; smbnode_t *rtnp = NULL; /* root of this fs */ smbmntinfo_t *smi = NULL; dev_t smbfs_dev; int version; int devfd; zone_t *zone = curproc->p_zone; zone_t *mntzone = NULL; smb_share_t *ssp = NULL; smb_cred_t scred; int flags, sec; STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) return (error); if (mvp->v_type != VDIR) return (ENOTDIR); /* * get arguments * * uap->datalen might be different from sizeof (args) * in a compatible situation. */ STRUCT_INIT(args, get_udatamodel()); bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)); if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen, SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)))) return (EFAULT); /* * Check mount program version */ version = STRUCT_FGET(args, version); if (version != SMBFS_VERSION) { cmn_err(CE_WARN, "mount version mismatch:" " kernel=%d, mount=%d\n", SMBFS_VERSION, version); return (EINVAL); } /* * Deal with re-mount requests. */ if (uap->flags & MS_REMOUNT) { cmn_err(CE_WARN, "MS_REMOUNT not implemented"); return (ENOTSUP); } /* * Check for busy */ mutex_enter(&mvp->v_lock); if (!(uap->flags & MS_OVERLAY) && (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { mutex_exit(&mvp->v_lock); return (EBUSY); } mutex_exit(&mvp->v_lock); /* * Get the "share" from the netsmb driver (ssp). * It is returned with a "ref" (hold) for us. * Release this hold: at errout below, or in * smbfs_freevfs(). */ devfd = STRUCT_FGET(args, devfd); error = smb_dev2share(devfd, &ssp); if (error) { cmn_err(CE_WARN, "invalid device handle %d (%d)\n", devfd, error); return (error); } /* * Use "goto errout" from here on. * See: ssp, smi, rtnp, mntzone */ /* * Determine the zone we're being mounted into. */ zone_hold(mntzone = zone); /* start with this assumption */ if (getzoneid() == GLOBAL_ZONEID) { zone_rele(mntzone); mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); ASSERT(mntzone != NULL); if (mntzone != zone) { error = EBUSY; goto errout; } } /* * Stop the mount from going any further if the zone is going away. */ if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) { error = EBUSY; goto errout; } /* * On a Trusted Extensions client, we may have to force read-only * for read-down mounts. */ if (is_system_labeled()) { void *addr; int ipvers = 0; struct smb_vc *vcp; vcp = SSTOVC(ssp); addr = smb_vc_getipaddr(vcp, &ipvers); error = smbfs_mount_label_policy(vfsp, addr, ipvers, cr); if (error > 0) goto errout; if (error == -1) { /* change mount to read-only to prevent write-down */ vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); } } /* Prevent unload. */ atomic_inc_32(&smbfs_mountcount); /* * Create a mount record and link it to the vfs struct. * No more possiblities for errors from here on. * Tear-down of this stuff is in smbfs_free_smi() * * Compare with NFS: nfsrootvp() */ smi = kmem_zalloc(sizeof (*smi), KM_SLEEP); mutex_init(&smi->smi_lock, NULL, MUTEX_DEFAULT, NULL); cv_init(&smi->smi_statvfs_cv, NULL, CV_DEFAULT, NULL); rw_init(&smi->smi_hash_lk, NULL, RW_DEFAULT, NULL); smbfs_init_hash_avl(&smi->smi_hash_avl); smi->smi_share = ssp; ssp = NULL; /* * Convert the anonymous zone hold acquired via zone_hold() above * into a zone reference. */ zone_init_ref(&smi->smi_zone_ref); zone_hold_ref(mntzone, &smi->smi_zone_ref, ZONE_REF_SMBFS); zone_rele(mntzone); mntzone = NULL; /* * Initialize option defaults */ smi->smi_flags = SMI_LLOCK; smi->smi_acregmin = SEC2HR(SMBFS_ACREGMIN); smi->smi_acregmax = SEC2HR(SMBFS_ACREGMAX); smi->smi_acdirmin = SEC2HR(SMBFS_ACDIRMIN); smi->smi_acdirmax = SEC2HR(SMBFS_ACDIRMAX); /* * All "generic" mount options have already been * handled in vfs.c:domount() - see mntopts stuff. * Query generic options using vfs_optionisset(). */ if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL)) smi->smi_flags |= SMI_INT; if (vfs_optionisset(vfsp, MNTOPT_ACL, NULL)) smi->smi_flags |= SMI_ACL; /* * Get the mount options that come in as smbfs_args, * starting with args.flags (SMBFS_MF_xxx) */ flags = STRUCT_FGET(args, flags); smi->smi_uid = STRUCT_FGET(args, uid); smi->smi_gid = STRUCT_FGET(args, gid); smi->smi_fmode = STRUCT_FGET(args, file_mode) & 0777; smi->smi_dmode = STRUCT_FGET(args, dir_mode) & 0777; /* * Hande the SMBFS_MF_xxx flags. */ if (flags & SMBFS_MF_NOAC) smi->smi_flags |= SMI_NOAC; if (flags & SMBFS_MF_ACREGMIN) { sec = STRUCT_FGET(args, acregmin); if (sec < 0 || sec > SMBFS_ACMINMAX) sec = SMBFS_ACMINMAX; smi->smi_acregmin = SEC2HR(sec); } if (flags & SMBFS_MF_ACREGMAX) { sec = STRUCT_FGET(args, acregmax); if (sec < 0 || sec > SMBFS_ACMAXMAX) sec = SMBFS_ACMAXMAX; smi->smi_acregmax = SEC2HR(sec); } if (flags & SMBFS_MF_ACDIRMIN) { sec = STRUCT_FGET(args, acdirmin); if (sec < 0 || sec > SMBFS_ACMINMAX) sec = SMBFS_ACMINMAX; smi->smi_acdirmin = SEC2HR(sec); } if (flags & SMBFS_MF_ACDIRMAX) { sec = STRUCT_FGET(args, acdirmax); if (sec < 0 || sec > SMBFS_ACMAXMAX) sec = SMBFS_ACMAXMAX; smi->smi_acdirmax = SEC2HR(sec); } /* * Get attributes of the remote file system, * i.e. ACL support, named streams, etc. */ smb_credinit(&scred, cr); error = smbfs_smb_qfsattr(smi->smi_share, &smi->smi_fsa, &scred); smb_credrele(&scred); if (error) { SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error); } /* * We enable XATTR by default (via smbfs_mntopts) * but if the share does not support named streams, * force the NOXATTR option (also clears XATTR). * Caller will set or clear VFS_XATTR after this. */ if ((smi->smi_fsattr & FILE_NAMED_STREAMS) == 0) vfs_setmntopt(vfsp, MNTOPT_NOXATTR, NULL, 0); /* * Ditto ACLs (disable if not supported on this share) */ if ((smi->smi_fsattr & FILE_PERSISTENT_ACLS) == 0) { vfs_setmntopt(vfsp, MNTOPT_NOACL, NULL, 0); smi->smi_flags &= ~SMI_ACL; } /* * Assign a unique device id to the mount */ mutex_enter(&smbfs_minor_lock); do { smbfs_minor = (smbfs_minor + 1) & MAXMIN32; smbfs_dev = makedevice(smbfs_major, smbfs_minor); } while (vfs_devismounted(smbfs_dev)); mutex_exit(&smbfs_minor_lock); vfsp->vfs_dev = smbfs_dev; vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp); vfsp->vfs_data = (caddr_t)smi; vfsp->vfs_fstype = smbfsfstyp; vfsp->vfs_bsize = MAXBSIZE; vfsp->vfs_bcount = 0; smi->smi_vfsp = vfsp; smbfs_zonelist_add(smi); /* undo in smbfs_freevfs */ /* * Create the root vnode, which we need in unmount * for the call to smbfs_check_table(), etc. * Release this hold in smbfs_unmount. */ rtnp = smbfs_node_findcreate(smi, "\\", 1, NULL, 0, 0, &smbfs_fattr0); ASSERT(rtnp != NULL); rtnp->r_vnode->v_type = VDIR; rtnp->r_vnode->v_flag |= VROOT; smi->smi_root = rtnp; /* * NFS does other stuff here too: * async worker threads * init kstats * * End of code from NFS nfsrootvp() */ return (0); errout: vfsp->vfs_data = NULL; if (smi != NULL) smbfs_free_smi(smi); if (mntzone != NULL) zone_rele(mntzone); if (ssp != NULL) smb_share_rele(ssp); return (error); }
static int smbfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) { struct smbfs_args args; /* will hold data from mount request */ struct smbmount *smp = NULL; struct smb_vc *vcp; struct smb_share *ssp = NULL; struct vnode *vp; struct smb_cred scred; int error; char *pc, *pe; if (data == NULL) { kprintf("missing data argument\n"); return EINVAL; } if (mp->mnt_flag & MNT_UPDATE) { kprintf("MNT_UPDATE not implemented"); return EOPNOTSUPP; } error = copyin(data, (caddr_t)&args, sizeof(struct smbfs_args)); if (error) return error; if (args.version != SMBFS_VERSION) { kprintf("mount version mismatch: kernel=%d, mount=%d\n", SMBFS_VERSION, args.version); return EINVAL; } smb_makescred(&scred, curthread, cred); error = smb_dev2share(args.dev, SMBM_EXEC, &scred, &ssp); if (error) { kprintf("invalid device handle %d (%d)\n", args.dev, error); return error; } vcp = SSTOVC(ssp); smb_share_unlock(ssp, 0); mp->mnt_stat.f_iosize = SSTOVC(ssp)->vc_txmax; #ifdef SMBFS_USEZONE smp = zalloc(smbfsmount_zone); #else MALLOC(smp, struct smbmount*, sizeof(*smp), M_SMBFSDATA, M_WAITOK|M_USE_RESERVE); #endif if (smp == NULL) { kprintf("could not alloc smbmount\n"); error = ENOMEM; goto bad; } bzero(smp, sizeof(*smp)); mp->mnt_data = (qaddr_t)smp; smp->sm_cred = crhold(cred); smp->sm_hash = hashinit(desiredvnodes, M_SMBFSHASH, &smp->sm_hashlen); if (smp->sm_hash == NULL) goto bad; lockinit(&smp->sm_hashlock, "smbfsh", 0, 0); smp->sm_share = ssp; smp->sm_root = NULL; smp->sm_args = args; smp->sm_caseopt = args.caseopt; smp->sm_args.file_mode = (smp->sm_args.file_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; smp->sm_args.dir_mode = (smp->sm_args.dir_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; /* simple_lock_init(&smp->sm_npslock);*/ pc = mp->mnt_stat.f_mntfromname; pe = pc + sizeof(mp->mnt_stat.f_mntfromname); bzero(pc, MNAMELEN); *pc++ = '/'; *pc++ = '/'; pc=index(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); if (pc < pe-1) { *(pc++) = '@'; pc = index(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); if (pc < pe - 1) { *(pc++) = '/'; strncpy(pc, ssp->ss_name, pe - pc - 2); } } /* protect against invalid mount points */ smp->sm_args.mount_point[sizeof(smp->sm_args.mount_point) - 1] = '\0'; vfs_getnewfsid(mp); vfs_add_vnodeops(mp, &smbfs_vnode_vops, &mp->mnt_vn_norm_ops); error = smbfs_root(mp, &vp); if (error) goto bad; vn_unlock(vp); SMBVDEBUG("root.v_sysrefs = %d\n", vp->v_sysref.refcnt); #ifdef DIAGNOSTICS SMBERROR("mp=%p\n", mp); #endif return error; bad: if (smp) { if (smp->sm_cred) crfree(smp->sm_cred); if (smp->sm_hash) kfree(smp->sm_hash, M_SMBFSHASH); lockdestroy(&smp->sm_hashlock); #ifdef SMBFS_USEZONE zfree(smbfsmount_zone, smp); #else kfree(smp, M_SMBFSDATA); #endif } if (ssp) smb_share_put(ssp, &scred); return error; }
/* ARGSUSED */ int smbfs_open(void *v) { struct vop_open_args /* { struct vnode *a_vp; int a_mode; kauth_cred_t a_cred; } */ *ap = v; struct lwp *l = curlwp; struct vnode *vp = ap->a_vp; struct smbnode *np = VTOSMB(vp); struct smb_cred scred; struct vattr vattr; u_int32_t sv_caps = SMB_CAPS(SSTOVC(np->n_mount->sm_share)); int error, accmode; SMBVDEBUG("%.*s,%d\n", (int) np->n_nmlen, np->n_name, (np->n_flag & NOPEN) != 0); if (vp->v_type != VREG && vp->v_type != VDIR) { SMBFSERR("open eacces vtype=%d\n", vp->v_type); return EACCES; } if (vp->v_type == VDIR) { if ((sv_caps & SMB_CAP_NT_SMBS) == 0) { np->n_flag |= NOPEN; return 0; } goto do_open; /* skip 'modified' check */ } if (np->n_flag & NMODIFIED) { if ((error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, l, 1)) == EINTR) return error; smbfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr, ap->a_cred); if (error) return error; np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; } else { error = VOP_GETATTR(vp, &vattr, ap->a_cred); if (error) return error; if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) { error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, l, 1); if (error == EINTR) return error; np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; } } do_open: if ((np->n_flag & NOPEN) != 0) return 0; smb_makescred(&scred, l, ap->a_cred); if (vp->v_type == VDIR) error = smbfs_smb_ntcreatex(np, SMB_SM_DENYNONE|SMB_AM_OPENREAD, &scred); else { /* * Use DENYNONE to give unixy semantics of permitting * everything not forbidden by permissions. Ie denial * is up to server with clients/openers needing to use * advisory locks for further control. */ accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD; if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) accmode = SMB_SM_DENYNONE|SMB_AM_OPENRW; error = smbfs_smb_open(np, accmode, &scred); if (error) { if (ap->a_mode & FWRITE) return EACCES; error = smbfs_smb_open(np, SMB_SM_DENYNONE|SMB_AM_OPENREAD, &scred); } } if (!error) np->n_flag |= NOPEN; smbfs_attr_cacheremove(vp); return error; }
/*ARGSUSED*/ int smbfs_uninit(struct vfsconf *vfsp) { SMBVDEBUG("done.\n"); return 0; }
static int smbfs_mount(struct mount *mp) { struct smbmount *smp = NULL; struct smb_vc *vcp; struct smb_share *ssp = NULL; struct vnode *vp; struct thread *td; struct smb_dev *dev; struct smb_cred *scred; int error, v; char *pc, *pe; dev = NULL; td = curthread; if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) return EOPNOTSUPP; if (vfs_filteropt(mp->mnt_optnew, smbfs_opts)) { vfs_mount_error(mp, "%s", "Invalid option"); return (EINVAL); } scred = smbfs_malloc_scred(); smb_makescred(scred, td, td->td_ucred); /* Ask userspace of `fd`, the file descriptor of this session */ if (1 != vfs_scanopt(mp->mnt_optnew, "fd", "%d", &v)) { vfs_mount_error(mp, "No fd option"); smbfs_free_scred(scred); return (EINVAL); } error = smb_dev2share(v, SMBM_EXEC, scred, &ssp, &dev); smp = malloc(sizeof(*smp), M_SMBFSDATA, M_WAITOK | M_ZERO); if (error) { printf("invalid device handle %d (%d)\n", v, error); vfs_mount_error(mp, "invalid device handle %d %d\n", v, error); smbfs_free_scred(scred); free(smp, M_SMBFSDATA); return error; } vcp = SSTOVC(ssp); smb_share_unlock(ssp); mp->mnt_stat.f_iosize = SSTOVC(ssp)->vc_txmax; mp->mnt_data = smp; smp->sm_share = ssp; smp->sm_root = NULL; smp->sm_dev = dev; if (1 != vfs_scanopt(mp->mnt_optnew, "caseopt", "%d", &smp->sm_caseopt)) { vfs_mount_error(mp, "Invalid caseopt"); error = EINVAL; goto bad; } if (1 != vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v)) { vfs_mount_error(mp, "Invalid uid"); error = EINVAL; goto bad; } smp->sm_uid = v; if (1 != vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v)) { vfs_mount_error(mp, "Invalid gid"); error = EINVAL; goto bad; } smp->sm_gid = v; if (1 != vfs_scanopt(mp->mnt_optnew, "file_mode", "%d", &v)) { vfs_mount_error(mp, "Invalid file_mode"); error = EINVAL; goto bad; } smp->sm_file_mode = (v & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; if (1 != vfs_scanopt(mp->mnt_optnew, "dir_mode", "%d", &v)) { vfs_mount_error(mp, "Invalid dir_mode"); error = EINVAL; goto bad; } smp->sm_dir_mode = (v & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; vfs_flagopt(mp->mnt_optnew, "nolong", &smp->sm_flags, SMBFS_MOUNT_NO_LONG); pc = mp->mnt_stat.f_mntfromname; pe = pc + sizeof(mp->mnt_stat.f_mntfromname); bzero(pc, MNAMELEN); *pc++ = '/'; *pc++ = '/'; pc = strchr(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); if (pc < pe-1) { *(pc++) = '@'; pc = strchr(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); if (pc < pe - 1) { *(pc++) = '/'; strncpy(pc, ssp->ss_name, pe - pc - 2); } } vfs_getnewfsid(mp); error = smbfs_root(mp, LK_EXCLUSIVE, &vp); if (error) { vfs_mount_error(mp, "smbfs_root error: %d", error); goto bad; } VOP_UNLOCK(vp, 0); SMBVDEBUG("root.v_usecount = %d\n", vrefcnt(vp)); #ifdef DIAGNOSTIC SMBERROR("mp=%p\n", mp); #endif smbfs_free_scred(scred); return error; bad: if (ssp) smb_share_put(ssp, scred); smbfs_free_scred(scred); SMB_LOCK(); if (error && smp->sm_dev == dev) { smp->sm_dev = NULL; sdp_trydestroy(dev); } SMB_UNLOCK(); free(smp, M_SMBFSDATA); return error; }
static int smbfs_node_alloc(struct mount *mp, struct vnode *dvp, const char *dirnm, int dirlen, const char *name, int nmlen, char sep, struct smbfattr *fap, struct vnode **vpp) { struct vattr vattr; struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct smbnode *np, *dnp; struct vnode *vp, *vp2; struct smbcmp sc; char *p, *rpath; int error, rplen; sc.n_parent = dvp; sc.n_nmlen = nmlen; sc.n_name = name; if (smp->sm_root != NULL && dvp == NULL) { SMBERROR("do not allocate root vnode twice!\n"); return EINVAL; } if (nmlen == 2 && bcmp(name, "..", 2) == 0) { if (dvp == NULL) return EINVAL; vp = VTOSMB(VTOSMB(dvp)->n_parent)->n_vnode; error = vget(vp, LK_EXCLUSIVE, td); if (error == 0) *vpp = vp; return error; } else if (nmlen == 1 && name[0] == '.') { SMBERROR("do not call me with dot!\n"); return EINVAL; } dnp = dvp ? VTOSMB(dvp) : NULL; if (dnp == NULL && dvp != NULL) { vprint("smbfs_node_alloc: dead parent vnode", dvp); return EINVAL; } error = vfs_hash_get(mp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, td, vpp, smbfs_vnode_cmp, &sc); if (error) return (error); if (*vpp) { np = VTOSMB(*vpp); /* Force cached attributes to be refreshed if stale. */ (void)VOP_GETATTR(*vpp, &vattr, td->td_ucred); /* * If the file type on the server is inconsistent with * what it was when we created the vnode, kill the * bogus vnode now and fall through to the code below * to create a new one with the right type. */ if (((*vpp)->v_type == VDIR && (np->n_dosattr & SMB_FA_DIR) == 0) || ((*vpp)->v_type == VREG && (np->n_dosattr & SMB_FA_DIR) != 0)) { vgone(*vpp); vput(*vpp); } else { SMBVDEBUG("vnode taken from the hashtable\n"); return (0); } } /* * If we don't have node attributes, then it is an explicit lookup * for an existing vnode. */ if (fap == NULL) return ENOENT; error = getnewvnode("smbfs", mp, &smbfs_vnodeops, vpp); if (error) return (error); vp = *vpp; np = malloc(sizeof *np, M_SMBNODE, M_WAITOK | M_ZERO); rplen = dirlen; if (sep != '\0') rplen++; rplen += nmlen; rpath = malloc(rplen + 1, M_SMBNODENAME, M_WAITOK); p = rpath; bcopy(dirnm, p, dirlen); p += dirlen; if (sep != '\0') *p++ = sep; if (name != NULL) { bcopy(name, p, nmlen); p += nmlen; } *p = '\0'; MPASS(p == rpath + rplen); lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); /* Vnode initialization */ vp->v_type = fap->fa_attr & SMB_FA_DIR ? VDIR : VREG; vp->v_data = np; np->n_vnode = vp; np->n_mount = VFSTOSMBFS(mp); np->n_rpath = rpath; np->n_rplen = rplen; np->n_nmlen = nmlen; np->n_name = smbfs_name_alloc(name, nmlen); np->n_ino = fap->fa_ino; if (dvp) { ASSERT_VOP_LOCKED(dvp, "smbfs_node_alloc"); np->n_parent = dvp; np->n_parentino = VTOSMB(dvp)->n_ino; if (/*vp->v_type == VDIR &&*/ (dvp->v_vflag & VV_ROOT) == 0) { vref(dvp); np->n_flag |= NREFPARENT; } } else if (vp->v_type == VREG) SMBERROR("new vnode '%s' born without parent ?\n", np->n_name); error = insmntque(vp, mp); if (error) { free(np, M_SMBNODE); return (error); } error = vfs_hash_insert(vp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, td, &vp2, smbfs_vnode_cmp, &sc); if (error) return (error); if (vp2 != NULL) *vpp = vp2; return (0); }
/* ARGSUSED */ int smbfs_start(struct mount *mp, int flags) { SMBVDEBUG("flags=%04x\n", flags); return 0; }
int smbfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) { struct lwp *l = curlwp; struct smbfs_args *args = data; /* holds data from mount request */ struct smbmount *smp = NULL; struct smb_vc *vcp; struct smb_share *ssp = NULL; struct smb_cred scred; struct proc *p; int error; if (*data_len < sizeof *args) return EINVAL; p = l->l_proc; if (mp->mnt_flag & MNT_GETARGS) { smp = VFSTOSMBFS(mp); if (smp == NULL) return EIO; *args = smp->sm_args; *data_len = sizeof *args; return 0; } if (mp->mnt_flag & MNT_UPDATE) return EOPNOTSUPP; if (args->version != SMBFS_VERSION) { SMBVDEBUG("mount version mismatch: kernel=%d, mount=%d\n", SMBFS_VERSION, args->version); return EINVAL; } error = set_statvfs_info(path, UIO_USERSPACE, NULL, UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l); if (error) return error; smb_makescred(&scred, l, l->l_cred); error = smb_dev2share(args->dev_fd, SMBM_EXEC, &scred, &ssp); if (error) return error; smb_share_unlock(ssp); /* keep ref, but unlock */ vcp = SSTOVC(ssp); mp->mnt_stat.f_iosize = vcp->vc_txmax; mp->mnt_stat.f_namemax = (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12; MALLOC(smp, struct smbmount *, sizeof(*smp), M_SMBFSDATA, M_WAITOK); memset(smp, 0, sizeof(*smp)); mp->mnt_data = smp; smp->sm_hash = hashinit(desiredvnodes, HASH_LIST, true, &smp->sm_hashlen); mutex_init(&smp->sm_hashlock, MUTEX_DEFAULT, IPL_NONE); smp->sm_share = ssp; smp->sm_root = NULL; smp->sm_args = *args; smp->sm_caseopt = args->caseopt; smp->sm_args.file_mode = (smp->sm_args.file_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; smp->sm_args.dir_mode = (smp->sm_args.dir_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN); snprintf(mp->mnt_stat.f_mntfromname, MNAMELEN, "//%s@%s/%s", vcp->vc_username, vcp->vc_srvname, ssp->ss_name); vfs_getnewfsid(mp); return (0); }
static int smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred) { struct dirent de; struct componentname cn; struct smb_cred *scred; struct smbfs_fctx *ctx; struct vnode *newvp; struct smbnode *np = VTOSMB(vp); int error/*, *eofflag = ap->a_eofflag*/; long offset, limit; np = VTOSMB(vp); SMBVDEBUG("dirname='%s'\n", np->n_name); scred = smbfs_malloc_scred(); smb_makescred(scred, uio->uio_td, cred); offset = uio->uio_offset / DE_SIZE; /* offset in the directory */ limit = uio->uio_resid / DE_SIZE; if (uio->uio_resid < DE_SIZE || uio->uio_offset < 0) { error = EINVAL; goto out; } while (limit && offset < 2) { limit--; bzero((caddr_t)&de, DE_SIZE); de.d_reclen = DE_SIZE; de.d_fileno = (offset == 0) ? np->n_ino : (np->n_parent ? np->n_parentino : 2); if (de.d_fileno == 0) de.d_fileno = 0x7ffffffd + offset; de.d_namlen = offset + 1; de.d_name[0] = '.'; de.d_name[1] = '.'; de.d_name[offset + 1] = '\0'; de.d_type = DT_DIR; error = uiomove(&de, DE_SIZE, uio); if (error) goto out; offset++; uio->uio_offset += DE_SIZE; } if (limit == 0) { error = 0; goto out; } if (offset != np->n_dirofs || np->n_dirseq == NULL) { SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs); if (np->n_dirseq) { smbfs_findclose(np->n_dirseq, scred); np->n_dirseq = NULL; } np->n_dirofs = 2; error = smbfs_findopen(np, "*", 1, SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx); if (error) { SMBVDEBUG("can not open search, error = %d", error); goto out; } np->n_dirseq = ctx; } else ctx = np->n_dirseq; while (np->n_dirofs < offset) { error = smbfs_findnext(ctx, offset - np->n_dirofs++, scred); if (error) { smbfs_findclose(np->n_dirseq, scred); np->n_dirseq = NULL; error = ENOENT ? 0 : error; goto out; } } error = 0; for (; limit; limit--, offset++) { error = smbfs_findnext(ctx, limit, scred); if (error) break; np->n_dirofs++; bzero((caddr_t)&de, DE_SIZE); de.d_reclen = DE_SIZE; de.d_fileno = ctx->f_attr.fa_ino; de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG; de.d_namlen = ctx->f_nmlen; bcopy(ctx->f_name, de.d_name, de.d_namlen); de.d_name[de.d_namlen] = '\0'; if (smbfs_fastlookup) { error = smbfs_nget(vp->v_mount, vp, ctx->f_name, ctx->f_nmlen, &ctx->f_attr, &newvp); if (!error) { cn.cn_nameptr = de.d_name; cn.cn_namelen = de.d_namlen; cache_enter(vp, newvp, &cn); vput(newvp); } } error = uiomove(&de, DE_SIZE, uio); if (error) break; } if (error == ENOENT) error = 0; uio->uio_offset = offset * DE_SIZE; out: smbfs_free_scred(scred); return error; }
/* * Get file system statistics. */ static int smbfs_statvfs(vfs_t *vfsp, statvfs64_t *sbp) { int error; smbmntinfo_t *smi = VFTOSMI(vfsp); smb_share_t *ssp = smi->smi_share; statvfs64_t stvfs; hrtime_t now; smb_cred_t scred; if (curproc->p_zone != smi->smi_zone_ref.zref_zone) return (EPERM); if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) return (EIO); mutex_enter(&smi->smi_lock); /* * Use cached result if still valid. */ recheck: now = gethrtime(); if (now < smi->smi_statfstime) { error = 0; goto cache_hit; } /* * FS attributes are stale, so someone * needs to do an OTW call to get them. * Serialize here so only one thread * does the OTW call. */ if (smi->smi_status & SM_STATUS_STATFS_BUSY) { smi->smi_status |= SM_STATUS_STATFS_WANT; if (!cv_wait_sig(&smi->smi_statvfs_cv, &smi->smi_lock)) { mutex_exit(&smi->smi_lock); return (EINTR); } /* Hope status is valid now. */ goto recheck; } smi->smi_status |= SM_STATUS_STATFS_BUSY; mutex_exit(&smi->smi_lock); /* * Do the OTW call. Note: lock NOT held. */ smb_credinit(&scred, NULL); bzero(&stvfs, sizeof (stvfs)); error = smbfs_smb_statfs(ssp, &stvfs, &scred); smb_credrele(&scred); if (error) { SMBVDEBUG("statfs error=%d\n", error); } else { /* * Set a few things the OTW call didn't get. */ stvfs.f_frsize = stvfs.f_bsize; stvfs.f_favail = stvfs.f_ffree; stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0]; bcopy(fs_type_name, stvfs.f_basetype, FSTYPSZ); stvfs.f_flag = vf_to_stf(vfsp->vfs_flag); stvfs.f_namemax = smi->smi_fsa.fsa_maxname; /* * Save the result, update lifetime */ now = gethrtime(); smi->smi_statfstime = now + (SM_MAX_STATFSTIME * (hrtime_t)NANOSEC); smi->smi_statvfsbuf = stvfs; /* struct assign! */ } mutex_enter(&smi->smi_lock); if (smi->smi_status & SM_STATUS_STATFS_WANT) cv_broadcast(&smi->smi_statvfs_cv); smi->smi_status &= ~(SM_STATUS_STATFS_BUSY | SM_STATUS_STATFS_WANT); /* * Copy the statvfs data to caller's buf. * Note: struct assignment */ cache_hit: if (error == 0) *sbp = smi->smi_statvfsbuf; mutex_exit(&smi->smi_lock); return (error); }
static int smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred) { struct smb_cred scred; struct smbfs_fctx *ctx; struct vnode *newvp; struct smbnode *np; int error, offset, retval; np = VTOSMB(vp); SMBVDEBUG("dirname='%s'\n", np->n_name); smb_makescred(&scred, uio->uio_td, cred); if (uio->uio_offset < 0 || uio->uio_offset > INT_MAX) return(EINVAL); error = 0; offset = uio->uio_offset; if (uio->uio_resid > 0 && offset < 1) { if (vop_write_dirent(&error, uio, np->n_ino, DT_DIR, 1, ".")) goto done; if (error) goto done; ++offset; } if (uio->uio_resid > 0 && offset < 2) { if (vop_write_dirent(&error, uio, np->n_parent ? VTOSMB(np->n_parent)->n_ino : 2, DT_DIR, 2, "..")) goto done; if (error) goto done; ++offset; } if (uio->uio_resid == 0) goto done; if (offset != np->n_dirofs || np->n_dirseq == NULL) { SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs); if (np->n_dirseq) { smbfs_findclose(np->n_dirseq, &scred); np->n_dirseq = NULL; } np->n_dirofs = 2; error = smbfs_findopen(np, "*", 1, SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, &scred, &ctx); if (error) { SMBVDEBUG("can not open search, error = %d", error); return error; } np->n_dirseq = ctx; } else { ctx = np->n_dirseq; } while (np->n_dirofs < offset) { error = smbfs_findnext(ctx, offset - np->n_dirofs, &scred); ++np->n_dirofs; if (error) { smbfs_findclose(np->n_dirseq, &scred); np->n_dirseq = NULL; return error == ENOENT ? 0 : error; } } error = 0; while (uio->uio_resid > 0 && !error) { /* * Overestimate the size of a record a bit, doesn't really * hurt to be wrong here. */ error = smbfs_findnext(ctx, uio->uio_resid / _DIRENT_RECLEN(255) + 1, &scred); if (error) break; np->n_dirofs++; ++offset; retval = vop_write_dirent(&error, uio, ctx->f_attr.fa_ino, (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG, ctx->f_nmlen, ctx->f_name); if (retval) break; if (smbfs_fastlookup && !error) { error = smbfs_nget(vp->v_mount, vp, ctx->f_name, ctx->f_nmlen, &ctx->f_attr, &newvp); if (!error) vput(newvp); } } if (error == ENOENT) error = 0; done: uio->uio_offset = offset; 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. * * smbfs_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 smbfs_putpages(struct vop_putpages_args *ap) { int error; struct vnode *vp = ap->a_vp; struct thread *td = curthread; /* XXX */ struct ucred *cred; #ifdef SMBFS_RWGENERIC KKASSERT(td->td_proc); cred = td->td_proc->p_ucred; VOP_OPEN(vp, FWRITE, cred, NULL); error = vop_stdputpages(ap); 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 doclose; int *rtvals; struct smbmount *smp; struct smbnode *np; struct smb_cred scred; vm_page_t *pages; KKASSERT(td->td_proc); cred = td->td_proc->p_ucred; /* VOP_OPEN(vp, FWRITE, cred, NULL);*/ np = VTOSMB(vp); smp = VFSTOSMBFS(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(&smbfs_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; SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid); smb_makescred(&scred, td, cred); /* * This is kinda nasty. Since smbfs is physically closing the * fid on close(), we have to reopen it if necessary. There are * other races here too, such as if another process opens the same * file while we are blocked in read, or the file is open read-only * XXX */ error = 0; doclose = 0; if (np->n_opencount == 0) { error = smbfs_smb_open(np, SMB_AM_OPENRW, &scred); if (error == 0) doclose = 1; } if (error == 0) error = smb_write(smp->sm_share, np->n_fid, &uio, &scred); if (doclose) smbfs_smb_close(smp->sm_share, np->n_fid, NULL, &scred); /* VOP_CLOSE(vp, FWRITE, cred);*/ SMBVDEBUG("paged write done: %d\n", error); pmap_qremove(kva, npages); relpbuf(bp, &smbfs_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 /* SMBFS_RWGENERIC */ }