Print this page
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-9808 SMB3 persistent handles
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-4538 SMB1 create file should support extended_response format (2)
NEX-6116 Failures in smbtorture raw.open
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Include this commit if upstreaming/backporting any of:
NEX-4540 SMB server declines EA support incorrectly
NEX-4239 smbtorture create failures re. allocation size
(illumos) 6398 SMB should support path names longer than 1024
NEX-5082 panic getting Mac attributes for a 255 character file name
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Conflicts:
usr/src/uts/common/fs/smbsrv/smb_pathname.c
NEX-3611 CLONE NEX-3550 Replace smb2_enable with max_protocol
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
SMB-115 Support SMB path names with length > 1024
SMB-100 Internal error if filename is too long
Approved by: Gordon Ross <gwr@nexenta.com>
SMB-136 Snapshots not visible in Windows previous versions
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-65 SMB server in non-global zones (use zone_kcred())
re #13470 rb4432 Sync some SMB differences from illumos
re #6854 FindFirstFile,FindFirstFileEx,... are not working correctly on Nexenta CIFS-shares
re #6813 rb1757 port 2976 Child folder visibility through shares
@@ -18,11 +18,11 @@
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <sys/pathname.h>
@@ -149,28 +149,30 @@
smb_node_t *cur_node,
smb_node_t **dir_node,
char *last_component)
{
smb_node_t *root_node;
- pathname_t ppn;
+ pathname_t ppn, mnt_pn;
char *usepath;
int lookup_flags = FOLLOW;
int trailing_slash = 0;
int err = 0;
int len;
- smb_node_t *vss_cur_node;
- smb_node_t *vss_root_node;
+ smb_node_t *vss_node;
smb_node_t *local_cur_node;
smb_node_t *local_root_node;
+ boolean_t chk_vss;
+ char *gmttoken;
ASSERT(dir_node);
ASSERT(last_component);
*dir_node = NULL;
*last_component = '\0';
- vss_cur_node = NULL;
- vss_root_node = NULL;
+ vss_node = NULL;
+ gmttoken = NULL;
+ chk_vss = B_FALSE;
if (sr && sr->tid_tree) {
if (STYPE_ISIPC(sr->tid_tree->t_res_type))
return (EACCES);
}
@@ -222,29 +224,32 @@
len = strlen(usepath);
}
}
if (sr != NULL) {
- boolean_t chk_vss;
- if (sr->session->dialect >= SMB_VERS_2_BASE)
+ if (sr->session->dialect >= SMB_VERS_2_BASE) {
chk_vss = sr->arg.open.create_timewarp;
- else
+ } else {
chk_vss = (sr->smb_flg2 &
SMB_FLAGS2_REPARSE_PATH) != 0;
+
if (chk_vss) {
- err = smb_vss_lookup_nodes(sr, root_node, cur_node,
- usepath, &vss_cur_node, &vss_root_node);
+ gmttoken = kmem_alloc(SMB_VSS_GMT_SIZE,
+ KM_SLEEP);
+ err = smb_vss_extract_gmttoken(usepath,
+ gmttoken);
if (err != 0) {
kmem_free(usepath, SMB_MAXPATHLEN);
+ kmem_free(gmttoken, SMB_VSS_GMT_SIZE);
return (err);
}
-
len = strlen(usepath);
- local_cur_node = vss_cur_node;
- local_root_node = vss_root_node;
}
}
+ if (chk_vss)
+ (void) pn_alloc(&mnt_pn);
+ }
if (usepath[len - 1] == '/')
trailing_slash = 1;
(void) strcanon(usepath, "/");
@@ -252,44 +257,101 @@
(void) pn_alloc_sz(&ppn, SMB_MAXPATHLEN);
if ((err = pn_set(&ppn, usepath)) != 0) {
(void) pn_free(&ppn);
kmem_free(usepath, SMB_MAXPATHLEN);
- if (vss_cur_node != NULL)
- (void) smb_node_release(vss_cur_node);
- if (vss_root_node != NULL)
- (void) smb_node_release(vss_root_node);
+ if (chk_vss)
+ (void) pn_free(&mnt_pn);
+ if (gmttoken != NULL)
+ kmem_free(gmttoken, SMB_VSS_GMT_SIZE);
return (err);
}
/*
* If a path does not have a trailing slash, strip off the
* last component. (We only need to return an smb_node for
* the second to last component; a name is returned for the
* last component.)
+ *
+ * For VSS requests, the last component might be a filesystem of its
+ * own, and we need to discover that before exiting this function,
+ * so allow the lookup to happen on the last component.
+ * We'll correct this later when we convert to the snapshot.
*/
+ if (!chk_vss) {
if (trailing_slash) {
(void) strlcpy(last_component, ".", MAXNAMELEN);
} else {
(void) pn_setlast(&ppn);
(void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN);
ppn.pn_path[0] = '\0';
}
+ }
if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) {
smb_node_ref(local_cur_node);
*dir_node = local_cur_node;
} else {
err = smb_pathname(sr, ppn.pn_buf, lookup_flags,
- local_root_node, local_cur_node, NULL, dir_node, cred);
+ local_root_node, local_cur_node, NULL, dir_node, cred,
+ chk_vss ? &mnt_pn : NULL);
}
(void) pn_free(&ppn);
kmem_free(usepath, SMB_MAXPATHLEN);
/*
+ * We need to try and convert to snapshots, even on error.
+ * This is to handle the following cases:
+ * - We're on the lowest level filesystem, but a directory got renamed
+ * on the live version. We'll get ENOENT, but can still find it in
+ * the snapshot.
+ * - The last component was actually a file. We need to leave the last
+ * component in in case it is, itself, a mountpoint, but that means
+ * we might get ENOTDIR if it's not actually a directory.
+ *
+ * Note that if you change the share-relative name of a mountpoint,
+ * you won't be able to access previous versions of files under it.
+ */
+ if (chk_vss && *dir_node != NULL) {
+ if ((err = smb_vss_lookup_nodes(sr, *dir_node, &vss_node,
+ gmttoken)) == 0) {
+ char *p = mnt_pn.pn_path;
+ size_t pathleft;
+
+ smb_node_release(*dir_node);
+ *dir_node = NULL;
+ pathleft = pn_pathleft(&mnt_pn);
+
+ if (pathleft == 0 || trailing_slash) {
+ (void) strlcpy(last_component, ".", MAXNAMELEN);
+ } else {
+ (void) pn_setlast(&mnt_pn);
+ (void) strlcpy(last_component, mnt_pn.pn_path,
+ MAXNAMELEN);
+ mnt_pn.pn_path[0] = '\0';
+ pathleft -= strlen(last_component);
+ }
+
+ if (pathleft != 0) {
+ err = smb_pathname(sr, p, lookup_flags,
+ vss_node, vss_node, NULL, dir_node, cred,
+ NULL);
+ } else {
+ *dir_node = vss_node;
+ vss_node = NULL;
+ }
+ }
+ }
+
+ if (chk_vss)
+ (void) pn_free(&mnt_pn);
+ if (gmttoken != NULL)
+ kmem_free(gmttoken, SMB_VSS_GMT_SIZE);
+
+ /*
* Prevent traversal to another file system if mount point
* traversal is disabled.
*
* Note that we disregard whether the traversal of the path went
* outside of the file system and then came back (say via a link).
@@ -316,15 +378,12 @@
*dir_node = NULL;
}
*last_component = 0;
}
- if (vss_cur_node != NULL)
- (void) smb_node_release(vss_cur_node);
- if (vss_root_node != NULL)
- (void) smb_node_release(vss_root_node);
-
+ if (vss_node != NULL)
+ (void) smb_node_release(vss_node);
return (err);
}
/*
* smb_pathname()
@@ -355,23 +414,24 @@
*/
int
smb_pathname(smb_request_t *sr, char *path, int flags,
smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node,
- smb_node_t **ret_node, cred_t *cred)
+ smb_node_t **ret_node, cred_t *cred, pathname_t *mnt_pn)
{
char *component, *real_name, *namep;
pathname_t pn, rpn, upn, link_pn;
- smb_node_t *dnode, *fnode;
+ smb_node_t *dnode, *fnode, *mnt_node;
smb_attr_t attr;
vnode_t *rootvp, *vp;
size_t pathleft;
int err = 0;
int nlink = 0;
int local_flags;
uint32_t abe_flag = 0;
char namebuf[MAXNAMELEN];
+ vnode_t *fsrootvp = NULL;
if (path == NULL)
return (EINVAL);
ASSERT(root_node);
@@ -388,19 +448,29 @@
if ((err = pn_set(&upn, path)) != 0) {
(void) pn_free(&upn);
return (err);
}
+ if (mnt_pn != NULL && (err = pn_set(mnt_pn, path) != 0)) {
+ (void) pn_free(&upn);
+ return (err);
+ }
+
if (SMB_TREE_SUPPORTS_ABE(sr))
abe_flag = SMB_ABE;
(void) pn_alloc(&pn);
(void) pn_alloc(&rpn);
component = kmem_alloc(MAXNAMELEN, KM_SLEEP);
real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
+ if (mnt_pn != NULL) {
+ mnt_node = cur_node;
+ smb_node_ref(cur_node);
+ } else
+ mnt_node = NULL;
fnode = NULL;
dnode = cur_node;
smb_node_ref(dnode);
rootvp = root_node->vp;
@@ -421,10 +491,14 @@
}
if ((err = pn_set(&pn, namep)) != 0)
break;
+ /* We want the DOS attributes. */
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_DOSATTR;
+
local_flags = flags & FIGNORECASE;
err = smb_pathname_lookup(&pn, &rpn, local_flags,
&vp, rootvp, dnode->vp, &attr, cred);
if (err) {
@@ -525,22 +599,56 @@
while (upn.pn_path[0] == '/') {
upn.pn_path++;
upn.pn_pathlen--;
}
+ /*
+ * If the node we looked up is the root of a filesystem,
+ * snapshot the lookup so we can replay this after discovering
+ * the lowest mounted filesystem.
+ */
+ if (mnt_pn != NULL &&
+ (err = VFS_ROOT(fnode->vp->v_vfsp, &fsrootvp)) == 0) {
+ if (fsrootvp == fnode->vp) {
+ mnt_pn->pn_pathlen = pn_pathleft(&upn);
+ mnt_pn->pn_path = mnt_pn->pn_buf +
+ ((ptrdiff_t)upn.pn_path -
+ (ptrdiff_t)upn.pn_buf);
+
+ smb_node_ref(fnode);
+ if (mnt_node != NULL)
+ smb_node_release(mnt_node);
+ mnt_node = fnode;
+
}
+ VN_RELE(fsrootvp);
+ }
+ }
if ((pathleft) && (err == ENOENT))
err = ENOTDIR;
- if (err) {
+ if (mnt_node == NULL)
+ mnt_pn = NULL;
+
+ /*
+ * We always want to return a node when we're doing VSS
+ * (mnt_pn != NULL)
+ */
+ if (mnt_pn == NULL && err != 0) {
if (fnode)
smb_node_release(fnode);
if (dnode)
smb_node_release(dnode);
} else {
+ if (mnt_pn != NULL) {
+ *ret_node = mnt_node;
+ if (fnode != NULL)
+ smb_node_release(fnode);
+ } else {
*ret_node = fnode;
+ }
if (dir_node)
*dir_node = dnode;
else
smb_node_release(dnode);
@@ -905,14 +1013,14 @@
smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
ERRDOS, ERROR_INVALID_NAME);
return (B_FALSE);
}
- /* If fname is "." -> INVALID_OBJECT_NAME */
+ /* If fname is "." -> OBJECT_NAME_INVALID */
if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) {
smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
- ERRDOS, ERROR_PATH_NOT_FOUND);
+ ERRDOS, ERROR_INVALID_NAME);
return (B_FALSE);
}
return (B_TRUE);
}
@@ -982,36 +1090,52 @@
/*
* smb_stream_parse_name
*
* smb_stream_parse_name should only be called for a path that
* contains a valid named stream. Path validation should have
- * been performed before this function is called.
+ * been performed before this function is called, typically by
+ * calling smb_is_stream_name() just before this.
*
* Find the last component of path and split it into filename
* and stream name.
*
* On return the named stream type will be present. The stream
* type defaults to ":$DATA", if it has not been defined
- * For exmaple, 'stream' contains :<sname>:$DATA
+ * For example, 'stream' contains :<sname>:$DATA
+ *
+ * Output args: filename, stream both MAXNAMELEN
*/
void
smb_stream_parse_name(char *path, char *filename, char *stream)
{
char *fname, *sname, *stype;
+ size_t flen, slen;
ASSERT(path);
ASSERT(filename);
ASSERT(stream);
fname = strrchr(path, '\\');
fname = (fname == NULL) ? path : fname + 1;
- (void) strlcpy(filename, fname, MAXNAMELEN);
+ sname = strchr(fname, ':');
+ /* Caller makes sure there is a ':' in path. */
+ VERIFY(sname != NULL);
+ /* LINTED: possible ptrdiff_t overflow */
+ flen = sname - fname;
+ slen = strlen(sname);
- sname = strchr(filename, ':');
- (void) strlcpy(stream, sname, MAXNAMELEN);
- *sname = '\0';
+ if (flen > (MAXNAMELEN-1))
+ flen = (MAXNAMELEN-1);
+ (void) strncpy(filename, fname, flen);
+ filename[flen] = '\0';
+ if (slen > (MAXNAMELEN-1))
+ slen = (MAXNAMELEN-1);
+ (void) strncpy(stream, sname, slen);
+ stream[slen] = '\0';
+
+ /* Add a "stream type" if there isn't one. */
stype = strchr(stream + 1, ':');
if (stype == NULL)
(void) strlcat(stream, ":$DATA", MAXNAMELEN);
else
(void) smb_strupr(stype);
@@ -1048,10 +1172,31 @@
return (B_TRUE);
}
/*
+ * Is this stream node a "restricted" type?
+ */
+boolean_t
+smb_strname_restricted(char *strname)
+{
+ char *stype;
+
+ stype = strrchr(strname, ':');
+ if (stype == NULL)
+ return (B_FALSE);
+
+ /*
+ * Only ":$CA" is restricted (for now).
+ */
+ if (strcmp(stype, ":$CA") == 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
* smb_validate_stream_name
*
* B_FALSE will be returned, and the error status ser in the sr, if:
* - the path is not a stream name
* - a path is specified but the fname is ommitted.
@@ -1061,10 +1206,11 @@
*/
boolean_t
smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
{
static char *strmtype[] = {
+ "$CA",
"$DATA",
"$INDEX_ALLOCATION"
};
int i;