Home | History | Annotate | Download | only in smbsrv
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * Volume Copy Shadow Services (VSS) provides a way for users to
     28  * restore/recover deleted files/directories.
     29  * For the server to support VSS for Microsoft clients, there is
     30  * two basic functions that need to be implemented.
     31  * The first is to intercept the NT_TRANSACT_IOCTL command with
     32  * the function code of FSCTL_SRV_ENUMERATE_SNAPSHOTS (0x00144064).
     33  * This is to report the count or the count and list of snapshots
     34  * for that share.
     35  * The second function need to trap commands with the
     36  * SMB_FLAGS2_REPARSE_PATH bit set in the smb header.  This bit
     37  * means that there is a @GMT token in path that needs to be
     38  * processed.  The @GMT token means to process this command, but
     39  * in the snapshot.
     40  */
     41 
     42 #include <smbsrv/smb_kproto.h>
     43 #include <smbsrv/string.h>
     44 #include <smbsrv/winioctl.h>
     45 #include <smbsrv/smb_door_svc.h>
     46 
     47 /* Size of the token on the wire due to encoding */
     48 #define	SMB_VSS_GMT_NET_SIZE(sr) (smb_ascii_or_unicode_null_len(sr) * \
     49     SMB_VSS_GMT_SIZE)
     50 
     51 #define	SMB_VSS_COUNT_SIZE 16
     52 
     53 static boolean_t smb_vss_is_gmttoken(const char *str);
     54 static const char *smb_vss_find_gmttoken(const char *path);
     55 static int smb_vss_get_fsmountpath(smb_request_t *sr, char *buf,
     56     uint32_t buflen);
     57 static uint32_t smb_vss_encode_gmttokens(smb_request_t *sr, smb_xa_t *xa,
     58     int32_t count, smb_dr_return_gmttokens_t *snap_data);
     59 static void smb_vss_remove_first_token_from_path(char *c);
     60 
     61 /*
     62  * This is to respond to the nt_transact_ioctl to either respond with the
     63  * number of snapshots, or to respond with the list.  It needs to be sorted
     64  * before the reply.  If the the max data bytes to return is
     65  * SMB_VSS_COUNT_SIZE, then all that is requested is the count, otherwise
     66  * return the count and the list of @GMT tokens (one token for each
     67  * snapshot).
     68  */
     69 uint32_t
     70 smb_vss_ioctl_enumerate_snaps(smb_request_t *sr, smb_xa_t *xa)
     71 {
     72 	uint32_t count = 0;
     73 	char *root_path;
     74 	uint32_t status = NT_STATUS_SUCCESS;
     75 	smb_dr_return_gmttokens_t gmttokens;
     76 
     77 	if (xa->smb_mdrcnt < SMB_VSS_COUNT_SIZE)
     78 		return (NT_STATUS_INVALID_PARAMETER);
     79 
     80 	root_path  = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
     81 	if (smb_vss_get_fsmountpath(sr, root_path, MAXPATHLEN) != 0)
     82 		return (NT_STATUS_INVALID_PARAMETER);
     83 
     84 	if (xa->smb_mdrcnt == SMB_VSS_COUNT_SIZE) {
     85 		count = smb_upcall_vss_get_count(root_path);
     86 		if (smb_mbc_encodef(&xa->rep_data_mb, "lllw", count, 0,
     87 		    (count * SMB_VSS_GMT_NET_SIZE(sr) +
     88 		    smb_ascii_or_unicode_null_len(sr)), 0) != 0) {
     89 			status = NT_STATUS_INVALID_PARAMETER;
     90 		}
     91 	} else {
     92 		count = xa->smb_mdrcnt / SMB_VSS_GMT_NET_SIZE(sr);
     93 
     94 		smb_upcall_vss_get_snapshots(root_path, count, &gmttokens);
     95 
     96 		status = smb_vss_encode_gmttokens(sr, xa, count, &gmttokens);
     97 
     98 		smb_upcall_vss_get_snapshots_free(&gmttokens);
     99 	}
    100 
    101 	kmem_free(root_path, MAXPATHLEN);
    102 	return (status);
    103 }
    104 
    105 /*
    106  * sr - the request info, used to find root of dataset,
    107  *      unicode or ascii, where the share is rooted in the
    108  *      dataset
    109  * root_node - root of the share
    110  * cur_node - where in the share for the command
    111  * buf - is the path for the command to be processed
    112  *       returned without @GMT if processed
    113  * vss_cur_node - returned value for the snapshot version
    114  *                of the cur_node
    115  * vss_root_node - returned value for the snapshot version
    116  *                 of the root_node
    117  *
    118  * This routine is the processing for handling the
    119  * SMB_FLAGS2_REPARSE_PATH bit being set in the smb header.
    120  *
    121  * By using the cur_node passed in, a new node is found or
    122  * created that is the same place in the directory tree, but
    123  * in the snapshot. We also use root_node to do the same for
    124  * the root.
    125  * One the new smb node is found, the path is modified by
    126  * removing the @GMT token from the path in the buf.
    127  */
    128 int
    129 smb_vss_lookup_nodes(smb_request_t *sr, smb_node_t *root_node,
    130     smb_node_t *cur_node, char *buf, smb_node_t **vss_cur_node,
    131     smb_node_t **vss_root_node)
    132 {
    133 	const char *p;
    134 	char *rootpath;
    135 	char *snapname;
    136 	char *nodepath;
    137 	char gmttoken[SMB_VSS_GMT_SIZE];
    138 	vnode_t *fsrootvp;
    139 	vnode_t *vp = NULL;
    140 	int err = 0;
    141 
    142 	if (sr->tid_tree == NULL)
    143 		return (ESTALE);
    144 
    145 	ASSERT(sr->tid_tree->t_snode);
    146 	ASSERT(sr->tid_tree->t_snode->vp);
    147 	ASSERT(sr->tid_tree->t_snode->vp->v_vfsp);
    148 
    149 	if ((p = smb_vss_find_gmttoken(buf)) == NULL)
    150 		return (ENOENT);
    151 
    152 	bcopy(p, gmttoken, SMB_VSS_GMT_SIZE);
    153 	gmttoken[SMB_VSS_GMT_SIZE - 1] = '\0';
    154 
    155 	err = VFS_ROOT(sr->tid_tree->t_snode->vp->v_vfsp, &fsrootvp);
    156 	if (err != 0)
    157 		return (err);
    158 
    159 	rootpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
    160 	snapname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
    161 	nodepath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
    162 
    163 	err = smb_vss_get_fsmountpath(sr, rootpath, MAXPATHLEN);
    164 	if (err != 0)
    165 		goto error;
    166 
    167 	*snapname = '\0';
    168 
    169 	smb_upcall_vss_map_gmttoken(rootpath, gmttoken, snapname);
    170 
    171 	if (!*snapname) {
    172 		err = ENOENT;
    173 		goto error;
    174 	}
    175 
    176 	/* note the value of root_node->vp */
    177 	err = vnodetopath(fsrootvp, root_node->vp, nodepath,
    178 	    MAXPATHLEN, kcred);
    179 
    180 	if (err != 0)
    181 		goto error;
    182 
    183 	(void) snprintf(rootpath, MAXPATHLEN, ".zfs/snapshot/%s/%s",
    184 	    snapname, nodepath);
    185 
    186 	vp = smb_lookuppathvptovp(sr, rootpath, fsrootvp, fsrootvp);
    187 
    188 	if (vp) {
    189 		/* note the value of cur_node->vp */
    190 		err = vnodetopath(fsrootvp, cur_node->vp, nodepath,
    191 		    MAXPATHLEN, kcred);
    192 		if (err != 0) {
    193 			VN_RELE(vp);
    194 			goto error;
    195 		}
    196 
    197 		*vss_root_node = smb_node_lookup(sr, NULL, kcred, vp,
    198 		    gmttoken, cur_node, NULL);
    199 		VN_RELE(vp);
    200 
    201 		if (*vss_root_node == NULL) {
    202 			err = ENOENT;
    203 			goto error;
    204 		}
    205 
    206 		(void) snprintf(rootpath, MAXPATHLEN, ".zfs/snapshot/%s/%s",
    207 		    snapname, nodepath);
    208 
    209 
    210 		vp = smb_lookuppathvptovp(sr, rootpath, fsrootvp, fsrootvp);
    211 
    212 		if (vp) {
    213 			*vss_cur_node = smb_node_lookup(sr, NULL, kcred, vp,
    214 			    gmttoken, cur_node, NULL);
    215 			VN_RELE(vp);
    216 
    217 			if (*vss_cur_node != NULL) {
    218 				smb_vss_remove_first_token_from_path(buf);
    219 			} else {
    220 				(void) smb_node_release(*vss_root_node);
    221 				err = ENOENT;
    222 			}
    223 		} else {
    224 			(void) smb_node_release(*vss_root_node);
    225 			err = ENOENT;
    226 		}
    227 	} else {
    228 		err = ENOENT;
    229 	}
    230 
    231 error:
    232 	VN_RELE(fsrootvp);
    233 	kmem_free(rootpath, MAXPATHLEN);
    234 	kmem_free(snapname, MAXNAMELEN);
    235 	kmem_free(nodepath, MAXPATHLEN);
    236 
    237 	return (err);
    238 }
    239 
    240 static boolean_t
    241 smb_vss_is_gmttoken(const char *s)
    242 {
    243 	char *t = "@GMT-NNNN.NN.NN-NN.NN.NN";
    244 	const char *str;
    245 	char *template;
    246 
    247 	template = t;
    248 	str = s;
    249 
    250 	while (*template) {
    251 		if (*template == 'N') {
    252 			if (!smb_isdigit(*str))
    253 				return (B_FALSE);
    254 		} else if (*template != *str) {
    255 			return (B_FALSE);
    256 		}
    257 
    258 		template++;
    259 		str++;
    260 	}
    261 
    262 	/* Make sure it is JUST the @GMT token */
    263 	if ((*str == '\0') || (*str == '/'))
    264 		return (B_TRUE);
    265 
    266 	return (B_FALSE);
    267 }
    268 
    269 static const char *
    270 smb_vss_find_gmttoken(const char *path)
    271 {
    272 	const char *p;
    273 
    274 	p = path;
    275 
    276 	while (*p) {
    277 		if (smb_vss_is_gmttoken(p))
    278 			return (p);
    279 		p++;
    280 	}
    281 	return (NULL);
    282 }
    283 
    284 static int
    285 smb_vss_get_fsmountpath(smb_request_t *sr, char *buf, uint32_t buflen)
    286 {
    287 	vnode_t *vp, *root_vp;
    288 	vfs_t *vfsp;
    289 	int err;
    290 
    291 	ASSERT(sr->tid_tree);
    292 	ASSERT(sr->tid_tree->t_snode);
    293 	ASSERT(sr->tid_tree->t_snode->vp);
    294 	ASSERT(sr->tid_tree->t_snode->vp->v_vfsp);
    295 
    296 	vp = sr->tid_tree->t_snode->vp;
    297 	vfsp = vp->v_vfsp;
    298 
    299 	if (VFS_ROOT(vfsp, &root_vp))
    300 		return (ENOENT);
    301 
    302 	VN_HOLD(vp);
    303 
    304 	/* NULL is passed in as we want to start at "/" */
    305 	err = vnodetopath(NULL, root_vp, buf, buflen, sr->user_cr);
    306 
    307 	VN_RELE(vp);
    308 	VN_RELE(root_vp);
    309 	return (err);
    310 }
    311 
    312 static uint32_t
    313 smb_vss_encode_gmttokens(smb_request_t *sr, smb_xa_t *xa,
    314     int32_t count, smb_dr_return_gmttokens_t *snap_data)
    315 {
    316 	uint32_t i;
    317 	uint32_t returned_count;
    318 	uint32_t num_gmttokens;
    319 	char **gmttokens;
    320 	uint32_t status = NT_STATUS_SUCCESS;
    321 	uint32_t data_size;
    322 
    323 	returned_count = snap_data->rg_count;
    324 	num_gmttokens = snap_data->rg_gmttokens.rg_gmttokens_len;
    325 	gmttokens = snap_data->rg_gmttokens.rg_gmttokens_val;
    326 
    327 	if (returned_count > count)
    328 		status = NT_STATUS_BUFFER_TOO_SMALL;
    329 
    330 	data_size = returned_count * SMB_VSS_GMT_NET_SIZE(sr) +
    331 	    smb_ascii_or_unicode_null_len(sr);
    332 
    333 	if (smb_mbc_encodef(&xa->rep_data_mb, "lll", returned_count,
    334 	    num_gmttokens, data_size) != 0)
    335 		return (NT_STATUS_INVALID_PARAMETER);
    336 
    337 	if (status == NT_STATUS_SUCCESS) {
    338 		for (i = 0; i < num_gmttokens; i++) {
    339 			if (smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
    340 			    *gmttokens) != 0)
    341 				status = NT_STATUS_INVALID_PARAMETER;
    342 			gmttokens++;
    343 		}
    344 	}
    345 
    346 	return (status);
    347 }
    348 
    349 /* This removes the first @GMT from the path */
    350 static void
    351 smb_vss_remove_first_token_from_path(char *path)
    352 {
    353 	boolean_t found;
    354 	char *src, *dest;
    355 
    356 	src = path;
    357 	dest = path;
    358 
    359 	found = B_FALSE;
    360 
    361 	while (*src != '\0') {
    362 		if (!found && smb_vss_is_gmttoken(src)) {
    363 			src += SMB_VSS_GMT_SIZE - 1;
    364 			if (*src == '/')
    365 				src += 1;
    366 			found = B_TRUE;
    367 			continue;
    368 		}
    369 		*dest = *src;
    370 		src++;
    371 		dest++;
    372 	}
    373 	*dest = *src;
    374 }
    375