diff options
author | Matt Barden <matt.barden@nexenta.com> | 2018-02-14 15:55:32 -0500 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2019-08-22 17:44:32 -0400 |
commit | 1bc6aeee80885d7c0e78d4eddf68dfdcb8520c7e (patch) | |
tree | 57aa5fc4f17d4f2e7f37129d793eb2011965981b /usr/src | |
parent | 58f3189518d9e749f916c2666f0d2914e1fac538 (diff) | |
download | illumos-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.c | 2 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_node.c | 4 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_pathname.c | 183 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_vss.c | 95 | ||||
-rw-r--r-- | usr/src/uts/common/smbsrv/smb_kproto.h | 8 |
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) |