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

*** 18,28 **** * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * SMB Node State Machine * ---------------------- * --- 18,28 ---- * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * SMB Node State Machine * ---------------------- *
*** 86,96 **** * releasing the mutex. That way, even if smb_node_lookup() finds it, the * state will indicate that the node should be treated as non existent (of * course the state of the node should be tested/updated under the * protection of the mutex). */ ! #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> #include <smbsrv/smb_kstat.h> #include <sys/ddi.h> #include <sys/extdirent.h> #include <sys/pathname.h> --- 86,96 ---- * releasing the mutex. That way, even if smb_node_lookup() finds it, the * state will indicate that the node should be treated as non existent (of * course the state of the node should be tested/updated under the * protection of the mutex). */ ! #include <smbsrv/smb2_kproto.h> #include <smbsrv/smb_fsops.h> #include <smbsrv/smb_kstat.h> #include <sys/ddi.h> #include <sys/extdirent.h> #include <sys/pathname.h>
*** 97,107 **** #include <sys/sdt.h> #include <sys/nbmlock.h> #include <fs/fs_reparse.h> uint32_t smb_is_executable(char *); - static void smb_node_delete_on_close(smb_node_t *); static void smb_node_create_audit_buf(smb_node_t *, int); static void smb_node_destroy_audit_buf(smb_node_t *); static void smb_node_audit(smb_node_t *); static smb_node_t *smb_node_alloc(char *, vnode_t *, smb_llist_t *, uint32_t); static void smb_node_free(smb_node_t *); --- 97,106 ----
*** 188,197 **** --- 187,197 ---- if (smb_node_cache == NULL) return; #ifdef DEBUG for (i = 0; i <= SMBND_HASH_MASK; i++) { + smb_llist_t *bucket; smb_node_t *node; /* * The following sequence is just intended for sanity check. * This will have to be modified when the code goes into
*** 203,215 **** * The reason why SMB nodes are still remaining in the hash * table is problably due to a mismatch between calls to * smb_node_lookup() and smb_node_release(). You must track that * down. */ ! node = smb_llist_head(&smb_node_hash_table[i]); ! ASSERT(node == NULL); } #endif for (i = 0; i <= SMBND_HASH_MASK; i++) { smb_llist_destructor(&smb_node_hash_table[i]); } --- 203,220 ---- * The reason why SMB nodes are still remaining in the hash * table is problably due to a mismatch between calls to * smb_node_lookup() and smb_node_release(). You must track that * down. */ ! bucket = &smb_node_hash_table[i]; ! node = smb_llist_head(bucket); ! while (node != NULL) { ! cmn_err(CE_NOTE, "leaked node: 0x%p %s", ! (void *)node, node->od_name); ! node = smb_llist_next(bucket, node); } + } #endif for (i = 0; i <= SMBND_HASH_MASK; i++) { smb_llist_destructor(&smb_node_hash_table[i]); }
*** 480,490 **** --- 485,497 ---- smb_llist_exit(node->n_hash_bucket); /* * Check if the file was deleted */ + if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { smb_node_delete_on_close(node); + } if (node->n_dnode) { ASSERT(node->n_dnode->n_magic == SMB_NODE_MAGIC); smb_node_release(node->n_dnode);
*** 505,524 **** } smb_node_audit(node); mutex_exit(&node->n_mutex); } ! static void smb_node_delete_on_close(smb_node_t *node) { smb_node_t *d_snode; int rc = 0; uint32_t flags = 0; d_snode = node->n_dnode; ! if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; flags = node->n_delete_on_close_flags; ASSERT(node->od_name != NULL); if (smb_node_is_dir(node)) rc = smb_fsop_rmdir(0, node->delete_on_close_cred, --- 512,534 ---- } smb_node_audit(node); mutex_exit(&node->n_mutex); } ! void smb_node_delete_on_close(smb_node_t *node) { smb_node_t *d_snode; int rc = 0; uint32_t flags = 0; d_snode = node->n_dnode; ! ! ASSERT((node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0); ! node->flags &= ~NODE_FLAGS_DELETE_ON_CLOSE; + node->flags |= NODE_FLAGS_DELETE_COMMITTED; flags = node->n_delete_on_close_flags; ASSERT(node->od_name != NULL); if (smb_node_is_dir(node)) rc = smb_fsop_rmdir(0, node->delete_on_close_cred,
*** 525,535 **** d_snode, node->od_name, flags); else rc = smb_fsop_remove(0, node->delete_on_close_cred, d_snode, node->od_name, flags); crfree(node->delete_on_close_cred); ! } if (rc != 0) cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", node->od_name, rc); DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); } --- 535,546 ---- d_snode, node->od_name, flags); else rc = smb_fsop_remove(0, node->delete_on_close_cred, d_snode, node->od_name, flags); crfree(node->delete_on_close_cred); ! node->delete_on_close_cred = NULL; ! if (rc != 0) cmn_err(CE_WARN, "File %s could not be removed, rc=%d\n", node->od_name, rc); DTRACE_PROBE2(smb_node_delete_on_close, int, rc, smb_node_t *, node); }
*** 586,641 **** /* * We're getting smb nodes below the zone root here, * so need to use kcred, not zone_kcred(). */ error = smb_pathname(NULL, zone->zone_rootpath, 0, ! smb_root_node, smb_root_node, NULL, svrootp, kcred); return (error); } /* * Helper function for smb_node_set_delete_on_close(). Assumes node is a dir. * Return 0 if this is an empty dir. Otherwise return a NT_STATUS code. ! * We distinguish between readdir failure and non-empty dir by returning ! * different values. */ static uint32_t ! smb_rmdir_possible(smb_node_t *n, uint32_t flags) { ASSERT(n->vp->v_type == VDIR); ! char buf[512]; /* Only large enough to see if the dir is empty. */ ! int eof, bsize = sizeof (buf), reclen = 0; ! char *name; ! boolean_t edp = vfs_has_feature(n->vp->v_vfsp, VFSFT_DIRENTFLAGS); ! union { ! char *u_bufptr; ! struct edirent *u_edp; ! struct dirent64 *u_dp; ! } u; ! #define bufptr u.u_bufptr ! #define extdp u.u_edp ! #define dp u.u_dp ! if (smb_vop_readdir(n->vp, 0, buf, &bsize, &eof, flags, zone_kcred())) ! return (NT_STATUS_CANNOT_DELETE); ! if (bsize == 0) ! return (NT_STATUS_CANNOT_DELETE); bufptr = buf; ! while ((bufptr += reclen) < buf + bsize) { ! if (edp) { ! reclen = extdp->ed_reclen; ! name = extdp->ed_name; ! } else { ! reclen = dp->d_reclen; ! name = dp->d_name; } ! if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) ! return (NT_STATUS_DIRECTORY_NOT_EMPTY); } ! return (0); } /* * When DeleteOnClose is set on an smb_node, the common open code will * reject subsequent open requests for the file. Observation of Windows --- 597,659 ---- /* * We're getting smb nodes below the zone root here, * so need to use kcred, not zone_kcred(). */ error = smb_pathname(NULL, zone->zone_rootpath, 0, ! smb_root_node, smb_root_node, NULL, svrootp, kcred, NULL); return (error); } /* * Helper function for smb_node_set_delete_on_close(). Assumes node is a dir. * Return 0 if this is an empty dir. Otherwise return a NT_STATUS code. ! * Unfortunately, to find out if a directory is empty, we have to read it ! * and check for anything other than "." or ".." in the readdir buf. */ static uint32_t ! smb_rmdir_possible(smb_node_t *n) { ASSERT(n->vp->v_type == VDIR); ! char *buf; ! char *bufptr; ! struct dirent64 *dp; ! uint32_t status = NT_STATUS_SUCCESS; ! int bsize = SMB_ODIR_BUFSIZE; ! int eof = 0; ! buf = kmem_alloc(SMB_ODIR_BUFSIZE, KM_SLEEP); ! /* Flags zero: no edirent, no ABE wanted here */ ! if (smb_vop_readdir(n->vp, 0, buf, &bsize, &eof, 0, zone_kcred())) { ! status = NT_STATUS_INTERNAL_ERROR; ! goto out; ! } ! bufptr = buf; ! while (bsize > 0) { ! /* LINTED pointer alignment */ ! dp = (struct dirent64 *)bufptr; ! ! bufptr += dp->d_reclen; ! bsize -= dp->d_reclen; ! if (bsize < 0) { ! /* partial record */ ! status = NT_STATUS_DIRECTORY_NOT_EMPTY; ! break; } ! ! if (strcmp(dp->d_name, ".") != 0 && ! strcmp(dp->d_name, "..") != 0) { ! status = NT_STATUS_DIRECTORY_NOT_EMPTY; ! break; } ! } ! ! out: ! kmem_free(buf, SMB_ODIR_BUFSIZE); ! return (status); } /* * When DeleteOnClose is set on an smb_node, the common open code will * reject subsequent open requests for the file. Observation of Windows
*** 645,688 **** * * If there are multiple opens with delete-on-close create options, * whichever the first file handle is closed will trigger the node to be * marked as delete-on-close. The credentials of that ofile will be used * as the delete-on-close credentials of the node. */ uint32_t smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags) { - int rc = 0; uint32_t status; - smb_attr_t attr; - if (node->n_pending_dosattr & FILE_ATTRIBUTE_READONLY) - return (NT_STATUS_CANNOT_DELETE); - - bzero(&attr, sizeof (smb_attr_t)); - attr.sa_mask = SMB_AT_DOSATTR; - rc = smb_fsop_getattr(NULL, zone_kcred(), node, &attr); - if ((rc != 0) || (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) { - return (NT_STATUS_CANNOT_DELETE); - } - /* * If the directory is not empty we should fail setting del-on-close * with STATUS_DIRECTORY_NOT_EMPTY. see MS's * "File System Behavior Overview" doc section 4.3.2 */ if (smb_node_is_dir(node)) { ! status = smb_rmdir_possible(node, flags); if (status != 0) { return (status); } } mutex_enter(&node->n_mutex); if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { mutex_exit(&node->n_mutex); ! return (NT_STATUS_CANNOT_DELETE); } crhold(cr); node->delete_on_close_cred = cr; node->n_delete_on_close_flags = flags; --- 663,697 ---- * * If there are multiple opens with delete-on-close create options, * whichever the first file handle is closed will trigger the node to be * marked as delete-on-close. The credentials of that ofile will be used * as the delete-on-close credentials of the node. + * + * Note that "read-only" tests have already happened before this call. */ uint32_t smb_node_set_delete_on_close(smb_node_t *node, cred_t *cr, uint32_t flags) { uint32_t status; /* * If the directory is not empty we should fail setting del-on-close * with STATUS_DIRECTORY_NOT_EMPTY. see MS's * "File System Behavior Overview" doc section 4.3.2 */ if (smb_node_is_dir(node)) { ! status = smb_rmdir_possible(node); if (status != 0) { return (status); } } mutex_enter(&node->n_mutex); if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { + /* It was already marked. We're done. */ mutex_exit(&node->n_mutex); ! return (NT_STATUS_SUCCESS); } crhold(cr); node->delete_on_close_cred = cr; node->n_delete_on_close_flags = flags;
*** 692,702 **** /* * Tell any change notify calls to close their handles * and get out of the way. FILE_ACTION_DELETE_PENDING * is a special, internal-only action for this purpose. */ ! smb_notify_event(node, FILE_ACTION_DELETE_PENDING, NULL); return (NT_STATUS_SUCCESS); } void --- 701,711 ---- /* * Tell any change notify calls to close their handles * and get out of the way. FILE_ACTION_DELETE_PENDING * is a special, internal-only action for this purpose. */ ! smb_node_notify_change(node, FILE_ACTION_DELETE_PENDING, NULL); return (NT_STATUS_SUCCESS); } void
*** 740,749 **** --- 749,762 ---- case NT_STATUS_SUCCESS: of = smb_llist_next(&node->n_ofile_list, of); break; default: ASSERT(status == NT_STATUS_SHARING_VIOLATION); + DTRACE_PROBE3(conflict3, + smb_ofile_t, of, + uint32_t, desired_access, + uint32_t, share_access); smb_llist_exit(&node->n_ofile_list); return (status); } }
*** 772,781 **** --- 785,795 ---- case NT_STATUS_SUCCESS: of = smb_llist_next(&node->n_ofile_list, of); break; default: ASSERT(status == NT_STATUS_SHARING_VIOLATION); + DTRACE_PROBE1(conflict1, smb_ofile_t, of); smb_llist_exit(&node->n_ofile_list); return (status); } } smb_llist_exit(&node->n_ofile_list);
*** 809,818 **** --- 823,833 ---- case NT_STATUS_SUCCESS: of = smb_llist_next(&node->n_ofile_list, of); break; default: ASSERT(status == NT_STATUS_SHARING_VIOLATION); + DTRACE_PROBE1(conflict1, smb_ofile_t, of); smb_llist_exit(&node->n_ofile_list); return (status); } } smb_llist_exit(&node->n_ofile_list);
*** 845,921 **** /* * SMB Change Notification */ void ! smb_node_fcn_subscribe(smb_node_t *node, smb_request_t *sr) { - smb_node_fcn_t *fcn = &node->n_fcn; ! mutex_enter(&fcn->fcn_mutex); ! if (fcn->fcn_count == 0) (void) smb_fem_fcn_install(node); ! fcn->fcn_count++; ! list_insert_tail(&fcn->fcn_watchers, sr); ! mutex_exit(&fcn->fcn_mutex); } void ! smb_node_fcn_unsubscribe(smb_node_t *node, smb_request_t *sr) { - smb_node_fcn_t *fcn = &node->n_fcn; ! mutex_enter(&fcn->fcn_mutex); ! list_remove(&fcn->fcn_watchers, sr); ! fcn->fcn_count--; ! if (fcn->fcn_count == 0) smb_fem_fcn_uninstall(node); ! mutex_exit(&fcn->fcn_mutex); } void smb_node_notify_change(smb_node_t *node, uint_t action, const char *name) { SMB_NODE_VALID(node); ! smb_notify_event(node, action, name); ! /* ! * These two events come as a pair: ! * FILE_ACTION_RENAMED_OLD_NAME ! * FILE_ACTION_RENAMED_NEW_NAME ! * Only do the parent notify for "new". */ ! if (action == FILE_ACTION_RENAMED_OLD_NAME) ! return; ! smb_node_notify_parents(node); ! } ! ! /* ! * smb_node_notify_parents ! * ! * Iterate up the directory tree notifying any parent ! * directories that are being watched for changes in ! * their sub directories. ! * Stop at the root node, which has a NULL parent node. */ ! void ! smb_node_notify_parents(smb_node_t *dnode) ! { ! smb_node_t *pnode; /* parent */ ! ! SMB_NODE_VALID(dnode); ! pnode = dnode->n_dnode; ! ! while (pnode != NULL) { ! SMB_NODE_VALID(pnode); ! smb_notify_event(pnode, FILE_ACTION_SUBDIR_CHANGED, NULL); ! /* cd .. */ ! dnode = pnode; ! pnode = dnode->n_dnode; } } /* * smb_node_start_crit() * --- 860,949 ---- /* * SMB Change Notification */ void ! smb_node_fcn_subscribe(smb_node_t *node) { ! mutex_enter(&node->n_mutex); ! if (node->n_fcn_count == 0) (void) smb_fem_fcn_install(node); ! node->n_fcn_count++; ! mutex_exit(&node->n_mutex); } void ! smb_node_fcn_unsubscribe(smb_node_t *node) { ! mutex_enter(&node->n_mutex); ! node->n_fcn_count--; ! if (node->n_fcn_count == 0) smb_fem_fcn_uninstall(node); ! mutex_exit(&node->n_mutex); } void smb_node_notify_change(smb_node_t *node, uint_t action, const char *name) { + smb_ofile_t *of; + SMB_NODE_VALID(node); ! smb_llist_enter(&node->n_ofile_list, RW_READER); ! of = smb_llist_head(&node->n_ofile_list); ! while (of) { /* ! * We'd rather deliver events only to ofiles that have ! * subscribed. There's no explicit synchronization with ! * where this flag is set, but other actions cause this ! * value to reach visibility soon enough for events to ! * start arriving by the time we need them to start. ! * Once nc_subscribed is set, it stays set for the ! * life of the ofile. */ ! if (of->f_notify.nc_subscribed) ! smb_notify_ofile(of, action, name); ! of = smb_llist_next(&node->n_ofile_list, of); ! } ! smb_llist_exit(&node->n_ofile_list); ! /* ! * After changes that add or remove a name, ! * we know the directory attributes changed, ! * and we can tell the immediate parent. */ ! switch (action) { ! case FILE_ACTION_ADDED: ! case FILE_ACTION_REMOVED: ! case FILE_ACTION_RENAMED_NEW_NAME: ! /* ! * Note: FILE_ACTION_RENAMED_OLD_NAME is intentionally ! * omitted, because it's always followed by another ! * event with FILE_ACTION_RENAMED_NEW_NAME posted to ! * the same directory, and we only need/want one. ! */ ! if (node->n_dnode != NULL) { ! smb_node_notify_change(node->n_dnode, ! FILE_ACTION_MODIFIED, node->od_name); } + break; + } + + /* + * If we wanted to support recursive notify events + * (where a notify call on some directory receives + * events from all objects below that directory), + * we might deliver _SUBDIR_CHANGED to all our + * parents, grandparents etc, here. However, we + * don't currently subscribe to changes on all the + * child (and grandchild) objects that would be + * needed to make that work. It's prohibitively + * expensive to do that, and support for recursive + * notify is optional anyway, so don't bother. + */ } /* * smb_node_start_crit() *
*** 1131,1140 **** --- 1159,1303 ---- return (rc); } /* + * smb_node_getpath_nofail + * + * Same as smb_node_getpath, but try to reconstruct on failure, + * and truncate from the beginning if we can't. + */ + void + smb_node_getpath_nofail(smb_node_t *node, vnode_t *rootvp, char *buf, + uint32_t buflen) + { + int rc, len, addlen; + vnode_t *vp; + smb_node_t *unode, *dnode; + cred_t *kcr = zone_kcred(); + boolean_t is_dir, is_stream; + + is_stream = (SMB_IS_STREAM(node) != NULL); + unode = (is_stream) ? node->n_unode : node; + is_dir = smb_node_is_dir(unode); + dnode = (is_dir) ? unode : unode->n_dnode; + + /* find path to directory node */ + vp = dnode->vp; + VN_HOLD(vp); + if (rootvp) { + VN_HOLD(rootvp); + rc = vnodetopath(rootvp, vp, buf, buflen, kcr); + VN_RELE(rootvp); + } else { + rc = vnodetopath(NULL, vp, buf, buflen, kcr); + } + VN_RELE(vp); + + /* On failure, reconstruct the path from the node_t's */ + if (rc != 0) { + smb_node_t *nodep = unode; + char *p = buf + buflen; + + /* append named stream name if necessary */ + if (is_stream) { + len = strlen(node->od_name) + 1; + ASSERT3U(buflen, >=, len); + p -= len; + (void) strcpy(p, node->od_name); + } + + len = strlen(nodep->od_name) + 1; + p -= len; + while (nodep->n_dnode != NULL && nodep->vp != rootvp && + p >= buf) { + (void) strcpy(p, nodep->od_name); + p[len - 1] = '/'; + nodep = nodep->n_dnode; + len = strlen(nodep->od_name) + 1; + p -= len; + } + if (nodep->n_dnode != NULL && nodep->vp != rootvp) { + /* something went horribly wrong... */ + #ifdef DEBUG + cmn_err(CE_WARN, + "smb_node_getpath_nofail: buffer too small: " + "size %d", buflen); + #else + cmn_err(CE_WARN, + "smb_node_getpath_nofail: couldn't get full path"); + #endif + p = buf; + *p = '*'; + } else { + p += len - 1; + if (p >= buf) + *p = '/'; + } + + buf[buflen - 1] = '\0'; + (void) memmove(buf, p, strlen(p) + 1); + cmn_err(CE_NOTE, + "smb_node_getpath_nofail: vnodetopath failed, rc=%d", rc); + return; + } + + len = strlen(buf) + 1; + + /* append filename if necessary */ + if (!is_dir) { + if (buf[len - 2] != '/' && strlcat(buf, "/", buflen) >= buflen) + goto trunc; + if (strlcat(buf, unode->od_name, buflen) >= buflen) + goto trunc; + } + + /* append named stream name if necessary */ + if (!is_stream || strlcat(buf, node->od_name, buflen) < buflen) + return; + + trunc: + buf[len - 1] = '\0'; + addlen = 0; + /* append filename if necessary */ + if (!is_dir) { + if (buf[len - 2] != '/') + addlen++; + addlen += strlen(unode->od_name); + } + + /* append named stream name if necessary */ + if (is_stream) + addlen += strlen(node->od_name); + + if ((buflen - len) < addlen) { + #ifdef DEBUG + cmn_err(CE_WARN, + "smb_node_getpath_nofail: vnodetopath succeeded, " + "but buffer too small for filename"); + #else + cmn_err(CE_WARN, + "smb_node_getpath_nofail: couldn't get full path"); + #endif + addlen = addlen - (buflen - len); + (void) memmove(buf, buf + addlen, len - addlen); + buf[0] = '*'; + } + + /* append filename if necessary */ + if (!is_dir) { + if (buf[len - 2] != '/') + (void) strlcat(buf, "/", buflen); + (void) strlcat(buf, unode->od_name, buflen); + } + + /* append named stream name if necessary */ + if (is_stream) + (void) strlcat(buf, node->od_name, buflen); + } + + /* * smb_node_alloc */ static smb_node_t * smb_node_alloc( char *od_name,
*** 1154,1174 **** VN_HOLD(vp); node->vp = vp; node->n_refcnt = 1; node->n_hash_bucket = bucket; node->n_hashkey = hashkey; - node->n_pending_dosattr = 0; node->n_open_count = 0; node->n_allocsz = 0; node->n_dnode = NULL; node->n_unode = NULL; node->delete_on_close_cred = NULL; node->n_delete_on_close_flags = 0; node->n_oplock.ol_fem = B_FALSE; - node->n_oplock.ol_xthread = NULL; - node->n_oplock.ol_count = 0; - node->n_oplock.ol_break = SMB_OPLOCK_NO_BREAK; (void) strlcpy(node->od_name, od_name, sizeof (node->od_name)); if (strcmp(od_name, XATTR_DIR) == 0) node->flags |= NODE_XATTR_DIR; --- 1317,1333 ----
*** 1193,1205 **** SMB_NODE_VALID(node); node->n_magic = 0; VERIFY(!list_link_active(&node->n_lnd)); VERIFY(node->n_lock_list.ll_count == 0); VERIFY(node->n_ofile_list.ll_count == 0); - VERIFY(node->n_oplock.ol_count == 0); - VERIFY(node->n_oplock.ol_xthread == NULL); VERIFY(node->n_oplock.ol_fem == B_FALSE); VERIFY(MUTEX_NOT_HELD(&node->n_mutex)); VERIFY(!RW_LOCK_HELD(&node->n_lock)); VN_RELE(node->vp); kmem_cache_free(smb_node_cache, node); --- 1352,1363 ---- SMB_NODE_VALID(node); node->n_magic = 0; VERIFY(!list_link_active(&node->n_lnd)); VERIFY(node->n_lock_list.ll_count == 0); + VERIFY(node->n_wlock_list.ll_count == 0); VERIFY(node->n_ofile_list.ll_count == 0); VERIFY(node->n_oplock.ol_fem == B_FALSE); VERIFY(MUTEX_NOT_HELD(&node->n_mutex)); VERIFY(!RW_LOCK_HELD(&node->n_lock)); VN_RELE(node->vp); kmem_cache_free(smb_node_cache, node);
*** 1216,1235 **** smb_node_t *node = (smb_node_t *)buf; bzero(node, sizeof (smb_node_t)); smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), ! offsetof(smb_ofile_t, f_nnd)); smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), offsetof(smb_lock_t, l_lnd)); ! mutex_init(&node->n_fcn.fcn_mutex, NULL, MUTEX_DEFAULT, NULL); ! list_create(&node->n_fcn.fcn_watchers, sizeof (smb_request_t), ! offsetof(smb_request_t, sr_ncr.nc_lnd)); ! cv_init(&node->n_oplock.ol_cv, NULL, CV_DEFAULT, NULL); mutex_init(&node->n_oplock.ol_mutex, NULL, MUTEX_DEFAULT, NULL); ! list_create(&node->n_oplock.ol_grants, sizeof (smb_oplock_grant_t), ! offsetof(smb_oplock_grant_t, og_lnd)); rw_init(&node->n_lock, NULL, RW_DEFAULT, NULL); mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL); smb_node_create_audit_buf(node, kmflags); return (0); } --- 1374,1390 ---- smb_node_t *node = (smb_node_t *)buf; bzero(node, sizeof (smb_node_t)); smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t), ! offsetof(smb_ofile_t, f_node_lnd)); smb_llist_constructor(&node->n_lock_list, sizeof (smb_lock_t), offsetof(smb_lock_t, l_lnd)); ! smb_llist_constructor(&node->n_wlock_list, sizeof (smb_lock_t), ! offsetof(smb_lock_t, l_lnd)); mutex_init(&node->n_oplock.ol_mutex, NULL, MUTEX_DEFAULT, NULL); ! cv_init(&node->n_oplock.WaitingOpenCV, NULL, CV_DEFAULT, NULL); rw_init(&node->n_lock, NULL, RW_DEFAULT, NULL); mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL); smb_node_create_audit_buf(node, kmflags); return (0); }
*** 1245,1261 **** smb_node_t *node = (smb_node_t *)buf; smb_node_destroy_audit_buf(node); mutex_destroy(&node->n_mutex); rw_destroy(&node->n_lock); ! cv_destroy(&node->n_oplock.ol_cv); mutex_destroy(&node->n_oplock.ol_mutex); - list_destroy(&node->n_fcn.fcn_watchers); - mutex_destroy(&node->n_fcn.fcn_mutex); smb_llist_destructor(&node->n_lock_list); smb_llist_destructor(&node->n_ofile_list); - list_destroy(&node->n_oplock.ol_grants); } /* * smb_node_create_audit_buf */ --- 1400,1414 ---- smb_node_t *node = (smb_node_t *)buf; smb_node_destroy_audit_buf(node); mutex_destroy(&node->n_mutex); rw_destroy(&node->n_lock); ! cv_destroy(&node->n_oplock.WaitingOpenCV); mutex_destroy(&node->n_oplock.ol_mutex); smb_llist_destructor(&node->n_lock_list); + smb_llist_destructor(&node->n_wlock_list); smb_llist_destructor(&node->n_ofile_list); } /* * smb_node_create_audit_buf */
*** 1377,1401 **** /* * smb_node_file_is_readonly * * Checks if the file (which node represents) is marked readonly ! * in the filesystem. No account is taken of any pending readonly ! * in the node, which must be handled by the callers. ! * (See SMB_OFILE_IS_READONLY and SMB_PATHFILE_IS_READONLY) */ boolean_t smb_node_file_is_readonly(smb_node_t *node) { smb_attr_t attr; if (node == NULL) return (B_FALSE); /* pipes */ - if (node->n_pending_dosattr & FILE_ATTRIBUTE_READONLY) - return (B_TRUE); - bzero(&attr, sizeof (smb_attr_t)); attr.sa_mask = SMB_AT_DOSATTR; (void) smb_fsop_getattr(NULL, zone_kcred(), node, &attr); return ((attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) != 0); } --- 1530,1551 ---- /* * smb_node_file_is_readonly * * Checks if the file (which node represents) is marked readonly ! * in the filesystem. Note that there may be handles open with ! * modify rights, and those continue to allow access even after ! * the DOS read-only flag has been set in the file system. */ boolean_t smb_node_file_is_readonly(smb_node_t *node) { smb_attr_t attr; if (node == NULL) return (B_FALSE); /* pipes */ bzero(&attr, sizeof (smb_attr_t)); attr.sa_mask = SMB_AT_DOSATTR; (void) smb_fsop_getattr(NULL, zone_kcred(), node, &attr); return ((attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) != 0); }
*** 1457,1466 **** --- 1607,1617 ---- cred_t *cr, smb_ofile_t *of, smb_attr_t *attr) { int rc; uint_t times_mask; smb_attr_t tmp_attr; + smb_node_t *unnamed_node; SMB_NODE_VALID(node); /* set attributes specified in attr */ if (attr->sa_mask == 0)
*** 1557,1614 **** if (times_mask & SMB_AT_CRTIME) pa->sa_crtime = attr->sa_crtime; mutex_exit(&of->f_mutex); /* * The f_pending_attr times are reapplied in * smb_ofile_close(). */ - } /* ! * After this point, tmp_attr is what we will actually ! * store in the file system _now_, which may differ ! * from the callers attr and f_pending_attr w.r.t. ! * the DOS readonly flag etc. */ ! bcopy(attr, &tmp_attr, sizeof (tmp_attr)); ! if (attr->sa_mask & (SMB_AT_DOSATTR | SMB_AT_ALLOCSZ)) { ! mutex_enter(&node->n_mutex); ! if ((attr->sa_mask & SMB_AT_DOSATTR) != 0) { ! tmp_attr.sa_dosattr &= smb_vop_dosattr_settable; ! if (((tmp_attr.sa_dosattr & ! FILE_ATTRIBUTE_READONLY) != 0) && ! (node->n_open_count != 0)) { ! /* Delay setting readonly */ ! node->n_pending_dosattr = ! tmp_attr.sa_dosattr; ! tmp_attr.sa_dosattr &= ! ~FILE_ATTRIBUTE_READONLY; ! } else { ! node->n_pending_dosattr = 0; } } /* * Simulate n_allocsz persistence only while * there are opens. See smb_node_getattr */ ! if ((attr->sa_mask & SMB_AT_ALLOCSZ) != 0 && ! node->n_open_count != 0) node->n_allocsz = attr->sa_allocsz; mutex_exit(&node->n_mutex); } ! rc = smb_fsop_setattr(sr, cr, node, &tmp_attr); if (rc != 0) return (rc); if (node->n_dnode != NULL) { smb_node_notify_change(node->n_dnode, FILE_ACTION_MODIFIED, node->od_name); } return (0); } /* * smb_node_getattr --- 1708,1760 ---- if (times_mask & SMB_AT_CRTIME) pa->sa_crtime = attr->sa_crtime; mutex_exit(&of->f_mutex); + /* * The f_pending_attr times are reapplied in * smb_ofile_close(). */ /* ! * If this change is comming directly from a client ! * (sr != NULL) and it's a persistent handle, save ! * the "sticky times" in the handle. */ ! if (sr != NULL && of->dh_persist) { ! smb2_dh_update_times(sr, of, attr); } } + + if ((attr->sa_mask & SMB_AT_ALLOCSZ) != 0) { + mutex_enter(&node->n_mutex); /* * Simulate n_allocsz persistence only while * there are opens. See smb_node_getattr */ ! if (node->n_open_count != 0) node->n_allocsz = attr->sa_allocsz; mutex_exit(&node->n_mutex); } ! rc = smb_fsop_setattr(sr, cr, node, attr); if (rc != 0) return (rc); if (node->n_dnode != NULL) { smb_node_notify_change(node->n_dnode, FILE_ACTION_MODIFIED, node->od_name); } + if ((unnamed_node = SMB_IS_STREAM(node)) != NULL) { + ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC); + ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING); + smb_node_notify_change(node->n_dnode, + FILE_ACTION_MODIFIED_STREAM, node->od_name); + } + return (0); } /* * smb_node_getattr
*** 1645,1669 **** isdir = smb_node_is_dir(node); mutex_enter(&node->n_mutex); - /* - * When there are open handles, and one of them has - * set the DOS readonly flag (in n_pending_dosattr), - * it will not have been stored in the file system. - * In this case use n_pending_dosattr. Note that - * n_pending_dosattr has only the settable bits, - * (setattr masks it with smb_vop_dosattr_settable) - * so we need to keep any non-settable bits we got - * from the file-system above. - */ if (attr->sa_mask & SMB_AT_DOSATTR) { - if (node->n_pending_dosattr) { - attr->sa_dosattr &= ~smb_vop_dosattr_settable; - attr->sa_dosattr |= node->n_pending_dosattr; - } if (attr->sa_dosattr == 0) { attr->sa_dosattr = (isdir) ? FILE_ATTRIBUTE_DIRECTORY: FILE_ATTRIBUTE_NORMAL; } --- 1791,1801 ----