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