1 /*- 2 * Copyright (c) 2000-2001 Boris Popov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/fnv_hash.h> 31 #include <sys/kernel.h> 32 #include <sys/lock.h> 33 #include <sys/malloc.h> 34 #include <sys/mount.h> 35 #include <sys/proc.h> 36 #include <sys/queue.h> 37 #include <sys/stat.h> 38 #include <sys/sx.h> 39 #include <sys/sysctl.h> 40 #include <sys/time.h> 41 #include <sys/vnode.h> 42 43 #include <netsmb/smb.h> 44 #include <netsmb/smb_conn.h> 45 #include <netsmb/smb_subr.h> 46 47 #include <vm/vm.h> 48 #include <vm/vm_extern.h> 49 /*#include <vm/vm_page.h> 50 #include <vm/vm_object.h>*/ 51 52 #include <fs/smbfs/smbfs.h> 53 #include <fs/smbfs/smbfs_node.h> 54 #include <fs/smbfs/smbfs_subr.h> 55 56 extern struct vop_vector smbfs_vnodeops; /* XXX -> .h file */ 57 58 static MALLOC_DEFINE(M_SMBNODE, "smbufs_node", "SMBFS vnode private part"); 59 static MALLOC_DEFINE(M_SMBNODENAME, "smbufs_nname", "SMBFS node name"); 60 61 u_int32_t __inline 62 smbfs_hash(const u_char *name, int nmlen) 63 { 64 return (fnv_32_buf(name, nmlen, FNV1_32_INIT)); 65 } 66 67 static char * 68 smbfs_name_alloc(const u_char *name, int nmlen) 69 { 70 u_char *cp; 71 72 nmlen++; 73 #ifdef SMBFS_NAME_DEBUG 74 cp = malloc(nmlen + 2 + sizeof(int), M_SMBNODENAME, M_WAITOK); 75 *(int*)cp = nmlen; 76 cp += sizeof(int); 77 cp[0] = 0xfc; 78 cp++; 79 bcopy(name, cp, nmlen - 1); 80 cp[nmlen] = 0xfe; 81 #else 82 cp = malloc(nmlen, M_SMBNODENAME, M_WAITOK); 83 bcopy(name, cp, nmlen - 1); 84 #endif 85 cp[nmlen - 1] = 0; 86 return cp; 87 } 88 89 static void 90 smbfs_name_free(u_char *name) 91 { 92 #ifdef SMBFS_NAME_DEBUG 93 int nmlen, slen; 94 u_char *cp; 95 96 cp = name; 97 cp--; 98 if (*cp != 0xfc) 99 panic("First byte of name entry '%s' corrupted", name); 100 cp -= sizeof(int); 101 nmlen = *(int*)cp; 102 slen = strlen(name) + 1; 103 if (nmlen != slen) 104 panic("Name length mismatch: was %d, now %d name '%s'", 105 nmlen, slen, name); 106 if (name[nmlen] != 0xfe) 107 panic("Last byte of name entry '%s' corrupted\n", name); 108 free(cp, M_SMBNODENAME); 109 #else 110 free(name, M_SMBNODENAME); 111 #endif 112 } 113 114 static int __inline 115 smbfs_vnode_cmp(struct vnode *vp, void *_sc) 116 { 117 struct smbnode *np; 118 struct smbcmp *sc; 119 120 np = (struct smbnode *) vp; 121 sc = (struct smbcmp *) _sc; 122 if (np->n_parent != sc->n_parent || np->n_nmlen != sc->n_nmlen || 123 bcmp(sc->n_name, np->n_name, sc->n_nmlen) != 0) 124 return 1; 125 return 0; 126 } 127 128 static int 129 smbfs_node_alloc(struct mount *mp, struct vnode *dvp, 130 const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) 131 { 132 struct vattr vattr; 133 struct thread *td = curthread; /* XXX */ 134 struct smbmount *smp = VFSTOSMBFS(mp); 135 struct smbnode *np, *dnp; 136 struct vnode *vp, *vp2; 137 struct smbcmp sc; 138 int error; 139 140 sc.n_parent = dvp; 141 sc.n_nmlen = nmlen; 142 sc.n_name = name; 143 *vpp = NULL; 144 if (smp->sm_root != NULL && dvp == NULL) { 145 SMBERROR("do not allocate root vnode twice!\n"); 146 return EINVAL; 147 } 148 if (nmlen == 2 && bcmp(name, "..", 2) == 0) { 149 if (dvp == NULL) 150 return EINVAL; 151 vp = VTOSMB(VTOSMB(dvp)->n_parent)->n_vnode; 152 error = vget(vp, LK_EXCLUSIVE, td); 153 if (error == 0) 154 *vpp = vp; 155 return error; 156 } else if (nmlen == 1 && name[0] == '.') { 157 SMBERROR("do not call me with dot!\n"); 158 return EINVAL; 159 } 160 dnp = dvp ? VTOSMB(dvp) : NULL; 161 if (dnp == NULL && dvp != NULL) { 162 vprint("smbfs_node_alloc: dead parent vnode", dvp); 163 return EINVAL; 164 } 165 *vpp = NULL; 166 error = vfs_hash_get(mp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, td, 167 vpp, smbfs_vnode_cmp, &sc); 168 if (error) 169 return (error); 170 if (*vpp) { 171 np = VTOSMB(*vpp); 172 /* Force cached attributes to be refreshed if stale. */ 173 (void)VOP_GETATTR(*vpp, &vattr, td->td_ucred); 174 /* 175 * If the file type on the server is inconsistent with 176 * what it was when we created the vnode, kill the 177 * bogus vnode now and fall through to the code below 178 * to create a new one with the right type. 179 */ 180 if (((*vpp)->v_type == VDIR && 181 (np->n_dosattr & SMB_FA_DIR) == 0) || 182 ((*vpp)->v_type == VREG && 183 (np->n_dosattr & SMB_FA_DIR) != 0)) { 184 vgone(*vpp); 185 vput(*vpp); 186 } 187 else { 188 SMBVDEBUG("vnode taken from the hashtable\n"); 189 return (0); 190 } 191 } 192 /* 193 * If we don't have node attributes, then it is an explicit lookup 194 * for an existing vnode. 195 */ 196 if (fap == NULL) 197 return ENOENT; 198 199 error = getnewvnode("smbfs", mp, &smbfs_vnodeops, vpp); 200 if (error) 201 return (error); 202 vp = *vpp; 203 np = malloc(sizeof *np, M_SMBNODE, M_WAITOK | M_ZERO); 204 lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); 205 /* Vnode initialization */ 206 vp->v_type = fap->fa_attr & SMB_FA_DIR ? VDIR : VREG; 207 vp->v_data = np; 208 np->n_vnode = vp; 209 np->n_mount = VFSTOSMBFS(mp); 210 np->n_nmlen = nmlen; 211 np->n_name = smbfs_name_alloc(name, nmlen); 212 np->n_ino = fap->fa_ino; 213 if (dvp) { 214 ASSERT_VOP_LOCKED(dvp, "smbfs_node_alloc"); 215 np->n_parent = dvp; 216 if (/*vp->v_type == VDIR &&*/ (dvp->v_vflag & VV_ROOT) == 0) { 217 vref(dvp); 218 np->n_flag |= NREFPARENT; 219 } 220 } else if (vp->v_type == VREG) 221 SMBERROR("new vnode '%s' born without parent ?\n", np->n_name); 222 error = insmntque(vp, mp); 223 if (error) { 224 free(np, M_SMBNODE); 225 return (error); 226 } 227 error = vfs_hash_insert(vp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, 228 td, &vp2, smbfs_vnode_cmp, &sc); 229 if (error) 230 return (error); 231 if (vp2 != NULL) 232 *vpp = vp2; 233 return (0); 234 } 235 236 int 237 smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, 238 struct smbfattr *fap, struct vnode **vpp) 239 { 240 struct smbnode *np; 241 struct vnode *vp; 242 int error; 243 244 *vpp = NULL; 245 error = smbfs_node_alloc(mp, dvp, name, nmlen, fap, &vp); 246 if (error) 247 return error; 248 np = VTOSMB(vp); 249 if (fap) 250 smbfs_attr_cacheenter(vp, fap); 251 *vpp = vp; 252 return 0; 253 } 254 255 /* 256 * Free smbnode, and give vnode back to system 257 */ 258 int 259 smbfs_reclaim(ap) 260 struct vop_reclaim_args /* { 261 struct vnode *a_vp; 262 struct thread *a_p; 263 } */ *ap; 264 { 265 struct vnode *vp = ap->a_vp; 266 struct vnode *dvp; 267 struct smbnode *np = VTOSMB(vp); 268 struct smbmount *smp = VTOSMBFS(vp); 269 270 SMBVDEBUG("%s,%d\n", np->n_name, vrefcnt(vp)); 271 272 KASSERT((np->n_flag & NOPEN) == 0, ("file not closed before reclaim")); 273 274 /* 275 * Destroy the vm object and flush associated pages. 276 */ 277 vnode_destroy_vobject(vp); 278 dvp = (np->n_parent && (np->n_flag & NREFPARENT)) ? 279 np->n_parent : NULL; 280 281 /* 282 * Remove the vnode from its hash chain. 283 */ 284 vfs_hash_remove(vp); 285 if (np->n_name) 286 smbfs_name_free(np->n_name); 287 free(np, M_SMBNODE); 288 vp->v_data = NULL; 289 if (dvp != NULL) { 290 vrele(dvp); 291 /* 292 * Indicate that we released something; see comment 293 * in smbfs_unmount(). 294 */ 295 smp->sm_didrele = 1; 296 } 297 return 0; 298 } 299 300 int 301 smbfs_inactive(ap) 302 struct vop_inactive_args /* { 303 struct vnode *a_vp; 304 struct thread *a_td; 305 } */ *ap; 306 { 307 struct thread *td = ap->a_td; 308 struct ucred *cred = td->td_ucred; 309 struct vnode *vp = ap->a_vp; 310 struct smbnode *np = VTOSMB(vp); 311 struct smb_cred *scred; 312 struct vattr va; 313 314 SMBVDEBUG("%s: %d\n", VTOSMB(vp)->n_name, vrefcnt(vp)); 315 if ((np->n_flag & NOPEN) != 0) { 316 scred = smbfs_malloc_scred(); 317 smb_makescred(scred, td, cred); 318 smbfs_vinvalbuf(vp, td); 319 if (vp->v_type == VREG) { 320 VOP_GETATTR(vp, &va, cred); 321 smbfs_smb_close(np->n_mount->sm_share, np->n_fid, 322 &np->n_mtime, scred); 323 } else if (vp->v_type == VDIR) { 324 if (np->n_dirseq != NULL) { 325 smbfs_findclose(np->n_dirseq, scred); 326 np->n_dirseq = NULL; 327 } 328 } 329 np->n_flag &= ~NOPEN; 330 smbfs_attr_cacheremove(vp); 331 smbfs_free_scred(scred); 332 } 333 if (np->n_flag & NGONE) 334 vrecycle(vp); 335 return (0); 336 } 337 /* 338 * routines to maintain vnode attributes cache 339 * smbfs_attr_cacheenter: unpack np.i to vattr structure 340 */ 341 void 342 smbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap) 343 { 344 struct smbnode *np = VTOSMB(vp); 345 346 if (vp->v_type == VREG) { 347 if (np->n_size != fap->fa_size) { 348 np->n_size = fap->fa_size; 349 vnode_pager_setsize(vp, np->n_size); 350 } 351 } else if (vp->v_type == VDIR) { 352 np->n_size = 16384; /* should be a better way ... */ 353 } else 354 return; 355 np->n_mtime = fap->fa_mtime; 356 np->n_dosattr = fap->fa_attr; 357 np->n_attrage = time_second; 358 return; 359 } 360 361 int 362 smbfs_attr_cachelookup(struct vnode *vp, struct vattr *va) 363 { 364 struct smbnode *np = VTOSMB(vp); 365 struct smbmount *smp = VTOSMBFS(vp); 366 int diff; 367 368 diff = time_second - np->n_attrage; 369 if (diff > 2) /* XXX should be configurable */ 370 return ENOENT; 371 va->va_type = vp->v_type; /* vnode type (for create) */ 372 if (vp->v_type == VREG) { 373 va->va_mode = smp->sm_file_mode; /* files access mode and type */ 374 if (np->n_dosattr & SMB_FA_RDONLY) 375 va->va_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); 376 } else if (vp->v_type == VDIR) { 377 va->va_mode = smp->sm_dir_mode; /* files access mode and type */ 378 } else 379 return EINVAL; 380 va->va_size = np->n_size; 381 va->va_nlink = 1; /* number of references to file */ 382 va->va_uid = smp->sm_uid; /* owner user id */ 383 va->va_gid = smp->sm_gid; /* owner group id */ 384 va->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 385 va->va_fileid = np->n_ino; /* file id */ 386 if (va->va_fileid == 0) 387 va->va_fileid = 2; 388 va->va_blocksize = SSTOVC(smp->sm_share)->vc_txmax; 389 va->va_mtime = np->n_mtime; 390 va->va_atime = va->va_ctime = va->va_mtime; /* time file changed */ 391 va->va_gen = VNOVAL; /* generation number of file */ 392 va->va_flags = 0; /* flags defined for file */ 393 va->va_rdev = NODEV; /* device the special file represents */ 394 va->va_bytes = va->va_size; /* bytes of disk space held by file */ 395 va->va_filerev = 0; /* file modification number */ 396 va->va_vaflags = 0; /* operations flags */ 397 return 0; 398 } 399