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,28 **** * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> #include <sys/pathname.h> --- 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. */ #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> #include <sys/pathname.h>
*** 149,176 **** smb_node_t *cur_node, smb_node_t **dir_node, char *last_component) { smb_node_t *root_node; ! pathname_t ppn; 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 *local_cur_node; smb_node_t *local_root_node; ASSERT(dir_node); ASSERT(last_component); *dir_node = NULL; *last_component = '\0'; ! vss_cur_node = NULL; ! vss_root_node = NULL; if (sr && sr->tid_tree) { if (STYPE_ISIPC(sr->tid_tree->t_res_type)) return (EACCES); } --- 149,178 ---- smb_node_t *cur_node, smb_node_t **dir_node, char *last_component) { smb_node_t *root_node; ! pathname_t ppn, mnt_pn; char *usepath; int lookup_flags = FOLLOW; int trailing_slash = 0; int err = 0; int len; ! 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_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,250 **** len = strlen(usepath); } } if (sr != NULL) { ! boolean_t chk_vss; ! if (sr->session->dialect >= SMB_VERS_2_BASE) chk_vss = sr->arg.open.create_timewarp; ! 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); if (err != 0) { kmem_free(usepath, SMB_MAXPATHLEN); return (err); } - len = strlen(usepath); - local_cur_node = vss_cur_node; - local_root_node = vss_root_node; } } if (usepath[len - 1] == '/') trailing_slash = 1; (void) strcanon(usepath, "/"); --- 224,255 ---- len = strlen(usepath); } } if (sr != NULL) { ! if (sr->session->dialect >= SMB_VERS_2_BASE) { chk_vss = sr->arg.open.create_timewarp; ! } else { chk_vss = (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH) != 0; + if (chk_vss) { ! 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); } } + if (chk_vss) + (void) pn_alloc(&mnt_pn); + } if (usepath[len - 1] == '/') trailing_slash = 1; (void) strcanon(usepath, "/");
*** 252,295 **** (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); 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.) */ 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); } (void) pn_free(&ppn); kmem_free(usepath, SMB_MAXPATHLEN); /* * 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). --- 257,357 ---- (void) pn_alloc_sz(&ppn, SMB_MAXPATHLEN); if ((err = pn_set(&ppn, usepath)) != 0) { (void) pn_free(&ppn); kmem_free(usepath, SMB_MAXPATHLEN); ! 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, ! 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,330 **** *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); ! return (err); } /* * smb_pathname() --- 378,389 ---- *dir_node = NULL; } *last_component = 0; } ! if (vss_node != NULL) ! (void) smb_node_release(vss_node); return (err); } /* * smb_pathname()
*** 355,377 **** */ 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) { char *component, *real_name, *namep; pathname_t pn, rpn, upn, link_pn; ! smb_node_t *dnode, *fnode; 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]; if (path == NULL) return (EINVAL); ASSERT(root_node); --- 414,437 ---- */ 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, pathname_t *mnt_pn) { char *component, *real_name, *namep; pathname_t pn, rpn, upn, link_pn; ! 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,406 **** --- 448,476 ---- 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,430 **** --- 491,504 ---- } 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,546 **** while (upn.pn_path[0] == '/') { upn.pn_path++; upn.pn_pathlen--; } } if ((pathleft) && (err == ENOENT)) err = ENOTDIR; ! if (err) { if (fnode) smb_node_release(fnode); if (dnode) smb_node_release(dnode); } else { *ret_node = fnode; if (dir_node) *dir_node = dnode; else smb_node_release(dnode); --- 599,654 ---- 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 (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,918 **** smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (B_FALSE); } ! /* If fname is "." -> INVALID_OBJECT_NAME */ if (pn->pn_fname && (strcmp(pn->pn_fname, ".") == 0)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ! ERRDOS, ERROR_PATH_NOT_FOUND); return (B_FALSE); } return (B_TRUE); } --- 1013,1026 ---- smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (B_FALSE); } ! /* 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_INVALID_NAME); return (B_FALSE); } return (B_TRUE); }
*** 982,1017 **** /* * 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. * * 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 */ void smb_stream_parse_name(char *path, char *filename, char *stream) { char *fname, *sname, *stype; ASSERT(path); ASSERT(filename); ASSERT(stream); fname = strrchr(path, '\\'); fname = (fname == NULL) ? path : fname + 1; ! (void) strlcpy(filename, fname, MAXNAMELEN); ! sname = strchr(filename, ':'); ! (void) strlcpy(stream, sname, MAXNAMELEN); ! *sname = '\0'; stype = strchr(stream + 1, ':'); if (stype == NULL) (void) strlcat(stream, ":$DATA", MAXNAMELEN); else (void) smb_strupr(stype); --- 1090,1141 ---- /* * 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, 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 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; ! 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); ! 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,1057 **** --- 1172,1202 ---- 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,1070 **** --- 1206,1216 ---- */ boolean_t smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn) { static char *strmtype[] = { + "$CA", "$DATA", "$INDEX_ALLOCATION" }; int i;