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