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) {
 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 vestigal 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 }