1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 * 25 * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. 26 * All rights reserved. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/thread.h> 32 #include <sys/t_lock.h> 33 #include <sys/time.h> 34 #include <sys/vnode.h> 35 #include <sys/vfs.h> 36 #include <sys/errno.h> 37 #include <sys/buf.h> 38 #include <sys/stat.h> 39 #include <sys/cred.h> 40 #include <sys/kmem.h> 41 #include <sys/debug.h> 42 #include <sys/vmsystm.h> 43 #include <sys/flock.h> 44 #include <sys/share.h> 45 #include <sys/cmn_err.h> 46 #include <sys/tiuser.h> 47 #include <sys/sysmacros.h> 48 #include <sys/callb.h> 49 #include <sys/acl.h> 50 #include <sys/kstat.h> 51 #include <sys/signal.h> 52 #include <sys/list.h> 53 #include <sys/zone.h> 54 55 #include <netsmb/smb.h> 56 #include <netsmb/smb_conn.h> 57 #include <netsmb/smb_subr.h> 58 59 #include <smbfs/smbfs.h> 60 #include <smbfs/smbfs_node.h> 61 #include <smbfs/smbfs_subr.h> 62 63 #include <vm/hat.h> 64 #include <vm/as.h> 65 #include <vm/page.h> 66 #include <vm/pvn.h> 67 #include <vm/seg.h> 68 #include <vm/seg_map.h> 69 #include <vm/seg_vn.h> 70 71 static int smbfs_getattr_cache(vnode_t *, struct smbfattr *); 72 static int smbfattr_to_vattr(vnode_t *, struct smbfattr *, 73 struct vattr *); 74 75 /* 76 * The following code provide zone support in order to perform an action 77 * for each smbfs mount in a zone. This is also where we would add 78 * per-zone globals and kernel threads for the smbfs module (since 79 * they must be terminated by the shutdown callback). 80 */ 81 82 struct smi_globals { 83 kmutex_t smg_lock; /* lock protecting smg_list */ 84 list_t smg_list; /* list of SMBFS mounts in zone */ 85 boolean_t smg_destructor_called; 86 }; 87 typedef struct smi_globals smi_globals_t; 88 89 static zone_key_t smi_list_key; 90 91 /* 92 * Attributes caching: 93 * 94 * Attributes are cached in the smbnode in struct vattr form. 95 * There is a time associated with the cached attributes (r_attrtime) 96 * which tells whether the attributes are valid. The time is initialized 97 * to the difference between current time and the modify time of the vnode 98 * when new attributes are cached. This allows the attributes for 99 * files that have changed recently to be timed out sooner than for files 100 * that have not changed for a long time. There are minimum and maximum 101 * timeout values that can be set per mount point. 102 */ 103 104 /* 105 * Validate caches by checking cached attributes. If they have timed out 106 * get the attributes from the server and compare mtimes. If mtimes are 107 * different purge all caches for this vnode. 108 */ 109 int 110 smbfs_validate_caches( 111 struct vnode *vp, 112 cred_t *cr) 113 { 114 struct vattr va; 115 116 va.va_mask = AT_SIZE; 117 return (smbfsgetattr(vp, &va, cr)); 118 } 119 120 /* 121 * Purge all of the various data caches. 122 */ 123 /*ARGSUSED*/ 124 void 125 smbfs_purge_caches(struct vnode *vp) 126 { 127 /* 128 * NFS: Purge the DNLC for this vp, 129 * Clear any readdir state bits, 130 * the readlink response cache, ... 131 */ 132 smbnode_t *np = VTOSMB(vp); 133 134 /* 135 * Flush the page cache. 136 */ 137 if (vn_has_cached_data(vp)) { 138 (void) VOP_PUTPAGE(vp, (u_offset_t) 0, 0, B_INVAL, np->r_cred, NULL); 139 } 140 } 141 142 /* 143 * Check the attribute cache to see if the new attributes match 144 * those cached. If they do, the various `data' caches are 145 * considered to be good. Otherwise, purge the cached data. 146 */ 147 void 148 smbfs_cache_check( 149 struct vnode *vp, 150 struct smbfattr *fap) 151 { 152 smbnode_t *np; 153 int purge_data = 0; 154 int purge_acl = 0; 155 156 np = VTOSMB(vp); 157 mutex_enter(&np->r_statelock); 158 159 /* 160 * Compare with NFS macro: CACHE_VALID 161 * If the mtime or size has changed, 162 * purge cached data. 163 */ 164 if (np->r_attr.fa_mtime.tv_sec != fap->fa_mtime.tv_sec || 165 np->r_attr.fa_mtime.tv_nsec != fap->fa_mtime.tv_nsec) 166 purge_data = 1; 167 if (np->r_attr.fa_size != fap->fa_size) 168 purge_data = 1; 169 170 if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec || 171 np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec) 172 purge_acl = 1; 173 174 if (purge_acl) { 175 /* just invalidate r_secattr (XXX: OK?) */ 176 np->r_sectime = gethrtime(); 177 } 178 179 mutex_exit(&np->r_statelock); 180 181 if (purge_data) 182 smbfs_purge_caches(vp); 183 } 184 185 /* 186 * Set attributes cache for given vnode using vnode attributes. 187 * From NFS: nfs_attrcache_va 188 */ 189 #if 0 /* not yet (not sure if we need this) */ 190 void 191 smbfs_attrcache_va(vnode_t *vp, struct vattr *vap) 192 { 193 smbfattr_t fa; 194 smbnode_t *np; 195 196 vattr_to_fattr(vp, vap, &fa); 197 smbfs_attrcache_fa(vp, &fa); 198 } 199 #endif /* not yet */ 200 201 /* 202 * Set attributes cache for given vnode using SMB fattr 203 * and update the attribute cache timeout. 204 * 205 * From NFS: nfs_attrcache, nfs_attrcache_va 206 */ 207 void 208 smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap) 209 { 210 smbnode_t *np; 211 smbmntinfo_t *smi; 212 hrtime_t delta, now; 213 u_offset_t newsize; 214 vtype_t vtype, oldvt; 215 mode_t mode; 216 217 np = VTOSMB(vp); 218 smi = VTOSMI(vp); 219 220 /* 221 * We allow v_type to change, so set that here 222 * (and the mode, which depends on the type). 223 */ 224 if (fap->fa_attr & SMB_FA_DIR) { 225 vtype = VDIR; 226 mode = smi->smi_dmode; 227 } else { 228 vtype = VREG; 229 mode = smi->smi_fmode; 230 } 231 232 mutex_enter(&np->r_statelock); 233 now = gethrtime(); 234 235 /* 236 * Delta is the number of nanoseconds that we will 237 * cache the attributes of the file. It is based on 238 * the number of nanoseconds since the last time that 239 * we detected a change. The assumption is that files 240 * that changed recently are likely to change again. 241 * There is a minimum and a maximum for regular files 242 * and for directories which is enforced though. 243 * 244 * Using the time since last change was detected 245 * eliminates direct comparison or calculation 246 * using mixed client and server times. SMBFS 247 * does not make any assumptions regarding the 248 * client and server clocks being synchronized. 249 */ 250 if (fap->fa_mtime.tv_sec != np->r_attr.fa_mtime.tv_sec || 251 fap->fa_mtime.tv_nsec != np->r_attr.fa_mtime.tv_nsec || 252 fap->fa_size != np->r_attr.fa_size) 253 np->r_mtime = now; 254 255 if ((smi->smi_flags & SMI_NOAC) || (vp->v_flag & VNOCACHE)) 256 delta = 0; 257 else { 258 delta = now - np->r_mtime; 259 if (vtype == VDIR) { 260 if (delta < smi->smi_acdirmin) 261 delta = smi->smi_acdirmin; 262 else if (delta > smi->smi_acdirmax) 263 delta = smi->smi_acdirmax; 264 } else { 265 if (delta < smi->smi_acregmin) 266 delta = smi->smi_acregmin; 267 else if (delta > smi->smi_acregmax) 268 delta = smi->smi_acregmax; 269 } 270 } 271 272 np->r_attrtime = now + delta; 273 np->r_attr = *fap; 274 np->n_mode = mode; 275 oldvt = vp->v_type; 276 vp->v_type = vtype; 277 278 /* 279 * Shall we update r_size? (local notion of size) 280 * 281 * The real criteria for updating r_size should be: 282 * if the file has grown on the server, or if 283 * the client has not modified the file. 284 * 285 * Also deal with the fact that SMB presents 286 * directories as having size=0. Doing that 287 * here and leaving fa_size as returned OtW 288 * avoids fixing the size lots of places. 289 */ 290 newsize = fap->fa_size; 291 if (vtype == VDIR && newsize < DEV_BSIZE) 292 newsize = DEV_BSIZE; 293 294 if (np->r_size != newsize) { 295 if (!vn_has_cached_data(vp) 296 || (!(np->r_flags & RDIRTY)&& np->r_count == 0)) { 297 /* OK to set the size. */ 298 np->r_size = newsize; 299 } 300 } 301 302 /* NFS: np->r_flags &= ~RWRITEATTR; */ 303 np->n_flag &= ~NATTRCHANGED; 304 305 mutex_exit(&np->r_statelock); 306 307 if (oldvt != vtype) { 308 SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype); 309 } 310 } 311 312 /* 313 * Fill in attribute from the cache. 314 * 315 * If valid, copy to *fap and return zero, 316 * otherwise return an error. 317 * 318 * From NFS: nfs_getattr_cache() 319 */ 320 int 321 smbfs_getattr_cache(vnode_t *vp, struct smbfattr *fap) 322 { 323 smbnode_t *np; 324 int error; 325 326 np = VTOSMB(vp); 327 328 mutex_enter(&np->r_statelock); 329 if (gethrtime() >= np->r_attrtime) { 330 /* cache expired */ 331 error = ENOENT; 332 } else { 333 /* cache is valid */ 334 *fap = np->r_attr; 335 error = 0; 336 } 337 mutex_exit(&np->r_statelock); 338 339 return (error); 340 } 341 342 /* 343 * Get attributes over-the-wire and update attributes cache 344 * if no error occurred in the over-the-wire operation. 345 * Return 0 if successful, otherwise error. 346 * From NFS: nfs_getattr_otw 347 */ 348 int 349 smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr) 350 { 351 struct smbnode *np; 352 struct smb_cred scred; 353 int error; 354 355 np = VTOSMB(vp); 356 357 /* 358 * NFS uses the ACL rpc here (if smi_flags & SMI_ACL) 359 * With SMB, getting the ACL is a significantly more 360 * expensive operation, so we do that only when asked 361 * for the uid/gid. See smbfsgetattr(). 362 */ 363 364 /* Shared lock for (possible) n_fid use. */ 365 if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) 366 return (EINTR); 367 smb_credinit(&scred, cr); 368 369 bzero(fap, sizeof (*fap)); 370 error = smbfs_smb_getfattr(np, fap, &scred); 371 372 smb_credrele(&scred); 373 smbfs_rw_exit(&np->r_lkserlock); 374 375 if (error) { 376 /* NFS had: PURGE_STALE_FH(error, vp, cr) */ 377 smbfs_attrcache_remove(np); 378 if (error == ENOENT || error == ENOTDIR) { 379 /* 380 * Getattr failed because the object was 381 * removed or renamed by another client. 382 * Remove any cached attributes under it. 383 */ 384 smbfs_attrcache_prune(np); 385 } 386 return (error); 387 } 388 389 /* 390 * NFS: smbfs_cache_fattr(vap, fa, vap, t, cr); 391 * which did: fattr_to_vattr, nfs_attr_cache. 392 * We cache the fattr form, so just do the 393 * cache check and store the attributes. 394 */ 395 smbfs_cache_check(vp, fap); 396 smbfs_attrcache_fa(vp, fap); 397 398 return (0); 399 } 400 401 /* 402 * Return either cached or remote attributes. If get remote attr 403 * use them to check and invalidate caches, then cache the new attributes. 404 * 405 * From NFS: nfsgetattr() 406 */ 407 int 408 smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr) 409 { 410 struct smbfattr fa; 411 smbmntinfo_t *smi; 412 uint_t mask; 413 int error; 414 415 smi = VTOSMI(vp); 416 417 ASSERT(curproc->p_zone == smi->smi_zone_ref.zref_zone); 418 419 /* 420 * If asked for UID or GID, update n_uid, n_gid. 421 */ 422 mask = AT_ALL; 423 if (vap->va_mask & (AT_UID | AT_GID)) { 424 if (smi->smi_flags & SMI_ACL) 425 (void) smbfs_acl_getids(vp, cr); 426 /* else leave as set in make_smbnode */ 427 } else { 428 mask &= ~(AT_UID | AT_GID); 429 } 430 431 /* 432 * If we've got cached attributes, just use them; 433 * otherwise go to the server to get attributes, 434 * which will update the cache in the process. 435 */ 436 error = smbfs_getattr_cache(vp, &fa); 437 if (error) 438 error = smbfs_getattr_otw(vp, &fa, cr); 439 if (error) 440 return (error); 441 442 /* 443 * Re. client's view of the file size, see: 444 * smbfs_attrcache_fa, smbfs_getattr_otw 445 */ 446 447 error = smbfattr_to_vattr(vp, &fa, vap); 448 vap->va_mask = mask; 449 450 return (error); 451 } 452 453 454 /* 455 * Convert SMB over the wire attributes to vnode form. 456 * Returns 0 for success, error if failed (overflow, etc). 457 * From NFS: nattr_to_vattr() 458 */ 459 int 460 smbfattr_to_vattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap) 461 { 462 struct smbnode *np = VTOSMB(vp); 463 464 /* Set va_mask in caller */ 465 466 /* 467 * Take type, mode, uid, gid from the smbfs node, 468 * which has have been updated by _getattr_otw. 469 */ 470 vap->va_type = vp->v_type; 471 vap->va_mode = np->n_mode; 472 473 vap->va_uid = np->n_uid; 474 vap->va_gid = np->n_gid; 475 476 vap->va_fsid = vp->v_vfsp->vfs_dev; 477 vap->va_nodeid = np->n_ino; 478 vap->va_nlink = 1; 479 480 /* 481 * Difference from NFS here: We cache attributes as 482 * reported by the server, so r_attr.fa_size is the 483 * server's idea of the file size. This is called 484 * for getattr, so we want to return the client's 485 * idea of the file size. NFS deals with that in 486 * nfsgetattr(), the equivalent of our caller. 487 */ 488 vap->va_size = np->r_size; 489 490 /* 491 * Times. Note, already converted from NT to 492 * Unix form (in the unmarshalling code). 493 */ 494 vap->va_atime = fa->fa_atime; 495 vap->va_mtime = fa->fa_mtime; 496 vap->va_ctime = fa->fa_ctime; 497 498 /* 499 * rdev, blksize, seq are made up. 500 * va_nblocks is 512 byte blocks. 501 */ 502 vap->va_rdev = vp->v_rdev; 503 vap->va_blksize = MAXBSIZE; 504 vap->va_nblocks = (fsblkcnt64_t)btod(np->r_attr.fa_allocsz); 505 vap->va_seq = 0; 506 507 return (0); 508 } 509 510 511 /* 512 * SMB Client initialization and cleanup. 513 * Much of it is per-zone now. 514 */ 515 516 517 /* ARGSUSED */ 518 static void * 519 smbfs_zone_init(zoneid_t zoneid) 520 { 521 smi_globals_t *smg; 522 523 smg = kmem_alloc(sizeof (*smg), KM_SLEEP); 524 mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL); 525 list_create(&smg->smg_list, sizeof (smbmntinfo_t), 526 offsetof(smbmntinfo_t, smi_zone_node)); 527 smg->smg_destructor_called = B_FALSE; 528 return (smg); 529 } 530 531 /* 532 * Callback routine to tell all SMBFS mounts in the zone to stop creating new 533 * threads. Existing threads should exit. 534 */ 535 /* ARGSUSED */ 536 static void 537 smbfs_zone_shutdown(zoneid_t zoneid, void *data) 538 { 539 smi_globals_t *smg = data; 540 smbmntinfo_t *smi; 541 542 ASSERT(smg != NULL); 543 again: 544 mutex_enter(&smg->smg_lock); 545 for (smi = list_head(&smg->smg_list); smi != NULL; 546 smi = list_next(&smg->smg_list, smi)) { 547 548 /* 549 * If we've done the shutdown work for this FS, skip. 550 * Once we go off the end of the list, we're done. 551 */ 552 if (smi->smi_flags & SMI_DEAD) 553 continue; 554 555 /* 556 * We will do work, so not done. Get a hold on the FS. 557 */ 558 VFS_HOLD(smi->smi_vfsp); 559 560 mutex_enter(&smi->smi_lock); 561 smi->smi_flags |= SMI_DEAD; 562 mutex_exit(&smi->smi_lock); 563 564 /* 565 * Drop lock and release FS, which may change list, then repeat. 566 * We're done when every mi has been done or the list is empty. 567 */ 568 mutex_exit(&smg->smg_lock); 569 VFS_RELE(smi->smi_vfsp); 570 goto again; 571 } 572 mutex_exit(&smg->smg_lock); 573 } 574 575 static void 576 smbfs_zone_free_globals(smi_globals_t *smg) 577 { 578 list_destroy(&smg->smg_list); /* makes sure the list is empty */ 579 mutex_destroy(&smg->smg_lock); 580 kmem_free(smg, sizeof (*smg)); 581 582 } 583 584 /* ARGSUSED */ 585 static void 586 smbfs_zone_destroy(zoneid_t zoneid, void *data) 587 { 588 smi_globals_t *smg = data; 589 590 ASSERT(smg != NULL); 591 mutex_enter(&smg->smg_lock); 592 if (list_head(&smg->smg_list) != NULL) { 593 /* Still waiting for VFS_FREEVFS() */ 594 smg->smg_destructor_called = B_TRUE; 595 mutex_exit(&smg->smg_lock); 596 return; 597 } 598 smbfs_zone_free_globals(smg); 599 } 600 601 /* 602 * Add an SMBFS mount to the per-zone list of SMBFS mounts. 603 */ 604 void 605 smbfs_zonelist_add(smbmntinfo_t *smi) 606 { 607 smi_globals_t *smg; 608 609 smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone); 610 mutex_enter(&smg->smg_lock); 611 list_insert_head(&smg->smg_list, smi); 612 mutex_exit(&smg->smg_lock); 613 } 614 615 /* 616 * Remove an SMBFS mount from the per-zone list of SMBFS mounts. 617 */ 618 void 619 smbfs_zonelist_remove(smbmntinfo_t *smi) 620 { 621 smi_globals_t *smg; 622 623 smg = zone_getspecific(smi_list_key, smi->smi_zone_ref.zref_zone); 624 mutex_enter(&smg->smg_lock); 625 list_remove(&smg->smg_list, smi); 626 /* 627 * We can be called asynchronously by VFS_FREEVFS() after the zone 628 * shutdown/destroy callbacks have executed; if so, clean up the zone's 629 * smi_globals. 630 */ 631 if (list_head(&smg->smg_list) == NULL && 632 smg->smg_destructor_called == B_TRUE) { 633 smbfs_zone_free_globals(smg); 634 return; 635 } 636 mutex_exit(&smg->smg_lock); 637 } 638 639 #ifdef lint 640 #define NEED_SMBFS_CALLBACKS 1 641 #endif 642 643 #ifdef NEED_SMBFS_CALLBACKS 644 /* 645 * Call-back hooks for netsmb, in case we want them. 646 * Apple's VFS wants them. We may not need them. 647 */ 648 /*ARGSUSED*/ 649 static void smbfs_dead(smb_share_t *ssp) 650 { 651 /* 652 * Walk the mount list, finding all mounts 653 * using this share... 654 */ 655 } 656 657 /*ARGSUSED*/ 658 static void smbfs_cb_nop(smb_share_t *ss) 659 { 660 /* no-op */ 661 } 662 663 smb_fscb_t smbfs_cb = { 664 .fscb_disconn = smbfs_dead, 665 .fscb_connect = smbfs_cb_nop, 666 .fscb_down = smbfs_cb_nop, 667 .fscb_up = smbfs_cb_nop }; 668 669 #endif /* NEED_SMBFS_CALLBACKS */ 670 671 /* 672 * SMBFS Client initialization routine. This routine should only be called 673 * once. It performs the following tasks: 674 * - Initalize all global locks 675 * - Call sub-initialization routines (localize access to variables) 676 */ 677 int 678 smbfs_clntinit(void) 679 { 680 681 zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown, 682 smbfs_zone_destroy); 683 #ifdef NEED_SMBFS_CALLBACKS 684 (void) smb_fscb_set(&smbfs_cb); 685 #endif /* NEED_SMBFS_CALLBACKS */ 686 return (0); 687 } 688 689 /* 690 * This routine is called when the modunload is called. This will cleanup 691 * the previously allocated/initialized nodes. 692 */ 693 void 694 smbfs_clntfini(void) 695 { 696 #ifdef NEED_SMBFS_CALLBACKS 697 (void) smb_fscb_set(NULL); 698 #endif /* NEED_SMBFS_CALLBACKS */ 699 (void) zone_key_delete(smi_list_key); 700 }