1139776Simp/*- 275374Sbp * Copyright (c) 2000-2001 Boris Popov 375374Sbp * All rights reserved. 475374Sbp * 575374Sbp * Redistribution and use in source and binary forms, with or without 675374Sbp * modification, are permitted provided that the following conditions 775374Sbp * are met: 875374Sbp * 1. Redistributions of source code must retain the above copyright 975374Sbp * notice, this list of conditions and the following disclaimer. 1075374Sbp * 2. Redistributions in binary form must reproduce the above copyright 1175374Sbp * notice, this list of conditions and the following disclaimer in the 1275374Sbp * documentation and/or other materials provided with the distribution. 1375374Sbp * 1475374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1575374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1675374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1775374Sbp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1875374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1975374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2075374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2175374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2275374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2375374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2475374Sbp * SUCH DAMAGE. 2575374Sbp * 2675374Sbp * $FreeBSD$ 2775374Sbp */ 2875374Sbp#include <sys/param.h> 2975374Sbp#include <sys/systm.h> 30242387Sdavide#include <sys/fnv_hash.h> 3175374Sbp#include <sys/kernel.h> 3276166Smarkm#include <sys/lock.h> 3376166Smarkm#include <sys/malloc.h> 3476166Smarkm#include <sys/mount.h> 3576166Smarkm#include <sys/proc.h> 3676166Smarkm#include <sys/queue.h> 37163993Sbp#include <sys/stat.h> 38176744Srwatson#include <sys/sx.h> 3976166Smarkm#include <sys/sysctl.h> 4075374Sbp#include <sys/time.h> 4175374Sbp#include <sys/vnode.h> 4276166Smarkm 4376166Smarkm#include <netsmb/smb.h> 4476166Smarkm#include <netsmb/smb_conn.h> 4576166Smarkm#include <netsmb/smb_subr.h> 4676166Smarkm 4775374Sbp#include <vm/vm.h> 4875374Sbp#include <vm/vm_extern.h> 4975374Sbp/*#include <vm/vm_page.h> 5075374Sbp#include <vm/vm_object.h>*/ 5175374Sbp 5275374Sbp#include <fs/smbfs/smbfs.h> 5375374Sbp#include <fs/smbfs/smbfs_node.h> 5475374Sbp#include <fs/smbfs/smbfs_subr.h> 5575374Sbp 56138290Sphkextern struct vop_vector smbfs_vnodeops; /* XXX -> .h file */ 5775374Sbp 58227293Sedstatic MALLOC_DEFINE(M_SMBNODE, "smbufs_node", "SMBFS vnode private part"); 59151897Srwatsonstatic MALLOC_DEFINE(M_SMBNODENAME, "smbufs_nname", "SMBFS node name"); 6075374Sbp 61242387Sdavideu_int32_t __inline 6275374Sbpsmbfs_hash(const u_char *name, int nmlen) 6375374Sbp{ 64242387Sdavide return (fnv_32_buf(name, nmlen, FNV1_32_INIT)); 6575374Sbp} 6675374Sbp 6775374Sbpstatic char * 6875374Sbpsmbfs_name_alloc(const u_char *name, int nmlen) 6975374Sbp{ 7075374Sbp u_char *cp; 7175374Sbp 7275374Sbp nmlen++; 73111119Simp cp = malloc(nmlen, M_SMBNODENAME, M_WAITOK); 7475374Sbp bcopy(name, cp, nmlen - 1); 7575374Sbp cp[nmlen - 1] = 0; 7675374Sbp return cp; 7775374Sbp} 7875374Sbp 7975374Sbpstatic void 8075374Sbpsmbfs_name_free(u_char *name) 8175374Sbp{ 8275374Sbp 8375374Sbp free(name, M_SMBNODENAME); 8475374Sbp} 8575374Sbp 86242387Sdavidestatic int __inline 87242387Sdavidesmbfs_vnode_cmp(struct vnode *vp, void *_sc) 88242387Sdavide{ 89242387Sdavide struct smbnode *np; 90242387Sdavide struct smbcmp *sc; 91242387Sdavide 92252355Sdavide np = (struct smbnode *) vp->v_data; 93242387Sdavide sc = (struct smbcmp *) _sc; 94242387Sdavide if (np->n_parent != sc->n_parent || np->n_nmlen != sc->n_nmlen || 95242387Sdavide bcmp(sc->n_name, np->n_name, sc->n_nmlen) != 0) 96242387Sdavide return 1; 97242387Sdavide return 0; 98242387Sdavide} 99242387Sdavide 10075374Sbpstatic int 101243396Sdavidesmbfs_node_alloc(struct mount *mp, struct vnode *dvp, const char *dirnm, 102243396Sdavide int dirlen, const char *name, int nmlen, char sep, 103243396Sdavide struct smbfattr *fap, struct vnode **vpp) 10475374Sbp{ 105125637Stjr struct vattr vattr; 10687194Sbp struct thread *td = curthread; /* XXX */ 10775374Sbp struct smbmount *smp = VFSTOSMBFS(mp); 108242387Sdavide struct smbnode *np, *dnp; 109242387Sdavide struct vnode *vp, *vp2; 110242387Sdavide struct smbcmp sc; 111243396Sdavide char *p, *rpath; 112243396Sdavide int error, rplen; 11375374Sbp 114242387Sdavide sc.n_parent = dvp; 115242387Sdavide sc.n_nmlen = nmlen; 116242387Sdavide sc.n_name = name; 11775374Sbp if (smp->sm_root != NULL && dvp == NULL) { 11875374Sbp SMBERROR("do not allocate root vnode twice!\n"); 11975374Sbp return EINVAL; 12075374Sbp } 12175374Sbp if (nmlen == 2 && bcmp(name, "..", 2) == 0) { 12275374Sbp if (dvp == NULL) 12375374Sbp return EINVAL; 124107821Stjr vp = VTOSMB(VTOSMB(dvp)->n_parent)->n_vnode; 12587194Sbp error = vget(vp, LK_EXCLUSIVE, td); 12675374Sbp if (error == 0) 12775374Sbp *vpp = vp; 12875374Sbp return error; 12975374Sbp } else if (nmlen == 1 && name[0] == '.') { 13075374Sbp SMBERROR("do not call me with dot!\n"); 13175374Sbp return EINVAL; 13275374Sbp } 13375374Sbp dnp = dvp ? VTOSMB(dvp) : NULL; 13475374Sbp if (dnp == NULL && dvp != NULL) { 13575374Sbp vprint("smbfs_node_alloc: dead parent vnode", dvp); 13675374Sbp return EINVAL; 13775374Sbp } 138242387Sdavide error = vfs_hash_get(mp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, td, 139242387Sdavide vpp, smbfs_vnode_cmp, &sc); 140242387Sdavide if (error) 141242387Sdavide return (error); 142242387Sdavide if (*vpp) { 143242387Sdavide np = VTOSMB(*vpp); 144125637Stjr /* Force cached attributes to be refreshed if stale. */ 145242387Sdavide (void)VOP_GETATTR(*vpp, &vattr, td->td_ucred); 146125637Stjr /* 147125637Stjr * If the file type on the server is inconsistent with 148125637Stjr * what it was when we created the vnode, kill the 149125637Stjr * bogus vnode now and fall through to the code below 150125637Stjr * to create a new one with the right type. 151125637Stjr */ 152242387Sdavide if (((*vpp)->v_type == VDIR && 153242387Sdavide (np->n_dosattr & SMB_FA_DIR) == 0) || 154242387Sdavide ((*vpp)->v_type == VREG && 155242387Sdavide (np->n_dosattr & SMB_FA_DIR) != 0)) { 156242387Sdavide vgone(*vpp); 157242387Sdavide vput(*vpp); 158125637Stjr } 159242387Sdavide else { 160242387Sdavide SMBVDEBUG("vnode taken from the hashtable\n"); 161242387Sdavide return (0); 162242387Sdavide } 16375374Sbp } 16475374Sbp /* 16575374Sbp * If we don't have node attributes, then it is an explicit lookup 16675374Sbp * for an existing vnode. 16775374Sbp */ 16875374Sbp if (fap == NULL) 16975374Sbp return ENOENT; 17075374Sbp 171242387Sdavide error = getnewvnode("smbfs", mp, &smbfs_vnodeops, vpp); 172242387Sdavide if (error) 173238539Sbrueffer return (error); 174242387Sdavide vp = *vpp; 175238539Sbrueffer np = malloc(sizeof *np, M_SMBNODE, M_WAITOK | M_ZERO); 176243396Sdavide rplen = dirlen; 177243396Sdavide if (sep != '\0') 178243396Sdavide rplen++; 179243396Sdavide rplen += nmlen; 180243396Sdavide rpath = malloc(rplen + 1, M_SMBNODENAME, M_WAITOK); 181243396Sdavide p = rpath; 182243396Sdavide bcopy(dirnm, p, dirlen); 183243396Sdavide p += dirlen; 184243396Sdavide if (sep != '\0') 185243396Sdavide *p++ = sep; 186243396Sdavide if (name != NULL) { 187243396Sdavide bcopy(name, p, nmlen); 188243396Sdavide p += nmlen; 189243396Sdavide } 190243548Sdavide *p = '\0'; 191243396Sdavide MPASS(p == rpath + rplen); 192242387Sdavide lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); 193242387Sdavide /* Vnode initialization */ 19475374Sbp vp->v_type = fap->fa_attr & SMB_FA_DIR ? VDIR : VREG; 19575374Sbp vp->v_data = np; 19675374Sbp np->n_vnode = vp; 19775374Sbp np->n_mount = VFSTOSMBFS(mp); 198243396Sdavide np->n_rpath = rpath; 199243396Sdavide np->n_rplen = rplen; 20075374Sbp np->n_nmlen = nmlen; 20175374Sbp np->n_name = smbfs_name_alloc(name, nmlen); 20275374Sbp np->n_ino = fap->fa_ino; 20375374Sbp if (dvp) { 204101308Sjeff ASSERT_VOP_LOCKED(dvp, "smbfs_node_alloc"); 205107821Stjr np->n_parent = dvp; 206243396Sdavide np->n_parentino = VTOSMB(dvp)->n_ino; 207101308Sjeff if (/*vp->v_type == VDIR &&*/ (dvp->v_vflag & VV_ROOT) == 0) { 20875374Sbp vref(dvp); 20975374Sbp np->n_flag |= NREFPARENT; 21075374Sbp } 21175374Sbp } else if (vp->v_type == VREG) 21275374Sbp SMBERROR("new vnode '%s' born without parent ?\n", np->n_name); 213242387Sdavide error = insmntque(vp, mp); 214242387Sdavide if (error) { 215242387Sdavide free(np, M_SMBNODE); 216242387Sdavide return (error); 21775374Sbp } 218242387Sdavide error = vfs_hash_insert(vp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, 219242387Sdavide td, &vp2, smbfs_vnode_cmp, &sc); 220242387Sdavide if (error) 221242387Sdavide return (error); 222242387Sdavide if (vp2 != NULL) 223242387Sdavide *vpp = vp2; 224242387Sdavide return (0); 22575374Sbp} 22675374Sbp 22775374Sbpint 22875374Sbpsmbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, 22975374Sbp struct smbfattr *fap, struct vnode **vpp) 23075374Sbp{ 231243396Sdavide struct smbnode *dnp, *np; 23275374Sbp struct vnode *vp; 233243396Sdavide int error, sep; 23475374Sbp 235243396Sdavide dnp = (dvp) ? VTOSMB(dvp) : NULL; 236243396Sdavide sep = 0; 237243396Sdavide if (dnp != NULL) { 238243396Sdavide sep = SMBFS_DNP_SEP(dnp); 239243396Sdavide error = smbfs_node_alloc(mp, dvp, dnp->n_rpath, dnp->n_rplen, 240243396Sdavide name, nmlen, sep, fap, &vp); 241243396Sdavide } else 242243396Sdavide error = smbfs_node_alloc(mp, NULL, "\\", 1, name, nmlen, 243243396Sdavide sep, fap, &vp); 24475374Sbp if (error) 24575374Sbp return error; 246243396Sdavide MPASS(vp != NULL); 24775374Sbp np = VTOSMB(vp); 24875374Sbp if (fap) 24975374Sbp smbfs_attr_cacheenter(vp, fap); 25075374Sbp *vpp = vp; 25175374Sbp return 0; 25275374Sbp} 25375374Sbp 25475374Sbp/* 25575374Sbp * Free smbnode, and give vnode back to system 25675374Sbp */ 25775374Sbpint 25875374Sbpsmbfs_reclaim(ap) 25975374Sbp struct vop_reclaim_args /* { 26075374Sbp struct vnode *a_vp; 26187194Sbp struct thread *a_p; 26275374Sbp } */ *ap; 26375374Sbp{ 26475374Sbp struct vnode *vp = ap->a_vp; 26575374Sbp struct vnode *dvp; 26675374Sbp struct smbnode *np = VTOSMB(vp); 26775374Sbp struct smbmount *smp = VTOSMBFS(vp); 26875374Sbp 269103936Sjeff SMBVDEBUG("%s,%d\n", np->n_name, vrefcnt(vp)); 27075374Sbp 271116486Stjr KASSERT((np->n_flag & NOPEN) == 0, ("file not closed before reclaim")); 272116486Stjr 273154487Salfred /* 274154487Salfred * Destroy the vm object and flush associated pages. 275154487Salfred */ 276154487Salfred vnode_destroy_vobject(vp); 27775374Sbp dvp = (np->n_parent && (np->n_flag & NREFPARENT)) ? 278107821Stjr np->n_parent : NULL; 279242387Sdavide 280242387Sdavide /* 281242387Sdavide * Remove the vnode from its hash chain. 282242387Sdavide */ 283242387Sdavide vfs_hash_remove(vp); 28475374Sbp if (np->n_name) 28575374Sbp smbfs_name_free(np->n_name); 286243396Sdavide if (np->n_rpath) 287243396Sdavide free(np->n_rpath, M_SMBNODENAME); 288184205Sdes free(np, M_SMBNODE); 289242387Sdavide vp->v_data = NULL; 290107890Stjr if (dvp != NULL) { 291107890Stjr vrele(dvp); 292107890Stjr /* 293107890Stjr * Indicate that we released something; see comment 294107890Stjr * in smbfs_unmount(). 295107890Stjr */ 296107890Stjr smp->sm_didrele = 1; 29775374Sbp } 29875374Sbp return 0; 29975374Sbp} 30075374Sbp 30175374Sbpint 30275374Sbpsmbfs_inactive(ap) 30375374Sbp struct vop_inactive_args /* { 30475374Sbp struct vnode *a_vp; 30587194Sbp struct thread *a_td; 30675374Sbp } */ *ap; 30775374Sbp{ 30887194Sbp struct thread *td = ap->a_td; 30991406Sjhb struct ucred *cred = td->td_ucred; 31075374Sbp struct vnode *vp = ap->a_vp; 31175374Sbp struct smbnode *np = VTOSMB(vp); 312242386Sdavide struct smb_cred *scred; 313116486Stjr struct vattr va; 31475374Sbp 315103936Sjeff SMBVDEBUG("%s: %d\n", VTOSMB(vp)->n_name, vrefcnt(vp)); 316116486Stjr if ((np->n_flag & NOPEN) != 0) { 317242386Sdavide scred = smbfs_malloc_scred(); 318242386Sdavide smb_makescred(scred, td, cred); 319140223Sphk smbfs_vinvalbuf(vp, td); 320116486Stjr if (vp->v_type == VREG) { 321182371Sattilio VOP_GETATTR(vp, &va, cred); 322116486Stjr smbfs_smb_close(np->n_mount->sm_share, np->n_fid, 323242386Sdavide &np->n_mtime, scred); 324116486Stjr } else if (vp->v_type == VDIR) { 325116486Stjr if (np->n_dirseq != NULL) { 326242386Sdavide smbfs_findclose(np->n_dirseq, scred); 327116486Stjr np->n_dirseq = NULL; 328116486Stjr } 329116486Stjr } 330116486Stjr np->n_flag &= ~NOPEN; 331124115Stjr smbfs_attr_cacheremove(vp); 332242386Sdavide smbfs_free_scred(scred); 33375374Sbp } 334125637Stjr if (np->n_flag & NGONE) 335234607Strasz vrecycle(vp); 33675374Sbp return (0); 33775374Sbp} 33875374Sbp/* 33975374Sbp * routines to maintain vnode attributes cache 34075374Sbp * smbfs_attr_cacheenter: unpack np.i to vattr structure 34175374Sbp */ 34275374Sbpvoid 34375374Sbpsmbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap) 34475374Sbp{ 34575374Sbp struct smbnode *np = VTOSMB(vp); 34675374Sbp 34775374Sbp if (vp->v_type == VREG) { 34875374Sbp if (np->n_size != fap->fa_size) { 34975374Sbp np->n_size = fap->fa_size; 35075374Sbp vnode_pager_setsize(vp, np->n_size); 35175374Sbp } 35275374Sbp } else if (vp->v_type == VDIR) { 35375374Sbp np->n_size = 16384; /* should be a better way ... */ 35475374Sbp } else 35575374Sbp return; 35675374Sbp np->n_mtime = fap->fa_mtime; 35775374Sbp np->n_dosattr = fap->fa_attr; 35875374Sbp np->n_attrage = time_second; 35975374Sbp return; 36075374Sbp} 36175374Sbp 36275374Sbpint 36375374Sbpsmbfs_attr_cachelookup(struct vnode *vp, struct vattr *va) 36475374Sbp{ 36575374Sbp struct smbnode *np = VTOSMB(vp); 36675374Sbp struct smbmount *smp = VTOSMBFS(vp); 36775374Sbp int diff; 36875374Sbp 36975374Sbp diff = time_second - np->n_attrage; 37075374Sbp if (diff > 2) /* XXX should be configurable */ 37175374Sbp return ENOENT; 37275374Sbp va->va_type = vp->v_type; /* vnode type (for create) */ 373254627Sken va->va_flags = 0; /* flags defined for file */ 37475374Sbp if (vp->v_type == VREG) { 375138490Sphk va->va_mode = smp->sm_file_mode; /* files access mode and type */ 376254627Sken if (np->n_dosattr & SMB_FA_RDONLY) { 377163993Sbp va->va_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); 378254627Sken va->va_flags |= UF_READONLY; 379254627Sken } 38075374Sbp } else if (vp->v_type == VDIR) { 381138490Sphk va->va_mode = smp->sm_dir_mode; /* files access mode and type */ 38275374Sbp } else 38375374Sbp return EINVAL; 38475374Sbp va->va_size = np->n_size; 38575374Sbp va->va_nlink = 1; /* number of references to file */ 386138490Sphk va->va_uid = smp->sm_uid; /* owner user id */ 387138490Sphk va->va_gid = smp->sm_gid; /* owner group id */ 38875374Sbp va->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 38975374Sbp va->va_fileid = np->n_ino; /* file id */ 39075374Sbp if (va->va_fileid == 0) 39175374Sbp va->va_fileid = 2; 39275374Sbp va->va_blocksize = SSTOVC(smp->sm_share)->vc_txmax; 39375374Sbp va->va_mtime = np->n_mtime; 39475374Sbp va->va_atime = va->va_ctime = va->va_mtime; /* time file changed */ 39575374Sbp va->va_gen = VNOVAL; /* generation number of file */ 396254627Sken if (np->n_dosattr & SMB_FA_HIDDEN) 397254627Sken va->va_flags |= UF_HIDDEN; 398254627Sken if (np->n_dosattr & SMB_FA_SYSTEM) 399254627Sken va->va_flags |= UF_SYSTEM; 400254627Sken /* 401254627Sken * We don't set the archive bit for directories. 402254627Sken */ 403254627Sken if ((vp->v_type != VDIR) && (np->n_dosattr & SMB_FA_ARCHIVE)) 404254627Sken va->va_flags |= UF_ARCHIVE; 405183214Skib va->va_rdev = NODEV; /* device the special file represents */ 40675374Sbp va->va_bytes = va->va_size; /* bytes of disk space held by file */ 40775374Sbp va->va_filerev = 0; /* file modification number */ 40875374Sbp va->va_vaflags = 0; /* operations flags */ 40975374Sbp return 0; 41075374Sbp} 411