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 2009 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 #if 0 /* not yet: ACL support */ 158 int purge_acl = 0; 159 vsecattr_t *vsp = NULL; 160 #endif /* not yet */ 161 162 np = VTOSMB(vp); 163 mutex_enter(&np->r_statelock); 164 165 /* 166 * Compare with NFS macro: CACHE_VALID 167 * If the mtime or size has changed, 168 * purge cached data. 169 */ 170 if (np->r_attr.fa_mtime.tv_sec != fap->fa_mtime.tv_sec || 171 np->r_attr.fa_mtime.tv_nsec != fap->fa_mtime.tv_nsec) 172 purge_data = 1; 173 if (np->r_attr.fa_size != fap->fa_size) 174 purge_data = 1; 175 176 #if 0 /* not yet: ACL support */ 177 if (np->r_attr.fa_ctime.tv_sec != fap->fa_ctime.tv_sec || 178 np->r_attr.fa_ctime.tv_nsec != fap->fa_ctime.tv_nsec) 179 purge_acl = 1; 180 #endif /* not yet */ 181 182 mutex_exit(&np->r_statelock); 183 184 if (purge_data) 185 smbfs_purge_caches(vp); 186 187 #if 0 /* not yet: ACL support */ 188 if (purge_acl) { 189 vsecattr_t *vsp; 190 191 if (np->r_secattr != NULL) { 192 mutex_enter(&np->r_statelock); 193 vsp = np->r_secattr; 194 np->r_secattr = NULL; 195 mutex_exit(&np->r_statelock); 196 if (vsp != NULL) 197 smbfs_acl_free(vsp); 198 } 199 } 200 #endif /* not yet */ 201 } 202 203 /* 204 * Set attributes cache for given vnode using vnode attributes. 205 * From NFS: nfs_attrcache_va 206 */ 207 #if 0 /* not yet (not sure if we need this) */ 208 void 209 smbfs_attrcache_va(vnode_t *vp, struct vattr *vap) 210 { 211 smbfattr_t fa; 212 smbnode_t *np; 213 214 vattr_to_fattr(vp, vap, &fa); 215 smbfs_attrcache_fa(vp, &fa); 216 } 217 #endif /* not yet */ 218 219 /* 220 * Set attributes cache for given vnode using SMB fattr 221 * and update the attribute cache timeout. 222 * 223 * From NFS: nfs_attrcache, nfs_attrcache_va 224 */ 225 void 226 smbfs_attrcache_fa(vnode_t *vp, struct smbfattr *fap) 227 { 228 smbnode_t *np; 229 smbmntinfo_t *smi; 230 hrtime_t delta, now; 231 u_offset_t newsize; 232 vtype_t vtype, oldvt; 233 mode_t mode; 234 235 np = VTOSMB(vp); 236 smi = VTOSMI(vp); 237 238 /* 239 * We allow v_type to change, so set that here 240 * (and the mode, which is derived from it). 241 */ 242 if (fap->fa_attr & SMB_FA_DIR) { 243 vtype = VDIR; 244 mode = S_IFDIR | smi->smi_dmode; 245 } else { 246 vtype = VREG; 247 mode = S_IFREG | smi->smi_fmode; 248 } 249 250 /* 251 * For now, n_uid/n_gid never change after they are 252 * set by: smbfs_node_findcreate / make_smbnode. 253 * Later, they will change in getsecattr. 254 */ 255 256 mutex_enter(&np->r_statelock); 257 258 now = gethrtime(); 259 260 /* 261 * Delta is the number of nanoseconds that we will 262 * cache the attributes of the file. It is based on 263 * the number of nanoseconds since the last time that 264 * we detected a change. The assumption is that files 265 * that changed recently are likely to change again. 266 * There is a minimum and a maximum for regular files 267 * and for directories which is enforced though. 268 * 269 * Using the time since last change was detected 270 * eliminates direct comparison or calculation 271 * using mixed client and server times. SMBFS 272 * does not make any assumptions regarding the 273 * client and server clocks being synchronized. 274 */ 275 if (fap->fa_mtime.tv_sec != np->r_attr.fa_mtime.tv_sec || 276 fap->fa_mtime.tv_nsec != np->r_attr.fa_mtime.tv_nsec || 277 fap->fa_size != np->r_attr.fa_size) 278 np->r_mtime = now; 279 280 if ((smi->smi_flags & SMI_NOAC) || (vp->v_flag & VNOCACHE)) 281 delta = 0; 282 else { 283 delta = now - np->r_mtime; 284 if (vtype == VDIR) { 285 if (delta < smi->smi_acdirmin) 286 delta = smi->smi_acdirmin; 287 else if (delta > smi->smi_acdirmax) 288 delta = smi->smi_acdirmax; 289 } else { 290 if (delta < smi->smi_acregmin) 291 delta = smi->smi_acregmin; 292 else if (delta > smi->smi_acregmax) 293 delta = smi->smi_acregmax; 294 } 295 } 296 297 np->r_attrtime = now + delta; 298 np->r_attr = *fap; 299 np->n_mode = mode; 300 oldvt = vp->v_type; 301 vp->v_type = vtype; 302 303 /* 304 * Shall we update r_size? (local notion of size) 305 * 306 * The real criteria for updating r_size should be: 307 * if the file has grown on the server, or if 308 * the client has not modified the file. 309 * 310 * Also deal with the fact that SMB presents 311 * directories as having size=0. Doing that 312 * here and leaving fa_size as returned OtW 313 * avoids fixing the size lots of places. 314 */ 315 newsize = fap->fa_size; 316 if (vtype == VDIR && newsize < DEV_BSIZE) 317 newsize = DEV_BSIZE; 318 319 if (np->r_size != newsize) { 320 #if 0 /* not yet: mmap support */ 321 if (!vn_has_cached_data(vp) || ...) 322 /* XXX: See NFS page cache code. */ 323 #endif /* not yet */ 324 /* OK to set the size. */ 325 np->r_size = newsize; 326 } 327 328 /* NFS: np->r_flags &= ~RWRITEATTR; */ 329 np->n_flag &= ~NATTRCHANGED; 330 331 mutex_exit(&np->r_statelock); 332 333 if (oldvt != vtype) { 334 SMBVDEBUG("vtype change %d to %d\n", oldvt, vtype); 335 } 336 } 337 338 /* 339 * Fill in attribute from the cache. 340 * 341 * If valid, copy to *fap and return zero, 342 * otherwise return an error. 343 * 344 * From NFS: nfs_getattr_cache() 345 */ 346 int 347 smbfs_getattr_cache(vnode_t *vp, struct smbfattr *fap) 348 { 349 smbnode_t *np; 350 int error; 351 352 np = VTOSMB(vp); 353 354 mutex_enter(&np->r_statelock); 355 if (gethrtime() >= np->r_attrtime) { 356 /* cache expired */ 357 error = ENOENT; 358 } else { 359 /* cache is valid */ 360 *fap = np->r_attr; 361 error = 0; 362 } 363 mutex_exit(&np->r_statelock); 364 365 return (error); 366 } 367 368 /* 369 * Get attributes over-the-wire and update attributes cache 370 * if no error occurred in the over-the-wire operation. 371 * Return 0 if successful, otherwise error. 372 * From NFS: nfs_getattr_otw 373 */ 374 int 375 smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr) 376 { 377 struct smbnode *np; 378 struct smb_cred scred; 379 int error; 380 381 np = VTOSMB(vp); 382 383 /* 384 * NFS uses the ACL rpc here 385 * (if smi_flags & SMI_ACL) 386 */ 387 388 /* Shared lock for (possible) n_fid use. */ 389 if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) 390 return (EINTR); 391 smb_credinit(&scred, cr); 392 393 bzero(fap, sizeof (*fap)); 394 error = smbfs_smb_getfattr(np, fap, &scred); 395 396 smb_credrele(&scred); 397 smbfs_rw_exit(&np->r_lkserlock); 398 399 if (error) { 400 /* NFS had: PURGE_STALE_FH(error, vp, cr) */ 401 smbfs_attrcache_remove(np); 402 if (error == ENOENT || error == ENOTDIR) { 403 /* 404 * Getattr failed because the object was 405 * removed or renamed by another client. 406 * Remove any cached attributes under it. 407 */ 408 smbfs_attrcache_prune(np); 409 } 410 return (error); 411 } 412 413 /* 414 * NFS: smbfs_cache_fattr(vap, fa, vap, t, cr); 415 * which did: fattr_to_vattr, nfs_attr_cache. 416 * We cache the fattr form, so just do the 417 * cache check and store the attributes. 418 */ 419 smbfs_cache_check(vp, fap); 420 smbfs_attrcache_fa(vp, fap); 421 422 return (0); 423 } 424 425 /* 426 * Return either cached or remote attributes. If get remote attr 427 * use them to check and invalidate caches, then cache the new attributes. 428 * 429 * From NFS: nfsgetattr() 430 */ 431 int 432 smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr) 433 { 434 struct smbfattr fa; 435 int error; 436 437 ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone); 438 439 /* 440 * If we've got cached attributes, just use them; 441 * otherwise go to the server to get attributes, 442 * which will update the cache in the process. 443 */ 444 error = smbfs_getattr_cache(vp, &fa); 445 if (error) 446 error = smbfs_getattr_otw(vp, &fa, cr); 447 if (error) 448 return (error); 449 450 /* 451 * Re. client's view of the file size, see: 452 * smbfs_attrcache_fa, smbfs_getattr_otw 453 */ 454 455 error = smbfattr_to_vattr(vp, &fa, vap); 456 return (error); 457 } 458 459 460 /* 461 * Convert SMB over the wire attributes to vnode form. 462 * Returns 0 for success, error if failed (overflow, etc). 463 * From NFS: nattr_to_vattr() 464 */ 465 int 466 smbfattr_to_vattr(vnode_t *vp, struct smbfattr *fa, struct vattr *vap) 467 { 468 struct smbnode *np = VTOSMB(vp); 469 470 vap->va_mask = AT_ALL; 471 472 /* 473 * Take type, mode, uid, gid from the smbfs node, 474 * which has have been updated by _getattr_otw. 475 */ 476 vap->va_type = vp->v_type; 477 vap->va_mode = np->n_mode; 478 479 vap->va_uid = np->n_uid; 480 vap->va_gid = np->n_gid; 481 482 vap->va_fsid = vp->v_vfsp->vfs_dev; 483 vap->va_nodeid = np->n_ino; 484 vap->va_nlink = 1; 485 486 /* 487 * Difference from NFS here: We cache attributes as 488 * reported by the server, so r_attr.fa_size is the 489 * server's idea of the file size. This is called 490 * for getattr, so we want to return the client's 491 * idea of the file size. NFS deals with that in 492 * nfsgetattr(), the equivalent of our caller. 493 */ 494 vap->va_size = np->r_size; 495 496 /* 497 * Times. Note, already converted from NT to 498 * Unix form (in the unmarshalling code). 499 */ 500 vap->va_atime = fa->fa_atime; 501 vap->va_mtime = fa->fa_mtime; 502 vap->va_ctime = fa->fa_ctime; 503 504 /* 505 * rdev, blksize, seq are made up. 506 * va_nblocks is 512 byte blocks. 507 */ 508 vap->va_rdev = vp->v_rdev; 509 vap->va_blksize = MAXBSIZE; 510 vap->va_nblocks = (fsblkcnt64_t)btod(np->r_attr.fa_allocsz); 511 vap->va_seq = 0; 512 513 return (0); 514 } 515 516 517 /* 518 * SMB Client initialization and cleanup. 519 * Much of it is per-zone now. 520 */ 521 522 523 /* ARGSUSED */ 524 static void * 525 smbfs_zone_init(zoneid_t zoneid) 526 { 527 smi_globals_t *smg; 528 529 smg = kmem_alloc(sizeof (*smg), KM_SLEEP); 530 mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL); 531 list_create(&smg->smg_list, sizeof (smbmntinfo_t), 532 offsetof(smbmntinfo_t, smi_zone_node)); 533 smg->smg_destructor_called = B_FALSE; 534 return (smg); 535 } 536 537 /* 538 * Callback routine to tell all SMBFS mounts in the zone to stop creating new 539 * threads. Existing threads should exit. 540 */ 541 /* ARGSUSED */ 542 static void 543 smbfs_zone_shutdown(zoneid_t zoneid, void *data) 544 { 545 smi_globals_t *smg = data; 546 smbmntinfo_t *smi; 547 548 ASSERT(smg != NULL); 549 again: 550 mutex_enter(&smg->smg_lock); 551 for (smi = list_head(&smg->smg_list); smi != NULL; 552 smi = list_next(&smg->smg_list, smi)) { 553 554 /* 555 * If we've done the shutdown work for this FS, skip. 556 * Once we go off the end of the list, we're done. 557 */ 558 if (smi->smi_flags & SMI_DEAD) 559 continue; 560 561 /* 562 * We will do work, so not done. Get a hold on the FS. 563 */ 564 VFS_HOLD(smi->smi_vfsp); 565 566 mutex_enter(&smi->smi_lock); 567 smi->smi_flags |= SMI_DEAD; 568 mutex_exit(&smi->smi_lock); 569 570 /* 571 * Drop lock and release FS, which may change list, then repeat. 572 * We're done when every mi has been done or the list is empty. 573 */ 574 mutex_exit(&smg->smg_lock); 575 VFS_RELE(smi->smi_vfsp); 576 goto again; 577 } 578 mutex_exit(&smg->smg_lock); 579 } 580 581 static void 582 smbfs_zone_free_globals(smi_globals_t *smg) 583 { 584 list_destroy(&smg->smg_list); /* makes sure the list is empty */ 585 mutex_destroy(&smg->smg_lock); 586 kmem_free(smg, sizeof (*smg)); 587 588 } 589 590 /* ARGSUSED */ 591 static void 592 smbfs_zone_destroy(zoneid_t zoneid, void *data) 593 { 594 smi_globals_t *smg = data; 595 596 ASSERT(smg != NULL); 597 mutex_enter(&smg->smg_lock); 598 if (list_head(&smg->smg_list) != NULL) { 599 /* Still waiting for VFS_FREEVFS() */ 600 smg->smg_destructor_called = B_TRUE; 601 mutex_exit(&smg->smg_lock); 602 return; 603 } 604 smbfs_zone_free_globals(smg); 605 } 606 607 /* 608 * Add an SMBFS mount to the per-zone list of SMBFS mounts. 609 */ 610 void 611 smbfs_zonelist_add(smbmntinfo_t *smi) 612 { 613 smi_globals_t *smg; 614 615 smg = zone_getspecific(smi_list_key, smi->smi_zone); 616 mutex_enter(&smg->smg_lock); 617 list_insert_head(&smg->smg_list, smi); 618 mutex_exit(&smg->smg_lock); 619 } 620 621 /* 622 * Remove an SMBFS mount from the per-zone list of SMBFS mounts. 623 */ 624 void 625 smbfs_zonelist_remove(smbmntinfo_t *smi) 626 { 627 smi_globals_t *smg; 628 629 smg = zone_getspecific(smi_list_key, smi->smi_zone); 630 mutex_enter(&smg->smg_lock); 631 list_remove(&smg->smg_list, smi); 632 /* 633 * We can be called asynchronously by VFS_FREEVFS() after the zone 634 * shutdown/destroy callbacks have executed; if so, clean up the zone's 635 * smi_globals. 636 */ 637 if (list_head(&smg->smg_list) == NULL && 638 smg->smg_destructor_called == B_TRUE) { 639 smbfs_zone_free_globals(smg); 640 return; 641 } 642 mutex_exit(&smg->smg_lock); 643 } 644 645 #ifdef lint 646 #define NEED_SMBFS_CALLBACKS 1 647 #endif 648 649 #ifdef NEED_SMBFS_CALLBACKS 650 /* 651 * Call-back hooks for netsmb, in case we want them. 652 * Apple's VFS wants them. We may not need them. 653 */ 654 /*ARGSUSED*/ 655 static void smbfs_dead(smb_share_t *ssp) 656 { 657 /* 658 * Walk the mount list, finding all mounts 659 * using this share... 660 */ 661 } 662 663 /*ARGSUSED*/ 664 static void smbfs_cb_nop(smb_share_t *ss) 665 { 666 /* no-op */ 667 } 668 669 smb_fscb_t smbfs_cb = { 670 .fscb_disconn = smbfs_dead, 671 .fscb_connect = smbfs_cb_nop, 672 .fscb_down = smbfs_cb_nop, 673 .fscb_up = smbfs_cb_nop }; 674 675 #endif /* NEED_SMBFS_CALLBACKS */ 676 677 /* 678 * SMBFS Client initialization routine. This routine should only be called 679 * once. It performs the following tasks: 680 * - Initalize all global locks 681 * - Call sub-initialization routines (localize access to variables) 682 */ 683 int 684 smbfs_clntinit(void) 685 { 686 687 zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown, 688 smbfs_zone_destroy); 689 #ifdef NEED_SMBFS_CALLBACKS 690 (void) smb_fscb_set(&smbfs_cb); 691 #endif /* NEED_SMBFS_CALLBACKS */ 692 return (0); 693 } 694 695 /* 696 * This routine is called when the modunload is called. This will cleanup 697 * the previously allocated/initialized nodes. 698 */ 699 void 700 smbfs_clntfini(void) 701 { 702 #ifdef NEED_SMBFS_CALLBACKS 703 (void) smb_fscb_set(NULL); 704 #endif /* NEED_SMBFS_CALLBACKS */ 705 (void) zone_key_delete(smi_list_key); 706 } 707