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); 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 vn_set_state(vp, VSTATE_CONSTRUCTED); 221 error = vfs_hash_insert(vp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, 222 td, &vp2, smbfs_vnode_cmp, &sc); 223 if (error) 224 return (error); 225 if (vp2 != NULL) 226 *vpp = vp2; 227 return (0); 228 } 229 230 int 231 smbfs_nget(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, 232 struct smbfattr *fap, struct vnode **vpp) 233 { 234 struct smbnode *dnp; 235 struct vnode *vp; 236 int error, sep; 237 238 dnp = (dvp) ? VTOSMB(dvp) : NULL; 239 sep = 0; 240 if (dnp != NULL) { 241 sep = SMBFS_DNP_SEP(dnp); 242 error = smbfs_node_alloc(mp, dvp, dnp->n_rpath, dnp->n_rplen, 243 name, nmlen, sep, fap, &vp); 244 } else 245 error = smbfs_node_alloc(mp, NULL, "\\", 1, name, nmlen, 246 sep, fap, &vp); 247 if (error) 248 return error; 249 MPASS(vp != NULL); 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(struct vop_reclaim_args *ap) 261 { 262 struct vnode *vp = ap->a_vp; 263 struct vnode *dvp; 264 struct smbnode *np = VTOSMB(vp); 265 struct smbmount *smp = VTOSMBFS(vp); 266 267 SMBVDEBUG("%s,%d\n", np->n_name, vrefcnt(vp)); 268 269 KASSERT((np->n_flag & NOPEN) == 0, ("file not closed before reclaim")); 270 271 dvp = (np->n_parent && (np->n_flag & NREFPARENT)) ? 272 np->n_parent : NULL; 273 274 /* 275 * Remove the vnode from its hash chain. 276 */ 277 vfs_hash_remove(vp); 278 if (np->n_name) 279 smbfs_name_free(np->n_name); 280 if (np->n_rpath) 281 free(np->n_rpath, M_SMBNODENAME); 282 free(np, M_SMBNODE); 283 vp->v_data = NULL; 284 if (dvp != NULL) { 285 vrele(dvp); 286 /* 287 * Indicate that we released something; see comment 288 * in smbfs_unmount(). 289 */ 290 smp->sm_didrele = 1; 291 } 292 return 0; 293 } 294 295 int 296 smbfs_inactive(struct vop_inactive_args *ap) 297 { 298 struct thread *td = curthread; 299 struct ucred *cred = td->td_ucred; 300 struct vnode *vp = ap->a_vp; 301 struct smbnode *np = VTOSMB(vp); 302 struct smb_cred *scred; 303 struct vattr va; 304 305 SMBVDEBUG("%s: %d\n", VTOSMB(vp)->n_name, vrefcnt(vp)); 306 if ((np->n_flag & NOPEN) != 0) { 307 scred = smbfs_malloc_scred(); 308 smb_makescred(scred, td, cred); 309 smbfs_vinvalbuf(vp, td); 310 if (vp->v_type == VREG) { 311 VOP_GETATTR(vp, &va, cred); 312 smbfs_smb_close(np->n_mount->sm_share, np->n_fid, 313 &np->n_mtime, scred); 314 } else if (vp->v_type == VDIR) { 315 if (np->n_dirseq != NULL) { 316 smbfs_findclose(np->n_dirseq, scred); 317 np->n_dirseq = NULL; 318 } 319 } 320 np->n_flag &= ~NOPEN; 321 smbfs_attr_cacheremove(vp); 322 smbfs_free_scred(scred); 323 } 324 if (np->n_flag & NGONE) 325 vrecycle(vp); 326 return (0); 327 } 328 /* 329 * routines to maintain vnode attributes cache 330 * smbfs_attr_cacheenter: unpack np.i to vattr structure 331 */ 332 void 333 smbfs_attr_cacheenter(struct vnode *vp, struct smbfattr *fap) 334 { 335 struct smbnode *np = VTOSMB(vp); 336 337 if (vp->v_type == VREG) { 338 if (np->n_size != fap->fa_size) { 339 np->n_size = fap->fa_size; 340 vnode_pager_setsize(vp, np->n_size); 341 } 342 } else if (vp->v_type == VDIR) { 343 np->n_size = 16384; /* should be a better way ... */ 344 } else 345 return; 346 np->n_mtime = fap->fa_mtime; 347 np->n_dosattr = fap->fa_attr; 348 np->n_attrage = time_second; 349 return; 350 } 351 352 int 353 smbfs_attr_cachelookup(struct vnode *vp, struct vattr *va) 354 { 355 struct smbnode *np = VTOSMB(vp); 356 struct smbmount *smp = VTOSMBFS(vp); 357 int diff; 358 359 diff = time_second - np->n_attrage; 360 if (diff > 2) /* XXX should be configurable */ 361 return ENOENT; 362 va->va_type = vp->v_type; /* vnode type (for create) */ 363 va->va_flags = 0; /* flags defined for file */ 364 if (vp->v_type == VREG) { 365 va->va_mode = smp->sm_file_mode; /* files access mode and type */ 366 if (np->n_dosattr & SMB_FA_RDONLY) { 367 va->va_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); 368 va->va_flags |= UF_READONLY; 369 } 370 } else if (vp->v_type == VDIR) { 371 va->va_mode = smp->sm_dir_mode; /* files access mode and type */ 372 } else 373 return EINVAL; 374 va->va_size = np->n_size; 375 va->va_nlink = 1; /* number of references to file */ 376 va->va_uid = smp->sm_uid; /* owner user id */ 377 va->va_gid = smp->sm_gid; /* owner group id */ 378 va->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 379 va->va_fileid = np->n_ino; /* file id */ 380 if (va->va_fileid == 0) 381 va->va_fileid = 2; 382 va->va_blocksize = SSTOVC(smp->sm_share)->vc_txmax; 383 va->va_mtime = np->n_mtime; 384 va->va_atime = va->va_ctime = va->va_mtime; /* time file changed */ 385 va->va_gen = VNOVAL; /* generation number of file */ 386 if (np->n_dosattr & SMB_FA_HIDDEN) 387 va->va_flags |= UF_HIDDEN; 388 if (np->n_dosattr & SMB_FA_SYSTEM) 389 va->va_flags |= UF_SYSTEM; 390 /* 391 * We don't set the archive bit for directories. 392 */ 393 if ((vp->v_type != VDIR) && (np->n_dosattr & SMB_FA_ARCHIVE)) 394 va->va_flags |= UF_ARCHIVE; 395 va->va_rdev = NODEV; /* device the special file represents */ 396 va->va_bytes = va->va_size; /* bytes of disk space held by file */ 397 va->va_filerev = 0; /* file modification number */ 398 va->va_vaflags = 0; /* operations flags */ 399 return 0; 400 } 401