1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 14 */ 15 16 /* 17 * SMB2 Durable Handle support 18 */ 19 20 #include <sys/types.h> 21 #include <sys/cmn_err.h> 22 #include <sys/fcntl.h> 23 #include <sys/nbmlock.h> 24 #include <sys/sid.h> 25 #include <smbsrv/string.h> 26 #include <smbsrv/smb_kproto.h> 27 #include <smbsrv/smb_fsops.h> 28 #include <smbsrv/smbinfo.h> 29 #include <smbsrv/smb2_kproto.h> 30 31 /* Windows default values from [MS-SMB2] */ 32 /* 33 * (times in seconds) 34 * resilient: 35 * MaxTimeout = 300 (win7+) 36 * if timeout > MaxTimeout, ERROR 37 * if timeout != 0, timeout = req.timeout 38 * if timeout == 0, timeout = (infinity) (Win7/w2k8r2) 39 * if timeout == 0, timeout = 120 (Win8+) 40 * v2: 41 * if timeout != 0, timeout = MIN(timeout, 300) (spec) 42 * if timeout != 0, timeout = timeout (win8/2k12) 43 * if timeout == 0, timeout = Share.CATimeout. \ 44 * if Share.CATimeout == 0, timeout = 60 (win8/w2k12) 45 * if timeout == 0, timeout = 180 (win8.1/w2k12r2) 46 * open.timeout = 60 (win8/w2k12r2) (i.e. we ignore the request) 47 * v1: 48 * open.timeout = 16 minutes 49 */ 50 51 uint32_t smb2_dh_def_timeout = 60 * MILLISEC; /* mSec. */ 52 uint32_t smb2_dh_max_timeout = 300 * MILLISEC; /* mSec. */ 53 54 uint32_t smb2_res_def_timeout = 120 * MILLISEC; /* mSec. */ 55 uint32_t smb2_res_max_timeout = 300 * MILLISEC; /* mSec. */ 56 57 uint32_t smb2_persist_timeout = 300 * MILLISEC; /* mSec. */ 58 59 /* Max. size of the file used to store a CA handle. */ 60 static uint32_t smb2_dh_max_cah_size = 64 * 1024; 61 static uint32_t smb2_ca_info_version = 1; 62 63 /* 64 * Want this to have invariant layout on disk, where the 65 * last two uint32_t values are stored as a uint64_t 66 */ 67 struct nvlk { 68 uint64_t lk_start; 69 uint64_t lk_len; 70 /* (lk_pid << 32) | lk_type */ 71 #ifdef _BIG_ENDIAN 72 uint32_t lk_pid, lk_type; 73 #else 74 uint32_t lk_type, lk_pid; 75 #endif 76 }; 77 78 static void smb2_dh_import_share(void *); 79 static smb_ofile_t *smb2_dh_import_handle(smb_request_t *, smb_node_t *, 80 uint64_t); 81 static int smb2_dh_read_nvlist(smb_request_t *, smb_node_t *, struct nvlist **); 82 static int smb2_dh_import_cred(smb_ofile_t *, char *); 83 84 #define DH_SN_SIZE 24 /* size of DH stream name buffers */ 85 /* 86 * Build the stream name used to store a CA handle. 87 * i.e. ":0123456789abcdef:$CA" 88 * Note: smb_fsop_create adds the SUNWsmb prefix, 89 * so we compose the name without the prefix. 90 */ 91 static inline void 92 smb2_dh_make_stream_name(char *buf, size_t buflen, uint64_t id) 93 { 94 ASSERT(buflen >= DH_SN_SIZE); 95 (void) snprintf(buf, buflen, 96 ":%016" PRIx64 ":$CA", id); 97 } 98 99 /* 100 * smb_dh_should_save 101 * 102 * During session tear-down, decide whether to keep a durable handle. 103 * 104 * There are two cases where we save durable handles: 105 * 1. An SMB2 LOGOFF request was received 106 * 2. An unexpected disconnect from the client 107 * Note: Specifying a PrevSessionID in session setup 108 * is considered a disconnect (we just haven't learned about it yet) 109 * In every other case, we close durable handles. 110 * 111 * [MS-SMB2] 3.3.5.6 SMB2_LOGOFF 112 * [MS-SMB2] 3.3.7.1 Handling Loss of a Connection 113 * 114 * If any of the following are true, preserve for reconnect: 115 * 116 * - Open.IsResilient is TRUE. 117 * 118 * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_BATCH and 119 * Open.OplockState == Held, and Open.IsDurable is TRUE. 120 * 121 * - Open.OplockLevel == SMB2_OPLOCK_LEVEL_LEASE, 122 * Lease.LeaseState SMB2_LEASE_HANDLE_CACHING, 123 * Open.OplockState == Held, and Open.IsDurable is TRUE. 124 * 125 * - Open.IsPersistent is TRUE. 126 * 127 * We also deal with some special cases for shutdown of the 128 * server, session, user, tree (in that order). Other than 129 * the cases above, shutdown (or forced termination) should 130 * destroy durable handles. 131 */ 132 boolean_t 133 smb_dh_should_save(smb_ofile_t *of) 134 { 135 ASSERT(MUTEX_HELD(&of->f_mutex)); 136 ASSERT(of->dh_vers != SMB2_NOT_DURABLE); 137 138 /* SMB service shutting down, destroy DH */ 139 if (of->f_server->sv_state == SMB_SERVER_STATE_STOPPING) 140 return (B_FALSE); 141 142 /* 143 * SMB Session (connection) going away (server up). 144 * If server initiated disconnect, destroy DH 145 * If client initiated disconnect, save all DH. 146 */ 147 if (of->f_session->s_state == SMB_SESSION_STATE_TERMINATED) 148 return (B_FALSE); 149 if (of->f_session->s_state == SMB_SESSION_STATE_DISCONNECTED) 150 return (B_TRUE); 151 152 /* 153 * SMB User logoff, session still "up". 154 * Action depends on why/how this logoff happened, 155 * determined based on user->preserve_opens 156 */ 157 if (of->f_user->u_state == SMB_USER_STATE_LOGGING_OFF) { 158 switch (of->f_user->preserve_opens) { 159 case SMB2_DH_PRESERVE_NONE: 160 /* Server-initiated */ 161 return (B_FALSE); 162 case SMB2_DH_PRESERVE_SOME: 163 /* Previous session logoff. */ 164 goto preserve_some; 165 case SMB2_DH_PRESERVE_ALL: 166 /* Protocol logoff request */ 167 return (B_TRUE); 168 } 169 } 170 171 /* 172 * SMB tree disconnecting (user still logged on) 173 * i.e. when kshare export forces disconnection. 174 */ 175 if (of->f_tree->t_state == SMB_TREE_STATE_DISCONNECTING) 176 return (B_FALSE); 177 178 preserve_some: 179 /* preserve_opens == SMB2_DH_PRESERVE_SOME */ 180 181 switch (of->dh_vers) { 182 case SMB2_RESILIENT: 183 return (B_TRUE); 184 185 case SMB2_DURABLE_V2: 186 if (of->dh_persist) 187 return (B_TRUE); 188 /* FALLTHROUGH */ 189 case SMB2_DURABLE_V1: 190 /* IS durable (v1 or v2) */ 191 if ((of->f_oplock.og_state & (OPLOCK_LEVEL_BATCH | 192 OPLOCK_LEVEL_CACHE_HANDLE)) != 0) 193 return (B_TRUE); 194 /* FALLTHROUGH */ 195 case SMB2_NOT_DURABLE: 196 default: 197 break; 198 } 199 200 return (B_FALSE); 201 } 202 203 /* 204 * Is this stream name a CA handle? i.e. 205 * ":0123456789abcdef:$CA" 206 */ 207 static boolean_t 208 smb2_dh_match_ca_name(const char *name, uint64_t *idp) 209 { 210 static const char suffix[] = ":$CA"; 211 u_longlong_t ull; 212 const char *p = name; 213 char *p2 = NULL; 214 int len, rc; 215 216 if (*p++ != ':') 217 return (B_FALSE); 218 219 rc = ddi_strtoull(p, &p2, 16, &ull); 220 if (rc != 0 || p2 != (p + 16)) 221 return (B_FALSE); 222 p += 16; 223 224 len = sizeof (suffix) - 1; 225 if (strncmp(p, suffix, len) != 0) 226 return (B_FALSE); 227 p += len; 228 229 if (*p != '\0') 230 return (B_FALSE); 231 232 *idp = (uint64_t)ull; 233 return (B_TRUE); 234 } 235 236 /* 237 * smb2_dh_new_ca_share 238 * 239 * Called when a new share has ca=true. Find or create the CA dir, 240 * and start a thread to import persistent handles. 241 */ 242 int 243 smb2_dh_new_ca_share(smb_server_t *sv, smb_kshare_t *shr) 244 { 245 smb_kshare_t *shr2; 246 smb_request_t *sr; 247 248 ASSERT(STYPE_ISDSK(shr->shr_type)); 249 250 /* 251 * Need to lookup the kshare again, to get a hold. 252 * Add a function to just get the hold? 253 */ 254 shr2 = smb_kshare_lookup(sv, shr->shr_name); 255 if (shr2 != shr) 256 return (EINVAL); 257 258 sr = smb_request_alloc(sv->sv_session, 0); 259 if (sr == NULL) { 260 /* shutting down? */ 261 smb_kshare_release(sv, shr); 262 return (EINTR); 263 } 264 sr->sr_state = SMB_REQ_STATE_SUBMITTED; 265 266 /* 267 * Mark this share as "busy importing persistent handles" 268 * so we can hold off tree connect until that's done. 269 * Will clear and wakeup below. 270 */ 271 mutex_enter(&shr->shr_mutex); 272 shr->shr_import_busy = sr; 273 mutex_exit(&shr->shr_mutex); 274 275 /* 276 * Start a taskq job to import any CA handles. 277 * The hold on the kshare is given to this job, 278 * which releases it when it's done. 279 */ 280 sr->arg.tcon.si = shr; /* hold from above */ 281 (void) taskq_dispatch( 282 sv->sv_worker_pool, 283 smb2_dh_import_share, sr, TQ_SLEEP); 284 285 return (0); 286 } 287 288 int smb2_dh_import_delay = 0; 289 290 static void 291 smb2_dh_import_share(void *arg) 292 { 293 smb_request_t *sr = arg; 294 smb_kshare_t *shr = sr->arg.tcon.si; 295 smb_node_t *snode; 296 cred_t *kcr = zone_kcred(); 297 smb_streaminfo_t *str_info = NULL; 298 uint64_t id; 299 smb_node_t *str_node; 300 smb_odir_t *od = NULL; 301 smb_ofile_t *of; 302 int rc; 303 boolean_t eof; 304 305 sr->sr_state = SMB_REQ_STATE_ACTIVE; 306 307 if (smb2_dh_import_delay > 0) 308 delay(SEC_TO_TICK(smb2_dh_import_delay)); 309 310 /* 311 * Borrow the server's "root" user. 312 * 313 * This takes the place of smb_session_lookup_ssnid() 314 * that would happen in smb2_dispatch for a normal SR. 315 * As usual, this hold is released in smb_request_free. 316 */ 317 sr->uid_user = sr->sr_server->sv_rootuser; 318 smb_user_hold_internal(sr->uid_user); 319 sr->user_cr = sr->uid_user->u_cred; 320 321 /* 322 * Create a temporary tree connect 323 */ 324 sr->arg.tcon.path = shr->shr_name; 325 sr->tid_tree = smb_tree_alloc(sr, shr, shr->shr_root_node, 326 ACE_ALL_PERMS, 0); 327 if (sr->tid_tree == NULL) { 328 cmn_err(CE_NOTE, "smb2_dh_import_share: " 329 "failed connect share <%s>", shr->shr_name); 330 goto out; 331 } 332 snode = sr->tid_tree->t_snode; 333 334 /* 335 * Get the buffers we'll use to read CA handle data. 336 * Stash in sr_request_buf for smb2_dh_import_handle(). 337 * Also a buffer for the stream name info. 338 */ 339 sr->sr_req_length = smb2_dh_max_cah_size; 340 sr->sr_request_buf = kmem_alloc(sr->sr_req_length, KM_SLEEP); 341 str_info = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP); 342 343 /* 344 * Open the ext. attr dir under the share root and 345 * import CA handles for this share. 346 */ 347 if (smb_odir_openat(sr, snode, &od) != 0) { 348 cmn_err(CE_NOTE, "Share [%s] CA import, no xattr dir?", 349 shr->shr_name); 350 goto out; 351 } 352 353 eof = B_FALSE; 354 do { 355 /* 356 * If the kshare gets unshared before we finish, 357 * bail out so we don't hold things up. 358 */ 359 if (shr->shr_flags & SMB_SHRF_REMOVED) 360 break; 361 362 /* 363 * Read a stream name and info 364 */ 365 rc = smb_odir_read_streaminfo(sr, od, str_info, &eof); 366 if ((rc != 0) || (eof)) 367 break; 368 369 /* 370 * Skip anything not a CA handle. 371 */ 372 if (!smb2_dh_match_ca_name(str_info->si_name, &id)) { 373 continue; 374 } 375 376 /* 377 * Lookup stream node and import 378 */ 379 str_node = NULL; 380 rc = smb_fsop_lookup_name(sr, kcr, SMB_CASE_SENSITIVE, 381 snode, snode, str_info->si_name, &str_node); 382 if (rc != 0) { 383 cmn_err(CE_NOTE, "Share [%s] CA import, " 384 "lookup <%s> failed rc=%d", 385 shr->shr_name, str_info->si_name, rc); 386 continue; 387 } 388 of = smb2_dh_import_handle(sr, str_node, id); 389 smb_node_release(str_node); 390 if (of != NULL) { 391 smb_ofile_release(of); 392 of = NULL; 393 } 394 sr->fid_ofile = NULL; 395 396 } while (!eof); 397 398 out: 399 if (od != NULL) { 400 smb_odir_close(od); 401 smb_odir_release(od); 402 } 403 404 if (str_info != NULL) 405 kmem_free(str_info, sizeof (smb_streaminfo_t)); 406 /* Let smb_request_free clean up sr->sr_request_buf */ 407 408 /* 409 * We did a (temporary, internal) tree connect above, 410 * which we need to undo before we return. Note that 411 * smb_request_free will do the final release of 412 * sr->tid_tree, sr->uid_user 413 */ 414 if (sr->tid_tree != NULL) 415 smb_tree_disconnect(sr->tid_tree, B_FALSE); 416 417 /* 418 * Wake up any waiting tree connect(s). 419 * See smb_tree_connect_disk(). 420 */ 421 mutex_enter(&shr->shr_mutex); 422 shr->shr_import_busy = NULL; 423 cv_broadcast(&shr->shr_cv); 424 mutex_exit(&shr->shr_mutex); 425 426 smb_kshare_release(sr->sr_server, shr); 427 smb_request_free(sr); 428 } 429 430 /* 431 * This returns the new ofile mostly for dtrace. 432 */ 433 static smb_ofile_t * 434 smb2_dh_import_handle(smb_request_t *sr, smb_node_t *str_node, 435 uint64_t persist_id) 436 { 437 uint8_t client_uuid[UUID_LEN]; 438 smb_tree_t *tree = sr->tid_tree; 439 smb_arg_open_t *op = &sr->arg.open; 440 smb_pathname_t *pn = &op->fqi.fq_path; 441 cred_t *kcr = zone_kcred(); 442 struct nvlist *nvl = NULL; 443 char *sidstr = NULL; 444 smb_ofile_t *of = NULL; 445 smb_attr_t *pa; 446 boolean_t did_open = B_FALSE; 447 boolean_t have_lease = B_FALSE; 448 hrtime_t hrt; 449 uint64_t *u64p; 450 uint64_t u64; 451 uint32_t u32; 452 uint32_t status; 453 char *s; 454 uint8_t *u8p; 455 uint_t alen; 456 int rc; 457 458 /* 459 * While we're called with arg.tcon, we now want to use 460 * smb_arg_open for the rest of import, so clear it. 461 */ 462 bzero(op, sizeof (*op)); 463 op->create_disposition = FILE_OPEN; 464 465 /* 466 * Read and unpack the NVL 467 */ 468 rc = smb2_dh_read_nvlist(sr, str_node, &nvl); 469 if (rc != 0) 470 return (NULL); 471 472 /* 473 * Known CA info version? 474 */ 475 u32 = 0; 476 rc = nvlist_lookup_uint32(nvl, "info_version", &u32); 477 if (rc != 0 || u32 != smb2_ca_info_version) { 478 cmn_err(CE_NOTE, "CA import (%s/%s) bad vers=%d", 479 tree->t_resource, str_node->od_name, u32); 480 goto errout; 481 } 482 483 /* 484 * The persist ID in the nvlist should match the one 485 * encoded in the file name. (not enforced) 486 */ 487 u64 = 0; 488 rc = nvlist_lookup_uint64(nvl, "file_persistid", &u64); 489 if (rc != 0 || u64 != persist_id) { 490 cmn_err(CE_WARN, "CA import (%s/%s) bad id=%016" PRIx64, 491 tree->t_resource, str_node->od_name, u64); 492 /* goto errout? (allow) */ 493 } 494 495 /* 496 * Does it belong in the share being imported? 497 */ 498 s = NULL; 499 rc = nvlist_lookup_string(nvl, "share_name", &s); 500 if (rc != 0) { 501 cmn_err(CE_NOTE, "CA import (%s/%s) no share_name", 502 tree->t_resource, str_node->od_name); 503 goto errout; 504 } 505 if (smb_strcasecmp(s, tree->t_sharename, 0) != 0) { 506 /* Normal (not an error) */ 507 #ifdef DEBUG 508 cmn_err(CE_NOTE, "CA import (%s/%s) other share", 509 tree->t_resource, str_node->od_name); 510 #endif 511 goto errout; 512 } 513 514 /* 515 * Get the path name (for lookup) 516 */ 517 rc = nvlist_lookup_string(nvl, "path_name", &pn->pn_path); 518 if (rc != 0) { 519 cmn_err(CE_NOTE, "CA import (%s/%s) no path_name", 520 tree->t_resource, str_node->od_name); 521 goto errout; 522 } 523 524 /* 525 * owner sid 526 */ 527 rc = nvlist_lookup_string(nvl, "owner_sid", &sidstr); 528 if (rc != 0) { 529 cmn_err(CE_NOTE, "CA import (%s/%s) no owner_sid", 530 tree->t_resource, str_node->od_name); 531 goto errout; 532 } 533 534 /* 535 * granted access 536 */ 537 rc = nvlist_lookup_uint32(nvl, 538 "granted_access", &op->desired_access); 539 if (rc != 0) { 540 cmn_err(CE_NOTE, "CA import (%s/%s) no granted_access", 541 tree->t_resource, str_node->od_name); 542 goto errout; 543 } 544 545 /* 546 * share access 547 */ 548 rc = nvlist_lookup_uint32(nvl, 549 "share_access", &op->share_access); 550 if (rc != 0) { 551 cmn_err(CE_NOTE, "CA import (%s/%s) no share_access", 552 tree->t_resource, str_node->od_name); 553 goto errout; 554 } 555 556 /* 557 * create options 558 */ 559 rc = nvlist_lookup_uint32(nvl, 560 "create_options", &op->create_options); 561 if (rc != 0) { 562 cmn_err(CE_NOTE, "CA import (%s/%s) no create_options", 563 tree->t_resource, str_node->od_name); 564 goto errout; 565 } 566 567 /* 568 * create guid (client-assigned) 569 */ 570 alen = UUID_LEN; 571 u8p = NULL; 572 rc = nvlist_lookup_uint8_array(nvl, "file_guid", &u8p, &alen); 573 if (rc != 0 || alen != UUID_LEN) { 574 cmn_err(CE_NOTE, "CA import (%s/%s) bad file_guid", 575 tree->t_resource, str_node->od_name); 576 goto errout; 577 } 578 bcopy(u8p, op->create_guid, UUID_LEN); 579 580 /* 581 * client uuid (identifies the client) 582 */ 583 alen = UUID_LEN; 584 u8p = NULL; 585 rc = nvlist_lookup_uint8_array(nvl, "client_uuid", &u8p, &alen); 586 if (rc != 0 || alen != UUID_LEN) { 587 cmn_err(CE_NOTE, "CA import (%s/%s) no client_uuid", 588 tree->t_resource, str_node->od_name); 589 goto errout; 590 } 591 bcopy(u8p, client_uuid, UUID_LEN); 592 593 /* 594 * Lease key (optional) 595 */ 596 alen = SMB_LEASE_KEY_SZ; 597 u8p = NULL; 598 rc = nvlist_lookup_uint8_array(nvl, "lease_uuid", &u8p, &alen); 599 if (rc == 0) { 600 bcopy(u8p, op->lease_key, UUID_LEN); 601 (void) nvlist_lookup_uint32(nvl, 602 "lease_state", &op->lease_state); 603 (void) nvlist_lookup_uint16(nvl, 604 "lease_epoch", &op->lease_epoch); 605 (void) nvlist_lookup_uint16(nvl, 606 "lease_version", &op->lease_version); 607 have_lease = B_TRUE; 608 } else { 609 (void) nvlist_lookup_uint32(nvl, 610 "oplock_state", &op->op_oplock_state); 611 } 612 613 /* 614 * Done getting what we need from the NV list. 615 * (re)open the file 616 */ 617 status = smb_common_open(sr); 618 if (status != 0) { 619 cmn_err(CE_NOTE, "CA import (%s/%s) open failed 0x%x", 620 tree->t_resource, str_node->od_name, status); 621 (void) smb_node_set_delete_on_close(str_node, kcr, 0); 622 goto errout; 623 } 624 of = sr->fid_ofile; 625 did_open = B_TRUE; 626 627 /* 628 * Now restore the rest of the SMB2 level state. 629 * See smb2_create after smb_common_open 630 */ 631 632 /* 633 * Setup of->f_cr with owner SID 634 */ 635 rc = smb2_dh_import_cred(of, sidstr); 636 if (rc != 0) { 637 cmn_err(CE_NOTE, "CA import (%s/%s) import cred failed", 638 tree->t_resource, str_node->od_name); 639 goto errout; 640 } 641 642 /* 643 * Use the persist ID we previously assigned. 644 * Like smb_ofile_set_persistid_ph() 645 */ 646 rc = smb_ofile_insert_persistid(of, persist_id); 647 if (rc != 0) { 648 cmn_err(CE_NOTE, "CA import (%s/%s) " 649 "insert_persistid rc=%d", 650 tree->t_resource, str_node->od_name, rc); 651 goto errout; 652 } 653 654 /* 655 * Like smb2_lease_create() 656 * 657 * Lease state is stored in each persistent handle, but 658 * only one handle has the state we want. As we import 659 * each handle, "upgrade" the lease if the handle we're 660 * importing has a "better" lease state (higher epoch or 661 * more cache rights). After all handles are imported, 662 * that will get the lease to the right state. 663 */ 664 if (have_lease) { 665 smb_lease_t *ls; 666 status = smb2_lease_create(sr, client_uuid); 667 if (status != 0) { 668 cmn_err(CE_NOTE, "CA import (%s/%s) get lease 0x%x", 669 tree->t_resource, str_node->od_name, status); 670 goto errout; 671 } 672 ls = of->f_lease; 673 674 /* Use most current "epoch". */ 675 mutex_enter(&ls->ls_mutex); 676 if (ls->ls_epoch < op->lease_epoch) 677 ls->ls_epoch = op->lease_epoch; 678 mutex_exit(&ls->ls_mutex); 679 680 /* 681 * Get the lease (and oplock) 682 * uses op->lease_state 683 */ 684 op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE; 685 smb2_lease_acquire(sr); 686 687 } else { 688 /* 689 * No lease; maybe get an oplock 690 * uses: op->op_oplock_level 691 */ 692 if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) { 693 op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH; 694 } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) { 695 op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; 696 } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) { 697 op->op_oplock_level = SMB2_OPLOCK_LEVEL_II; 698 } else { 699 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 700 } 701 smb2_oplock_acquire(sr); 702 } 703 704 /* 705 * Byte range locks 706 */ 707 alen = 0; 708 u64p = NULL; 709 if (nvlist_lookup_uint64_array(nvl, "locks", &u64p, &alen) == 0) { 710 uint_t i; 711 uint_t nlocks = alen / 3; 712 struct nvlk *nlp; 713 714 nlp = (struct nvlk *)u64p; 715 for (i = 0; i < nlocks; i++) { 716 status = smb_lock_range( 717 sr, 718 nlp->lk_start, 719 nlp->lk_len, 720 nlp->lk_pid, 721 nlp->lk_type, 722 0); 723 if (status != 0) { 724 cmn_err(CE_NOTE, "CA import (%s/%s) " 725 "get lock %d failed 0x%x", 726 tree->t_resource, 727 str_node->od_name, 728 i, status); 729 } 730 nlp++; 731 } 732 } 733 alen = SMB_OFILE_LSEQ_MAX; 734 u8p = NULL; 735 if (nvlist_lookup_uint8_array(nvl, "lockseq", &u8p, &alen) == 0) { 736 if (alen != SMB_OFILE_LSEQ_MAX) { 737 cmn_err(CE_NOTE, "CA import (%s/%s) " 738 "get lockseq bad len=%d", 739 tree->t_resource, 740 str_node->od_name, 741 alen); 742 } else { 743 mutex_enter(&of->f_mutex); 744 bcopy(u8p, of->f_lock_seq, alen); 745 mutex_exit(&of->f_mutex); 746 } 747 } 748 749 /* 750 * Optional "sticky" times (set pending attributes) 751 */ 752 mutex_enter(&of->f_mutex); 753 pa = &of->f_pending_attr; 754 if (nvlist_lookup_hrtime(nvl, "atime", &hrt) == 0) { 755 hrt2ts(hrt, &pa->sa_vattr.va_atime); 756 pa->sa_mask |= SMB_AT_ATIME; 757 } 758 if (nvlist_lookup_hrtime(nvl, "mtime", &hrt) == 0) { 759 hrt2ts(hrt, &pa->sa_vattr.va_mtime); 760 pa->sa_mask |= SMB_AT_MTIME; 761 } 762 if (nvlist_lookup_hrtime(nvl, "ctime", &hrt) == 0) { 763 hrt2ts(hrt, &pa->sa_vattr.va_ctime); 764 pa->sa_mask |= SMB_AT_CTIME; 765 } 766 mutex_exit(&of->f_mutex); 767 768 /* 769 * Make durable and persistent. 770 * See smb2_dh_make_persistent() 771 */ 772 of->dh_vers = SMB2_DURABLE_V2; 773 bcopy(op->create_guid, of->dh_create_guid, UUID_LEN); 774 of->dh_persist = B_TRUE; 775 of->dh_nvfile = str_node; 776 smb_node_ref(str_node); 777 of->dh_nvlist = nvl; 778 nvl = NULL; 779 780 /* 781 * Now make it state orphaned... 782 * See smb_ofile_drop(), then 783 * smb_ofile_save_dh() 784 */ 785 mutex_enter(&of->f_mutex); 786 of->f_state = SMB_OFILE_STATE_SAVE_DH; 787 of->dh_timeout_offset = MSEC2NSEC(smb2_persist_timeout); 788 mutex_exit(&of->f_mutex); 789 790 /* 791 * Finished! 792 */ 793 return (of); 794 795 errout: 796 if (did_open) { 797 smb_ofile_close(of, 0); 798 smb_ofile_release(of); 799 } else { 800 ASSERT(of == NULL); 801 } 802 803 if (nvl != NULL) 804 nvlist_free(nvl); 805 806 return (NULL); 807 } 808 809 static int 810 smb2_dh_read_nvlist(smb_request_t *sr, smb_node_t *node, 811 struct nvlist **nvlpp) 812 { 813 smb_attr_t attr; 814 iovec_t iov; 815 uio_t uio; 816 smb_kshare_t *shr = sr->arg.tcon.si; 817 cred_t *kcr = zone_kcred(); 818 size_t flen; 819 int rc; 820 821 bzero(&attr, sizeof (attr)); 822 attr.sa_mask = SMB_AT_SIZE; 823 rc = smb_node_getattr(NULL, node, kcr, NULL, &attr); 824 if (rc != 0) { 825 cmn_err(CE_NOTE, "CA import (%s/%s) getattr rc=%d", 826 shr->shr_path, node->od_name, rc); 827 return (rc); 828 } 829 830 if (attr.sa_vattr.va_size < 4 || 831 attr.sa_vattr.va_size > sr->sr_req_length) { 832 cmn_err(CE_NOTE, "CA import (%s/%s) bad size=%" PRIu64, 833 shr->shr_path, node->od_name, 834 (uint64_t)attr.sa_vattr.va_size); 835 return (EINVAL); 836 } 837 flen = (size_t)attr.sa_vattr.va_size; 838 839 bzero(&uio, sizeof (uio)); 840 iov.iov_base = sr->sr_request_buf; 841 iov.iov_len = flen; 842 uio.uio_iov = &iov; 843 uio.uio_iovcnt = 1; 844 uio.uio_resid = flen; 845 uio.uio_segflg = UIO_SYSSPACE; 846 uio.uio_extflg = UIO_COPY_DEFAULT; 847 rc = smb_fsop_read(sr, kcr, node, NULL, &uio, 0); 848 if (rc != 0) { 849 cmn_err(CE_NOTE, "CA import (%s/%s) read, rc=%d", 850 shr->shr_path, node->od_name, rc); 851 return (rc); 852 } 853 if (uio.uio_resid != 0) { 854 cmn_err(CE_NOTE, "CA import (%s/%s) short read", 855 shr->shr_path, node->od_name); 856 return (EIO); 857 } 858 859 rc = nvlist_unpack(sr->sr_request_buf, flen, nvlpp, KM_SLEEP); 860 if (rc != 0) { 861 cmn_err(CE_NOTE, "CA import (%s/%s) unpack, rc=%d", 862 shr->shr_path, node->od_name, rc); 863 return (rc); 864 } 865 866 return (0); 867 } 868 869 /* 870 * Setup a vestigial credential in of->f_cr just good enough for 871 * smb_is_same_user to determine if the caller owned this ofile. 872 * At reconnect, of->f_cr will be replaced with the caller's. 873 */ 874 static int 875 smb2_dh_import_cred(smb_ofile_t *of, char *sidstr) 876 { 877 #ifdef _FAKE_KERNEL 878 _NOTE(ARGUNUSED(sidstr)) 879 /* fksmbd doesn't have real credentials. */ 880 of->f_cr = CRED(); 881 crhold(of->f_cr); 882 #else 883 char tmpstr[SMB_SID_STRSZ]; 884 ksid_t ksid; 885 cred_t *cr, *oldcr; 886 int rc; 887 888 (void) strlcpy(tmpstr, sidstr, sizeof (tmpstr)); 889 bzero(&ksid, sizeof (ksid)); 890 891 rc = smb_sid_splitstr(tmpstr, &ksid.ks_rid); 892 if (rc != 0) 893 return (rc); 894 cr = crget(); 895 896 ksid.ks_domain = ksid_lookupdomain(tmpstr); 897 crsetsid(cr, &ksid, KSID_USER); 898 ksiddomain_hold(ksid.ks_domain); 899 crsetsid(cr, &ksid, KSID_OWNER); 900 901 /* 902 * Just to avoid leaving the KSID_GROUP slot NULL, 903 * put the "everyone" SID there (S-1-1-0). 904 */ 905 ksid.ks_domain = ksid_lookupdomain("S-1-1"); 906 ksid.ks_rid = 0; 907 crsetsid(cr, &ksid, KSID_GROUP); 908 909 oldcr = of->f_cr; 910 of->f_cr = cr; 911 if (oldcr != NULL) 912 crfree(oldcr); 913 #endif 914 915 return (0); 916 } 917 918 /* 919 * Set Delete-on-Close (DoC) on the persistent state file so it will be 920 * removed when the last ref. goes away (in smb2_dh_close_persistent). 921 * 922 * This is called in just two places: 923 * (1) SMB2_close request -- client tells us to destroy the handle. 924 * (2) smb2_dh_expire -- client has forgotten about this handle. 925 * All other (server-initiated) close calls should leave these 926 * persistent state files in the file system. 927 */ 928 void 929 smb2_dh_setdoc_persistent(smb_ofile_t *of) 930 { 931 smb_node_t *strnode; 932 uint32_t status; 933 934 mutex_enter(&of->dh_nvlock); 935 if ((strnode = of->dh_nvfile) != NULL) 936 smb_node_ref(strnode); 937 mutex_exit(&of->dh_nvlock); 938 939 if (strnode != NULL) { 940 status = smb_node_set_delete_on_close(strnode, 941 zone_kcred(), SMB_CASE_SENSITIVE); 942 if (status != 0) { 943 cmn_err(CE_WARN, "Can't set DoC on CA file: %s", 944 strnode->od_name); 945 DTRACE_PROBE1(rm__ca__err, smb_ofile_t *, of); 946 } 947 smb_node_release(strnode); 948 } 949 } 950 951 /* 952 * During ofile close, free the persistent handle state nvlist and 953 * drop our reference to the state file node (which may unlink it 954 * if smb2_dh_setdoc_persistent was called). 955 */ 956 void 957 smb2_dh_close_persistent(smb_ofile_t *of) 958 { 959 smb_node_t *strnode; 960 struct nvlist *nvl; 961 962 /* 963 * Clear out nvlist and stream linkage 964 */ 965 mutex_enter(&of->dh_nvlock); 966 strnode = of->dh_nvfile; 967 of->dh_nvfile = NULL; 968 nvl = of->dh_nvlist; 969 of->dh_nvlist = NULL; 970 mutex_exit(&of->dh_nvlock); 971 972 if (nvl != NULL) 973 nvlist_free(nvl); 974 975 if (strnode != NULL) 976 smb_node_release(strnode); 977 } 978 979 /* 980 * Make this durable handle persistent. 981 * If we succeed, set of->dh_persist = TRUE. 982 */ 983 int 984 smb2_dh_make_persistent(smb_request_t *sr, smb_ofile_t *of) 985 { 986 char fname[DH_SN_SIZE]; 987 char sidstr[SMB_SID_STRSZ]; 988 smb_attr_t attr; 989 smb_arg_open_t *op = &sr->arg.open; 990 cred_t *kcr = zone_kcred(); 991 smb_node_t *dnode = of->f_tree->t_snode; 992 smb_node_t *fnode = NULL; 993 ksid_t *ksid; 994 int rc; 995 996 ASSERT(of->dh_nvfile == NULL); 997 998 /* 999 * Create the persistent handle nvlist file. 1000 * It's a named stream in the share root. 1001 */ 1002 smb2_dh_make_stream_name(fname, sizeof (fname), of->f_persistid); 1003 1004 bzero(&attr, sizeof (attr)); 1005 attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_SIZE; 1006 attr.sa_vattr.va_type = VREG; 1007 attr.sa_vattr.va_mode = 0640; 1008 attr.sa_vattr.va_size = 4; 1009 rc = smb_fsop_create(sr, kcr, dnode, fname, &attr, &fnode); 1010 if (rc != 0) 1011 return (rc); 1012 1013 mutex_enter(&of->dh_nvlock); 1014 1015 /* fnode is held. rele in smb2_dh_close_persistent */ 1016 of->dh_nvfile = fnode; 1017 (void) nvlist_alloc(&of->dh_nvlist, NV_UNIQUE_NAME, KM_SLEEP); 1018 1019 /* 1020 * Want the ksid as a string 1021 */ 1022 ksid = crgetsid(of->f_user->u_cred, KSID_USER); 1023 (void) snprintf(sidstr, sizeof (sidstr), "%s-%u", 1024 ksid->ks_domain->kd_name, ksid->ks_rid); 1025 1026 /* 1027 * Fill in the fixed parts of the nvlist 1028 */ 1029 (void) nvlist_add_uint32(of->dh_nvlist, 1030 "info_version", smb2_ca_info_version); 1031 (void) nvlist_add_string(of->dh_nvlist, 1032 "owner_sid", sidstr); 1033 (void) nvlist_add_string(of->dh_nvlist, 1034 "share_name", of->f_tree->t_sharename); 1035 (void) nvlist_add_uint64(of->dh_nvlist, 1036 "file_persistid", of->f_persistid); 1037 (void) nvlist_add_uint8_array(of->dh_nvlist, 1038 "file_guid", of->dh_create_guid, UUID_LEN); 1039 (void) nvlist_add_string(of->dh_nvlist, 1040 "client_ipaddr", sr->session->ip_addr_str); 1041 (void) nvlist_add_uint8_array(of->dh_nvlist, 1042 "client_uuid", sr->session->clnt_uuid, UUID_LEN); 1043 (void) nvlist_add_string(of->dh_nvlist, 1044 "path_name", op->fqi.fq_path.pn_path); 1045 (void) nvlist_add_uint32(of->dh_nvlist, 1046 "granted_access", of->f_granted_access); 1047 (void) nvlist_add_uint32(of->dh_nvlist, 1048 "share_access", of->f_share_access); 1049 (void) nvlist_add_uint32(of->dh_nvlist, 1050 "create_options", of->f_create_options); 1051 if (of->f_lease != NULL) { 1052 smb_lease_t *ls = of->f_lease; 1053 (void) nvlist_add_uint8_array(of->dh_nvlist, 1054 "lease_uuid", ls->ls_key, 16); 1055 (void) nvlist_add_uint32(of->dh_nvlist, 1056 "lease_state", ls->ls_state); 1057 (void) nvlist_add_uint16(of->dh_nvlist, 1058 "lease_epoch", ls->ls_epoch); 1059 (void) nvlist_add_uint16(of->dh_nvlist, 1060 "lease_version", ls->ls_version); 1061 } else { 1062 (void) nvlist_add_uint32(of->dh_nvlist, 1063 "oplock_state", of->f_oplock.og_state); 1064 } 1065 mutex_exit(&of->dh_nvlock); 1066 1067 smb2_dh_update_locks(sr, of); 1068 1069 /* Tell sr update nvlist file */ 1070 sr->dh_nvl_dirty = B_TRUE; 1071 1072 return (0); 1073 } 1074 1075 void 1076 smb2_dh_update_nvfile(smb_request_t *sr) 1077 { 1078 smb_attr_t attr; 1079 iovec_t iov; 1080 uio_t uio; 1081 smb_ofile_t *of = sr->fid_ofile; 1082 cred_t *kcr = zone_kcred(); 1083 char *buf = NULL; 1084 size_t buflen = 0; 1085 uint32_t wcnt; 1086 int rc; 1087 1088 if (of == NULL || of->dh_persist == B_FALSE) 1089 return; 1090 1091 mutex_enter(&of->dh_nvlock); 1092 if (of->dh_nvlist == NULL || of->dh_nvfile == NULL) { 1093 mutex_exit(&of->dh_nvlock); 1094 return; 1095 } 1096 1097 rc = nvlist_size(of->dh_nvlist, &buflen, NV_ENCODE_XDR); 1098 if (rc != 0) 1099 goto out; 1100 buf = kmem_zalloc(buflen, KM_SLEEP); 1101 1102 rc = nvlist_pack(of->dh_nvlist, &buf, &buflen, 1103 NV_ENCODE_XDR, KM_SLEEP); 1104 if (rc != 0) 1105 goto out; 1106 1107 bzero(&attr, sizeof (attr)); 1108 attr.sa_mask = SMB_AT_SIZE; 1109 attr.sa_vattr.va_size = buflen; 1110 rc = smb_node_setattr(sr, of->dh_nvfile, kcr, NULL, &attr); 1111 if (rc != 0) 1112 goto out; 1113 1114 bzero(&uio, sizeof (uio)); 1115 iov.iov_base = (void *) buf; 1116 iov.iov_len = buflen; 1117 uio.uio_iov = &iov; 1118 uio.uio_iovcnt = 1; 1119 uio.uio_resid = buflen; 1120 uio.uio_segflg = UIO_SYSSPACE; 1121 uio.uio_extflg = UIO_COPY_DEFAULT; 1122 rc = smb_fsop_write(sr, kcr, of->dh_nvfile, 1123 NULL, &uio, &wcnt, 0); 1124 if (rc == 0 && wcnt != buflen) 1125 rc = EIO; 1126 1127 out: 1128 mutex_exit(&of->dh_nvlock); 1129 1130 if (rc != 0) { 1131 cmn_err(CE_WARN, 1132 "clnt(%s) failed to update persistent handle, rc=%d", 1133 sr->session->ip_addr_str, rc); 1134 } 1135 1136 if (buf != NULL) { 1137 kmem_free(buf, buflen); 1138 } 1139 } 1140 1141 /* 1142 * Called after f_oplock (and lease) changes 1143 * If lease, update: lease_state, lease_epoch 1144 * else (oplock) update: oplock_state 1145 */ 1146 void 1147 smb2_dh_update_oplock(smb_request_t *sr, smb_ofile_t *of) 1148 { 1149 smb_lease_t *ls; 1150 1151 mutex_enter(&of->dh_nvlock); 1152 if (of->dh_nvlist == NULL) { 1153 mutex_exit(&of->dh_nvlock); 1154 return; 1155 } 1156 1157 if (of->f_lease != NULL) { 1158 ls = of->f_lease; 1159 (void) nvlist_add_uint32(of->dh_nvlist, 1160 "lease_state", ls->ls_state); 1161 (void) nvlist_add_uint16(of->dh_nvlist, 1162 "lease_epoch", ls->ls_epoch); 1163 } else { 1164 (void) nvlist_add_uint32(of->dh_nvlist, 1165 "oplock_state", of->f_oplock.og_state); 1166 } 1167 mutex_exit(&of->dh_nvlock); 1168 1169 sr->dh_nvl_dirty = B_TRUE; 1170 } 1171 1172 /* 1173 * Save locks from this ofile as an array of uint64_t, where the 1174 * elements are triplets: (start, length, (pid << 32) | type) 1175 * Note pid should always be zero for SMB2, so we could use 1176 * that 32-bit spot for something else if needed. 1177 */ 1178 void 1179 smb2_dh_update_locks(smb_request_t *sr, smb_ofile_t *of) 1180 { 1181 uint8_t lseq[SMB_OFILE_LSEQ_MAX]; 1182 smb_node_t *node = of->f_node; 1183 smb_llist_t *llist = &node->n_lock_list; 1184 size_t vec_sz; // storage size 1185 uint_t my_cnt = 0; 1186 uint64_t *vec = NULL; 1187 struct nvlk *nlp; 1188 smb_lock_t *lock; 1189 1190 smb_llist_enter(llist, RW_READER); 1191 vec_sz = (llist->ll_count + 1) * sizeof (struct nvlk); 1192 vec = kmem_alloc(vec_sz, KM_SLEEP); 1193 nlp = (struct nvlk *)vec; 1194 for (lock = smb_llist_head(llist); 1195 lock != NULL; 1196 lock = smb_llist_next(llist, lock)) { 1197 if (lock->l_file != of) 1198 continue; 1199 nlp->lk_start = lock->l_start; 1200 nlp->lk_len = lock->l_length; 1201 nlp->lk_pid = lock->l_pid; 1202 nlp->lk_type = lock->l_type; 1203 nlp++; 1204 my_cnt++; 1205 } 1206 smb_llist_exit(llist); 1207 1208 mutex_enter(&of->f_mutex); 1209 bcopy(of->f_lock_seq, lseq, sizeof (lseq)); 1210 mutex_exit(&of->f_mutex); 1211 1212 mutex_enter(&of->dh_nvlock); 1213 if (of->dh_nvlist != NULL) { 1214 1215 (void) nvlist_add_uint64_array(of->dh_nvlist, 1216 "locks", vec, my_cnt * 3); 1217 1218 (void) nvlist_add_uint8_array(of->dh_nvlist, 1219 "lockseq", lseq, sizeof (lseq)); 1220 } 1221 mutex_exit(&of->dh_nvlock); 1222 1223 kmem_free(vec, vec_sz); 1224 1225 sr->dh_nvl_dirty = B_TRUE; 1226 } 1227 1228 /* 1229 * Save "sticky" times 1230 */ 1231 void 1232 smb2_dh_update_times(smb_request_t *sr, smb_ofile_t *of, smb_attr_t *attr) 1233 { 1234 hrtime_t t; 1235 1236 mutex_enter(&of->dh_nvlock); 1237 if (of->dh_nvlist == NULL) { 1238 mutex_exit(&of->dh_nvlock); 1239 return; 1240 } 1241 1242 if (attr->sa_mask & SMB_AT_ATIME) { 1243 t = ts2hrt(&attr->sa_vattr.va_atime); 1244 (void) nvlist_add_hrtime(of->dh_nvlist, "atime", t); 1245 } 1246 if (attr->sa_mask & SMB_AT_MTIME) { 1247 t = ts2hrt(&attr->sa_vattr.va_mtime); 1248 (void) nvlist_add_hrtime(of->dh_nvlist, "mtime", t); 1249 } 1250 if (attr->sa_mask & SMB_AT_CTIME) { 1251 t = ts2hrt(&attr->sa_vattr.va_ctime); 1252 (void) nvlist_add_hrtime(of->dh_nvlist, "ctime", t); 1253 } 1254 mutex_exit(&of->dh_nvlock); 1255 1256 sr->dh_nvl_dirty = B_TRUE; 1257 } 1258 1259 1260 /* 1261 * Requirements for ofile found during reconnect (MS-SMB2 3.3.5.9.7): 1262 * - security descriptor must match provided descriptor 1263 * 1264 * If file is leased: 1265 * - lease must be requested 1266 * - client guid must match session guid 1267 * - file name must match given name 1268 * - lease key must match provided lease key 1269 * If file is not leased: 1270 * - Lease must not be requested 1271 * 1272 * dh_v2 only: 1273 * - SMB2_DHANDLE_FLAG_PERSISTENT must be set if dh_persist is true 1274 * - SMB2_DHANDLE_FLAG_PERSISTENT must not be set if dh_persist is false 1275 * - desired access, share access, and create_options must be ignored 1276 * - createguid must match 1277 */ 1278 static uint32_t 1279 smb2_dh_reconnect_checks(smb_request_t *sr, smb_ofile_t *of) 1280 { 1281 smb_arg_open_t *op = &sr->sr_open; 1282 char *fname; 1283 1284 if (of->f_lease != NULL) { 1285 if (bcmp(sr->session->clnt_uuid, 1286 of->f_lease->ls_clnt, 16) != 0) 1287 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1288 1289 if (op->op_oplock_level != SMB2_OPLOCK_LEVEL_LEASE) 1290 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1291 if (bcmp(op->lease_key, of->f_lease->ls_key, 1292 SMB_LEASE_KEY_SZ) != 0) 1293 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1294 1295 /* 1296 * We're supposed to check the name is the same. 1297 * Not really necessary to do this, so just do 1298 * minimal effort (check last component) 1299 */ 1300 fname = strrchr(op->fqi.fq_path.pn_path, '\\'); 1301 if (fname != NULL) 1302 fname++; 1303 else 1304 fname = op->fqi.fq_path.pn_path; 1305 if (smb_strcasecmp(fname, of->f_node->od_name, 0) != 0) { 1306 #ifdef DEBUG 1307 cmn_err(CE_NOTE, "reconnect name <%s> of name <%s>", 1308 fname, of->f_node->od_name); 1309 #endif 1310 return (NT_STATUS_INVALID_PARAMETER); 1311 } 1312 } else { 1313 if (op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) 1314 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1315 } 1316 1317 if (op->dh_vers == SMB2_DURABLE_V2) { 1318 boolean_t op_persist = 1319 ((op->dh_v2_flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0); 1320 if (of->dh_persist != op_persist) 1321 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1322 if (memcmp(op->create_guid, of->dh_create_guid, UUID_LEN)) 1323 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 1324 } 1325 1326 if (!smb_is_same_user(sr->user_cr, of->f_cr)) 1327 return (NT_STATUS_ACCESS_DENIED); 1328 1329 return (NT_STATUS_SUCCESS); 1330 } 1331 1332 /* 1333 * [MS-SMB2] 3.3.5.9.7 and 3.3.5.9.12 (durable reconnect v1/v2) 1334 * 1335 * Looks up an ofile on the server's sv_dh_list by the persistid. 1336 * If found, it validates the request. 1337 * (see smb2_dh_reconnect_checks() for details) 1338 * If the checks are passed, add it onto the new tree's list. 1339 * 1340 * Note that the oplock break code path can get to an ofile via the node 1341 * ofile list. It starts with a ref taken in smb_ofile_hold_olbrk, which 1342 * waits if the ofile is found in state RECONNECT. That wait happens with 1343 * the node ofile list lock held as reader, and the oplock mutex held. 1344 * Implications of that are: While we're in state RECONNECT, we shoud NOT 1345 * block (at least, not for long) and must not try to enter any of the 1346 * node ofile list lock or oplock mutex. Thankfully, we don't need to 1347 * enter those while reclaiming an orphaned ofile. 1348 */ 1349 uint32_t 1350 smb2_dh_reconnect(smb_request_t *sr) 1351 { 1352 smb_arg_open_t *op = &sr->sr_open; 1353 smb_tree_t *tree = sr->tid_tree; 1354 smb_ofile_t *of; 1355 cred_t *old_cr; 1356 uint32_t status = NT_STATUS_OBJECT_NAME_NOT_FOUND; 1357 uint16_t fid = 0; 1358 1359 if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) 1360 return (NT_STATUS_TOO_MANY_OPENED_FILES); 1361 1362 /* Find orphaned handle. */ 1363 of = smb_ofile_lookup_by_persistid(sr, op->dh_fileid.persistent); 1364 if (of == NULL) 1365 goto errout; 1366 1367 mutex_enter(&of->f_mutex); 1368 if (of->f_state != SMB_OFILE_STATE_ORPHANED) { 1369 mutex_exit(&of->f_mutex); 1370 goto errout; 1371 } 1372 1373 status = smb2_dh_reconnect_checks(sr, of); 1374 if (status != NT_STATUS_SUCCESS) { 1375 mutex_exit(&of->f_mutex); 1376 goto errout; 1377 } 1378 1379 /* 1380 * Note: cv_broadcast(&of->f_cv) when we're 1381 * done messing around in this state. 1382 * See: smb_ofile_hold_olbrk() 1383 */ 1384 of->f_state = SMB_OFILE_STATE_RECONNECT; 1385 mutex_exit(&of->f_mutex); 1386 1387 /* 1388 * At this point, we should be the only thread with a ref on the 1389 * ofile, and the RECONNECT state should prevent new refs from 1390 * being granted, or other durable threads from observing or 1391 * reclaiming it. Put this ofile in the new tree, similar to 1392 * the last part of smb_ofile_open. 1393 */ 1394 1395 old_cr = of->f_cr; 1396 of->f_cr = sr->user_cr; 1397 crhold(of->f_cr); 1398 crfree(old_cr); 1399 1400 of->f_session = sr->session; /* hold is via user and tree */ 1401 smb_user_hold_internal(sr->uid_user); 1402 of->f_user = sr->uid_user; 1403 smb_tree_hold_internal(tree); 1404 of->f_tree = tree; 1405 of->f_fid = fid; 1406 1407 smb_llist_enter(&tree->t_ofile_list, RW_WRITER); 1408 smb_llist_insert_tail(&tree->t_ofile_list, of); 1409 smb_llist_exit(&tree->t_ofile_list); 1410 atomic_inc_32(&tree->t_open_files); 1411 atomic_inc_32(&sr->session->s_file_cnt); 1412 1413 /* 1414 * The ofile is now in the caller's session & tree. 1415 * 1416 * In case smb_ofile_hold or smb_oplock_send_brk() are 1417 * waiting for state RECONNECT to complete, wakeup. 1418 */ 1419 mutex_enter(&of->f_mutex); 1420 of->dh_expire_time = 0; 1421 of->f_state = SMB_OFILE_STATE_OPEN; 1422 cv_broadcast(&of->f_cv); 1423 mutex_exit(&of->f_mutex); 1424 1425 /* 1426 * The ofile is now visible in the new session. 1427 * From here, this is similar to the last part of 1428 * smb_common_open(). 1429 */ 1430 op->fqi.fq_fattr.sa_mask = SMB_AT_ALL; 1431 (void) smb_node_getattr(sr, of->f_node, zone_kcred(), of, 1432 &op->fqi.fq_fattr); 1433 1434 /* 1435 * Set up the fileid and dosattr in open_param for response 1436 */ 1437 op->fileid = op->fqi.fq_fattr.sa_vattr.va_nodeid; 1438 op->dattr = op->fqi.fq_fattr.sa_dosattr; 1439 1440 /* 1441 * Set up the file type in open_param for the response 1442 * The ref. from ofile lookup is "given" to fid_ofile. 1443 */ 1444 op->ftype = SMB_FTYPE_DISK; 1445 sr->smb_fid = of->f_fid; 1446 sr->fid_ofile = of; 1447 1448 if (smb_node_is_file(of->f_node)) { 1449 op->dsize = op->fqi.fq_fattr.sa_vattr.va_size; 1450 } else { 1451 /* directory or symlink */ 1452 op->dsize = 0; 1453 } 1454 1455 op->create_options = 0; /* no more modifications wanted */ 1456 op->action_taken = SMB_OACT_OPENED; 1457 return (NT_STATUS_SUCCESS); 1458 1459 errout: 1460 if (of != NULL) 1461 smb_ofile_release(of); 1462 if (fid != 0) 1463 smb_idpool_free(&tree->t_fid_pool, fid); 1464 1465 return (status); 1466 } 1467 1468 /* 1469 * Durable handle expiration 1470 * ofile state is _EXPIRED 1471 */ 1472 static void 1473 smb2_dh_expire(void *arg) 1474 { 1475 smb_ofile_t *of = (smb_ofile_t *)arg; 1476 1477 if (of->dh_persist) 1478 smb2_dh_setdoc_persistent(of); 1479 smb_ofile_close(of, 0); 1480 smb_ofile_release(of); 1481 } 1482 1483 void 1484 smb2_durable_timers(smb_server_t *sv) 1485 { 1486 smb_hash_t *hash; 1487 smb_llist_t *bucket; 1488 smb_ofile_t *of; 1489 hrtime_t now; 1490 int i; 1491 1492 hash = sv->sv_persistid_ht; 1493 now = gethrtime(); 1494 1495 for (i = 0; i < hash->num_buckets; i++) { 1496 bucket = &hash->buckets[i].b_list; 1497 smb_llist_enter(bucket, RW_READER); 1498 for (of = smb_llist_head(bucket); 1499 of != NULL; 1500 of = smb_llist_next(bucket, of)) { 1501 SMB_OFILE_VALID(of); 1502 1503 /* 1504 * Check outside the mutex first to avoid some 1505 * mutex_enter work in this loop. If the state 1506 * changes under foot, the worst that happens 1507 * is we either enter the mutex when we might 1508 * not have needed to, or we miss some DH in 1509 * this pass and get it on the next. 1510 */ 1511 if (of->f_state != SMB_OFILE_STATE_ORPHANED) 1512 continue; 1513 1514 mutex_enter(&of->f_mutex); 1515 /* STATE_ORPHANED implies dh_expire_time != 0 */ 1516 if (of->f_state == SMB_OFILE_STATE_ORPHANED && 1517 of->dh_expire_time <= now) { 1518 of->f_state = SMB_OFILE_STATE_EXPIRED; 1519 /* inline smb_ofile_hold_internal() */ 1520 of->f_refcnt++; 1521 smb_llist_post(bucket, of, smb2_dh_expire); 1522 } 1523 mutex_exit(&of->f_mutex); 1524 } 1525 smb_llist_exit(bucket); 1526 } 1527 } 1528 1529 /* 1530 * This is called when we're about to add a new open to some node. 1531 * If we still have orphaned durable handles on this node, let's 1532 * assume the client has lost interest in those and close them, 1533 * otherwise we might conflict with our own orphaned handles. 1534 * 1535 * We need this because we import persistent handles "speculatively" 1536 * during share import (before the client ever asks for reconnect). 1537 * That allows us to avoid any need for a "create blackout" (or 1538 * "grace period") because the imported handles prevent unwanted 1539 * conflicting opens from other clients. However, if some client 1540 * "forgets" about a persistent handle (*cough* Hyper-V) and tries 1541 * a new (conflicting) open instead of a reconnect, that might 1542 * fail unless we expire our orphaned durables handle first. 1543 * 1544 * Logic similar to smb_node_open_check() 1545 */ 1546 void 1547 smb2_dh_close_my_orphans(smb_request_t *sr, smb_ofile_t *new_of) 1548 { 1549 smb_node_t *node = new_of->f_node; 1550 smb_ofile_t *of; 1551 1552 SMB_NODE_VALID(node); 1553 1554 smb_llist_enter(&node->n_ofile_list, RW_READER); 1555 for (of = smb_llist_head(&node->n_ofile_list); 1556 of != NULL; 1557 of = smb_llist_next(&node->n_ofile_list, of)) { 1558 1559 /* Same client? */ 1560 if (of->f_lease != NULL && 1561 bcmp(sr->session->clnt_uuid, 1562 of->f_lease->ls_clnt, 16) != 0) 1563 continue; 1564 1565 if (!smb_is_same_user(sr->user_cr, of->f_cr)) 1566 continue; 1567 1568 mutex_enter(&of->f_mutex); 1569 if (of->f_state == SMB_OFILE_STATE_ORPHANED) { 1570 of->f_state = SMB_OFILE_STATE_EXPIRED; 1571 /* inline smb_ofile_hold_internal() */ 1572 of->f_refcnt++; 1573 smb_llist_post(&node->n_ofile_list, 1574 of, smb2_dh_expire); 1575 } 1576 mutex_exit(&of->f_mutex); 1577 } 1578 1579 smb_llist_exit(&node->n_ofile_list); 1580 } 1581 1582 /* 1583 * Called for each orphaned DH during shutdown. 1584 * Clean out any in-memory state, but leave any 1585 * on-disk persistent handle state in place. 1586 */ 1587 static void 1588 smb2_dh_cleanup(void *arg) 1589 { 1590 smb_ofile_t *of = (smb_ofile_t *)arg; 1591 smb_node_t *strnode; 1592 struct nvlist *nvl; 1593 1594 /* 1595 * Intentionally skip smb2_dh_close_persistent by 1596 * clearing dh_nvfile before smb_ofile_close(). 1597 */ 1598 mutex_enter(&of->dh_nvlock); 1599 strnode = of->dh_nvfile; 1600 of->dh_nvfile = NULL; 1601 nvl = of->dh_nvlist; 1602 of->dh_nvlist = NULL; 1603 mutex_exit(&of->dh_nvlock); 1604 1605 if (nvl != NULL) 1606 nvlist_free(nvl); 1607 1608 if (strnode != NULL) 1609 smb_node_release(strnode); 1610 1611 smb_ofile_close(of, 0); 1612 smb_ofile_release(of); 1613 } 1614 1615 /* 1616 * Clean out durable handles during shutdown. 1617 * 1618 * Like, smb2_durable_timers but cleanup only in-memory state, 1619 * and leave any persistent state there for later reconnect. 1620 */ 1621 void 1622 smb2_dh_shutdown(smb_server_t *sv) 1623 { 1624 smb_hash_t *hash; 1625 smb_llist_t *bucket; 1626 smb_ofile_t *of; 1627 int i; 1628 1629 hash = sv->sv_persistid_ht; 1630 1631 for (i = 0; i < hash->num_buckets; i++) { 1632 bucket = &hash->buckets[i].b_list; 1633 smb_llist_enter(bucket, RW_READER); 1634 of = smb_llist_head(bucket); 1635 while (of != NULL) { 1636 SMB_OFILE_VALID(of); 1637 mutex_enter(&of->f_mutex); 1638 1639 switch (of->f_state) { 1640 case SMB_OFILE_STATE_ORPHANED: 1641 of->f_state = SMB_OFILE_STATE_EXPIRED; 1642 /* inline smb_ofile_hold_internal() */ 1643 of->f_refcnt++; 1644 smb_llist_post(bucket, of, smb2_dh_cleanup); 1645 break; 1646 default: 1647 break; 1648 } 1649 mutex_exit(&of->f_mutex); 1650 of = smb_llist_next(bucket, of); 1651 } 1652 smb_llist_exit(bucket); 1653 } 1654 1655 #ifdef DEBUG 1656 for (i = 0; i < hash->num_buckets; i++) { 1657 bucket = &hash->buckets[i].b_list; 1658 smb_llist_enter(bucket, RW_READER); 1659 of = smb_llist_head(bucket); 1660 while (of != NULL) { 1661 SMB_OFILE_VALID(of); 1662 cmn_err(CE_NOTE, "dh_shutdown leaked of=%p", 1663 (void *)of); 1664 of = smb_llist_next(bucket, of); 1665 } 1666 smb_llist_exit(bucket); 1667 } 1668 #endif // DEBUG 1669 } 1670 1671 uint32_t 1672 smb2_fsctl_set_resilient(smb_request_t *sr, smb_fsctl_t *fsctl) 1673 { 1674 uint32_t timeout; 1675 smb_ofile_t *of = sr->fid_ofile; 1676 1677 /* 1678 * Note: The spec does not explicitly prohibit resilient directories 1679 * the same way it prohibits durable directories. We prohibit them 1680 * anyway as a simplifying assumption, as there doesn't seem to be 1681 * much use for it. (HYPER-V only seems to use it on files anyway) 1682 */ 1683 if (fsctl->InputCount < 8 || !smb_node_is_file(of->f_node)) 1684 return (NT_STATUS_INVALID_PARAMETER); 1685 1686 (void) smb_mbc_decodef(fsctl->in_mbc, "l4.", 1687 &timeout); /* milliseconds */ 1688 1689 if (smb2_enable_dh == 0) 1690 return (NT_STATUS_NOT_SUPPORTED); 1691 1692 /* 1693 * The spec wants us to return INVALID_PARAMETER if the timeout 1694 * is too large, but we have no way of informing the client 1695 * what an appropriate timeout is, so just set the timeout to 1696 * our max and return SUCCESS. 1697 */ 1698 if (timeout == 0) 1699 timeout = smb2_res_def_timeout; 1700 if (timeout > smb2_res_max_timeout) 1701 timeout = smb2_res_max_timeout; 1702 1703 mutex_enter(&of->f_mutex); 1704 of->dh_vers = SMB2_RESILIENT; 1705 of->dh_timeout_offset = MSEC2NSEC(timeout); 1706 mutex_exit(&of->f_mutex); 1707 1708 return (NT_STATUS_SUCCESS); 1709 } 1710