xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_vss.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 
69 static void smbd_vss_time2gmttoken(time_t time, char *gmttoken);
70 static int smbd_vss_cmp_time(const void *a, const void *b);
71 static int smbd_vss_iterate_count(zfs_handle_t *zhp, void *data);
72 static int smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data);
73 static int smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data);
74 
75 typedef 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  */
84 typedef 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 
90 typedef 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  */
100 int
101 smbd_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 
143 void
144 smbd_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  */
227 int
228 smbd_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 
276 static void
277 smbd_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 
287 static int
288 smbd_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  */
302 static int
303 smbd_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  */
322 static int
323 smbd_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  */
347 static int
348 smbd_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