xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_vss.c (revision 2264ca7f5db194583c672cb5779a67f52bcd92a9)
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 #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/libsmbrdr.h>
36 #include <smbsrv/libsmbns.h>
37 #include <smbsrv/libmlsvc.h>
38 #include <smbsrv/smbinfo.h>
39 #include <smbsrv/ntstatus.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 static void smbd_vss_time2gmttoken(time_t time, char *gmttoken);
67 static int smbd_vss_cmp_time(const void *a, const void *b);
68 static int smbd_vss_iterate_count(zfs_handle_t *zhp, void *data);
69 static int smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data);
70 static int smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data);
71 
72 typedef struct smbd_vss_count {
73 	int vc_count;
74 } smbd_vss_count_t;
75 
76 /*
77  * gd_count how many @GMT tokens are expected
78  * gd_return_count how many @GMT tokens are being returned
79  * gd_gmt_array array of the @GMT token with max size of gd_count
80  */
81 typedef struct smbd_vss_get_uint64_date {
82 	int gd_count;
83 	int gd_return_count;
84 	uint64_t *gd_gmt_array;
85 } smbd_vss_get_uint64_date_t;
86 
87 typedef struct smbd_vss_map_gmttoken {
88 	char *mg_gmttoken;
89 	char *mg_snapname;
90 } smbd_vss_map_gmttoken_t;
91 
92 
93 /*
94  * path - path of the dataset
95  * count - return value of the number of snapshots for the dataset
96  */
97 int
98 smbd_vss_get_count(const char *path, uint32_t *count)
99 {
100 	char dataset[MAXPATHLEN];
101 	libzfs_handle_t *libhd;
102 	zfs_handle_t *zfshd;
103 	smbd_vss_count_t vss_count;
104 
105 	bzero(&vss_count, sizeof (smbd_vss_count_t));
106 	*count = 0;
107 
108 	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0)
109 		return (-1);
110 
111 	if ((libhd = libzfs_init()) == NULL)
112 		return (-1);
113 
114 	if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) {
115 		libzfs_fini(libhd);
116 		return (-1);
117 	}
118 
119 	(void) zfs_iter_snapshots(zfshd, smbd_vss_iterate_count,
120 	    (void *)&vss_count);
121 
122 	*count = vss_count.vc_count;
123 	zfs_close(zfshd);
124 	libzfs_fini(libhd);
125 	return (0);
126 }
127 
128 /*
129  * path - is the path of the dataset
130  * count - is the maxium number of GMT tokens allowed to be returned
131  * return_count - is how many should be returned
132  * num_gmttokens - how many gmttokens in gmttokenp (0 if error)
133  * gmttokenp - array of @GMT tokens (even if zero, elements still need
134  * to be freed)
135  */
136 
137 void
138 smbd_vss_get_snapshots(const char *path, uint32_t count,
139     uint32_t *return_count, uint32_t *num_gmttokens, char **gmttokenp)
140 {
141 	char dataset[MAXPATHLEN];
142 	libzfs_handle_t *libhd;
143 	zfs_handle_t *zfshd;
144 	smbd_vss_get_uint64_date_t vss_uint64_date;
145 	int i;
146 	uint64_t *timep;
147 
148 	*return_count = 0;
149 	*num_gmttokens = 0;
150 
151 	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0)
152 		return;
153 
154 	if ((libhd = libzfs_init()) == NULL)
155 		return;
156 
157 	if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) {
158 		libzfs_fini(libhd);
159 		return;
160 	}
161 
162 	vss_uint64_date.gd_count = count;
163 	vss_uint64_date.gd_return_count = 0;
164 	vss_uint64_date.gd_gmt_array = malloc(count * sizeof (uint64_t));
165 
166 	if (vss_uint64_date.gd_gmt_array != NULL) {
167 		(void) zfs_iter_snapshots(zfshd,
168 		    smbd_vss_iterate_get_uint64_date,
169 		    (void *)&vss_uint64_date);
170 
171 		*num_gmttokens = vss_uint64_date.gd_return_count;
172 		*return_count = vss_uint64_date.gd_return_count;
173 
174 		if (vss_uint64_date.gd_return_count <= count) {
175 			/*
176 			 * Sort the list since neither
177 			 * zfs nor the client sorts it.
178 			 */
179 			qsort((char *)vss_uint64_date.gd_gmt_array,
180 			    vss_uint64_date.gd_return_count,
181 			    sizeof (uint64_t), smbd_vss_cmp_time);
182 
183 			timep = vss_uint64_date.gd_gmt_array;
184 
185 			for (i = 0; i < vss_uint64_date.gd_return_count; i++) {
186 				*gmttokenp = malloc(SMB_VSS_GMT_SIZE);
187 
188 				if (*gmttokenp) {
189 					smbd_vss_time2gmttoken(*timep,
190 					    *gmttokenp);
191 				} else {
192 					vss_uint64_date.gd_return_count = 0;
193 				}
194 
195 				timep++;
196 				gmttokenp++;
197 			}
198 		}
199 
200 		free(vss_uint64_date.gd_gmt_array);
201 	}
202 
203 	zfs_close(zfshd);
204 	libzfs_fini(libhd);
205 }
206 
207 /*
208  * path - path of the dataset for the operation
209  * gmttoken - the @GMT token to be looked up
210  * snapname - the snapshot name to be returned
211  *
212  * Here we are going to get the snapshot name from the @GMT token
213  * The snapname returned by ZFS is : <dataset name>@<snapshot name>
214  * So we are going to make sure there is the @ symbol in
215  * the right place and then just return the snapshot name
216  */
217 int
218 smbd_vss_map_gmttoken(const char *path, char *gmttoken, char *snapname)
219 {
220 	char dataset[MAXPATHLEN];
221 	libzfs_handle_t *libhd;
222 	zfs_handle_t *zfshd;
223 	smbd_vss_map_gmttoken_t vss_map_gmttoken;
224 	char *zsnap;
225 	const char *lsnap;
226 
227 	vss_map_gmttoken.mg_gmttoken = gmttoken;
228 	vss_map_gmttoken.mg_snapname = snapname;
229 	*snapname = '\0';
230 
231 	if (smb_getdataset(path, dataset, MAXPATHLEN) != 0)
232 		return (-1);
233 
234 	if ((libhd = libzfs_init()) == NULL)
235 		return (-1);
236 
237 	if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_DATASET)) == NULL) {
238 		libzfs_fini(libhd);
239 		return (-1);
240 	}
241 
242 	(void) zfs_iter_snapshots(zfshd, smbd_vss_iterate_map_gmttoken,
243 	    (void *)&vss_map_gmttoken);
244 
245 	/* compare the zfs snapshot name and the local snap name */
246 	zsnap = snapname;
247 	lsnap = dataset;
248 	while ((*lsnap != '\0') && (*zsnap != '\0') && (*lsnap == *zsnap)) {
249 		zsnap++;
250 		lsnap++;
251 	}
252 
253 	/* Now we should be passed the dataset name */
254 	if ((*zsnap == '@') && (*lsnap == '\0')) {
255 		zsnap++;
256 		(void) strlcpy(snapname, zsnap, MAXPATHLEN);
257 	} else {
258 		*snapname = '\0';
259 	}
260 
261 	zfs_close(zfshd);
262 	libzfs_fini(libhd);
263 	return (0);
264 }
265 
266 static void
267 smbd_vss_time2gmttoken(time_t time, char *gmttoken)
268 {
269 	struct tm t;
270 
271 	(void) gmtime_r(&time, &t);
272 
273 	(void) strftime(gmttoken, SMB_VSS_GMT_SIZE,
274 	    "@GMT-%Y.%m.%d-%H.%M.%S", &t);
275 }
276 
277 static int
278 smbd_vss_cmp_time(const void *a, const void *b)
279 {
280 	if (*(uint64_t *)a < *(uint64_t *)b)
281 		return (1);
282 	if (*(uint64_t *)a == *(uint64_t *)b)
283 		return (0);
284 	return (-1);
285 }
286 
287 static int
288 smbd_vss_iterate_count(zfs_handle_t *zhp, void *data)
289 {
290 	smbd_vss_count_t *vss_data;
291 	vss_data = data;
292 	vss_data->vc_count++;
293 	/* libzfs expects us to close the handle */
294 	zfs_close(zhp);
295 	return (0);
296 }
297 
298 static int
299 smbd_vss_iterate_get_uint64_date(zfs_handle_t *zhp, void *data)
300 {
301 	smbd_vss_get_uint64_date_t *vss_data;
302 
303 	vss_data = data;
304 
305 	if (vss_data->gd_return_count < vss_data->gd_count) {
306 		vss_data->gd_gmt_array[vss_data->gd_return_count] =
307 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
308 	}
309 
310 	vss_data->gd_return_count += 1;
311 	/* libzfs expects us to close the handle */
312 	zfs_close(zhp);
313 	return (0);
314 }
315 
316 static int
317 smbd_vss_iterate_map_gmttoken(zfs_handle_t *zhp, void *data)
318 {
319 	smbd_vss_map_gmttoken_t *vss_data;
320 	time_t time;
321 	char gmttoken[SMB_VSS_GMT_SIZE];
322 
323 	vss_data = data;
324 
325 	time = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
326 	smbd_vss_time2gmttoken(time, gmttoken);
327 
328 	if (strncmp(gmttoken, vss_data->mg_gmttoken, SMB_VSS_GMT_SIZE) == 0) {
329 		(void) strlcpy(vss_data->mg_snapname, zfs_get_name(zhp),
330 		    MAXPATHLEN);
331 
332 		/* libzfs expects us to close the handle */
333 		zfs_close(zhp);
334 
335 		/* we found a match, do not process anymore snapshots */
336 		return (-1);
337 	}
338 
339 	/* libzfs expects us to close the handle */
340 	zfs_close(zhp);
341 	return (0);
342 }
343