smbd_vss.c revision 12508:edb7861a1533
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/*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include <synch.h>
27#include <pthread.h>
28#include <unistd.h>
29#include <string.h>
30#include <strings.h>
31#include <sys/errno.h>
32#include <libzfs.h>
33
34#include <smbsrv/libsmb.h>
35#include <smbsrv/libsmbns.h>
36#include <smbsrv/libmlsvc.h>
37#include <smbsrv/smbinfo.h>
38#include "smbd.h"
39
40/*
41 * This file supports three basic functions that all use the
42 * the zfs_iter_snapshots function to get the snapshot info
43 * from ZFS.  If the filesystem is not ZFS, the an error is sent
44 * to the caller (door functions in this case) with the count of
45 * zero in the case of smbd_vss_get_count.  Each function
46 * is expecting a path that is the root of the dataset.
47 * The basic idea is to define a structure for the data and
48 * an iterator function that will be called for every snapshot
49 * in the dataset that was opened.  The iterator function gets
50 * a zfs_handle_t(that needs to be closed) for the snapshot
51 * and a pointer to the structure of data defined passed to it.
52 * If the iterator function returns a non-zero value, no more
53 * snapshots will be processed.  There is no guarantee in the
54 * order in which the snapshots are processed.
55 *
56 * The structure of this file is:
57 * Three structures that are used between the iterator functions
58 * and "main" functions
59 * The 3 "main" functions
60 * Support functions
61 * The 3 iterator functions
62 */
63
64/*
65 * The maximum number of snapshots returned per request.
66 */
67#define	SMBD_VSS_SNAPSHOT_MAX	725
68
69static void smbd_vss_time2gmttoken(time_t time, char *gmttoken);
70static int smbd_vss_cmp_time(const void *a, const void *b);
71static int smbd_vss_iterate_count(zfs_handle_t *zhp, void *data);
72static int smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data);
73static int smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data);
74
75typedef struct smbd_vss_count {
76	int vc_count;
77} smbd_vss_count_t;
78
79/*
80 * gd_count how many @GMT tokens are expected
81 * gd_return_count how many @GMT tokens are being returned
82 * gd_gmt_array array of the @GMT token with max size of gd_count
83 */
84typedef struct smbd_vss_get_uint64_date {
85	int gd_count;
86	int gd_return_count;
87	uint64_t *gd_gmt_array;
88} smbd_vss_get_uint64_date_t;
89
90typedef struct smbd_vss_map_gmttoken {
91	char *mg_gmttoken;
92	char *mg_snapname;
93} smbd_vss_map_gmttoken_t;
94
95
96/*
97 * path - path of the dataset
98 * count - return value of the number of snapshots for the dataset
99 */
100int
101smbd_vss_get_count(const char *path, uint32_t *count)
102{
103	char dataset[MAXPATHLEN];
104	libzfs_handle_t *libhd;
105	zfs_handle_t *zfshd;
106	smbd_vss_count_t vss_count;
107
108	bzero(&vss_count, sizeof (smbd_vss_count_t));
109	*count = 0;
110
111	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0)
112		return (-1);
113
114	if ((libhd = libzfs_init()) == NULL)
115		return (-1);
116
117	if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) {
118		libzfs_fini(libhd);
119		return (-1);
120	}
121
122	(void) zfs_iter_snapshots(zfshd, smbd_vss_iterate_count,
123	    (void *)&vss_count);
124
125	if (vss_count.vc_count > SMBD_VSS_SNAPSHOT_MAX)
126		vss_count.vc_count = SMBD_VSS_SNAPSHOT_MAX;
127
128	*count = vss_count.vc_count;
129	zfs_close(zfshd);
130	libzfs_fini(libhd);
131	return (0);
132}
133
134/*
135 * path - is the path of the dataset
136 * count - is the maxium number of GMT tokens allowed to be returned
137 * return_count - is how many should be returned
138 * num_gmttokens - how many gmttokens in gmttokenp (0 if error)
139 * gmttokenp - array of @GMT tokens (even if zero, elements still need
140 * to be freed)
141 */
142
143void
144smbd_vss_get_snapshots(const char *path, uint32_t count,
145    uint32_t *return_count, uint32_t *num_gmttokens, char **gmttokenp)
146{
147	char dataset[MAXPATHLEN];
148	libzfs_handle_t *libhd;
149	zfs_handle_t *zfshd;
150	smbd_vss_get_uint64_date_t vss_uint64_date;
151	int i;
152	uint64_t *timep;
153
154	*return_count = 0;
155	*num_gmttokens = 0;
156
157	if (count == 0)
158		return;
159
160	if (count > SMBD_VSS_SNAPSHOT_MAX)
161		count = SMBD_VSS_SNAPSHOT_MAX;
162
163	vss_uint64_date.gd_count = count;
164	vss_uint64_date.gd_return_count = 0;
165	vss_uint64_date.gd_gmt_array = malloc(count * sizeof (uint64_t));
166	if (vss_uint64_date.gd_gmt_array == NULL)
167		return;
168
169	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0) {
170		free(vss_uint64_date.gd_gmt_array);
171		return;
172	}
173
174	if ((libhd = libzfs_init()) == NULL) {
175		free(vss_uint64_date.gd_gmt_array);
176		return;
177	}
178
179	if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) {
180		free(vss_uint64_date.gd_gmt_array);
181		libzfs_fini(libhd);
182		return;
183	}
184
185	(void) zfs_iter_snapshots(zfshd, smbd_vss_iterate_get_uint64_date,
186	    (void *)&vss_uint64_date);
187
188	*num_gmttokens = vss_uint64_date.gd_return_count;
189	*return_count = vss_uint64_date.gd_return_count;
190
191	/*
192	 * Sort the list since neither zfs nor the client sorts it.
193	 */
194	qsort((char *)vss_uint64_date.gd_gmt_array,
195	    vss_uint64_date.gd_return_count,
196	    sizeof (uint64_t), smbd_vss_cmp_time);
197
198	timep = vss_uint64_date.gd_gmt_array;
199
200	for (i = 0; i < vss_uint64_date.gd_return_count; i++) {
201		*gmttokenp = malloc(SMB_VSS_GMT_SIZE);
202
203		if (*gmttokenp)
204			smbd_vss_time2gmttoken(*timep, *gmttokenp);
205		else
206			vss_uint64_date.gd_return_count = 0;
207
208		timep++;
209		gmttokenp++;
210	}
211
212	free(vss_uint64_date.gd_gmt_array);
213	zfs_close(zfshd);
214	libzfs_fini(libhd);
215}
216
217/*
218 * path - path of the dataset for the operation
219 * gmttoken - the @GMT token to be looked up
220 * snapname - the snapshot name to be returned
221 *
222 * Here we are going to get the snapshot name from the @GMT token
223 * The snapname returned by ZFS is : <dataset name>@<snapshot name>
224 * So we are going to make sure there is the @ symbol in
225 * the right place and then just return the snapshot name
226 */
227int
228smbd_vss_map_gmttoken(const char *path, char *gmttoken, char *snapname)
229{
230	char dataset[MAXPATHLEN];
231	libzfs_handle_t *libhd;
232	zfs_handle_t *zfshd;
233	smbd_vss_map_gmttoken_t vss_map_gmttoken;
234	char *zsnap;
235	const char *lsnap;
236
237	vss_map_gmttoken.mg_gmttoken = gmttoken;
238	vss_map_gmttoken.mg_snapname = snapname;
239	*snapname = '\0';
240
241	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0)
242		return (-1);
243
244	if ((libhd = libzfs_init()) == NULL)
245		return (-1);
246
247	if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) {
248		libzfs_fini(libhd);
249		return (-1);
250	}
251
252	(void) zfs_iter_snapshots(zfshd, smbd_vss_iterate_map_gmttoken,
253	    (void *)&vss_map_gmttoken);
254
255	/* compare the zfs snapshot name and the local snap name */
256	zsnap = snapname;
257	lsnap = dataset;
258	while ((*lsnap != '\0') && (*zsnap != '\0') && (*lsnap == *zsnap)) {
259		zsnap++;
260		lsnap++;
261	}
262
263	/* Now we should be passed the dataset name */
264	if ((*zsnap == '@') && (*lsnap == '\0')) {
265		zsnap++;
266		(void) strlcpy(snapname, zsnap, MAXPATHLEN);
267	} else {
268		*snapname = '\0';
269	}
270
271	zfs_close(zfshd);
272	libzfs_fini(libhd);
273	return (0);
274}
275
276static void
277smbd_vss_time2gmttoken(time_t time, char *gmttoken)
278{
279	struct tm t;
280
281	(void) gmtime_r(&time, &t);
282
283	(void) strftime(gmttoken, SMB_VSS_GMT_SIZE,
284	    "@GMT-%Y.%m.%d-%H.%M.%S", &t);
285}
286
287static int
288smbd_vss_cmp_time(const void *a, const void *b)
289{
290	if (*(uint64_t *)a < *(uint64_t *)b)
291		return (1);
292	if (*(uint64_t *)a == *(uint64_t *)b)
293		return (0);
294	return (-1);
295}
296
297/*
298 * ZFS snapshot iterator to count snapshots.
299 * Note: libzfs expects us to close the handle.
300 * Return 0 to continue iterating or non-zreo to terminate the iteration.
301 */
302static int
303smbd_vss_iterate_count(zfs_handle_t *zhp, void *data)
304{
305	smbd_vss_count_t *vss_data = data;
306
307	if (vss_data->vc_count < SMBD_VSS_SNAPSHOT_MAX) {
308		vss_data->vc_count++;
309		zfs_close(zhp);
310		return (0);
311	}
312
313	zfs_close(zhp);
314	return (-1);
315}
316
317/*
318 * ZFS snapshot iterator to get snapshot creation time.
319 * Note: libzfs expects us to close the handle.
320 * Return 0 to continue iterating or non-zreo to terminate the iteration.
321 */
322static int
323smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data)
324{
325	smbd_vss_get_uint64_date_t *vss_data = data;
326	int count;
327
328	count = vss_data->gd_return_count;
329
330	if (count < vss_data->gd_count) {
331		vss_data->gd_gmt_array[count] =
332		    zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
333		vss_data->gd_return_count++;
334		zfs_close(zhp);
335		return (0);
336	}
337
338	zfs_close(zhp);
339	return (-1);
340}
341
342/*
343 * ZFS snapshot iterator to map a snapshot creation time to a token.
344 * Note: libzfs expects us to close the handle.
345 * Return 0 to continue iterating or non-zreo to terminate the iteration.
346 */
347static int
348smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data)
349{
350	smbd_vss_map_gmttoken_t *vss_data = data;
351	time_t time;
352	char gmttoken[SMB_VSS_GMT_SIZE];
353
354	time = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
355	smbd_vss_time2gmttoken(time, gmttoken);
356
357	if (strncmp(gmttoken, vss_data->mg_gmttoken, SMB_VSS_GMT_SIZE) == 0) {
358		(void) strlcpy(vss_data->mg_snapname, zfs_get_name(zhp),
359		    MAXPATHLEN);
360
361		/* we found a match, do not process anymore snapshots */
362		zfs_close(zhp);
363		return (-1);
364	}
365
366	zfs_close(zhp);
367	return (0);
368}
369