189dc44ceSjose borrego /* 289dc44ceSjose borrego * CDDL HEADER START 389dc44ceSjose borrego * 489dc44ceSjose borrego * The contents of this file are subject to the terms of the 589dc44ceSjose borrego * Common Development and Distribution License (the "License"). 689dc44ceSjose borrego * You may not use this file except in compliance with the License. 789dc44ceSjose borrego * 889dc44ceSjose borrego * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 989dc44ceSjose borrego * or http://www.opensolaris.org/os/licensing. 1089dc44ceSjose borrego * See the License for the specific language governing permissions 1189dc44ceSjose borrego * and limitations under the License. 1289dc44ceSjose borrego * 1389dc44ceSjose borrego * When distributing Covered Code, include this CDDL HEADER in each 1489dc44ceSjose borrego * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1589dc44ceSjose borrego * If applicable, add the following below this CDDL HEADER, with the 1689dc44ceSjose borrego * fields enclosed by brackets "[]" replaced with your own identifying 1789dc44ceSjose borrego * information: Portions Copyright [yyyy] [name of copyright owner] 1889dc44ceSjose borrego * 1989dc44ceSjose borrego * CDDL HEADER END 2089dc44ceSjose borrego */ 2189dc44ceSjose borrego /* 2289dc44ceSjose borrego * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2389dc44ceSjose borrego * Use is subject to license terms. 2489dc44ceSjose borrego */ 2589dc44ceSjose borrego 2689dc44ceSjose borrego /* 2789dc44ceSjose borrego * Volume Copy Shadow Services (VSS) provides a way for users to 2889dc44ceSjose borrego * restore/recover deleted files/directories. 2989dc44ceSjose borrego * For the server to support VSS for Microsoft clients, there is 3089dc44ceSjose borrego * two basic functions that need to be implemented. 3189dc44ceSjose borrego * The first is to intercept the NT_TRANSACT_IOCTL command with 3289dc44ceSjose borrego * the function code of FSCTL_SRV_ENUMERATE_SNAPSHOTS (0x00144064). 3389dc44ceSjose borrego * This is to report the count or the count and list of snapshots 3489dc44ceSjose borrego * for that share. 3589dc44ceSjose borrego * The second function need to trap commands with the 3689dc44ceSjose borrego * SMB_FLAGS2_REPARSE_PATH bit set in the smb header. This bit 3789dc44ceSjose borrego * means that there is a @GMT token in path that needs to be 3889dc44ceSjose borrego * processed. The @GMT token means to process this command, but 3989dc44ceSjose borrego * in the snapshot. 4089dc44ceSjose borrego */ 4189dc44ceSjose borrego 4289dc44ceSjose borrego #include <smbsrv/smb_incl.h> 4389dc44ceSjose borrego #include <smbsrv/winioctl.h> 4489dc44ceSjose borrego #include <smbsrv/ntstatus.h> 4589dc44ceSjose borrego #include <smbsrv/smb_door_svc.h> 4689dc44ceSjose borrego 4789dc44ceSjose borrego /* Size of the token on the wire due to encoding */ 4889dc44ceSjose borrego #define SMB_VSS_GMT_NET_SIZE(sr) (smb_ascii_or_unicode_null_len(sr) * \ 4989dc44ceSjose borrego SMB_VSS_GMT_SIZE) 5089dc44ceSjose borrego 5189dc44ceSjose borrego #define SMB_VSS_COUNT_SIZE 16 5289dc44ceSjose borrego 5389dc44ceSjose borrego static boolean_t smb_vss_is_gmttoken(const char *str); 5489dc44ceSjose borrego static const char *smb_vss_find_gmttoken(const char *path); 5589dc44ceSjose borrego static int smb_vss_get_fsmountpath(smb_request_t *sr, char *buf, 5689dc44ceSjose borrego uint32_t buflen); 5789dc44ceSjose borrego static uint32_t smb_vss_encode_gmttokens(smb_request_t *sr, smb_xa_t *xa, 5889dc44ceSjose borrego int32_t count, smb_dr_return_gmttokens_t *snap_data); 5989dc44ceSjose borrego static void smb_vss_remove_first_token_from_path(char *c); 6089dc44ceSjose borrego 6189dc44ceSjose borrego /* 6289dc44ceSjose borrego * This is to respond to the nt_transact_ioctl to either respond with the 6389dc44ceSjose borrego * number of snapshots, or to respond with the list. It needs to be sorted 6489dc44ceSjose borrego * before the reply. If the the max data bytes to return is 6589dc44ceSjose borrego * SMB_VSS_COUNT_SIZE, then all that is requested is the count, otherwise 6689dc44ceSjose borrego * return the count and the list of @GMT tokens (one token for each 6789dc44ceSjose borrego * snapshot). 6889dc44ceSjose borrego */ 6989dc44ceSjose borrego uint32_t 7089dc44ceSjose borrego smb_vss_ioctl_enumerate_snaps(smb_request_t *sr, smb_xa_t *xa) 7189dc44ceSjose borrego { 7289dc44ceSjose borrego uint32_t count = 0; 7389dc44ceSjose borrego char *root_path; 74*b1352070SAlan Wright uint32_t status = NT_STATUS_SUCCESS; 7589dc44ceSjose borrego smb_dr_return_gmttokens_t gmttokens; 7689dc44ceSjose borrego 77*b1352070SAlan Wright if (xa->smb_mdrcnt < SMB_VSS_COUNT_SIZE) 78*b1352070SAlan Wright return (NT_STATUS_INVALID_PARAMETER); 7989dc44ceSjose borrego 8089dc44ceSjose borrego root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 81*b1352070SAlan Wright if (smb_vss_get_fsmountpath(sr, root_path, MAXPATHLEN) != 0) 82*b1352070SAlan Wright return (NT_STATUS_INVALID_PARAMETER); 8389dc44ceSjose borrego 8489dc44ceSjose borrego if (xa->smb_mdrcnt == SMB_VSS_COUNT_SIZE) { 8589dc44ceSjose borrego count = smb_upcall_vss_get_count(root_path); 8689dc44ceSjose borrego if (smb_mbc_encodef(&xa->rep_data_mb, "lllw", count, 0, 8789dc44ceSjose borrego (count * SMB_VSS_GMT_NET_SIZE(sr) + 8889dc44ceSjose borrego smb_ascii_or_unicode_null_len(sr)), 0) != 0) { 89*b1352070SAlan Wright status = NT_STATUS_INVALID_PARAMETER; 9089dc44ceSjose borrego } 9189dc44ceSjose borrego } else { 9289dc44ceSjose borrego count = xa->smb_mdrcnt / SMB_VSS_GMT_NET_SIZE(sr); 9389dc44ceSjose borrego 9489dc44ceSjose borrego smb_upcall_vss_get_snapshots(root_path, count, &gmttokens); 9589dc44ceSjose borrego 96*b1352070SAlan Wright status = smb_vss_encode_gmttokens(sr, xa, count, &gmttokens); 9789dc44ceSjose borrego 9889dc44ceSjose borrego smb_upcall_vss_get_snapshots_free(&gmttokens); 9989dc44ceSjose borrego } 10089dc44ceSjose borrego 10189dc44ceSjose borrego kmem_free(root_path, MAXPATHLEN); 102*b1352070SAlan Wright return (status); 10389dc44ceSjose borrego } 10489dc44ceSjose borrego 10589dc44ceSjose borrego /* 10689dc44ceSjose borrego * sr - the request info, used to find root of dataset, 10789dc44ceSjose borrego * unicode or ascii, where the share is rooted in the 10889dc44ceSjose borrego * dataset 10989dc44ceSjose borrego * root_node - root of the share 11089dc44ceSjose borrego * cur_node - where in the share for the command 11189dc44ceSjose borrego * buf - is the path for the command to be processed 11289dc44ceSjose borrego * returned without @GMT if processed 11389dc44ceSjose borrego * vss_cur_node - returned value for the snapshot version 11489dc44ceSjose borrego * of the cur_node 11589dc44ceSjose borrego * vss_root_node - returned value for the snapshot version 11689dc44ceSjose borrego * of the root_node 11789dc44ceSjose borrego * 11889dc44ceSjose borrego * This routine is the processing for handling the 11989dc44ceSjose borrego * SMB_FLAGS2_REPARSE_PATH bit being set in the smb header. 12089dc44ceSjose borrego * 12189dc44ceSjose borrego * By using the cur_node passed in, a new node is found or 12289dc44ceSjose borrego * created that is the same place in the directory tree, but 12389dc44ceSjose borrego * in the snapshot. We also use root_node to do the same for 12489dc44ceSjose borrego * the root. 12589dc44ceSjose borrego * One the new smb node is found, the path is modified by 12689dc44ceSjose borrego * removing the @GMT token from the path in the buf. 12789dc44ceSjose borrego */ 12889dc44ceSjose borrego int 12989dc44ceSjose borrego smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node, 13089dc44ceSjose borrego smb_node_t *cur_node, char *buf, smb_node_t **vss_cur_node, 13189dc44ceSjose borrego smb_node_t **vss_root_node) 13289dc44ceSjose borrego { 13389dc44ceSjose borrego const char *p; 13489dc44ceSjose borrego char *rootpath; 13589dc44ceSjose borrego char *snapname; 13689dc44ceSjose borrego char *nodepath; 13789dc44ceSjose borrego char gmttoken[SMB_VSS_GMT_SIZE]; 13889dc44ceSjose borrego smb_attr_t attr; 13989dc44ceSjose borrego vnode_t *fsrootvp; 14089dc44ceSjose borrego vnode_t *vp = NULL; 14189dc44ceSjose borrego int err = 0; 14289dc44ceSjose borrego 14389dc44ceSjose borrego if (sr->tid_tree == NULL) 14489dc44ceSjose borrego return (ESTALE); 14589dc44ceSjose borrego 14689dc44ceSjose borrego ASSERT(sr->tid_tree->t_snode); 14789dc44ceSjose borrego ASSERT(sr->tid_tree->t_snode->vp); 14889dc44ceSjose borrego ASSERT(sr->tid_tree->t_snode->vp->v_vfsp); 14989dc44ceSjose borrego 1506e3e9d9cSafshin salek ardakani - Sun Microsystems - Irvine United States if ((p = smb_vss_find_gmttoken(buf)) == NULL) 15189dc44ceSjose borrego return (ENOENT); 15289dc44ceSjose borrego 15389dc44ceSjose borrego bcopy(p, gmttoken, SMB_VSS_GMT_SIZE); 15489dc44ceSjose borrego gmttoken[SMB_VSS_GMT_SIZE - 1] = '\0'; 15589dc44ceSjose borrego 1566e3e9d9cSafshin salek ardakani - Sun Microsystems - Irvine United States err = VFS_ROOT(sr->tid_tree->t_snode->vp->v_vfsp, &fsrootvp); 1576e3e9d9cSafshin salek ardakani - Sun Microsystems - Irvine United States if (err != 0) 1586e3e9d9cSafshin salek ardakani - Sun Microsystems - Irvine United States return (err); 15989dc44ceSjose borrego 16089dc44ceSjose borrego rootpath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 16189dc44ceSjose borrego snapname = kmem_alloc(MAXNAMELEN, KM_SLEEP); 16289dc44ceSjose borrego nodepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 16389dc44ceSjose borrego 16489dc44ceSjose borrego err = smb_vss_get_fsmountpath(sr, rootpath, MAXPATHLEN); 1656e3e9d9cSafshin salek ardakani - Sun Microsystems - Irvine United States if (err != 0) 16689dc44ceSjose borrego goto error; 16789dc44ceSjose borrego 16889dc44ceSjose borrego *snapname = '\0'; 16989dc44ceSjose borrego 17089dc44ceSjose borrego smb_upcall_vss_map_gmttoken(rootpath, gmttoken, snapname); 17189dc44ceSjose borrego 17289dc44ceSjose borrego if (!*snapname) { 17389dc44ceSjose borrego err = ENOENT; 17489dc44ceSjose borrego goto error; 17589dc44ceSjose borrego } 17689dc44ceSjose borrego 17789dc44ceSjose borrego /* note the value of root_node->vp */ 17889dc44ceSjose borrego err = vnodetopath(fsrootvp, root_node->vp, nodepath, 17989dc44ceSjose borrego MAXPATHLEN, kcred); 18089dc44ceSjose borrego 18189dc44ceSjose borrego if (err != 0) 18289dc44ceSjose borrego goto error; 18389dc44ceSjose borrego 18489dc44ceSjose borrego (void) snprintf(rootpath, MAXPATHLEN, ".zfs/snapshot/%s/%s", 18589dc44ceSjose borrego snapname, nodepath); 18689dc44ceSjose borrego 18789dc44ceSjose borrego vp = smb_lookuppathvptovp(sr, rootpath, fsrootvp, fsrootvp); 18889dc44ceSjose borrego 18989dc44ceSjose borrego if (vp) { 19089dc44ceSjose borrego /* note the value of cur_node->vp */ 19189dc44ceSjose borrego err = vnodetopath(fsrootvp, cur_node->vp, nodepath, 19289dc44ceSjose borrego MAXPATHLEN, kcred); 1937f667e74Sjose borrego if (err != 0) { 1947f667e74Sjose borrego VN_RELE(vp); 19589dc44ceSjose borrego goto error; 1967f667e74Sjose borrego } 19789dc44ceSjose borrego 19889dc44ceSjose borrego *vss_root_node = smb_node_lookup(sr, NULL, kcred, vp, 19989dc44ceSjose borrego gmttoken, cur_node, NULL, &attr); 2007f667e74Sjose borrego VN_RELE(vp); 2017f667e74Sjose borrego 2027f667e74Sjose borrego if (*vss_root_node == NULL) { 2037f667e74Sjose borrego err = ENOENT; 2047f667e74Sjose borrego goto error; 2057f667e74Sjose borrego } 20689dc44ceSjose borrego 20789dc44ceSjose borrego (void) snprintf(rootpath, MAXPATHLEN, ".zfs/snapshot/%s/%s", 20889dc44ceSjose borrego snapname, nodepath); 20989dc44ceSjose borrego 21089dc44ceSjose borrego 21189dc44ceSjose borrego vp = smb_lookuppathvptovp(sr, rootpath, fsrootvp, fsrootvp); 21289dc44ceSjose borrego 21389dc44ceSjose borrego if (vp) { 21489dc44ceSjose borrego *vss_cur_node = smb_node_lookup(sr, NULL, kcred, vp, 21589dc44ceSjose borrego gmttoken, cur_node, NULL, &attr); 2167f667e74Sjose borrego VN_RELE(vp); 2177f667e74Sjose borrego 21889dc44ceSjose borrego if (*vss_cur_node != NULL) { 21989dc44ceSjose borrego smb_vss_remove_first_token_from_path(buf); 22089dc44ceSjose borrego } else { 22189dc44ceSjose borrego (void) smb_node_release(*vss_root_node); 22289dc44ceSjose borrego err = ENOENT; 22389dc44ceSjose borrego } 22489dc44ceSjose borrego } else { 22589dc44ceSjose borrego (void) smb_node_release(*vss_root_node); 22689dc44ceSjose borrego err = ENOENT; 22789dc44ceSjose borrego } 22889dc44ceSjose borrego } else { 22989dc44ceSjose borrego err = ENOENT; 23089dc44ceSjose borrego } 23189dc44ceSjose borrego 23289dc44ceSjose borrego error: 23389dc44ceSjose borrego VN_RELE(fsrootvp); 23489dc44ceSjose borrego kmem_free(rootpath, MAXPATHLEN); 23589dc44ceSjose borrego kmem_free(snapname, MAXNAMELEN); 23689dc44ceSjose borrego kmem_free(nodepath, MAXPATHLEN); 23789dc44ceSjose borrego 23889dc44ceSjose borrego return (err); 23989dc44ceSjose borrego } 24089dc44ceSjose borrego 24189dc44ceSjose borrego static boolean_t 24289dc44ceSjose borrego smb_vss_is_gmttoken(const char *s) 24389dc44ceSjose borrego { 24489dc44ceSjose borrego char *t = "@GMT-NNNN.NN.NN-NN.NN.NN"; 24589dc44ceSjose borrego const char *str; 24689dc44ceSjose borrego char *template; 24789dc44ceSjose borrego 24889dc44ceSjose borrego template = t; 24989dc44ceSjose borrego str = s; 25089dc44ceSjose borrego 25189dc44ceSjose borrego while (*template) { 25289dc44ceSjose borrego if (*template == 'N') { 25389dc44ceSjose borrego if (!mts_isdigit(*str)) 25489dc44ceSjose borrego return (B_FALSE); 25589dc44ceSjose borrego } else if (*template != *str) { 25689dc44ceSjose borrego return (B_FALSE); 25789dc44ceSjose borrego } 25889dc44ceSjose borrego 25989dc44ceSjose borrego template++; 26089dc44ceSjose borrego str++; 26189dc44ceSjose borrego } 26289dc44ceSjose borrego 26389dc44ceSjose borrego /* Make sure it is JUST the @GMT token */ 26489dc44ceSjose borrego if ((*str == '\0') || (*str == '/')) 26589dc44ceSjose borrego return (B_TRUE); 26689dc44ceSjose borrego 26789dc44ceSjose borrego return (B_FALSE); 26889dc44ceSjose borrego } 26989dc44ceSjose borrego 27089dc44ceSjose borrego static const char * 27189dc44ceSjose borrego smb_vss_find_gmttoken(const char *path) 27289dc44ceSjose borrego { 27389dc44ceSjose borrego const char *p; 27489dc44ceSjose borrego 27589dc44ceSjose borrego p = path; 27689dc44ceSjose borrego 27789dc44ceSjose borrego while (*p) { 27889dc44ceSjose borrego if (smb_vss_is_gmttoken(p)) 27989dc44ceSjose borrego return (p); 28089dc44ceSjose borrego p++; 28189dc44ceSjose borrego } 28289dc44ceSjose borrego return (NULL); 28389dc44ceSjose borrego } 28489dc44ceSjose borrego 28589dc44ceSjose borrego static int 28689dc44ceSjose borrego smb_vss_get_fsmountpath(smb_request_t *sr, char *buf, uint32_t buflen) 28789dc44ceSjose borrego { 28889dc44ceSjose borrego vnode_t *vp, *root_vp; 28989dc44ceSjose borrego vfs_t *vfsp; 29089dc44ceSjose borrego int err; 29189dc44ceSjose borrego 29289dc44ceSjose borrego ASSERT(sr->tid_tree); 29389dc44ceSjose borrego ASSERT(sr->tid_tree->t_snode); 29489dc44ceSjose borrego ASSERT(sr->tid_tree->t_snode->vp); 29589dc44ceSjose borrego ASSERT(sr->tid_tree->t_snode->vp->v_vfsp); 29689dc44ceSjose borrego 29789dc44ceSjose borrego vp = sr->tid_tree->t_snode->vp; 29889dc44ceSjose borrego vfsp = vp->v_vfsp; 29989dc44ceSjose borrego 30089dc44ceSjose borrego if (VFS_ROOT(vfsp, &root_vp)) 30189dc44ceSjose borrego return (ENOENT); 30289dc44ceSjose borrego 30389dc44ceSjose borrego VN_HOLD(vp); 30489dc44ceSjose borrego 30589dc44ceSjose borrego /* NULL is passed in as we want to start at "/" */ 30689dc44ceSjose borrego err = vnodetopath(NULL, root_vp, buf, buflen, sr->user_cr); 30789dc44ceSjose borrego 30889dc44ceSjose borrego VN_RELE(vp); 30989dc44ceSjose borrego VN_RELE(root_vp); 31089dc44ceSjose borrego return (err); 31189dc44ceSjose borrego } 31289dc44ceSjose borrego 31389dc44ceSjose borrego static uint32_t 31489dc44ceSjose borrego smb_vss_encode_gmttokens(smb_request_t *sr, smb_xa_t *xa, 31589dc44ceSjose borrego int32_t count, smb_dr_return_gmttokens_t *snap_data) 31689dc44ceSjose borrego { 31789dc44ceSjose borrego uint32_t i; 31889dc44ceSjose borrego uint32_t returned_count; 31989dc44ceSjose borrego uint32_t num_gmttokens; 32089dc44ceSjose borrego char **gmttokens; 321*b1352070SAlan Wright uint32_t status = NT_STATUS_SUCCESS; 32289dc44ceSjose borrego uint32_t data_size; 32389dc44ceSjose borrego 32489dc44ceSjose borrego returned_count = snap_data->rg_count; 32589dc44ceSjose borrego num_gmttokens = snap_data->rg_gmttokens.rg_gmttokens_len; 32689dc44ceSjose borrego gmttokens = snap_data->rg_gmttokens.rg_gmttokens_val; 32789dc44ceSjose borrego 328*b1352070SAlan Wright if (returned_count > count) 329*b1352070SAlan Wright status = NT_STATUS_BUFFER_TOO_SMALL; 33089dc44ceSjose borrego 33189dc44ceSjose borrego data_size = returned_count * SMB_VSS_GMT_NET_SIZE(sr) + 33289dc44ceSjose borrego smb_ascii_or_unicode_null_len(sr); 33389dc44ceSjose borrego 33489dc44ceSjose borrego if (smb_mbc_encodef(&xa->rep_data_mb, "lll", returned_count, 335*b1352070SAlan Wright num_gmttokens, data_size) != 0) 336*b1352070SAlan Wright return (NT_STATUS_INVALID_PARAMETER); 33789dc44ceSjose borrego 338*b1352070SAlan Wright if (status == NT_STATUS_SUCCESS) { 33989dc44ceSjose borrego for (i = 0; i < num_gmttokens; i++) { 34089dc44ceSjose borrego if (smb_mbc_encodef(&xa->rep_data_mb, "%u", sr, 341*b1352070SAlan Wright *gmttokens) != 0) 342*b1352070SAlan Wright status = NT_STATUS_INVALID_PARAMETER; 34389dc44ceSjose borrego gmttokens++; 34489dc44ceSjose borrego } 34589dc44ceSjose borrego } 34689dc44ceSjose borrego 347*b1352070SAlan Wright return (status); 34889dc44ceSjose borrego } 34989dc44ceSjose borrego 35089dc44ceSjose borrego /* This removes the first @GMT from the path */ 35189dc44ceSjose borrego static void 35289dc44ceSjose borrego smb_vss_remove_first_token_from_path(char *path) 35389dc44ceSjose borrego { 35489dc44ceSjose borrego boolean_t found; 35589dc44ceSjose borrego char *src, *dest; 35689dc44ceSjose borrego 35789dc44ceSjose borrego src = path; 35889dc44ceSjose borrego dest = path; 35989dc44ceSjose borrego 36089dc44ceSjose borrego found = B_FALSE; 36189dc44ceSjose borrego 36289dc44ceSjose borrego while (*src != '\0') { 36389dc44ceSjose borrego if (!found && smb_vss_is_gmttoken(src)) { 36489dc44ceSjose borrego src += SMB_VSS_GMT_SIZE - 1; 36589dc44ceSjose borrego if (*src == '/') 36689dc44ceSjose borrego src += 1; 36789dc44ceSjose borrego found = B_TRUE; 36889dc44ceSjose borrego continue; 36989dc44ceSjose borrego } 37089dc44ceSjose borrego *dest = *src; 37189dc44ceSjose borrego src++; 37289dc44ceSjose borrego dest++; 37389dc44ceSjose borrego } 37489dc44ceSjose borrego *dest = *src; 37589dc44ceSjose borrego } 376