summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorMatt Barden <matt.barden@nexenta.com>2018-02-14 15:55:32 -0500
committerGordon Ross <gwr@nexenta.com>2019-08-22 17:44:32 -0400
commit1bc6aeee80885d7c0e78d4eddf68dfdcb8520c7e (patch)
tree57aa5fc4f17d4f2e7f37129d793eb2011965981b /usr/src
parent58f3189518d9e749f916c2666f0d2914e1fac538 (diff)
downloadillumos-joyent-1bc6aeee80885d7c0e78d4eddf68dfdcb8520c7e.tar.gz
11034 Restoring previous versions from snapshots doesn't work with nested datasets
Reviewed by: Gordon Ross <gordon.ross@nexenta.com> Reviewed by: Evan Layton <evan.layton@nexenta.com> Reviewed by: Garrett D'Amore <garrett@damore.org> Approved by: Garrett D'Amore <garrett@damore.org>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_fsops.c2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_node.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_pathname.c183
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_vss.c95
-rw-r--r--usr/src/uts/common/smbsrv/smb_kproto.h8
5 files changed, 190 insertions, 102 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
index 1b7c3a9fa9..d5cbea9400 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
@@ -1980,7 +1980,7 @@ smb_fsop_lookup(
if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK) &&
((attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)) {
rc = smb_pathname(sr, od_name, FOLLOW, root_node, dnode,
- &lnk_dnode, &lnk_target_node, cr);
+ &lnk_dnode, &lnk_target_node, cr, NULL);
if (rc != 0) {
/*
diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c
index 3e9933d51a..4a932b2f0b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
* SMB Node State Machine
@@ -599,7 +599,7 @@ smb_node_root_init(smb_server_t *sv, smb_node_t **svrootp)
* 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);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_pathname.c b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
index fbf003c7c0..b1c096cde5 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_pathname.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -151,24 +151,26 @@ smb_pathname_reduce(
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))
@@ -224,24 +226,27 @@ smb_pathname_reduce(
}
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);
- 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 (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] == '/')
@@ -254,10 +259,10 @@ smb_pathname_reduce(
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);
}
@@ -266,14 +271,21 @@ smb_pathname_reduce(
* 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 (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 (!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')) {
@@ -281,13 +293,63 @@ smb_pathname_reduce(
*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.
*
@@ -318,11 +380,8 @@ smb_pathname_reduce(
*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);
}
@@ -357,11 +416,11 @@ smb_pathname_reduce(
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;
@@ -370,6 +429,7 @@ smb_pathname(smb_request_t *sr, char *path, int flags,
int local_flags;
uint32_t abe_flag = 0;
char namebuf[MAXNAMELEN];
+ vnode_t *fsrootvp = NULL;
if (path == NULL)
return (EINVAL);
@@ -390,6 +450,11 @@ smb_pathname(smb_request_t *sr, char *path, int flags,
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;
@@ -399,6 +464,11 @@ smb_pathname(smb_request_t *sr, char *path, int flags,
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);
@@ -531,18 +601,53 @@ smb_pathname(smb_request_t *sr, char *path, int flags,
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 &&
+ fnode != 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 {
- *ret_node = fnode;
+ 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;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_vss.c b/usr/src/uts/common/fs/smbsrv/smb_vss.c
index 72657ae3be..d0339e7352 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_vss.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_vss.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2009, 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.
*/
/*
@@ -78,18 +78,22 @@ smb_vss_enum_snapshots(smb_request_t *sr, smb_fsctl_t *fsctl)
uint32_t count = 0;
char *root_path;
uint32_t status = NT_STATUS_SUCCESS;
- smb_node_t *tnode;
smb_gmttoken_response_t snaps;
- ASSERT(sr->tid_tree);
- ASSERT(sr->tid_tree->t_snode);
+ ASSERT(sr->fid_ofile);
+ ASSERT(sr->fid_ofile->f_node);
if (fsctl->MaxOutputResp < SMB_VSS_COUNT_SIZE)
return (NT_STATUS_INVALID_PARAMETER);
- tnode = sr->tid_tree->t_snode;
+ /*
+ * smbd will find the root of the lowest filesystem from mntpath of a
+ * file by comparing it agaisnt mnttab, repeatedly removing components
+ * until one matches.
+ */
root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
- if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0)
+ if (smb_node_getmntpath(sr->fid_ofile->f_node, root_path,
+ MAXPATHLEN) != 0)
return (NT_STATUS_INVALID_PARAMETER);
if (fsctl->MaxOutputResp == SMB_VSS_COUNT_SIZE) {
@@ -118,71 +122,43 @@ smb_vss_enum_snapshots(smb_request_t *sr, smb_fsctl_t *fsctl)
* sr - the request info, used to find root of dataset,
* unicode or ascii, where the share is rooted in the
* dataset
- * root_node - root of the share
* cur_node - where in the share for the command
- * buf - is the path for the command to be processed
- * returned without @GMT if processed
* vss_cur_node - returned value for the snapshot version
* of the cur_node
- * vss_root_node - returned value for the snapshot version
- * of the root_node
+ * gmttoken - if SMB1, the gmttoken to be used to find the snapshot.
+ * Otherwise, NULL.
*
* This routine is the processing for handling the
* SMB_FLAGS2_REPARSE_PATH bit being set in the smb header.
*
* By using the cur_node passed in, a new node is found or
* created that is the same place in the directory tree, but
- * in the snapshot. We also use root_node to do the same for
- * the root.
- * Once the new smb node is found, the path is modified by
- * removing the @GMT token from the path in the buf.
+ * in the snapshot.
*/
int
-smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
- smb_node_t *cur_node, char *buf, smb_node_t **vss_cur_node,
- smb_node_t **vss_root_node)
+smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *cur_node,
+ smb_node_t **vss_cur_node, char *gmttoken)
{
smb_arg_open_t *op = &sr->arg.open;
- smb_node_t *tnode;
char *snapname, *path;
- char *gmttoken;
- char gmttok_buf[SMB_VSS_GMT_SIZE];
vnode_t *fsrootvp = NULL;
time_t toktime;
int err = 0;
- boolean_t smb1;
if (sr->tid_tree == NULL)
return (ESTALE);
- tnode = sr->tid_tree->t_snode;
-
- ASSERT(tnode);
- ASSERT(tnode->vp);
- ASSERT(tnode->vp->v_vfsp);
-
- smb1 = (sr->session->dialect < SMB_VERS_2_BASE);
- if (smb1) {
- const char *p;
-
- /* get gmttoken from buf */
- if ((p = smb_vss_find_gmttoken(buf)) == NULL)
- return (ENOENT);
-
- bcopy(p, gmttok_buf, SMB_VSS_GMT_SIZE);
- gmttok_buf[SMB_VSS_GMT_SIZE - 1] = '\0';
- gmttoken = gmttok_buf;
+ if (gmttoken != NULL) {
toktime = 0;
} else {
/* SMB2 and later */
- gmttoken = NULL;
toktime = op->timewarp.tv_sec;
}
path = smb_srm_alloc(sr, MAXPATHLEN);
snapname = smb_srm_alloc(sr, MAXPATHLEN);
- err = smb_node_getmntpath(tnode, path, MAXPATHLEN);
+ err = smb_node_getmntpath(cur_node, path, MAXPATHLEN);
if (err != 0)
return (err);
@@ -197,26 +173,16 @@ smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
return (ENOENT);
/* find snapshot nodes */
- err = VFS_ROOT(tnode->vp->v_vfsp, &fsrootvp);
+ err = VFS_ROOT(cur_node->vp->v_vfsp, &fsrootvp);
if (err != 0)
return (err);
- /* find snapshot node corresponding to root_node */
- err = smb_vss_lookup_node(sr, root_node, fsrootvp,
- snapname, cur_node, vss_root_node);
- if (err == 0) {
- /* find snapshot node corresponding to cur_node */
- err = smb_vss_lookup_node(sr, cur_node, fsrootvp,
- snapname, cur_node, vss_cur_node);
- if (err != 0)
- smb_node_release(*vss_root_node);
- }
+ /* find snapshot node corresponding to cur_node */
+ err = smb_vss_lookup_node(sr, cur_node, fsrootvp,
+ snapname, cur_node, vss_cur_node);
VN_RELE(fsrootvp);
- if (smb1)
- smb_vss_remove_first_token_from_path(buf);
-
return (err);
}
@@ -426,7 +392,7 @@ smb_vss_get_snapshots_free(smb_gmttoken_response_t *reply)
*/
static void
smb_vss_map_gmttoken(smb_tree_t *tree, char *path, char *gmttoken,
- time_t toktime, char *snapname)
+ time_t toktime, char *snapname)
{
smb_gmttoken_snapname_t request;
smb_string_t result;
@@ -442,3 +408,20 @@ smb_vss_map_gmttoken(smb_tree_t *tree, char *path, char *gmttoken,
&request, smb_gmttoken_snapname_xdr,
&result, smb_string_xdr);
}
+
+int
+smb_vss_extract_gmttoken(char *buf, char *gmttoken)
+{
+ const char *p;
+
+ /* get gmttoken from buf */
+ if ((p = smb_vss_find_gmttoken(buf)) == NULL)
+ return (ENOENT);
+
+ bcopy(p, gmttoken, SMB_VSS_GMT_SIZE);
+ gmttoken[SMB_VSS_GMT_SIZE - 1] = '\0';
+
+ smb_vss_remove_first_token_from_path(buf);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h
index 751f047e0c..aeb780dc35 100644
--- a/usr/src/uts/common/smbsrv/smb_kproto.h
+++ b/usr/src/uts/common/smbsrv/smb_kproto.h
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2016 Syneto S.R.L. All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -552,7 +552,7 @@ int smb_pathname_reduce(smb_request_t *, cred_t *,
const char *, smb_node_t *, smb_node_t *, smb_node_t **, char *);
int smb_pathname(smb_request_t *, char *, int, smb_node_t *,
- smb_node_t *, smb_node_t **, smb_node_t **, cred_t *);
+ smb_node_t *, smb_node_t **, smb_node_t **, cred_t *, pathname_t *);
/*
* smb_notify.c
@@ -861,9 +861,9 @@ boolean_t smb_ace_is_access(int);
boolean_t smb_ace_is_audit(int);
uint32_t smb_vss_enum_snapshots(smb_request_t *, smb_fsctl_t *);
-int smb_vss_lookup_nodes(smb_request_t *, smb_node_t *, smb_node_t *,
- char *, smb_node_t **, smb_node_t **);
+int smb_vss_lookup_nodes(smb_request_t *, smb_node_t *, smb_node_t **, char *);
vnode_t *smb_lookuppathvptovp(smb_request_t *, char *, vnode_t *, vnode_t *);
+int smb_vss_extract_gmttoken(char *, char *);
void smb_panic(char *, const char *, int);
#pragma does_not_return(smb_panic)