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