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,11 +18,11 @@
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
* SMB Node State Machine
* ----------------------
*
@@ -86,11 +86,11 @@
* 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/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,11 +97,10 @@
#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 *);
@@ -188,10 +187,11 @@
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,13 +203,18 @@
* 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);
+ 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,11 +485,13 @@
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,20 +512,23 @@
}
smb_node_audit(node);
mutex_exit(&node->n_mutex);
}
-static void
+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) {
+
+ 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,11 +535,12 @@
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,56 +597,63 @@
/*
* 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);
+ 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.
- * We distinguish between readdir failure and non-empty dir by returning
- * different values.
+ * 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, uint32_t flags)
+smb_rmdir_possible(smb_node_t *n)
{
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);
+ char *buf;
+ char *bufptr;
+ struct dirent64 *dp;
+ uint32_t status = NT_STATUS_SUCCESS;
+ int bsize = SMB_ODIR_BUFSIZE;
+ int eof = 0;
- 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
+ buf = kmem_alloc(SMB_ODIR_BUFSIZE, KM_SLEEP);
- 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);
+ /* 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 ((bufptr += reclen) < buf + bsize) {
- if (edp) {
- reclen = extdp->ed_reclen;
- name = extdp->ed_name;
- } else {
- reclen = dp->d_reclen;
- name = dp->d_name;
+ 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(name, ".") != 0 && strcmp(name, "..") != 0)
- return (NT_STATUS_DIRECTORY_NOT_EMPTY);
+
+ if (strcmp(dp->d_name, ".") != 0 &&
+ strcmp(dp->d_name, "..") != 0) {
+ status = NT_STATUS_DIRECTORY_NOT_EMPTY;
+ break;
}
- return (0);
+ }
+
+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,44 +663,35 @@
*
* 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)
{
- 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);
+ 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_CANNOT_DELETE);
+ return (NT_STATUS_SUCCESS);
}
crhold(cr);
node->delete_on_close_cred = cr;
node->n_delete_on_close_flags = flags;
@@ -692,11 +701,11 @@
/*
* 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);
+ smb_node_notify_change(node, FILE_ACTION_DELETE_PENDING, NULL);
return (NT_STATUS_SUCCESS);
}
void
@@ -740,10 +749,14 @@
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,10 +785,11 @@
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,10 +823,11 @@
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,77 +860,90 @@
/*
* SMB Change Notification
*/
void
-smb_node_fcn_subscribe(smb_node_t *node, smb_request_t *sr)
+smb_node_fcn_subscribe(smb_node_t *node)
{
- smb_node_fcn_t *fcn = &node->n_fcn;
- mutex_enter(&fcn->fcn_mutex);
- if (fcn->fcn_count == 0)
+ mutex_enter(&node->n_mutex);
+ if (node->n_fcn_count == 0)
(void) smb_fem_fcn_install(node);
- fcn->fcn_count++;
- list_insert_tail(&fcn->fcn_watchers, sr);
- mutex_exit(&fcn->fcn_mutex);
+ node->n_fcn_count++;
+ mutex_exit(&node->n_mutex);
}
void
-smb_node_fcn_unsubscribe(smb_node_t *node, smb_request_t *sr)
+smb_node_fcn_unsubscribe(smb_node_t *node)
{
- 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)
+ mutex_enter(&node->n_mutex);
+ node->n_fcn_count--;
+ if (node->n_fcn_count == 0)
smb_fem_fcn_uninstall(node);
- mutex_exit(&fcn->fcn_mutex);
+ 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_notify_event(node, action, name);
-
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ of = smb_llist_head(&node->n_ofile_list);
+ while (of) {
/*
- * These two events come as a pair:
- * FILE_ACTION_RENAMED_OLD_NAME
- * FILE_ACTION_RENAMED_NEW_NAME
- * Only do the parent notify for "new".
+ * 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 (action == FILE_ACTION_RENAMED_OLD_NAME)
- return;
+ 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);
- 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.
+ /*
+ * After changes that add or remove a name,
+ * we know the directory attributes changed,
+ * and we can tell the immediate parent.
*/
-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;
+ 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,10 +1159,145 @@
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,21 +1317,17 @@
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;
@@ -1193,13 +1352,12 @@
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_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);
@@ -1216,20 +1374,17 @@
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));
+ offsetof(smb_ofile_t, f_node_lnd));
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);
+ 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);
- list_create(&node->n_oplock.ol_grants, sizeof (smb_oplock_grant_t),
- offsetof(smb_oplock_grant_t, og_lnd));
+ 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,17 +1400,15 @@
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);
+ cv_destroy(&node->n_oplock.WaitingOpenCV);
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_wlock_list);
smb_llist_destructor(&node->n_ofile_list);
- list_destroy(&node->n_oplock.ol_grants);
}
/*
* smb_node_create_audit_buf
*/
@@ -1377,25 +1530,22 @@
/*
* 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)
+ * 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 */
- 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);
}
@@ -1457,10 +1607,11 @@
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,58 +1708,53 @@
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.
+ * If this change is comming directly from a client
+ * (sr != NULL) and it's a persistent handle, save
+ * the "sticky times" in the handle.
*/
- 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;
+ 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 ((attr->sa_mask & SMB_AT_ALLOCSZ) != 0 &&
- node->n_open_count != 0)
+ if (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);
+ 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,25 +1791,11 @@
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;
}