Print this page
NEX-13644 File access audit logging
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-17779 Creating named streams on existing files is not quite right
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-2807 Restoring previous versions from snapshots doesn't work with nested folders.
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15931 Panic removing files in SMB3 CA share
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Include in backports of:
  NEX-9808 SMB3 persistent handles
NEX-15931 Panic removing files in SMB3 CA share
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Include in backports of:
  NEX-9808 SMB3 persistent handles
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-13653 Obsolete SMB server work-around for ZFS read-only
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9604 SMB: smb2 does not delete a read-only file, where smb1 does
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-6041 Should pass the smbtorture lock tests
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5312 delete_on_close should be acted on earlier
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-3906 Prefer that SMB change notify not tie up a worker thread
NEX-5278 SMB notify should buffer per file handle
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4083 Upstream changes from illumos 5917 and 5995
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-3620 need upstream cleanups for smbsrv
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
SMB-142 Deadlock in SMB2
SMB-131 Don't allow setting delete-on-close on non empty dirs
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-50 User-mode SMB server
 Includes work by these authors:
 Thomas Keiser <thomas.keiser@nexenta.com>
 Albert Lee <trisk@nexenta.com>
SMB-65 SMB server in non-global zones (use zone_kcred())
SMB-65 SMB server in non-global zones (kmem_caches)
common kmem_cache instances across zones
separate GZ-only init from NGZ init
SUP-599 smb_oplock_acquire thread deadlock
re #7815 SMB server delivers old modification time... (fix allocsz)
re #13470 rb4432 Sync some SMB differences from illumos
re #7815 SMB server delivers old modification time...
re #11215 rb3676 sesctl to SGI JBOD hangs in biowait() with a command stuck in mptsas driver
re #10734 NT Trans. Notify returning too quickly

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/smbsrv/smb_node.c
          +++ new/usr/src/uts/common/fs/smbsrv/smb_node.c
↓ open down ↓ 12 lines elided ↑ open up ↑
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23      - * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
       23 + * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  24   24   */
  25   25  /*
  26   26   * SMB Node State Machine
  27   27   * ----------------------
  28   28   *
  29   29   *
  30   30   *                  +----------- Creation/Allocation
  31   31   *                  |
  32   32   *                  | T0
  33   33   *                  |
↓ open down ↓ 47 lines elided ↑ open up ↑
  81   81   *    reference count drops to zero and triggers the deletion of the node, the
  82   82   *    mutex has to be released before entering the lock of the bucket (to
  83   83   *    remove the node). This creates a window during which the node that is
  84   84   *    about to be freed could be given out by smb_node_lookup(). To close that
  85   85   *    window the node is moved to the state SMB_NODE_STATE_DESTROYING before
  86   86   *    releasing the mutex. That way, even if smb_node_lookup() finds it, the
  87   87   *    state will indicate that the node should be treated as non existent (of
  88   88   *    course the state of the node should be tested/updated under the
  89   89   *    protection of the mutex).
  90   90   */
  91      -#include <smbsrv/smb_kproto.h>
       91 +#include <smbsrv/smb2_kproto.h>
  92   92  #include <smbsrv/smb_fsops.h>
  93   93  #include <smbsrv/smb_kstat.h>
  94   94  #include <sys/ddi.h>
  95   95  #include <sys/extdirent.h>
  96   96  #include <sys/pathname.h>
  97   97  #include <sys/sdt.h>
  98   98  #include <sys/nbmlock.h>
  99   99  #include <fs/fs_reparse.h>
 100  100  
 101  101  uint32_t smb_is_executable(char *);
 102      -static void smb_node_delete_on_close(smb_node_t *);
 103  102  static void smb_node_create_audit_buf(smb_node_t *, int);
 104  103  static void smb_node_destroy_audit_buf(smb_node_t *);
 105  104  static void smb_node_audit(smb_node_t *);
 106  105  static smb_node_t *smb_node_alloc(char *, vnode_t *, smb_llist_t *, uint32_t);
 107  106  static void smb_node_free(smb_node_t *);
 108  107  static int smb_node_constructor(void *, void *, int);
 109  108  static void smb_node_destructor(void *, void *);
 110  109  static smb_llist_t *smb_node_get_hash(fsid_t *, smb_attr_t *, uint32_t *);
 111  110  
 112  111  static void smb_node_init_reparse(smb_node_t *, smb_attr_t *);
↓ open down ↓ 70 lines elided ↑ open up ↑
 183  182          if (smb_root_node != NULL) {
 184  183                  smb_node_release(smb_root_node);
 185  184                  smb_root_node = NULL;
 186  185          }
 187  186  
 188  187          if (smb_node_cache == NULL)
 189  188                  return;
 190  189  
 191  190  #ifdef DEBUG
 192  191          for (i = 0; i <= SMBND_HASH_MASK; i++) {
      192 +                smb_llist_t     *bucket;
 193  193                  smb_node_t      *node;
 194  194  
 195  195                  /*
 196  196                   * The following sequence is just intended for sanity check.
 197  197                   * This will have to be modified when the code goes into
 198  198                   * production.
 199  199                   *
 200  200                   * The SMB node hash table should be emtpy at this point. If the
 201  201                   * hash table is not empty a panic will be triggered.
 202  202                   *
 203  203                   * The reason why SMB nodes are still remaining in the hash
 204  204                   * table is problably due to a mismatch between calls to
 205  205                   * smb_node_lookup() and smb_node_release(). You must track that
 206  206                   * down.
 207  207                   */
 208      -                node = smb_llist_head(&smb_node_hash_table[i]);
 209      -                ASSERT(node == NULL);
      208 +                bucket = &smb_node_hash_table[i];
      209 +                node = smb_llist_head(bucket);
      210 +                while (node != NULL) {
      211 +                        cmn_err(CE_NOTE, "leaked node: 0x%p %s",
      212 +                            (void *)node, node->od_name);
      213 +                        node = smb_llist_next(bucket, node);
      214 +                }
 210  215          }
 211  216  #endif
 212  217  
 213  218          for (i = 0; i <= SMBND_HASH_MASK; i++) {
 214  219                  smb_llist_destructor(&smb_node_hash_table[i]);
 215  220          }
 216  221          kmem_cache_destroy(smb_node_cache);
 217  222          smb_node_cache = NULL;
 218  223  }
 219  224  
↓ open down ↓ 255 lines elided ↑ open up ↑
 475  480                          node->n_state = SMB_NODE_STATE_DESTROYING;
 476  481                          mutex_exit(&node->n_mutex);
 477  482  
 478  483                          smb_llist_enter(node->n_hash_bucket, RW_WRITER);
 479  484                          smb_llist_remove(node->n_hash_bucket, node);
 480  485                          smb_llist_exit(node->n_hash_bucket);
 481  486  
 482  487                          /*
 483  488                           * Check if the file was deleted
 484  489                           */
 485      -                        smb_node_delete_on_close(node);
      490 +                        if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
      491 +                                smb_node_delete_on_close(node);
      492 +                        }
 486  493  
 487  494                          if (node->n_dnode) {
 488  495                                  ASSERT(node->n_dnode->n_magic ==
 489  496                                      SMB_NODE_MAGIC);
 490  497                                  smb_node_release(node->n_dnode);
 491  498                          }
 492  499  
 493  500                          if (node->n_unode) {
 494  501                                  ASSERT(node->n_unode->n_magic ==
 495  502                                      SMB_NODE_MAGIC);
↓ open down ↓ 4 lines elided ↑ open up ↑
 500  507                          return;
 501  508  
 502  509                  default:
 503  510                          SMB_PANIC();
 504  511                  }
 505  512          }
 506  513          smb_node_audit(node);
 507  514          mutex_exit(&node->n_mutex);
 508  515  }
 509  516  
 510      -static void
      517 +void
 511  518  smb_node_delete_on_close(smb_node_t *node)
 512  519  {
 513  520          smb_node_t      *d_snode;
 514  521          int             rc = 0;
 515  522          uint32_t        flags = 0;
 516  523  
 517  524          d_snode = node->n_dnode;
 518      -        if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
 519      -                node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
 520      -                flags = node->n_delete_on_close_flags;
 521      -                ASSERT(node->od_name != NULL);
 522  525  
 523      -                if (smb_node_is_dir(node))
 524      -                        rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
 525      -                            d_snode, node->od_name, flags);
 526      -                else
 527      -                        rc = smb_fsop_remove(0, node->delete_on_close_cred,
 528      -                            d_snode, node->od_name, flags);
 529      -                crfree(node->delete_on_close_cred);
 530      -        }
      526 +        ASSERT((node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0);
      527 +
      528 +        node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
      529 +        node->flags |= NODE_FLAGS_DELETE_COMMITTED;
      530 +        flags = node->n_delete_on_close_flags;
      531 +        ASSERT(node->od_name != NULL);
      532 +
      533 +        if (smb_node_is_dir(node))
      534 +                rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
      535 +                    d_snode, node->od_name, flags);
      536 +        else
      537 +                rc = smb_fsop_remove(0, node->delete_on_close_cred,
      538 +                    d_snode, node->od_name, flags);
      539 +        crfree(node->delete_on_close_cred);
      540 +        node->delete_on_close_cred = NULL;
      541 +
 531  542          if (rc != 0)
 532  543                  cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n",
 533  544                      node->od_name, rc);
 534  545          DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node);
 535  546  }
 536  547  
 537  548  /*
 538  549   * smb_node_rename()
 539  550   *
 540  551   */
↓ open down ↓ 40 lines elided ↑ open up ↑
 581  592  
 582  593          ASSERT(zone->zone_id == sv->sv_zid);
 583  594          if (smb_root_node == NULL)
 584  595                  return (ENOENT);
 585  596  
 586  597          /*
 587  598           * We're getting smb nodes below the zone root here,
 588  599           * so need to use kcred, not zone_kcred().
 589  600           */
 590  601          error = smb_pathname(NULL, zone->zone_rootpath, 0,
 591      -            smb_root_node, smb_root_node, NULL, svrootp, kcred);
      602 +            smb_root_node, smb_root_node, NULL, svrootp, kcred, NULL);
 592  603  
 593  604          return (error);
 594  605  }
 595  606  
 596  607  /*
 597  608   * Helper function for smb_node_set_delete_on_close(). Assumes node is a dir.
 598  609   * Return 0 if this is an empty dir. Otherwise return a NT_STATUS code.
 599      - * We distinguish between readdir failure and non-empty dir by returning
 600      - * different values.
      610 + * Unfortunately, to find out if a directory is empty, we have to read it
      611 + * and check for anything other than "." or ".." in the readdir buf.
 601  612   */
 602  613  static uint32_t
 603      -smb_rmdir_possible(smb_node_t *n, uint32_t flags)
      614 +smb_rmdir_possible(smb_node_t *n)
 604  615  {
 605  616          ASSERT(n->vp->v_type == VDIR);
 606      -        char buf[512]; /* Only large enough to see if the dir is empty. */
 607      -        int eof, bsize = sizeof (buf), reclen = 0;
 608      -        char *name;
 609      -        boolean_t edp = vfs_has_feature(n->vp->v_vfsp, VFSFT_DIRENTFLAGS);
      617 +        char *buf;
      618 +        char *bufptr;
      619 +        struct dirent64 *dp;
      620 +        uint32_t status = NT_STATUS_SUCCESS;
      621 +        int bsize = SMB_ODIR_BUFSIZE;
      622 +        int eof = 0;
 610  623  
 611      -        union {
 612      -                char            *u_bufptr;
 613      -                struct edirent  *u_edp;
 614      -                struct dirent64 *u_dp;
 615      -        } u;
 616      -#define bufptr  u.u_bufptr
 617      -#define extdp   u.u_edp
 618      -#define dp      u.u_dp
      624 +        buf = kmem_alloc(SMB_ODIR_BUFSIZE, KM_SLEEP);
 619  625  
 620      -        if (smb_vop_readdir(n->vp, 0, buf, &bsize, &eof, flags, zone_kcred()))
 621      -                return (NT_STATUS_CANNOT_DELETE);
 622      -        if (bsize == 0)
 623      -                return (NT_STATUS_CANNOT_DELETE);
      626 +        /* Flags zero: no edirent, no ABE wanted here */
      627 +        if (smb_vop_readdir(n->vp, 0, buf, &bsize, &eof, 0, zone_kcred())) {
      628 +                status = NT_STATUS_INTERNAL_ERROR;
      629 +                goto out;
      630 +        }
      631 +
 624  632          bufptr = buf;
 625      -        while ((bufptr += reclen) < buf + bsize) {
 626      -                if (edp) {
 627      -                        reclen = extdp->ed_reclen;
 628      -                        name = extdp->ed_name;
 629      -                } else {
 630      -                        reclen = dp->d_reclen;
 631      -                        name = dp->d_name;
      633 +        while (bsize > 0) {
      634 +                /* LINTED pointer alignment */
      635 +                dp = (struct dirent64 *)bufptr;
      636 +
      637 +                bufptr += dp->d_reclen;
      638 +                bsize  -= dp->d_reclen;
      639 +                if (bsize < 0) {
      640 +                        /* partial record */
      641 +                        status = NT_STATUS_DIRECTORY_NOT_EMPTY;
      642 +                        break;
 632  643                  }
 633      -                if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
 634      -                        return (NT_STATUS_DIRECTORY_NOT_EMPTY);
      644 +
      645 +                if (strcmp(dp->d_name, ".") != 0 &&
      646 +                    strcmp(dp->d_name, "..") != 0) {
      647 +                        status = NT_STATUS_DIRECTORY_NOT_EMPTY;
      648 +                        break;
      649 +                }
 635  650          }
 636      -        return (0);
      651 +
      652 +out:
      653 +        kmem_free(buf, SMB_ODIR_BUFSIZE);
      654 +        return (status);
 637  655  }
 638  656  
 639  657  /*
 640  658   * When DeleteOnClose is set on an smb_node, the common open code will
 641  659   * reject subsequent open requests for the file. Observation of Windows
 642  660   * 2000 indicates that subsequent opens should be allowed (assuming
 643  661   * there would be no sharing violation) until the file is closed using
 644  662   * the fid on which the DeleteOnClose was requested.
 645  663   *
 646  664   * If there are multiple opens with delete-on-close create options,
 647  665   * whichever the first file handle is closed will trigger the node to be
 648  666   * marked as delete-on-close. The credentials of that ofile will be used
 649  667   * as the delete-on-close credentials of the node.
      668 + *
      669 + * Note that "read-only" tests have already happened before this call.
 650  670   */
 651  671  uint32_t
 652  672  smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags)
 653  673  {
 654      -        int rc = 0;
 655  674          uint32_t status;
 656      -        smb_attr_t attr;
 657  675  
 658      -        if (node->n_pending_dosattr & FILE_ATTRIBUTE_READONLY)
 659      -                return (NT_STATUS_CANNOT_DELETE);
 660      -
 661      -        bzero(&attr, sizeof (smb_attr_t));
 662      -        attr.sa_mask = SMB_AT_DOSATTR;
 663      -        rc = smb_fsop_getattr(NULL, zone_kcred(), node, &attr);
 664      -        if ((rc != 0) || (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) {
 665      -                return (NT_STATUS_CANNOT_DELETE);
 666      -        }
 667      -
 668  676          /*
 669  677           * If the directory is not empty we should fail setting del-on-close
 670  678           * with STATUS_DIRECTORY_NOT_EMPTY. see MS's
 671  679           * "File System Behavior Overview" doc section 4.3.2
 672  680           */
 673  681          if (smb_node_is_dir(node)) {
 674      -                status = smb_rmdir_possible(node, flags);
      682 +                status = smb_rmdir_possible(node);
 675  683                  if (status != 0) {
 676  684                          return (status);
 677  685                  }
 678  686          }
 679  687  
 680  688          mutex_enter(&node->n_mutex);
 681  689          if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
      690 +                /* It was already marked.  We're done. */
 682  691                  mutex_exit(&node->n_mutex);
 683      -                return (NT_STATUS_CANNOT_DELETE);
      692 +                return (NT_STATUS_SUCCESS);
 684  693          }
 685  694  
 686  695          crhold(cr);
 687  696          node->delete_on_close_cred = cr;
 688  697          node->n_delete_on_close_flags = flags;
 689  698          node->flags |= NODE_FLAGS_DELETE_ON_CLOSE;
 690  699          mutex_exit(&node->n_mutex);
 691  700  
 692  701          /*
 693  702           * Tell any change notify calls to close their handles
 694  703           * and get out of the way.  FILE_ACTION_DELETE_PENDING
 695  704           * is a special, internal-only action for this purpose.
 696  705           */
 697      -        smb_notify_event(node, FILE_ACTION_DELETE_PENDING, NULL);
      706 +        smb_node_notify_change(node, FILE_ACTION_DELETE_PENDING, NULL);
 698  707  
 699  708          return (NT_STATUS_SUCCESS);
 700  709  }
 701  710  
 702  711  void
 703  712  smb_node_reset_delete_on_close(smb_node_t *node)
 704  713  {
 705  714          mutex_enter(&node->n_mutex);
 706  715          if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
 707  716                  node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE;
↓ open down ↓ 27 lines elided ↑ open up ↑
 735  744          while (of) {
 736  745                  status = smb_ofile_open_check(of, desired_access, share_access);
 737  746  
 738  747                  switch (status) {
 739  748                  case NT_STATUS_INVALID_HANDLE:
 740  749                  case NT_STATUS_SUCCESS:
 741  750                          of = smb_llist_next(&node->n_ofile_list, of);
 742  751                          break;
 743  752                  default:
 744  753                          ASSERT(status == NT_STATUS_SHARING_VIOLATION);
      754 +                        DTRACE_PROBE3(conflict3,
      755 +                            smb_ofile_t, of,
      756 +                            uint32_t, desired_access,
      757 +                            uint32_t, share_access);
 745  758                          smb_llist_exit(&node->n_ofile_list);
 746  759                          return (status);
 747  760                  }
 748  761          }
 749  762  
 750  763          smb_llist_exit(&node->n_ofile_list);
 751  764          return (NT_STATUS_SUCCESS);
 752  765  }
 753  766  
 754  767  uint32_t
↓ open down ↓ 12 lines elided ↑ open up ↑
 767  780          while (of) {
 768  781                  status = smb_ofile_rename_check(of);
 769  782  
 770  783                  switch (status) {
 771  784                  case NT_STATUS_INVALID_HANDLE:
 772  785                  case NT_STATUS_SUCCESS:
 773  786                          of = smb_llist_next(&node->n_ofile_list, of);
 774  787                          break;
 775  788                  default:
 776  789                          ASSERT(status == NT_STATUS_SHARING_VIOLATION);
      790 +                        DTRACE_PROBE1(conflict1, smb_ofile_t, of);
 777  791                          smb_llist_exit(&node->n_ofile_list);
 778  792                          return (status);
 779  793                  }
 780  794          }
 781  795          smb_llist_exit(&node->n_ofile_list);
 782  796          return (NT_STATUS_SUCCESS);
 783  797  }
 784  798  
 785  799  uint32_t
 786  800  smb_node_delete_check(smb_node_t *node)
↓ open down ↓ 17 lines elided ↑ open up ↑
 804  818          while (of) {
 805  819                  status = smb_ofile_delete_check(of);
 806  820  
 807  821                  switch (status) {
 808  822                  case NT_STATUS_INVALID_HANDLE:
 809  823                  case NT_STATUS_SUCCESS:
 810  824                          of = smb_llist_next(&node->n_ofile_list, of);
 811  825                          break;
 812  826                  default:
 813  827                          ASSERT(status == NT_STATUS_SHARING_VIOLATION);
      828 +                        DTRACE_PROBE1(conflict1, smb_ofile_t, of);
 814  829                          smb_llist_exit(&node->n_ofile_list);
 815  830                          return (status);
 816  831                  }
 817  832          }
 818  833          smb_llist_exit(&node->n_ofile_list);
 819  834          return (NT_STATUS_SUCCESS);
 820  835  }
 821  836  
 822  837  /*
 823  838   * smb_node_share_check
↓ open down ↓ 16 lines elided ↑ open up ↑
 840  855          smb_llist_exit(&node->n_ofile_list);
 841  856  
 842  857          return (status);
 843  858  }
 844  859  
 845  860  /*
 846  861   * SMB Change Notification
 847  862   */
 848  863  
 849  864  void
 850      -smb_node_fcn_subscribe(smb_node_t *node, smb_request_t *sr)
      865 +smb_node_fcn_subscribe(smb_node_t *node)
 851  866  {
 852      -        smb_node_fcn_t          *fcn = &node->n_fcn;
 853  867  
 854      -        mutex_enter(&fcn->fcn_mutex);
 855      -        if (fcn->fcn_count == 0)
      868 +        mutex_enter(&node->n_mutex);
      869 +        if (node->n_fcn_count == 0)
 856  870                  (void) smb_fem_fcn_install(node);
 857      -        fcn->fcn_count++;
 858      -        list_insert_tail(&fcn->fcn_watchers, sr);
 859      -        mutex_exit(&fcn->fcn_mutex);
      871 +        node->n_fcn_count++;
      872 +        mutex_exit(&node->n_mutex);
 860  873  }
 861  874  
 862  875  void
 863      -smb_node_fcn_unsubscribe(smb_node_t *node, smb_request_t *sr)
      876 +smb_node_fcn_unsubscribe(smb_node_t *node)
 864  877  {
 865      -        smb_node_fcn_t          *fcn = &node->n_fcn;
 866  878  
 867      -        mutex_enter(&fcn->fcn_mutex);
 868      -        list_remove(&fcn->fcn_watchers, sr);
 869      -        fcn->fcn_count--;
 870      -        if (fcn->fcn_count == 0)
      879 +        mutex_enter(&node->n_mutex);
      880 +        node->n_fcn_count--;
      881 +        if (node->n_fcn_count == 0)
 871  882                  smb_fem_fcn_uninstall(node);
 872      -        mutex_exit(&fcn->fcn_mutex);
      883 +        mutex_exit(&node->n_mutex);
 873  884  }
 874  885  
 875  886  void
 876  887  smb_node_notify_change(smb_node_t *node, uint_t action, const char *name)
 877  888  {
      889 +        smb_ofile_t     *of;
      890 +
 878  891          SMB_NODE_VALID(node);
 879  892  
 880      -        smb_notify_event(node, action, name);
      893 +        smb_llist_enter(&node->n_ofile_list, RW_READER);
      894 +        of = smb_llist_head(&node->n_ofile_list);
      895 +        while (of) {
      896 +                /*
      897 +                 * We'd rather deliver events only to ofiles that have
      898 +                 * subscribed.  There's no explicit synchronization with
      899 +                 * where this flag is set, but other actions cause this
      900 +                 * value to reach visibility soon enough for events to
      901 +                 * start arriving by the time we need them to start.
      902 +                 * Once nc_subscribed is set, it stays set for the
      903 +                 * life of the ofile.
      904 +                 */
      905 +                if (of->f_notify.nc_subscribed)
      906 +                        smb_notify_ofile(of, action, name);
      907 +                of = smb_llist_next(&node->n_ofile_list, of);
      908 +        }
      909 +        smb_llist_exit(&node->n_ofile_list);
 881  910  
 882  911          /*
 883      -         * These two events come as a pair:
 884      -         *   FILE_ACTION_RENAMED_OLD_NAME
 885      -         *   FILE_ACTION_RENAMED_NEW_NAME
 886      -         * Only do the parent notify for "new".
      912 +         * After changes that add or remove a name,
      913 +         * we know the directory attributes changed,
      914 +         * and we can tell the immediate parent.
 887  915           */
 888      -        if (action == FILE_ACTION_RENAMED_OLD_NAME)
 889      -                return;
      916 +        switch (action) {
      917 +        case FILE_ACTION_ADDED:
      918 +        case FILE_ACTION_REMOVED:
      919 +        case FILE_ACTION_RENAMED_NEW_NAME:
      920 +                /*
      921 +                 * Note: FILE_ACTION_RENAMED_OLD_NAME is intentionally
      922 +                 * omitted, because it's always followed by another
      923 +                 * event with FILE_ACTION_RENAMED_NEW_NAME posted to
      924 +                 * the same directory, and we only need/want one.
      925 +                 */
      926 +                if (node->n_dnode != NULL) {
      927 +                        smb_node_notify_change(node->n_dnode,
      928 +                            FILE_ACTION_MODIFIED, node->od_name);
      929 +                }
      930 +                break;
      931 +        }
 890  932  
 891      -        smb_node_notify_parents(node);
      933 +        /*
      934 +         * If we wanted to support recursive notify events
      935 +         * (where a notify call on some directory receives
      936 +         * events from all objects below that directory),
      937 +         * we might deliver _SUBDIR_CHANGED to all our
      938 +         * parents, grandparents etc, here.  However, we
      939 +         * don't currently subscribe to changes on all the
      940 +         * child (and grandchild) objects that would be
      941 +         * needed to make that work. It's prohibitively
      942 +         * expensive to do that, and support for recursive
      943 +         * notify is optional anyway, so don't bother.
      944 +         */
 892  945  }
 893  946  
 894  947  /*
 895      - * smb_node_notify_parents
 896      - *
 897      - * Iterate up the directory tree notifying any parent
 898      - * directories that are being watched for changes in
 899      - * their sub directories.
 900      - * Stop at the root node, which has a NULL parent node.
 901      - */
 902      -void
 903      -smb_node_notify_parents(smb_node_t *dnode)
 904      -{
 905      -        smb_node_t *pnode;      /* parent */
 906      -
 907      -        SMB_NODE_VALID(dnode);
 908      -        pnode = dnode->n_dnode;
 909      -
 910      -        while (pnode != NULL) {
 911      -                SMB_NODE_VALID(pnode);
 912      -                smb_notify_event(pnode, FILE_ACTION_SUBDIR_CHANGED, NULL);
 913      -                /* cd .. */
 914      -                dnode = pnode;
 915      -                pnode = dnode->n_dnode;
 916      -        }
 917      -}
 918      -
 919      -/*
 920  948   * smb_node_start_crit()
 921  949   *
 922  950   * Enter critical region for share reservations.
 923  951   * See comments above smb_fsop_shrlock().
 924  952   */
 925  953  void
 926  954  smb_node_start_crit(smb_node_t *node, krw_t mode)
 927  955  {
 928  956          rw_enter(&node->n_lock, mode);
 929  957          nbl_start_crit(node->vp, mode);
↓ open down ↓ 196 lines elided ↑ open up ↑
1126 1154          }
1127 1155  
1128 1156          /* append named stream name if necessary */
1129 1157          if (SMB_IS_STREAM(node))
1130 1158                  (void) strlcat(buf, node->od_name, buflen);
1131 1159  
1132 1160          return (rc);
1133 1161  }
1134 1162  
1135 1163  /*
     1164 + * smb_node_getpath_nofail
     1165 + *
     1166 + * Same as smb_node_getpath, but try to reconstruct on failure,
     1167 + * and truncate from the beginning if we can't.
     1168 + */
     1169 +void
     1170 +smb_node_getpath_nofail(smb_node_t *node, vnode_t *rootvp, char *buf,
     1171 +    uint32_t buflen)
     1172 +{
     1173 +        int rc, len, addlen;
     1174 +        vnode_t *vp;
     1175 +        smb_node_t *unode, *dnode;
     1176 +        cred_t *kcr = zone_kcred();
     1177 +        boolean_t is_dir, is_stream;
     1178 +
     1179 +        is_stream = (SMB_IS_STREAM(node) != NULL);
     1180 +        unode = (is_stream) ? node->n_unode : node;
     1181 +        is_dir = smb_node_is_dir(unode);
     1182 +        dnode = (is_dir) ? unode : unode->n_dnode;
     1183 +
     1184 +        /* find path to directory node */
     1185 +        vp = dnode->vp;
     1186 +        VN_HOLD(vp);
     1187 +        if (rootvp) {
     1188 +                VN_HOLD(rootvp);
     1189 +                rc = vnodetopath(rootvp, vp, buf, buflen, kcr);
     1190 +                VN_RELE(rootvp);
     1191 +        } else {
     1192 +                rc = vnodetopath(NULL, vp, buf, buflen, kcr);
     1193 +        }
     1194 +        VN_RELE(vp);
     1195 +
     1196 +        /* On failure, reconstruct the path from the node_t's */
     1197 +        if (rc != 0) {
     1198 +                smb_node_t *nodep = unode;
     1199 +                char *p = buf + buflen;
     1200 +
     1201 +                /* append named stream name if necessary */
     1202 +                if (is_stream) {
     1203 +                        len = strlen(node->od_name) + 1;
     1204 +                        ASSERT3U(buflen, >=, len);
     1205 +                        p -= len;
     1206 +                        (void) strcpy(p, node->od_name);
     1207 +                }
     1208 +
     1209 +                len = strlen(nodep->od_name) + 1;
     1210 +                p -= len;
     1211 +                while (nodep->n_dnode != NULL && nodep->vp != rootvp &&
     1212 +                    p >= buf) {
     1213 +                        (void) strcpy(p, nodep->od_name);
     1214 +                        p[len - 1] = '/';
     1215 +                        nodep = nodep->n_dnode;
     1216 +                        len = strlen(nodep->od_name) + 1;
     1217 +                        p -= len;
     1218 +                }
     1219 +                if (nodep->n_dnode != NULL && nodep->vp != rootvp) {
     1220 +                        /* something went horribly wrong... */
     1221 +#ifdef DEBUG
     1222 +                        cmn_err(CE_WARN,
     1223 +                            "smb_node_getpath_nofail: buffer too small: "
     1224 +                            "size %d", buflen);
     1225 +#else
     1226 +                        cmn_err(CE_WARN,
     1227 +                            "smb_node_getpath_nofail: couldn't get full path");
     1228 +#endif
     1229 +                        p = buf;
     1230 +                        *p = '*';
     1231 +                } else {
     1232 +                        p += len - 1;
     1233 +                        if (p >= buf)
     1234 +                                *p = '/';
     1235 +                }
     1236 +
     1237 +                buf[buflen - 1] = '\0';
     1238 +                (void) memmove(buf, p, strlen(p) + 1);
     1239 +                cmn_err(CE_NOTE,
     1240 +                    "smb_node_getpath_nofail: vnodetopath failed, rc=%d", rc);
     1241 +                return;
     1242 +        }
     1243 +
     1244 +        len = strlen(buf) + 1;
     1245 +
     1246 +        /* append filename if necessary */
     1247 +        if (!is_dir) {
     1248 +                if (buf[len - 2] != '/' && strlcat(buf, "/", buflen) >= buflen)
     1249 +                        goto trunc;
     1250 +                if (strlcat(buf, unode->od_name, buflen) >= buflen)
     1251 +                        goto trunc;
     1252 +        }
     1253 +
     1254 +        /* append named stream name if necessary */
     1255 +        if (!is_stream || strlcat(buf, node->od_name, buflen) < buflen)
     1256 +                return;
     1257 +
     1258 +trunc:
     1259 +        buf[len - 1] = '\0';
     1260 +        addlen = 0;
     1261 +        /* append filename if necessary */
     1262 +        if (!is_dir) {
     1263 +                if (buf[len - 2] != '/')
     1264 +                        addlen++;
     1265 +                addlen += strlen(unode->od_name);
     1266 +        }
     1267 +
     1268 +        /* append named stream name if necessary */
     1269 +        if (is_stream)
     1270 +                addlen += strlen(node->od_name);
     1271 +
     1272 +        if ((buflen - len) < addlen) {
     1273 +#ifdef DEBUG
     1274 +                cmn_err(CE_WARN,
     1275 +                    "smb_node_getpath_nofail: vnodetopath succeeded, "
     1276 +                    "but buffer too small for filename");
     1277 +#else
     1278 +                cmn_err(CE_WARN,
     1279 +                    "smb_node_getpath_nofail: couldn't get full path");
     1280 +#endif
     1281 +                addlen = addlen - (buflen - len);
     1282 +                (void) memmove(buf, buf + addlen, len - addlen);
     1283 +                buf[0] = '*';
     1284 +        }
     1285 +
     1286 +        /* append filename if necessary */
     1287 +        if (!is_dir) {
     1288 +                if (buf[len - 2] != '/')
     1289 +                        (void) strlcat(buf, "/", buflen);
     1290 +                (void) strlcat(buf, unode->od_name, buflen);
     1291 +        }
     1292 +
     1293 +        /* append named stream name if necessary */
     1294 +        if (is_stream)
     1295 +                (void) strlcat(buf, node->od_name, buflen);
     1296 +}
     1297 +
     1298 +/*
1136 1299   * smb_node_alloc
1137 1300   */
1138 1301  static smb_node_t *
1139 1302  smb_node_alloc(
1140 1303      char        *od_name,
1141 1304      vnode_t     *vp,
1142 1305      smb_llist_t *bucket,
1143 1306      uint32_t    hashkey)
1144 1307  {
1145 1308          smb_node_t      *node;
↓ open down ↓ 3 lines elided ↑ open up ↑
1149 1312  
1150 1313          if (node->n_audit_buf != NULL)
1151 1314                  node->n_audit_buf->anb_index = 0;
1152 1315  
1153 1316          node->flags = 0;
1154 1317          VN_HOLD(vp);
1155 1318          node->vp = vp;
1156 1319          node->n_refcnt = 1;
1157 1320          node->n_hash_bucket = bucket;
1158 1321          node->n_hashkey = hashkey;
1159      -        node->n_pending_dosattr = 0;
1160 1322          node->n_open_count = 0;
1161 1323          node->n_allocsz = 0;
1162 1324          node->n_dnode = NULL;
1163 1325          node->n_unode = NULL;
1164 1326          node->delete_on_close_cred = NULL;
1165 1327          node->n_delete_on_close_flags = 0;
1166 1328          node->n_oplock.ol_fem = B_FALSE;
1167      -        node->n_oplock.ol_xthread = NULL;
1168      -        node->n_oplock.ol_count = 0;
1169      -        node->n_oplock.ol_break = SMB_OPLOCK_NO_BREAK;
1170 1329  
1171 1330          (void) strlcpy(node->od_name, od_name, sizeof (node->od_name));
1172 1331          if (strcmp(od_name, XATTR_DIR) == 0)
1173 1332                  node->flags |= NODE_XATTR_DIR;
1174 1333  
1175 1334          if (VFS_ROOT(vp->v_vfsp, &root_vp) == 0) {
1176 1335                  if (vp == root_vp)
1177 1336                          node->flags |= NODE_FLAGS_VFSROOT;
1178 1337                  VN_RELE(root_vp);
1179 1338          }
↓ open down ↓ 8 lines elided ↑ open up ↑
1188 1347   * smb_node_free
1189 1348   */
1190 1349  static void
1191 1350  smb_node_free(smb_node_t *node)
1192 1351  {
1193 1352          SMB_NODE_VALID(node);
1194 1353  
1195 1354          node->n_magic = 0;
1196 1355          VERIFY(!list_link_active(&node->n_lnd));
1197 1356          VERIFY(node->n_lock_list.ll_count == 0);
     1357 +        VERIFY(node->n_wlock_list.ll_count == 0);
1198 1358          VERIFY(node->n_ofile_list.ll_count == 0);
1199      -        VERIFY(node->n_oplock.ol_count == 0);
1200      -        VERIFY(node->n_oplock.ol_xthread == NULL);
1201 1359          VERIFY(node->n_oplock.ol_fem == B_FALSE);
1202 1360          VERIFY(MUTEX_NOT_HELD(&node->n_mutex));
1203 1361          VERIFY(!RW_LOCK_HELD(&node->n_lock));
1204 1362          VN_RELE(node->vp);
1205 1363          kmem_cache_free(smb_node_cache, node);
1206 1364  }
1207 1365  
1208 1366  /*
1209 1367   * smb_node_constructor
1210 1368   */
1211 1369  static int
1212 1370  smb_node_constructor(void *buf, void *un, int kmflags)
1213 1371  {
1214 1372          _NOTE(ARGUNUSED(kmflags, un))
1215 1373  
1216 1374          smb_node_t      *node = (smb_node_t *)buf;
1217 1375  
1218 1376          bzero(node, sizeof (smb_node_t));
1219 1377  
1220 1378          smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
1221      -            offsetof(smb_ofile_t, f_nnd));
     1379 +            offsetof(smb_ofile_t, f_node_lnd));
1222 1380          smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t),
1223 1381              offsetof(smb_lock_t, l_lnd));
1224      -        mutex_init(&node->n_fcn.fcn_mutex, NULL, MUTEX_DEFAULT, NULL);
1225      -        list_create(&node->n_fcn.fcn_watchers, sizeof (smb_request_t),
1226      -            offsetof(smb_request_t, sr_ncr.nc_lnd));
1227      -        cv_init(&node->n_oplock.ol_cv, NULL, CV_DEFAULT, NULL);
     1382 +        smb_llist_constructor(&node->n_wlock_list, sizeof (smb_lock_t),
     1383 +            offsetof(smb_lock_t, l_lnd));
1228 1384          mutex_init(&node->n_oplock.ol_mutex, NULL, MUTEX_DEFAULT, NULL);
1229      -        list_create(&node->n_oplock.ol_grants, sizeof (smb_oplock_grant_t),
1230      -            offsetof(smb_oplock_grant_t, og_lnd));
     1385 +        cv_init(&node->n_oplock.WaitingOpenCV, NULL, CV_DEFAULT, NULL);
1231 1386          rw_init(&node->n_lock, NULL, RW_DEFAULT, NULL);
1232 1387          mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL);
1233 1388          smb_node_create_audit_buf(node, kmflags);
1234 1389          return (0);
1235 1390  }
1236 1391  
1237 1392  /*
1238 1393   * smb_node_destructor
1239 1394   */
1240 1395  static void
1241 1396  smb_node_destructor(void *buf, void *un)
1242 1397  {
1243 1398          _NOTE(ARGUNUSED(un))
1244 1399  
1245 1400          smb_node_t      *node = (smb_node_t *)buf;
1246 1401  
1247 1402          smb_node_destroy_audit_buf(node);
1248 1403          mutex_destroy(&node->n_mutex);
1249 1404          rw_destroy(&node->n_lock);
1250      -        cv_destroy(&node->n_oplock.ol_cv);
     1405 +        cv_destroy(&node->n_oplock.WaitingOpenCV);
1251 1406          mutex_destroy(&node->n_oplock.ol_mutex);
1252      -        list_destroy(&node->n_fcn.fcn_watchers);
1253      -        mutex_destroy(&node->n_fcn.fcn_mutex);
1254 1407          smb_llist_destructor(&node->n_lock_list);
     1408 +        smb_llist_destructor(&node->n_wlock_list);
1255 1409          smb_llist_destructor(&node->n_ofile_list);
1256      -        list_destroy(&node->n_oplock.ol_grants);
1257 1410  }
1258 1411  
1259 1412  /*
1260 1413   * smb_node_create_audit_buf
1261 1414   */
1262 1415  static void
1263 1416  smb_node_create_audit_buf(smb_node_t *node, int kmflags)
1264 1417  {
1265 1418          smb_audit_buf_node_t    *abn;
1266 1419  
↓ open down ↓ 105 lines elided ↑ open up ↑
1372 1525  smb_node_is_system(smb_node_t *node)
1373 1526  {
1374 1527          SMB_NODE_VALID(node);
1375 1528          return ((node->flags & NODE_FLAGS_SYSTEM) == NODE_FLAGS_SYSTEM);
1376 1529  }
1377 1530  
1378 1531  /*
1379 1532   * smb_node_file_is_readonly
1380 1533   *
1381 1534   * Checks if the file (which node represents) is marked readonly
1382      - * in the filesystem. No account is taken of any pending readonly
1383      - * in the node, which must be handled by the callers.
1384      - * (See SMB_OFILE_IS_READONLY and SMB_PATHFILE_IS_READONLY)
     1535 + * in the filesystem.  Note that there may be handles open with
     1536 + * modify rights, and those continue to allow access even after
     1537 + * the DOS read-only flag has been set in the file system.
1385 1538   */
1386 1539  boolean_t
1387 1540  smb_node_file_is_readonly(smb_node_t *node)
1388 1541  {
1389 1542          smb_attr_t attr;
1390 1543  
1391 1544          if (node == NULL)
1392 1545                  return (B_FALSE);       /* pipes */
1393 1546  
1394      -        if (node->n_pending_dosattr & FILE_ATTRIBUTE_READONLY)
1395      -                return (B_TRUE);
1396      -
1397 1547          bzero(&attr, sizeof (smb_attr_t));
1398 1548          attr.sa_mask = SMB_AT_DOSATTR;
1399 1549          (void) smb_fsop_getattr(NULL, zone_kcred(), node, &attr);
1400 1550          return ((attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) != 0);
1401 1551  }
1402 1552  
1403 1553  /*
1404 1554   * smb_node_setattr
1405 1555   *
1406 1556   * The sr may be NULL, for example when closing an ofile.
↓ open down ↓ 45 lines elided ↑ open up ↑
1452 1602   * to block size. If the file size is smaller than the allocation
1453 1603   * size the file is truncated by setting the filesize to allocsz.
1454 1604   */
1455 1605  int
1456 1606  smb_node_setattr(smb_request_t *sr, smb_node_t *node,
1457 1607      cred_t *cr, smb_ofile_t *of, smb_attr_t *attr)
1458 1608  {
1459 1609          int rc;
1460 1610          uint_t times_mask;
1461 1611          smb_attr_t tmp_attr;
     1612 +        smb_node_t *unnamed_node;
1462 1613  
1463 1614          SMB_NODE_VALID(node);
1464 1615  
1465 1616          /* set attributes specified in attr */
1466 1617          if (attr->sa_mask == 0)
1467 1618                  return (0);  /* nothing to do (caller bug?) */
1468 1619  
1469 1620          /*
1470 1621           * Allocation size and EOF position interact.
1471 1622           * We don't persistently store the allocation size
↓ open down ↓ 80 lines elided ↑ open up ↑
1552 1703                          pa->sa_vattr.va_mtime =
1553 1704                              attr->sa_vattr.va_mtime;
1554 1705                  if (times_mask & SMB_AT_CTIME)
1555 1706                          pa->sa_vattr.va_ctime =
1556 1707                              attr->sa_vattr.va_ctime;
1557 1708                  if (times_mask & SMB_AT_CRTIME)
1558 1709                          pa->sa_crtime =
1559 1710                              attr->sa_crtime;
1560 1711  
1561 1712                  mutex_exit(&of->f_mutex);
     1713 +
1562 1714                  /*
1563 1715                   * The f_pending_attr times are reapplied in
1564 1716                   * smb_ofile_close().
1565 1717                   */
     1718 +
     1719 +                /*
     1720 +                 * If this change is comming directly from a client
     1721 +                 * (sr != NULL) and it's a persistent handle, save
     1722 +                 * the "sticky times" in the handle.
     1723 +                 */
     1724 +                if (sr != NULL && of->dh_persist) {
     1725 +                        smb2_dh_update_times(sr, of, attr);
     1726 +                }
1566 1727          }
1567 1728  
1568      -        /*
1569      -         * After this point, tmp_attr is what we will actually
1570      -         * store in the file system _now_, which may differ
1571      -         * from the callers attr and f_pending_attr w.r.t.
1572      -         * the DOS readonly flag etc.
1573      -         */
1574      -        bcopy(attr, &tmp_attr, sizeof (tmp_attr));
1575      -        if (attr->sa_mask & (SMB_AT_DOSATTR | SMB_AT_ALLOCSZ)) {
     1729 +        if ((attr->sa_mask & SMB_AT_ALLOCSZ) != 0) {
1576 1730                  mutex_enter(&node->n_mutex);
1577      -                if ((attr->sa_mask & SMB_AT_DOSATTR) != 0) {
1578      -                        tmp_attr.sa_dosattr &= smb_vop_dosattr_settable;
1579      -                        if (((tmp_attr.sa_dosattr &
1580      -                            FILE_ATTRIBUTE_READONLY) != 0) &&
1581      -                            (node->n_open_count != 0)) {
1582      -                                /* Delay setting readonly */
1583      -                                node->n_pending_dosattr =
1584      -                                    tmp_attr.sa_dosattr;
1585      -                                tmp_attr.sa_dosattr &=
1586      -                                    ~FILE_ATTRIBUTE_READONLY;
1587      -                        } else {
1588      -                                node->n_pending_dosattr = 0;
1589      -                        }
1590      -                }
1591 1731                  /*
1592 1732                   * Simulate n_allocsz persistence only while
1593 1733                   * there are opens.  See smb_node_getattr
1594 1734                   */
1595      -                if ((attr->sa_mask & SMB_AT_ALLOCSZ) != 0 &&
1596      -                    node->n_open_count != 0)
     1735 +                if (node->n_open_count != 0)
1597 1736                          node->n_allocsz = attr->sa_allocsz;
1598 1737                  mutex_exit(&node->n_mutex);
1599 1738          }
1600 1739  
1601      -        rc = smb_fsop_setattr(sr, cr, node, &tmp_attr);
     1740 +        rc = smb_fsop_setattr(sr, cr, node, attr);
1602 1741          if (rc != 0)
1603 1742                  return (rc);
1604 1743  
1605 1744          if (node->n_dnode != NULL) {
1606 1745                  smb_node_notify_change(node->n_dnode,
1607 1746                      FILE_ACTION_MODIFIED, node->od_name);
1608 1747          }
1609 1748  
     1749 +        if ((unnamed_node = SMB_IS_STREAM(node)) != NULL) {
     1750 +                ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
     1751 +                ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
     1752 +                smb_node_notify_change(node->n_dnode,
     1753 +                    FILE_ACTION_MODIFIED_STREAM, node->od_name);
     1754 +        }
     1755 +
1610 1756          return (0);
1611 1757  }
1612 1758  
1613 1759  /*
1614 1760   * smb_node_getattr
1615 1761   *
1616 1762   * Get attributes from the file system and apply any smb-specific
1617 1763   * overrides for size, dos attributes and timestamps
1618 1764   *
1619 1765   * When node->n_pending_readonly is set on a node, pretend that
↓ open down ↓ 20 lines elided ↑ open up ↑
1640 1786                  attr->sa_mask |= SMB_AT_TYPE;
1641 1787  
1642 1788          rc = smb_fsop_getattr(sr, cr, node, attr);
1643 1789          if (rc != 0)
1644 1790                  return (rc);
1645 1791  
1646 1792          isdir = smb_node_is_dir(node);
1647 1793  
1648 1794          mutex_enter(&node->n_mutex);
1649 1795  
1650      -        /*
1651      -         * When there are open handles, and one of them has
1652      -         * set the DOS readonly flag (in n_pending_dosattr),
1653      -         * it will not have been stored in the file system.
1654      -         * In this case use n_pending_dosattr. Note that
1655      -         * n_pending_dosattr has only the settable bits,
1656      -         * (setattr masks it with smb_vop_dosattr_settable)
1657      -         * so we need to keep any non-settable bits we got
1658      -         * from the file-system above.
1659      -         */
1660 1796          if (attr->sa_mask & SMB_AT_DOSATTR) {
1661      -                if (node->n_pending_dosattr) {
1662      -                        attr->sa_dosattr &= ~smb_vop_dosattr_settable;
1663      -                        attr->sa_dosattr |= node->n_pending_dosattr;
1664      -                }
1665 1797                  if (attr->sa_dosattr == 0) {
1666 1798                          attr->sa_dosattr = (isdir) ?
1667 1799                              FILE_ATTRIBUTE_DIRECTORY:
1668 1800                              FILE_ATTRIBUTE_NORMAL;
1669 1801                  }
1670 1802          }
1671 1803  
1672 1804          /*
1673 1805           * Also fix-up sa_allocsz, which is not persistent.
1674 1806           * When there are no open files, allocsz is faked.
↓ open down ↓ 135 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX