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