int smbfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn) { struct smbnode node; struct mount mnt; int error; assert(kd); assert(vn); error = kvm_read_all(kd, (unsigned long)VTOSMB(vp), &node, sizeof(node)); if (error != 0) { warnx("can't read smbfs fnode at %p", (void *)VTOSMB(vp)); return (1); } error = kvm_read_all(kd, (unsigned long)getvnodemount(vp), &mnt, sizeof(mnt)); if (error != 0) { warnx("can't read mount at %p for vnode %p", (void *)getvnodemount(vp), vp); return (1); } vn->vn_fileid = node.n_ino; if (vn->vn_fileid == 0) vn->vn_fileid = 2; vn->vn_fsid = mnt.mnt_stat.f_fsid.val[0]; return (0); }
int smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) { struct smbnode *dnp, *np; struct vnode *vp; int error, sep; dnp = (dvp) ? VTOSMB(dvp) : NULL; sep = 0; if (dnp != NULL) { sep = SMBFS_DNP_SEP(dnp); error = smbfs_node_alloc(mp, dvp, dnp->n_rpath, dnp->n_rplen, name, nmlen, sep, fap, &vp); } else error = smbfs_node_alloc(mp, NULL, "\\", 1, name, nmlen, sep, fap, &vp); if (error) return error; MPASS(vp != NULL); np = VTOSMB(vp); if (fap) smbfs_attr_cacheenter(vp, fap); *vpp = vp; return 0; }
static int smbfs_node_alloc(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) { struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct smbnode_hashhead *nhpp; struct smbnode *np, *np2, *dnp; struct vnode *vp; u_long hashval; int error; *vpp = NULL; 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(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; } hashval = smbfs_hash(name, nmlen); retry: smbfs_hash_lock(smp, td); loop: nhpp = SMBFS_NOHASH(smp, hashval); LIST_FOREACH(np, nhpp, n_hash) { vp = SMBTOV(np); if (np->n_parent != dnp || np->n_nmlen != nmlen || bcmp(name, np->n_name, nmlen) != 0) continue; VI_LOCK(vp); smbfs_hash_unlock(smp, td); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td) != 0) goto retry; *vpp = vp; return 0; }
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; }
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; }
/* * Flush out the buffer cache */ int smbfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) { struct vnode *vp; struct vnode_iterator *marker; struct smbnode *np; int error, allerror = 0; vfs_vnode_iterator_init(mp, &marker); while (vfs_vnode_iterator_next(marker, &vp)) { error = vn_lock(vp, LK_EXCLUSIVE); if (error) { vrele(vp); continue; } np = VTOSMB(vp); if (np == NULL) { vput(vp); continue; } if ((vp->v_type == VNON || (np->n_flag & NMODIFIED) == 0) && LIST_EMPTY(&vp->v_dirtyblkhd) && vp->v_uobj.uo_npages == 0) { vput(vp); continue; } error = VOP_FSYNC(vp, cred, waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0); if (error) allerror = error; vput(vp); } vfs_vnode_iterator_destroy(marker); return (allerror); }
static int smbfs_getextattr(struct vop_getextattr_args *ap) /* { IN struct vnode *a_vp; IN char *a_name; INOUT struct uio *a_uio; IN kauth_cred_t a_cred; }; */ { struct vnode *vp = ap->a_vp; struct lwp *l = ap->a_l; kauth_cred_t cred = ap->a_cred; struct uio *uio = ap->a_uio; const char *name = ap->a_name; struct smbnode *np = VTOSMB(vp); struct vattr vattr; char buf[10]; int i, attr, error; error = VOP_ACCESS(vp, VREAD, cred, td); if (error) return error; error = VOP_GETATTR(vp, &vattr, cred, td); if (error) return error; if (strcmp(name, "dosattr") == 0) { attr = np->n_dosattr; for (i = 0; i < 6; i++, attr >>= 1) buf[i] = (attr & 1) ? smbfs_atl[i] : '-'; buf[i] = 0; error = uiomove(buf, i, uio); } else
int smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred) { struct smbmount *smp = VFSTOSMBFS(vp->v_mount); struct smbnode *np = VTOSMB(vp); struct thread *td; struct vattr vattr; struct smb_cred scred; int error, lks; /* * Protect against method which is not supported for now */ if (uiop->uio_segflg == UIO_NOCOPY) return EOPNOTSUPP; if (vp->v_type != VREG && vp->v_type != VDIR) { SMBFSERR("vn types other than VREG or VDIR are unsupported !\n"); return EIO; } if (uiop->uio_resid == 0) return 0; if (uiop->uio_offset < 0) return EINVAL; /* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize) return EFBIG;*/ td = uiop->uio_td; if (vp->v_type == VDIR) { lks = LK_EXCLUSIVE;/*lockstatus(vp->v_vnlock, td);*/ if (lks == LK_SHARED) vn_lock(vp, LK_UPGRADE | LK_RETRY, td); error = smbfs_readvdir(vp, uiop, cred); if (lks == LK_SHARED) vn_lock(vp, LK_DOWNGRADE | LK_RETRY, td); return error; } /* biosize = SSTOCN(smp->sm_share)->sc_txmax;*/ if (np->n_flag & NMODIFIED) { smbfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr, cred, td); if (error) return error; np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; } else { error = VOP_GETATTR(vp, &vattr, cred, td); if (error) return error; if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) { error = smbfs_vinvalbuf(vp, td); if (error) return error; np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; } } smb_makescred(&scred, td, cred); return smb_read(smp->sm_share, np->n_fid, uiop, &scred); }
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; }
/* * Flush out the buffer cache */ int smbfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) { struct vnode *vp, *mvp; struct smbnode *np; int error, allerror = 0; /* Allocate a marker vnode. */ if ((mvp = vnalloc(mp)) == NULL) return ENOMEM; /* * Force stale buffer cache information to be flushed. */ mutex_enter(&mntvnode_lock); loop: for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) { vmark(mvp, vp); /* * If the vnode that we are about to sync is no longer * associated with this mount point, start over. */ if (vp->v_mount != mp || vismarker(vp)) continue; mutex_enter(&vp->v_interlock); np = VTOSMB(vp); if (np == NULL) { mutex_exit(&vp->v_interlock); continue; } if ((vp->v_type == VNON || (np->n_flag & NMODIFIED) == 0) && LIST_EMPTY(&vp->v_dirtyblkhd) && vp->v_uobj.uo_npages == 0) { mutex_exit(&vp->v_interlock); continue; } mutex_exit(&mntvnode_lock); error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK); if (error) { mutex_enter(&mntvnode_lock); if (error == ENOENT) { (void)vunmark(mvp); goto loop; } continue; } error = VOP_FSYNC(vp, cred, waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0); if (error) allerror = error; vput(vp); mutex_enter(&mntvnode_lock); } mutex_exit(&mntvnode_lock); vnfree(mvp); return (allerror); }
/* * smbfs_remove directory call */ int smbfs_rmdir(void *v) { struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; /* struct smbmount *smp = VTOSMBFS(vp);*/ struct smbnode *dnp = VTOSMB(dvp); struct smbnode *np = VTOSMB(vp); struct smb_cred scred; int error; if (dvp == vp) { vrele(dvp); vput(dvp); return (EINVAL); } smb_makescred(&scred, curlwp, cnp->cn_cred); error = smbfs_smb_rmdir(np, &scred); if (error == 0) np->n_flag |= NGONE; dnp->n_flag |= NMODIFIED; smbfs_attr_cacheremove(dvp); VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); VN_KNOTE(vp, NOTE_DELETE); cache_purge(dvp); cache_purge(vp); vput(vp); vput(dvp); return (error); }
int smbfs_print(void *v) { struct vop_print_args /* { struct vnode *a_vp; } */ *ap = v; struct vnode *vp = ap->a_vp; struct smbnode *np = VTOSMB(vp); printf("tag VT_SMBFS, name = %.*s, parent = %p, open = %d\n", (int)np->n_nmlen, np->n_name, np->n_parent ? np->n_parent : NULL, (np->n_flag & NOPEN) != 0); return (0); }
/* * Close called. */ int smbfs_close(void *v) { struct vop_close_args /* { struct vnodeop_desc *a_desc; struct vnode *a_vp; int a_fflag; kauth_cred_t a_cred; } */ *ap = v; int error; struct lwp *l = curlwp; struct vnode *vp = ap->a_vp; struct smbnode *np = VTOSMB(vp); /* Flush all file data */ error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, l, 1); if (error) return (error); /* * We must close the directory lookup context now, so that * later directory changes would be properly detected. * Ideally, the lookup routines should handle such case, and * the context would be removed only in smbfs_inactive(). */ if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0) { struct smb_share *ssp = np->n_mount->sm_share; struct smb_cred scred; smb_makescred(&scred, l, ap->a_cred); if (np->n_dirseq != NULL) { smbfs_findclose(np->n_dirseq, &scred); np->n_dirseq = NULL; } if (SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_NT_SMBS) smbfs_smb_close(ssp, np->n_fid, &np->n_mtime, &scred); np->n_flag &= ~NOPEN; smbfs_attr_cacheremove(vp); } return (0); }
int smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen, struct smbfattr *fap, struct smb_cred *scred) { struct smbfs_fctx *ctx; int error; if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) { memset(fap, 0, sizeof(*fap)); fap->fa_attr = SMB_FA_DIR; fap->fa_ino = 2; return 0; } if (nmlen == 1 && name[0] == '.') { error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred); return error; } else if (nmlen == 2 && name[0] == '.' && name[1] == '.') { error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap, scred); printf("%s: knows NOTHING about '..'\n", __func__); return error; } error = smbfs_findopen(dnp, name, nmlen, SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx); if (error) return error; ctx->f_flags |= SMBFS_RDD_FINDSINGLE; error = smbfs_findnext(ctx, 1, scred); if (error == 0) { *fap = ctx->f_attr; if (name == NULL) fap->fa_ino = dnp->n_ino; /* * Check the returned file name case exactly * matches requested file name. ctx->f_nmlen is * guaranteed to always match nmlen. */ if (nmlen > 0 && strncmp(name, ctx->f_name, nmlen) != 0) error = ENOENT; } smbfs_findclose(ctx, scred); return error; }
/* * smbfs_create call * Create a regular file. On entry the directory to contain the file being * created is locked. We must release before we return. */ int smbfs_create(void *v) { struct vop_create_v3_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; struct vnode *dvp = ap->a_dvp; struct vattr *vap = ap->a_vap; struct componentname *cnp = ap->a_cnp; struct smbnode *dnp = VTOSMB(dvp); struct smbfattr fattr; struct smb_cred scred; const char *name = cnp->cn_nameptr; int nmlen = cnp->cn_namelen; int error = EINVAL; if (vap->va_type != VREG) goto out; smb_makescred(&scred, curlwp, cnp->cn_cred); error = smbfs_smb_create(dnp, name, nmlen, &scred); if (error) goto out; error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred); if (error) goto out; error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, ap->a_vpp); if (error) goto out; VOP_UNLOCK(*ap->a_vpp); cache_enter(dvp, *ap->a_vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); out: VN_KNOTE(dvp, NOTE_WRITE); return (error); }
int smbfs_mkdir(void *v) { struct vop_mkdir_v3_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap = v; struct vnode *dvp = ap->a_dvp; /* struct vattr *vap = ap->a_vap;*/ struct vnode *vp; struct componentname *cnp = ap->a_cnp; struct smbnode *dnp = VTOSMB(dvp); struct smb_cred scred; struct smbfattr fattr; const char *name = cnp->cn_nameptr; int len = cnp->cn_namelen; int error; if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))){ error = EEXIST; goto out; } smb_makescred(&scred, curlwp, cnp->cn_cred); error = smbfs_smb_mkdir(dnp, name, len, &scred); if (error) goto out; error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred); if (error) goto out; error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp); if (error) goto out; VOP_UNLOCK(vp); *ap->a_vpp = vp; out: VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK); return (error); }
/* * Flush and invalidate all dirty buffers. If another process is already * doing the flush, just wait for completion. */ int smbfs_vinvalbuf(struct vnode *vp, int flags, int intrflg) { struct smbnode *np = VTOSMB(vp); int error = 0, slpflag, slptimeo; if (vp->v_flag & VRECLAIMED) return 0; if (intrflg) { slpflag = PCATCH; slptimeo = 2 * hz; } else { slpflag = 0; slptimeo = 0; } while (np->n_flag & NFLUSHINPROG) { np->n_flag |= NFLUSHWANT; error = tsleep((caddr_t)&np->n_flag, 0, "smfsvinv", slptimeo); error = smb_proc_intr(curthread); if (error == EINTR && intrflg) return EINTR; } np->n_flag |= NFLUSHINPROG; error = vinvalbuf(vp, flags, slpflag, 0); while (error) { if (intrflg && (error == ERESTART || error == EINTR)) { np->n_flag &= ~NFLUSHINPROG; if (np->n_flag & NFLUSHWANT) { np->n_flag &= ~NFLUSHWANT; wakeup((caddr_t)&np->n_flag); } return EINTR; } error = vinvalbuf(vp, flags, slpflag, 0); } np->n_flag &= ~(NMODIFIED | NFLUSHINPROG); if (np->n_flag & NFLUSHWANT) { np->n_flag &= ~NFLUSHWANT; wakeup((caddr_t)&np->n_flag); } return (error); }
/* * Return locked root vnode of a filesystem */ static int smbfs_root(struct mount *mp, struct vnode **vpp) { struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct vnode *vp; struct smbnode *np; struct smbfattr fattr; struct ucred *cred; struct smb_cred scred; int error; if (smp == NULL) { SMBERROR("smp == NULL (bug in umount)\n"); return EINVAL; } if (smp->sm_root) { *vpp = SMBTOV(smp->sm_root); return vget(*vpp, LK_EXCLUSIVE | LK_RETRY); } if (td->td_proc) cred = td->td_proc->p_ucred; else cred = proc0.p_ucred; smb_makescred(&scred, td, cred); error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred); if (error) return error; error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp); if (error) return error; vsetflags(vp, VROOT); np = VTOSMB(vp); smp->sm_root = np; *vpp = vp; return 0; }
/* * Get root vnode of the smbfs filesystem, and store it in sm_root. */ static int smbfs_setroot(struct mount *mp) { struct smbmount *smp = VFSTOSMBFS(mp); struct vnode *vp; struct smbfattr fattr; struct lwp *l = curlwp; kauth_cred_t cred = l->l_cred; struct smb_cred scred; int error; KASSERT(smp->sm_root == NULL); smb_makescred(&scred, l, cred); error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred); if (error) return error; error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp); if (error) return error; /* * Someone might have already set sm_root while we slept * in smb_lookup or malloc/getnewvnode. */ if (smp->sm_root) vput(vp); else { vp->v_vflag |= VV_ROOT; smp->sm_root = VTOSMB(vp); /* Keep reference, but unlock */ VOP_UNLOCK(vp, 0); } return (0); }
int smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen, struct smbfattr *fap, struct smb_cred *scred) { struct smbfs_fctx *ctx; int error; if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) { bzero(fap, sizeof(*fap)); fap->fa_attr = SMB_FA_DIR; fap->fa_ino = 2; return 0; } if (nmlen == 1 && name[0] == '.') { error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred); return error; } else if (nmlen == 2 && name[0] == '.' && name[1] == '.') { error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap, scred); kprintf("%s: knows NOTHING about '..'\n", __func__); return error; } error = smbfs_findopen(dnp, name, nmlen, SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx); if (error) return error; ctx->f_flags |= SMBFS_RDD_FINDSINGLE; error = smbfs_findnext(ctx, 1, scred); if (error == 0) { *fap = ctx->f_attr; if (name == NULL) fap->fa_ino = dnp->n_ino; } smbfs_findclose(ctx, scred); return error; }
int smbfs_remove(void *v) { struct vop_remove_args /* { struct vnodeop_desc *a_desc; struct vnode * a_dvp; struct vnode * a_vp; struct componentname * a_cnp; } */ *ap = v; struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; struct smbnode *np = VTOSMB(vp); struct smb_cred scred; int error; if (vp->v_type == VDIR || (np->n_flag & NOPEN) != 0 || vp->v_usecount != 1) { /* XXX Eventually should do something along NFS sillyrename */ error = EPERM; } else { smb_makescred(&scred, curlwp, cnp->cn_cred); error = smbfs_smb_delete(np, &scred); } if (error == 0) np->n_flag |= NGONE; VN_KNOTE(ap->a_vp, NOTE_DELETE); VN_KNOTE(ap->a_dvp, NOTE_WRITE); if (dvp == vp) vrele(vp); else vput(vp); vput(dvp); 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; }
/* * Return locked root vnode of a filesystem */ static int smbfs_root(struct mount *mp, int flags, struct vnode **vpp) { struct smbmount *smp = VFSTOSMBFS(mp); struct vnode *vp; struct smbnode *np; struct smbfattr fattr; struct thread *td; struct ucred *cred; struct smb_cred *scred; int error; td = curthread; cred = td->td_ucred; if (smp->sm_root) { *vpp = SMBTOV(smp->sm_root); return vget(*vpp, LK_EXCLUSIVE | LK_RETRY, td); } scred = smbfs_malloc_scred(); smb_makescred(scred, td, cred); error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, scred); if (error) goto out; error = smbfs_nget(mp, NULL, NULL, 0, &fattr, &vp); if (error) goto out; ASSERT_VOP_LOCKED(vp, "smbfs_root"); vp->v_vflag |= VV_ROOT; np = VTOSMB(vp); smp->sm_root = np; *vpp = vp; out: smbfs_free_scred(scred); return error; }
/* * smbfs_file rename call */ int smbfs_rename(void *v) { struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap = v; struct vnode *fvp = ap->a_fvp; struct vnode *tvp = ap->a_tvp; struct vnode *fdvp = ap->a_fdvp; struct vnode *tdvp = ap->a_tdvp; struct componentname *tcnp = ap->a_tcnp; /* struct componentname *fcnp = ap->a_fcnp;*/ struct smb_cred scred; #ifdef notyet u_int16_t flags = 6; #endif int error=0; /* Check for cross-device rename */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { error = EXDEV; goto out; } if (tvp && tvp->v_usecount > 1) { error = EBUSY; goto out; } #ifdef notnow flags = 0x10; /* verify all writes */ if (fvp->v_type == VDIR) { flags |= 2; } else if (fvp->v_type == VREG) { flags |= 1; } else { error = EINVAL; goto out; } #endif smb_makescred(&scred, curlwp, tcnp->cn_cred); /* * It seems that Samba doesn't implement SMB_COM_MOVE call... */ #ifdef notnow if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) { error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp), tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred); } else #endif { /* * We have to do the work atomicaly */ if (tvp && tvp != fvp) { error = smbfs_smb_delete(VTOSMB(tvp), &scred); if (error) goto out; VTOSMB(tvp)->n_flag |= NGONE; VN_KNOTE(tdvp, NOTE_WRITE); VN_KNOTE(tvp, NOTE_DELETE); cache_purge(tvp); } error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp), tcnp->cn_nameptr, tcnp->cn_namelen, &scred); VTOSMB(fvp)->n_flag |= NGONE; VN_KNOTE(fdvp, NOTE_WRITE); VN_KNOTE(fvp, NOTE_RENAME); } if (fvp->v_type == VDIR) { if (tvp != NULL && tvp->v_type == VDIR) cache_purge(tdvp); cache_purge(fdvp); } smbfs_attr_cacheremove(fdvp); smbfs_attr_cacheremove(tdvp); out: if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fdvp); vrele(fvp); return (error); }
int smbfs_setattr(void *v) { struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; kauth_cred_t a_cred; } */ *ap = v; struct lwp *l = curlwp; struct vnode *vp = ap->a_vp; struct smbnode *np = VTOSMB(vp); struct vattr *vap = ap->a_vap; struct timespec *mtime, *atime; struct smb_cred scred; struct smb_share *ssp = np->n_mount->sm_share; struct smb_vc *vcp = SSTOVC(ssp); u_quad_t tsize = 0; int isreadonly, doclose, error = 0; SMBVDEBUG0("\n"); if (vap->va_flags != VNOVAL) return EOPNOTSUPP; isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY); /* * 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) && isreadonly) return EROFS; smb_makescred(&scred, l, ap->a_cred); if (vap->va_size != VNOVAL) { switch (vp->v_type) { case VDIR: return EISDIR; case VREG: break; default: return EINVAL; }; if (isreadonly) return EROFS; doclose = 0; tsize = np->n_size; np->n_size = vap->va_size; uvm_vnp_setsize(vp, vap->va_size); if ((np->n_flag & NOPEN) == 0) { error = smbfs_smb_open(np, SMB_SM_DENYNONE|SMB_AM_OPENRW, &scred); if (error == 0) doclose = 1; } if (error == 0) error = smbfs_smb_setfsize(np, vap->va_size, &scred); if (doclose) smbfs_smb_close(ssp, np->n_fid, NULL, &scred); if (error) { np->n_size = tsize; uvm_vnp_setsize(vp, tsize); return (error); } } mtime = atime = NULL; if (vap->va_mtime.tv_sec != VNOVAL) mtime = &vap->va_mtime; if (vap->va_atime.tv_sec != VNOVAL) atime = &vap->va_atime; if (mtime != atime) { error = kauth_authorize_vnode(ap->a_cred, KAUTH_VNODE_WRITE_TIMES, ap->a_vp, NULL, genfs_can_chtimes(ap->a_vp, vap->va_vaflags, VTOSMBFS(vp)->sm_args.uid, ap->a_cred)); if (error) return (error); #if 0 if (mtime == NULL) mtime = &np->n_mtime; if (atime == NULL) atime = &np->n_atime; #endif /* * If file is opened, then we can use handle based calls. * If not, use path based ones. */ if ((np->n_flag & NOPEN) == 0) { if (vcp->vc_flags & SMBV_WIN95) { error = VOP_OPEN(vp, FWRITE, ap->a_cred); if (!error) { /* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); VOP_GETATTR(vp, &vattr, ap->a_cred);*/ if (mtime) np->n_mtime = *mtime; VOP_CLOSE(vp, FWRITE, ap->a_cred); } } else if (SMB_CAPS(vcp) & SMB_CAP_NT_SMBS) { error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred); } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) { error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); } else { error = smbfs_smb_setpattr(np, 0, mtime, &scred); } } else { if (SMB_CAPS(vcp) & SMB_CAP_NT_SMBS) { error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) { error = smbfs_smb_setftime(np, mtime, atime, &scred); } else { /* * XXX I have no idea how to handle this for core * level servers. The possible solution is to * update mtime after file is closed. */ } } } /* * Invalidate attribute cache in case if server doesn't set * required attributes. */ smbfs_attr_cacheremove(vp); /* invalidate cache */ VOP_GETATTR(vp, vap, ap->a_cred); np->n_mtime.tv_sec = vap->va_mtime.tv_sec; VN_KNOTE(vp, NOTE_ATTRIB); return error; }
/* * Do an I/O operation to/from a cache block. */ int smbfs_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td) { struct smbmount *smp = VFSTOSMBFS(vp->v_mount); struct smbnode *np = VTOSMB(vp); struct uio *uiop; struct iovec io; struct smb_cred *scred; int error = 0; uiop = malloc(sizeof(struct uio), M_SMBFSDATA, M_WAITOK); uiop->uio_iov = &io; uiop->uio_iovcnt = 1; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_td = td; scred = smbfs_malloc_scred(); smb_makescred(scred, td, cr); if (bp->b_iocmd == BIO_READ) { io.iov_len = uiop->uio_resid = bp->b_bcount; io.iov_base = bp->b_data; uiop->uio_rw = UIO_READ; switch (vp->v_type) { case VREG: uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; error = smb_read(smp->sm_share, np->n_fid, uiop, scred); if (error) break; if (uiop->uio_resid) { int left = uiop->uio_resid; int nread = bp->b_bcount - left; if (left > 0) bzero((char *)bp->b_data + nread, left); } break; default: printf("smbfs_doio: type %x unexpected\n",vp->v_type); break; }; if (error) { bp->b_error = error; bp->b_ioflags |= BIO_ERROR; } } else { /* write */ if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size) bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE); if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; error = smb_write(smp->sm_share, np->n_fid, uiop, scred); /* * For an interrupted write, the buffer is still valid * and the write hasn't been pushed to the server yet, * so we can't set BIO_ERROR and report the interruption * by setting B_EINTR. For the B_ASYNC case, B_EINTR * is not relevant, so the rpc attempt is essentially * a noop. For the case of a V3 write rpc not being * committed to stable storage, the block is still * dirty and requires either a commit rpc or another * write rpc with iomode == NFSV3WRITE_FILESYNC before * the block is reused. This is indicated by setting * the B_DELWRI and B_NEEDCOMMIT flags. */ if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) { int s; s = splbio(); bp->b_flags &= ~(B_INVAL|B_NOCACHE); if ((bp->b_flags & B_ASYNC) == 0) bp->b_flags |= B_EINTR; if ((bp->b_flags & B_PAGING) == 0) { bdirty(bp); bp->b_flags &= ~B_DONE; } if ((bp->b_flags & B_ASYNC) == 0) bp->b_flags |= B_EINTR; splx(s); } else { if (error) { bp->b_ioflags |= BIO_ERROR; bp->b_error = error; } bp->b_dirtyoff = bp->b_dirtyend = 0; } } else { bp->b_resid = 0; bufdone(bp); free(uiop, M_SMBFSDATA); smbfs_free_scred(scred); return 0; } } bp->b_resid = uiop->uio_resid; bufdone(bp); free(uiop, M_SMBFSDATA); smbfs_free_scred(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; }
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; }
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); }
static int smbfs_node_alloc(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) { struct vattr vattr; struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct smbnode_hashhead *nhpp; struct smbnode *np, *np2, *dnp; struct vnode *vp; u_long hashval; int error; *vpp = NULL; 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; } hashval = smbfs_hash(name, nmlen); retry: smbfs_hash_lock(smp); loop: nhpp = SMBFS_NOHASH(smp, hashval); LIST_FOREACH(np, nhpp, n_hash) { vp = SMBTOV(np); if (np->n_parent != dvp || np->n_nmlen != nmlen || bcmp(name, np->n_name, nmlen) != 0) continue; VI_LOCK(vp); smbfs_hash_unlock(smp); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td) != 0) goto retry; /* Force cached attributes to be refreshed if stale. */ (void)VOP_GETATTR(vp, &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 ((vp->v_type == VDIR && (np->n_dosattr & SMB_FA_DIR) == 0) || (vp->v_type == VREG && (np->n_dosattr & SMB_FA_DIR) != 0)) { vgone(vp); vput(vp); break; } *vpp = vp; return 0; }